@promptctl/cc-candybar 1.42.1 → 1.43.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/dist/index.mjs +72 -71
- package/package.json +5 -6
- package/src/check.ts +0 -478
- package/src/cli-flags.ts +0 -8
- package/src/click/wire.ts +0 -158
- package/src/config/action.ts +0 -329
- package/src/config/cli.ts +0 -71
- package/src/config/default-dsl-config.ts +0 -1645
- package/src/config/disclosure.ts +0 -170
- package/src/config/dsl-loader.ts +0 -339
- package/src/config/dsl-types.ts +0 -581
- package/src/config/edit-chrome.ts +0 -559
- package/src/config/help.ts +0 -151
- package/src/config/ident.ts +0 -22
- package/src/config/layout-ops.ts +0 -177
- package/src/config/loader/actions.ts +0 -972
- package/src/config/loader/cache.ts +0 -206
- package/src/config/loader/cross-ref.ts +0 -714
- package/src/config/loader/cycles.ts +0 -148
- package/src/config/loader/diagnostics.ts +0 -99
- package/src/config/loader/discovery.ts +0 -182
- package/src/config/loader/edit-mode.ts +0 -137
- package/src/config/loader/emit-schema.ts +0 -68
- package/src/config/loader/globals.ts +0 -269
- package/src/config/loader/helpers.ts +0 -48
- package/src/config/loader/layout.ts +0 -693
- package/src/config/loader/looks.ts +0 -96
- package/src/config/loader/menu-synth.ts +0 -435
- package/src/config/loader/merge.ts +0 -115
- package/src/config/loader/persist-target.ts +0 -67
- package/src/config/loader/presets.ts +0 -119
- package/src/config/loader/refs.ts +0 -100
- package/src/config/loader/reserved-namespace.ts +0 -38
- package/src/config/loader/segments.ts +0 -120
- package/src/config/loader/validate-core.ts +0 -737
- package/src/config/loader/variables.ts +0 -260
- package/src/config/menu-keys.ts +0 -139
- package/src/config/option-domain.ts +0 -164
- package/src/config/presets.ts +0 -326
- package/src/config/settings-menu.ts +0 -775
- package/src/daemon/acquire.ts +0 -684
- package/src/daemon/cache/git.ts +0 -649
- package/src/daemon/cache/render.ts +0 -623
- package/src/daemon/cache/session-usage-store.ts +0 -720
- package/src/daemon/cache/watchers.ts +0 -249
- package/src/daemon/client-debug.ts +0 -120
- package/src/daemon/client-stats.ts +0 -130
- package/src/daemon/client-transport.ts +0 -273
- package/src/daemon/client.ts +0 -78
- package/src/daemon/config-overrides-store.ts +0 -663
- package/src/daemon/debug-types.ts +0 -91
- package/src/daemon/debug.ts +0 -264
- package/src/daemon/fork-bomb-breaker.ts +0 -351
- package/src/daemon/limits.ts +0 -211
- package/src/daemon/log.ts +0 -81
- package/src/daemon/parent-watchdog.ts +0 -87
- package/src/daemon/paths.ts +0 -211
- package/src/daemon/process-fingerprint.ts +0 -146
- package/src/daemon/protocol.ts +0 -292
- package/src/daemon/render-payload.ts +0 -1256
- package/src/daemon/server.ts +0 -1330
- package/src/daemon/session-state-file.ts +0 -108
- package/src/daemon/session-state.ts +0 -237
- package/src/daemon/socket-lease.ts +0 -209
- package/src/daemon/socket-ownership.ts +0 -209
- package/src/daemon/stats.ts +0 -235
- package/src/daemon/verbs/config-validators.ts +0 -250
- package/src/daemon/verbs/index.ts +0 -706
- package/src/daemon/verbs/state-validators.ts +0 -249
- package/src/daemon/verbs/validator-registry.ts +0 -457
- package/src/demo/dsl.ts +0 -143
- package/src/demo/mock-data.ts +0 -67
- package/src/demo/statusline.json5 +0 -94
- package/src/dsl/node-registry.ts +0 -374
- package/src/dsl/render.ts +0 -803
- package/src/help-text.ts +0 -90
- package/src/index.ts +0 -210
- package/src/install/currency.ts +0 -197
- package/src/install/index.ts +0 -557
- package/src/proc/launch.ts +0 -459
- package/src/proc/stats-handle.ts +0 -13
- package/src/render/action.ts +0 -883
- package/src/render/active-segment.ts +0 -78
- package/src/render/diagnostic-style.ts +0 -23
- package/src/render/diagnostic-text.ts +0 -77
- package/src/render/error-glyph.ts +0 -53
- package/src/render/menu.ts +0 -257
- package/src/render/outcome-plan.ts +0 -45
- package/src/render/picker.ts +0 -372
- package/src/render/segment-color.ts +0 -74
- package/src/render/split-lines.ts +0 -51
- package/src/render/strip.ts +0 -228
- package/src/segments/cache.ts +0 -131
- package/src/segments/context.ts +0 -190
- package/src/segments/git.ts +0 -1084
- package/src/segments/metrics.ts +0 -187
- package/src/segments/pricing.ts +0 -452
- package/src/segments/session.ts +0 -23
- package/src/segments/tmux.ts +0 -74
- package/src/template-engine/cells.ts +0 -90
- package/src/template-engine/colors.ts +0 -124
- package/src/template-engine/engine.ts +0 -108
- package/src/template-engine/funcs.ts +0 -232
- package/src/template-engine/index.ts +0 -11
- package/src/template-engine/layout.ts +0 -133
- package/src/template-engine/scope.ts +0 -62
- package/src/template-engine/sparkline.ts +0 -79
- package/src/themes/index.ts +0 -20
- package/src/themes/palette-resolvers.ts +0 -84
- package/src/themes/policy.ts +0 -393
- package/src/utils/cache.ts +0 -206
- package/src/utils/claude.ts +0 -683
- package/src/utils/color-support.ts +0 -118
- package/src/utils/formatters.ts +0 -99
- package/src/utils/logger.ts +0 -5
- package/src/utils/outcome.ts +0 -33
- package/src/utils/schema-validator.ts +0 -126
- package/src/utils/single-flight.ts +0 -57
- package/src/utils/terminal-width.ts +0 -51
- package/src/utils/terminal.ts +0 -11
- package/src/utils/transcript-fs.ts +0 -279
- package/src/var-system/index.ts +0 -24
- package/src/var-system/sources.ts +0 -1047
- package/src/var-system/store.ts +0 -223
- package/src/var-system/types.ts +0 -57
- package/src/version.ts +0 -17
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
// [LAW:types-are-the-program] Wire-level snapshot shapes for the `debug`
|
|
2
|
-
// protocol message. The `what` discriminator carries the response shape
|
|
3
|
-
// forward — a client requesting "vars" gets vars data, "segments" gets
|
|
4
|
-
// segments data, "config" gets config data. There is no `data: unknown`
|
|
5
|
-
// field that callers must shape-check.
|
|
6
|
-
//
|
|
7
|
-
// [LAW:one-source-of-truth] These types are the single contract between the
|
|
8
|
-
// daemon's introspection layer (src/daemon/debug.ts) and any external
|
|
9
|
-
// consumer (the future `cc-candybar vars` / `lint` CLIs, debug tooling).
|
|
10
|
-
// The protocol wraps them; the introspector produces them.
|
|
11
|
-
//
|
|
12
|
-
// Lives in its own module to break a potential cycle: protocol.ts re-exports
|
|
13
|
-
// these for the wire type, debug.ts produces them, and both could otherwise
|
|
14
|
-
// pull each other in.
|
|
15
|
-
|
|
16
|
-
import type { VarType, VarValue } from "../var-system/types";
|
|
17
|
-
import type { DslConfig, SourceKind } from "../config/dsl-types";
|
|
18
|
-
|
|
19
|
-
// [LAW:one-source-of-truth] DEBUG_WHATS is the canonical list; DebugWhat is
|
|
20
|
-
// *derived* from it via indexed access on the `as const` tuple type. Adding
|
|
21
|
-
// a new entry to the array automatically expands DebugWhat — and the switch
|
|
22
|
-
// in buildDebugSnapshot (src/daemon/debug.ts) becomes non-exhaustive,
|
|
23
|
-
// failing the typecheck until a new arm is added. The dispatcher arm and
|
|
24
|
-
// the canonical list are kept in lockstep by the type system, not by
|
|
25
|
-
// human discipline.
|
|
26
|
-
export const DEBUG_WHATS = ["vars", "segments", "config"] as const;
|
|
27
|
-
export type DebugWhat = (typeof DEBUG_WHATS)[number];
|
|
28
|
-
|
|
29
|
-
// Wire-side type guard for an untrusted JSON value. Used by the daemon at
|
|
30
|
-
// the request boundary; symmetrically usable by future CLI shims that
|
|
31
|
-
// validate user input before sending the frame.
|
|
32
|
-
// [LAW:one-source-of-truth] Implemented in terms of DEBUG_WHATS so the
|
|
33
|
-
// guard cannot drift from the canonical list — a new `what` added to
|
|
34
|
-
// DEBUG_WHATS becomes accepted by isDebugWhat with no second-site edit.
|
|
35
|
-
export function isDebugWhat(v: unknown): v is DebugWhat {
|
|
36
|
-
return (
|
|
37
|
-
typeof v === "string" && (DEBUG_WHATS as readonly string[]).includes(v)
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// One row of the `vars` snapshot. Captures everything an operator needs to
|
|
42
|
-
// answer "is this variable computing? did it fail? when did it last update?"
|
|
43
|
-
// without re-reading config or grepping logs.
|
|
44
|
-
export interface VarSnapshot {
|
|
45
|
-
readonly name: string;
|
|
46
|
-
// The DSL source kind that declared this variable (literal/env/input/...).
|
|
47
|
-
// null when a variable exists in the store but was not declared via DSL —
|
|
48
|
-
// present for completeness so a programmatic-only var still shows up in
|
|
49
|
-
// introspection rather than being silently invisible.
|
|
50
|
-
readonly source: SourceKind | null;
|
|
51
|
-
readonly type: VarType;
|
|
52
|
-
// Current store value. For Computed nodes this triggers re-evaluation if
|
|
53
|
-
// MobX has invalidated the cache; for Box nodes it is the last value set.
|
|
54
|
-
readonly value: VarValue;
|
|
55
|
-
readonly lastError: {
|
|
56
|
-
readonly timestampMs: number;
|
|
57
|
-
readonly message: string;
|
|
58
|
-
} | null;
|
|
59
|
-
// Wall-clock ms since the box was last set (Box) or null for Computed
|
|
60
|
-
// nodes whose freshness is governed by MobX invalidation, not a single
|
|
61
|
-
// timestamp. Null is structurally distinct from 0 so consumers can
|
|
62
|
-
// distinguish "no age tracking applies" from "just updated."
|
|
63
|
-
readonly ageMs: number | null;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// One row of the `segments` snapshot.
|
|
67
|
-
export interface SegmentSnapshot {
|
|
68
|
-
readonly name: string;
|
|
69
|
-
// The template source string (verbatim, as authored in the config).
|
|
70
|
-
readonly template: string;
|
|
71
|
-
// Names of variables potentially referenced by the template, found via
|
|
72
|
-
// static analysis: dotted paths inside `{{ ... }}` actions, matched
|
|
73
|
-
// against the store's declared names. Exact runtime deps may be a subset
|
|
74
|
-
// (branches not taken), but every name returned IS in the store.
|
|
75
|
-
readonly referencedVars: readonly string[];
|
|
76
|
-
// The last rendered output for this segment, when the daemon has captured
|
|
77
|
-
// one. null today — the daemon does not yet render through the DSL spine
|
|
78
|
-
// (see bzh.2). Populated when the daemon flips to renderDsl.
|
|
79
|
-
readonly lastRender: string | null;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// [LAW:types-are-the-program] The discriminated union ensures every response
|
|
83
|
-
// carries exactly the data shape its `what` promises. There is no path that
|
|
84
|
-
// produces e.g. `{ what: "vars", segments: [...] }` — the type forbids it.
|
|
85
|
-
export type DebugSnapshot =
|
|
86
|
-
| { readonly what: "vars"; readonly vars: readonly VarSnapshot[] }
|
|
87
|
-
| {
|
|
88
|
-
readonly what: "segments";
|
|
89
|
-
readonly segments: readonly SegmentSnapshot[];
|
|
90
|
-
}
|
|
91
|
-
| { readonly what: "config"; readonly config: DslConfig | null };
|
package/src/daemon/debug.ts
DELETED
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
// [LAW:single-enforcer] One module owns the projection from daemon DSL
|
|
2
|
-
// state (VariableStore + SourceRegistry + DslConfig + CompiledConfig) to
|
|
3
|
-
// the wire-level DebugSnapshot. buildDebugSnapshot dispatches via an
|
|
4
|
-
// exhaustive switch on `what` — TypeScript enforces every DebugWhat arm
|
|
5
|
-
// at compile time (the function's return-type narrowing fails if a case
|
|
6
|
-
// is missing), so adding a new `what` requires one new arm here, one new
|
|
7
|
-
// DEBUG_WHATS entry, and one new DebugSnapshot variant — the type system
|
|
8
|
-
// keeps the three sites in lockstep.
|
|
9
|
-
//
|
|
10
|
-
// [LAW:one-source-of-truth] The introspector reads through the live
|
|
11
|
-
// VariableStore (current values), SourceRegistry (lastErrors), VarNode
|
|
12
|
-
// (lastUpdatedMs), and DslConfig (declared source kinds, segment templates).
|
|
13
|
-
// There is no parallel cache, no shadow snapshot kept in sync — the daemon
|
|
14
|
-
// has one DSL state and this module projects it.
|
|
15
|
-
//
|
|
16
|
-
// [LAW:dataflow-not-control-flow] The state slot (DaemonDslState | null) is
|
|
17
|
-
// the only branch: null state → empty snapshots; populated state → real
|
|
18
|
-
// snapshots. No special-case introspection paths for the "DSL not active
|
|
19
|
-
// yet" case — the same code produces both outcomes from the same data.
|
|
20
|
-
//
|
|
21
|
-
// Today the daemon does not yet hold a DSL state (bzh.2 has not fired); all
|
|
22
|
-
// snapshots are empty in production. When bzh.2 wires the store, the
|
|
23
|
-
// snapshots populate without any change to this module or the protocol.
|
|
24
|
-
|
|
25
|
-
import type { VariableStore } from "../var-system/store";
|
|
26
|
-
import type { SourceRegistry } from "../var-system/sources";
|
|
27
|
-
import type { DslConfig, SourceKind, VariableDecl } from "../config/dsl-types";
|
|
28
|
-
import { walkNodes } from "../config/dsl-types";
|
|
29
|
-
import { extractTemplateRefs } from "../config/dsl-loader";
|
|
30
|
-
import type { CompiledConfig } from "../dsl/render";
|
|
31
|
-
import type {
|
|
32
|
-
DebugSnapshot,
|
|
33
|
-
DebugWhat,
|
|
34
|
-
SegmentSnapshot,
|
|
35
|
-
VarSnapshot,
|
|
36
|
-
} from "./debug-types";
|
|
37
|
-
|
|
38
|
-
// The daemon's DSL state. Bundled because the four fields are co-installed
|
|
39
|
-
// by registerDslConfig — exposing them as independently-optional would let
|
|
40
|
-
// callers represent illegal combinations (e.g. store with no config).
|
|
41
|
-
export interface DaemonDslState {
|
|
42
|
-
readonly store: VariableStore;
|
|
43
|
-
readonly registry: SourceRegistry;
|
|
44
|
-
readonly config: DslConfig;
|
|
45
|
-
readonly compiled: CompiledConfig;
|
|
46
|
-
// [LAW:dataflow-not-control-flow] Per-segment last-render strings live in
|
|
47
|
-
// the same state bundle the renderer mutates. Today (legacy renderer) it
|
|
48
|
-
// is empty; bzh.2 populates it from inside renderDsl.
|
|
49
|
-
readonly lastRenderBySegment: ReadonlyMap<string, string>;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// ─── Dispatcher ──────────────────────────────────────────────────────────────
|
|
53
|
-
|
|
54
|
-
// [LAW:dataflow-not-control-flow] One arm per `what` — the discriminator is
|
|
55
|
-
// data; the branch chooses the projection function, not whether projection
|
|
56
|
-
// runs. Adding a new `what` is one new arm + one new projection + one new
|
|
57
|
-
// DebugSnapshot variant — the type system enforces all three.
|
|
58
|
-
export function buildDebugSnapshot(
|
|
59
|
-
what: DebugWhat,
|
|
60
|
-
state: DaemonDslState | null,
|
|
61
|
-
): DebugSnapshot {
|
|
62
|
-
switch (what) {
|
|
63
|
-
case "vars":
|
|
64
|
-
return { what, vars: introspectVars(state) };
|
|
65
|
-
case "segments":
|
|
66
|
-
return { what, segments: introspectSegments(state) };
|
|
67
|
-
case "config":
|
|
68
|
-
return { what, config: introspectConfig(state) };
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// ─── vars ────────────────────────────────────────────────────────────────────
|
|
73
|
-
|
|
74
|
-
// Project every variable currently registered in the store into one
|
|
75
|
-
// VarSnapshot. Names sorted for deterministic snapshot tests. Source kind
|
|
76
|
-
// is looked up from the DslConfig (top-level variables + per-segment
|
|
77
|
-
// `vars` blocks); a variable not found in either is reported with
|
|
78
|
-
// source=null so it still appears in introspection.
|
|
79
|
-
export function introspectVars(
|
|
80
|
-
state: DaemonDslState | null,
|
|
81
|
-
): readonly VarSnapshot[] {
|
|
82
|
-
if (state === null) return [];
|
|
83
|
-
|
|
84
|
-
const { store, registry, config } = state;
|
|
85
|
-
const sourceByName = buildSourceKindIndex(config);
|
|
86
|
-
const names = store.names().sort();
|
|
87
|
-
|
|
88
|
-
const out: VarSnapshot[] = [];
|
|
89
|
-
for (const name of names) {
|
|
90
|
-
// [LAW:single-enforcer] One requireNode lookup per row. Reading the
|
|
91
|
-
// value through node.read() instead of store.read(name) avoids a
|
|
92
|
-
// second Map.get + requireNode round-trip in the inner loop and
|
|
93
|
-
// makes the per-row data dependency obvious: every field below
|
|
94
|
-
// comes from this one node.
|
|
95
|
-
const node = store.getNode(name);
|
|
96
|
-
const err = registry.getLastError(name);
|
|
97
|
-
// [LAW:no-defensive-null-guards] No try/catch around node.read():
|
|
98
|
-
// every SourceRegistry-declared variable either holds a typed
|
|
99
|
-
// fallback (declareShell/declareFile/declareGit/declareInput catch
|
|
100
|
-
// internally and write a fallback) or is a computed whose deriver
|
|
101
|
-
// also catches (declareTemplate). Cycles are detected eagerly at
|
|
102
|
-
// register time (declareTemplate's force-read). So a read-throw
|
|
103
|
-
// here would be a *programming* error, not a runtime condition
|
|
104
|
-
// the snapshot should mask. Letting it propagate keeps the failure
|
|
105
|
-
// loud at the source instead of laundering it as a synthesized
|
|
106
|
-
// lastError with an unstable Date.now() timestamp.
|
|
107
|
-
//
|
|
108
|
-
// [LAW:single-enforcer] lastError is sourced from SourceRegistry
|
|
109
|
-
// only. There is no second timestamp-producer that could drift
|
|
110
|
-
// from the registry's record.
|
|
111
|
-
out.push({
|
|
112
|
-
name,
|
|
113
|
-
source: sourceByName.get(name) ?? null,
|
|
114
|
-
type: node.type,
|
|
115
|
-
value: node.read(),
|
|
116
|
-
lastError:
|
|
117
|
-
err !== undefined
|
|
118
|
-
? { timestampMs: err.timestamp, message: err.message }
|
|
119
|
-
: null,
|
|
120
|
-
ageMs: ageFromNode(node.lastUpdatedMs()),
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
return out;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function ageFromNode(lastUpdatedMs: number | null): number | null {
|
|
127
|
-
if (lastUpdatedMs === null) return null;
|
|
128
|
-
return Math.max(0, Date.now() - lastUpdatedMs);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Build a name → SourceKind index from the DslConfig. Walks top-level
|
|
132
|
-
// variables and each segment's per-segment vars block; segment-local vars
|
|
133
|
-
// live under the namespaced key `<segName>.<varName>` in the store (same
|
|
134
|
-
// shape registerDslConfig uses to declare them).
|
|
135
|
-
function buildSourceKindIndex(
|
|
136
|
-
config: DslConfig,
|
|
137
|
-
): ReadonlyMap<string, SourceKind> {
|
|
138
|
-
const index = new Map<string, SourceKind>();
|
|
139
|
-
for (const [name, decl] of Object.entries(config.variables)) {
|
|
140
|
-
index.set(name, sourceKindOf(decl));
|
|
141
|
-
}
|
|
142
|
-
for (const [segName, seg] of Object.entries(config.segments)) {
|
|
143
|
-
if (!seg.vars) continue;
|
|
144
|
-
for (const [varName, decl] of Object.entries(seg.vars)) {
|
|
145
|
-
index.set(`${segName}.${varName}`, sourceKindOf(decl));
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return index;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function sourceKindOf(decl: VariableDecl): SourceKind {
|
|
152
|
-
return decl.kind;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// ─── segments ────────────────────────────────────────────────────────────────
|
|
156
|
-
|
|
157
|
-
export function introspectSegments(
|
|
158
|
-
state: DaemonDslState | null,
|
|
159
|
-
): readonly SegmentSnapshot[] {
|
|
160
|
-
if (state === null) return [];
|
|
161
|
-
|
|
162
|
-
const { store, config, lastRenderBySegment } = state;
|
|
163
|
-
const declaredNames = new Set(store.names());
|
|
164
|
-
// [LAW:dataflow-not-control-flow] Walk in layout order so the introspection
|
|
165
|
-
// snapshot mirrors render order — operators reading the snapshot see the
|
|
166
|
-
// same sequence the bar produces, not an alphabetical reshuffling.
|
|
167
|
-
const segNames = orderedSegmentNames(config);
|
|
168
|
-
|
|
169
|
-
const out: SegmentSnapshot[] = [];
|
|
170
|
-
for (const name of segNames) {
|
|
171
|
-
const seg = config.segments[name];
|
|
172
|
-
if (!seg) continue;
|
|
173
|
-
out.push({
|
|
174
|
-
name,
|
|
175
|
-
template: seg.template,
|
|
176
|
-
referencedVars: extractReferencedVars(seg.template, declaredNames),
|
|
177
|
-
lastRender: lastRenderBySegment.get(name) ?? null,
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
return out;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Layout order, with any declared-but-not-laid-out segments appended in
|
|
184
|
-
// declaration order so they still appear in the snapshot (an operator
|
|
185
|
-
// debugging "why isn't this rendering" wants to see the segment, not have
|
|
186
|
-
// it filtered out for being absent from layout).
|
|
187
|
-
function orderedSegmentNames(config: DslConfig): readonly string[] {
|
|
188
|
-
const out: string[] = [];
|
|
189
|
-
const seen = new Set<string>();
|
|
190
|
-
for (const node of walkNodes(config.root)) {
|
|
191
|
-
if (node.kind !== "segment") continue;
|
|
192
|
-
if (config.segments[node.name] && !seen.has(node.name)) {
|
|
193
|
-
out.push(node.name);
|
|
194
|
-
seen.add(node.name);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
for (const name of Object.keys(config.segments)) {
|
|
198
|
-
if (!seen.has(name)) {
|
|
199
|
-
out.push(name);
|
|
200
|
-
seen.add(name);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
return out;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// [LAW:single-enforcer] Static analysis of which variables a segment
|
|
207
|
-
// template references. Raw candidate extraction (find dotted paths inside
|
|
208
|
-
// `{{ ... }}` actions, strip string literals so `{{ printf ".foo" }}`
|
|
209
|
-
// does not falsely match a declared `foo`) is delegated to
|
|
210
|
-
// extractTemplateRefs in src/config/dsl-loader.ts — that helper already
|
|
211
|
-
// owns the template-ref parsing rules and is exercised by the loader's
|
|
212
|
-
// cycle detector. Reusing it means a future improvement to the parser
|
|
213
|
-
// (e.g. supporting `$x.field` variable references) lands here for free.
|
|
214
|
-
//
|
|
215
|
-
// Static analysis (not runtime evaluation) is the right tool here:
|
|
216
|
-
// evaluation would couple introspection to a working store and would
|
|
217
|
-
// vary by current values (if/with branches taken). Static finds every
|
|
218
|
-
// potentially-referenced name regardless of current state — which is
|
|
219
|
-
// what an operator debugging "what does this segment depend on" needs.
|
|
220
|
-
//
|
|
221
|
-
// This function adds the introspection-specific layers on top of the raw
|
|
222
|
-
// extraction:
|
|
223
|
-
// 1. Intersect with the declared-name set (only report names that exist).
|
|
224
|
-
// 2. Ancestor credit: a candidate `.session.id.extra` resolves to the
|
|
225
|
-
// declared `session.id` if `extra` is not declared.
|
|
226
|
-
// 3. Sort the result for deterministic snapshots.
|
|
227
|
-
export function extractReferencedVars(
|
|
228
|
-
template: string,
|
|
229
|
-
declared: ReadonlySet<string>,
|
|
230
|
-
): readonly string[] {
|
|
231
|
-
const found = new Set<string>();
|
|
232
|
-
for (const candidate of extractTemplateRefs(template)) {
|
|
233
|
-
if (declared.has(candidate)) {
|
|
234
|
-
found.add(candidate);
|
|
235
|
-
continue;
|
|
236
|
-
}
|
|
237
|
-
// Drop trailing segments until we hit a declared name. Handles
|
|
238
|
-
// `.session.id.something_extra` where only `session.id` is declared —
|
|
239
|
-
// still credit it as a reference to `session.id`.
|
|
240
|
-
const parts = candidate.split(".");
|
|
241
|
-
while (parts.length > 1) {
|
|
242
|
-
parts.pop();
|
|
243
|
-
const prefix = parts.join(".");
|
|
244
|
-
if (declared.has(prefix)) {
|
|
245
|
-
found.add(prefix);
|
|
246
|
-
break;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
return Array.from(found).sort();
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// ─── config ──────────────────────────────────────────────────────────────────
|
|
254
|
-
|
|
255
|
-
// [LAW:no-defensive-null-guards] No defensive copy. DslConfig is `readonly`
|
|
256
|
-
// throughout and the wire-encoder JSON-serializes it; downstream callers
|
|
257
|
-
// see a fresh value. Returning the live reference is the cheapest correct
|
|
258
|
-
// answer.
|
|
259
|
-
export function introspectConfig(
|
|
260
|
-
state: DaemonDslState | null,
|
|
261
|
-
): DslConfig | null {
|
|
262
|
-
if (state === null) return null;
|
|
263
|
-
return state.config;
|
|
264
|
-
}
|