@sous-io/sous 0.2.37 → 1.0.1

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 +1093 -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,107 +0,0 @@
1
- /**
2
- * The Inversify container of the ref services.
3
- *
4
- * Every part is bound once and shared: the parser, every splitter, every
5
- * pruner, the resolver and the picker. Splitters, pruners and providers are
6
- * bound many times under one token each (`REF_TOKENS`), and the parser and the
7
- * resolver receive them as lists, so a plugin adds a splitter, a pruner or a
8
- * provider by binding one more under the same token on a container it builds
9
- * with `createRefContainer()`:
10
- *
11
- * const container = createRefContainer();
12
- * container.bind(REF_TOKENS.Splitter).to(MyNewFormSplitter);
13
- * const resolver = container.get<RefResolverService>(REF_TOKENS.Resolver);
14
- *
15
- * The `#` names are bound the same way: one more `HashName` under
16
- * `REF_TOKENS.HashName` registers a view or a built-in, and a name bound twice
17
- * is an error when the registry is first read.
18
- *
19
- * Callers that have no container of their own use `sharedRefResolver`,
20
- * `sharedHashNames` and `sharedRefPicker`, which come from one container built the first time they
21
- * are read.
22
- */
23
-
24
- import { Container } from "inversify";
25
- import { builtInProviders } from "../../lib/repos/providers/index.js";
26
- import { EnvVarSplitter } from "./parser/env-var-splitter.js";
27
- import { LocationSplitter } from "./parser/location-splitter.js";
28
- import { NamePathSplitter } from "./parser/name-path-splitter.js";
29
- import { QuerySplitter } from "./parser/query-splitter.js";
30
- import { RangeSplitter } from "./parser/range-splitter.js";
31
- import { RefParser } from "./parser/ref-parser.js";
32
- import { RepoQualifierSplitter } from "./parser/repo-qualifier-splitter.js";
33
- import { CommandLinePruner } from "./pruners/command-line-pruner.js";
34
- import { IncludePruner } from "./pruners/include-pruner.js";
35
- import { ManifestPruner } from "./pruners/manifest-pruner.js";
36
- import { ConfigPruner, LockfilePruner } from "./pruners/stored-key-pruner.js";
37
- import { MemoriesHashName } from "../../lib/repos/memories-hash-name.js";
38
- import { HashNameRegistry, ProjectHashName } from "./hash-names.js";
39
- import { RefPickerService } from "./ref-picker-service.js";
40
- import { RefResolverService } from "./ref-resolver-service.js";
41
- import { REF_TOKENS } from "./tokens.js";
42
-
43
- /**
44
- * Builds a container holding every ref service. Each call returns a fresh
45
- * container, so a plugin or a test may bind more splitters or pruners without
46
- * touching anyone else's.
47
- */
48
- export function createRefContainer(): Container {
49
- const container = new Container({ defaultScope: "Singleton" });
50
-
51
- for (const provider of builtInProviders()) {
52
- container.bind(REF_TOKENS.Provider).toConstantValue(provider);
53
- }
54
-
55
- for (const splitter of [
56
- QuerySplitter,
57
- RangeSplitter,
58
- LocationSplitter,
59
- RepoQualifierSplitter,
60
- NamePathSplitter,
61
- EnvVarSplitter,
62
- ]) {
63
- container.bind(REF_TOKENS.Splitter).to(splitter);
64
- }
65
-
66
- for (const pruner of [
67
- CommandLinePruner,
68
- ConfigPruner,
69
- ManifestPruner,
70
- LockfilePruner,
71
- IncludePruner,
72
- ]) {
73
- container.bind(REF_TOKENS.Pruner).to(pruner);
74
- }
75
-
76
- container.bind(REF_TOKENS.HashName).to(ProjectHashName);
77
- container.bind(REF_TOKENS.HashName).to(MemoriesHashName);
78
- container.bind(REF_TOKENS.HashNames).to(HashNameRegistry);
79
- container.bind(REF_TOKENS.Parser).to(RefParser);
80
- container.bind(REF_TOKENS.Resolver).to(RefResolverService);
81
- container.bind(REF_TOKENS.Picker).to(RefPickerService);
82
- return container;
83
- }
84
-
85
- /** The container the shared instances come from, built on first use. */
86
- let sharedContainer: Container | undefined;
87
-
88
- /** The one container callers with no container of their own share. */
89
- export function getSharedRefContainer(): Container {
90
- sharedContainer ??= createRefContainer();
91
- return sharedContainer;
92
- }
93
-
94
- /** The shared resolver, for callers that have no container. */
95
- export function sharedRefResolver(): RefResolverService {
96
- return getSharedRefContainer().get<RefResolverService>(REF_TOKENS.Resolver);
97
- }
98
-
99
- /** The shared registry of `#` names, for callers that have no container. */
100
- export function sharedHashNames(): HashNameRegistry {
101
- return getSharedRefContainer().get<HashNameRegistry>(REF_TOKENS.HashNames);
102
- }
103
-
104
- /** The shared picker, for callers that have no container. */
105
- export function sharedRefPicker(): RefPickerService {
106
- return getSharedRefContainer().get<RefPickerService>(REF_TOKENS.Picker);
107
- }
@@ -1,60 +0,0 @@
1
- /**
2
- * Settling a manifest dependency that reads more than one way, from what is
3
- * already at hand.
4
- *
5
- * A manifest entry usually says exactly what it means. A GitLab URL with nested
6
- * groups does not say where the project path ends, and a browser URL names a
7
- * folder, so the parser returns every reading. A release settles such an entry
8
- * and records the repository it settled on beside each key it resolved
9
- * (`RecordedDependencies`); a consumer reads that record and never probes.
10
- */
11
-
12
- import { sharedRefResolver } from "./container.js";
13
- import {
14
- isConfirmedByRecord,
15
- type RecordedDependencies,
16
- } from "./lookups/recorded-dependency-lookup.js";
17
- import { RefSource } from "./source.js";
18
- import type { SousRef } from "./types.js";
19
-
20
- /** What `settleDependency` may consult, beyond the dependency itself. */
21
- export type SettleDependencyOptions = {
22
- /** What the declaring version's index entry records about its dependencies. */
23
- recorded?: RecordedDependencies;
24
- /**
25
- * Whether a reading is already known to be right from what is on this
26
- * machine: its repository is trusted and its cached index publishes what it
27
- * names. Used only when the index recorded nothing, and it never fetches.
28
- */
29
- known?: (reading: SousRef) => boolean;
30
- };
31
-
32
- /**
33
- * The one reading a manifest dependency settles on, or undefined when it reads
34
- * more than one way and nothing at hand says which: the declaring recipe's
35
- * index recorded no answer, and what this machine already knows does not pick
36
- * exactly one.
37
- *
38
- * settleDependency("workflow/alpha");
39
- * // -> the recipe workflow/alpha
40
- *
41
- * @param written - The dependency as the manifest wrote it.
42
- * @param options - The index's record, and what is known locally.
43
- * @throws A ConfigError when a manifest may not write the entry that way.
44
- */
45
- export function settleDependency(
46
- written: string,
47
- options: SettleDependencyOptions = {}
48
- ): SousRef | undefined {
49
- const { refs } = sharedRefResolver().parse(written, RefSource.Manifest);
50
- if (refs.length === 1) return refs[0];
51
-
52
- const confirmed = refs.filter((ref) => isConfirmedByRecord(ref, options.recorded));
53
- if (confirmed.length === 1) return confirmed[0];
54
-
55
- if (options.known !== undefined) {
56
- const known = refs.filter(options.known);
57
- if (known.length === 1) return known[0];
58
- }
59
- return undefined;
60
- }
@@ -1,197 +0,0 @@
1
- /**
2
- * Printing a ref: its key (what everything is stored and compared under) and
3
- * its canonical written form (how sous prints it).
4
- */
5
-
6
- import {
7
- builtInProviders,
8
- providerById,
9
- type RepoProvider,
10
- } from "../../lib/repos/providers/index.js";
11
- import { repoOf } from "./parts.js";
12
- import { KIND_LABELS, type RepoRef, type SousRef } from "./types.js";
13
-
14
- /**
15
- * The part of a key that names the repository: its short name, or its
16
- * identity when it was named by location.
17
- */
18
- function repoPart(repo: RepoRef | undefined): string | undefined {
19
- if (repo === undefined) return undefined;
20
- return repo.name ?? repo.location?.identity;
21
- }
22
-
23
- /**
24
- * The path part of a key, without any repository.
25
- *
26
- * @param ref - Any ref.
27
- */
28
- function pathPart(ref: SousRef): string {
29
- switch (ref.kind) {
30
- case "repo":
31
- return ref.browsed === undefined ? "" : `tree/${ref.browsed}`;
32
- case "namespace":
33
- return ref.name;
34
- case "recipe":
35
- return ref.namespace === undefined ? ref.name : `${ref.namespace.name}/${ref.name}`;
36
- case "recipeFile":
37
- return `${pathPart(ref.recipe)}/${ref.path}`;
38
- case "variable": {
39
- const recipe = ref.recipe === undefined ? "" : `${pathPart(ref.recipe)}.`;
40
- return `${recipe}${ref.name}`;
41
- }
42
- case "envVar":
43
- return ref.name;
44
- }
45
- }
46
-
47
- /**
48
- * The canonical key of a ref, with its version range and query values left
49
- * off, so the same thing is never keyed twice under two spellings. A
50
- * repository qualifier is part of the key, because the same recipe name in two
51
- * repositories is two things.
52
- *
53
- * refKey(namespace("workflow")); // -> "workflow"
54
- * refKey(recipe("task-files", "workflow", "sous-recipes")); // -> "sous-recipes:workflow/task-files"
55
- * refKey(variable("apiUrl", recipe)); // -> "workflow/task-files.apiUrl"
56
- *
57
- * @param ref - Any ref.
58
- */
59
- export function refKey(ref: SousRef): string {
60
- const repo = ref.kind === "envVar" ? undefined : repoPart(repoOf(ref));
61
- const rest = pathPart(ref);
62
- if (ref.kind === "repo") return repo === undefined ? rest : rest === "" ? repo : `${repo}:${rest}`;
63
- return repo === undefined ? rest : `${repo}:${rest}`;
64
- }
65
-
66
- /**
67
- * The key of a ref together with its kind, which is what tells a namespace
68
- * `workflow` from a repository `workflow` apart.
69
- *
70
- * @param ref - Any ref.
71
- */
72
- export function refIdentity(ref: SousRef): string {
73
- return `${ref.kind} ${refKey(ref)}`;
74
- }
75
-
76
- /**
77
- * Writes the repository qualifier in front of what is named inside it: `name:`
78
- * for a repository this project knows by short name (whatever else it knows of
79
- * it), and the provider's canonical locator for one known only by location.
80
- */
81
- function qualified(
82
- repo: RepoRef | undefined,
83
- rest: string,
84
- providers: RepoProvider[]
85
- ): string {
86
- if (repo === undefined) return rest;
87
- if (repo.name !== undefined) return rest === "" ? repo.name : `${repo.name}:${rest}`;
88
- if (repo.location !== undefined) {
89
- const { host, repoPath } = repo.location;
90
- const provider = providerById(repo.location.provider, providers);
91
- if (provider !== undefined && provider.id !== "local") {
92
- return provider.formatLocator(host, repoPath, rest);
93
- }
94
- return rest === "" ? repo.location.url : `${repo.location.url}/${rest}`;
95
- }
96
- return rest;
97
- }
98
-
99
- /**
100
- * The canonical written form of a ref, which is how sous prints it: the short
101
- * form for a short ref and each provider's own locator for a located one, then
102
- * the version range, then the query values.
103
- *
104
- * formatRef(parse("https://github.com/o/r/w/a@^1")[0]); // -> "github://o/r/w/a@^1"
105
- *
106
- * @param ref - Any ref.
107
- * @param providers - The providers that format locators. Defaults to the built-ins.
108
- */
109
- export function formatRef(ref: SousRef, providers: RepoProvider[] = builtInProviders()): string {
110
- const body = qualified(ref.kind === "envVar" ? undefined : repoOf(ref), pathPart(ref), providers);
111
- const written =
112
- ref.kind === "recipe" || ref.kind === "recipeFile" || ref.kind === "repo"
113
- ? ref.range
114
- : undefined;
115
- const range = written === undefined ? "" : `@${written}`;
116
- const pairs = Object.entries(ref.vars ?? {}).map(
117
- ([name, value]) => `${encodeURIComponent(name)}=${encodeURIComponent(value)}`
118
- );
119
- return `${body}${range}${pairs.length === 0 ? "" : `?${pairs.join("&")}`}`;
120
- }
121
-
122
- /**
123
- * What kind of thing a ref names, in a sentence.
124
- *
125
- * @param ref - Any ref.
126
- */
127
- export function refKindLabel(ref: SousRef): string {
128
- return KIND_LABELS[ref.kind];
129
- }
130
-
131
- /**
132
- * How a ref is written to a person, so they can type it back: its key when a
133
- * repository's short name qualifies it, and its canonical locator when only a
134
- * location does (a ref that has not been matched to a repository this project
135
- * knows by name).
136
- *
137
- * refSpelling(recipe "task-files" in "sous-recipes") // -> "sous-recipes:workflow/task-files"
138
- * refSpelling(a recipe at a located repository) // -> its canonical locator
139
- *
140
- * @param ref - Any ref.
141
- */
142
- export function refSpelling(ref: SousRef): string {
143
- const repo = ref.kind === "envVar" ? undefined : repoOf(ref);
144
- if (repo !== undefined && repo.name === undefined && repo.location !== undefined) {
145
- return formatRef(ref);
146
- }
147
- return refKey(ref);
148
- }
149
-
150
- /** The repository a person is told a ref lives in: its short name, or where it lives. */
151
- function repoLabel(repo: RepoRef | undefined): string {
152
- return repo?.name ?? repo?.location?.url ?? "unknown";
153
- }
154
-
155
- /**
156
- * Describes one ref in the words a person choosing between several needs: the
157
- * fully qualified name, and what it actually means.
158
- *
159
- * @param ref - A known ref, as a lookup returns it.
160
- */
161
- export function describeRef(ref: SousRef): string {
162
- const summary = ref.description === undefined ? "" : `: ${ref.description}`;
163
- const key = refSpelling(ref);
164
-
165
- switch (ref.kind) {
166
- case "repo": {
167
- const where = ref.location?.url;
168
- return `${key} (the repository '${ref.name ?? key}'${where === undefined ? "" : `, at ${where}`})`;
169
- }
170
- case "namespace":
171
- return (
172
- `${key} (the whole namespace '${ref.name}' in the ` +
173
- `repository '${repoLabel(ref.repo)}')`
174
- );
175
- case "recipe":
176
- return (
177
- `${key} (the recipe '${ref.name}' in the namespace ` +
178
- `'${ref.namespace?.name ?? "unknown"}' of the repository ` +
179
- `'${repoLabel(repoOf(ref))}'${summary})`
180
- );
181
- case "recipeFile":
182
- return `${key} (the file '${ref.path}' of the recipe '${pathPart(ref.recipe)}'${summary})`;
183
- case "variable":
184
- return (
185
- `${key} (the variable '${ref.name}' of the recipe ` +
186
- `'${ref.recipe === undefined ? "unknown" : pathPart(ref.recipe)}'${summary})`
187
- );
188
- case "envVar": {
189
- const first = ref.variables?.[0];
190
- const answers =
191
- first === undefined
192
- ? ""
193
- : `answering '${first.name}' of the recipe '${first.recipe === undefined ? "unknown" : pathPart(first.recipe)}'`;
194
- return `${ref.name} (the environment variable${answers === "" ? "" : ` ${answers}`}${summary})`;
195
- }
196
- }
197
- }
@@ -1,115 +0,0 @@
1
- /**
2
- * Names, glob patterns and how a written name is compared with a known one.
3
- *
4
- * Every name or path part of a ref may be a glob pattern (`*`, `**`, `?`,
5
- * `[..]`, `{a,b}`), matched with minimatch. Plain names are compared as
6
- * strings. Either way the exact spelling is tried first and ignoring case is
7
- * the fallback, so the caller can tell which one matched.
8
- */
9
-
10
- import { minimatch } from "minimatch";
11
-
12
- /** A name as the parser accepts it: kebab-case, in any case. */
13
- export const NAME_ANY_CASE = /^[a-z][a-z0-9-]*$/i;
14
-
15
- /** A name as it is stored: lowercase kebab-case. */
16
- export const NAME_STORED = /^[a-z][a-z0-9-]*$/;
17
-
18
- /** A variable name, as a recipe's author writes it: camelCase. */
19
- export const VARIABLE_NAME = /^[a-z][a-zA-Z0-9]*$/;
20
-
21
- /** An environment variable name: whatever a shell accepts. */
22
- export const ENV_VAR_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
23
-
24
- /** The characters a glob pattern is made of. */
25
- const GLOB_CHARS = /[*?[\]{}]/;
26
-
27
- /** The characters a glob pattern standing for a name may use. */
28
- const GLOB_NAME = /^[A-Za-z0-9_\-.*?[\]{},!^]+$/;
29
-
30
- /** True when a string holds glob syntax. */
31
- export function hasGlob(text: string): boolean {
32
- return GLOB_CHARS.test(text);
33
- }
34
-
35
- /** True when a string is a glob pattern that could stand for a name. */
36
- export function isGlobName(text: string): boolean {
37
- return hasGlob(text) && GLOB_NAME.test(text);
38
- }
39
-
40
- /**
41
- * Splits text at `/`, except inside braces, so `{a/b,c}` stays whole.
42
- *
43
- * splitSegments("a/{b/c,d}/e"); // -> ["a", "{b/c,d}", "e"]
44
- *
45
- * @param text - The text to split.
46
- */
47
- export function splitSegments(text: string): string[] {
48
- const segments: string[] = [];
49
- let depth = 0;
50
- let current = "";
51
- for (const character of text) {
52
- if (character === "{") depth += 1;
53
- if (character === "}" && depth > 0) depth -= 1;
54
- if (character === "/" && depth === 0) {
55
- segments.push(current);
56
- current = "";
57
- continue;
58
- }
59
- current += character;
60
- }
61
- segments.push(current);
62
- return segments;
63
- }
64
-
65
- /** How a written name matched a known one. */
66
- export type NameMatch = "exact" | "folded" | undefined;
67
-
68
- /**
69
- * Whether a written name (or glob pattern) matches a known one: exactly, only
70
- * ignoring case, or not at all.
71
- *
72
- * matchName("Workflow", "workflow"); // -> "folded"
73
- * matchName("work*", "workflow"); // -> "exact"
74
- *
75
- * @param written - The name or pattern as written.
76
- * @param known - The name as it is spelled where it is published.
77
- */
78
- export function matchName(written: string, known: string): NameMatch {
79
- if (hasGlob(written)) {
80
- if (minimatch(known, written, { dot: true })) return "exact";
81
- return minimatch(known, written, { dot: true, nocase: true }) ? "folded" : undefined;
82
- }
83
- if (written === known) return "exact";
84
- return written.toLowerCase() === known.toLowerCase() ? "folded" : undefined;
85
- }
86
-
87
- /**
88
- * The names that match a written one: those spelled exactly as written, or,
89
- * when there is none, those that differ only in case.
90
- *
91
- * matchNames("Workflow", ["workflow", "tooling"]); // -> ["workflow"]
92
- * matchNames("workflow", ["workflow", "Workflow"]); // -> ["workflow"]
93
- *
94
- * @param written - The name as written.
95
- * @param names - The names it could mean.
96
- */
97
- export function matchNames(written: string, names: Iterable<string>): string[] {
98
- const all = [...names];
99
- const exact = all.filter((name) => matchName(written, name) === "exact");
100
- if (exact.length > 0) return exact;
101
- return all.filter((name) => matchName(written, name) === "folded");
102
- }
103
-
104
- /**
105
- * Orders two strings by their UTF-8 bytes, so the order is the same on every
106
- * machine whatever its locale.
107
- *
108
- * ["b", "B", "a"].sort(compareBytewise); // -> ["B", "a", "b"]
109
- *
110
- * @param left - The first string.
111
- * @param right - The second string.
112
- */
113
- export function compareBytewise(left: string, right: string): number {
114
- return Buffer.compare(Buffer.from(left), Buffer.from(right));
115
- }
@@ -1,155 +0,0 @@
1
- /**
2
- * The `#` names: the built-in names an include line, a `{% render %}` path or
3
- * an `entryGlob` may start with, such as `#project`.
4
- *
5
- * `~` keeps meaning the home directory and recipe namespaces; `#` is for names
6
- * sous or a plugin provides. Each name is registered once, with the directories
7
- * it stands for, and a registration that repeats a name is an error naming both
8
- * registrants. A later layer registers a view the same way.
9
- *
10
- * A plugin adds a name by binding one more `HashName` under
11
- * `REF_TOKENS.HashName` on a container it builds with `createRefContainer()`.
12
- */
13
-
14
- import { ConfigError } from "../../lib/errors.js";
15
- import { compareBytewise } from "./glob.js";
16
- import { makeInjectable } from "./injectable.js";
17
- import { REF_TOKENS } from "./tokens.js";
18
-
19
- /** One file a view lists: where it sits in the view, and the real file behind it. */
20
- export interface ViewFile {
21
- /** The path inside the view, without the `#name/` prefix, with `/` separators. */
22
- readonly path: string;
23
- /** The real file's absolute path. */
24
- readonly file: string;
25
- }
26
-
27
- /** What a view needs to know about the project to list its files. */
28
- export interface HashViewContext {
29
- /** The project's `.sous/` directory. */
30
- sousDir: string;
31
- /** The environment to read; decides where the recipe store is. */
32
- env?: NodeJS.ProcessEnv;
33
- /** The merged project config, read for the `recipes` key. */
34
- settings: {
35
- recipes?: { memories?: { first?: string[] | undefined; exclude?: string[] | undefined } };
36
- };
37
- }
38
-
39
- /** One `#` name and what it stands for. */
40
- export interface HashName {
41
- /** The name without its `#`: lowercase kebab-case, such as `project`. */
42
- readonly name: string;
43
- /** Who registers it, for the error a duplicate raises (`sous`, or a plugin's name). */
44
- readonly registeredBy: string;
45
- /** What the name is, in one sentence. */
46
- readonly description: string;
47
- /**
48
- * The directories the name stands for, in the order they are tried. A path
49
- * after the name is looked up under each in turn, the first that exists
50
- * wins. Empty when the name has nothing to stand for in this project.
51
- *
52
- * @param scope - The settings scope the project's paths come from.
53
- */
54
- bases(scope: Record<string, string>): string[];
55
- /**
56
- * A view lists virtual files instead of standing for directories: an include
57
- * of `#name/<glob>` selects among the listed paths and includes the real
58
- * files behind them, in the listed order. A view has no bases.
59
- *
60
- * @param context - The project the files are listed for.
61
- */
62
- view?(context: HashViewContext): ViewFile[];
63
- }
64
-
65
- /** The characters a registered name may hold. */
66
- const VALID_NAME = /^[a-z][a-z0-9-]*$/;
67
-
68
- /** The built-in `#project`: the consuming project's root. */
69
- export class ProjectHashName implements HashName {
70
- readonly name = "project";
71
- readonly registeredBy = "sous";
72
- readonly description = "the root directory of the project being built.";
73
-
74
- bases(scope: Record<string, string>): string[] {
75
- return scope.projectRoot ? [scope.projectRoot] : [];
76
- }
77
- }
78
-
79
- makeInjectable(ProjectHashName);
80
-
81
- /** Every registered `#` name. */
82
- export class HashNameRegistry {
83
- private readonly entries = new Map<string, HashName>();
84
-
85
- /**
86
- * @param names - The names to register, each once.
87
- * @throws A ConfigError when a name is invalid or registered twice.
88
- */
89
- constructor(names: HashName[] = []) {
90
- for (const entry of names) this.register(entry);
91
- }
92
-
93
- /**
94
- * Adds a name.
95
- *
96
- * @param entry - The name to add.
97
- * @throws A ConfigError naming both registrants when the name is already taken.
98
- */
99
- register(entry: HashName): void {
100
- if (!VALID_NAME.test(entry.name)) {
101
- throw new ConfigError(
102
- `The name '#${entry.name}' registered by '${entry.registeredBy}' is invalid: a name is ` +
103
- "lowercase kebab-case, a letter then letters, digits or hyphens."
104
- );
105
- }
106
- const existing = this.entries.get(entry.name);
107
- if (existing !== undefined) {
108
- throw new ConfigError(
109
- `The name '#${entry.name}' is registered twice: by '${existing.registeredBy}' and by ` +
110
- `'${entry.registeredBy}'.`
111
- );
112
- }
113
- this.entries.set(entry.name, entry);
114
- }
115
-
116
- /** The registered name, or undefined. */
117
- get(name: string): HashName | undefined {
118
- return this.entries.get(name);
119
- }
120
-
121
- /** Every registered name, sorted. */
122
- list(): HashName[] {
123
- return [...this.entries.values()].sort((left, right) => compareBytewise(left.name, right.name));
124
- }
125
-
126
- /**
127
- * Every view's listed files, keyed by the name with its `#`.
128
- *
129
- * @param context - The project the files are listed for.
130
- */
131
- viewMap(context: HashViewContext): Record<string, ViewFile[]> {
132
- const map: Record<string, ViewFile[]> = {};
133
- for (const entry of this.list()) {
134
- if (entry.view !== undefined) map[`#${entry.name}`] = entry.view(context);
135
- }
136
- return map;
137
- }
138
-
139
- /**
140
- * The alias map the registered names make for a project: `#name` to its
141
- * directories, leaving out a name with none.
142
- *
143
- * @param scope - The settings scope the project's paths come from.
144
- */
145
- aliasMap(scope: Record<string, string>): Record<string, string[]> {
146
- const map: Record<string, string[]> = {};
147
- for (const entry of this.list()) {
148
- const bases = entry.bases(scope);
149
- if (bases.length > 0) map[`#${entry.name}`] = bases;
150
- }
151
- return map;
152
- }
153
- }
154
-
155
- makeInjectable(HashNameRegistry, [{ multi: REF_TOKENS.HashName }]);
@@ -1,55 +0,0 @@
1
- /**
2
- * The ref resolver service: how every sous command and template turns a
3
- * written ref into the thing it names.
4
- *
5
- * Import from here. `types.ts` says what a ref is, the parser reads every way a
6
- * text can be one, a pruner per place says which readings count there, a
7
- * lookup says which exist, `RefResolverService` runs the three in order, and
8
- * `RefPickerService` settles which meaning a run proceeds with.
9
- *
10
- * `EnvVarLookup` is not exported here. It reads the variable ladder, which
11
- * reaches config discovery, and config discovery reaches this module through
12
- * the recipe formats, so exporting it from the barrel would make an import
13
- * cycle that fails at load time. Import it from `lookups/env-var-lookup.js`.
14
- */
15
-
16
- export * from "./source.js";
17
- export * from "./types.js";
18
- export * from "./parts.js";
19
- export * from "./glob.js";
20
- export * from "./location.js";
21
- export * from "./format.js";
22
- export * from "./tokens.js";
23
- export * from "./hash-names.js";
24
- export * from "./injectable.js";
25
- export * from "./parser/partial-ref.js";
26
- export * from "./parser/ref-parser.js";
27
- export * from "./parser/query-splitter.js";
28
- export * from "./parser/range-splitter.js";
29
- export * from "./parser/location-splitter.js";
30
- export * from "./parser/repo-qualifier-splitter.js";
31
- export * from "./parser/name-path-splitter.js";
32
- export * from "./parser/env-var-splitter.js";
33
- export * from "./pruners/ref-pruner.js";
34
- export * from "./pruners/rule-helpers.js";
35
- export * from "./pruners/command-line-pruner.js";
36
- export * from "./pruners/stored-key-pruner.js";
37
- export * from "./pruners/manifest-pruner.js";
38
- export * from "./pruners/include-pruner.js";
39
- export * from "./lookups/ref-lookup.js";
40
- export * from "./lookups/chained-lookup.js";
41
- export * from "./lookups/catalog-matcher.js";
42
- export * from "./lookups/catalog-lookup.js";
43
- export * from "./lookups/cached-index-lookup.js";
44
- export * from "./lookups/recorded-dependency-lookup.js";
45
- export * from "./lookups/fetched-index-lookup.js";
46
- export * from "./lookups/variable-lookup.js";
47
- export * from "./ref-resolve-arguments.js";
48
- export * from "./ref-resolve-result.js";
49
- export * from "./ref-resolver-service.js";
50
- export * from "./ref-picker-service.js";
51
- export * from "./ref-report.js";
52
- export * from "./named-refs.js";
53
- export * from "./dependency.js";
54
- export * from "./ref-helpers.js";
55
- export * from "./container.js";