@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,98 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The links map: `sous.links.json`.
|
|
3
|
-
*
|
|
4
|
-
* A link redirects a repo's resolution away from the store and at a real
|
|
5
|
-
* working copy on disk, which is how a maintainer edits recipes: edits happen
|
|
6
|
-
* in a checkout, never in the store. `sous repo link` writes an entry;
|
|
7
|
-
* `sous repo unlink` removes it and leaves the checkout in place unless asked
|
|
8
|
-
* to delete one sous cloned.
|
|
9
|
-
*
|
|
10
|
-
* Two maps are read: the project's `.sous/sous.links.json` and the machine-wide
|
|
11
|
-
* `$SOUS_HOME/sous.links.json`, with the project map winning on conflict. The
|
|
12
|
-
* file is MACHINE-WRITTEN and machine-local; it is never committed, because a
|
|
13
|
-
* link bypasses versions, the lockfile and freshness checks, and those bypasses
|
|
14
|
-
* belong to one person's machine rather than to the team.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { z } from "zod";
|
|
18
|
-
import {
|
|
19
|
-
absolutePathSchema,
|
|
20
|
-
formatVersionSchema,
|
|
21
|
-
isoTimestampSchema,
|
|
22
|
-
parseFormat,
|
|
23
|
-
repoNameSchema,
|
|
24
|
-
stableJsonStringify,
|
|
25
|
-
} from "./common.js";
|
|
26
|
-
|
|
27
|
-
/** How the linked working copy came to exist. */
|
|
28
|
-
export const LINK_ORIGINS = ["clone", "path"] as const;
|
|
29
|
-
|
|
30
|
-
/** One linked repo. */
|
|
31
|
-
export const repoLinkSchema = z.strictObject({
|
|
32
|
-
/** Absolute path to the working copy sous reads instead of the store. */
|
|
33
|
-
path: absolutePathSchema,
|
|
34
|
-
/** When the link was created. */
|
|
35
|
-
linkedAt: isoTimestampSchema,
|
|
36
|
-
/**
|
|
37
|
-
* Whether sous cloned the working copy itself ('clone') or was pointed at an
|
|
38
|
-
* existing checkout ('path'). Unlinking leaves either in place unless
|
|
39
|
-
* `--remove` is passed, and even then only a 'clone' is ever deleted: a
|
|
40
|
-
* 'path' checkout belongs to whoever linked it.
|
|
41
|
-
*/
|
|
42
|
-
origin: z.enum(LINK_ORIGINS),
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
/** The links map schema. */
|
|
46
|
-
export const linksMapSchema = z.strictObject({
|
|
47
|
-
formatVersion: formatVersionSchema,
|
|
48
|
-
/** Every linked repo, keyed by the repo's configured short name. */
|
|
49
|
-
links: z.record(repoNameSchema, repoLinkSchema),
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
/** A validated links map. */
|
|
53
|
-
export type LinksMap = z.infer<typeof linksMapSchema>;
|
|
54
|
-
|
|
55
|
-
/** One validated link entry. */
|
|
56
|
-
export type RepoLink = z.infer<typeof repoLinkSchema>;
|
|
57
|
-
|
|
58
|
-
/** How the linked working copy came to exist. */
|
|
59
|
-
export type LinkOrigin = (typeof LINK_ORIGINS)[number];
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Validates a parsed links map, throwing a ConfigError that names the file and
|
|
63
|
-
* the path of every bad field.
|
|
64
|
-
*
|
|
65
|
-
* @param value - The parsed contents of the links file.
|
|
66
|
-
* @param sourceLabel - The links file's path, named in error messages.
|
|
67
|
-
*/
|
|
68
|
-
export function parseLinksMap(value: unknown, sourceLabel: string): LinksMap {
|
|
69
|
-
return parseFormat(linksMapSchema, value, sourceLabel, "links map");
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** An empty links map, for a project or machine with nothing linked. */
|
|
73
|
-
export function createEmptyLinksMap(): LinksMap {
|
|
74
|
-
return { formatVersion: 1, links: {} };
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Serializes a links map for writing, with keys sorted.
|
|
79
|
-
*
|
|
80
|
-
* @param map - The links map to write.
|
|
81
|
-
*/
|
|
82
|
-
export function stringifyLinksMap(map: LinksMap): string {
|
|
83
|
-
return stableJsonStringify(map);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Merges a machine-wide links map with a project's, with the project's entries
|
|
88
|
-
* winning, which is the precedence `sous repo link` documents.
|
|
89
|
-
*
|
|
90
|
-
* @param global - The machine-wide map, or undefined when there is none.
|
|
91
|
-
* @param project - The project's map, or undefined when there is none.
|
|
92
|
-
*/
|
|
93
|
-
export function mergeLinksMaps(
|
|
94
|
-
global: LinksMap | undefined,
|
|
95
|
-
project: LinksMap | undefined
|
|
96
|
-
): Record<string, RepoLink> {
|
|
97
|
-
return { ...(global?.links ?? {}), ...(project?.links ?? {}) };
|
|
98
|
-
}
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The project lockfile: `.sous/sous.lock.json`, committed to the project.
|
|
3
|
-
*
|
|
4
|
-
* The lockfile is MACHINE-WRITTEN and records the exact version and content
|
|
5
|
-
* hash of everything a project currently uses, so a fresh clone restores
|
|
6
|
-
* deterministically with no prompts and no version drift. Together with repo
|
|
7
|
-
* trust it is the supply-chain defense: nothing new enters a project except
|
|
8
|
-
* through an explicit, visible change to these files.
|
|
9
|
-
*
|
|
10
|
-
* A repository appears twice over: the project's own short name is the key of
|
|
11
|
-
* the `repos` map and is what every recipe entry and every message names, while
|
|
12
|
-
* the entry's `identity` is what the machine-wide store is keyed by.
|
|
13
|
-
*
|
|
14
|
-
* `requestedBy` is what makes removal safe. Every entry lists who holds it, the
|
|
15
|
-
* literal string `project` for something the project subscribed to directly and
|
|
16
|
-
* a recipe key for something pulled in as a dependency. Unsubscribing removes
|
|
17
|
-
* one holder; the entry itself goes only when the last holder does.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { z } from "zod";
|
|
21
|
-
import { repoIdentity } from "../identity.js";
|
|
22
|
-
import { requireProvider } from "../providers/index.js";
|
|
23
|
-
import {
|
|
24
|
-
contentHashSchema,
|
|
25
|
-
formatVersionSchema,
|
|
26
|
-
parseFormat,
|
|
27
|
-
recipeKeySchema,
|
|
28
|
-
repoIdentitySchema,
|
|
29
|
-
repoNameSchema,
|
|
30
|
-
repoUrlSchema,
|
|
31
|
-
semverVersionSchema,
|
|
32
|
-
stableJsonStringify,
|
|
33
|
-
DEPENDENCY_KINDS,
|
|
34
|
-
type DependencyKind,
|
|
35
|
-
} from "./common.js";
|
|
36
|
-
|
|
37
|
-
/** The literal `requestedBy` holder meaning "the project subscribed to this directly". */
|
|
38
|
-
export const PROJECT_HOLDER = "project";
|
|
39
|
-
|
|
40
|
-
/** How a locked recipe entered the project: the dependency kinds every format shares. */
|
|
41
|
-
export const LOCK_KINDS = DEPENDENCY_KINDS;
|
|
42
|
-
|
|
43
|
-
/** One repo the project resolves against. */
|
|
44
|
-
export const lockedRepoSchema = z
|
|
45
|
-
.strictObject({
|
|
46
|
-
/**
|
|
47
|
-
* Where the repo lives, as recorded when it was added: a URL, or an absolute
|
|
48
|
-
* path for a repository on this machine read through the `local` provider.
|
|
49
|
-
*/
|
|
50
|
-
url: repoUrlSchema,
|
|
51
|
-
/**
|
|
52
|
-
* The repository's canonical identity, derived from that URL. The keys of
|
|
53
|
-
* `repos` are the project's own short names, which no other project has to
|
|
54
|
-
* agree with; this is what the machine-wide store and the index cache file
|
|
55
|
-
* the repository under, so a restore finds the same cached copy every other
|
|
56
|
-
* project uses.
|
|
57
|
-
*
|
|
58
|
-
* Optional ON READ only, for lockfiles written before the store was keyed by
|
|
59
|
-
* identity: an entry without one has its identity derived from `url` below,
|
|
60
|
-
* exactly as the writer would have derived it. Every lockfile sous writes
|
|
61
|
-
* carries it, so the field fills itself in on the next write.
|
|
62
|
-
*/
|
|
63
|
-
identity: repoIdentitySchema.optional(),
|
|
64
|
-
/** Content hash of the index this lock was resolved against, when known. */
|
|
65
|
-
indexHash: contentHashSchema.optional(),
|
|
66
|
-
})
|
|
67
|
-
.transform((entry, ctx) => {
|
|
68
|
-
if (entry.identity !== undefined) return { ...entry, identity: entry.identity };
|
|
69
|
-
|
|
70
|
-
// An older lockfile recorded only the URL. Deriving the identity through the
|
|
71
|
-
// provider that handles that URL is what the writer itself does, so the
|
|
72
|
-
// answer is the one the entry would have carried had it been written today.
|
|
73
|
-
try {
|
|
74
|
-
const identity = repoIdentity(requireProvider(entry.url).canonicalize(entry.url));
|
|
75
|
-
return { ...entry, identity };
|
|
76
|
-
} catch {
|
|
77
|
-
ctx.addIssue({
|
|
78
|
-
code: "custom",
|
|
79
|
-
path: ["identity"],
|
|
80
|
-
message:
|
|
81
|
-
`is missing, and sous could not work one out from the url '${entry.url}' ` +
|
|
82
|
-
`because no provider recognizes it. Add an 'identity' to this entry, or ` +
|
|
83
|
-
`remove the lockfile and subscribe again to have sous rebuild it.`,
|
|
84
|
-
});
|
|
85
|
-
return z.NEVER;
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
/** One locked recipe. */
|
|
90
|
-
export const lockedRecipeSchema = z.strictObject({
|
|
91
|
-
/** The short name of the repo it came from; must appear under `repos`. */
|
|
92
|
-
repo: repoNameSchema,
|
|
93
|
-
/** The exact version resolved. */
|
|
94
|
-
version: semverVersionSchema,
|
|
95
|
-
/** Content hash of that version, verified against the store after every fetch. */
|
|
96
|
-
hash: contentHashSchema,
|
|
97
|
-
/**
|
|
98
|
-
* Who holds this entry: `project` for a direct subscription, or the ref key of
|
|
99
|
-
* a recipe that requires it. Used to refcount removal.
|
|
100
|
-
*/
|
|
101
|
-
requestedBy: z
|
|
102
|
-
.array(z.string().min(1, "must not be empty"))
|
|
103
|
-
.min(1, "must name at least one holder"),
|
|
104
|
-
/** Whether the holder relationship is a co-subscription or a build dependency. */
|
|
105
|
-
kind: z.enum(LOCK_KINDS),
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
/** The lockfile schema. */
|
|
109
|
-
export const lockfileSchema = z
|
|
110
|
-
.strictObject({
|
|
111
|
-
formatVersion: formatVersionSchema,
|
|
112
|
-
/** Every repo the locked recipes came from, keyed by short name. */
|
|
113
|
-
repos: z.record(repoNameSchema, lockedRepoSchema),
|
|
114
|
-
/** Every locked recipe, keyed `namespace/recipe`. */
|
|
115
|
-
recipes: z.record(recipeKeySchema, lockedRecipeSchema),
|
|
116
|
-
})
|
|
117
|
-
.superRefine((lock, ctx) => {
|
|
118
|
-
// A recipe pointing at a repo the lockfile does not describe cannot be
|
|
119
|
-
// restored, so name the pair rather than failing later at fetch time.
|
|
120
|
-
for (const [key, entry] of Object.entries(lock.recipes)) {
|
|
121
|
-
if (!Object.hasOwn(lock.repos, entry.repo)) {
|
|
122
|
-
ctx.addIssue({
|
|
123
|
-
code: "custom",
|
|
124
|
-
path: ["recipes", key, "repo"],
|
|
125
|
-
message:
|
|
126
|
-
`names the repo '${entry.repo}', which this lockfile does not describe ` +
|
|
127
|
-
`under 'repos'`,
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
/** A validated lockfile. */
|
|
134
|
-
export type Lockfile = z.infer<typeof lockfileSchema>;
|
|
135
|
-
|
|
136
|
-
/** One locked repo entry. */
|
|
137
|
-
export type LockedRepo = z.infer<typeof lockedRepoSchema>;
|
|
138
|
-
|
|
139
|
-
/** One locked recipe entry. */
|
|
140
|
-
export type LockedRecipe = z.infer<typeof lockedRecipeSchema>;
|
|
141
|
-
|
|
142
|
-
/** How a locked recipe entered the project. */
|
|
143
|
-
export type LockKind = DependencyKind;
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Validates a parsed lockfile, throwing a ConfigError that names the file and
|
|
147
|
-
* the path of every bad field.
|
|
148
|
-
*
|
|
149
|
-
* @param value - The parsed contents of the lockfile.
|
|
150
|
-
* @param sourceLabel - The lockfile's path, named in error messages.
|
|
151
|
-
*/
|
|
152
|
-
export function parseLockfile(value: unknown, sourceLabel: string): Lockfile {
|
|
153
|
-
return parseFormat(lockfileSchema, value, sourceLabel, "lockfile");
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/** An empty lockfile, for a project that has locked nothing yet. */
|
|
157
|
-
export function createEmptyLockfile(): Lockfile {
|
|
158
|
-
return { formatVersion: 1, repos: {}, recipes: {} };
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Serializes a lockfile for writing, with every object key sorted so the
|
|
163
|
-
* committed file changes only when its content genuinely does.
|
|
164
|
-
*
|
|
165
|
-
* @param lockfile - The lockfile to write.
|
|
166
|
-
*/
|
|
167
|
-
export function stringifyLockfile(lockfile: Lockfile): string {
|
|
168
|
-
return stableJsonStringify(lockfile);
|
|
169
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dependency-free regular expressions shared by the Repositories on-disk
|
|
3
|
-
* formats, the ref parser and the sous config schema.
|
|
4
|
-
*
|
|
5
|
-
* This module deliberately imports nothing, so `config-schema.ts` can reuse the
|
|
6
|
-
* patterns without pulling in zod schemas, semver, or the rest of the repos
|
|
7
|
-
* layer. `formats/common.ts` re-exports everything here.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Lowercase kebab-case identifier: starts with a letter, then letters, digits
|
|
12
|
-
* or hyphens. Used for repo short names, namespace names and recipe names
|
|
13
|
-
* (`sous-recipes`, `tool-usage`, `automated-browser-tasks`).
|
|
14
|
-
*/
|
|
15
|
-
export const KEBAB_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
16
|
-
|
|
17
|
-
/** A repo's configured short name, as used by the `repo:` ref qualifier. */
|
|
18
|
-
export const REPO_NAME_PATTERN = KEBAB_NAME_PATTERN;
|
|
19
|
-
|
|
20
|
-
/** A namespace name. */
|
|
21
|
-
export const NAMESPACE_NAME_PATTERN = KEBAB_NAME_PATTERN;
|
|
22
|
-
|
|
23
|
-
/** A recipe name, unique within its namespace. */
|
|
24
|
-
export const RECIPE_NAME_PATTERN = KEBAB_NAME_PATTERN;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* A ref key: either a bare namespace (`workflow`) or a fully qualified recipe
|
|
28
|
-
* (`workflow/task-files`). Never carries a repo qualifier or a version range.
|
|
29
|
-
*/
|
|
30
|
-
export const REF_KEY_PATTERN = /^[a-z][a-z0-9-]*(\/[a-z][a-z0-9-]*)?$/;
|
|
31
|
-
|
|
32
|
-
/** A recipe key, which always has both segments (`workflow/task-files`). */
|
|
33
|
-
export const RECIPE_KEY_PATTERN = /^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* A repository's canonical identity: the host, then the path it lives at, all
|
|
37
|
-
* lowercase (`github.com/sous-io/sous-recipes`). It is what every machine-wide
|
|
38
|
-
* key uses, because a project's short name for a repository is its own label
|
|
39
|
-
* and no other project has to agree with it.
|
|
40
|
-
*/
|
|
41
|
-
export const REPO_IDENTITY_PATTERN = /^[^\s/]+(\/[^\s/]+)+$/;
|
|
42
|
-
|
|
43
|
-
/** A content hash, written as the algorithm name followed by lowercase hex. */
|
|
44
|
-
export const CONTENT_HASH_PATTERN = /^sha256-[0-9a-f]{64}$/;
|
|
45
|
-
|
|
46
|
-
/** An environment variable name, as declared by a recipe variable definition. */
|
|
47
|
-
export const ENV_VAR_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/;
|
|
48
|
-
|
|
49
|
-
/** A camelCase variable name, as declared by a recipe variable definition. */
|
|
50
|
-
export const VARIABLE_NAME_PATTERN = /^[a-z][a-zA-Z0-9]*$/;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* An ISO 8601 timestamp carrying an explicit offset (`Z` or `+hh:mm`). Machine
|
|
54
|
-
* written timestamps come from `new Date().toISOString()`, which matches.
|
|
55
|
-
*/
|
|
56
|
-
export const ISO_TIMESTAMP_PATTERN =
|
|
57
|
-
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?(Z|[+-]\d{2}:\d{2})$/;
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The recipe manifest: `sous.recipe.yaml` (or `.yml`, or `.json`) in each
|
|
3
|
-
* recipe folder. It is HAND-WRITTEN and is the complete, executable-free
|
|
4
|
-
* description of one publishable unit: what it is called, what version it is,
|
|
5
|
-
* what it depends on, what files it contributes, and what variables it needs
|
|
6
|
-
* answered.
|
|
7
|
-
*
|
|
8
|
-
* Two dependency lists exist, and they mean different things:
|
|
9
|
-
*
|
|
10
|
-
* - `depends` is a build dependency. The target is fetched, pinned and trust
|
|
11
|
-
* gated, and is addressable from this recipe's own files, but its files do
|
|
12
|
-
* NOT enter the subscribing project's output.
|
|
13
|
-
* - `subscribes` is a co-subscription. Subscribing to this recipe subscribes
|
|
14
|
-
* the project to the target too, with full semantics: its questions run and
|
|
15
|
-
* its files DO enter the output. A curated bundle is simply a recipe made
|
|
16
|
-
* mostly of `subscribes` entries.
|
|
17
|
-
*
|
|
18
|
-
* Both lists name their targets BY LOCATION:
|
|
19
|
-
*
|
|
20
|
-
* workflow/sat a sibling in this repository
|
|
21
|
-
* github://sous-io/sous-recipes/workflow/sat a recipe in another one
|
|
22
|
-
*
|
|
23
|
-
* Every other location form works too (an HTTPS or SSH URL, a browser URL); a
|
|
24
|
-
* short name such as `sous-recipes:` is a consuming project's own label, so it
|
|
25
|
-
* never appears in a published manifest. See `RefResolverService.parse` with
|
|
26
|
-
* `RefSource.Manifest`.
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
import { z } from "zod";
|
|
30
|
-
import {
|
|
31
|
-
extensibleObject,
|
|
32
|
-
formatVersionSchema,
|
|
33
|
-
namespaceNameSchema,
|
|
34
|
-
parseFormat,
|
|
35
|
-
recipeNameSchema,
|
|
36
|
-
relativePathSchema,
|
|
37
|
-
semverVersionSchema,
|
|
38
|
-
submissionsSchema,
|
|
39
|
-
} from "./common.js";
|
|
40
|
-
import { RefSource, sharedRefResolver } from "../../../services/ref-resolver/index.js";
|
|
41
|
-
import { variableDefinitionSchema } from "./variable-definition.js";
|
|
42
|
-
|
|
43
|
-
// Variable definitions live in their own module, so the index can read them
|
|
44
|
-
// without importing the ref parser this manifest needs; they are re-exported
|
|
45
|
-
// here because a definition is part of the manifest.
|
|
46
|
-
export * from "./variable-definition.js";
|
|
47
|
-
|
|
48
|
-
// --- Dependency refs ----------------------------------------------------------------------------
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* A dependency in `depends` or `subscribes`: a bare ref naming a recipe in this
|
|
52
|
-
* same repository, or a location naming one in another repository. Parsed with the real parser so
|
|
53
|
-
* a manifest and the rest of sous never disagree about what a dependency means;
|
|
54
|
-
* the parser's message is carried through as the zod issue message.
|
|
55
|
-
*/
|
|
56
|
-
const dependencyRefSchema = z.string().superRefine((value, ctx) => {
|
|
57
|
-
try {
|
|
58
|
-
sharedRefResolver().parse(value, RefSource.Manifest);
|
|
59
|
-
} catch (error) {
|
|
60
|
-
ctx.addIssue({ code: "custom", message: (error as Error).message });
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
/** Builds a list-of-refs schema that also rejects the same target listed twice. */
|
|
65
|
-
function refListSchema(label: string) {
|
|
66
|
-
return z.array(dependencyRefSchema).superRefine((refs, ctx) => {
|
|
67
|
-
const seen = new Set<string>();
|
|
68
|
-
refs.forEach((entry, index) => {
|
|
69
|
-
const trimmed = entry.trim();
|
|
70
|
-
if (seen.has(trimmed)) {
|
|
71
|
-
ctx.addIssue({
|
|
72
|
-
code: "custom",
|
|
73
|
-
path: [index],
|
|
74
|
-
message: `'${trimmed}' is listed more than once in ${label}`,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
seen.add(trimmed);
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// --- Contents -----------------------------------------------------------------------------------
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* What a content entry contributes. `skills`, `memories` and `prompts` are
|
|
86
|
-
* compiled or copied into the subscribing project's agent directories; `config`
|
|
87
|
-
* entries name config layer files that are merged into the subscriber's config.
|
|
88
|
-
*/
|
|
89
|
-
export const CONTENT_KINDS = ["skills", "memories", "prompts", "config"] as const;
|
|
90
|
-
|
|
91
|
-
/** One group of files this recipe contributes, all of the same kind. */
|
|
92
|
-
export const recipeContentSchema = extensibleObject({
|
|
93
|
-
/** What the files are, which decides where they land in a subscribing project. */
|
|
94
|
-
kind: z.enum(CONTENT_KINDS),
|
|
95
|
-
/** Glob patterns, relative to the recipe folder, naming the files to contribute. */
|
|
96
|
-
include: z
|
|
97
|
-
.array(relativePathSchema("an include pattern", true))
|
|
98
|
-
.min(1, "must list at least one include pattern"),
|
|
99
|
-
/** Glob patterns, relative to the recipe folder, removed from the include set. */
|
|
100
|
-
exclude: z.array(relativePathSchema("an exclude pattern", true)).optional(),
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// --- The manifest -------------------------------------------------------------------------------
|
|
104
|
-
|
|
105
|
-
/** The recipe manifest schema. */
|
|
106
|
-
export const recipeManifestSchema = extensibleObject({
|
|
107
|
-
formatVersion: formatVersionSchema,
|
|
108
|
-
/** The namespace this recipe belongs to. Must be declared by the repo manifest. */
|
|
109
|
-
namespace: namespaceNameSchema,
|
|
110
|
-
/** The recipe's name, unique within its namespace. */
|
|
111
|
-
name: recipeNameSchema,
|
|
112
|
-
/**
|
|
113
|
-
* The published version. Recipe metadata is the source of truth for versions;
|
|
114
|
-
* a git tag of the shape `namespace/recipe@1.2.3` is a convenience ref that
|
|
115
|
-
* `sous repo release` keeps consistent with this field.
|
|
116
|
-
*/
|
|
117
|
-
version: semverVersionSchema,
|
|
118
|
-
/** One-paragraph summary, shown by `sous repo search` and `sous repo list`. */
|
|
119
|
-
description: z.string().optional(),
|
|
120
|
-
/**
|
|
121
|
-
* Whether this recipe takes proposed changes, winning over the repository's
|
|
122
|
-
* own `submissions` block. A recipe whose files are copied in from somewhere
|
|
123
|
-
* else sets `allowed: false` and says where to go instead.
|
|
124
|
-
*/
|
|
125
|
-
submissions: submissionsSchema.optional(),
|
|
126
|
-
/**
|
|
127
|
-
* Build dependencies: fetched and addressable here, but not added to the
|
|
128
|
-
* project. Each entry is a bare ref naming a sibling recipe in this same
|
|
129
|
-
* repository, or a locator URL naming a recipe in another one.
|
|
130
|
-
*/
|
|
131
|
-
depends: refListSchema("'depends'").optional(),
|
|
132
|
-
/** Co-subscriptions: subscribing here subscribes the project to these too. */
|
|
133
|
-
subscribes: refListSchema("'subscribes'").optional(),
|
|
134
|
-
/**
|
|
135
|
-
* The files this recipe contributes. A curated bundle contributes no files of
|
|
136
|
-
* its own, so this may be omitted; it then defaults to an empty list.
|
|
137
|
-
*/
|
|
138
|
-
contents: z.array(recipeContentSchema).default([]),
|
|
139
|
-
/** Variable definitions this recipe publishes. */
|
|
140
|
-
variables: z
|
|
141
|
-
.array(variableDefinitionSchema)
|
|
142
|
-
.superRefine((definitions, ctx) => {
|
|
143
|
-
const seenNames = new Set<string>();
|
|
144
|
-
const seenEnv = new Map<string, number>();
|
|
145
|
-
definitions.forEach((definition, index) => {
|
|
146
|
-
if (seenNames.has(definition.name)) {
|
|
147
|
-
ctx.addIssue({
|
|
148
|
-
code: "custom",
|
|
149
|
-
path: [index, "name"],
|
|
150
|
-
message: `the variable '${definition.name}' is defined more than once`,
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
seenNames.add(definition.name);
|
|
154
|
-
|
|
155
|
-
if (definition.env !== undefined) {
|
|
156
|
-
const first = seenEnv.get(definition.env);
|
|
157
|
-
if (first !== undefined) {
|
|
158
|
-
ctx.addIssue({
|
|
159
|
-
code: "custom",
|
|
160
|
-
path: [index, "env"],
|
|
161
|
-
message:
|
|
162
|
-
`the environment variable '${definition.env}' is already claimed by ` +
|
|
163
|
-
`variables[${first}]; two definitions cannot share one environment variable`,
|
|
164
|
-
});
|
|
165
|
-
} else {
|
|
166
|
-
seenEnv.set(definition.env, index);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
})
|
|
171
|
-
.optional(),
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
/** A validated recipe manifest. */
|
|
175
|
-
export type RecipeManifest = z.infer<typeof recipeManifestSchema>;
|
|
176
|
-
|
|
177
|
-
/** One validated content group from a recipe manifest. */
|
|
178
|
-
export type RecipeContent = z.infer<typeof recipeContentSchema>;
|
|
179
|
-
|
|
180
|
-
/** What a content group contributes. */
|
|
181
|
-
export type ContentKind = (typeof CONTENT_KINDS)[number];
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Validates a parsed recipe manifest, throwing a ConfigError that names the
|
|
185
|
-
* file and the path of every bad field.
|
|
186
|
-
*
|
|
187
|
-
* @param value - The parsed contents of the manifest file.
|
|
188
|
-
* @param sourceLabel - The manifest's file path, named in error messages.
|
|
189
|
-
*/
|
|
190
|
-
export function parseRecipeManifest(
|
|
191
|
-
value: unknown,
|
|
192
|
-
sourceLabel: string
|
|
193
|
-
): RecipeManifest {
|
|
194
|
-
return parseFormat(recipeManifestSchema, value, sourceLabel, "recipe manifest");
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* The recipe's key (`namespace/name`), which is how it is stored in an index,
|
|
199
|
-
* a lockfile and a project's subscriptions.
|
|
200
|
-
*
|
|
201
|
-
* @param manifest - A validated recipe manifest.
|
|
202
|
-
*/
|
|
203
|
-
export function recipeManifestKey(manifest: RecipeManifest): string {
|
|
204
|
-
return `${manifest.namespace}/${manifest.name}`;
|
|
205
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The repo manifest: `sous.repo.yaml` (or `.yml`, or `.json`) at the root of a
|
|
3
|
-
* repository. It is HAND-WRITTEN by the repo's maintainers and is the entry
|
|
4
|
-
* point sous reads to learn what a repo publishes: its namespaces, and where
|
|
5
|
-
* each recipe folder lives.
|
|
6
|
-
*
|
|
7
|
-
* The manifest is deliberately declarative and executable-free: it is data, so
|
|
8
|
-
* sous can read, validate and browse it before the repository is trusted, in a
|
|
9
|
-
* dry run and in `repo release --check` without executing anything. So
|
|
10
|
-
* manifests are YAML or JSON only.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { z } from "zod";
|
|
14
|
-
import {
|
|
15
|
-
extensibleObject,
|
|
16
|
-
formatVersionSchema,
|
|
17
|
-
namespaceNameSchema,
|
|
18
|
-
parseFormat,
|
|
19
|
-
relativePathSchema,
|
|
20
|
-
repoNameSchema,
|
|
21
|
-
submissionsSchema,
|
|
22
|
-
} from "./common.js";
|
|
23
|
-
|
|
24
|
-
/** A namespace declaration. Namespaces group recipes and are not versioned. */
|
|
25
|
-
export const repoNamespaceSchema = extensibleObject({
|
|
26
|
-
/** One-line, plain-language summary of what the namespace holds. */
|
|
27
|
-
description: z.string().optional(),
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
/** Path to a recipe folder, relative to the repo root. */
|
|
31
|
-
const recipePathSchema = relativePathSchema("a recipe path");
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* The repo manifest schema.
|
|
35
|
-
*
|
|
36
|
-
* `name` is only a SUGGESTED short name. The short name a project actually uses
|
|
37
|
-
* is recorded in its own config by `sous repo add`, which defaults to the last
|
|
38
|
-
* URL segment and can be overridden, so two repos suggesting the same name
|
|
39
|
-
* never collide in a project.
|
|
40
|
-
*/
|
|
41
|
-
export const repoManifestSchema = extensibleObject({
|
|
42
|
-
formatVersion: formatVersionSchema,
|
|
43
|
-
/** Suggested short name for the repo. */
|
|
44
|
-
name: repoNameSchema,
|
|
45
|
-
/** One-paragraph summary of the repo, shown by `sous repo list` and `sous repo search`. */
|
|
46
|
-
description: z.string().optional(),
|
|
47
|
-
/**
|
|
48
|
-
* Where to send a contribution: a URL, or free text describing the process.
|
|
49
|
-
* Surfaced when a provider does not support `sous repo submit`, so a
|
|
50
|
-
* contributor is never left without a route.
|
|
51
|
-
*/
|
|
52
|
-
contribute: z.string().min(1, "must not be empty").optional(),
|
|
53
|
-
/**
|
|
54
|
-
* Whether the repository's recipes take proposed changes. A recipe's own
|
|
55
|
-
* `submissions` block wins over this one. `sous repo submit` warns before
|
|
56
|
-
* proposing a change to a recipe that does not, and `sous repo release
|
|
57
|
-
* --check` fails a pull request that changes one.
|
|
58
|
-
*/
|
|
59
|
-
submissions: submissionsSchema.optional(),
|
|
60
|
-
/** Every namespace the repo publishes, keyed by namespace name. */
|
|
61
|
-
namespaces: z.record(namespaceNameSchema, repoNamespaceSchema),
|
|
62
|
-
/**
|
|
63
|
-
* Every recipe folder in the repo, as a path relative to the repo root. Each
|
|
64
|
-
* folder must contain a recipe manifest (`sous.recipe.yaml` or a supported
|
|
65
|
-
* variant); `sous repo release` verifies that and builds the index from it.
|
|
66
|
-
*/
|
|
67
|
-
recipes: z.array(recipePathSchema).superRefine((paths, ctx) => {
|
|
68
|
-
const seen = new Set<string>();
|
|
69
|
-
paths.forEach((entry, index) => {
|
|
70
|
-
if (seen.has(entry)) {
|
|
71
|
-
ctx.addIssue({
|
|
72
|
-
code: "custom",
|
|
73
|
-
path: [index],
|
|
74
|
-
message: `the recipe path '${entry}' is listed more than once`,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
seen.add(entry);
|
|
78
|
-
});
|
|
79
|
-
}),
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
/** A validated repo manifest. */
|
|
83
|
-
export type RepoManifest = z.infer<typeof repoManifestSchema>;
|
|
84
|
-
|
|
85
|
-
/** A validated namespace declaration from a repo manifest. */
|
|
86
|
-
export type RepoNamespace = z.infer<typeof repoNamespaceSchema>;
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Validates a parsed repo manifest, throwing a ConfigError that names the file
|
|
90
|
-
* and the path of every bad field.
|
|
91
|
-
*
|
|
92
|
-
* @param value - The parsed contents of the manifest file.
|
|
93
|
-
* @param sourceLabel - The manifest's file path, named in error messages.
|
|
94
|
-
*/
|
|
95
|
-
export function parseRepoManifest(value: unknown, sourceLabel: string): RepoManifest {
|
|
96
|
-
return parseFormat(repoManifestSchema, value, sourceLabel, "repo manifest");
|
|
97
|
-
}
|