@vivswan/github-settings-as-code 0.0.0 → 2.0.1-main.658.20260922.gdcb742f

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.
@@ -0,0 +1,160 @@
1
+ import { C as foldLayers, D as runMulti, M as concludeRun, N as failRun, Q as validateSettingsDoc, S as writeReplacing, Ut as collectingIo, X as runForRepo, _ as renderSnapshotYaml, at as describeRemoval, b as readEntries, g as runSnapshot, h as concludeSnapshot, j as concludeRender, m as SNAPSHOT_SCHEMA_URL, nt as SectionSelection, v as snapshotRepository$1, w as readLayerFiles, x as renameEntry, y as runSingle } from "./inputs-BOuUCdpl.js";
2
+ import { err, ok } from "neverthrow";
3
+ //#region src/flows/render.ts
4
+ /**
5
+ * The mode: render run flow. The written document is exactly what a later apply or check runs from that path; nothing
6
+ * here touches GitHub, so the flow takes no client and needs no token.
7
+ */
8
+ const RENDERED_LABEL = "the rendered settings document";
9
+ /**
10
+ * An input layer is never the destination, under any name the read follows or the rename reaches: the entry the
11
+ * rename replaces (the leaf under its resolved parent, a link there unfollowed) against every entry each layer's read
12
+ * follows (each component, every link hop, the final file), compared by identity, so spellings, directory links, case
13
+ * aliases, and link chains all meet. `out.yml -> layer.yml` as the destination with `layer.yml` as the layer is
14
+ * admitted: the write replaces the link and leaves the layer intact. Guarded beside the write: the next run would
15
+ * fold the merged document as if it were a layer.
16
+ */
17
+ function renderedFileCollision(cfg) {
18
+ const replaced = renameEntry(cfg.renderedFile);
19
+ if (replaced === null) return ok();
20
+ const index = cfg.settingsFiles.findIndex((layer) => readEntries(layer).has(replaced));
21
+ const layer = cfg.settingsFiles[index];
22
+ return layer === void 0 ? ok() : err({
23
+ code: "rendered-file-is-layer",
24
+ renderedFile: cfg.renderedFile,
25
+ index,
26
+ layer
27
+ });
28
+ }
29
+ function runRender(cfg, io) {
30
+ return renderedFileCollision(cfg).andThen(() => readLayerFiles(cfg.settingsFiles)).andThen((layers) => foldLayers(layers, RENDERED_LABEL, {
31
+ layering: cfg.layering,
32
+ undeclared: cfg.undeclared
33
+ }, io)).andThen((folded) => {
34
+ for (const notice of folded.notices) io.annotate("notice", describeRemoval(notice));
35
+ return writeReplacing(cfg.renderedFile, folded.yaml).mapErr((reason) => ({
36
+ code: "rendered-file-unwritable",
37
+ path: cfg.renderedFile,
38
+ reason
39
+ })).map(() => ({
40
+ layers: cfg.settingsFiles,
41
+ renderedFile: cfg.renderedFile
42
+ }));
43
+ });
44
+ }
45
+ //#endregion
46
+ //#region src/flows/execute.ts
47
+ function executeRun(cfg, deps) {
48
+ const { io } = deps;
49
+ const fail = (problem) => ({
50
+ exitCode: failRun(io, problem),
51
+ fatal: problem
52
+ });
53
+ const end = (exitCode) => ({ exitCode });
54
+ if (cfg.kind === "render") return Promise.resolve(runRender(cfg, io).match((merged) => end(concludeRender(io, merged)), fail));
55
+ const api = deps.createClient(cfg.token, io, cfg.apiVersion);
56
+ switch (cfg.kind) {
57
+ case "snapshot": return runSnapshot(api, cfg, io).match((finished) => end(concludeSnapshot(io, finished)), fail);
58
+ case "multi": return runMulti(api, cfg, io, deps.uploader).match((targets) => end(concludeRun(io, {
59
+ kind: "multi",
60
+ mode: cfg.mode,
61
+ targets
62
+ })), fail);
63
+ case "single": return runSingle(api, cfg, io, deps.uploader).match((target) => end(concludeRun(io, {
64
+ kind: "single",
65
+ mode: cfg.mode,
66
+ target
67
+ })), fail);
68
+ }
69
+ }
70
+ //#endregion
71
+ //#region src/flows/library.ts
72
+ const UNNAMED_SOURCE = "the settings document";
73
+ const MERGED_SOURCE = "the rendered settings document";
74
+ /** The Io a verb prints through, and the lines the report carries: the caller's own Io leaves the log empty. */
75
+ function sink(io) {
76
+ if (io !== void 0) return {
77
+ io,
78
+ log: () => []
79
+ };
80
+ const collected = collectingIo();
81
+ return {
82
+ io: collected.io,
83
+ log: () => collected.lines
84
+ };
85
+ }
86
+ /** Validate a parsed document into the branded settings every other verb takes. */
87
+ function validateSettings(doc, options = {}) {
88
+ const out = sink(options.io);
89
+ return validateSettingsDoc(doc, options.source ?? UNNAMED_SOURCE, options.sections ?? SectionSelection.ALL, out.io, {
90
+ undeclared: options.undeclared,
91
+ secretSource: options.secretSource
92
+ }).map((settings) => ({
93
+ settings,
94
+ log: out.log()
95
+ }));
96
+ }
97
+ /** Fold an ordered list of layers into one validated document, as mode: render does. */
98
+ function mergeSettings(layers, options = {}) {
99
+ const out = sink(options.io);
100
+ return foldLayers(layers, options.source ?? MERGED_SOURCE, {
101
+ layering: options.layering ?? "deep",
102
+ undeclared: options.undeclared
103
+ }, out.io).map((folded) => ({
104
+ ...folded,
105
+ log: out.log()
106
+ }));
107
+ }
108
+ async function runMode(client, repo, settings, mode, options) {
109
+ const out = sink(options.io);
110
+ return {
111
+ ...await runForRepo(client, {
112
+ repo,
113
+ settings,
114
+ mode,
115
+ onMissingPermission: options.onMissingPermission ?? "fail",
116
+ sections: options.sections ?? SectionSelection.ALL,
117
+ secretEnv: options.secretEnv
118
+ }, out.io),
119
+ log: out.log()
120
+ };
121
+ }
122
+ /** Plan and diff every active section without writing. */
123
+ function checkRepository(client, repo, settings, options = {}) {
124
+ return runMode(client, repo, settings, "check", options);
125
+ }
126
+ /** Execute the plan: the repository converges on the document. */
127
+ function applyRepository(client, repo, settings, options = {}) {
128
+ return runMode(client, repo, settings, "apply", options);
129
+ }
130
+ /** Read one repository's supported sections back as a settings document and its rendered file. */
131
+ async function snapshotRepository(client, repo, options = {}) {
132
+ const out = sink(options.io);
133
+ const takenAt = (/* @__PURE__ */ new Date()).toISOString();
134
+ const result = await snapshotRepository$1(client, {
135
+ repo,
136
+ sections: options.sections ?? SectionSelection.ALL,
137
+ onMissingPermission: options.onMissingPermission ?? "fail"
138
+ }, out.io);
139
+ const log = out.log();
140
+ if (result.result === "failed") return {
141
+ ...result,
142
+ takenAt,
143
+ log
144
+ };
145
+ const yaml = renderSnapshotYaml(result, SNAPSHOT_SCHEMA_URL);
146
+ return {
147
+ ...result,
148
+ yaml,
149
+ takenAt,
150
+ log
151
+ };
152
+ }
153
+ /** Snapshot several repositories in order, one report each; a failed target never stops the rest. */
154
+ async function snapshotRepositories(client, repos, options = {}) {
155
+ const reports = [];
156
+ for (const repo of repos) reports.push(await snapshotRepository(client, repo, options));
157
+ return reports;
158
+ }
159
+ //#endregion
160
+ export { snapshotRepository as a, runRender as c, snapshotRepositories as i, checkRepository as n, validateSettings as o, mergeSettings as r, executeRun as s, applyRepository as t };