@sous-io/sous 0.2.37 → 1.0.2

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.
Files changed (244) hide show
  1. package/README.md +14 -256
  2. package/dist/bin.js +11 -0
  3. package/dist/sous.js +1094 -0
  4. package/docs/markdown/README.md +5 -4
  5. package/docs/markdown/_sidebar.md +21 -1
  6. package/docs/markdown/commands.md +294 -431
  7. package/docs/markdown/config-discovery.md +25 -14
  8. package/docs/markdown/config-inspection.md +53 -31
  9. package/docs/markdown/config-layers.md +168 -66
  10. package/docs/markdown/config-variables.md +44 -11
  11. package/docs/markdown/configuration.md +104 -17
  12. package/docs/markdown/design-principles.md +4 -1
  13. package/docs/markdown/repositories-authoring.md +30 -0
  14. package/docs/markdown/repositories-consuming.md +72 -14
  15. package/docs/markdown/repositories-file-formats.md +31 -13
  16. package/docs/markdown/repositories-providers.md +48 -57
  17. package/docs/markdown/repositories-quickstart.md +3 -2
  18. package/docs/markdown/repositories-troubleshooting.md +27 -1
  19. package/docs/markdown/repositories-variables.md +6 -1
  20. package/docs/markdown/repositories.md +18 -8
  21. package/package.json +48 -107
  22. package/recipes/core/sous-skills/skills/about-liquid-templates/SKILL.tpl.md +4 -6
  23. package/recipes/core/sous-skills/skills/about-liquid-templates/references/liquid-filters.tpl.md +2 -0
  24. package/recipes/core/sous-skills/skills/about-sous/SKILL.tpl.md +10 -9
  25. package/recipes/core/sous-skills/skills/about-sous-configuration/SKILL.tpl.md +35 -14
  26. package/recipes/core/sous-skills/skills/about-sous-recipes/SKILL.tpl.md +51 -20
  27. package/recipes/core/sous-skills/sous.recipe.yaml +1 -1
  28. package/sous.config.schema.json +351 -132
  29. package/bin/run.js +0 -51
  30. package/src/base-command.ts +0 -457
  31. package/src/commands/build.ts +0 -181
  32. package/src/commands/clear.ts +0 -79
  33. package/src/commands/compile.ts +0 -155
  34. package/src/commands/config/get.ts +0 -110
  35. package/src/commands/config/show.ts +0 -32
  36. package/src/commands/config/validate.ts +0 -53
  37. package/src/commands/help.ts +0 -46
  38. package/src/commands/init.ts +0 -289
  39. package/src/commands/launch.ts +0 -139
  40. package/src/commands/lock/rebuild.ts +0 -239
  41. package/src/commands/lock/show.ts +0 -115
  42. package/src/commands/namespace/list.ts +0 -138
  43. package/src/commands/namespace/show.ts +0 -130
  44. package/src/commands/prune.ts +0 -43
  45. package/src/commands/recipe/list.ts +0 -112
  46. package/src/commands/recipe/show.ts +0 -439
  47. package/src/commands/repo/add.ts +0 -145
  48. package/src/commands/repo/contribute.ts +0 -604
  49. package/src/commands/repo/gc.ts +0 -173
  50. package/src/commands/repo/init.ts +0 -136
  51. package/src/commands/repo/link.ts +0 -899
  52. package/src/commands/repo/list.ts +0 -236
  53. package/src/commands/repo/release.ts +0 -685
  54. package/src/commands/repo/remove.ts +0 -194
  55. package/src/commands/repo/search.ts +0 -243
  56. package/src/commands/repo/submit.ts +0 -341
  57. package/src/commands/repo/unlink.ts +0 -461
  58. package/src/commands/subscription/add.ts +0 -286
  59. package/src/commands/subscription/list.ts +0 -208
  60. package/src/commands/subscription/remove.ts +0 -182
  61. package/src/commands/subscription/update.ts +0 -216
  62. package/src/commands/update.ts +0 -178
  63. package/src/commands/vars/ask.ts +0 -417
  64. package/src/commands/vars/index.ts +0 -79
  65. package/src/commands/vars/list.ts +0 -67
  66. package/src/commands/vars/show.ts +0 -77
  67. package/src/config-command.ts +0 -30
  68. package/src/lib/build-preparation.ts +0 -128
  69. package/src/lib/build-service.ts +0 -580
  70. package/src/lib/config-discovery.ts +0 -378
  71. package/src/lib/config-inspect.ts +0 -145
  72. package/src/lib/config-kernel.mjs +0 -379
  73. package/src/lib/config-schema.ts +0 -401
  74. package/src/lib/env-file.ts +0 -328
  75. package/src/lib/env-local.ts +0 -212
  76. package/src/lib/errors.ts +0 -32
  77. package/src/lib/glob-pattern.ts +0 -24
  78. package/src/lib/include-resolver.ts +0 -523
  79. package/src/lib/interactive.ts +0 -165
  80. package/src/lib/markdown-compiler.ts +0 -947
  81. package/src/lib/package-info.ts +0 -25
  82. package/src/lib/pid-service.ts +0 -99
  83. package/src/lib/project-install.d.mts +0 -44
  84. package/src/lib/project-install.mjs +0 -234
  85. package/src/lib/project-scaffold/index.ts +0 -332
  86. package/src/lib/project-scaffold/templates.ts +0 -240
  87. package/src/lib/repos/catalog-display.ts +0 -215
  88. package/src/lib/repos/catalog-inputs.ts +0 -288
  89. package/src/lib/repos/catalog.ts +0 -842
  90. package/src/lib/repos/contribute.ts +0 -452
  91. package/src/lib/repos/core-recipe.ts +0 -105
  92. package/src/lib/repos/declarations.ts +0 -187
  93. package/src/lib/repos/defaults.ts +0 -175
  94. package/src/lib/repos/formats/common.ts +0 -440
  95. package/src/lib/repos/formats/index-file.ts +0 -253
  96. package/src/lib/repos/formats/links-map.ts +0 -98
  97. package/src/lib/repos/formats/lockfile.ts +0 -169
  98. package/src/lib/repos/formats/patterns.ts +0 -57
  99. package/src/lib/repos/formats/recipe-manifest.ts +0 -205
  100. package/src/lib/repos/formats/repo-manifest.ts +0 -97
  101. package/src/lib/repos/formats/store-entry.ts +0 -84
  102. package/src/lib/repos/formats/variable-definition.ts +0 -268
  103. package/src/lib/repos/freshness.ts +0 -264
  104. package/src/lib/repos/git-clone.ts +0 -850
  105. package/src/lib/repos/identity.ts +0 -89
  106. package/src/lib/repos/index.ts +0 -57
  107. package/src/lib/repos/links.ts +0 -356
  108. package/src/lib/repos/load-manifest.ts +0 -237
  109. package/src/lib/repos/lock-service.ts +0 -454
  110. package/src/lib/repos/locked-namespace-resolver.ts +0 -232
  111. package/src/lib/repos/locked-recipe-lookup.ts +0 -123
  112. package/src/lib/repos/locked-recipes.ts +0 -254
  113. package/src/lib/repos/managed-layer.ts +0 -422
  114. package/src/lib/repos/memories-hash-name.ts +0 -33
  115. package/src/lib/repos/namespace-resolver.ts +0 -527
  116. package/src/lib/repos/providers/base.ts +0 -268
  117. package/src/lib/repos/providers/git.ts +0 -245
  118. package/src/lib/repos/providers/github.ts +0 -589
  119. package/src/lib/repos/providers/gitlab.ts +0 -332
  120. package/src/lib/repos/providers/http.ts +0 -128
  121. package/src/lib/repos/providers/index-cache.ts +0 -421
  122. package/src/lib/repos/providers/index.ts +0 -106
  123. package/src/lib/repos/providers/local.ts +0 -410
  124. package/src/lib/repos/providers/provider.ts +0 -615
  125. package/src/lib/repos/recipe-config-layers.ts +0 -316
  126. package/src/lib/repos/recipe-key-matcher.ts +0 -62
  127. package/src/lib/repos/recipe-memories.ts +0 -178
  128. package/src/lib/repos/recipe-targets.ts +0 -236
  129. package/src/lib/repos/ref-search.ts +0 -46
  130. package/src/lib/repos/release/bump.ts +0 -161
  131. package/src/lib/repos/release/changelog.ts +0 -461
  132. package/src/lib/repos/release/git-state.ts +0 -460
  133. package/src/lib/repos/release/index-builder.ts +0 -1108
  134. package/src/lib/repos/release/index.ts +0 -19
  135. package/src/lib/repos/release/plan.ts +0 -521
  136. package/src/lib/repos/release/settle.ts +0 -154
  137. package/src/lib/repos/release/submissions.ts +0 -214
  138. package/src/lib/repos/release/submit-checkout.ts +0 -290
  139. package/src/lib/repos/release/submit-questions.ts +0 -153
  140. package/src/lib/repos/release/submit-service.ts +0 -898
  141. package/src/lib/repos/release/tags.ts +0 -239
  142. package/src/lib/repos/release/validate.ts +0 -463
  143. package/src/lib/repos/resolver.ts +0 -1028
  144. package/src/lib/repos/scaffold/index.ts +0 -245
  145. package/src/lib/repos/scaffold/templates.ts +0 -517
  146. package/src/lib/repos/seed.ts +0 -473
  147. package/src/lib/repos/store/contract.ts +0 -64
  148. package/src/lib/repos/store/hash.ts +0 -191
  149. package/src/lib/repos/store/recipe-store.ts +0 -599
  150. package/src/lib/repos/store/settings.ts +0 -58
  151. package/src/lib/repos/subscription-service.ts +0 -3859
  152. package/src/lib/repos/trust.ts +0 -447
  153. package/src/lib/repos/update-plan.ts +0 -235
  154. package/src/lib/self-update/index.ts +0 -14
  155. package/src/lib/self-update/installs.ts +0 -413
  156. package/src/lib/self-update/managers.ts +0 -126
  157. package/src/lib/self-update/plan.ts +0 -315
  158. package/src/lib/self-update/registry.ts +0 -99
  159. package/src/lib/self-update/run.ts +0 -506
  160. package/src/lib/self-update/versions.ts +0 -243
  161. package/src/lib/settings.ts +0 -1107
  162. package/src/lib/sous-home.ts +0 -104
  163. package/src/lib/state.ts +0 -167
  164. package/src/lib/vars/answers.ts +0 -192
  165. package/src/lib/vars/ask.ts +0 -1152
  166. package/src/lib/vars/definition-source.ts +0 -261
  167. package/src/lib/vars/display.ts +0 -231
  168. package/src/lib/vars/index.ts +0 -20
  169. package/src/lib/vars/ladder.ts +0 -282
  170. package/src/lib/vars/mappings.ts +0 -260
  171. package/src/lib/vars/names.ts +0 -94
  172. package/src/lib/vars/preanswers.ts +0 -395
  173. package/src/lib/vars/question-plan.ts +0 -241
  174. package/src/lib/vars/report.ts +0 -228
  175. package/src/lib/vars/safe-regex.ts +0 -235
  176. package/src/lib/vars/secrets.ts +0 -131
  177. package/src/lib/vars/validate.ts +0 -312
  178. package/src/lib/version-report.ts +0 -90
  179. package/src/lib/watch-loop.ts +0 -148
  180. package/src/lib/watch-service.ts +0 -116
  181. package/src/services/ref-resolver/container.ts +0 -107
  182. package/src/services/ref-resolver/dependency.ts +0 -60
  183. package/src/services/ref-resolver/format.ts +0 -197
  184. package/src/services/ref-resolver/glob.ts +0 -115
  185. package/src/services/ref-resolver/hash-names.ts +0 -155
  186. package/src/services/ref-resolver/index.ts +0 -55
  187. package/src/services/ref-resolver/injectable.ts +0 -36
  188. package/src/services/ref-resolver/location.ts +0 -62
  189. package/src/services/ref-resolver/lookups/cached-index-lookup.ts +0 -53
  190. package/src/services/ref-resolver/lookups/catalog-lookup.ts +0 -23
  191. package/src/services/ref-resolver/lookups/catalog-matcher.ts +0 -261
  192. package/src/services/ref-resolver/lookups/chained-lookup.ts +0 -27
  193. package/src/services/ref-resolver/lookups/env-var-lookup.ts +0 -65
  194. package/src/services/ref-resolver/lookups/fetched-index-lookup.ts +0 -150
  195. package/src/services/ref-resolver/lookups/recorded-dependency-lookup.ts +0 -62
  196. package/src/services/ref-resolver/lookups/ref-lookup.ts +0 -54
  197. package/src/services/ref-resolver/lookups/variable-lookup.ts +0 -123
  198. package/src/services/ref-resolver/named-refs.ts +0 -144
  199. package/src/services/ref-resolver/parser/env-var-splitter.ts +0 -27
  200. package/src/services/ref-resolver/parser/location-splitter.ts +0 -239
  201. package/src/services/ref-resolver/parser/name-path-splitter.ts +0 -200
  202. package/src/services/ref-resolver/parser/partial-ref.ts +0 -84
  203. package/src/services/ref-resolver/parser/query-splitter.ts +0 -61
  204. package/src/services/ref-resolver/parser/range-splitter.ts +0 -52
  205. package/src/services/ref-resolver/parser/ref-parser.ts +0 -115
  206. package/src/services/ref-resolver/parser/repo-qualifier-splitter.ts +0 -51
  207. package/src/services/ref-resolver/parts.ts +0 -115
  208. package/src/services/ref-resolver/pruners/command-line-pruner.ts +0 -47
  209. package/src/services/ref-resolver/pruners/include-pruner.ts +0 -66
  210. package/src/services/ref-resolver/pruners/manifest-pruner.ts +0 -130
  211. package/src/services/ref-resolver/pruners/ref-pruner.ts +0 -135
  212. package/src/services/ref-resolver/pruners/rule-helpers.ts +0 -44
  213. package/src/services/ref-resolver/pruners/stored-key-pruner.ts +0 -136
  214. package/src/services/ref-resolver/ref-helpers.ts +0 -62
  215. package/src/services/ref-resolver/ref-picker-service.ts +0 -204
  216. package/src/services/ref-resolver/ref-report.ts +0 -84
  217. package/src/services/ref-resolver/ref-resolve-arguments.ts +0 -47
  218. package/src/services/ref-resolver/ref-resolve-result.ts +0 -36
  219. package/src/services/ref-resolver/ref-resolver-service.ts +0 -323
  220. package/src/services/ref-resolver/source.ts +0 -48
  221. package/src/services/ref-resolver/tokens.ts +0 -25
  222. package/src/services/ref-resolver/types.ts +0 -155
  223. package/src/templating/filters/bullet-list.ts +0 -9
  224. package/src/templating/filters/index.ts +0 -8
  225. package/src/templating/init-liquid-engine.ts +0 -139
  226. package/src/templating/lib/glob-files.ts +0 -74
  227. package/src/templating/lib/import-export.ts +0 -32
  228. package/src/templating/lib/secret-scope.ts +0 -41
  229. package/src/templating/lib/tag-args.ts +0 -19
  230. package/src/templating/tags/exportScalarVarsJs.ts +0 -46
  231. package/src/templating/tags/getFiles.ts +0 -89
  232. package/src/templating/tags/index.ts +0 -14
  233. package/src/templating/tags/listFiles.ts +0 -54
  234. package/src/templating/tags/showVars.ts +0 -28
  235. package/src/utils/choice-prompt.ts +0 -143
  236. package/src/utils/command-errors.ts +0 -186
  237. package/src/utils/command-help.ts +0 -45
  238. package/src/utils/confirm-prompt.ts +0 -110
  239. package/src/utils/flags.ts +0 -177
  240. package/src/utils/formatting.ts +0 -849
  241. package/src/utils/prompts.ts +0 -53
  242. package/src/utils/sous-directory.ts +0 -245
  243. package/src/utils/table.ts +0 -603
  244. package/src/utils/value-prompt.ts +0 -119
@@ -1,261 +0,0 @@
1
- /**
2
- * Where variable definitions come from.
3
- *
4
- * A definition is a published specification, not a value: it says what a
5
- * variable is called, what shape an answer takes, and what question to ask.
6
- * Definitions ship inside recipe manifests, so the real source is the set of
7
- * recipes a project's lockfile pins. Every consumer goes through ONE seam,
8
- * `loadProjectDefinitions`; nothing else in the variables layer knows where
9
- * definitions came from, which is what lets `sous vars ask --file` read a
10
- * standalone file through the same machinery.
11
- */
12
-
13
- import path from "node:path";
14
- import { z } from "zod";
15
- import { parseFormat } from "../repos/formats/common.js";
16
- import {
17
- variableDefinitionSchema,
18
- type VariableDefinition,
19
- } from "../repos/formats/recipe-manifest.js";
20
- import { loadManifestFile } from "../repos/load-manifest.js";
21
- import {
22
- listLockedRecipes,
23
- readProjectLockfile,
24
- readRecipeManifestIn,
25
- } from "../repos/locked-recipes.js";
26
- import type { Settings } from "../settings.js";
27
-
28
- /** Which recipe published a definition, spelled out for display and for naming. */
29
- export interface DefiningRecipe {
30
- /** The short name of the repository the recipe came from. */
31
- repo: string;
32
- /** The recipe's namespace. */
33
- namespace: string;
34
- /** The recipe's name. */
35
- name: string;
36
- /** The exact version of the recipe in play. */
37
- version: string;
38
- /**
39
- * Where the repository lives: a URL for a hosted repository, or an absolute
40
- * path for one read through the `local` provider. Used to show a recipe as a
41
- * link rather than as a bare name.
42
- */
43
- url?: string;
44
- /** The recipe's folder inside the repository, when it is known. */
45
- path?: string;
46
- /** The recipe's directory on this machine, when it is present. */
47
- dir?: string;
48
- }
49
-
50
- /** One variable definition together with the recipe that published it. */
51
- export interface DefinedVariable {
52
- /** The published specification. */
53
- definition: VariableDefinition;
54
- /** The recipe the definition came from. */
55
- recipe: DefiningRecipe;
56
- /**
57
- * How this variable came to be in play: the subscribed recipe first, then
58
- * each recipe it depends on, ending with the recipe that declares the
59
- * definition. A direct subscription has one entry; an indirect one shows the
60
- * whole chain. Absent when nothing recorded it, in which case the defining
61
- * recipe stands for itself.
62
- */
63
- requiredBy?: DefiningRecipe[];
64
- }
65
-
66
- /** Anything that can produce the variable definitions in play for a project. */
67
- export interface VariableDefinitionSource {
68
- /** Loads every definition this source knows about. */
69
- load(): Promise<DefinedVariable[]>;
70
- }
71
-
72
- /**
73
- * A source backed by a fixed array. Used by tests and by any caller that has
74
- * already assembled its definitions.
75
- */
76
- export class StaticDefinitionSource implements VariableDefinitionSource {
77
- /**
78
- * @param defined - The definitions this source returns.
79
- */
80
- constructor(private readonly defined: DefinedVariable[]) {}
81
-
82
- /** Returns the definitions handed to the constructor. */
83
- async load(): Promise<DefinedVariable[]> {
84
- return this.defined;
85
- }
86
- }
87
-
88
- /**
89
- * The project's own definitions: every variable declared by every recipe the
90
- * lockfile pins, whether the project subscribed to it directly or a dependency
91
- * pulled it in.
92
- *
93
- * Both kinds count, deliberately. A recipe may `depends` on another purely to
94
- * reuse its published variable definitions, and a build that cannot answer
95
- * those variables is just as broken as one that cannot answer a subscription's.
96
- *
97
- * A recipe the store does not hold yet contributes nothing rather than failing:
98
- * a fresh clone lists what it can until `sous build` restores the rest.
99
- */
100
- export class ProjectDefinitionSource implements VariableDefinitionSource {
101
- /**
102
- * @param settings - The merged project config, which holds the subscriptions.
103
- * @param sousDir - The project's `.sous/` directory, where the lockfile lives.
104
- * @param env - The environment to read; decides where the store is.
105
- */
106
- constructor(
107
- private readonly settings: Settings,
108
- private readonly sousDir: string,
109
- private readonly env: NodeJS.ProcessEnv = process.env
110
- ) {}
111
-
112
- /** Every variable published by every recipe this project's lockfile pins. */
113
- async load(): Promise<DefinedVariable[]> {
114
- return this.loadSync();
115
- }
116
-
117
- /**
118
- * The same list, read synchronously. The lockfile and the manifests are
119
- * ordinary files, and the settings scope a build renders with is assembled
120
- * synchronously, so the build path needs this form.
121
- */
122
- loadSync(): DefinedVariable[] {
123
- void this.settings;
124
-
125
- const defined: DefinedVariable[] = [];
126
- const lock = readProjectLockfile(this.sousDir);
127
-
128
- for (const located of listLockedRecipes({ sousDir: this.sousDir, env: this.env })) {
129
- if (!located.present) continue;
130
-
131
- const manifest = readRecipeManifestIn(located.dir);
132
- if (manifest === undefined) continue;
133
-
134
- const url = lock.repos[located.repo]?.url;
135
- const recipe: DefiningRecipe = {
136
- repo: located.repo,
137
- namespace: located.namespace,
138
- name: located.name,
139
- version: located.version,
140
- dir: located.dir,
141
- ...(url === undefined ? {} : { url }),
142
- };
143
- for (const definition of manifest.variables ?? []) {
144
- defined.push({ definition, recipe });
145
- }
146
- }
147
-
148
- return defined;
149
- }
150
- }
151
-
152
- /**
153
- * Builds the definition source for a project. This is the single injection
154
- * point every `sous vars` command uses.
155
- *
156
- * @param settings - The merged project config.
157
- * @param sousDir - The project's `.sous/` directory.
158
- * @param env - The environment to read; decides where the store is.
159
- */
160
- export function loadProjectDefinitions(
161
- settings: Settings,
162
- sousDir: string,
163
- env: NodeJS.ProcessEnv = process.env
164
- ): VariableDefinitionSource {
165
- return new ProjectDefinitionSource(settings, sousDir, env);
166
- }
167
-
168
- // --- Standalone definition files ------------------------------------------------------------------
169
-
170
- /**
171
- * A standalone definitions document: the same `variables:` array a recipe
172
- * manifest carries, in a file of its own. `sous vars ask --file` reads one of
173
- * these, which is how a project asks questions that are not published by any
174
- * recipe yet.
175
- *
176
- * It is the same schema, so the same rules apply: every definition must carry a
177
- * description and an example, and both are shown when the question is asked.
178
- */
179
- export const definitionsFileSchema = z.object({
180
- /** The definitions to ask, in the recipe manifest's own shape. */
181
- variables: z.array(variableDefinitionSchema).min(1, "must list at least one variable"),
182
- });
183
-
184
- /** A validated standalone definitions document. */
185
- export type DefinitionsFile = z.infer<typeof definitionsFileSchema>;
186
-
187
- /** The repository name recorded for definitions that came from a local file. */
188
- export const LOCAL_FILE_REPO = "local";
189
-
190
- /** The namespace recorded for definitions that came from a local file. */
191
- export const LOCAL_FILE_NAMESPACE = "local";
192
-
193
- /**
194
- * Turns a file name into a kebab-case pseudo-recipe name, so definitions read
195
- * from a file still have a recipe to be attributed to and still generate the
196
- * usual scoped environment variable names.
197
- *
198
- * @param filePath - The definitions file's path.
199
- */
200
- export function pseudoRecipeName(filePath: string): string {
201
- const base = path.basename(filePath).replace(/\.[^.]+$/, "");
202
- const slug = base
203
- .toLowerCase()
204
- .replace(/[^a-z0-9]+/g, "-")
205
- .replace(/^-+|-+$/g, "");
206
- return slug.length === 0 || !/^[a-z]/.test(slug) ? `file-${slug}` : slug;
207
- }
208
-
209
- /**
210
- * Loads a standalone definitions file (YAML or JSON, with the same permissive
211
- * JSON dialect manifests use) and attributes every definition in it to a
212
- * pseudo-recipe named after the file.
213
- *
214
- * @param filePath - Absolute path to the definitions file.
215
- */
216
- export function loadDefinitionsFile(filePath: string): DefinedVariable[] {
217
- const raw = loadManifestFile(filePath);
218
- const parsed = parseFormat(
219
- definitionsFileSchema,
220
- raw,
221
- filePath,
222
- "variable definitions file"
223
- );
224
- const recipe: DefiningRecipe = {
225
- repo: LOCAL_FILE_REPO,
226
- namespace: LOCAL_FILE_NAMESPACE,
227
- name: pseudoRecipeName(filePath),
228
- version: "0.0.0",
229
- dir: filePath,
230
- };
231
- return parsed.variables.map((definition) => ({ definition, recipe }));
232
- }
233
-
234
- /** A source backed by a standalone definitions file. */
235
- export class FileDefinitionSource implements VariableDefinitionSource {
236
- /**
237
- * @param filePath - Absolute path to the definitions file.
238
- */
239
- constructor(private readonly filePath: string) {}
240
-
241
- /** Reads and validates the file, throwing a ConfigError when it does not fit. */
242
- async load(): Promise<DefinedVariable[]> {
243
- return loadDefinitionsFile(this.filePath);
244
- }
245
- }
246
-
247
- /**
248
- * The display key for a defined variable: `namespace/recipe.variableName`.
249
- * Sorting and lookups use it, so one variable never appears twice under two
250
- * spellings.
251
- *
252
- * @param defined - The definition and the recipe that published it.
253
- */
254
- export function definedVariableKey(defined: DefinedVariable): string {
255
- return `${defined.recipe.namespace}/${defined.recipe.name}.${defined.definition.name}`;
256
- }
257
-
258
- /** The recipe's key (`namespace/recipe`), as shown in listings. */
259
- export function definingRecipeKey(recipe: DefiningRecipe): string {
260
- return `${recipe.namespace}/${recipe.name}`;
261
- }
@@ -1,231 +0,0 @@
1
- /**
2
- * Shared display helpers for the `sous vars` commands: masking secrets, and the
3
- * labeled facts block both the listing and the ask report use. The tables those
4
- * commands print are laid out by the shared renderer in `src/utils/table.ts`.
5
- */
6
-
7
- import path from "node:path";
8
- import type { VariableDefinition } from "../repos/formats/recipe-manifest.js";
9
- import {
10
- BULLET,
11
- formatVariable,
12
- VARIABLE_INDENT,
13
- wrapColumns,
14
- type VariableEntry,
15
- } from "../../utils/formatting.js";
16
- import {
17
- definingRecipeKey,
18
- type DefinedVariable,
19
- type DefiningRecipe,
20
- } from "./definition-source.js";
21
- import { constraintBullets } from "./validate.js";
22
- import { HIDDEN_VALUE } from "./secrets.js";
23
-
24
- /** What the value column shows for a variable nothing has answered. */
25
- export const UNANSWERED_VALUE = "(unanswered)";
26
-
27
- /**
28
- * The display form of a value: a secret never prints, so that a terminal
29
- * recording, a screen share or a scrollback buffer cannot leak one.
30
- *
31
- * @param value - The stored value, or undefined when there is no answer.
32
- * @param secret - Whether the definition declared the variable a secret.
33
- */
34
- export function displayValue(value: string | undefined, secret: boolean): string {
35
- if (value === undefined) return UNANSWERED_VALUE;
36
- if (secret) return HIDDEN_VALUE;
37
- return value;
38
- }
39
-
40
- /**
41
- * The two documentation rows every command shows for a variable: the
42
- * publisher's description, and the sample answer that makes the one-line
43
- * question concrete. A published definition must carry both, so every caller
44
- * can show them without checking first.
45
- *
46
- * @param definition - The variable definition to document.
47
- * @returns Label-to-text rows, ready for `showVariables` or an aligned label block.
48
- */
49
- export function documentationRows(
50
- definition: VariableDefinition
51
- ): Record<string, string> {
52
- return {
53
- About: definition.description,
54
- "For example": String(definition.example),
55
- };
56
- }
57
-
58
- // --- The labeled facts about one variable --------------------------------------------------------
59
-
60
- /**
61
- * One line of a fact: the text, and any secondary detail shown after it in
62
- * muted grey (a repository location, for instance) rather than in parentheses.
63
- */
64
- export interface FactLine {
65
- /** The text shown beside the label. */
66
- text: string;
67
- /** The secondary detail that follows it, muted. */
68
- detail?: string;
69
- }
70
-
71
- /** One labeled fact: the label, and the lines shown beside it. */
72
- export interface LabeledFact {
73
- /**
74
- * The label, in the plain-word form every key and value display uses
75
- * (`default`, `required-by`); the `@` the recipe manifests write is not part
76
- * of it.
77
- */
78
- label: string;
79
- /** The text shown beside the label, one entry per line. */
80
- lines: Array<string | FactLine>;
81
- }
82
-
83
- /** True when a repository location is a URL rather than a path on this machine. */
84
- function isHostedUrl(location: string): boolean {
85
- return /^[a-z][a-z0-9+.-]*:\/\//i.test(location) || location.startsWith("git@");
86
- }
87
-
88
- /**
89
- * Where a recipe's source lives: the repository URL with the recipe's folder
90
- * appended for a hosted repository, the filesystem path for one read from this
91
- * machine, and nothing at all when neither was recorded.
92
- *
93
- * @param recipe - The recipe to locate.
94
- */
95
- export function recipeLocation(recipe: DefiningRecipe): string | undefined {
96
- if (recipe.url !== undefined && isHostedUrl(recipe.url)) {
97
- const base = recipe.url.replace(/\/+$/, "");
98
- return recipe.path === undefined ? base : `${base}/${recipe.path}`;
99
- }
100
-
101
- return recipe.url !== undefined
102
- ? recipe.path === undefined
103
- ? recipe.url
104
- : path.join(recipe.url, recipe.path)
105
- : recipe.dir;
106
- }
107
-
108
- /**
109
- * A recipe written as a fact line: its key, with its location following it in
110
- * muted grey. The location is a trailing detail rather than a parenthetical, so
111
- * the recipe key stays the thing the eye lands on.
112
- *
113
- * @param recipe - The recipe to link to.
114
- */
115
- export function recipeLink(recipe: DefiningRecipe): FactLine {
116
- const key = definingRecipeKey(recipe);
117
- const location = recipeLocation(recipe);
118
- return location === undefined ? { text: key } : { text: key, detail: location };
119
- }
120
-
121
- /** Everything the facts renderer needs that the definition itself does not carry. */
122
- export interface VariableFactsInput {
123
- /** The variable and the recipe that published it. */
124
- defined: DefinedVariable;
125
- /** Absolute path of the env file the answer is stored in. */
126
- storagePath: string;
127
- /** The environment variable name the answer is stored under. */
128
- storedAs: string;
129
- }
130
-
131
- /**
132
- * The labeled facts about one variable, in the order both the advanced view and
133
- * `sous vars show` print them. One function builds them so the two never drift
134
- * apart in wording or in order.
135
- *
136
- * @param input - The variable, where its answer is stored, and under what name.
137
- * @returns The facts, ready for `renderFacts`.
138
- */
139
- export function variableFacts(input: VariableFactsInput): LabeledFact[] {
140
- const { defined, storagePath, storedAs } = input;
141
- const { definition } = defined;
142
- const facts: LabeledFact[] = [];
143
-
144
- if (definition.default !== undefined) {
145
- facts.push({ label: "default", lines: [String(definition.default)] });
146
- }
147
- facts.push({ label: "example", lines: [String(definition.example)] });
148
-
149
- const chain = defined.requiredBy ?? [defined.recipe];
150
- const requiredBy: Array<string | FactLine> = [recipeLink(chain[0] ?? defined.recipe)];
151
- if (chain.length > 1) {
152
- requiredBy.push(
153
- `pulled in through ${chain.map((recipe) => definingRecipeKey(recipe)).join(" then ")}`
154
- );
155
- }
156
- facts.push({ label: "required-by", lines: requiredBy });
157
- facts.push({ label: "defined-by", lines: [recipeLink(defined.recipe)] });
158
- facts.push({ label: "storage-path", lines: [storagePath] });
159
- facts.push({ label: "stored-as", lines: [storedAs] });
160
- facts.push({
161
- label: "constraints",
162
- lines: constraintBullets(definition).map((bullet) => `${BULLET} ${bullet}`),
163
- });
164
-
165
- return facts;
166
- }
167
-
168
- /**
169
- * The facts the basic view of a question shows, in the order it shows them. It
170
- * is a subset of the same list the advanced view prints, selected by label, so
171
- * the two views can never word a fact differently or lay it out differently.
172
- */
173
- export const BASIC_FACT_LABELS = [
174
- "default",
175
- "example",
176
- "stored-as",
177
- "storage-path",
178
- ];
179
-
180
- /**
181
- * Picks the named facts out of a fact list, in the order the labels were given
182
- * and skipping any the variable does not have (a variable with no default has
183
- * no `default` fact).
184
- *
185
- * @param facts - Every fact about the variable.
186
- * @param labels - The labels to keep, in the order they should be shown.
187
- */
188
- export function selectFacts(facts: LabeledFact[], labels: string[]): LabeledFact[] {
189
- const byLabel = new Map(facts.map((fact) => [fact.label, fact]));
190
- return labels
191
- .map((label) => byLabel.get(label))
192
- .filter((fact): fact is LabeledFact => fact !== undefined);
193
- }
194
-
195
- /**
196
- * How far a rendered facts block is indented under the text above it. It is the
197
- * same depth as every other key and value block, so a facts block never reads
198
- * as a different kind of list.
199
- */
200
- export const FACTS_INDENT = VARIABLE_INDENT;
201
-
202
- /**
203
- * Lays the labeled facts out through the one key and value renderer, so a fact
204
- * about a variable looks exactly like every other key and value sous prints:
205
- * labels aligned, colons lined up, values in the value color, and a location
206
- * trailing in muted grey.
207
- *
208
- * @param facts - The facts to render.
209
- * @param width - The column to wrap at, indentation included.
210
- * @returns The rendered lines, colored for a terminal, indented by `FACTS_INDENT`.
211
- */
212
- export function renderFacts(facts: LabeledFact[], width = wrapColumns()): string[] {
213
- const labelWidth = Math.max(...facts.map((fact) => fact.label.length));
214
- const lines: string[] = [];
215
-
216
- for (const fact of facts) {
217
- fact.lines.forEach((raw, index) => {
218
- const line: FactLine = typeof raw === "string" ? { text: raw } : raw;
219
- const entry: VariableEntry = {
220
- // A fact needing more than one line labels only the first of them; the
221
- // rest continue underneath it.
222
- label: index === 0 ? fact.label : "",
223
- value: line.text,
224
- ...(line.detail === undefined ? {} : { detail: line.detail }),
225
- };
226
- lines.push(...formatVariable(entry, { indent: FACTS_INDENT, labelWidth, width }));
227
- });
228
- }
229
-
230
- return lines;
231
- }
@@ -1,20 +0,0 @@
1
- /**
2
- * The variables layer: where variable definitions come from, how a stored
3
- * answer is found, how an answer is validated, and how a question is asked.
4
- *
5
- * Import from here rather than from the individual modules, so the commands and
6
- * later phases have one place to look for what this layer offers.
7
- */
8
-
9
- export * from "./answers.js";
10
- export * from "./definition-source.js";
11
- export * from "./display.js";
12
- export * from "./ladder.js";
13
- export * from "./mappings.js";
14
- export * from "./names.js";
15
- export * from "./validate.js";
16
- export * from "./ask.js";
17
- export * from "./report.js";
18
- export * from "./preanswers.js";
19
- export * from "./question-plan.js";
20
- export * from "./secrets.js";