@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
|
@@ -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
|
-
}
|