@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,47 +0,0 @@
1
- import { RefSource, SOURCE_LABELS } from "../source.js";
2
- import { makeInjectable } from "../injectable.js";
3
- import { isLocal } from "./rule-helpers.js";
4
- import { RuleRefPruner, type RefPruneRule } from "./ref-pruner.js";
5
-
6
- /**
7
- * The command line allows every form a ref has: a bare name, a namespace and
8
- * recipe, a `repo:` qualifier, a range, a location, a browser URL, a variable
9
- * and an environment variable name. It refuses a repository on this machine
10
- * written as a location (it is added by its path), a file inside a recipe (a
11
- * folder in a repository is a URL copied from the browser) and a glob.
12
- */
13
- export class CommandLinePruner extends RuleRefPruner {
14
- readonly source = RefSource.CommandLine;
15
- readonly place = SOURCE_LABELS[RefSource.CommandLine];
16
-
17
- protected rules(): RefPruneRule[] {
18
- return [
19
- {
20
- matches: isLocal,
21
- action: "drop",
22
- message:
23
- "a repository on this machine is added by its path, and its recipes are then named " +
24
- "by their short form.",
25
- instead: "'sous repo add <path>', then 'namespace/recipe'",
26
- },
27
- {
28
- matches: (ref) => ref.kind === "recipeFile",
29
- action: "drop",
30
- message:
31
- "a short ref has at most two path segments, a namespace and a recipe. A folder path " +
32
- "inside a repository is written as a URL copied from the browser instead.",
33
- instead: "'namespace/recipe', or the URL copied from the browser",
34
- },
35
- {
36
- matches: (ref) => ref.glob === true,
37
- action: "drop",
38
- message:
39
- "a name here must be kebab-case (a letter, then letters, digits or hyphens), so a " +
40
- "glob pattern is not accepted.",
41
- instead: "the name itself",
42
- },
43
- ];
44
- }
45
- }
46
-
47
- makeInjectable(CommandLinePruner);
@@ -1,66 +0,0 @@
1
- import { splitSegments } from "../glob.js";
2
- import { makeInjectable } from "../injectable.js";
3
- import { rangeOf } from "../parts.js";
4
- import { RefSource, SOURCE_LABELS } from "../source.js";
5
- import type { SousRef } from "../types.js";
6
- import { RuleRefPruner, type RefPruneRule } from "./ref-pruner.js";
7
- import { hasLocation } from "./rule-helpers.js";
8
-
9
- /** The path of a recipe file ref, or an empty string for any other ref. */
10
- function pathOf(ref: SousRef): string {
11
- return ref.kind === "recipeFile" ? ref.path : "";
12
- }
13
-
14
- /** The spelling a refused include is told to use: `namespace/recipe/path`. */
15
- function instead(ref: SousRef): string {
16
- if (ref.kind !== "recipeFile") return "'namespace/recipe/path/to/file.md'";
17
- const namespace = ref.recipe.namespace?.name ?? "namespace";
18
- return `'${namespace}/${ref.recipe.name}/${ref.path}'`;
19
- }
20
-
21
- /**
22
- * The path of a template include line, once its sigil is taken off. Only a
23
- * file inside a recipe is allowed, and its path, namespace and recipe name may
24
- * be globs. A `repo:` qualifier may name the repository by the short name this
25
- * project gave it. A location never counts, and neither does a version range
26
- * (the version is the one the project pins).
27
- */
28
- export class IncludePruner extends RuleRefPruner {
29
- readonly source = RefSource.Include;
30
- readonly place = SOURCE_LABELS[RefSource.Include];
31
-
32
- protected rules(): RefPruneRule[] {
33
- return [
34
- {
35
- matches: (ref) => ref.kind !== "recipeFile",
36
- action: "drop",
37
- message:
38
- "an include line names a file inside a recipe: its namespace, its recipe and the " +
39
- "path inside it.",
40
- instead,
41
- },
42
- {
43
- matches: hasLocation,
44
- action: "drop",
45
- message: "an include line never names a location; it reads the recipes this project pins.",
46
- instead,
47
- },
48
- {
49
- matches: (ref) => rangeOf(ref) !== undefined,
50
- action: "drop",
51
- message:
52
- "an include line takes no version range; the version of a recipe is the one this " +
53
- "project pins.",
54
- instead,
55
- },
56
- {
57
- matches: (ref) => splitSegments(pathOf(ref)).some((part) => part === "." || part === ".."),
58
- action: "drop",
59
- message: "the path inside a recipe may not contain '.' or '..' segments.",
60
- instead,
61
- },
62
- ];
63
- }
64
- }
65
-
66
- makeInjectable(IncludePruner);
@@ -1,130 +0,0 @@
1
- import type { RepoProvider } from "../../../lib/repos/providers/index.js";
2
- import { makeInjectable } from "../injectable.js";
3
- import { REF_TOKENS } from "../tokens.js";
4
- import { providerById } from "../../../lib/repos/providers/index.js";
5
- import { rangeOf } from "../parts.js";
6
- import { RefSource, SOURCE_LABELS } from "../source.js";
7
- import type { SousRef } from "../types.js";
8
- import { RuleRefPruner, type RefPruneRule } from "./ref-pruner.js";
9
- import { hasRepoName, isLocal, isStoredKind, KEBAB_REASON, shortKey } from "./rule-helpers.js";
10
-
11
- /**
12
- * An entry of a recipe manifest's `depends` or `subscribes` list. A bare ref
13
- * names a namespace or a recipe in the same repository, and a location names
14
- * one in another. A manifest is published, so its names are stored lowercase.
15
- * It refuses a `repo:` qualifier (one project's private name for a repository),
16
- * a repository on this machine, a whole repository with nothing inside it, and
17
- * everything that is not a namespace or a recipe.
18
- */
19
- export class ManifestPruner extends RuleRefPruner {
20
- readonly source = RefSource.Manifest;
21
- readonly place = SOURCE_LABELS[RefSource.Manifest];
22
-
23
- /**
24
- * @param providers - The providers that format a locator, for what to write instead.
25
- */
26
- constructor(private readonly providers: RepoProvider[]) {
27
- super();
28
- }
29
-
30
- protected rules(): RefPruneRule[] {
31
- return [
32
- {
33
- matches: hasRepoName,
34
- action: "drop",
35
- message:
36
- "a 'repo:' qualifier names a short name that only the consuming project knows, so it " +
37
- "cannot appear in a published manifest.",
38
- instead: (ref) =>
39
- `'${shortKey(ref).toLowerCase()}' for a recipe in this same repository, or name the ` +
40
- `other repository by its location, as in ` +
41
- `'github://owner/repository/${shortKey(ref).toLowerCase()}'`,
42
- },
43
- {
44
- matches: isLocal,
45
- action: "drop",
46
- message:
47
- "a local repository is a consumer's convenience, not a published location, so a " +
48
- "manifest cannot depend on one.",
49
- instead:
50
- "the recipe's published location, such as 'github://owner/repository/namespace/recipe'",
51
- },
52
- {
53
- matches: (ref) => ref.kind === "recipeFile",
54
- action: "drop",
55
- message:
56
- "a short ref has at most two path segments, a namespace and a recipe. A folder path " +
57
- "inside a repository is written as a URL copied from the browser instead.",
58
- instead: "'namespace/recipe', or the URL copied from the browser",
59
- },
60
- {
61
- matches: (ref) => ref.glob === true,
62
- action: "drop",
63
- message: KEBAB_REASON,
64
- instead: "'namespace/recipe'",
65
- },
66
- {
67
- matches: (ref) => ref.kind === "repo" && ref.browsed === undefined && ref.location !== undefined,
68
- action: "drop",
69
- message: (ref) =>
70
- `it names the repository at ${locationUrl(ref)} and nothing inside it, and a ` +
71
- "dependency is a namespace or a recipe.",
72
- instead: (ref) => `'${this.locator(ref)}'`,
73
- },
74
- {
75
- matches: (ref) => ref.kind === "recipe" && ref.namespace === undefined,
76
- action: "drop",
77
- message: (ref) =>
78
- rangeOf(ref) === undefined
79
- ? "a recipe is named with its namespace here, and a bare name is read as a " +
80
- "namespace."
81
- : "a version range applies to a recipe, and namespaces are not versioned. Name " +
82
- "the recipe with its namespace.",
83
- instead: "'namespace/recipe', optionally with a range such as '@^1.1'",
84
- },
85
- {
86
- matches: (ref) => !isStoredKind(ref) && !(ref.kind === "repo" && ref.browsed !== undefined),
87
- action: "drop",
88
- message: KEBAB_REASON,
89
- instead: "'namespace/recipe'",
90
- },
91
- {
92
- matches: (ref) => ref.vars !== undefined,
93
- action: "drop",
94
- message: "a manifest's dependency carries no query values.",
95
- instead: (ref) => `'${shortKey(ref).toLowerCase()}'`,
96
- },
97
- ];
98
- }
99
-
100
- protected normalize(ref: SousRef): SousRef {
101
- if (ref.kind === "namespace") return { ...ref, name: ref.name.toLowerCase() };
102
- if (ref.kind === "recipe") {
103
- return {
104
- ...ref,
105
- name: ref.name.toLowerCase(),
106
- ...(ref.namespace === undefined
107
- ? {}
108
- : { namespace: { ...ref.namespace, name: ref.namespace.name.toLowerCase() } }),
109
- };
110
- }
111
- return ref;
112
- }
113
-
114
- /** The canonical locator for something inside the ref's repository, as an example. */
115
- private locator(ref: SousRef): string {
116
- if (ref.kind !== "repo" || ref.location === undefined) return "github://owner/repository";
117
- const { host, repoPath } = ref.location;
118
- const provider = providerById(ref.location.provider, this.providers);
119
- return provider === undefined
120
- ? `${ref.location.url}/namespace/recipe`
121
- : provider.formatLocator(host, repoPath, "namespace/recipe");
122
- }
123
- }
124
-
125
- /** Where a located ref says its repository is. */
126
- function locationUrl(ref: SousRef): string {
127
- return ref.kind === "repo" && ref.location !== undefined ? ref.location.url : "that location";
128
- }
129
-
130
- makeInjectable(ManifestPruner, [{ multi: REF_TOKENS.Provider }]);
@@ -1,135 +0,0 @@
1
- /**
2
- * Pruners: the place a ref was written decides which readings of it count.
3
- *
4
- * The parser returns every reading. One pruner per `RefSource` then drops the
5
- * readings that place does not allow, each with the reason and what to write
6
- * there instead, so a refused ref is an error that tells the writer how to fix
7
- * it rather than a puzzle.
8
- */
9
-
10
- import { ConfigError } from "../../../lib/errors.js";
11
- import type { RefSource } from "../source.js";
12
- import type { SousRef } from "../types.js";
13
-
14
- /** One reading a pruner refused. */
15
- export class DroppedRef {
16
- /**
17
- * @param ref - The reading.
18
- * @param reason - Why this place does not allow it, as a sentence.
19
- * @param instead - What to write there instead, as it would appear after "Write".
20
- */
21
- constructor(
22
- readonly ref: SousRef,
23
- readonly reason: string,
24
- readonly instead?: string
25
- ) {}
26
-
27
- /** The reason, with what to write instead on a line of its own. */
28
- describe(): string {
29
- return this.instead === undefined
30
- ? this.reason
31
- : `${this.reason}\n Write ${this.instead} instead.`;
32
- }
33
- }
34
-
35
- /** What a pruner decided about a list of readings. */
36
- export class RefPruneResult {
37
- /**
38
- * @param kept - The readings this place allows, in the order they came.
39
- * @param dropped - The readings it refuses, each with its reason.
40
- * @param warnings - Sentences about readings that were kept all the same.
41
- */
42
- constructor(
43
- readonly kept: SousRef[] = [],
44
- readonly dropped: DroppedRef[] = [],
45
- readonly warnings: string[] = []
46
- ) {}
47
- }
48
-
49
- /** Decides which readings of a ref count in one place. */
50
- export interface RefPruner {
51
- /** The place this pruner speaks for. */
52
- readonly source: RefSource;
53
- /** What the place is called in a sentence, for the error. */
54
- readonly place: string;
55
- /**
56
- * Splits the readings into those the place allows and those it refuses.
57
- *
58
- * @param candidates - Every reading the parser returned.
59
- * @param input - The ref exactly as it was written, for messages.
60
- */
61
- prune(candidates: SousRef[], input: string): RefPruneResult;
62
- }
63
-
64
- /** What a rule does with a reading it matches. */
65
- export type RefPruneAction = "drop" | "warn" | "throw";
66
-
67
- /** The text of a rule's message: fixed, or made from the reading and what was written. */
68
- export type RefRuleText = string | ((ref: SousRef, input: string) => string);
69
-
70
- /** One rule of a pruner: when it matches a reading, what it does and why. */
71
- export type RefPruneRule = {
72
- /** True when the rule applies to this reading. */
73
- matches: (ref: SousRef, input: string) => boolean;
74
- /** `drop` removes the reading, `warn` keeps it with a warning, `throw` fails at once. */
75
- action: RefPruneAction;
76
- /** The reason: why the place refuses the reading, or what is wrong with it. */
77
- message: RefRuleText;
78
- /** What to write instead, which makes the error say how to fix it. */
79
- instead?: RefRuleText;
80
- };
81
-
82
- /** Resolves a rule's text for one reading. */
83
- function textOf(text: RefRuleText, ref: SousRef, input: string): string {
84
- return typeof text === "function" ? text(ref, input) : text;
85
- }
86
-
87
- /**
88
- * A pruner that applies an ordered list of rules. The first rule that matches
89
- * a reading decides; a reading no rule matches is kept. A subclass lists its
90
- * rules, and may rewrite a kept reading (a manifest stores lowercase names).
91
- */
92
- export abstract class RuleRefPruner implements RefPruner {
93
- abstract readonly source: RefSource;
94
- abstract readonly place: string;
95
-
96
- /** The rules, in the order they are tried. */
97
- protected abstract rules(): RefPruneRule[];
98
-
99
- /**
100
- * Rewrites a reading this place keeps. Most places keep it as it is.
101
- *
102
- * @param ref - A kept reading.
103
- */
104
- protected normalize(ref: SousRef): SousRef {
105
- return ref;
106
- }
107
-
108
- prune(candidates: SousRef[], input: string): RefPruneResult {
109
- const rules = this.rules();
110
- const result = new RefPruneResult();
111
-
112
- for (const ref of candidates) {
113
- const rule = rules.find((entry) => entry.matches(ref, input));
114
- if (rule === undefined) {
115
- result.kept.push(this.normalize(ref));
116
- continue;
117
- }
118
- const message = textOf(rule.message, ref, input);
119
- if (rule.action === "throw") throw new ConfigError(message);
120
- if (rule.action === "warn") {
121
- result.kept.push(this.normalize(ref));
122
- result.warnings.push(message);
123
- continue;
124
- }
125
- result.dropped.push(
126
- new DroppedRef(
127
- ref,
128
- message,
129
- rule.instead === undefined ? undefined : textOf(rule.instead, ref, input)
130
- )
131
- );
132
- }
133
- return result;
134
- }
135
- }
@@ -1,44 +0,0 @@
1
- /**
2
- * Predicates and wording the per-place pruners share.
3
- */
4
-
5
- import { locationOf, namespaceOf, recipeOf, repoOf } from "../parts.js";
6
- import type { SousRef } from "../types.js";
7
-
8
- /** True when the ref names a repository that lives on this machine. */
9
- export function isLocal(ref: SousRef): boolean {
10
- return locationOf(ref)?.provider === "local";
11
- }
12
-
13
- /** True when the ref names where its repository lives. */
14
- export function hasLocation(ref: SousRef): boolean {
15
- return locationOf(ref) !== undefined;
16
- }
17
-
18
- /** True when the ref is qualified by a repository short name (`repo:`). */
19
- export function hasRepoName(ref: SousRef): boolean {
20
- return ref.kind !== "repo" && repoOf(ref)?.name !== undefined;
21
- }
22
-
23
- /** True when the ref is a namespace, or a recipe that names its namespace. */
24
- export function isStoredKind(ref: SousRef): boolean {
25
- return ref.kind === "namespace" || (ref.kind === "recipe" && ref.namespace !== undefined);
26
- }
27
-
28
- /**
29
- * The short key of a namespace or a recipe: `namespace` or `namespace/recipe`,
30
- * as the lockfile and the index store it, in the case it was written.
31
- *
32
- * @param ref - A namespace or a recipe.
33
- */
34
- export function shortKey(ref: SousRef): string {
35
- const namespace = namespaceOf(ref)?.name;
36
- const recipe = ref.kind === "namespace" ? undefined : recipeOf(ref)?.name;
37
- if (namespace === undefined) return recipe ?? "";
38
- return recipe === undefined ? namespace : `${namespace}/${recipe}`;
39
- }
40
-
41
- /** The sentence for a reading that is not a name a place can hold. */
42
- export const KEBAB_REASON =
43
- "only a namespace or a recipe can be named here, and their names must be kebab-case: " +
44
- "a letter, then letters, digits or hyphens.";
@@ -1,136 +0,0 @@
1
- /**
2
- * What a config file and a stored key have in common: only `namespace` or
3
- * `namespace/recipe`, lowercase, with nothing else attached. They differ in
4
- * what they tell a writer to do instead.
5
- */
6
-
7
- import { makeInjectable } from "../injectable.js";
8
- import { rangeOf, repoOf } from "../parts.js";
9
- import { RefSource, SOURCE_LABELS } from "../source.js";
10
- import { RuleRefPruner, type RefPruneRule } from "./ref-pruner.js";
11
- import {
12
- hasLocation,
13
- hasRepoName,
14
- isStoredKind,
15
- KEBAB_REASON,
16
- shortKey,
17
- } from "./rule-helpers.js";
18
-
19
- /** A subscription key in a config layer. */
20
- export class ConfigPruner extends RuleRefPruner {
21
- readonly source = RefSource.Config;
22
- readonly place = SOURCE_LABELS[RefSource.Config];
23
-
24
- protected rules(): RefPruneRule[] {
25
- return [
26
- {
27
- matches: hasLocation,
28
- action: "drop",
29
- message:
30
- "a config file names a subscription by its short form; the repository it lives in is " +
31
- "a separate entry under 'repos'.",
32
- instead: (_ref, input) =>
33
- `'namespace/recipe', or run 'sous subscribe ${input.trim()}', which adds the ` +
34
- "repository and writes the subscription in its short form",
35
- },
36
- {
37
- matches: (ref) => !isStoredKind(ref),
38
- action: "drop",
39
- message: KEBAB_REASON,
40
- instead: "'namespace/recipe'",
41
- },
42
- {
43
- matches: hasRepoName,
44
- action: "drop",
45
- message: (ref) =>
46
- "the repository a subscription resolves into is recorded in the lockfile, not in the " +
47
- "key. To choose between repositories, run " +
48
- `'sous subscribe ${repoOf(ref)?.name}:${shortKey(ref).toLowerCase()}', which records the choice.`,
49
- instead: (ref) => `'${shortKey(ref).toLowerCase()}'`,
50
- },
51
- {
52
- matches: (ref) => ref.glob === true,
53
- action: "drop",
54
- message: "a glob pattern is not a stored key; names here must be kebab-case.",
55
- instead: (ref) => `'${shortKey(ref).toLowerCase()}'`,
56
- },
57
- {
58
- matches: (ref) => rangeOf(ref) !== undefined,
59
- action: "drop",
60
- message: "a subscription's version range belongs in the entry's own 'range' field.",
61
- instead: (ref) => `'${shortKey(ref).toLowerCase()}': { range: '${rangeOf(ref)}' }`,
62
- },
63
- ...sharedStoredRules(),
64
- ];
65
- }
66
- }
67
-
68
- /** A key sous stored: the lockfile, the index and the store. */
69
- export class LockfilePruner extends RuleRefPruner {
70
- readonly source = RefSource.Lockfile;
71
- readonly place = SOURCE_LABELS[RefSource.Lockfile];
72
-
73
- protected rules(): RefPruneRule[] {
74
- return [
75
- {
76
- matches: hasLocation,
77
- action: "drop",
78
- message: "a stored key never names a location.",
79
- instead: "'namespace/recipe'",
80
- },
81
- {
82
- matches: (ref) => !isStoredKind(ref),
83
- action: "drop",
84
- message: KEBAB_REASON,
85
- instead: "'namespace/recipe'",
86
- },
87
- {
88
- matches: hasRepoName,
89
- action: "drop",
90
- message: "a stored key never names a repository.",
91
- instead: (ref) => `'${shortKey(ref).toLowerCase()}'`,
92
- },
93
- {
94
- matches: (ref) => ref.glob === true,
95
- action: "drop",
96
- message: "a glob pattern is not a stored key; names here must be kebab-case.",
97
- instead: (ref) => `'${shortKey(ref).toLowerCase()}'`,
98
- },
99
- {
100
- matches: (ref) => rangeOf(ref) !== undefined,
101
- action: "drop",
102
- message: "a stored key carries no version range.",
103
- instead: (ref) => `'${shortKey(ref).toLowerCase()}'`,
104
- },
105
- ...sharedStoredRules(),
106
- ];
107
- }
108
- }
109
-
110
- /** The rules a config key and a stored key share, after their own. */
111
- function sharedStoredRules(): RefPruneRule[] {
112
- return [
113
- {
114
- matches: (ref) => ref.kind === "namespace" && ref.wildcard === true,
115
- action: "drop",
116
- message: "a whole namespace is stored as its name alone.",
117
- instead: (ref) => `'${shortKey(ref).toLowerCase()}'`,
118
- },
119
- {
120
- matches: (ref) => ref.vars !== undefined,
121
- action: "drop",
122
- message: "a stored key carries no query values.",
123
- instead: (ref) => `'${shortKey(ref).toLowerCase()}'`,
124
- },
125
- {
126
- matches: (ref) => shortKey(ref) !== shortKey(ref).toLowerCase(),
127
- action: "drop",
128
- message: "stored names are lowercase.",
129
- instead: (ref) => `'${shortKey(ref).toLowerCase()}'`,
130
- },
131
- ];
132
- }
133
-
134
- makeInjectable(ConfigPruner);
135
-
136
- makeInjectable(LockfilePruner);
@@ -1,62 +0,0 @@
1
- /**
2
- * Helpers around the resolver, for callers that have no container: the ones
3
- * that need a resolver use the shared one, and the ones that only work on refs
4
- * need none.
5
- */
6
-
7
- import { sharedRefResolver } from "./container.js";
8
- import { RefResolveResult } from "./ref-resolve-result.js";
9
- import type { RefInString } from "./ref-resolver-service.js";
10
- import { RefSource } from "./source.js";
11
- import type { RefKind, SousRef } from "./types.js";
12
-
13
- /**
14
- * True when the text is a ref the place allows. A caller that finds text that
15
- * looks like a ref but is not (an include line, say) treats it as an error.
16
- *
17
- * @param input - The ref exactly as it was written.
18
- * @param from - Where it was written.
19
- */
20
- export function isValidRef(input: string, from: RefSource = RefSource.CommandLine): boolean {
21
- return sharedRefResolver().isValidRef(input, from);
22
- }
23
-
24
- /**
25
- * Every valid ref in a longer text, with the punctuation around each word taken off.
26
- *
27
- * @param text - The text to search.
28
- * @param from - Where the text was written.
29
- */
30
- export function getRefsInString(text: string, from: RefSource = RefSource.CommandLine): RefInString[] {
31
- return sharedRefResolver().getRefsInString(text, from);
32
- }
33
-
34
- /**
35
- * The best ref of a result, or the first reading of a string the place allows.
36
- *
37
- * @param source - A resolve result, or a written ref.
38
- * @param from - Where a written ref was written.
39
- * @returns The ref, or undefined when a result holds none.
40
- */
41
- export function getFirstRef(
42
- source: RefResolveResult | string,
43
- from: RefSource = RefSource.CommandLine
44
- ): SousRef | undefined {
45
- return typeof source === "string" ? sharedRefResolver().parse(source, from).first() : source.first();
46
- }
47
-
48
- /**
49
- * Keeps the refs a caller can use: those of the given kinds, or those a
50
- * predicate accepts.
51
- *
52
- * pruneRefs(result.refs, ["namespace", "recipe"]);
53
- *
54
- * @param refs - The refs to narrow.
55
- * @param keep - The kinds to keep, or a test.
56
- */
57
- export function pruneRefs(
58
- refs: readonly SousRef[],
59
- keep: readonly RefKind[] | ((ref: SousRef) => boolean)
60
- ): SousRef[] {
61
- return refs.filter((ref) => (typeof keep === "function" ? keep(ref) : keep.includes(ref.kind)));
62
- }