@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,269 +0,0 @@
|
|
|
1
|
-
// [LAW:types-are-the-program] The globals schema: a fixed set of string fields
|
|
2
|
-
// plus a validated palette name, declared as DATA and interpreted by the record
|
|
3
|
-
// engine. This file changes when a global default field is added or removed —
|
|
4
|
-
// add a key to GLOBALS_SCHEMA and Globals; the engine does the rest.
|
|
5
|
-
|
|
6
|
-
import { type Globals } from "../dsl-types.js";
|
|
7
|
-
import {
|
|
8
|
-
CHARSETS,
|
|
9
|
-
COLOR_COMPATIBILITIES,
|
|
10
|
-
DEFAULT_PADDING,
|
|
11
|
-
PADDING_RANGE,
|
|
12
|
-
STRIP_STYLES,
|
|
13
|
-
type ColorCompatibility,
|
|
14
|
-
} from "../../themes/policy.js";
|
|
15
|
-
import {
|
|
16
|
-
optionalBooleanSpec,
|
|
17
|
-
optionalEnum,
|
|
18
|
-
optionalEnumSpec,
|
|
19
|
-
optionalIntSpec,
|
|
20
|
-
optionalStringSpec,
|
|
21
|
-
paletteSpec,
|
|
22
|
-
record,
|
|
23
|
-
recordJson,
|
|
24
|
-
type FieldSpec,
|
|
25
|
-
type FieldSpecMap,
|
|
26
|
-
type JsonNode,
|
|
27
|
-
type RecordSchema,
|
|
28
|
-
type ValidateCtx,
|
|
29
|
-
} from "./validate-core.js";
|
|
30
|
-
import { findKeyLine } from "./diagnostics.js";
|
|
31
|
-
|
|
32
|
-
// [LAW:types-are-the-program] Closed enum like `charset`, plus one
|
|
33
|
-
// migration-pointing rejection: "auto" was the LEGACY default, so migrating
|
|
34
|
-
// configs will carry it — but the daemon runs detached, so rich-js env
|
|
35
|
-
// detection would downsample against the daemon's terminal, not the client's.
|
|
36
|
-
// Rather than ship that silent lie [LAW:no-silent-failure], "auto" is outside
|
|
37
|
-
// the ColorCompatibility domain and gets an error that says why (same species
|
|
38
|
-
// as the removed-layout migration errors in layout.ts). The json emit and the
|
|
39
|
-
// membership check derive from the same COLOR_COMPATIBILITIES literal.
|
|
40
|
-
const colorCompatibilitySpec: FieldSpec<ColorCompatibility> = {
|
|
41
|
-
required: false,
|
|
42
|
-
json: { enum: [...COLOR_COMPATIBILITIES] },
|
|
43
|
-
parse: (ctx, path, field, raw) => {
|
|
44
|
-
if (raw[field] === "auto") {
|
|
45
|
-
ctx.issues.push({
|
|
46
|
-
path: `${path}.${field}`,
|
|
47
|
-
message:
|
|
48
|
-
`${path}.${field}: "auto" is not supported — the render daemon runs detached, ` +
|
|
49
|
-
`so terminal detection would read the daemon's environment, not your terminal's. ` +
|
|
50
|
-
`Pick an explicit depth: ${COLOR_COMPATIBILITIES.join(", ")}`,
|
|
51
|
-
line: findKeyLine(ctx.source, [...path.split("."), field]),
|
|
52
|
-
});
|
|
53
|
-
return undefined;
|
|
54
|
-
}
|
|
55
|
-
return optionalEnum(ctx, path, raw, field, COLOR_COMPATIBILITIES);
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
// [LAW:one-source-of-truth] THE globals field table, declared once. Both the
|
|
60
|
-
// top-level `globals:` schema and the preset-scoped one below are built from
|
|
61
|
-
// this map, so a field added here is automatically settable from a preset —
|
|
62
|
-
// there is no second list to remember to grow.
|
|
63
|
-
const GLOBALS_FIELDS: FieldSpecMap<Globals> = {
|
|
64
|
-
default_bg: optionalStringSpec(),
|
|
65
|
-
default_fg: optionalStringSpec(),
|
|
66
|
-
default_empty_value: optionalStringSpec(),
|
|
67
|
-
default_separator: optionalStringSpec(),
|
|
68
|
-
default_truncate_marker: optionalStringSpec(),
|
|
69
|
-
palette: paletteSpec(),
|
|
70
|
-
// [LAW:types-are-the-program] The config-default LOOK name. Unlike the
|
|
71
|
-
// registry-static palette set, the look domain is per-config (the merged
|
|
72
|
-
// `looks` block), so membership is a cross-ref check on the MERGED config —
|
|
73
|
-
// a user's globals.look may name a default-provided look. Shape-only here,
|
|
74
|
-
// exactly the shape/meaning split paletteSpec's schema facet keeps.
|
|
75
|
-
look: optionalStringSpec(),
|
|
76
|
-
// [LAW:types-are-the-program] The config-default PRESET name — same
|
|
77
|
-
// per-config-domain shape as `look` (membership is a post-merge cross-ref
|
|
78
|
-
// check, since a user's globals.preset may name a default-provided preset).
|
|
79
|
-
preset: optionalStringSpec(),
|
|
80
|
-
// [LAW:types-are-the-program] The strip style is a CLOSED enum (the powerline
|
|
81
|
-
// shapes the joiner can render), unlike the open-ended palette NAME — so it
|
|
82
|
-
// validates by membership and emits a JSON-Schema `enum`.
|
|
83
|
-
style: optionalEnumSpec(STRIP_STYLES),
|
|
84
|
-
autoWrap: optionalBooleanSpec(),
|
|
85
|
-
// Intra-cell spaces per side. Bounded above so a config value can never
|
|
86
|
-
// drive an unbounded `" ".repeat` allocation in the daemon
|
|
87
|
-
// [LAW:no-silent-failure] — an absurd value is a loud load error, not a
|
|
88
|
-
// silently-huge render.
|
|
89
|
-
// [LAW:one-source-of-truth] The bound comes from PADDING_RANGE, the same
|
|
90
|
-
// literal the bundled default's stepper actions bound clicks by and the
|
|
91
|
-
// session-half parse admits values from — so the file, the click, and the
|
|
92
|
-
// session pick cannot end up honouring three different ranges.
|
|
93
|
-
padding: optionalIntSpec(PADDING_RANGE),
|
|
94
|
-
// [LAW:types-are-the-program] Closed enum like `style`: the joiner glyph
|
|
95
|
-
// vocabularies pickJoiner can render — validates by membership, emits a
|
|
96
|
-
// JSON-Schema `enum` from the same CHARSETS literal.
|
|
97
|
-
charset: optionalEnumSpec(CHARSETS),
|
|
98
|
-
// Closed enum with a bespoke "auto" rejection — see colorCompatibilitySpec.
|
|
99
|
-
colorCompatibility: colorCompatibilitySpec,
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const GLOBALS_SCHEMA: RecordSchema<Globals> = {
|
|
103
|
-
noun: "globals key",
|
|
104
|
-
fields: GLOBALS_FIELDS,
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
// [LAW:one-source-of-truth] A globals FRAGMENT — a delta layered over the
|
|
108
|
-
// config's own globals at render time — may not carry `preset`: which preset is
|
|
109
|
-
// active has exactly one authority (session pick over globals.preset over the
|
|
110
|
-
// floor), and a fragment re-selecting a preset would be a second one. For a
|
|
111
|
-
// preset's own fragment that second authority is also cyclic; for edit mode's
|
|
112
|
-
// it would let a look-only fragment restage the whole layout, which edit chrome
|
|
113
|
-
// already owns. Same species of bespoke, migration-pointing rejection as
|
|
114
|
-
// colorCompatibility's "auto" above, and for the same reason: an author who
|
|
115
|
-
// writes it deserves to be told WHY, not handed a bare unknown-key message.
|
|
116
|
-
//
|
|
117
|
-
// [LAW:one-type-per-behavior] Both fragments reject the field identically and
|
|
118
|
-
// differ only in the SUBJECT a diagnostic names, so this is one spec taking
|
|
119
|
-
// that noun as data — never two specs that could drift in what they reject.
|
|
120
|
-
function nestedPresetSpec(subject: string): FieldSpec<string> {
|
|
121
|
-
return {
|
|
122
|
-
required: false,
|
|
123
|
-
// Always-fail: JSON Schema's `not: {}` matches nothing, so an editor flags
|
|
124
|
-
// the key at the same moment the validator does.
|
|
125
|
-
json: {
|
|
126
|
-
not: {},
|
|
127
|
-
description: `not allowed here — ${subject} cannot select a preset`,
|
|
128
|
-
},
|
|
129
|
-
parse: (ctx, path, field, raw) => {
|
|
130
|
-
if (raw[field] !== undefined) {
|
|
131
|
-
ctx.issues.push({
|
|
132
|
-
path: `${path}.${field}`,
|
|
133
|
-
message:
|
|
134
|
-
`${path}.${field}: ${subject} cannot select a preset. Which preset is active is ` +
|
|
135
|
-
`resolved once, as session pick over globals.preset over "default"; a fragment ` +
|
|
136
|
-
`naming another would be a second authority over that. ` +
|
|
137
|
-
`Set the default arrangement in the top-level globals.preset instead.`,
|
|
138
|
-
line: findKeyLine(ctx.source, [...path.split("."), field]),
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
return undefined;
|
|
142
|
-
},
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// [LAW:one-source-of-truth] Each fragment-scoped globals schema is the SAME
|
|
147
|
-
// field table with exactly one field swapped for its rejection — not a
|
|
148
|
-
// hand-listed subset that a future globals field could be forgotten from.
|
|
149
|
-
const PRESET_GLOBALS_SCHEMA: RecordSchema<Globals> = {
|
|
150
|
-
noun: "preset globals key",
|
|
151
|
-
fields: { ...GLOBALS_FIELDS, preset: nestedPresetSpec("a preset") },
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
// [LAW:one-type-per-behavior] Edit mode's staged globals are the same shape one
|
|
155
|
-
// rung later in the precedence chain, so they reuse the same table rather than
|
|
156
|
-
// declaring which fields edit mode "supports" — a field added to Globals is
|
|
157
|
-
// edit-settable the same day, with no edit here.
|
|
158
|
-
const EDIT_GLOBALS_SCHEMA: RecordSchema<Globals> = {
|
|
159
|
-
noun: "editGlobals key",
|
|
160
|
-
fields: {
|
|
161
|
-
...GLOBALS_FIELDS,
|
|
162
|
-
preset: nestedPresetSpec("the editGlobals fragment"),
|
|
163
|
-
},
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
// An absent globals block is the empty default (no issue); a non-object is a
|
|
167
|
-
// reported error that recovers to the empty default, since parseDslConfig throws
|
|
168
|
-
// once any issue exists so the recovery value never renders [LAW:no-silent-failure].
|
|
169
|
-
// `path` is explicit because the same schema validates the top-level `globals:`
|
|
170
|
-
// block and a preset's nested one, and a diagnostic must name where it actually is.
|
|
171
|
-
export function validateGlobals(
|
|
172
|
-
ctx: ValidateCtx,
|
|
173
|
-
path: string,
|
|
174
|
-
raw: unknown,
|
|
175
|
-
): Globals {
|
|
176
|
-
if (raw === undefined) return {};
|
|
177
|
-
return record(ctx, GLOBALS_SCHEMA, path, raw) ?? {};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// The preset-scoped twin: same interpreter, the schema that rejects `preset`.
|
|
181
|
-
export function validatePresetGlobals(
|
|
182
|
-
ctx: ValidateCtx,
|
|
183
|
-
path: string,
|
|
184
|
-
raw: unknown,
|
|
185
|
-
): Globals {
|
|
186
|
-
return record(ctx, PRESET_GLOBALS_SCHEMA, path, raw) ?? {};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// [LAW:one-source-of-truth] The schema emitter derives from the SAME declaration
|
|
190
|
-
// the validator interprets — `globals` emit is `recordJson(GLOBALS_SCHEMA)`,
|
|
191
|
-
// symmetric to `validateGlobals` calling `record(GLOBALS_SCHEMA)`.
|
|
192
|
-
export function globalsJson(): JsonNode {
|
|
193
|
-
return recordJson(GLOBALS_SCHEMA);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
export function presetGlobalsJson(): JsonNode {
|
|
197
|
-
return recordJson(PRESET_GLOBALS_SCHEMA);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Edit mode's twin of the two above: same interpreter, same field table, the
|
|
201
|
-
// schema whose `preset` rejection names the editGlobals fragment.
|
|
202
|
-
export function validateEditGlobals(
|
|
203
|
-
ctx: ValidateCtx,
|
|
204
|
-
path: string,
|
|
205
|
-
raw: unknown,
|
|
206
|
-
): Globals {
|
|
207
|
-
return record(ctx, EDIT_GLOBALS_SCHEMA, path, raw) ?? {};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export function editGlobalsJson(): JsonNode {
|
|
211
|
-
return recordJson(EDIT_GLOBALS_SCHEMA);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// [LAW:one-source-of-truth] THE membership check for "is this a real Globals
|
|
215
|
-
// field" — derived from GLOBALS_SCHEMA.fields, the same declaration
|
|
216
|
-
// validateGlobals/globalsJson interpret, so a `persist`/`reset` action's
|
|
217
|
-
// target key is checked against exactly the field set a hand-authored
|
|
218
|
-
// `globals: {...}` block would be. Used by cross-ref.ts (candybar-config-
|
|
219
|
-
// engine-71o.2) to catch a typo'd persist target at config-load time instead
|
|
220
|
-
// of a confusing click-time "invariant broken" error.
|
|
221
|
-
const GLOBALS_FIELD_NAMES: ReadonlySet<string> = new Set(
|
|
222
|
-
Object.keys(GLOBALS_SCHEMA.fields),
|
|
223
|
-
);
|
|
224
|
-
export function isGlobalsField(key: string): key is keyof Globals {
|
|
225
|
-
return GLOBALS_FIELD_NAMES.has(key);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
export function listGlobalsFieldNames(): readonly string[] {
|
|
229
|
-
return [...GLOBALS_FIELD_NAMES];
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// [LAW:types-are-the-program] Every NUMERIC globals field with the value that
|
|
233
|
-
// renders when a config declares none, typed TOTAL over those fields — so
|
|
234
|
-
// adding a numeric globals field without a floor is a COMPILE error, not a
|
|
235
|
-
// silent seed-from-`min` (the bug this table exists to prevent, recurring for
|
|
236
|
-
// the new field). It lives here rather than beside DEFAULT_PADDING in
|
|
237
|
-
// themes/policy.ts because it must name `Globals`, and policy.ts is the leaf
|
|
238
|
-
// dsl-types imports FROM — a table that needs both belongs on this side of
|
|
239
|
-
// that edge [LAW:one-way-deps].
|
|
240
|
-
// The direction matters: `NonNullable<Globals[K]> extends number` asks "is this
|
|
241
|
-
// field's type a number", which is what the totality claim needs. The mirror
|
|
242
|
-
// (`number extends …`) happens to agree for a field declared as plain `number`
|
|
243
|
-
// — the two are symmetric there — but silently drops a field typed as a
|
|
244
|
-
// literal union (`zoom?: 1 | 2 | 3`), which is a perfectly ordinary way to
|
|
245
|
-
// declare a bounded numeric setting and exactly the case this guard exists for.
|
|
246
|
-
type NumericGlobalsField = {
|
|
247
|
-
[K in keyof Globals]-?: NonNullable<Globals[K]> extends number ? K : never;
|
|
248
|
-
}[keyof Globals];
|
|
249
|
-
|
|
250
|
-
const NUMERIC_GLOBALS_FLOORS: Readonly<Record<NumericGlobalsField, number>> = {
|
|
251
|
-
padding: DEFAULT_PADDING,
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
// [LAW:single-enforcer] THE seed for a bounded stepper over a globals field:
|
|
255
|
-
// what the bar renders with no write of any kind — the config's own value, or
|
|
256
|
-
// the field's floor when it declares none. Both write gates read it (the
|
|
257
|
-
// SessionState one through stateKeySeeds, the config-overrides one through
|
|
258
|
-
// configKeySeeds), so a session stepper and its durable twin can never start
|
|
259
|
-
// from different numbers, and neither can silently start from `min`.
|
|
260
|
-
export function numericGlobalsSeeds(
|
|
261
|
-
globals: Globals,
|
|
262
|
-
): ReadonlyMap<string, number> {
|
|
263
|
-
const seeds = new Map<string, number>();
|
|
264
|
-
for (const [field, floor] of Object.entries(NUMERIC_GLOBALS_FLOORS)) {
|
|
265
|
-
const declared = globals[field as keyof Globals];
|
|
266
|
-
seeds.set(field, typeof declared === "number" ? declared : floor);
|
|
267
|
-
}
|
|
268
|
-
return seeds;
|
|
269
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
// [LAW:types-are-the-program] The helpers schema is the simplest possible: a
|
|
2
|
-
// record of name → template-body STRING. Each value is a Go-template source the
|
|
3
|
-
// renderer compiles into a `{{ define }}` block; whether the body PARSES (and
|
|
4
|
-
// whether a `{{ template "name" }}` reference resolves) is a render-time concern
|
|
5
|
-
// (registerDslConfig parses each helper and throws a per-helper diagnostic).
|
|
6
|
-
// This file changes only if the helper authoring shape changes.
|
|
7
|
-
|
|
8
|
-
import { findKeyLine } from "./diagnostics.js";
|
|
9
|
-
import {
|
|
10
|
-
describeType,
|
|
11
|
-
describeValue,
|
|
12
|
-
isPlainObject,
|
|
13
|
-
type ValidateCtx,
|
|
14
|
-
} from "./validate-core.js";
|
|
15
|
-
|
|
16
|
-
// [LAW:single-enforcer] Structural validation of the `helpers` block: an object
|
|
17
|
-
// whose every value is a string template body. Null-prototype record so a helper
|
|
18
|
-
// named "__proto__"/"constructor" is an ordinary own property, matching actions.
|
|
19
|
-
export function validateHelpers(
|
|
20
|
-
ctx: ValidateCtx,
|
|
21
|
-
raw: unknown,
|
|
22
|
-
): Record<string, string> {
|
|
23
|
-
if (raw === undefined) return {};
|
|
24
|
-
if (!isPlainObject(raw)) {
|
|
25
|
-
ctx.issues.push({
|
|
26
|
-
path: "helpers",
|
|
27
|
-
message: `helpers must be an object, got ${describeType(raw)}`,
|
|
28
|
-
line: findKeyLine(ctx.source, ["helpers"]),
|
|
29
|
-
});
|
|
30
|
-
return {};
|
|
31
|
-
}
|
|
32
|
-
const out: Record<string, string> = Object.create(null) as Record<
|
|
33
|
-
string,
|
|
34
|
-
string
|
|
35
|
-
>;
|
|
36
|
-
for (const [name, body] of Object.entries(raw)) {
|
|
37
|
-
if (typeof body !== "string") {
|
|
38
|
-
ctx.issues.push({
|
|
39
|
-
path: `helpers.${name}`,
|
|
40
|
-
message: `helpers.${name} must be a string template body, got ${describeValue(body)}`,
|
|
41
|
-
line: findKeyLine(ctx.source, ["helpers", name]),
|
|
42
|
-
});
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
out[name] = body;
|
|
46
|
-
}
|
|
47
|
-
return out;
|
|
48
|
-
}
|