@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.
- package/README.md +14 -256
- package/dist/bin.js +11 -0
- package/dist/sous.js +1094 -0
- package/docs/markdown/README.md +5 -4
- package/docs/markdown/_sidebar.md +21 -1
- package/docs/markdown/commands.md +294 -431
- package/docs/markdown/config-discovery.md +25 -14
- package/docs/markdown/config-inspection.md +53 -31
- package/docs/markdown/config-layers.md +168 -66
- package/docs/markdown/config-variables.md +44 -11
- package/docs/markdown/configuration.md +104 -17
- package/docs/markdown/design-principles.md +4 -1
- package/docs/markdown/repositories-authoring.md +30 -0
- package/docs/markdown/repositories-consuming.md +72 -14
- package/docs/markdown/repositories-file-formats.md +31 -13
- package/docs/markdown/repositories-providers.md +48 -57
- package/docs/markdown/repositories-quickstart.md +3 -2
- package/docs/markdown/repositories-troubleshooting.md +27 -1
- package/docs/markdown/repositories-variables.md +6 -1
- package/docs/markdown/repositories.md +18 -8
- package/package.json +48 -107
- package/recipes/core/sous-skills/skills/about-liquid-templates/SKILL.tpl.md +4 -6
- package/recipes/core/sous-skills/skills/about-liquid-templates/references/liquid-filters.tpl.md +2 -0
- package/recipes/core/sous-skills/skills/about-sous/SKILL.tpl.md +10 -9
- package/recipes/core/sous-skills/skills/about-sous-configuration/SKILL.tpl.md +35 -14
- package/recipes/core/sous-skills/skills/about-sous-recipes/SKILL.tpl.md +51 -20
- package/recipes/core/sous-skills/sous.recipe.yaml +1 -1
- package/sous.config.schema.json +351 -132
- package/bin/run.js +0 -51
- package/src/base-command.ts +0 -457
- package/src/commands/build.ts +0 -181
- package/src/commands/clear.ts +0 -79
- package/src/commands/compile.ts +0 -155
- package/src/commands/config/get.ts +0 -110
- package/src/commands/config/show.ts +0 -32
- package/src/commands/config/validate.ts +0 -53
- package/src/commands/help.ts +0 -46
- package/src/commands/init.ts +0 -289
- package/src/commands/launch.ts +0 -139
- package/src/commands/lock/rebuild.ts +0 -239
- package/src/commands/lock/show.ts +0 -115
- package/src/commands/namespace/list.ts +0 -138
- package/src/commands/namespace/show.ts +0 -130
- package/src/commands/prune.ts +0 -43
- package/src/commands/recipe/list.ts +0 -112
- package/src/commands/recipe/show.ts +0 -439
- package/src/commands/repo/add.ts +0 -145
- package/src/commands/repo/contribute.ts +0 -604
- package/src/commands/repo/gc.ts +0 -173
- package/src/commands/repo/init.ts +0 -136
- package/src/commands/repo/link.ts +0 -899
- package/src/commands/repo/list.ts +0 -236
- package/src/commands/repo/release.ts +0 -685
- package/src/commands/repo/remove.ts +0 -194
- package/src/commands/repo/search.ts +0 -243
- package/src/commands/repo/submit.ts +0 -341
- package/src/commands/repo/unlink.ts +0 -461
- package/src/commands/subscription/add.ts +0 -286
- package/src/commands/subscription/list.ts +0 -208
- package/src/commands/subscription/remove.ts +0 -182
- package/src/commands/subscription/update.ts +0 -216
- package/src/commands/update.ts +0 -178
- package/src/commands/vars/ask.ts +0 -417
- package/src/commands/vars/index.ts +0 -79
- package/src/commands/vars/list.ts +0 -67
- package/src/commands/vars/show.ts +0 -77
- package/src/config-command.ts +0 -30
- package/src/lib/build-preparation.ts +0 -128
- package/src/lib/build-service.ts +0 -580
- package/src/lib/config-discovery.ts +0 -378
- package/src/lib/config-inspect.ts +0 -145
- package/src/lib/config-kernel.mjs +0 -379
- package/src/lib/config-schema.ts +0 -401
- package/src/lib/env-file.ts +0 -328
- package/src/lib/env-local.ts +0 -212
- package/src/lib/errors.ts +0 -32
- package/src/lib/glob-pattern.ts +0 -24
- package/src/lib/include-resolver.ts +0 -523
- package/src/lib/interactive.ts +0 -165
- package/src/lib/markdown-compiler.ts +0 -947
- package/src/lib/package-info.ts +0 -25
- package/src/lib/pid-service.ts +0 -99
- package/src/lib/project-install.d.mts +0 -44
- package/src/lib/project-install.mjs +0 -234
- package/src/lib/project-scaffold/index.ts +0 -332
- package/src/lib/project-scaffold/templates.ts +0 -240
- package/src/lib/repos/catalog-display.ts +0 -215
- package/src/lib/repos/catalog-inputs.ts +0 -288
- package/src/lib/repos/catalog.ts +0 -842
- package/src/lib/repos/contribute.ts +0 -452
- package/src/lib/repos/core-recipe.ts +0 -105
- package/src/lib/repos/declarations.ts +0 -187
- package/src/lib/repos/defaults.ts +0 -175
- package/src/lib/repos/formats/common.ts +0 -440
- package/src/lib/repos/formats/index-file.ts +0 -253
- package/src/lib/repos/formats/links-map.ts +0 -98
- package/src/lib/repos/formats/lockfile.ts +0 -169
- package/src/lib/repos/formats/patterns.ts +0 -57
- package/src/lib/repos/formats/recipe-manifest.ts +0 -205
- package/src/lib/repos/formats/repo-manifest.ts +0 -97
- package/src/lib/repos/formats/store-entry.ts +0 -84
- package/src/lib/repos/formats/variable-definition.ts +0 -268
- package/src/lib/repos/freshness.ts +0 -264
- package/src/lib/repos/git-clone.ts +0 -850
- package/src/lib/repos/identity.ts +0 -89
- package/src/lib/repos/index.ts +0 -57
- package/src/lib/repos/links.ts +0 -356
- package/src/lib/repos/load-manifest.ts +0 -237
- package/src/lib/repos/lock-service.ts +0 -454
- package/src/lib/repos/locked-namespace-resolver.ts +0 -232
- package/src/lib/repos/locked-recipe-lookup.ts +0 -123
- package/src/lib/repos/locked-recipes.ts +0 -254
- package/src/lib/repos/managed-layer.ts +0 -422
- package/src/lib/repos/memories-hash-name.ts +0 -33
- package/src/lib/repos/namespace-resolver.ts +0 -527
- package/src/lib/repos/providers/base.ts +0 -268
- package/src/lib/repos/providers/git.ts +0 -245
- package/src/lib/repos/providers/github.ts +0 -589
- package/src/lib/repos/providers/gitlab.ts +0 -332
- package/src/lib/repos/providers/http.ts +0 -128
- package/src/lib/repos/providers/index-cache.ts +0 -421
- package/src/lib/repos/providers/index.ts +0 -106
- package/src/lib/repos/providers/local.ts +0 -410
- package/src/lib/repos/providers/provider.ts +0 -615
- package/src/lib/repos/recipe-config-layers.ts +0 -316
- package/src/lib/repos/recipe-key-matcher.ts +0 -62
- package/src/lib/repos/recipe-memories.ts +0 -178
- package/src/lib/repos/recipe-targets.ts +0 -236
- package/src/lib/repos/ref-search.ts +0 -46
- package/src/lib/repos/release/bump.ts +0 -161
- package/src/lib/repos/release/changelog.ts +0 -461
- package/src/lib/repos/release/git-state.ts +0 -460
- package/src/lib/repos/release/index-builder.ts +0 -1108
- package/src/lib/repos/release/index.ts +0 -19
- package/src/lib/repos/release/plan.ts +0 -521
- package/src/lib/repos/release/settle.ts +0 -154
- package/src/lib/repos/release/submissions.ts +0 -214
- package/src/lib/repos/release/submit-checkout.ts +0 -290
- package/src/lib/repos/release/submit-questions.ts +0 -153
- package/src/lib/repos/release/submit-service.ts +0 -898
- package/src/lib/repos/release/tags.ts +0 -239
- package/src/lib/repos/release/validate.ts +0 -463
- package/src/lib/repos/resolver.ts +0 -1028
- package/src/lib/repos/scaffold/index.ts +0 -245
- package/src/lib/repos/scaffold/templates.ts +0 -517
- package/src/lib/repos/seed.ts +0 -473
- package/src/lib/repos/store/contract.ts +0 -64
- package/src/lib/repos/store/hash.ts +0 -191
- package/src/lib/repos/store/recipe-store.ts +0 -599
- package/src/lib/repos/store/settings.ts +0 -58
- package/src/lib/repos/subscription-service.ts +0 -3859
- package/src/lib/repos/trust.ts +0 -447
- package/src/lib/repos/update-plan.ts +0 -235
- package/src/lib/self-update/index.ts +0 -14
- package/src/lib/self-update/installs.ts +0 -413
- package/src/lib/self-update/managers.ts +0 -126
- package/src/lib/self-update/plan.ts +0 -315
- package/src/lib/self-update/registry.ts +0 -99
- package/src/lib/self-update/run.ts +0 -506
- package/src/lib/self-update/versions.ts +0 -243
- package/src/lib/settings.ts +0 -1107
- package/src/lib/sous-home.ts +0 -104
- package/src/lib/state.ts +0 -167
- package/src/lib/vars/answers.ts +0 -192
- package/src/lib/vars/ask.ts +0 -1152
- package/src/lib/vars/definition-source.ts +0 -261
- package/src/lib/vars/display.ts +0 -231
- package/src/lib/vars/index.ts +0 -20
- package/src/lib/vars/ladder.ts +0 -282
- package/src/lib/vars/mappings.ts +0 -260
- package/src/lib/vars/names.ts +0 -94
- package/src/lib/vars/preanswers.ts +0 -395
- package/src/lib/vars/question-plan.ts +0 -241
- package/src/lib/vars/report.ts +0 -228
- package/src/lib/vars/safe-regex.ts +0 -235
- package/src/lib/vars/secrets.ts +0 -131
- package/src/lib/vars/validate.ts +0 -312
- package/src/lib/version-report.ts +0 -90
- package/src/lib/watch-loop.ts +0 -148
- package/src/lib/watch-service.ts +0 -116
- package/src/services/ref-resolver/container.ts +0 -107
- package/src/services/ref-resolver/dependency.ts +0 -60
- package/src/services/ref-resolver/format.ts +0 -197
- package/src/services/ref-resolver/glob.ts +0 -115
- package/src/services/ref-resolver/hash-names.ts +0 -155
- package/src/services/ref-resolver/index.ts +0 -55
- package/src/services/ref-resolver/injectable.ts +0 -36
- package/src/services/ref-resolver/location.ts +0 -62
- package/src/services/ref-resolver/lookups/cached-index-lookup.ts +0 -53
- package/src/services/ref-resolver/lookups/catalog-lookup.ts +0 -23
- package/src/services/ref-resolver/lookups/catalog-matcher.ts +0 -261
- package/src/services/ref-resolver/lookups/chained-lookup.ts +0 -27
- package/src/services/ref-resolver/lookups/env-var-lookup.ts +0 -65
- package/src/services/ref-resolver/lookups/fetched-index-lookup.ts +0 -150
- package/src/services/ref-resolver/lookups/recorded-dependency-lookup.ts +0 -62
- package/src/services/ref-resolver/lookups/ref-lookup.ts +0 -54
- package/src/services/ref-resolver/lookups/variable-lookup.ts +0 -123
- package/src/services/ref-resolver/named-refs.ts +0 -144
- package/src/services/ref-resolver/parser/env-var-splitter.ts +0 -27
- package/src/services/ref-resolver/parser/location-splitter.ts +0 -239
- package/src/services/ref-resolver/parser/name-path-splitter.ts +0 -200
- package/src/services/ref-resolver/parser/partial-ref.ts +0 -84
- package/src/services/ref-resolver/parser/query-splitter.ts +0 -61
- package/src/services/ref-resolver/parser/range-splitter.ts +0 -52
- package/src/services/ref-resolver/parser/ref-parser.ts +0 -115
- package/src/services/ref-resolver/parser/repo-qualifier-splitter.ts +0 -51
- package/src/services/ref-resolver/parts.ts +0 -115
- package/src/services/ref-resolver/pruners/command-line-pruner.ts +0 -47
- package/src/services/ref-resolver/pruners/include-pruner.ts +0 -66
- package/src/services/ref-resolver/pruners/manifest-pruner.ts +0 -130
- package/src/services/ref-resolver/pruners/ref-pruner.ts +0 -135
- package/src/services/ref-resolver/pruners/rule-helpers.ts +0 -44
- package/src/services/ref-resolver/pruners/stored-key-pruner.ts +0 -136
- package/src/services/ref-resolver/ref-helpers.ts +0 -62
- package/src/services/ref-resolver/ref-picker-service.ts +0 -204
- package/src/services/ref-resolver/ref-report.ts +0 -84
- package/src/services/ref-resolver/ref-resolve-arguments.ts +0 -47
- package/src/services/ref-resolver/ref-resolve-result.ts +0 -36
- package/src/services/ref-resolver/ref-resolver-service.ts +0 -323
- package/src/services/ref-resolver/source.ts +0 -48
- package/src/services/ref-resolver/tokens.ts +0 -25
- package/src/services/ref-resolver/types.ts +0 -155
- package/src/templating/filters/bullet-list.ts +0 -9
- package/src/templating/filters/index.ts +0 -8
- package/src/templating/init-liquid-engine.ts +0 -139
- package/src/templating/lib/glob-files.ts +0 -74
- package/src/templating/lib/import-export.ts +0 -32
- package/src/templating/lib/secret-scope.ts +0 -41
- package/src/templating/lib/tag-args.ts +0 -19
- package/src/templating/tags/exportScalarVarsJs.ts +0 -46
- package/src/templating/tags/getFiles.ts +0 -89
- package/src/templating/tags/index.ts +0 -14
- package/src/templating/tags/listFiles.ts +0 -54
- package/src/templating/tags/showVars.ts +0 -28
- package/src/utils/choice-prompt.ts +0 -143
- package/src/utils/command-errors.ts +0 -186
- package/src/utils/command-help.ts +0 -45
- package/src/utils/confirm-prompt.ts +0 -110
- package/src/utils/flags.ts +0 -177
- package/src/utils/formatting.ts +0 -849
- package/src/utils/prompts.ts +0 -53
- package/src/utils/sous-directory.ts +0 -245
- package/src/utils/table.ts +0 -603
- package/src/utils/value-prompt.ts +0 -119
package/src/lib/repos/catalog.ts
DELETED
|
@@ -1,842 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Browsing what a project trusts.
|
|
3
|
-
*
|
|
4
|
-
* Every trusted repository publishes an index, and the project's lockfile
|
|
5
|
-
* records what it currently pins. Put those two together and a project can be
|
|
6
|
-
* asked four questions without downloading anything: which namespaces exist,
|
|
7
|
-
* what is in one of them, which recipes exist, and everything about one recipe.
|
|
8
|
-
* This module answers all four.
|
|
9
|
-
*
|
|
10
|
-
* Everything here is a pure function over data the caller hands it: the cached
|
|
11
|
-
* indexes, the lockfile, and the subscription keys the project declares. The
|
|
12
|
-
* content a recipe contributes is known only from its own manifest, inside the
|
|
13
|
-
* recipe's files, so the caller supplies a reader for it; a recipe whose files
|
|
14
|
-
* are not on this machine is described from its index alone rather than being
|
|
15
|
-
* an error, and an index recorded by a current release says how each of its
|
|
16
|
-
* dependencies was declared.
|
|
17
|
-
*
|
|
18
|
-
* Refs resolve the way `sous subscribe` resolves them: `namespace`,
|
|
19
|
-
* `namespace/recipe`, and either of those qualified with `repo:`. A one-word ref
|
|
20
|
-
* is searched across every trusted repository, and a word that means more than
|
|
21
|
-
* one thing is an error listing what it could have meant.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import semver from "semver";
|
|
25
|
-
import { ConfigError } from "../errors.js";
|
|
26
|
-
import type { IndexDependency, IndexFile } from "./formats/index-file.js";
|
|
27
|
-
import type { Lockfile } from "./formats/lockfile.js";
|
|
28
|
-
import type { ContentKind, RecipeManifest } from "./formats/recipe-manifest.js";
|
|
29
|
-
import {
|
|
30
|
-
CachedIndexLookup,
|
|
31
|
-
RefResolveArguments,
|
|
32
|
-
describeRef,
|
|
33
|
-
isNamedRef,
|
|
34
|
-
locationOf,
|
|
35
|
-
namespaceOf,
|
|
36
|
-
namespaceOfKey,
|
|
37
|
-
repoOf,
|
|
38
|
-
settleDependency,
|
|
39
|
-
shortKey,
|
|
40
|
-
sharedRefResolver,
|
|
41
|
-
splitRecipeKey,
|
|
42
|
-
type NamedRef,
|
|
43
|
-
type RefKind,
|
|
44
|
-
type SousRef,
|
|
45
|
-
} from "../../services/ref-resolver/index.js";
|
|
46
|
-
import { declarationFor, type DependencyLists } from "./declarations.js";
|
|
47
|
-
|
|
48
|
-
// --- What the catalog reads ---------------------------------------------------------------------
|
|
49
|
-
|
|
50
|
-
/** One trusted repository whose index has been fetched. */
|
|
51
|
-
export type CatalogRepo = {
|
|
52
|
-
/** The short name this project calls it. */
|
|
53
|
-
name: string;
|
|
54
|
-
/** Where it lives, as the project's config records it. */
|
|
55
|
-
url?: string;
|
|
56
|
-
/** Its cached index. */
|
|
57
|
-
index: IndexFile;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/** Where one published recipe's files would be read from. */
|
|
61
|
-
export type RecipeLocation = {
|
|
62
|
-
/** The recipe key, `namespace/recipe`. */
|
|
63
|
-
key: string;
|
|
64
|
-
namespace: string;
|
|
65
|
-
name: string;
|
|
66
|
-
/** The short name of the repository publishing it. */
|
|
67
|
-
repo: string;
|
|
68
|
-
/** The version being described. */
|
|
69
|
-
version: string;
|
|
70
|
-
/** The recipe folder, relative to the repository root. */
|
|
71
|
-
path: string;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
/** Everything the catalog functions read. */
|
|
75
|
-
export type CatalogInputs = {
|
|
76
|
-
/**
|
|
77
|
-
* The trusted repositories with a readable index, in the order a one-word ref
|
|
78
|
-
* searches them: the built-in repository first, then the ones the config
|
|
79
|
-
* names, in config order.
|
|
80
|
-
*/
|
|
81
|
-
repos: CatalogRepo[];
|
|
82
|
-
/** The project's lockfile, which is where a pinned version comes from. */
|
|
83
|
-
lock: Lockfile;
|
|
84
|
-
/** The ref keys the project subscribes to: namespaces, and `namespace/recipe`. */
|
|
85
|
-
subscriptions: string[];
|
|
86
|
-
/**
|
|
87
|
-
* The repositories read from a linked working copy instead of the store, by
|
|
88
|
-
* short name, each with the checkout's path.
|
|
89
|
-
*/
|
|
90
|
-
linked?: Record<string, string>;
|
|
91
|
-
/**
|
|
92
|
-
* Reads one published recipe's manifest, when its files are on this machine.
|
|
93
|
-
* Returning undefined means "not available", and the recipe is described from
|
|
94
|
-
* its index alone.
|
|
95
|
-
*/
|
|
96
|
-
readManifest?: (recipe: RecipeLocation) => RecipeManifest | undefined;
|
|
97
|
-
/** Where one content kind's files land in this project, when anywhere does. */
|
|
98
|
-
destinationsFor?: (kind: ContentKind) => string[];
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
// --- What the catalog answers -------------------------------------------------------------------
|
|
102
|
-
|
|
103
|
-
/** How much of a namespace a project subscribes to. */
|
|
104
|
-
export type NamespaceCoverage = "whole namespace" | "some recipes" | "none";
|
|
105
|
-
|
|
106
|
-
/** One namespace, as the listing shows it. */
|
|
107
|
-
export type NamespaceListing = {
|
|
108
|
-
/** The short name of the repository publishing it. */
|
|
109
|
-
repo: string;
|
|
110
|
-
/** The namespace name. */
|
|
111
|
-
namespace: string;
|
|
112
|
-
/** The namespace's one-paragraph summary, when its index carries one. */
|
|
113
|
-
description?: string;
|
|
114
|
-
/** How many recipes the repository publishes in it. */
|
|
115
|
-
recipeCount: number;
|
|
116
|
-
/** How much of it this project subscribes to. */
|
|
117
|
-
subscribed: NamespaceCoverage;
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
/** One recipe, as a listing shows it. */
|
|
121
|
-
export type RecipeListing = {
|
|
122
|
-
/** The recipe key, `namespace/recipe`. */
|
|
123
|
-
key: string;
|
|
124
|
-
namespace: string;
|
|
125
|
-
name: string;
|
|
126
|
-
/** The short name of the repository publishing it. */
|
|
127
|
-
repo: string;
|
|
128
|
-
/** The highest published version, prereleases considered only when nothing else is published. */
|
|
129
|
-
latest?: string;
|
|
130
|
-
/** The version this project's lockfile pins, when it pins one. */
|
|
131
|
-
pinned?: string;
|
|
132
|
-
/**
|
|
133
|
-
* The linked checkout builds read this recipe from instead of the pinned
|
|
134
|
-
* version, when the project pins it and its repository is linked.
|
|
135
|
-
*/
|
|
136
|
-
linkedPath?: string;
|
|
137
|
-
/** True when the project subscribes to this recipe, or to the whole namespace holding it. */
|
|
138
|
-
subscribed: boolean;
|
|
139
|
-
/** The recipe's one-paragraph summary, when its index carries one. */
|
|
140
|
-
description?: string;
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
/** One namespace in full: the namespace itself, and every recipe in it. */
|
|
144
|
-
export type NamespaceDetail = {
|
|
145
|
-
repo: string;
|
|
146
|
-
/** Where the repository lives, as the project's config records it. */
|
|
147
|
-
repoUrl?: string;
|
|
148
|
-
namespace: string;
|
|
149
|
-
description?: string;
|
|
150
|
-
subscribed: NamespaceCoverage;
|
|
151
|
-
/** Every recipe the repository publishes in the namespace, by key. */
|
|
152
|
-
recipes: RecipeListing[];
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
/** What one published version is to this project. */
|
|
156
|
-
export type VersionStatus = "latest" | "pinned" | "latest and pinned" | "other";
|
|
157
|
-
|
|
158
|
-
/** One published version of one recipe. */
|
|
159
|
-
export type RecipeVersionListing = {
|
|
160
|
-
/** The exact version. */
|
|
161
|
-
version: string;
|
|
162
|
-
/** What the version is to this project. */
|
|
163
|
-
status: VersionStatus;
|
|
164
|
-
/** True when the index marks it a prerelease. */
|
|
165
|
-
prerelease: boolean;
|
|
166
|
-
/** When it was released, when the index records it. */
|
|
167
|
-
releasedAt?: string;
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
/** One dependency of the version being described. */
|
|
171
|
-
export type RecipeDependencyListing = {
|
|
172
|
-
/** The recipe key the dependency names, `namespace/recipe`. */
|
|
173
|
-
key: string;
|
|
174
|
-
/**
|
|
175
|
-
* The manifest entry that brings it in, exactly as written (a recipe, a whole
|
|
176
|
-
* namespace, or a locator), from the index when it records one and from the
|
|
177
|
-
* manifest otherwise.
|
|
178
|
-
*/
|
|
179
|
-
declared?: string;
|
|
180
|
-
/** Whether that entry makes it a build dependency or a co-subscription. */
|
|
181
|
-
kind?: "depends" | "subscribes";
|
|
182
|
-
/** The exact version the release resolved it to, when the index records one. */
|
|
183
|
-
resolvedVersion?: string;
|
|
184
|
-
/** The range the index recorded instead, when it could not resolve an exact version. */
|
|
185
|
-
resolvedRange?: string;
|
|
186
|
-
/** The canonical identity of the repository publishing it, for a cross-repository dependency. */
|
|
187
|
-
repo?: string;
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
/** One content kind a recipe contributes, and where its files would land. */
|
|
191
|
-
export type RecipeContentListing = {
|
|
192
|
-
/** The content kind, as the manifest declares it. */
|
|
193
|
-
kind: ContentKind;
|
|
194
|
-
/** The glob patterns the manifest includes. */
|
|
195
|
-
include: string[];
|
|
196
|
-
/** Every directory in this project the files would be written into. */
|
|
197
|
-
destinations: string[];
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
/** One recipe in full. */
|
|
201
|
-
export type RecipeDetail = {
|
|
202
|
-
/** The recipe key, `namespace/recipe`. */
|
|
203
|
-
key: string;
|
|
204
|
-
namespace: string;
|
|
205
|
-
name: string;
|
|
206
|
-
/** The short name of the repository publishing it. */
|
|
207
|
-
repo: string;
|
|
208
|
-
/** Where that repository lives, as the project's config records it. */
|
|
209
|
-
repoUrl?: string;
|
|
210
|
-
/** The recipe's one-paragraph summary, when its index carries one. */
|
|
211
|
-
description?: string;
|
|
212
|
-
/** The recipe folder, relative to the repository root. */
|
|
213
|
-
path: string;
|
|
214
|
-
/** The highest published version. */
|
|
215
|
-
latest?: string;
|
|
216
|
-
/** The version this project's lockfile pins, when it pins one. */
|
|
217
|
-
pinned?: string;
|
|
218
|
-
/**
|
|
219
|
-
* The linked checkout builds read this recipe from instead of the pinned
|
|
220
|
-
* version, when the project pins it and its repository is linked.
|
|
221
|
-
*/
|
|
222
|
-
linkedPath?: string;
|
|
223
|
-
/** True when the project subscribes to this recipe, or to the whole namespace holding it. */
|
|
224
|
-
subscribed: boolean;
|
|
225
|
-
/**
|
|
226
|
-
* The version everything else here describes: the pinned one when the project
|
|
227
|
-
* pins one, and the latest published one otherwise.
|
|
228
|
-
*/
|
|
229
|
-
describing?: string;
|
|
230
|
-
/** Every published version, newest first. */
|
|
231
|
-
versions: RecipeVersionListing[];
|
|
232
|
-
/** What the described version depends on, by key. */
|
|
233
|
-
dependencies: RecipeDependencyListing[];
|
|
234
|
-
/**
|
|
235
|
-
* True when the index records the described version's dependencies, even as
|
|
236
|
-
* none at all, so an empty list means "depends on nothing" rather than "not
|
|
237
|
-
* recorded".
|
|
238
|
-
*/
|
|
239
|
-
dependenciesRecorded: boolean;
|
|
240
|
-
/** What the recipe contributes, and where each kind's files land. */
|
|
241
|
-
contents: RecipeContentListing[];
|
|
242
|
-
/**
|
|
243
|
-
* True when the recipe's own manifest could be read. When it is false, the
|
|
244
|
-
* contents are unknown rather than empty, because the recipe's files are not
|
|
245
|
-
* on this machine.
|
|
246
|
-
*/
|
|
247
|
-
manifestRead: boolean;
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
// --- Listings -----------------------------------------------------------------------------------
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Every namespace every trusted repository publishes, sorted by namespace and
|
|
254
|
-
* then by repository, so two repositories publishing the same namespace name sit
|
|
255
|
-
* beside each other.
|
|
256
|
-
*
|
|
257
|
-
* @param inputs - The cached indexes, the lockfile and the project's subscriptions.
|
|
258
|
-
*/
|
|
259
|
-
export function listNamespaces(inputs: CatalogInputs): NamespaceListing[] {
|
|
260
|
-
const subscriptions = new Set(inputs.subscriptions);
|
|
261
|
-
const listings: NamespaceListing[] = [];
|
|
262
|
-
|
|
263
|
-
for (const repo of inputs.repos) {
|
|
264
|
-
for (const [namespace, declared] of Object.entries(repo.index.namespaces)) {
|
|
265
|
-
listings.push({
|
|
266
|
-
repo: repo.name,
|
|
267
|
-
namespace,
|
|
268
|
-
...(declared.description === undefined ? {} : { description: declared.description }),
|
|
269
|
-
recipeCount: recipeKeysIn(repo.index, namespace).length,
|
|
270
|
-
subscribed: coverageOf(namespace, repo.index, subscriptions),
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
return listings.sort(byNamespaceThenRepo);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Every recipe every trusted repository publishes, sorted by key and then by
|
|
280
|
-
* repository.
|
|
281
|
-
*
|
|
282
|
-
* @param inputs - The cached indexes, the lockfile and the project's subscriptions.
|
|
283
|
-
*/
|
|
284
|
-
export function listRecipes(inputs: CatalogInputs): RecipeListing[] {
|
|
285
|
-
const subscriptions = new Set(inputs.subscriptions);
|
|
286
|
-
const listings: RecipeListing[] = [];
|
|
287
|
-
|
|
288
|
-
for (const repo of inputs.repos) {
|
|
289
|
-
for (const key of Object.keys(repo.index.recipes)) {
|
|
290
|
-
listings.push(recipeListing(key, repo, inputs, subscriptions));
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
return listings.sort((left, right) =>
|
|
295
|
-
left.key === right.key
|
|
296
|
-
? compare(left.repo, right.repo)
|
|
297
|
-
: compare(left.key, right.key)
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* One namespace in full: what the repository says about it, and every recipe it
|
|
303
|
-
* publishes in it.
|
|
304
|
-
*
|
|
305
|
-
* @param inputs - The cached indexes, the lockfile and the project's subscriptions.
|
|
306
|
-
* @param ref - The namespace, optionally qualified with `repo:`.
|
|
307
|
-
*/
|
|
308
|
-
export async function describeNamespace(
|
|
309
|
-
inputs: CatalogInputs,
|
|
310
|
-
ref: string
|
|
311
|
-
): Promise<NamespaceDetail> {
|
|
312
|
-
const found = await resolveNamespaceRef(inputs, ref);
|
|
313
|
-
const subscriptions = new Set(inputs.subscriptions);
|
|
314
|
-
const declared = found.repo.index.namespaces[found.namespace]!;
|
|
315
|
-
|
|
316
|
-
return {
|
|
317
|
-
repo: found.repo.name,
|
|
318
|
-
...(found.repo.url === undefined ? {} : { repoUrl: found.repo.url }),
|
|
319
|
-
namespace: found.namespace,
|
|
320
|
-
...(declared.description === undefined ? {} : { description: declared.description }),
|
|
321
|
-
subscribed: coverageOf(found.namespace, found.repo.index, subscriptions),
|
|
322
|
-
recipes: recipeKeysIn(found.repo.index, found.namespace).map((key) =>
|
|
323
|
-
recipeListing(key, found.repo, inputs, subscriptions)
|
|
324
|
-
),
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* One recipe in full: its identity, every version it publishes, what it depends
|
|
330
|
-
* on, what it asks about, and where its files would land in this project.
|
|
331
|
-
*
|
|
332
|
-
* @param inputs - The cached indexes, the lockfile, the subscriptions and the manifest reader.
|
|
333
|
-
* @param ref - The recipe, as `namespace/recipe`, a bare recipe name, or either qualified with `repo:`.
|
|
334
|
-
*/
|
|
335
|
-
export async function describeRecipe(
|
|
336
|
-
inputs: CatalogInputs,
|
|
337
|
-
ref: string
|
|
338
|
-
): Promise<RecipeDetail> {
|
|
339
|
-
const found = await resolveRecipeRef(inputs, ref);
|
|
340
|
-
const subscriptions = new Set(inputs.subscriptions);
|
|
341
|
-
const listing = recipeListing(found.key, found.repo, inputs, subscriptions);
|
|
342
|
-
const entry = found.repo.index.recipes[found.key]!;
|
|
343
|
-
|
|
344
|
-
const describing = listing.pinned ?? listing.latest;
|
|
345
|
-
const published = entry.versions[describing ?? ""];
|
|
346
|
-
|
|
347
|
-
const manifest =
|
|
348
|
-
describing === undefined || inputs.readManifest === undefined
|
|
349
|
-
? undefined
|
|
350
|
-
: inputs.readManifest({
|
|
351
|
-
key: found.key,
|
|
352
|
-
namespace: found.namespace,
|
|
353
|
-
name: found.name,
|
|
354
|
-
repo: found.repo.name,
|
|
355
|
-
version: describing,
|
|
356
|
-
path: entry.path,
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
return {
|
|
360
|
-
...listing,
|
|
361
|
-
...(found.repo.url === undefined ? {} : { repoUrl: found.repo.url }),
|
|
362
|
-
path: entry.path,
|
|
363
|
-
...(describing === undefined ? {} : { describing }),
|
|
364
|
-
versions: versionListings(entry.versions, listing.latest, listing.pinned),
|
|
365
|
-
dependencies: dependencyListings(published?.dependencies, manifest),
|
|
366
|
-
dependenciesRecorded: published?.dependencies !== undefined,
|
|
367
|
-
contents: (manifest?.contents ?? []).map((content) => ({
|
|
368
|
-
kind: content.kind,
|
|
369
|
-
include: [...content.include],
|
|
370
|
-
destinations: inputs.destinationsFor?.(content.kind) ?? [],
|
|
371
|
-
})),
|
|
372
|
-
manifestRead: manifest !== undefined,
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* The same inputs, narrowed to what this project has installed: each
|
|
378
|
-
* repository's index keeps only the recipes the lockfile pins from that
|
|
379
|
-
* repository, and only the namespaces holding one of them. Every listing and
|
|
380
|
-
* every ref lookup over the result therefore sees only installed recipes. A
|
|
381
|
-
* recipe's published version history is kept whole, so the installed version
|
|
382
|
-
* still sits among the others.
|
|
383
|
-
*
|
|
384
|
-
* narrowToInstalled(inputs)
|
|
385
|
-
* // -> the index of "r" holds "a/x" only, when the lockfile pins "a/x" from "r"
|
|
386
|
-
*
|
|
387
|
-
* @param inputs - The catalog's inputs.
|
|
388
|
-
*/
|
|
389
|
-
export function narrowToInstalled(inputs: CatalogInputs): CatalogInputs {
|
|
390
|
-
const repos = inputs.repos.map((repo) => {
|
|
391
|
-
const recipes = Object.fromEntries(
|
|
392
|
-
Object.entries(repo.index.recipes).filter(
|
|
393
|
-
([key]) => inputs.lock.recipes[key]?.repo === repo.name
|
|
394
|
-
)
|
|
395
|
-
);
|
|
396
|
-
const held = new Set(Object.keys(recipes).map(namespaceOfKey));
|
|
397
|
-
const namespaces = Object.fromEntries(
|
|
398
|
-
Object.entries(repo.index.namespaces).filter(([namespace]) => held.has(namespace))
|
|
399
|
-
);
|
|
400
|
-
return { ...repo, index: { ...repo.index, recipes, namespaces } };
|
|
401
|
-
});
|
|
402
|
-
return { ...inputs, repos };
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/**
|
|
406
|
-
* Looks a ref up among the installed recipes only, so an ambiguity between an
|
|
407
|
-
* installed recipe and one the project does not use settles itself. A ref that
|
|
408
|
-
* names something published but not installed is an error saying exactly that,
|
|
409
|
-
* rather than the "no repository publishes" error the narrowed lookup alone
|
|
410
|
-
* would raise.
|
|
411
|
-
*
|
|
412
|
-
* describeInstalled(inputs, "workflow/task-files", describeRecipe, "recipe")
|
|
413
|
-
* // -> the recipe, when the lockfile pins it; otherwise an error saying the
|
|
414
|
-
* // project has not installed it
|
|
415
|
-
*
|
|
416
|
-
* @param inputs - The catalog's inputs, not yet narrowed.
|
|
417
|
-
* @param ref - The ref being looked up.
|
|
418
|
-
* @param describe - The lookup to run: `describeNamespace` or `describeRecipe`.
|
|
419
|
-
* @param what - What the ref names, for the error.
|
|
420
|
-
*/
|
|
421
|
-
export async function describeInstalled<T>(
|
|
422
|
-
inputs: CatalogInputs,
|
|
423
|
-
ref: string,
|
|
424
|
-
describe: (inputs: CatalogInputs, ref: string) => Promise<T>,
|
|
425
|
-
what: "namespace" | "recipe"
|
|
426
|
-
): Promise<T> {
|
|
427
|
-
try {
|
|
428
|
-
return await describe(narrowToInstalled(inputs), ref);
|
|
429
|
-
} catch {
|
|
430
|
-
// The full lookup either explains the ref better (ambiguous, unknown) or
|
|
431
|
-
// proves it is published and simply not installed.
|
|
432
|
-
await describe(inputs, ref);
|
|
433
|
-
throw what === "namespace"
|
|
434
|
-
? new ConfigError(
|
|
435
|
-
`This project has installed no recipe from the namespace '${ref}', so there is ` +
|
|
436
|
-
`nothing to show with '--installed'.`
|
|
437
|
-
)
|
|
438
|
-
: new ConfigError(
|
|
439
|
-
`This project has not installed the recipe '${ref}', so there is nothing to ` +
|
|
440
|
-
`show with '--installed'.`
|
|
441
|
-
);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// --- Resolving a ref ----------------------------------------------------------------------------
|
|
446
|
-
|
|
447
|
-
/** One namespace a ref resolved to. */
|
|
448
|
-
export type ResolvedNamespace = {
|
|
449
|
-
/** The repository publishing it. */
|
|
450
|
-
repo: CatalogRepo;
|
|
451
|
-
/** The namespace name. */
|
|
452
|
-
namespace: string;
|
|
453
|
-
};
|
|
454
|
-
|
|
455
|
-
/** One recipe a ref resolved to. */
|
|
456
|
-
export type ResolvedRecipeRef = {
|
|
457
|
-
/** The repository publishing it. */
|
|
458
|
-
repo: CatalogRepo;
|
|
459
|
-
/** The recipe key, `namespace/recipe`. */
|
|
460
|
-
key: string;
|
|
461
|
-
namespace: string;
|
|
462
|
-
name: string;
|
|
463
|
-
};
|
|
464
|
-
|
|
465
|
-
/**
|
|
466
|
-
* The namespace a ref names. A one-word ref is searched across every trusted
|
|
467
|
-
* repository; a word that names a namespace in two of them is an error listing
|
|
468
|
-
* both.
|
|
469
|
-
*
|
|
470
|
-
* @param inputs - The cached indexes.
|
|
471
|
-
* @param ref - The namespace, optionally qualified with `repo:`.
|
|
472
|
-
*/
|
|
473
|
-
export async function resolveNamespaceRef(
|
|
474
|
-
inputs: CatalogInputs,
|
|
475
|
-
ref: string
|
|
476
|
-
): Promise<ResolvedNamespace> {
|
|
477
|
-
const parsed = readShortRef(ref);
|
|
478
|
-
|
|
479
|
-
if (parsed?.kind === "recipe") {
|
|
480
|
-
throw new ConfigError(
|
|
481
|
-
`'${ref}' names the recipe '${parsed.name}', not a namespace.\n` +
|
|
482
|
-
` The namespace it belongs to is '${parsed.namespace!.name}'.`
|
|
483
|
-
);
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
const matches = await findIn(inputs, ref, ["namespace"]);
|
|
487
|
-
|
|
488
|
-
if (matches.length === 1) {
|
|
489
|
-
const match = matches[0]!;
|
|
490
|
-
return { repo: repoNamed(inputs, repoOf(match)!.name!), namespace: namespaceOf(match)!.name };
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
if (matches.length > 1) throw ambiguousError(ref, "namespace", matches);
|
|
494
|
-
|
|
495
|
-
const asRecipe = await findIn(inputs, ref, ["recipe"]);
|
|
496
|
-
if (parsed !== undefined && repoOf(parsed) === undefined && asRecipe.length > 0) {
|
|
497
|
-
throw new ConfigError(
|
|
498
|
-
`No repository this project trusts publishes a namespace called ` +
|
|
499
|
-
`'${parsed.name}'.\n` +
|
|
500
|
-
` It is the name of a recipe:\n` +
|
|
501
|
-
asRecipe.map((candidate) => ` ${describeRef(candidate)}`).join("\n")
|
|
502
|
-
);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
throw unknownError(inputs, ref, "namespace", parsed === undefined ? undefined : repoOf(parsed)?.name);
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
/**
|
|
509
|
-
* The recipe a ref names. A two-segment ref names it exactly; a one-word ref is
|
|
510
|
-
* searched as a recipe name across every trusted repository, and a name two of
|
|
511
|
-
* them publish is an error listing both.
|
|
512
|
-
*
|
|
513
|
-
* @param inputs - The cached indexes.
|
|
514
|
-
* @param ref - The recipe, as `namespace/recipe`, a bare recipe name, or either qualified with `repo:`.
|
|
515
|
-
*/
|
|
516
|
-
export async function resolveRecipeRef(
|
|
517
|
-
inputs: CatalogInputs,
|
|
518
|
-
ref: string
|
|
519
|
-
): Promise<ResolvedRecipeRef> {
|
|
520
|
-
const parsed = readShortRef(ref);
|
|
521
|
-
|
|
522
|
-
const matches = await findIn(inputs, ref, ["recipe"]);
|
|
523
|
-
|
|
524
|
-
if (matches.length === 1) {
|
|
525
|
-
const match = matches[0]!;
|
|
526
|
-
const namespace = namespaceOf(match)!.name;
|
|
527
|
-
const name = (match as { name: string }).name;
|
|
528
|
-
return {
|
|
529
|
-
repo: repoNamed(inputs, repoOf(match)!.name!),
|
|
530
|
-
key: `${namespace}/${name}`,
|
|
531
|
-
namespace,
|
|
532
|
-
name,
|
|
533
|
-
};
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
if (matches.length > 1) throw ambiguousError(ref, "recipe", matches);
|
|
537
|
-
|
|
538
|
-
const asNamespace = await findIn(inputs, ref, ["namespace"]);
|
|
539
|
-
if (asNamespace.length > 0) {
|
|
540
|
-
throw new ConfigError(
|
|
541
|
-
`No repository this project trusts publishes a recipe called ` +
|
|
542
|
-
`'${parsed === undefined ? ref : namespaceNameOf(parsed)}'.\n` +
|
|
543
|
-
` It is the name of a namespace:\n` +
|
|
544
|
-
asNamespace.map((candidate) => ` ${describeRef(candidate)}`).join("\n")
|
|
545
|
-
);
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
throw unknownError(inputs, ref, "recipe", parsed === undefined ? undefined : repoOf(parsed)?.name);
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
// --- The pieces ---------------------------------------------------------------------------------
|
|
552
|
-
|
|
553
|
-
/** Every recipe key one index publishes in one namespace, sorted. */
|
|
554
|
-
function recipeKeysIn(index: IndexFile, namespace: string): string[] {
|
|
555
|
-
return Object.keys(index.recipes)
|
|
556
|
-
.filter((key) => namespaceOfKey(key) === namespace)
|
|
557
|
-
.sort();
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
/**
|
|
561
|
-
* How much of one namespace a project subscribes to: the whole namespace when it
|
|
562
|
-
* subscribes to the namespace itself, some recipes when it subscribes to at
|
|
563
|
-
* least one recipe in it, and none otherwise.
|
|
564
|
-
*
|
|
565
|
-
* @param namespace - The namespace name.
|
|
566
|
-
* @param index - The index publishing it.
|
|
567
|
-
* @param subscriptions - The ref keys the project subscribes to.
|
|
568
|
-
*/
|
|
569
|
-
function coverageOf(
|
|
570
|
-
namespace: string,
|
|
571
|
-
index: IndexFile,
|
|
572
|
-
subscriptions: Set<string>
|
|
573
|
-
): NamespaceCoverage {
|
|
574
|
-
if (subscriptions.has(namespace)) return "whole namespace";
|
|
575
|
-
const some = recipeKeysIn(index, namespace).some((key) => subscriptions.has(key));
|
|
576
|
-
return some ? "some recipes" : "none";
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
/**
|
|
580
|
-
* One recipe's listing row: what the index publishes, what the lockfile pins,
|
|
581
|
-
* and whether the project subscribes to it.
|
|
582
|
-
*
|
|
583
|
-
* @param key - The recipe key.
|
|
584
|
-
* @param repo - The repository publishing it.
|
|
585
|
-
* @param inputs - The lockfile and the linked repositories.
|
|
586
|
-
* @param subscriptions - The ref keys the project subscribes to.
|
|
587
|
-
*/
|
|
588
|
-
function recipeListing(
|
|
589
|
-
key: string,
|
|
590
|
-
repo: CatalogRepo,
|
|
591
|
-
inputs: Pick<CatalogInputs, "lock" | "linked">,
|
|
592
|
-
subscriptions: Set<string>
|
|
593
|
-
): RecipeListing {
|
|
594
|
-
const lock = inputs.lock;
|
|
595
|
-
const entry = repo.index.recipes[key]!;
|
|
596
|
-
const { namespace, name } = splitRecipeKey(key);
|
|
597
|
-
const latest = latestVersion(Object.keys(entry.versions), entry.versions);
|
|
598
|
-
|
|
599
|
-
// A locked entry pins one recipe from one repository. Two repositories can
|
|
600
|
-
// publish the same key, so the row only claims the pin when the lockfile says
|
|
601
|
-
// the recipe came from this repository.
|
|
602
|
-
const locked = lock.recipes[key];
|
|
603
|
-
const pinned = locked !== undefined && locked.repo === repo.name ? locked.version : undefined;
|
|
604
|
-
const linkedPath = pinned === undefined ? undefined : inputs.linked?.[repo.name];
|
|
605
|
-
|
|
606
|
-
return {
|
|
607
|
-
key,
|
|
608
|
-
namespace,
|
|
609
|
-
name,
|
|
610
|
-
repo: repo.name,
|
|
611
|
-
...(latest === undefined ? {} : { latest }),
|
|
612
|
-
...(pinned === undefined ? {} : { pinned }),
|
|
613
|
-
...(linkedPath === undefined ? {} : { linkedPath }),
|
|
614
|
-
subscribed: subscriptions.has(key) || subscriptions.has(namespace),
|
|
615
|
-
...(entry.description === undefined ? {} : { description: entry.description }),
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
/**
|
|
620
|
-
* The highest published version. Prereleases are considered only when a recipe
|
|
621
|
-
* has published nothing else, so a repository mid-prerelease still reports a
|
|
622
|
-
* latest version rather than none.
|
|
623
|
-
*
|
|
624
|
-
* @param versions - Every published version string.
|
|
625
|
-
* @param published - The index entries those versions carry.
|
|
626
|
-
*/
|
|
627
|
-
function latestVersion(
|
|
628
|
-
versions: string[],
|
|
629
|
-
published: Record<string, { prerelease: boolean }>
|
|
630
|
-
): string | undefined {
|
|
631
|
-
const stable = versions.filter((version) => published[version]?.prerelease !== true);
|
|
632
|
-
const pool = stable.length > 0 ? stable : versions;
|
|
633
|
-
return semver.maxSatisfying(pool, "*", { includePrerelease: true }) ?? undefined;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
/**
|
|
637
|
-
* Every published version, newest first, each labeled with what it is to this
|
|
638
|
-
* project.
|
|
639
|
-
*
|
|
640
|
-
* @param versions - The index's version entries.
|
|
641
|
-
* @param latest - The highest published version.
|
|
642
|
-
* @param pinned - The version the lockfile pins, when it pins one.
|
|
643
|
-
*/
|
|
644
|
-
function versionListings(
|
|
645
|
-
versions: Record<string, { prerelease: boolean; releasedAt?: string }>,
|
|
646
|
-
latest: string | undefined,
|
|
647
|
-
pinned: string | undefined
|
|
648
|
-
): RecipeVersionListing[] {
|
|
649
|
-
return Object.keys(versions)
|
|
650
|
-
.sort((left, right) => semver.rcompare(left, right, { loose: true }))
|
|
651
|
-
.map((version) => {
|
|
652
|
-
const entry = versions[version]!;
|
|
653
|
-
const isLatest = version === latest;
|
|
654
|
-
const isPinned = version === pinned;
|
|
655
|
-
const status: VersionStatus =
|
|
656
|
-
isLatest && isPinned
|
|
657
|
-
? "latest and pinned"
|
|
658
|
-
: isLatest
|
|
659
|
-
? "latest"
|
|
660
|
-
: isPinned
|
|
661
|
-
? "pinned"
|
|
662
|
-
: "other";
|
|
663
|
-
return {
|
|
664
|
-
version,
|
|
665
|
-
status,
|
|
666
|
-
prerelease: entry.prerelease,
|
|
667
|
-
...(entry.releasedAt === undefined ? {} : { releasedAt: entry.releasedAt }),
|
|
668
|
-
};
|
|
669
|
-
});
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
/**
|
|
673
|
-
* What one version depends on, from both sides: the dependencies the recipe's
|
|
674
|
-
* manifest declares, and the versions the release resolved them to in the
|
|
675
|
-
* index. A dependency that appears on only one side is still a row, because the
|
|
676
|
-
* two disagreeing is exactly what somebody reading this needs to see.
|
|
677
|
-
*
|
|
678
|
-
* @param resolved - The index's record of what the version was released against.
|
|
679
|
-
* @param manifest - The recipe's manifest, when its files could be read.
|
|
680
|
-
*/
|
|
681
|
-
function dependencyListings(
|
|
682
|
-
resolved: Record<string, IndexDependency> | undefined,
|
|
683
|
-
manifest: RecipeManifest | undefined
|
|
684
|
-
): RecipeDependencyListing[] {
|
|
685
|
-
const rows = new Map<string, RecipeDependencyListing>();
|
|
686
|
-
const lists: DependencyLists | undefined =
|
|
687
|
-
manifest === undefined
|
|
688
|
-
? undefined
|
|
689
|
-
: { depends: manifest.depends ?? [], subscribes: manifest.subscribes ?? [] };
|
|
690
|
-
|
|
691
|
-
for (const [key, entry] of Object.entries(resolved ?? {})) {
|
|
692
|
-
// The index says how the release that published the version saw it. An
|
|
693
|
-
// entry recorded before the index said so is answered from the manifest,
|
|
694
|
-
// by the same rule the release applies.
|
|
695
|
-
const declaration =
|
|
696
|
-
entry.declared !== undefined && entry.kind !== undefined
|
|
697
|
-
? { declared: entry.declared, kind: entry.kind }
|
|
698
|
-
: lists === undefined
|
|
699
|
-
? undefined
|
|
700
|
-
: declarationFor(lists, key, resolved);
|
|
701
|
-
rows.set(key, {
|
|
702
|
-
key,
|
|
703
|
-
...(entry.version === undefined ? {} : { resolvedVersion: entry.version }),
|
|
704
|
-
...(entry.range === undefined ? {} : { resolvedRange: entry.range }),
|
|
705
|
-
...(entry.repo === undefined ? {} : { repo: entry.repo }),
|
|
706
|
-
...(declaration ?? {}),
|
|
707
|
-
});
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
// A manifest entry that covers no row the index recorded is still shown, so a
|
|
711
|
-
// reader sees everything the manifest declares.
|
|
712
|
-
const covered = new Set([...rows.values()].map((row) => row.declared));
|
|
713
|
-
for (const [kind, declared] of [
|
|
714
|
-
["depends", lists?.depends ?? []],
|
|
715
|
-
["subscribes", lists?.subscribes ?? []],
|
|
716
|
-
] as Array<["depends" | "subscribes", string[]]>) {
|
|
717
|
-
for (const written of declared) {
|
|
718
|
-
if (covered.has(written.trim())) continue;
|
|
719
|
-
// A dependency that reads more than one way is listed under the key the
|
|
720
|
-
// index recorded for it. One sous cannot parse or settle is still worth
|
|
721
|
-
// showing; it is listed under what it was written as, so the reader sees
|
|
722
|
-
// the entry as it stands.
|
|
723
|
-
let key = written;
|
|
724
|
-
try {
|
|
725
|
-
const reading = settleDependency(written, {
|
|
726
|
-
...(resolved === undefined ? {} : { recorded: resolved }),
|
|
727
|
-
});
|
|
728
|
-
if (reading !== undefined && isNamedRef(reading)) key = shortKey(reading);
|
|
729
|
-
} catch {
|
|
730
|
-
key = written;
|
|
731
|
-
}
|
|
732
|
-
rows.set(key, { ...(rows.get(key) ?? { key }), declared: written, kind });
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
return [...rows.values()].sort((left, right) => compare(left.key, right.key));
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
/**
|
|
740
|
-
* The repository a match named. Every match comes from this catalog's own
|
|
741
|
-
* repositories, so the lookup always finds one.
|
|
742
|
-
*
|
|
743
|
-
* @param inputs - The cached indexes.
|
|
744
|
-
* @param name - The repository's short name.
|
|
745
|
-
*/
|
|
746
|
-
function repoNamed(inputs: CatalogInputs, name: string): CatalogRepo {
|
|
747
|
-
return inputs.repos.find((repo) => repo.name === name)!;
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
/**
|
|
751
|
-
* Every ref in this catalog's repositories that a ref written on the command
|
|
752
|
-
* line names, of the kinds given, best match first.
|
|
753
|
-
*
|
|
754
|
-
* @param inputs - The cached indexes.
|
|
755
|
-
* @param ref - The ref as typed.
|
|
756
|
-
* @param kinds - The kinds of ref the caller accepts.
|
|
757
|
-
*/
|
|
758
|
-
async function findIn(
|
|
759
|
-
inputs: CatalogInputs,
|
|
760
|
-
ref: string,
|
|
761
|
-
kinds: readonly RefKind[]
|
|
762
|
-
): Promise<SousRef[]> {
|
|
763
|
-
const lookup = new CachedIndexLookup(
|
|
764
|
-
new Map(inputs.repos.map((repo) => [repo.name, repo.index])),
|
|
765
|
-
{
|
|
766
|
-
order: inputs.repos.map((repo) => repo.name),
|
|
767
|
-
urls: Object.fromEntries(inputs.repos.map((repo) => [repo.name, repo.url])),
|
|
768
|
-
}
|
|
769
|
-
);
|
|
770
|
-
const result = await sharedRefResolver().resolve(new RefResolveArguments({ input: ref, lookup, kinds }));
|
|
771
|
-
return result.refs;
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
/**
|
|
775
|
-
* The namespace or recipe a ref from the command line names by its short form,
|
|
776
|
-
* checked by the one parser: undefined when the ref names a location instead.
|
|
777
|
-
*
|
|
778
|
-
* @param ref - The ref as typed.
|
|
779
|
-
*/
|
|
780
|
-
function readShortRef(ref: string): NamedRef | undefined {
|
|
781
|
-
const { refs } = sharedRefResolver().parse(ref);
|
|
782
|
-
return refs.find(
|
|
783
|
-
(candidate): candidate is NamedRef => isNamedRef(candidate) && locationOf(candidate) === undefined
|
|
784
|
-
);
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
/** The name a short ref was written with: its namespace for a namespace, its recipe name otherwise. */
|
|
788
|
-
function namespaceNameOf(ref: NamedRef): string {
|
|
789
|
-
return ref.kind === "namespace" ? ref.name : ref.namespace!.name;
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
/** The error a ref that could have meant several things raises. */
|
|
793
|
-
function ambiguousError(
|
|
794
|
-
ref: string,
|
|
795
|
-
what: "namespace" | "recipe",
|
|
796
|
-
candidates: SousRef[]
|
|
797
|
-
): ConfigError {
|
|
798
|
-
return new ConfigError(
|
|
799
|
-
`'${ref}' names a ${what} in more than one repository this project trusts:\n` +
|
|
800
|
-
candidates.map((candidate) => ` ${describeRef(candidate)}`).join("\n") +
|
|
801
|
-
`\n Name the repository as well, as 'repository:${ref}', to say which one you mean.`
|
|
802
|
-
);
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
/** The error a ref that matched nothing raises, naming what was searched. */
|
|
806
|
-
function unknownError(
|
|
807
|
-
inputs: CatalogInputs,
|
|
808
|
-
ref: string,
|
|
809
|
-
what: "namespace" | "recipe",
|
|
810
|
-
qualifier: string | undefined
|
|
811
|
-
): ConfigError {
|
|
812
|
-
if (qualifier !== undefined && !inputs.repos.some((repo) => repo.name === qualifier)) {
|
|
813
|
-
return new ConfigError(
|
|
814
|
-
`This project trusts no repository called '${qualifier}', so '${ref}' could not ` +
|
|
815
|
-
`be looked up.\n` +
|
|
816
|
-
` Repositories with an index sous has read: ${describeRepos(inputs)}.`
|
|
817
|
-
);
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
return new ConfigError(
|
|
821
|
-
`No repository this project trusts publishes a ${what} called '${ref}'.\n` +
|
|
822
|
-
` Repositories with an index sous has read: ${describeRepos(inputs)}.`
|
|
823
|
-
);
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
/** The repositories that were searched, in plain language. */
|
|
827
|
-
function describeRepos(inputs: CatalogInputs): string {
|
|
828
|
-
if (inputs.repos.length === 0) return "none";
|
|
829
|
-
return inputs.repos.map((repo) => repo.name).join(", ");
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
/** Sorts namespace listings by namespace, then by repository. */
|
|
833
|
-
function byNamespaceThenRepo(left: NamespaceListing, right: NamespaceListing): number {
|
|
834
|
-
return left.namespace === right.namespace
|
|
835
|
-
? compare(left.repo, right.repo)
|
|
836
|
-
: compare(left.namespace, right.namespace);
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
/** Bytewise string ordering, so a listing sorts the same on every machine. */
|
|
840
|
-
function compare(left: string, right: string): number {
|
|
841
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
842
|
-
}
|