@stll/oxlint-config 0.12.1 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +303 -6
  2. package/dist/cloud-setup-cli.cjs +6 -4
  3. package/dist/cloud-setup-cli.cjs.map +1 -1
  4. package/dist/cloud-setup-cli.mjs +3 -1
  5. package/dist/cloud-setup-cli.mjs.map +1 -1
  6. package/dist/compiler-packages.cjs +28 -0
  7. package/dist/compiler-packages.cjs.map +1 -0
  8. package/dist/compiler-packages.mjs +17 -0
  9. package/dist/compiler-packages.mjs.map +1 -0
  10. package/dist/consumer-compat-cli.cjs +1091 -0
  11. package/dist/consumer-compat-cli.cjs.map +1 -0
  12. package/dist/consumer-compat-cli.d.cts +1 -0
  13. package/dist/consumer-compat-cli.d.mts +1 -0
  14. package/dist/consumer-compat-cli.mjs +1089 -0
  15. package/dist/consumer-compat-cli.mjs.map +1 -0
  16. package/dist/publish-build-target-nuxt-helper.cjs +71 -0
  17. package/dist/publish-build-target-nuxt-helper.cjs.map +1 -0
  18. package/dist/publish-build-target-nuxt-helper.d.cts +10 -0
  19. package/dist/publish-build-target-nuxt-helper.d.mts +10 -0
  20. package/dist/publish-build-target-nuxt-helper.mjs +68 -0
  21. package/dist/publish-build-target-nuxt-helper.mjs.map +1 -0
  22. package/dist/publish-build-target-vite-dts-helper.cjs +3 -0
  23. package/dist/publish-build-target-vite-dts-helper.d.cts +24 -0
  24. package/dist/publish-build-target-vite-dts-helper.d.mts +24 -0
  25. package/dist/publish-build-target-vite-dts-helper.mjs +2 -0
  26. package/dist/publish-build-target-vite-dts-helper2.cjs +372 -0
  27. package/dist/publish-build-target-vite-dts-helper2.cjs.map +1 -0
  28. package/dist/publish-build-target-vite-dts-helper2.mjs +359 -0
  29. package/dist/publish-build-target-vite-dts-helper2.mjs.map +1 -0
  30. package/dist/publish-contract-cli.cjs +983 -0
  31. package/dist/publish-contract-cli.cjs.map +1 -0
  32. package/dist/publish-contract-cli.d.cts +1 -0
  33. package/dist/publish-contract-cli.d.mts +1 -0
  34. package/dist/publish-contract-cli.mjs +981 -0
  35. package/dist/publish-contract-cli.mjs.map +1 -0
  36. package/dist/publish-contract.cjs +431 -0
  37. package/dist/publish-contract.cjs.map +1 -0
  38. package/dist/publish-contract.mjs +396 -0
  39. package/dist/publish-contract.mjs.map +1 -0
  40. package/dist/rolldown-runtime.cjs +28 -0
  41. package/dist/toolchain-changed-cli.cjs +34 -0
  42. package/dist/toolchain-changed-cli.cjs.map +1 -0
  43. package/dist/toolchain-changed-cli.d.cts +1 -0
  44. package/dist/toolchain-changed-cli.d.mts +1 -0
  45. package/dist/toolchain-changed-cli.mjs +34 -0
  46. package/dist/toolchain-changed-cli.mjs.map +1 -0
  47. package/dist/toolchain-changed.cjs +1470 -0
  48. package/dist/toolchain-changed.cjs.map +1 -0
  49. package/dist/toolchain-changed.mjs +1463 -0
  50. package/dist/toolchain-changed.mjs.map +1 -0
  51. package/dist/toolchain-check-cli.cjs +2 -1
  52. package/dist/toolchain-check-cli.cjs.map +1 -1
  53. package/dist/toolchain-check-cli.mjs +2 -1
  54. package/dist/toolchain-check-cli.mjs.map +1 -1
  55. package/dist/toolchain-guard.cjs +551 -1166
  56. package/dist/toolchain-guard.cjs.map +1 -1
  57. package/dist/toolchain-guard.mjs +529 -1105
  58. package/dist/toolchain-guard.mjs.map +1 -1
  59. package/dist/toolchain-inputs.cjs +926 -0
  60. package/dist/toolchain-inputs.cjs.map +1 -0
  61. package/dist/toolchain-inputs.mjs +816 -0
  62. package/dist/toolchain-inputs.mjs.map +1 -0
  63. package/dist/toolchain-schema.cjs +702 -0
  64. package/dist/toolchain-schema.cjs.map +1 -0
  65. package/dist/toolchain-schema.mjs +580 -0
  66. package/dist/toolchain-schema.mjs.map +1 -0
  67. package/dist/typecheck-parity-cli.cjs +38 -898
  68. package/dist/typecheck-parity-cli.cjs.map +1 -1
  69. package/dist/typecheck-parity-cli.mjs +39 -899
  70. package/dist/typecheck-parity-cli.mjs.map +1 -1
  71. package/dist/typecheck-parity.cjs +905 -0
  72. package/dist/typecheck-parity.cjs.map +1 -0
  73. package/dist/typecheck-parity.mjs +894 -0
  74. package/dist/typecheck-parity.mjs.map +1 -0
  75. package/dist/typecheck-probe-cli.cjs +176 -0
  76. package/dist/typecheck-probe-cli.cjs.map +1 -0
  77. package/dist/typecheck-probe-cli.d.cts +1 -0
  78. package/dist/typecheck-probe-cli.d.mts +1 -0
  79. package/dist/typecheck-probe-cli.mjs +174 -0
  80. package/dist/typecheck-probe-cli.mjs.map +1 -0
  81. package/package.json +43 -5
  82. package/toolchain.json +9 -1
@@ -1,758 +1,238 @@
1
+ import { _ as nodeSupportRangeMatches, a as isPythonDependencyManifest, c as toolchainInputKind, d as isKubernetesDefinitionPath, f as workspaceContains, g as nodeSelectorMatches, i as isMiseConfigPath, l as containerDocumentImages, m as generateCloudSetup, n as isDependabotGithubActionsPath, o as javascriptDependencyLockfiles, p as cloudSetupPath, r as isDockerDefinitionPath, s as pythonDependencyManifestKind, t as githubAutomationFileKind, u as isComposeDefinitionPath, v as parseCloudSetup } from "./toolchain-inputs.mjs";
2
+ import { _ as parseConsumerFixtures, d as consumerPackageClosure, g as discoverConsumerPackages, i as assertConsumerFixtureKind, n as assertConsumerNodeSupport, o as assertConsumerFixtureSelection } from "./toolchain-schema.mjs";
1
3
  import { readFileSync, realpathSync } from "node:fs";
2
4
  import { execFileSync } from "node:child_process";
3
5
  import path from "node:path";
4
- import { minVersion, satisfies, subset, valid, validRange } from "semver";
5
- import picomatch from "picomatch";
6
- import { parse } from "smol-toml";
6
+ import { minVersion, satisfies, valid, validRange } from "semver";
7
7
  import { LineCounter, isAlias, isMap, isNode, isScalar, isSeq, parseAllDocuments, parseDocument } from "yaml";
8
- //#region src/cloud-setup-install.ts
9
- const packageInstall = String.raw`
10
- # Prevent package maintainer scripts from starting unmanaged system services.
11
- apt_install() (
12
- # EXIT runs after function locals expire; this subshell owns the trap state.
13
- backup=''
14
- policy_state=untouched
15
- [[ ! -L /var/lock/stll-cloud-apt.lock ]] || fail 'Invalid package install lock'
16
- exec 8>/var/lock/stll-cloud-apt.lock
17
- flock -x -w 120 8 || fail 'Another package installation is active'
18
- [[ ! -L /usr/sbin/policy-rc.d ]] || fail 'Unsupported service-start policy symlink'
19
- [[ ! -e /usr/sbin/policy-rc.d || -f /usr/sbin/policy-rc.d ]] || fail 'Unsupported service-start policy file'
20
- backup="$(mktemp -d)"
21
- cleanup_package_policy() {
22
- local status=$?
23
- trap - EXIT HUP INT TERM
24
- if [[ "$policy_state" == installed ]]; then
25
- if [[ -f "$backup/original" ]]; then
26
- if ! cp -p "$backup/original" /usr/sbin/policy-rc.d; then status=1; fi
27
- else
28
- if ! rm -f /usr/sbin/policy-rc.d; then status=1; fi
29
- fi
30
- fi
31
- rm -rf -- "$backup"
32
- exit "$status"
33
- }
34
- trap cleanup_package_policy EXIT
35
- trap 'exit 129' HUP
36
- trap 'exit 130' INT
37
- trap 'exit 143' TERM
38
- if [[ -e /usr/sbin/policy-rc.d ]]; then cp -p /usr/sbin/policy-rc.d "$backup/original"; fi
39
- policy_state=installed
40
- printf '%s\n' '#!/bin/sh' 'exit 101' > /usr/sbin/policy-rc.d
41
- chmod 755 /usr/sbin/policy-rc.d
42
- DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "$@"
43
- )
44
-
45
- `;
46
- const postgresSourceLine = "deb [signed-by=/usr/share/keyrings/stll-pgdg.asc] https://apt.postgresql.org/pub/repos/apt noble-pgdg main";
47
- const postgresSigningFingerprint = "B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8";
48
- const postgresSources = String.raw`
49
- pgdg_key_fingerprint() {
50
- gpg --batch --show-keys --with-colons "$1" | awk -F: '$1 == "pub" { primary = 1 } $1 == "sub" { primary = 0 } $1 == "fpr" && primary { print toupper($10); primary = 0 }'
51
- }
52
- pgdg_sources_ready() {
53
- local managed="$1" file mode records suite keyring override fingerprint selected_keyring=''
54
- shift
55
- PGDG_SOURCE_STATE=missing
56
- [[ ! -L "$managed" ]] || fail "Managed PGDG source must not be a symlink: $managed"
57
- for file in "$@"; do
58
- [[ -e "$file" ]] || continue
59
- [[ -f "$file" ]] || fail "APT source must be a regular file: $file"
60
- case "$file" in *.sources) mode=sources ;; *) mode=list ;; esac
61
- records="$(awk -v mode="$mode" '
62
- function trim(value) { gsub(/^[[:space:]]+|[[:space:]]+$/, "", value); return value }
63
- function pgdg_uri(uri) {
64
- uri = tolower(uri)
65
- if (uri !~ /^[a-z][a-z0-9+.-]*:\/\//) return 0
66
- sub(/^[a-z][a-z0-9+.-]*:\/\//, "", uri)
67
- sub(/[\/?#].*$/, "", uri)
68
- sub(/^.*@/, "", uri)
69
- sub(/:.*$/, "", uri)
70
- return uri == "apt.postgresql.org" || uri == "apt.postgresql.org."
71
- }
72
- function contains_pgdg(value, tokens, count, token_index) {
73
- count = split(value, tokens, /[[:space:]]+/)
74
- for (token_index = 1; token_index <= count; token_index++) if (pgdg_uri(tokens[token_index])) return 1
75
- return 0
76
- }
77
- function unsafe_field(name) {
78
- return name ~ /^(trusted|allow-insecure|allow-weak|allow-downgrade-to-insecure)$/
79
- }
80
- function disabled_override(value) {
81
- value = tolower(trim(value))
82
- gsub(/^["\047]|["\047]$/, "", value)
83
- return value ~ /^(no|false|without|off|disable|0)$/
84
- }
85
- function stanza(override_index) {
86
- override = 0
87
- for (override_index = 1; override_index <= override_count; override_index++) {
88
- if (!disabled_override(override_values[override_index])) override = 1
89
- delete override_values[override_index]
90
- }
91
- if (tolower(trim(enabled)) != "no" && pgdg) print (trim(suites) == "" ? "-" : trim(suites)) "\t" (trim(keyring) == "" ? "-" : trim(keyring)) "\t" (override ? "unsafe" : "safe")
92
- enabled = ""; pgdg = 0; field = ""; suites = ""; keyring = ""; override = 0; override_count = 0
93
- }
94
- /^[[:space:]]*#/ { if (mode == "list" || $0 ~ /^#/) next }
95
- {
96
- line = $0
97
- if (mode == "list") {
98
- sub(/[[:space:]]*#.*$/, "", line)
99
- if (line !~ /^[[:space:]]*deb(-src)?[[:space:]]/ || !contains_pgdg(line)) next
100
- keyring = ""; override = 0
101
- if (match(line, /\[[^]]*\]/)) {
102
- options = substr(line, RSTART + 1, RLENGTH - 2)
103
- count = split(options, tokens, /[[:space:]]+/)
104
- for (token_index = 1; token_index <= count; token_index++) {
105
- if (tolower(tokens[token_index]) ~ /^signed-by=/) { keyring = tokens[token_index]; sub(/^[^=]*=/, "", keyring) }
106
- option_name = tolower(tokens[token_index]); sub(/=.*/, "", option_name)
107
- option_value = tokens[token_index]; sub(/^[^=]*=/, "", option_value)
108
- if (unsafe_field(option_name) && !disabled_override(option_value)) override = 1
109
- }
110
- sub(/\[[^]]*\]/, "", line)
111
- }
112
- count = split(line, tokens, /[[:space:]]+/)
113
- for (token_index = 1; token_index <= count; token_index++) if (pgdg_uri(tokens[token_index])) print (tokens[token_index + 1] == "" ? "-" : tokens[token_index + 1]) "\t" (keyring == "" ? "-" : keyring) "\t" (override ? "unsafe" : "safe")
114
- next
115
- }
116
- if (line ~ /^[[:space:]]*$/) { stanza(); next }
117
- if (line ~ /^[[:space:]]/) {
118
- if (field == "uris" && contains_pgdg(line)) pgdg = 1
119
- if (field == "enabled") enabled = enabled " " line
120
- if (field == "suites") suites = suites " " line
121
- if (field == "signed-by") keyring = keyring " " line
122
- if (unsafe_field(field)) override_values[override_count] = override_values[override_count] " " line
123
- next
124
- }
125
- field = tolower(line)
126
- sub(/:.*/, "", field)
127
- value = line
128
- sub(/^[^:]*:[[:space:]]*/, "", value)
129
- if (field == "uris" && contains_pgdg(value)) pgdg = 1
130
- if (field == "enabled") enabled = value
131
- if (field == "suites") suites = value
132
- if (field == "signed-by") keyring = value
133
- if (unsafe_field(field)) override_values[++override_count] = value
134
- }
135
- END { if (mode == "sources") stanza() }
136
- ' "$file")" || fail "Cannot inspect APT source: $file"
137
- if [[ "$file" == "$managed" && -z "$records" ]]; then
138
- fail "Managed PGDG source has no active PGDG entry: $file"
139
- fi
140
- while IFS=$'\t' read -r suite keyring override; do
141
- [[ -n "$suite" ]] || continue
142
- [[ "$override" == safe ]] || fail "PGDG source enables an unsafe trust override: $file"
143
- [[ "$suite" == noble-pgdg && "$keyring" == /* && ! "$keyring" =~ [[:space:],] ]] || fail "PGDG source requires noble-pgdg and one Signed-By keyring: $file"
144
- [[ -z "$selected_keyring" || "$selected_keyring" == "$keyring" ]] || fail "Conflicting PGDG Signed-By keyrings: $file"
145
- fingerprint="$(pgdg_key_fingerprint "$keyring")" || fail "Cannot verify PGDG Signed-By keyring: $file"
146
- [[ "$fingerprint" == @PGDG_FINGERPRINT@ ]] || fail "PGDG Signed-By fingerprint mismatch: $file"
147
- selected_keyring="$keyring"
148
- PGDG_SOURCE_STATE=reuse
149
- done <<< "$records"
150
- done
151
- }
152
- `;
153
- const postgresInstall = String.raw`
154
- postgres_ready() {
155
- local binary
156
- for binary in postgres initdb pg_ctl psql createdb; do
157
- [[ -x "/usr/lib/postgresql/@POSTGRES@/bin/$binary" ]] || return 1
158
- done
159
- /usr/lib/postgresql/@POSTGRES@/bin/postgres --version | grep -Eq '^postgres \(PostgreSQL\) @POSTGRES@\.'
160
- }
161
- if ! postgres_ready; then
162
- pgdg_sources_ready /etc/apt/sources.list.d/stll-pgdg.list /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources
163
- if [[ "$PGDG_SOURCE_STATE" == missing ]]; then
164
- local key key_fingerprint
165
- key="$(mktemp)"
166
- curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 https://www.postgresql.org/media/keys/ACCC4CF8.asc -o "$key"
167
- key_fingerprint="$(pgdg_key_fingerprint "$key")"
168
- [[ "$key_fingerprint" == @PGDG_FINGERPRINT@ ]] || fail 'PostgreSQL signing key mismatch'
169
- install -d -m 755 /usr/share/keyrings
170
- install -m 644 "$key" /usr/share/keyrings/stll-pgdg.asc
171
- rm -- "$key"
172
- printf '%s\n' '@PGDG_SOURCE@' > /etc/apt/sources.list.d/stll-pgdg.list
173
- fi
174
- apt-get update
175
- apt_install postgresql-@POSTGRES@ postgresql-client-@POSTGRES@
176
- fi
177
- postgres_ready || fail 'PostgreSQL toolchain is incomplete or differs from the policy major'
178
- `;
179
- const valkeyInstall = String.raw`
180
- if ! command -v valkey-server >/dev/null || [[ "$(valkey-server --version | sed -nE 's/.*v=([0-9.]+).*/\1/p')" != '@VALKEY@' ]] || ! command -v valkey-cli >/dev/null || [[ "$(valkey-cli --version)" != 'valkey-cli @VALKEY@' ]]; then
181
- local scratch server_url server_sha tools_url tools_sha package
182
- scratch="$(mktemp -d)"
183
- chmod 755 "$scratch"
184
- case "$ARCH" in
185
- amd64) server_url='@AMD64_SERVER_URL@'; server_sha='@AMD64_SERVER_SHA@'; tools_url='@AMD64_TOOLS_URL@'; tools_sha='@AMD64_TOOLS_SHA@' ;;
186
- arm64) server_url='@ARM64_SERVER_URL@'; server_sha='@ARM64_SERVER_SHA@'; tools_url='@ARM64_TOOLS_URL@'; tools_sha='@ARM64_TOOLS_SHA@' ;;
187
- *) fail 'Unsupported Valkey architecture' ;;
188
- esac
189
- curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "$server_url" -o "$scratch/valkey-server.deb"
190
- curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "$tools_url" -o "$scratch/valkey-tools.deb"
191
- (cd "$scratch"; printf '%s %s\n' "$server_sha" valkey-server.deb "$tools_sha" valkey-tools.deb | sha256sum --strict -c -)
192
- for package in valkey-server valkey-tools; do
193
- [[ "$(dpkg-deb -f "$scratch/$package.deb" Package)" == "$package" ]] || fail 'Valkey artifact package mismatch'
194
- [[ "$(dpkg-deb -f "$scratch/$package.deb" Version)" == '@VALKEY@-1.noble' ]] || fail 'Valkey artifact version mismatch'
195
- [[ "$(dpkg-deb -f "$scratch/$package.deb" Architecture)" == "$ARCH" ]] || fail 'Valkey artifact architecture mismatch'
196
- done
197
- apt-get update
198
- apt_install "$scratch/valkey-server.deb" "$scratch/valkey-tools.deb"
199
- rm -rf -- "$scratch"
200
- fi
201
- [[ "$(valkey-server --version | sed -nE 's/.*v=([0-9.]+).*/\1/p')" == '@VALKEY@' ]] || fail 'Valkey version differs from policy'
202
- [[ "$(valkey-cli --version)" == 'valkey-cli @VALKEY@' ]] || fail 'Valkey client version differs from policy'
203
- `;
204
- const state = String.raw`
205
- service_account_ready() {
206
- local uid gid shell
207
- uid="$(id -u stll-cloud)" || fail 'Service user is unavailable; run install'
208
- gid="$(id -g stll-cloud)"
209
- shell="$(getent passwd stll-cloud | cut -d: -f7)"
210
- [[ "$uid" =~ ^[1-9][0-9]*$ && "$gid" =~ ^[1-9][0-9]*$ && "$shell" == /usr/sbin/nologin ]] || fail 'Service user must be unprivileged with no login shell'
211
- }
212
- prepare_state() {
213
- service_account_ready
214
- [[ ! -L /var/lib/stll-cloud ]] || fail 'Service state root must not be a symlink'
215
- if [[ -e /var/lib/stll-cloud ]]; then
216
- [[ -d /var/lib/stll-cloud && "$(stat -c '%u:%a' /var/lib/stll-cloud)" == 0:755 ]] || fail 'Invalid service state root'
217
- else
218
- install -d -m 755 -o root -g root /var/lib/stll-cloud
219
- fi
220
- STATE="/var/lib/stll-cloud/$(printf '%s' "$REPO_ROOT" | sha256sum | cut -d' ' -f1)"
221
- [[ ! -L "$STATE" ]] || fail 'Service state must not be a symlink'
222
- if [[ -e "$STATE" ]]; then
223
- [[ -d "$STATE" && "$(stat -c '%u:%a' "$STATE")" == 0:710 && "$(stat -c '%g' "$STATE")" == "$(id -g stll-cloud)" ]] || fail 'Invalid service state ownership'
224
- else
225
- install -d -m 710 -o root -g stll-cloud "$STATE"
226
- fi
227
- [[ ! -L "$STATE/lock" ]] || fail 'Service lock must not be a symlink'
228
- exec 9>"$STATE/lock"
229
- flock -x -w 10 9 || fail 'Another setup process owns the service state'
230
- }
231
- `;
232
- const renderCloudServiceInstall = ({ services, postgres, valkey, valkeyArtifacts }) => {
233
- const parts = services.map((service) => {
234
- switch (service) {
235
- case "postgres": return postgresInstall.replaceAll("@POSTGRES@", postgres).replaceAll("@PGDG_SOURCE@", postgresSourceLine).replaceAll("@PGDG_FINGERPRINT@", postgresSigningFingerprint);
236
- case "valkey": {
237
- const replacements = /* @__PURE__ */ new Map([
238
- ["@VALKEY@", valkey],
239
- ["@AMD64_SERVER_URL@", valkeyArtifacts.amd64.server.url],
240
- ["@AMD64_SERVER_SHA@", valkeyArtifacts.amd64.server.sha256],
241
- ["@AMD64_TOOLS_URL@", valkeyArtifacts.amd64.tools.url],
242
- ["@AMD64_TOOLS_SHA@", valkeyArtifacts.amd64.tools.sha256],
243
- ["@ARM64_SERVER_URL@", valkeyArtifacts.arm64.server.url],
244
- ["@ARM64_SERVER_SHA@", valkeyArtifacts.arm64.server.sha256],
245
- ["@ARM64_TOOLS_URL@", valkeyArtifacts.arm64.tools.url],
246
- ["@ARM64_TOOLS_SHA@", valkeyArtifacts.arm64.tools.sha256]
247
- ]);
248
- return valkeyInstall.replace(/@[A-Z0-9_]+@/g, (token) => {
249
- const value = replacements.get(token);
250
- if (value === void 0) throw new Error(`Missing Valkey artifact field: ${token}`);
251
- return value;
252
- });
253
- }
254
- default: throw new Error(`Unsupported service: ${String(service)}`);
255
- }
256
- });
257
- const user = services.length === 0 ? "" : " if ! id stll-cloud >/dev/null 2>&1; then useradd --system --user-group --no-create-home --shell /usr/sbin/nologin stll-cloud; fi\n service_account_ready\n";
258
- return `${packageInstall}\n${services.includes("postgres") ? postgresSources.replaceAll("@PGDG_FINGERPRINT@", postgresSigningFingerprint) : ""}\n${services.length === 0 ? "prepare_state() { :; }\n" : state}\ninstall_services() {\n${user}${parts.join("")}\n :\n}\n`;
259
- };
8
+ import picomatch from "picomatch";
9
+ import { parse as parse$2 } from "smol-toml";
260
10
  //#endregion
261
- //#region src/cloud-setup-schema.ts
262
- const cloudServices = ["postgres", "valkey"];
263
- const cloudInstallCommand = "bun install --frozen-lockfile";
264
- const cloudService = (value) => cloudServices.some((service) => value === service);
265
- /** Parse an explicit declaration without assuming services for undeclared repositories. */
266
- const parseCloudSetup = (input) => {
267
- if (input === void 0) return void 0;
268
- if (typeof input !== "object" || input === null || Array.isArray(input)) throw new Error("cloud must be an object");
269
- for (const key of Object.keys(input)) if (key !== "services" && key !== "install" && key !== "envFile") throw new Error(`unsupported cloud key: ${key}`);
270
- const selected = "services" in input ? input.services : [];
271
- if (!Array.isArray(selected)) throw new Error("cloud.services must be an array when provided");
272
- if (!("install" in input) || input.install !== "bun install --frozen-lockfile") throw new Error(`cloud.install must be exactly ${cloudInstallCommand}`);
273
- const seen = /* @__PURE__ */ new Set();
274
- const services = Array.from(selected, (service) => {
275
- if (!cloudService(service)) throw new Error("cloud.services contains an unsupported service");
276
- if (seen.has(service)) throw new Error(`cloud.services contains duplicate service: ${service}`);
277
- seen.add(service);
278
- return service;
279
- });
280
- if (services.length === 0) {
281
- if ("envFile" in input) throw new Error("cloud.envFile is not allowed without services");
282
- return {
283
- services,
284
- install: cloudInstallCommand
285
- };
11
+ //#region src/toolchain-consumer-checks.ts
12
+ const consumerRunnerVersion = "0.14.1";
13
+ const consumerCheckWorkflow = "stella/.github/.github/workflows/package-consumer-compat.yml";
14
+ const record$6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
15
+ const repositoryDirectory = (value) => typeof value === "string" && value !== "" && !value.includes("\\") && !value.includes("${{") && !value.split("").some((character) => {
16
+ const code = character.charCodeAt(0);
17
+ return code < 32 || code === 127;
18
+ }) && !/^[A-Za-z]:/.test(value) && !path.posix.isAbsolute(value) && (value === "." || value.split("/").every((part) => part !== "" && part !== "." && part !== ".."));
19
+ const packageDirectories = (value) => Array.isArray(value) && value.length > 0 && value.every(repositoryDirectory) && new Set(value).size === value.length;
20
+ const cronFields = [
21
+ {
22
+ minimum: 0,
23
+ maximum: 59
24
+ },
25
+ {
26
+ minimum: 0,
27
+ maximum: 23
28
+ },
29
+ {
30
+ minimum: 1,
31
+ maximum: 31
32
+ },
33
+ {
34
+ minimum: 1,
35
+ maximum: 12,
36
+ names: Object.fromEntries([
37
+ "JAN",
38
+ "FEB",
39
+ "MAR",
40
+ "APR",
41
+ "MAY",
42
+ "JUN",
43
+ "JUL",
44
+ "AUG",
45
+ "SEP",
46
+ "OCT",
47
+ "NOV",
48
+ "DEC"
49
+ ].map((name, index) => [name, index + 1]))
50
+ },
51
+ {
52
+ minimum: 0,
53
+ maximum: 6,
54
+ names: Object.fromEntries([
55
+ "SUN",
56
+ "MON",
57
+ "TUE",
58
+ "WED",
59
+ "THU",
60
+ "FRI",
61
+ "SAT"
62
+ ].map((name, index) => [name, index]))
286
63
  }
287
- if (!("envFile" in input) || typeof input.envFile !== "string") throw new Error("cloud.envFile must be a canonical repository-relative path");
288
- const envFile = input.envFile;
289
- if (envFile.startsWith("/") || /^[A-Za-z]:/.test(envFile) || envFile.includes("\\") || /\p{Cc}/u.test(envFile) || envFile.split("/").some((part) => part === "" || part === "." || part === "..")) throw new Error("cloud.envFile must be a canonical repository-relative path");
290
- return {
291
- services,
292
- install: cloudInstallCommand,
293
- envFile
64
+ ];
65
+ const cronFieldValues = (text, field) => {
66
+ const value = (token) => {
67
+ const number = /^\d+$/.test(token) ? Number(token) : field.names?.[token.toUpperCase()];
68
+ return number !== void 0 && Number.isSafeInteger(number) && number >= field.minimum && number <= field.maximum ? number : void 0;
294
69
  };
295
- };
296
- //#endregion
297
- //#region src/cloud-setup-services.ts
298
- const common = String.raw`
299
- service_file() {
300
- local file="$1" owner="$2"
301
- [[ ! -L "$file" ]] || fail "Service state must not contain symbolic links"
302
- if [[ -e "$file" ]]; then
303
- [[ -f "$file" && "$(stat -c '%u:%a' "$file")" == "$owner:600" ]] || fail "Invalid service state file"
304
- fi
305
- }
306
- service_directory() {
307
- local directory="$1"
308
- [[ ! -L "$directory" ]] || fail "Service directory must not be a symbolic link"
309
- if [[ -e "$directory" ]]; then
310
- [[ -d "$directory" && "$(stat -c '%u:%a' "$directory")" == "$SERVICE_UID:700" ]] || fail "Invalid service directory"
311
- else
312
- root install -d -m 700 -o stll-cloud -g stll-cloud "$directory"
313
- fi
314
- }
315
- service_password() {
316
- local file="$1"
317
- service_file "$file" 0
318
- if [[ ! -e "$file" ]]; then
319
- (umask 077; openssl rand -hex 32 > "$file")
320
- root chown root:root "$file"
321
- fi
322
- [[ "$(cat "$file")" =~ ^[0-9a-f]{64}$ ]] || fail "Invalid service credential state"
323
- }
324
- service_process_matches() {
325
- local pid="$1" executable="$2" owner actual expected
326
- [[ "$pid" =~ ^[1-9][0-9]*$ && -d "/proc/$pid" ]] || return 1
327
- owner="$(stat -c '%u' "/proc/$pid" 2>/dev/null)" || return 2
328
- [[ "$owner" =~ ^[0-9]+$ ]] || return 2
329
- [[ "$owner" == "$SERVICE_UID" ]] || return 1
330
- actual="$(readlink -f "/proc/$pid/exe" 2>/dev/null)" || return 2
331
- expected="$(readlink -f "$executable")" || return 2
332
- [[ -n "$actual" && -n "$expected" && "$actual" != *' (deleted)' ]] || return 2
333
- [[ "$actual" == "$expected" ]]
334
- }
335
- service_process() {
336
- service_process_matches "$1" "$2" || fail "Managed service process identity differs from the recorded state"
337
- }
338
- service_prune_pid() {
339
- local file="$1" executable="$2" service="$3" pid identity
340
- [[ -e "$file" ]] || return 0
341
- pid="$(head -n 1 "$file")"
342
- # PostgreSQL encodes a standalone backend with a negative PID.
343
- if [[ "$service" == postgres ]]; then pid="$(printf '%s' "$pid" | sed 's/^-//')"; fi
344
- if service_process_matches "$pid" "$executable"; then return 0; else identity=$?; fi
345
- [[ "$identity" != 2 ]] || fail "Cannot verify recorded live service process; retaining its PID file"
346
- # Let PostgreSQL check orphan shared memory before removing a dead-PID lock.
347
- if [[ "$service" == postgres && "$pid" =~ ^[1-9][0-9]*$ && ! -d "/proc/$pid" ]]; then return 0; fi
348
- if [[ "$service" == postgres ]]; then
349
- [[ "$pid" =~ ^[1-9][0-9]*$ && "$SERVICE_UID" != 0 ]] || fail "Cannot safely recover PostgreSQL PID state"
350
- # The root controller is not our nonroot postgres. Preserve the remaining
351
- # lock rows so PostgreSQL's EPERM/ancestor path still checks orphan memory.
352
- root sed -i "1s/.*/$$/" "$file"
353
- return 0
354
- fi
355
- # Restored state can name an unrelated live process; never signal that PID.
356
- root rm -- "$file"
357
- }
358
- `;
359
- const postgresStart = String.raw`
360
- local pg_bin='/usr/lib/postgresql/@POSTGRES@/bin' pg_data="$STATE/postgres" pg_password pg_details pg_pid pg_version
361
- service_password "$STATE/postgres.password"
362
- pg_password="$(cat "$STATE/postgres.password")"
363
- service_directory "$pg_data"
364
- for file in PG_VERSION postmaster.pid postgresql.conf pg_hba.conf pg_ident.conf postgresql.auto.conf; do
365
- service_file "$pg_data/$file" "$SERVICE_UID"
366
- done
367
- if [[ ! -e "$pg_data/PG_VERSION" ]]; then
368
- [[ -z "$(ls -A "$pg_data")" ]] || fail "PostgreSQL data directory is not initialized"
369
- service_file "$STATE/postgres.init-password" "$SERVICE_UID"
370
- (umask 077; printf '%s\n' "$pg_password" > "$STATE/postgres.init-password")
371
- root chown stll-cloud:stll-cloud "$STATE/postgres.init-password"
372
- service_run env PGHOST=127.0.0.1 PGHOSTADDR=127.0.0.1 "$pg_bin/initdb" -D "$pg_data" --username=stll_cloud --auth-host=scram-sha-256 --auth-local=scram-sha-256 --pwfile="$STATE/postgres.init-password" --encoding=UTF8 --locale=C --set=unix_socket_directories= >/dev/null || fail "PostgreSQL initialization failed"
373
- root rm "$STATE/postgres.init-password"
374
- fi
375
- [[ -f "$pg_data/PG_VERSION" && "$(cat "$pg_data/PG_VERSION")" == '@POSTGRES@' ]] || fail "PostgreSQL data has a different major version"
376
- service_prune_pid "$pg_data/postmaster.pid" "$pg_bin/postgres" postgres
377
- if ! service_run env PGHOST=127.0.0.1 PGHOSTADDR=127.0.0.1 "$pg_bin/pg_ctl" -D "$pg_data" status >/dev/null 2>&1; then
378
- service_run env PGHOST=127.0.0.1 PGHOSTADDR=127.0.0.1 "$pg_bin/pg_ctl" -D "$pg_data" -l "$pg_data/cloud.log" -o "-h 127.0.0.1 -p 55432 -c unix_socket_directories=" -t 10 -w start >/dev/null || fail "PostgreSQL start failed; port 55432 must be available"
379
- fi
380
- pg_details="$(env -u PGSERVICE -u PGSERVICEFILE -u PGOPTIONS PGHOSTADDR=127.0.0.1 PGPASSWORD="$pg_password" PGCONNECT_TIMEOUT=2 "$pg_bin/psql" -X -h 127.0.0.1 -p 55432 -U stll_cloud -d postgres -At -v ON_ERROR_STOP=1 -c "SELECT current_setting('data_directory'), current_setting('listen_addresses'), current_setting('port'), current_setting('server_version_num'), current_setting('unix_socket_directories')")" || fail "PostgreSQL authenticated readiness failed"
381
- pg_version="$(printf '%s\n' "$pg_details" | cut -d '|' -f4)"
382
- [[ "$pg_details" == "$pg_data|127.0.0.1|55432|$pg_version|" && "$pg_version" =~ ^[0-9]{6}$ ]] || fail "PostgreSQL endpoint does not match managed data and listener"
383
- (( pg_version / 10000 == @POSTGRES@ )) || fail "PostgreSQL endpoint has a different major version"
384
- pg_pid="$(head -n 1 "$pg_data/postmaster.pid")"
385
- service_process "$pg_pid" "$pg_bin/postgres"
386
- [[ "$(env -u PGSERVICE -u PGSERVICEFILE -u PGOPTIONS PGHOSTADDR=127.0.0.1 PGPASSWORD="$pg_password" PGCONNECT_TIMEOUT=2 "$pg_bin/psql" -X -h 127.0.0.1 -p 55432 -U stll_cloud -d postgres -At -v ON_ERROR_STOP=1 -c "SELECT count(*) FROM pg_database WHERE datname = 'cloud_test'")" == 1 ]] || env -u PGSERVICE -u PGSERVICEFILE -u PGOPTIONS PGHOSTADDR=127.0.0.1 PGPASSWORD="$pg_password" PGCONNECT_TIMEOUT=2 "$pg_bin/createdb" -h 127.0.0.1 -p 55432 -U stll_cloud cloud_test || fail "PostgreSQL test database creation failed"
387
- DATABASE_URL="postgresql://stll_cloud:$pg_password@127.0.0.1:55432/cloud_test"
388
- `;
389
- const valkeyStart = String.raw`
390
- local vk_data="$STATE/valkey" vk_password vk_config vk_info vk_pid vk_expected ready=0
391
- service_password "$STATE/valkey.password"
392
- vk_password="$(cat "$STATE/valkey.password")"
393
- service_directory "$vk_data"
394
- vk_config="$vk_data/valkey.conf"
395
- service_file "$vk_config" "$SERVICE_UID"
396
- for file in valkey.pid valkey.log dump.rdb; do
397
- [[ ! -L "$vk_data/$file" ]] || fail "Valkey state must not contain symbolic links"
398
- if [[ -e "$vk_data/$file" ]]; then
399
- [[ -f "$vk_data/$file" && "$(stat -c '%u' "$vk_data/$file")" == "$SERVICE_UID" ]] || fail "Invalid Valkey state file"
400
- fi
401
- done
402
- vk_expected="$(printf '%s\n' 'bind 127.0.0.1' 'port 56379' 'protected-mode yes' 'daemonize yes' "requirepass $vk_password" "dir $vk_data" "pidfile $vk_data/valkey.pid" "logfile $vk_data/valkey.log" 'save ""' 'appendonly no')"
403
- if [[ ! -e "$vk_config" ]]; then
404
- (umask 077; printf '%s\n' "$vk_expected" > "$vk_config")
405
- root chown stll-cloud:stll-cloud "$vk_config"
406
- fi
407
- [[ "$(cat "$vk_config")" == "$vk_expected" ]] || fail "Valkey configuration differs from managed settings"
408
- service_prune_pid "$vk_data/valkey.pid" "$(command -v valkey-server)" valkey
409
- if [[ ! -e "$vk_data/valkey.pid" ]]; then
410
- service_run valkey-server "$vk_config" || fail "Valkey start failed; port 56379 must be available"
411
- fi
412
- for ((attempt=0; attempt<20; attempt++)); do
413
- if vk_info="$(VALKEYCLI_AUTH="$vk_password" timeout 0.2 valkey-cli -h 127.0.0.1 -p 56379 --raw INFO server 2>/dev/null)"; then
414
- if [[ "$vk_info" == *$'valkey_version:@VALKEY@\r\n'* && "$vk_info" == *"config_file:$vk_config"$'\r\n'* ]]; then ready=1; break; fi
415
- fi
416
- sleep 0.25
417
- done
418
- [[ "$ready" == 1 ]] || fail "Valkey authenticated readiness or managed version/configuration check failed"
419
- [[ -f "$vk_data/valkey.pid" ]] || fail "Valkey managed process identifier is unavailable"
420
- vk_pid="$(cat "$vk_data/valkey.pid")"
421
- service_process "$vk_pid" "$(command -v valkey-server)"
422
- [[ "$vk_info" == *"process_id:$vk_pid"$'\r\n'* ]] || fail "Valkey endpoint has a different process identifier"
423
- [[ "$(VALKEYCLI_AUTH="$vk_password" timeout 2 valkey-cli -h 127.0.0.1 -p 56379 --raw CONFIG GET bind)" == $'bind\n127.0.0.1' ]] || fail "Valkey endpoint must bind only loopback"
424
- [[ "$(VALKEYCLI_AUTH="$vk_password" timeout 2 valkey-cli -h 127.0.0.1 -p 56379 --raw CONFIG GET dir)" == "$(printf 'dir\n%s' "$vk_data")" ]] || fail "Valkey endpoint has a different data directory"
425
- REDIS_URL="redis://:$vk_password@127.0.0.1:56379/0"
426
- `;
427
- /** Render only declared services; installation and environment-file ownership stay with the caller. */
428
- const renderCloudServiceStart = ({ services, postgres, valkey }) => {
429
- if (!/^[1-9][0-9]*$/.test(postgres) || !/^\d+\.\d+\.\d+$/.test(valkey)) throw new Error("Invalid cloud service runtime policy");
430
- if (services.length === 0) return "start_services() { :; }\n";
431
- const bodies = services.map((service) => {
432
- switch (service) {
433
- case "postgres": return postgresStart.replaceAll("@POSTGRES@", postgres);
434
- case "valkey": return valkeyStart.replaceAll("@VALKEY@", valkey);
435
- default: throw new Error(`Unsupported cloud service: ${String(service)}`);
436
- }
437
- });
438
- return `${common}\nstart_services() {\n local SERVICE_UID file attempt\n SERVICE_UID="$(id -u stll-cloud)"\n [[ -d "$STATE" && ! -L "$STATE" && "$(stat -c '%u' "$STATE")" == 0 ]] || fail "Invalid cloud service state directory"\n${bodies.join("")}\n}\n`;
439
- };
440
- //#endregion
441
- //#region src/toolchain-node.ts
442
- const numericComponent = "(?:0|[1-9]\\d*)";
443
- const policyPattern = new RegExp(`^${numericComponent}\\.x$`);
444
- const selectorPattern = new RegExp(`^(${numericComponent})(?:\\.(?:x|${numericComponent}(?:\\.${numericComponent})?))?$`);
445
- /** A Node policy selects the complete stable release series of one major. */
446
- const nodePolicyValid = (value) => policyPattern.test(value) && validRange(value) !== null;
447
- /** Static selectors may narrow that series, but cannot float across majors. */
448
- const nodeSelectorMatches = (value, policy) => {
449
- if (!nodePolicyValid(policy) || typeof value !== "string") return false;
450
- const major = selectorPattern.exec(value)?.[1];
451
- return major !== void 0 && major === policy.slice(0, -2) && validRange(value) !== null;
452
- };
453
- /** Engines must support every stable release in the approved major series. */
454
- const nodeSupportRangeMatches = (policy, range) => {
455
- if (!nodePolicyValid(policy)) return false;
456
- try {
457
- return subset(policy, range);
458
- } catch {
459
- return false;
70
+ const values = /* @__PURE__ */ new Set();
71
+ for (const part of text.split(",")) {
72
+ const split = part.split("/");
73
+ if (split.length > 2) return void 0;
74
+ const [base, stepText] = split;
75
+ const step = stepText === void 0 ? 1 : Number(stepText);
76
+ if (stepText !== void 0 && !/^\d+$/.test(stepText)) return void 0;
77
+ if (!Number.isSafeInteger(step) || step <= 0 || base === void 0) return void 0;
78
+ const bounds = base.split("-");
79
+ if (bounds.length > 2) return void 0;
80
+ const lower = bounds.at(0);
81
+ if (lower === void 0) return void 0;
82
+ const start = base === "*" ? field.minimum : value(lower);
83
+ const upper = bounds.at(1);
84
+ let end = start;
85
+ if (base === "*" || bounds.length === 1 && stepText !== void 0) end = field.maximum;
86
+ else if (upper !== void 0) end = value(upper);
87
+ if (start === void 0 || end === void 0 || start > end) return void 0;
88
+ for (let selected = start; selected <= end; selected += step) values.add(selected);
460
89
  }
90
+ return values;
461
91
  };
462
- //#endregion
463
- //#region src/cloud-setup.ts
464
- const cloudSetupPath = ".agents/cloud-setup.sh";
465
- const cloudEnvironmentMarker = "# Generated by stll-cloud-setup; disposable local test services.";
466
- const script = String.raw`#!/usr/bin/env bash
467
- # Generated by stll-cloud-setup. Regenerate with stll-cloud-setup.
468
- # Networked installation and offline service startup are separate lifecycle steps.
469
- # Cached filesystems may not preserve processes; run start at every session start.
470
- # Supported platform: Ubuntu 24.04 (noble), amd64/arm64, root or passwordless sudo.
471
- set -euo pipefail
472
-
473
- usage() { echo 'Usage: cloud-setup.sh install|start' >&2; exit 2; }
474
- [[ "$#" == 1 ]] || usage
475
- case "$1" in install|start) MODE="$1" ;; *) usage ;; esac
476
- fail() { echo "cloud-setup: $*" >&2; exit 1; }
477
- trap 'echo "cloud-setup: command failed at line $LINENO" >&2' ERR
478
- [[ -f /etc/os-release ]] || fail 'Ubuntu 24.04 is required'
479
- . /etc/os-release
480
- [[ "$ID" == ubuntu && "$VERSION_ID" == 24.04 && "$VERSION_CODENAME" == noble ]] || fail 'Only Ubuntu 24.04 (noble) is supported'
481
- SCRIPT="$(realpath -- "$0")"
482
- REPO_ROOT="$(cd -- "$(dirname -- "$SCRIPT")/.." && pwd -P)"
483
- [[ "$SCRIPT" == "$REPO_ROOT/.agents/cloud-setup.sh" ]] || fail 'Run the generated repository script'
484
- if [[ "$(id -u)" != 0 ]]; then
485
- command -v sudo >/dev/null && sudo -n true || fail 'Root or passwordless sudo is required'
486
- exec sudo -n -- bash "$SCRIPT" "$MODE"
487
- fi
488
- OUTPUT_UID="$(printenv SUDO_UID || id -u)"
489
- [[ "$OUTPUT_UID" =~ ^[0-9]+$ ]] || fail 'Invalid invoking user'
490
- OUTPUT_GID="$(getent passwd "$OUTPUT_UID" | cut -d: -f4)"
491
- [[ "$OUTPUT_GID" =~ ^[0-9]+$ ]] || fail 'Invoking user is unavailable'
492
- OUTPUT_USER="$(getent passwd "$OUTPUT_UID" | cut -d: -f1)"
493
- OUTPUT_HOME="$(getent passwd "$OUTPUT_UID" | cut -d: -f6)"
494
- [[ -n "$OUTPUT_USER" && "$OUTPUT_HOME" == /* && -d "$OUTPUT_HOME" ]] || fail 'Invoking user requires an existing home directory'
495
- root() { "$@"; }
496
- service_run() { runuser -u stll-cloud -- "$@" 9>&-; }
497
- BUN_VERSION='@BUN@'
498
- NODE_VERSION='@NODE@'
499
- NODE_DIR="/opt/stll-cloud/node/$NODE_VERSION"
500
- BUN_DIR="/opt/stll-cloud/bun/$BUN_VERSION"
501
- export PATH="$NODE_DIR/bin:$BUN_DIR:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
502
- BUN_CACHE="/var/cache/stll-cloud/bun/$OUTPUT_UID"
503
- @ENV_ASSIGNMENTS@
504
- [[ -f "$REPO_ROOT/.node-version" && ! -L "$REPO_ROOT/.node-version" ]] || fail 'A regular root .node-version is required'
505
- [[ "$(awk 'NF { if (++count > 1 || NF != 1) exit 1; value=$1 } END { if (count != 1) exit 1; print value }' "$REPO_ROOT/.node-version")" == "$NODE_VERSION" ]] || fail 'Node selector changed; regenerate cloud-setup.sh'
506
- cd -- "$REPO_ROOT"
507
- case "$(uname -m)" in
508
- x86_64) ARCH=amd64; NODE_ARCH=x64; BUN_ARCH=x64 ;;
509
- aarch64|arm64) ARCH=arm64; NODE_ARCH=arm64; BUN_ARCH=aarch64 ;;
510
- *) fail 'Only amd64 and arm64 are supported' ;;
511
- esac
512
-
513
- runtime_ready() {
514
- [[ -x "$NODE_DIR/bin/node" && "$($NODE_DIR/bin/node --version)" == "v$NODE_VERSION" ]] || fail 'Pinned Node is unavailable; run install'
515
- [[ -x "$BUN_DIR/bun" && "$($BUN_DIR/bun --version)" == "$BUN_VERSION" ]] || fail 'Pinned Bun is unavailable; run install'
516
- }
517
- install_runtimes() {
518
- if ! command -v curl >/dev/null || ! command -v unzip >/dev/null || ! command -v xz >/dev/null || ! command -v openssl >/dev/null || ! command -v gpg >/dev/null; then
519
- apt-get update
520
- DEBIAN_FRONTEND=noninteractive apt_install ca-certificates curl unzip xz-utils openssl gnupg util-linux
521
- fi
522
- local scratch node_archive bun_archive bun_folder
523
- scratch="$(mktemp -d)"
524
- trap 'rm -rf -- "$scratch"' RETURN
525
- if [[ ! -x "$NODE_DIR/bin/node" ]] || [[ "$($NODE_DIR/bin/node --version)" != "v$NODE_VERSION" ]]; then
526
- node_archive="node-v$NODE_VERSION-linux-$NODE_ARCH.tar.xz"
527
- curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "https://nodejs.org/dist/v$NODE_VERSION/$node_archive" -o "$scratch/$node_archive"
528
- curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt" -o "$scratch/node-sums"
529
- (cd "$scratch"; awk -v name="$node_archive" '$2 == name { print; count++ } END { if (count != 1) exit 1 }' node-sums | sha256sum --strict -c -)
530
- install -d -m 755 "$NODE_DIR"
531
- tar -xJf "$scratch/$node_archive" --strip-components=1 -C "$NODE_DIR"
532
- fi
533
- if [[ ! -x "$BUN_DIR/bun" ]] || [[ "$($BUN_DIR/bun --version)" != "$BUN_VERSION" ]]; then
534
- bun_folder="bun-linux-$BUN_ARCH"
535
- bun_archive="$bun_folder.zip"
536
- curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "https://github.com/oven-sh/bun/releases/download/bun-v$BUN_VERSION/$bun_archive" -o "$scratch/$bun_archive"
537
- curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "https://github.com/oven-sh/bun/releases/download/bun-v$BUN_VERSION/SHASUMS256.txt" -o "$scratch/bun-sums"
538
- (cd "$scratch"; awk -v name="$bun_archive" '$2 == name || $2 == "*" name { print $1 " " name; count++ } END { if (count != 1) exit 1 }' bun-sums | sha256sum --strict -c -)
539
- unzip -q "$scratch/$bun_archive" -d "$scratch/bun"
540
- install -d -m 755 "$BUN_DIR"
541
- install -m 755 "$scratch/bun/$bun_folder/bun" "$BUN_DIR/bun"
542
- fi
543
- runtime_ready
544
- ln -sfnT -- "$NODE_DIR/bin/node" /usr/local/bin/node
545
- ln -sfnT -- "$NODE_DIR/bin/npm" /usr/local/bin/npm
546
- ln -sfnT -- "$NODE_DIR/bin/npx" /usr/local/bin/npx
547
- ln -sfnT -- "$BUN_DIR/bun" /usr/local/bin/bun
548
- rm -rf -- "$scratch"
549
- trap - RETURN
550
- }
551
-
552
- dependency_install() {
553
- local directory
554
- for directory in /var/cache/stll-cloud /var/cache/stll-cloud/bun; do
555
- [[ ! -L "$directory" ]] || fail 'Dependency cache parent must not be a symlink'
556
- if [[ -e "$directory" ]]; then
557
- [[ -d "$directory" && "$(stat -c '%u' "$directory")" == 0 ]] || fail 'Dependency cache parent must be root-owned'
558
- fi
559
- install -d -m 755 -o root -g root "$directory"
560
- done
561
- [[ ! -L "$BUN_CACHE" ]] || fail 'Dependency cache must not be a symlink'
562
- if [[ -e "$BUN_CACHE" ]]; then
563
- [[ -d "$BUN_CACHE" && "$(stat -c '%u' "$BUN_CACHE")" == "$OUTPUT_UID" ]] || fail 'Dependency cache ownership differs from the invoking user'
564
- fi
565
- install -d -m 700 -o "$OUTPUT_UID" -g "$OUTPUT_GID" "$BUN_CACHE"
566
- runuser -u "$OUTPUT_USER" -- env HOME="$OUTPUT_HOME" BUN_INSTALL_CACHE_DIR="$BUN_CACHE" PATH="$PATH" "$BUN_DIR/bun" install --frozen-lockfile
567
- }
568
-
569
- @SERVICE_INSTALL@
570
- @SERVICE_START@
571
-
572
- @ENV_FUNCTIONS@
573
-
574
- if [[ "$MODE" == install ]]; then
575
- install_runtimes
576
- install_services
577
- dependency_install
578
- exit 0
579
- fi
580
- runtime_ready
581
- @ENV_VALIDATE@
582
- prepare_state
583
- start_services
584
- @ENV_WRITE@
585
- `;
586
- const environmentFunctions = String.raw`
587
- validate_environment() {
588
- local part target="$REPO_ROOT" env_path="$REPO_ROOT/$ENV_FILE" first_line temporary
589
- while IFS= read -r part; do
590
- target="$target/$part"
591
- [[ ! -L "$target" ]] || fail 'Environment path must not contain symbolic links'
592
- done < <(printf '%s\n' "$ENV_FILE" | tr '/' '\n')
593
- [[ -d "$(dirname -- "$env_path")" ]] || fail 'Environment parent directory is missing'
594
- if [[ -e "$env_path" ]]; then
595
- [[ -f "$env_path" ]] || fail 'Environment target must be a regular file'
596
- IFS= read -r first_line < "$env_path" || fail 'Refusing to overwrite an unowned environment file'
597
- [[ "$first_line" == "$ENV_MARKER" ]] || fail 'Refusing to overwrite an unowned environment file'
598
- fi
599
- }
600
- write_environment() {
601
- validate_environment
602
- local env_path="$REPO_ROOT/$ENV_FILE" temporary
603
- temporary="$(mktemp "$(dirname -- "$env_path")/.stll-cloud-env.XXXXXX")"
604
- chmod 600 "$temporary"
605
- {
606
- printf '%s\n' "$ENV_MARKER" 'NODE_ENV=test'
607
- if [[ -n "$DATABASE_URL" ]]; then printf 'DATABASE_URL=%s\n' "$DATABASE_URL"; fi
608
- if [[ -n "$REDIS_URL" ]]; then printf 'REDIS_URL=%s\n' "$REDIS_URL"; fi
609
- } > "$temporary"
610
- chown "$OUTPUT_UID:$OUTPUT_GID" "$temporary"
611
- mv -f -- "$temporary" "$env_path"
612
- }
613
- `;
614
- const shellQuote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
615
- const generateCloudSetup = ({ policy, cloud, nodeVersion }) => {
616
- const selected = parseCloudSetup(cloud);
617
- if (selected === void 0) throw new Error("cloud declaration is required");
618
- const exactNode = nodeVersion.trim();
619
- if (valid(exactNode) !== exactNode || !nodeSelectorMatches(exactNode, policy.node)) throw new Error("cloud requires a tracked exact .node-version within the shared Node series");
620
- const services = cloudServices.filter((service) => selected.services.includes(service));
621
- const replacements = /* @__PURE__ */ new Map([
622
- ["@BUN@", policy.bun],
623
- ["@NODE@", exactNode],
624
- ["@ENV_ASSIGNMENTS@", selected.envFile === void 0 ? "" : `ENV_FILE=${shellQuote(selected.envFile)}\nENV_MARKER=${shellQuote(cloudEnvironmentMarker)}`],
625
- ["@ENV_FUNCTIONS@", selected.envFile === void 0 ? "" : environmentFunctions],
626
- ["@ENV_VALIDATE@", selected.envFile === void 0 ? "" : "DATABASE_URL=''\nREDIS_URL=''\nvalidate_environment"],
627
- ["@ENV_WRITE@", selected.envFile === void 0 ? "" : "write_environment"],
628
- ["@SERVICE_INSTALL@", renderCloudServiceInstall({
629
- services,
630
- postgres: policy.postgres,
631
- valkey: policy.valkey,
632
- valkeyArtifacts: policy.valkeyArtifacts
633
- })],
634
- ["@SERVICE_START@", renderCloudServiceStart({
635
- services,
636
- postgres: policy.postgres,
637
- valkey: policy.valkey
638
- })]
639
- ]);
640
- return script.replace(/@[A-Z0-9_]+@/g, (token) => {
641
- const value = replacements.get(token);
642
- if (value === void 0) throw new Error(`Missing cloud setup field: ${token}`);
643
- return value;
92
+ const validConsumerCron = (text) => {
93
+ const fields = text.trim().split(/\s+/);
94
+ return fields.length === cronFields.length && cronFields.every((field, index) => {
95
+ const fieldText = fields.at(index);
96
+ return fieldText !== void 0 && cronFieldValues(fieldText, field) !== void 0;
644
97
  });
645
98
  };
646
- //#endregion
647
- //#region src/toolchain-container-inputs.ts
648
- const record$6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
649
- /** Filename class recognized by Dependabot's Docker Compose fetcher. */
650
- const isComposeDefinitionPath = (file) => /^\.?(?:docker-)?compose(?:-[\w]+)?(?:\.[\w-]+)?\.ya?ml$/i.test(file.split("/").at(-1) ?? "");
651
- /** Candidate YAML files are classified by their document shape after reading. */
652
- const isKubernetesDefinitionPath = (file) => /^[^.].*\.ya?ml$/i.test(file.split("/").at(-1) ?? "");
653
- const kubernetesPodSpecPaths = {
654
- Pod: ["spec"],
655
- Deployment: [
656
- "spec",
657
- "template",
658
- "spec"
659
- ],
660
- DaemonSet: [
661
- "spec",
662
- "template",
663
- "spec"
664
- ],
665
- StatefulSet: [
666
- "spec",
667
- "template",
668
- "spec"
669
- ],
670
- ReplicaSet: [
671
- "spec",
672
- "template",
673
- "spec"
674
- ],
675
- ReplicationController: [
676
- "spec",
677
- "template",
678
- "spec"
679
- ],
680
- Job: [
681
- "spec",
682
- "template",
683
- "spec"
684
- ],
685
- CronJob: [
686
- "spec",
687
- "jobTemplate",
688
- "spec",
689
- "template",
690
- "spec"
691
- ]
692
- };
693
- /** Shared semantic image enumeration for runtime checks and update-root generation. */
694
- const containerDocumentImages = (document, file) => {
695
- if (!record$6(document)) return void 0;
696
- const services = document["services"];
697
- if (isComposeDefinitionPath(file) && record$6(services)) {
698
- const images = [];
699
- for (const [name, service] of Object.entries(services)) if (record$6(service) && service["image"] !== void 0) images.push({
700
- image: service["image"],
701
- path: [
702
- "services",
703
- name,
704
- "image"
705
- ]
99
+ const nightlySchedule = (value) => {
100
+ if (!record$6(value)) return false;
101
+ const schedule = value["schedule"];
102
+ if (!Array.isArray(schedule) || schedule.length === 0) return false;
103
+ const coveredDays = /* @__PURE__ */ new Set();
104
+ for (const entry of schedule) {
105
+ if (!record$6(entry) || typeof entry["cron"] !== "string" || !validConsumerCron(entry["cron"])) return false;
106
+ const fields = entry["cron"].trim().split(/\s+/);
107
+ const selections = cronFields.map((field, index) => {
108
+ const text = fields.at(index);
109
+ return text === void 0 ? void 0 : cronFieldValues(text, field);
706
110
  });
111
+ if (selections.at(2)?.size !== 31 || selections.at(3)?.size !== 12) continue;
112
+ for (const day of selections.at(4) ?? []) coveredDays.add(day);
113
+ }
114
+ return coveredDays.size === 7;
115
+ };
116
+ const parseConsumerChecks = (input) => {
117
+ if (input === void 0) return [];
118
+ if (!Array.isArray(input)) throw new Error("consumerChecks must be an array");
119
+ const targets = /* @__PURE__ */ new Set();
120
+ return input.map((entry) => {
121
+ if (!record$6(entry) || Object.keys(entry).some((key) => ![
122
+ "workflow",
123
+ "job",
124
+ "packages",
125
+ "toolingVersion",
126
+ "fixturePath"
127
+ ].includes(key)) || typeof entry["workflow"] !== "string" || githubAutomationFileKind(entry["workflow"]) !== "workflow" || !/^\.github\/workflows\/[^/]+\.ya?ml$/.test(entry["workflow"]) || typeof entry["job"] !== "string" || !/^[A-Za-z_][A-Za-z0-9_-]*$/.test(entry["job"]) || !packageDirectories(entry["packages"]) || typeof entry["toolingVersion"] !== "string" || !/^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/.test(entry["toolingVersion"]) || !repositoryDirectory(entry["fixturePath"])) throw new Error("consumerChecks requires canonical workflow, job, unique package directories, exact toolingVersion and repository-relative fixturePath");
128
+ const target = `${entry["workflow"]}:${entry["job"]}`;
129
+ if (targets.has(target)) throw new Error("consumerChecks targets must be unique");
130
+ targets.add(target);
707
131
  return {
708
- ecosystem: "docker-compose",
709
- images
132
+ workflow: entry["workflow"],
133
+ job: entry["job"],
134
+ packages: entry["packages"],
135
+ toolingVersion: entry["toolingVersion"],
136
+ fixturePath: entry["fixturePath"]
710
137
  };
711
- }
712
- if (typeof document["apiVersion"] !== "string" || typeof document["kind"] !== "string") return void 0;
713
- const images = [];
714
- const visited = /* @__PURE__ */ new Set();
715
- const walkResource = (resource, prefix) => {
716
- if (visited.has(resource)) return;
717
- visited.add(resource);
718
- if (typeof resource["apiVersion"] !== "string" || typeof resource["kind"] !== "string") return;
719
- if (resource["kind"] === "List" && Array.isArray(resource["items"])) {
720
- for (const [index, item] of resource["items"].entries()) if (record$6(item)) walkResource(item, [
721
- ...prefix,
722
- "items",
723
- index
724
- ]);
725
- return;
726
- }
727
- const specPath = Object.entries(kubernetesPodSpecPaths).find(([kind]) => kind === resource["kind"])?.[1];
728
- if (specPath === void 0) return;
729
- let spec = resource;
730
- for (const key of specPath) spec = record$6(spec) ? spec[key] : void 0;
731
- if (!record$6(spec)) return;
732
- for (const key of [
733
- "containers",
734
- "initContainers",
735
- "ephemeralContainers"
736
- ]) {
737
- const containers = spec[key];
738
- if (!Array.isArray(containers)) continue;
739
- for (const [index, container] of containers.entries()) if (record$6(container) && container["image"] !== void 0) images.push({
740
- image: container["image"],
741
- path: [
742
- ...prefix,
743
- ...specPath,
744
- key,
745
- index,
746
- "image"
747
- ]
138
+ });
139
+ };
140
+ /** Consumer checks are scoped declarations, never general runtime exceptions. */
141
+ const checkConsumerChecks = ({ declarations, files, policy }) => {
142
+ const diagnostics = [];
143
+ const exercised = /* @__PURE__ */ new Set();
144
+ const add = (message) => diagnostics.push({
145
+ rule: "configuration",
146
+ path: "stll-toolchain.json",
147
+ line: 1,
148
+ message
149
+ });
150
+ for (const [workflow, text] of Object.entries(files)) {
151
+ if (githubAutomationFileKind(workflow) !== "workflow") continue;
152
+ try {
153
+ const document = parseDocument(text, {
154
+ uniqueKeys: true,
155
+ merge: true
748
156
  });
157
+ if (document.errors.length > 0) throw new Error("invalid workflow YAML");
158
+ const source = document.toJS({ maxAliasCount: 100 });
159
+ if (!record$6(source) || !record$6(source["jobs"])) continue;
160
+ for (const [jobName, job] of Object.entries(source["jobs"])) {
161
+ if (!record$6(job) || typeof job["uses"] !== "string") continue;
162
+ const [workflowPath, ref] = job["uses"].split("@");
163
+ if (workflowPath?.toLowerCase() !== "stella/.github/.github/workflows/package-consumer-compat.yml") continue;
164
+ const declaration = declarations.find((entry) => entry.workflow === workflow && entry.job === jobName);
165
+ if (declaration === void 0) {
166
+ add(`${workflow}:${jobName} requires a consumerChecks declaration`);
167
+ continue;
168
+ }
169
+ if (!nightlySchedule(source["on"])) throw new Error(`${workflow} consumer checks require a nonempty valid on.schedule covering every day`);
170
+ if ("if" in job || "needs" in job || "strategy" in job) throw new Error(`${workflow}:${jobName} consumer checks require a stand-alone unconditional reusable job without if or needs or strategy`);
171
+ exercised.add(declaration);
172
+ const approved = Object.entries(policy.actions).find(([name]) => name.toLowerCase() === consumerCheckWorkflow)?.[1];
173
+ if (job["uses"].split("@").length !== 2 || ref === void 0 || !/^[a-f0-9]{40}$/.test(ref) || approved === void 0 || ref !== approved.sha || "steps" in job || "runs-on" in job) throw new Error(`${workflow}:${jobName} must invoke the approved immutable consumer workflow as a reusable job`);
174
+ const inputs = job["with"];
175
+ if (!record$6(inputs)) throw new Error(`${workflow}:${jobName} requires consumer inputs`);
176
+ const names = /* @__PURE__ */ new Set();
177
+ for (const key of Object.keys(inputs)) {
178
+ const normalized = key.toLowerCase();
179
+ if (names.has(normalized) || ![
180
+ "packages",
181
+ "consumer-node",
182
+ "tooling-version",
183
+ "fixture-path"
184
+ ].includes(normalized) || normalized !== key) throw new Error(`${workflow}:${jobName} requires unique canonical consumer input names`);
185
+ names.add(normalized);
186
+ }
187
+ if (inputs["consumer-node"] !== policy.consumerNode) throw new Error(`${workflow}:${jobName} consumer-node must be ${policy.consumerNode}`);
188
+ if (declaration.toolingVersion !== consumerRunnerVersion) throw new Error(`${workflow}:${jobName} toolingVersion must match installed @stll/oxlint-config ${consumerRunnerVersion}`);
189
+ if (inputs["tooling-version"] !== declaration.toolingVersion) throw new Error(`${workflow}:${jobName} tooling-version must match consumerChecks.toolingVersion`);
190
+ if (inputs["fixture-path"] !== declaration.fixturePath) throw new Error(`${workflow}:${jobName} fixture-path must match consumerChecks.fixturePath`);
191
+ const fixtureManifest = path.posix.join(declaration.fixturePath, "consumer-compat.json");
192
+ const fixtureSource = files[fixtureManifest];
193
+ if (fixtureSource === void 0) throw new Error(`consumerChecks fixture configuration must be tracked: ${fixtureManifest}`);
194
+ const fixtures = parseConsumerFixtures(JSON.parse(fixtureSource));
195
+ assertConsumerFixtureSelection({
196
+ selected: declaration.packages,
197
+ fixtures
198
+ });
199
+ const rawPackages = inputs["packages"];
200
+ if (typeof rawPackages !== "string") throw new Error(`${workflow}:${jobName} packages must be a static JSON array`);
201
+ const selected = JSON.parse(rawPackages);
202
+ if (!packageDirectories(selected) || selected.length !== declaration.packages.length || !selected.every((directory) => declaration.packages.includes(directory))) throw new Error(`${workflow}:${jobName} packages must exactly match consumerChecks`);
203
+ const workspacePackages = discoverConsumerPackages(files);
204
+ const packagesByDirectory = new Map([...workspacePackages.values()].map((entry) => [entry.directory, entry]));
205
+ for (const directory of declaration.packages) {
206
+ const manifestPath = path.posix.join(directory, "package.json");
207
+ if (files[manifestPath] === void 0) throw new Error(`consumerChecks package manifest must be tracked: ${manifestPath}`);
208
+ const selectedPackage = packagesByDirectory.get(directory);
209
+ if (selectedPackage === void 0) throw new Error(`consumerChecks package must be a discovered workspace member: ${manifestPath}`);
210
+ const fixture = fixtures.find((candidate) => candidate.package === directory);
211
+ if (fixture === void 0) throw new Error(`missing declared consumer fixture: ${directory}`);
212
+ assertConsumerFixtureKind({
213
+ fixture,
214
+ pkg: selectedPackage
215
+ });
216
+ const manifest = selectedPackage.manifest;
217
+ if (manifest["private"] === true || typeof manifest["name"] !== "string" || manifest["name"].trim() === "") throw new Error(`consumerChecks requires a named published package: ${manifestPath}`);
218
+ const closure = consumerPackageClosure({
219
+ selected: selectedPackage,
220
+ packages: workspacePackages,
221
+ files
222
+ });
223
+ for (const member of closure.values()) assertConsumerNodeSupport({
224
+ manifest: member.manifest,
225
+ node: policy.consumerNode,
226
+ label: path.posix.join(member.directory, "package.json")
227
+ });
228
+ }
229
+ }
230
+ } catch (error) {
231
+ if (declarations.some((entry) => entry.workflow === workflow) || text.toLowerCase().includes("stella/.github/.github/workflows/package-consumer-compat.yml")) add(error instanceof Error ? error.message : String(error));
749
232
  }
750
- };
751
- walkResource(document, []);
752
- return images.length === 0 ? void 0 : {
753
- ecosystem: "docker",
754
- images
755
- };
233
+ }
234
+ for (const declaration of declarations) if (!exercised.has(declaration)) add(`${declaration.workflow}:${declaration.job} consumerChecks declaration has no matching reusable invocation`);
235
+ return diagnostics;
756
236
  };
757
237
  //#endregion
758
238
  //#region src/toolchain-images.ts
@@ -777,66 +257,6 @@ const canonicalDockerRuntime = (image) => {
777
257
  return (registry === void 0 ? image : image.slice(registry.length + 1)).replace(/^library\//i, "");
778
258
  };
779
259
  //#endregion
780
- //#region src/toolchain-inputs.ts
781
- const excludedInputPath = (file) => file === "" || file.split("/").some((part) => part === "node_modules" || part === "vendor");
782
- const javascriptDependencyLockfiles = [
783
- "bun.lock",
784
- "package-lock.json",
785
- "yarn.lock",
786
- "pnpm-lock.yaml"
787
- ];
788
- /** Match project, environment, local, and fragment filenames recognized by mise. */
789
- const isMiseConfigPath = (file) => !excludedInputPath(file) && (/(?:^|\/)\.?mise(?:\.[^./][^/]*)?\.toml$/.test(file) || /(?:^|\/)(?:\.?mise|\.config\/mise)\/config(?:\.[^./][^/]*)?\.toml$/.test(file) || /(?:^|\/)(?:\.?mise|\.config\/mise)\/conf\.d\/[^./][^/]*\.toml$/.test(file));
790
- /** GitHub action metadata may live at the repository root or in any directory. */
791
- const githubAutomationFileKind = (file) => {
792
- if (file === "") return void 0;
793
- if (/^\.github\/workflows\/[^/]+\.ya?ml$/.test(file)) return "workflow";
794
- if (/(?:^|\/)action\.ya?ml$/.test(file)) return "action";
795
- };
796
- /** Dependabot updates workflows and repository-root action metadata. */
797
- const isDependabotGithubActionsPath = (file) => {
798
- const kind = githubAutomationFileKind(file);
799
- return kind === "workflow" || kind === "action" && !file.includes("/");
800
- };
801
- /** Use one filename discriminator for reader selection and Python ecosystem decisions. */
802
- const pythonDependencyManifestKind = (file) => {
803
- if (excludedInputPath(file)) return void 0;
804
- const name = file.split("/").at(-1);
805
- if (name === "pyproject.toml") return "project";
806
- if (name === "Pipfile") return "pipfile";
807
- if (name === "Pipfile.lock") return "pipfile-lock";
808
- if (name === "setup.py" || name === "setup.cfg") return "setup";
809
- if (name !== void 0 && /^requirements[^/]*\.(?:txt|in)$/.test(name)) return "requirements";
810
- };
811
- const isPythonDependencyManifest = (file) => pythonDependencyManifestKind(file) !== void 0;
812
- /** Docker definition names use dot-delimited variants, excluding source and documentation. */
813
- const isDockerDefinitionPath = (file) => {
814
- if (excludedInputPath(file)) return false;
815
- const name = file.split("/").at(-1) ?? "";
816
- if (/\.(?:[cm]?[jt]sx?|mdx?|rst|txt|html?|py|rs|go|java|sh|c|cc|cpp|cxx|h|hpp|cs|rb|php|swift|kt|css|scss)$/i.test(name)) return false;
817
- return /^(?:[^./]+\.)*(?:dockerfile|containerfile)(?:\.[^./]+)*$/i.test(name);
818
- };
819
- /** Lockfiles establish ecosystem presence without reading their dependency graphs. */
820
- const toolchainInputKind = (file) => {
821
- if (file === ".agents/cloud-setup.sh") return "config";
822
- if (githubAutomationFileKind(file) !== void 0) return "config";
823
- if (excludedInputPath(file)) return void 0;
824
- if (pythonDependencyManifestKind(file) === "pipfile-lock" || javascriptDependencyLockfiles.some((name) => file.split("/").at(-1) === name) || /(?:^|\/)uv\.lock$/.test(file)) return "presence";
825
- if (githubAutomationFileKind(file) !== void 0 || isMiseConfigPath(file) || isPythonDependencyManifest(file) || isDockerDefinitionPath(file) || isComposeDefinitionPath(file) || isKubernetesDefinitionPath(file) || /(?:^|\/)(?:package\.json|pnpm-workspace\.yaml|stll-toolchain\.json|\.bun-version|\.node-version|\.nvmrc|\.python-version|rust-toolchain(?:\.toml)?|\.tool-versions|\.?mise\.toml|uv\.toml|\.uv\.toml|\.github\/dependabot\.ya?ml|Cargo\.toml)$/.test(file)) return "config";
826
- };
827
- //#endregion
828
- //#region src/toolchain-workspaces.ts
829
- /** Match manifest directories with the same positive and negative workspace patterns. */
830
- const workspaceContains = ({ directory, file, patterns, rootMembership }) => {
831
- const relative = path.posix.relative(directory, path.posix.dirname(file));
832
- if (relative === "" && rootMembership === "implicit") return true;
833
- if (relative === ".." || relative.startsWith("../")) return false;
834
- if (!Array.isArray(patterns)) return false;
835
- const normalize = (pattern) => path.posix.normalize(pattern);
836
- const selected = patterns.filter((value) => typeof value === "string");
837
- return picomatch(selected.filter((pattern) => !pattern.startsWith("!")).map(normalize))(relative) && !picomatch(selected.filter((pattern) => pattern.startsWith("!")).map((pattern) => normalize(pattern.slice(1))))(relative);
838
- };
839
- //#endregion
840
260
  //#region src/toolchain-dependabot.ts
841
261
  const dependabotRules = ["dependabot-policy"];
842
262
  const record$5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -893,7 +313,7 @@ const workspaceRoots = (files, ecosystem) => {
893
313
  }
894
314
  }
895
315
  try {
896
- const parsed = ecosystem === "npm" ? JSON.parse(files[file] ?? "") : parse(files[file] ?? "");
316
+ const parsed = ecosystem === "npm" ? JSON.parse(files[file] ?? "") : parse$2(files[file] ?? "");
897
317
  if (!record$5(parsed)) continue;
898
318
  if (ecosystem === "npm" && typeof parsed["packageManager"] === "string" && parsed["packageManager"].startsWith("pnpm@")) continue;
899
319
  const value = workspaceTable(parsed, ecosystem);
@@ -949,8 +369,8 @@ const workspaceRoots = (files, ecosystem) => {
949
369
  if (includes.some((pattern) => picomatch.isMatch(relative, pattern)) && !excludes.some((pattern) => picomatch.isMatch(relative, pattern.slice(1)))) memberDirectories.add(directory);
950
370
  }
951
371
  for (const directory of memberDirectories) try {
952
- const parsed = parse(files[path.posix.join(directory, "Cargo.toml")] ?? "");
953
- const workspace = parse(files[path.posix.join(root, "Cargo.toml")] ?? "")["workspace"];
372
+ const parsed = parse$2(files[path.posix.join(directory, "Cargo.toml")] ?? "");
373
+ const workspace = parse$2(files[path.posix.join(root, "Cargo.toml")] ?? "")["workspace"];
954
374
  const inherited = record$5(workspace) ? workspace["dependencies"] : void 0;
955
375
  const dependencyOwners = [parsed];
956
376
  const targets = parsed["target"];
@@ -1007,7 +427,7 @@ const pythonEcosystem = (file, files) => {
1007
427
  if (files[path.posix.join(root, "uv.lock")] !== void 0 || files[path.posix.join(root, "uv.toml")] !== void 0) return "uv";
1008
428
  const pyproject = files[path.posix.join(root, "pyproject.toml")];
1009
429
  if (pyproject !== void 0) try {
1010
- const tool = parse(pyproject)["tool"];
430
+ const tool = parse$2(pyproject)["tool"];
1011
431
  if (record$5(tool) && record$5(tool["uv"])) return "uv";
1012
432
  } catch {}
1013
433
  return "pip";
@@ -1160,8 +580,57 @@ const checkDependabot = ({ files, policy }) => {
1160
580
  return diagnostics;
1161
581
  };
1162
582
  //#endregion
1163
- //#region src/toolchain-engine-floors.ts
583
+ //#region src/toolchain-dynamic-selectors.ts
584
+ const locatorValidators = {
585
+ image: /^(?:jobs\.[\w-]+\.(?:container|services\.[\w-]+)|services\.[\w.-]+\.image)$/,
586
+ "bun-source": /^(?:jobs\.[\w-]+|runs)\.steps\.[\w-]+$/
587
+ };
588
+ const knownKind = (value) => typeof value === "string" && Object.hasOwn(locatorValidators, value);
1164
589
  const record$4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
590
+ /** One reviewed declaration identifies one unresolved selector, never a rule. */
591
+ const parseDynamicSelectors = (input) => {
592
+ if (input === void 0) return [];
593
+ if (!Array.isArray(input)) throw new Error("dynamicSelectors must be an array");
594
+ const result = [];
595
+ const keys = /* @__PURE__ */ new Set();
596
+ for (const entry of input) {
597
+ if (!record$4(entry) || Object.keys(entry).some((key) => ![
598
+ "path",
599
+ "kind",
600
+ "reason",
601
+ "at",
602
+ "line"
603
+ ].includes(key)) || typeof entry["path"] !== "string" || entry["path"] === "" || /[\\:$*?]|[^\S ]/.test(entry["path"]) || entry["path"].split("/").some((part) => part === "" || part === "." || part === "..") || typeof entry["reason"] !== "string" || entry["reason"].trim() === "") throw new Error("each dynamic selector requires an exact repository path, known kind and nonempty reason");
604
+ const kind = entry["kind"];
605
+ if (!knownKind(kind)) throw new Error("unknown dynamic selector kind");
606
+ let selector;
607
+ if (Object.hasOwn(entry, "line") && !Object.hasOwn(entry, "at") && typeof entry["line"] === "number" && Number.isSafeInteger(entry["line"]) && entry["line"] > 0) selector = {
608
+ path: entry["path"],
609
+ kind,
610
+ reason: entry["reason"],
611
+ line: entry["line"]
612
+ };
613
+ else if (!Object.hasOwn(entry, "line") && typeof entry["at"] === "string" && locatorValidators[kind].test(entry["at"])) selector = {
614
+ path: entry["path"],
615
+ kind,
616
+ reason: entry["reason"],
617
+ at: entry["at"]
618
+ };
619
+ else throw new Error("dynamic selector requires one positive line or a kind-specific job/service/step ID locator");
620
+ const key = JSON.stringify([
621
+ selector.path,
622
+ selector.kind,
623
+ selector.at ?? selector.line
624
+ ]);
625
+ if (keys.has(key)) throw new Error("dynamic selector locations must be unique");
626
+ keys.add(key);
627
+ result.push(selector);
628
+ }
629
+ return result;
630
+ };
631
+ //#endregion
632
+ //#region src/toolchain-engine-floors.ts
633
+ const record$3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1165
634
  const repositoryPath = (value) => typeof value === "string" && value !== "" && !/^[A-Za-z]:/.test(value) && !/[\\\s]/.test(value) && !Array.from(value).some((character) => character.charCodeAt(0) < 32) && value.split("/").every((part) => part !== "" && part !== "." && part !== "..");
1166
635
  const parseEngineFloors = (input) => {
1167
636
  if (input === void 0) return [];
@@ -1169,7 +638,7 @@ const parseEngineFloors = (input) => {
1169
638
  const result = [];
1170
639
  const jobs = /* @__PURE__ */ new Set();
1171
640
  for (const entry of input) {
1172
- if (!record$4(entry) || Object.keys(entry).some((key) => ![
641
+ if (!record$3(entry) || Object.keys(entry).some((key) => ![
1173
642
  "package",
1174
643
  "workflow",
1175
644
  "job"
@@ -1190,16 +659,16 @@ const resolveEngineFloor = (entry, files) => {
1190
659
  const workflowText = files[entry.workflow];
1191
660
  if (manifestText === void 0 || workflowText === void 0) throw new Error("engine floor package and workflow must be tracked");
1192
661
  const manifest = JSON.parse(manifestText);
1193
- if (!record$4(manifest) || manifest["private"] === true || typeof manifest["name"] !== "string" || manifest["name"].trim() === "" || !record$4(manifest["engines"]) || typeof manifest["engines"]["node"] !== "string") throw new Error("engine floor package must be published and declare engines.node");
662
+ if (!record$3(manifest) || manifest["private"] === true || typeof manifest["name"] !== "string" || manifest["name"].trim() === "" || !record$3(manifest["engines"]) || typeof manifest["engines"]["node"] !== "string") throw new Error("engine floor package must be published and declare engines.node");
1194
663
  const range = manifest["engines"]["node"];
1195
664
  const minimum = validRange(range) === null ? null : minVersion(range);
1196
665
  if (minimum === null || minimum.prerelease.length !== 0) throw new Error("engine floor engines.node must declare a stable minimum");
1197
666
  const document = parseDocument(workflowText, { merge: true });
1198
667
  if (document.errors.length !== 0) throw new Error("engine floor workflow must be valid YAML");
1199
668
  const workflow = document.toJS({ maxAliasCount: 100 });
1200
- if (!record$4(workflow) || !record$4(workflow["jobs"])) throw new Error("engine floor job must exist in its tracked workflow");
669
+ if (!record$3(workflow) || !record$3(workflow["jobs"])) throw new Error("engine floor job must exist in its tracked workflow");
1201
670
  const job = workflow["jobs"][entry.job];
1202
- if (!record$4(job)) throw new Error("engine floor job must exist in its tracked workflow");
671
+ if (!record$3(job)) throw new Error("engine floor job must exist in its tracked workflow");
1203
672
  if (!Array.isArray(job["steps"]) || Object.hasOwn(job, "uses")) throw new Error("engine floor job must declare steps and cannot reuse a workflow");
1204
673
  return {
1205
674
  ...entry,
@@ -1219,7 +688,7 @@ const packageRules = [
1219
688
  "typescript-layout",
1220
689
  "node-engine"
1221
690
  ];
1222
- const record$3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
691
+ const record$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1223
692
  const shellSubstitutions = (command) => {
1224
693
  const frames = [{
1225
694
  kind: "root",
@@ -1284,7 +753,7 @@ const compilerCommandSegments = (command) => {
1284
753
  const segments = [[]];
1285
754
  for (const source of [command, ...shellSubstitutions(command)]) {
1286
755
  segments.push([]);
1287
- const shellWords = source.match(/(?:[^\s"'\\;&|()]|\\[^\n]|"(?:[^"\\]|\\.)*"|'[^']*')+|&&|\|\||[();&|\n]/g);
756
+ const shellWords = source.match(/(?:[^\s"'\\;&|()]|\\[\s\S]|"(?:[^"\\]|\\[\s\S])*"|'[^']*')+|&&|\|\||[();&|\n]/g);
1288
757
  for (const word of shellWords ?? []) if (/^(?:&&|\|\||[;&|\n])$/.test(word)) segments.push([]);
1289
758
  else segments.at(-1)?.push(word);
1290
759
  }
@@ -1371,29 +840,29 @@ const launcherValueOptions = new Map(Object.entries({
1371
840
  "--input-type"
1372
841
  ]
1373
842
  }).map(([launcher, options]) => [launcher, new Set(options)]));
1374
- const invokesCompiler = (words) => {
1375
- const literal = (word) => {
1376
- let result = "";
1377
- let quote;
1378
- for (let index = 0; index < word.length; index += 1) {
1379
- const character = word.at(index);
1380
- if (character === quote) {
1381
- quote = void 0;
1382
- continue;
1383
- }
1384
- if (quote === void 0 && (character === "'" || character === "\"")) {
1385
- quote = character;
1386
- continue;
1387
- }
1388
- const next = word.at(index + 1);
1389
- if (character === "\\" && next !== void 0 && quote !== "'" && (quote === void 0 || /[$`"\\\n]/.test(next))) {
1390
- result += next;
1391
- index += 1;
1392
- } else result += character;
843
+ const decodeShellWord = (word) => {
844
+ let result = "";
845
+ let quote;
846
+ for (let index = 0; index < word.length; index += 1) {
847
+ const character = word.at(index);
848
+ if (character === quote) {
849
+ quote = void 0;
850
+ continue;
1393
851
  }
1394
- return result;
1395
- };
1396
- const tokens = words.map(literal);
852
+ if (quote === void 0 && (character === "'" || character === "\"")) {
853
+ quote = character;
854
+ continue;
855
+ }
856
+ const next = word.at(index + 1);
857
+ if (character === "\\" && next !== void 0 && quote !== "'" && (quote === void 0 || /[$`"\\\n]/.test(next))) {
858
+ if (next !== "\n") result += next;
859
+ index += 1;
860
+ } else result += character;
861
+ }
862
+ return result;
863
+ };
864
+ const invokesCompiler = (words) => {
865
+ const tokens = words.map(decodeShellWord);
1397
866
  const compiler = (word) => /(?:^|\/)(?:tsc|tsgo)(?:\.js)?$/.test(word);
1398
867
  let start = 0;
1399
868
  const skipOptions = (index, launcher) => {
@@ -1450,10 +919,10 @@ const checkPackageFiles = ({ files, policy }) => {
1450
919
  const document = pnpm ? parseDocument(text, { lineCounter }) : void 0;
1451
920
  if (document !== void 0 && document.errors.length > 0) throw new Error("invalid YAML");
1452
921
  const json = document === void 0 ? JSON.parse(text) : document.toJS();
1453
- if (!record$3(json)) throw new Error("manifest must be an object");
922
+ if (!record$2(json)) throw new Error("manifest must be an object");
1454
923
  if (pnpm) {
1455
- if (json["catalog"] !== void 0 && !record$3(json["catalog"])) throw new Error("catalog must be a mapping");
1456
- if (json["catalogs"] !== void 0 && (!record$3(json["catalogs"]) || Object.values(json["catalogs"]).some((catalog) => !record$3(catalog)))) throw new Error("named catalogs must be mappings");
924
+ if (json["catalog"] !== void 0 && !record$2(json["catalog"])) throw new Error("catalog must be a mapping");
925
+ if (json["catalogs"] !== void 0 && (!record$2(json["catalogs"]) || Object.values(json["catalogs"]).some((catalog) => !record$2(catalog)))) throw new Error("named catalogs must be mappings");
1457
926
  if (json["packages"] !== void 0 && (!Array.isArray(json["packages"]) || !json["packages"].every((pattern) => typeof pattern === "string"))) throw new Error("workspace packages must be patterns");
1458
927
  }
1459
928
  manifests.set(file, json);
@@ -1467,13 +936,13 @@ const checkPackageFiles = ({ files, policy }) => {
1467
936
  catalog: json["overrides"],
1468
937
  prefix: ["overrides"]
1469
938
  },
1470
- ...record$3(json["catalogs"]) ? Object.entries(json["catalogs"]).map(([name, catalog]) => ({
939
+ ...record$2(json["catalogs"]) ? Object.entries(json["catalogs"]).map(([name, catalog]) => ({
1471
940
  catalog,
1472
941
  prefix: ["catalogs", name]
1473
942
  })) : []
1474
943
  ];
1475
944
  for (const { catalog, prefix } of catalogEntries) {
1476
- if (!record$3(catalog)) continue;
945
+ if (!record$2(catalog)) continue;
1477
946
  for (const [name, value] of Object.entries(catalog)) {
1478
947
  const node = document.getIn([...prefix, name], true);
1479
948
  if (!isNode(node) || node.range === void 0 || node.range === null) continue;
@@ -1492,7 +961,7 @@ const checkPackageFiles = ({ files, policy }) => {
1492
961
  });
1493
962
  }
1494
963
  }
1495
- const catalogSources = (json) => [json, ...record$3(json["workspaces"]) ? [json["workspaces"]] : []];
964
+ const catalogSources = (json) => [json, ...record$2(json["workspaces"]) ? [json["workspaces"]] : []];
1496
965
  const pnpmWorkspaceFor = (file) => {
1497
966
  let directory = path.posix.dirname(file);
1498
967
  while (true) {
@@ -1515,8 +984,8 @@ const checkPackageFiles = ({ files, policy }) => {
1515
984
  patterns: workspace["packages"],
1516
985
  rootMembership: "implicit"
1517
986
  })) return void 0;
1518
- if (name === "" || name === "default") return workspace["catalog"] ?? (record$3(workspace["catalogs"]) ? workspace["catalogs"]["default"] : void 0);
1519
- return record$3(workspace["catalogs"]) ? workspace["catalogs"][name] : void 0;
987
+ if (name === "" || name === "default") return workspace["catalog"] ?? (record$2(workspace["catalogs"]) ? workspace["catalogs"]["default"] : void 0);
988
+ return record$2(workspace["catalogs"]) ? workspace["catalogs"][name] : void 0;
1520
989
  }
1521
990
  let directory = path.posix.dirname(file);
1522
991
  while (true) {
@@ -1524,7 +993,7 @@ const checkPackageFiles = ({ files, policy }) => {
1524
993
  if (manifest !== void 0) {
1525
994
  for (const source of catalogSources(manifest)) {
1526
995
  if (name === "" && source["catalog"] !== void 0) return source["catalog"];
1527
- if (record$3(source["catalogs"]) && source["catalogs"][name] !== void 0) return source["catalogs"][name];
996
+ if (record$2(source["catalogs"]) && source["catalogs"][name] !== void 0) return source["catalogs"][name];
1528
997
  }
1529
998
  if (manifest["workspaces"] !== void 0) return void 0;
1530
999
  }
@@ -1538,7 +1007,7 @@ const checkPackageFiles = ({ files, policy }) => {
1538
1007
  if (visited.has(value)) return void 0;
1539
1008
  visited.add(value);
1540
1009
  const catalog = catalogFor(file, value.slice(8));
1541
- value = record$3(catalog) ? catalog[name] : void 0;
1010
+ value = record$2(catalog) ? catalog[name] : void 0;
1542
1011
  }
1543
1012
  return value;
1544
1013
  };
@@ -1554,7 +1023,7 @@ const checkPackageFiles = ({ files, policy }) => {
1554
1023
  const workspaces = manifests.get(path.posix.join(directory, "package.json"))?.["workspaces"];
1555
1024
  if (workspaces !== void 0) return {
1556
1025
  directory,
1557
- patterns: record$3(workspaces) ? workspaces["packages"] : workspaces,
1026
+ patterns: record$2(workspaces) ? workspaces["packages"] : workspaces,
1558
1027
  source: "json"
1559
1028
  };
1560
1029
  if (directory === ".") return void 0;
@@ -1610,7 +1079,7 @@ const checkPackageFiles = ({ files, policy }) => {
1610
1079
  "peerDependencies"
1611
1080
  ]) {
1612
1081
  const dependencies = json[section];
1613
- if (!record$3(dependencies)) continue;
1082
+ if (!record$2(dependencies)) continue;
1614
1083
  for (const [name, value] of Object.entries(dependencies)) {
1615
1084
  if (!tsSpecifiers.has(name)) continue;
1616
1085
  const resolved = resolve(file, name, value);
@@ -1672,7 +1141,7 @@ const checkPackageFiles = ({ files, policy }) => {
1672
1141
  message: `packageManager Bun version must be ${policy.bun}, found ${JSON.stringify(manager)}`
1673
1142
  });
1674
1143
  const checkEntries = (entries, location) => {
1675
- if (!record$3(entries)) return;
1144
+ if (!record$2(entries)) return;
1676
1145
  for (const [name, raw] of Object.entries(entries)) {
1677
1146
  const value = resolve(file, name, raw);
1678
1147
  const tsAllowed = tsSpecifiers.get(name);
@@ -1709,12 +1178,12 @@ const checkPackageFiles = ({ files, policy }) => {
1709
1178
  "devDependencies",
1710
1179
  "optionalDependencies"
1711
1180
  ]) checkEntries(json[key]);
1712
- if (record$3(json["peerDependencies"])) {
1181
+ if (record$2(json["peerDependencies"])) {
1713
1182
  for (const [name, value] of Object.entries(json["peerDependencies"])) if (tsSpecifiers.has(name) && !peerRange(resolve(file, name, value))) checkEntries({ [name]: value });
1714
1183
  }
1715
1184
  for (const source of catalogSources(json)) {
1716
1185
  checkEntries(source["catalog"]);
1717
- if (!record$3(source["catalogs"])) continue;
1186
+ if (!record$2(source["catalogs"])) continue;
1718
1187
  for (const catalog of Object.values(source["catalogs"])) checkEntries(catalog);
1719
1188
  }
1720
1189
  const ownedNames = /* @__PURE__ */ new Set([...pins.keys(), ...tsSpecifiers.keys()]);
@@ -1726,7 +1195,7 @@ const checkPackageFiles = ({ files, policy }) => {
1726
1195
  "optionalDependencies"
1727
1196
  ]) {
1728
1197
  const entries = resolutionRoot[section];
1729
- if (!record$3(entries)) continue;
1198
+ if (!record$2(entries)) continue;
1730
1199
  for (const [name, value] of Object.entries(entries)) resolutionDependencies[name] = resolve(file, name, value);
1731
1200
  }
1732
1201
  const affectedSpecifiers = /* @__PURE__ */ new Map();
@@ -1746,7 +1215,7 @@ const checkPackageFiles = ({ files, policy }) => {
1746
1215
  "optionalDependencies"
1747
1216
  ]) {
1748
1217
  const entries = member[section];
1749
- if (!record$3(entries)) continue;
1218
+ if (!record$2(entries)) continue;
1750
1219
  for (const [name, value] of Object.entries(entries)) {
1751
1220
  if (!tsSpecifiers.has(name)) continue;
1752
1221
  const values = affectedSpecifiers.get(name) ?? /* @__PURE__ */ new Set();
@@ -1756,7 +1225,7 @@ const checkPackageFiles = ({ files, policy }) => {
1756
1225
  }
1757
1226
  }
1758
1227
  const checkResolutionMap = (entries, parent) => {
1759
- if (!record$3(entries)) return;
1228
+ if (!record$2(entries)) return;
1760
1229
  for (const [selector, replacement] of Object.entries(entries)) {
1761
1230
  const target = selector === "." ? parent : selector;
1762
1231
  if (target === void 0) continue;
@@ -1779,7 +1248,7 @@ const checkPackageFiles = ({ files, policy }) => {
1779
1248
  });
1780
1249
  continue;
1781
1250
  }
1782
- if (record$3(replacement)) {
1251
+ if (record$2(replacement)) {
1783
1252
  checkResolutionMap(replacement, target);
1784
1253
  continue;
1785
1254
  }
@@ -1813,7 +1282,7 @@ const checkPackageFiles = ({ files, policy }) => {
1813
1282
  };
1814
1283
  checkResolutionMap(json["overrides"]);
1815
1284
  checkResolutionMap(json["resolutions"]);
1816
- if (record$3(json["pnpm"])) checkResolutionMap(json["pnpm"]["overrides"]);
1285
+ if (record$2(json["pnpm"])) checkResolutionMap(json["pnpm"]["overrides"]);
1817
1286
  const localEntries = toolchainEntries(file, json);
1818
1287
  const usesCompiler = policy.typescriptInstallLayouts.some((layout) => localEntries.some(({ name }) => name === layout.compilerPackage));
1819
1288
  let selectedLayout = layoutFor(specifierMap(localEntries));
@@ -1837,7 +1306,7 @@ const checkPackageFiles = ({ files, policy }) => {
1837
1306
  key: "dependencies",
1838
1307
  message: "TypeScript compiler and compatibility packages must match one complete declared install layout"
1839
1308
  });
1840
- const command = record$3(json["scripts"]) ? json["scripts"]["typecheck"] : void 0;
1309
+ const command = record$2(json["scripts"]) ? json["scripts"]["typecheck"] : void 0;
1841
1310
  if (selectedLayout !== void 0 && typeof command === "string") {
1842
1311
  const normalizedExpected = selectedLayout.typecheckCommand.replace(/\s+/g, " ").trim();
1843
1312
  for (const words of compilerCommandSegments(command)) {
@@ -1855,7 +1324,7 @@ const checkPackageFiles = ({ files, policy }) => {
1855
1324
  }
1856
1325
  }
1857
1326
  }
1858
- if (record$3(json["peerDependencies"])) {
1327
+ if (record$2(json["peerDependencies"])) {
1859
1328
  const peerLayout = selectedLayout ?? workspaceLayoutFor(file)?.layout;
1860
1329
  for (const [name, raw] of Object.entries(json["peerDependencies"])) {
1861
1330
  if (!tsSpecifiers.has(name)) continue;
@@ -1879,7 +1348,7 @@ const checkPackageFiles = ({ files, policy }) => {
1879
1348
  });
1880
1349
  }
1881
1350
  }
1882
- if (record$3(json["engines"]) && json["engines"]["node"] !== void 0 && (typeof json["engines"]["node"] !== "string" || !nodeSupportRangeMatches(policy.node, json["engines"]["node"]))) add({
1351
+ if (record$2(json["engines"]) && json["engines"]["node"] !== void 0 && (typeof json["engines"]["node"] !== "string" || !nodeSupportRangeMatches(policy.node, json["engines"]["node"]))) add({
1883
1352
  file,
1884
1353
  rule: "node-engine",
1885
1354
  key: "node",
@@ -1901,7 +1370,7 @@ const runtimeRules = [
1901
1370
  "runtime-workflow",
1902
1371
  "action-pins"
1903
1372
  ];
1904
- const record$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1373
+ const record$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1905
1374
  const pythonSelectorMatches = (value, expected) => value === expected || typeof value === "string" && /^\d+\.\d+$/.test(expected) && value.startsWith(`${expected}.`) && /^\d+\.\d+\.\d+$/.test(value);
1906
1375
  const compareRelease = (left, right) => {
1907
1376
  for (let index = 0; index < Math.max(left.length, right.length); index++) {
@@ -1971,7 +1440,7 @@ const pythonRequirementMatches = (requirement, version) => {
1971
1440
  });
1972
1441
  };
1973
1442
  /** Check runtime declarations only in the files that declare or configure them. */
1974
- const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, repository, onDelegated, engineFloors = [], onEngineFloor }) => {
1443
+ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, repository, onDelegated, dynamicSelectors = [], onDynamicSelector, engineFloors = [], onEngineFloor }) => {
1975
1444
  const diagnostics = [];
1976
1445
  const name = path.posix.basename(file);
1977
1446
  const lines = text.split(/\r?\n/);
@@ -1983,6 +1452,16 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
1983
1452
  message
1984
1453
  });
1985
1454
  };
1455
+ let currentLocation;
1456
+ const dynamic = (value) => typeof value === "string" && value.includes("$");
1457
+ const authorizeDynamic = (kind, line, at = currentLocation) => {
1458
+ const matches = dynamicSelectors.filter((entry) => entry.path === file && entry.kind === kind && (entry.at !== void 0 ? entry.at === at : entry.line === line));
1459
+ if (matches.length !== 1) return false;
1460
+ const entry = matches.at(0);
1461
+ if (entry === void 0) return false;
1462
+ onDynamicSelector?.(entry);
1463
+ return true;
1464
+ };
1986
1465
  const lineOf = (key) => Math.max(1, lines.findIndex((line) => line.includes(key)) + 1);
1987
1466
  const pin = ({ rule, value, expected, label, line = 1, selector }) => {
1988
1467
  let matches = value === expected;
@@ -1996,7 +1475,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
1996
1475
  };
1997
1476
  const toml = (rule) => {
1998
1477
  try {
1999
- return parse(text);
1478
+ return parse$2(text);
2000
1479
  } catch {
2001
1480
  add({
2002
1481
  rule,
@@ -2055,11 +1534,11 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2055
1534
  const parsed = toml("rust-version");
2056
1535
  if (parsed !== void 0) {
2057
1536
  const toolchain = parsed["toolchain"];
2058
- const components = record$2(toolchain) ? toolchain["components"] : void 0;
1537
+ const components = record$1(toolchain) ? toolchain["components"] : void 0;
2059
1538
  const expected = Array.isArray(components) && components.includes("rustc-dev") ? policy.rustCompilerDevelopment : policy.rust;
2060
1539
  pin({
2061
1540
  rule: "rust-version",
2062
- value: record$2(toolchain) ? toolchain["channel"] : void 0,
1541
+ value: record$1(toolchain) ? toolchain["channel"] : void 0,
2063
1542
  expected,
2064
1543
  label: "Rust channel",
2065
1544
  line: lineOf("channel")
@@ -2080,21 +1559,21 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2080
1559
  if (candidate === file) return parsed;
2081
1560
  if (!trackedFiles.has(candidate)) return void 0;
2082
1561
  try {
2083
- return parse(readFile(candidate) ?? "");
1562
+ return parse$2(readFile(candidate) ?? "");
2084
1563
  } catch {
2085
1564
  return;
2086
1565
  }
2087
1566
  };
2088
1567
  if (parsed !== void 0) {
2089
1568
  const workspace = parsed["workspace"];
2090
- const defaults = record$2(workspace) ? workspace["package"] : void 0;
2091
- if (record$2(defaults) && defaults["rust-version"] !== void 0) rustFloor(defaults["rust-version"]);
1569
+ const defaults = record$1(workspace) ? workspace["package"] : void 0;
1570
+ if (record$1(defaults) && defaults["rust-version"] !== void 0) rustFloor(defaults["rust-version"]);
2092
1571
  const pkg = parsed["package"];
2093
- const version = record$2(pkg) ? pkg["rust-version"] : void 0;
2094
- if (version !== void 0) if (!record$2(version)) rustFloor(version);
1572
+ const version = record$1(pkg) ? pkg["rust-version"] : void 0;
1573
+ if (version !== void 0) if (!record$1(version)) rustFloor(version);
2095
1574
  else {
2096
1575
  let inherited;
2097
- if (version["workspace"] === true && record$2(pkg)) {
1576
+ if (version["workspace"] === true && record$1(pkg)) {
2098
1577
  const explicit = pkg["workspace"];
2099
1578
  let directory = path.posix.dirname(file);
2100
1579
  const explicitPath = typeof explicit === "string" && !explicit.includes("\\") && !explicit.includes(":") && !explicit.includes("${{") && !path.posix.isAbsolute(explicit) ? path.posix.normalize(path.posix.join(directory, explicit)) : void 0;
@@ -2102,9 +1581,9 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2102
1581
  if (explicitPath !== void 0) directory = explicitPath;
2103
1582
  while (true) {
2104
1583
  const table = readManifest(path.posix.join(directory, "Cargo.toml"))?.["workspace"];
2105
- if (record$2(table)) {
1584
+ if (record$1(table)) {
2106
1585
  const fields = table["package"];
2107
- inherited = record$2(fields) ? fields["rust-version"] : void 0;
1586
+ inherited = record$1(fields) ? fields["rust-version"] : void 0;
2108
1587
  break;
2109
1588
  }
2110
1589
  if (explicit !== void 0 || directory === ".") break;
@@ -2118,7 +1597,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2118
1597
  }
2119
1598
  if (name === "Pipfile") {
2120
1599
  const requires = toml("python-version")?.["requires"];
2121
- if (record$2(requires)) {
1600
+ if (record$1(requires)) {
2122
1601
  const minor = requires["python_version"];
2123
1602
  if (minor !== void 0 && minor !== policy.python.split(".").slice(0, 2).join(".")) add({
2124
1603
  rule: "python-version",
@@ -2137,7 +1616,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2137
1616
  const parsed = toml("python-version");
2138
1617
  if (parsed !== void 0) {
2139
1618
  const project = parsed["project"];
2140
- if (record$2(project) && project["requires-python"] !== void 0) {
1619
+ if (record$1(project) && project["requires-python"] !== void 0) {
2141
1620
  if (!pythonRequirementMatches(project["requires-python"], policy.python)) add({
2142
1621
  rule: "python-version",
2143
1622
  line: lineOf("requires-python"),
@@ -2145,9 +1624,9 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2145
1624
  });
2146
1625
  }
2147
1626
  const tool = parsed["tool"];
2148
- const configuredUv = record$2(tool) ? tool["uv"] : void 0;
1627
+ const configuredUv = record$1(tool) ? tool["uv"] : void 0;
2149
1628
  const uv = name === "uv.toml" ? parsed : configuredUv;
2150
- if (record$2(uv) && uv["python"] !== void 0) pin({
1629
+ if (record$1(uv) && uv["python"] !== void 0) pin({
2151
1630
  rule: "python-version",
2152
1631
  value: uv["python"],
2153
1632
  expected: policy.python,
@@ -2158,11 +1637,11 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2158
1637
  }
2159
1638
  if (isMiseConfigPath(file)) {
2160
1639
  const tools = toml("runtime-manager")?.["tools"];
2161
- if (record$2(tools)) for (const [tool, expected] of Object.entries(versions)) {
1640
+ if (record$1(tools)) for (const [tool, expected] of Object.entries(versions)) {
2162
1641
  const configured = tools[tool];
2163
1642
  if (configured !== void 0) pin({
2164
1643
  rule: "runtime-manager",
2165
- value: record$2(configured) ? configured["version"] : configured,
1644
+ value: record$1(configured) ? configured["version"] : configured,
2166
1645
  expected,
2167
1646
  selector: nodeTools.has(tool) ? "node" : void 0,
2168
1647
  label: `mise ${tool}`,
@@ -2184,12 +1663,13 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2184
1663
  line: index + 1
2185
1664
  });
2186
1665
  });
2187
- const checkRuntimeImage = ({ image, line, label }) => {
1666
+ const checkRuntimeImage = ({ image, line, label, at }) => {
2188
1667
  const unknownRuntime = () => {
1668
+ if (dynamic(image) && authorizeDynamic("image", line, at)) return;
2189
1669
  add({
2190
1670
  rule: "runtime-docker",
2191
1671
  line,
2192
- message: `cannot determine ${label} runtime; use a static image or a reasoned runtime-docker opt-out`
1672
+ message: `cannot determine ${label} runtime; use a static image or a scoped dynamicSelectors declaration`
2193
1673
  });
2194
1674
  };
2195
1675
  if (typeof image !== "string") {
@@ -2418,6 +1898,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2418
1898
  checkRuntimeImage({
2419
1899
  image: entry.image,
2420
1900
  line,
1901
+ at: entry.path.join("."),
2421
1902
  label: classified?.ecosystem === "docker-compose" ? "Compose image" : "Kubernetes image"
2422
1903
  });
2423
1904
  }
@@ -2445,15 +1926,29 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2445
1926
  return diagnostics;
2446
1927
  }
2447
1928
  const nodeLine = (node) => {
2448
- if (!record$2(node) || !Array.isArray(node["range"])) return 1;
1929
+ if (!record$1(node) || !Array.isArray(node["range"])) return 1;
2449
1930
  const offset = node["range"].at(0);
2450
1931
  return typeof offset === "number" ? text.slice(0, offset).split("\n").length : 1;
2451
1932
  };
2452
1933
  let checkoutBindings = [];
2453
1934
  let checkoutDestinations = /* @__PURE__ */ new Map();
2454
1935
  let unknownCheckoutDestination = false;
1936
+ let unreviewableCheckoutSource = false;
2455
1937
  const staticRepositoryPath = (value) => typeof value === "string" && value !== "" && !value.includes("${{") && !value.includes("\\") && !value.includes(":") && !path.posix.isAbsolute(value) && !value.split("/").includes("..");
2456
1938
  const normalizeCheckoutPath = (prefix) => path.posix.normalize(prefix).replace(/\/$/, "") || ".";
1939
+ const checkoutProvidersFor = (selector) => {
1940
+ if (selector === void 0) return checkoutBindings;
1941
+ let deepest;
1942
+ for (const entry of checkoutBindings) {
1943
+ if (entry.path !== "." && selector !== entry.path && !selector.startsWith(`${entry.path}/`)) continue;
1944
+ if (deepest === void 0 || entry.path !== "." && (deepest.path === "." || entry.path.length > deepest.path.length)) deepest = entry;
1945
+ }
1946
+ return checkoutBindings.filter((entry) => entry.path === deepest?.path);
1947
+ };
1948
+ const hasAmbiguousCheckoutWriters = (selector, providers) => {
1949
+ if (providers.length > 0) return providers.some((entry) => (checkoutDestinations.get(entry.path) ?? 0) > 1);
1950
+ return [...checkoutDestinations].some(([prefix, count]) => count > 1 && (selector === void 0 || prefix === "." || selector === prefix || selector.startsWith(`${prefix}/`)));
1951
+ };
2457
1952
  const checkReference = ({ value, tool, line }) => {
2458
1953
  const allowed = {
2459
1954
  node: [".node-version", ".nvmrc"],
@@ -2462,8 +1957,28 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2462
1957
  }[tool];
2463
1958
  const rule = tool === "bun" ? "bun-pins" : "runtime-workflow";
2464
1959
  const selector = staticRepositoryPath(value) ? path.posix.normalize(value) : void 0;
2465
- const binding = selector === void 0 ? void 0 : checkoutBindings.findLast((entry) => entry.path === "." || selector === entry.path || selector.startsWith(`${entry.path}/`));
1960
+ const providers = checkoutProvidersFor(selector);
1961
+ const binding = selector === void 0 ? void 0 : providers.at(-1);
1962
+ const dynamicSource = providers.some((entry) => entry.dynamicSource);
1963
+ const sparseFailure = providers.find((entry) => {
1964
+ if (entry.sparse.mode === "invalid") return true;
1965
+ if (entry.sparse.mode !== "files" && entry.sparse.mode !== "mixed") return false;
1966
+ if (selector === void 0) return true;
1967
+ const selectedFile = entry.path === "." ? selector : path.posix.relative(entry.path, selector);
1968
+ return !entry.sparse.paths.has(selectedFile);
1969
+ });
1970
+ if (sparseFailure !== void 0) {
1971
+ let missingFile = "the selected version file";
1972
+ if (selector !== void 0) missingFile = sparseFailure.path === "." ? selector : path.posix.relative(sparseFailure.path, selector);
1973
+ add({
1974
+ rule,
1975
+ line,
1976
+ message: `checkout sparse-checkout must explicitly list ${missingFile} without invalid literal patterns`
1977
+ });
1978
+ return;
1979
+ }
2466
1980
  if (selector === void 0) {
1981
+ if (tool === "bun" && dynamic(value) && !unreviewableCheckoutSource && authorizeDynamic("bun-source", line)) return;
2467
1982
  add({
2468
1983
  rule,
2469
1984
  line,
@@ -2471,7 +1986,8 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2471
1986
  });
2472
1987
  return;
2473
1988
  }
2474
- if (unknownCheckoutDestination || [...checkoutDestinations].some(([prefix, count]) => count > 1 && (prefix === "." || selector === prefix || selector.startsWith(`${prefix}/`)))) {
1989
+ if (unknownCheckoutDestination || hasAmbiguousCheckoutWriters(selector, providers)) {
1990
+ if (tool === "bun" && dynamicSource && !unreviewableCheckoutSource && authorizeDynamic("bun-source", line)) return;
2475
1991
  add({
2476
1992
  rule,
2477
1993
  line,
@@ -2480,6 +1996,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2480
1996
  return;
2481
1997
  }
2482
1998
  if (binding?.source === "untrusted") {
1999
+ if (tool === "bun" && dynamicSource && !unreviewableCheckoutSource && authorizeDynamic("bun-source", line)) return;
2483
2000
  add({
2484
2001
  rule,
2485
2002
  line: binding.line,
@@ -2496,13 +2013,15 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2496
2013
  });
2497
2014
  return;
2498
2015
  }
2499
- if (binding !== void 0 && (binding.sparse.mode === "invalid" || binding.sparse.mode === "files" && !binding.sparse.paths.has(target))) {
2500
- add({
2501
- rule,
2502
- line,
2503
- message: `checkout sparse-checkout must explicitly list ${target} without dynamic, glob, or negation patterns`
2504
- });
2505
- return;
2016
+ if (binding !== void 0 && (binding.sparse.mode === "dynamic" || binding.sparse.mode === "mixed")) {
2017
+ if (tool !== "bun" || unreviewableCheckoutSource || !authorizeDynamic("bun-source", line)) {
2018
+ add({
2019
+ rule,
2020
+ line,
2021
+ message: `checkout sparse-checkout must explicitly list ${target} without dynamic, glob, or negation patterns`
2022
+ });
2023
+ return;
2024
+ }
2506
2025
  }
2507
2026
  if (binding?.source === "delegated") {
2508
2027
  onDelegated?.({
@@ -2521,7 +2040,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2521
2040
  const manifest = JSON.parse(content ?? "");
2522
2041
  pin({
2523
2042
  rule,
2524
- value: record$2(manifest) ? manifest["packageManager"] : void 0,
2043
+ value: record$1(manifest) ? manifest["packageManager"] : void 0,
2525
2044
  expected: `bun@${policy.bun}`,
2526
2045
  label: "setup-bun version file packageManager",
2527
2046
  line
@@ -2624,10 +2143,10 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2624
2143
  return inputs;
2625
2144
  };
2626
2145
  const getInput = (node, key) => inputsOf(node).get(key.toUpperCase());
2627
- const dynamicRefBody = (value) => {
2628
- if (typeof value !== "string" || !value.startsWith("${{")) return void 0;
2146
+ const expressionAt = (value, start) => {
2147
+ if (!value.startsWith("${{", start)) return void 0;
2629
2148
  let quote;
2630
- for (let index = 3; index < value.length; index++) {
2149
+ for (let index = start + 3; index < value.length; index++) {
2631
2150
  const character = value.at(index);
2632
2151
  if (quote !== void 0) {
2633
2152
  if (character === quote) if (quote === "'" && value.at(index + 1) === "'") index++;
@@ -2637,9 +2156,33 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2637
2156
  }
2638
2157
  if (character === "'" || character === "\"") quote = character;
2639
2158
  else if (value.startsWith("${{", index)) return void 0;
2640
- else if (character === "}" && value.at(index + 1) === "}") return index === value.length - 2 ? value.slice(3, index).trim() : void 0;
2159
+ else if (character === "}" && value.at(index + 1) === "}") return {
2160
+ body: value.slice(start + 3, index).trim(),
2161
+ end: index + 2
2162
+ };
2641
2163
  }
2642
2164
  };
2165
+ const dynamicRefBody = (value) => {
2166
+ if (typeof value !== "string") return void 0;
2167
+ const expression = expressionAt(value, 0);
2168
+ return expression?.end === value.length ? expression.body : void 0;
2169
+ };
2170
+ const sparseEntryIsValid = (entry) => {
2171
+ let literalPath = "";
2172
+ let position = 0;
2173
+ while (position < entry.length) {
2174
+ const start = entry.indexOf("${{", position);
2175
+ const literal = entry.slice(position, start < 0 ? entry.length : start);
2176
+ if (/[!$*?[\]\\:]/.test(literal) || literal.split("/").includes("..")) return false;
2177
+ literalPath += literal;
2178
+ if (start < 0) break;
2179
+ const expression = expressionAt(entry, start);
2180
+ if (expression === void 0 || expression.body === "") return false;
2181
+ literalPath += "expression";
2182
+ position = expression.end;
2183
+ }
2184
+ return staticRepositoryPath(literalPath);
2185
+ };
2643
2186
  const checkoutPrefixOf = (node) => {
2644
2187
  const destination = getInput(node, "path");
2645
2188
  if (destination === void 0) return ".";
@@ -2652,11 +2195,19 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2652
2195
  const value = getInput(node, "sparse-checkout");
2653
2196
  const cone = getInput(node, "sparse-checkout-cone-mode");
2654
2197
  if (!isScalar(value) || typeof value.value !== "string" || cone !== void 0 && (!isScalar(cone) || cone.value !== true && cone.value !== false && cone.value !== "true" && cone.value !== "false")) return { mode: "invalid" };
2655
- const paths = value.value.split(/\r?\n/).map((entry) => entry.trim()).filter((entry) => entry !== "");
2656
- if (paths.length === 0 || paths.some((entry) => !staticRepositoryPath(entry) || entry.startsWith("!") || /[*?[\]]/.test(entry))) return { mode: "invalid" };
2198
+ const paths = value.value.split(/\r?\n/).map((entry) => entry.trim().replace(/^\//, "")).filter((entry) => entry !== "");
2199
+ if (paths.length === 0 || paths.some((entry) => !sparseEntryIsValid(entry))) return { mode: "invalid" };
2200
+ const literalPaths = new Set(paths.filter((entry) => !dynamic(entry)));
2201
+ if (paths.some(dynamic)) {
2202
+ if (literalPaths.size === 0) return { mode: "dynamic" };
2203
+ return {
2204
+ mode: "mixed",
2205
+ paths: literalPaths
2206
+ };
2207
+ }
2657
2208
  return {
2658
2209
  mode: "files",
2659
- paths: new Set(paths)
2210
+ paths: literalPaths
2660
2211
  };
2661
2212
  };
2662
2213
  let currentEngineFloor;
@@ -2686,8 +2237,9 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2686
2237
  }
2687
2238
  if (value.startsWith("./")) {
2688
2239
  const selector = staticRepositoryPath(value) ? path.posix.normalize(value) : void 0;
2689
- const binding = selector === void 0 ? void 0 : checkoutBindings.findLast((entry) => entry.path === "." || selector === entry.path || selector.startsWith(`${entry.path}/`));
2690
- if (selector === void 0 || unknownCheckoutDestination || binding !== void 0 && binding.source !== "tracked" || [...checkoutDestinations].some(([prefix, count]) => count > 1 && (prefix === "." || selector === prefix || selector.startsWith(`${prefix}/`)))) add({
2240
+ const providers = checkoutProvidersFor(selector);
2241
+ const binding = selector === void 0 ? void 0 : providers.at(-1);
2242
+ if (selector === void 0 || unknownCheckoutDestination || binding !== void 0 && binding.source !== "tracked" || hasAmbiguousCheckoutWriters(selector, providers)) add({
2691
2243
  rule: "action-pins",
2692
2244
  line,
2693
2245
  message: "local actions require an unambiguous checkout of the current source snapshot"
@@ -2743,24 +2295,55 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2743
2295
  const checkoutRepository = getInput(node, "repository");
2744
2296
  const checkoutRef = getInput(node, "ref");
2745
2297
  const prefix = checkoutPrefixOf(node);
2298
+ const clean = getInput(node, "clean");
2299
+ const cleanIsResolved = clean === void 0 || isScalar(clean) && !dynamic(clean.value);
2746
2300
  let repo;
2747
2301
  if (checkoutRepository !== void 0) repo = isScalar(checkoutRepository) ? checkoutRepository.value : null;
2748
2302
  const self = repo === void 0 || repo === "${{ github.repository }}" || typeof repo === "string" && repository !== void 0 && repo.toLowerCase() === repository.toLowerCase();
2749
2303
  const workflowSource = repo === "${{ job.workflow_repository }}" && isScalar(checkoutRef) && checkoutRef.value === "${{ job.workflow_sha }}";
2750
- const currentSource = self && checkoutRef === void 0;
2751
- const refValue = isScalar(checkoutRef) ? checkoutRef.value : void 0;
2304
+ const triggers = getNode(document.contents, "on");
2305
+ const callerContext = (() => {
2306
+ if (isMap(triggers)) return [...keysOf(triggers)];
2307
+ if (isSeq(triggers)) return triggers.items.map((event) => isScalar(event) ? event.value : void 0);
2308
+ if (isScalar(triggers)) return [triggers.value];
2309
+ return [];
2310
+ })().includes("workflow_call");
2311
+ let refValue = isScalar(checkoutRef) ? checkoutRef.value : void 0;
2312
+ if (callerContext && (repo === void 0 || repo === "${{ github.repository }}") && checkoutRef === void 0) refValue = "${{ github.sha }}";
2313
+ const currentRef = dynamicRefBody(refValue);
2314
+ const currentSource = self && !callerContext && (checkoutRef === void 0 || currentRef === "github.sha");
2752
2315
  const dynamicRef = dynamicRefBody(refValue);
2753
- const delegatedSource = (self || repo === "${{ job.workflow_repository }}") && dynamicRef !== void 0 && dynamicRef !== "" && !/^(?:'(?:[^']|'')*'|"(?:[^"\\]|\\.)*"|(?:true|false|null)|-?(?:0x[\da-f]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?))$/i.test(dynamicRef) && !/^(?:github\s*(?:\.\s*sha|\[\s*['"]sha['"]\s*\])|job\s*(?:\.\s*workflow_sha|\[\s*['"]workflow_sha['"]\s*\]))$/i.test(dynamicRef);
2316
+ const delegatedSource = !currentSource && (self || repo === "${{ job.workflow_repository }}") && dynamicRef !== void 0 && dynamicRef !== "" && !/^(?:'(?:[^']|'')*'|"(?:[^"\\]|\\.)*"|(?:true|false|null)|-?(?:0x[\da-f]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?))$/i.test(dynamicRef) && (!/^(?:github\s*(?:\.\s*sha|\[\s*['"]sha['"]\s*\])|job\s*(?:\.\s*workflow_sha|\[\s*['"]workflow_sha['"]\s*\]))$/i.test(dynamicRef) || callerContext && currentRef === "github.sha");
2317
+ const validSource = cleanIsResolved && ref === approved?.sha && sourceMap({
2318
+ node,
2319
+ key: "if"
2320
+ }) === void 0 && sourceMap({
2321
+ node,
2322
+ key: "continue-on-error"
2323
+ }) === void 0 && (currentSource || workflowSource || delegatedSource);
2324
+ if (!validSource) unreviewableCheckoutSource = true;
2754
2325
  if (!staticRepositoryPath(prefix)) checkoutBindings.push({
2755
2326
  path: ".",
2327
+ dynamicSource: validSource && dynamic(prefix),
2756
2328
  source: "untrusted",
2757
2329
  reason: "checkout destination must be a static repository path",
2758
2330
  line,
2759
- sparse: { mode: "invalid" }
2331
+ sparse: sparseCheckoutOf(node)
2760
2332
  });
2761
2333
  else {
2762
2334
  const destination = normalizeCheckoutPath(prefix);
2763
- const trustedWriter = ref === approved?.sha && sourceMap({
2335
+ checkoutBindings = checkoutBindings.map((entry) => {
2336
+ if (entry.path === destination || destination !== "." && !entry.path.startsWith(`${destination}/`)) return entry;
2337
+ return {
2338
+ path: entry.path,
2339
+ sparse: { mode: "all" },
2340
+ dynamicSource: false,
2341
+ source: "untrusted",
2342
+ reason: "a later ancestor checkout invalidates this descendant destination",
2343
+ line
2344
+ };
2345
+ });
2346
+ const trustedWriter = cleanIsResolved && ref === approved?.sha && sourceMap({
2764
2347
  node,
2765
2348
  key: "if"
2766
2349
  }) === void 0 && sourceMap({
@@ -2769,18 +2352,21 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2769
2352
  }) === void 0 && checkoutDestinations.get(destination) === 1 && !unknownCheckoutDestination;
2770
2353
  if (trustedWriter && delegatedSource && typeof refValue === "string") checkoutBindings.push({
2771
2354
  path: destination,
2355
+ dynamicSource: validSource && delegatedSource,
2772
2356
  sparse: sparseCheckoutOf(node),
2773
2357
  source: "delegated",
2774
2358
  ref: refValue
2775
2359
  });
2776
2360
  else if (trustedWriter && (workflowSource || currentSource)) checkoutBindings.push({
2777
2361
  path: destination,
2362
+ dynamicSource: validSource && delegatedSource,
2778
2363
  sparse: sparseCheckoutOf(node),
2779
2364
  source: "tracked"
2780
2365
  });
2781
2366
  else {
2782
2367
  let reason = "checkout does not select a trusted repository source snapshot";
2783
- if (ref !== approved?.sha) reason = "actions/checkout must use the approved action SHA";
2368
+ if (!cleanIsResolved) reason = "checkout clean must be statically resolved";
2369
+ else if (ref !== approved?.sha) reason = "actions/checkout must use the approved action SHA";
2784
2370
  else if (sourceMap({
2785
2371
  node,
2786
2372
  key: "if"
@@ -2791,6 +2377,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2791
2377
  else if (checkoutDestinations.get(destination) !== 1 || unknownCheckoutDestination) reason = "checkout destination must have exactly one static writer";
2792
2378
  checkoutBindings.push({
2793
2379
  path: destination,
2380
+ dynamicSource: validSource && delegatedSource,
2794
2381
  sparse: sparseCheckoutOf(node),
2795
2382
  source: "untrusted",
2796
2383
  reason,
@@ -2839,21 +2426,23 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2839
2426
  line: reference === void 0 ? line : nodeLine(reference)
2840
2427
  });
2841
2428
  };
2842
- const checkContainer = (node) => {
2429
+ const checkContainer = (node, at) => {
2843
2430
  node = resolveNode(node);
2844
2431
  if (node === void 0) return;
2845
2432
  const image = isMap(node) ? getNode(node, "image") : node;
2846
2433
  checkRuntimeImage({
2847
2434
  image: isScalar(image) ? image.value : void 0,
2848
2435
  line: nodeLine(image ?? node),
2849
- label: "container image"
2436
+ label: "container image",
2437
+ at
2850
2438
  });
2851
2439
  };
2852
- const checkSteps = (node, floor) => {
2440
+ const checkSteps = (node, locationPrefix, floor) => {
2853
2441
  currentEngineFloor = void 0;
2854
2442
  checkoutBindings = [];
2855
2443
  checkoutDestinations = /* @__PURE__ */ new Map();
2856
2444
  unknownCheckoutDestination = false;
2445
+ unreviewableCheckoutSource = false;
2857
2446
  node = resolveNode(node);
2858
2447
  if (!isSeq(node)) return;
2859
2448
  for (const step of node.items) {
@@ -2869,9 +2458,14 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2869
2458
  }
2870
2459
  currentEngineFloor = floor;
2871
2460
  try {
2872
- for (const step of node.items) checkAction(step);
2461
+ for (const step of node.items) {
2462
+ const id = getNode(step, "id");
2463
+ currentLocation = isScalar(id) && typeof id.value === "string" ? `${locationPrefix}.steps.${id.value}` : void 0;
2464
+ checkAction(step);
2465
+ }
2873
2466
  } finally {
2874
2467
  currentEngineFloor = void 0;
2468
+ currentLocation = void 0;
2875
2469
  }
2876
2470
  };
2877
2471
  try {
@@ -2885,15 +2479,15 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2885
2479
  currentEngineFloor = void 0;
2886
2480
  const job = getNode(jobs, key);
2887
2481
  checkAction(job);
2888
- checkContainer(getNode(job, "container"));
2482
+ checkContainer(getNode(job, "container"), `jobs.${key}.container`);
2889
2483
  const services = getNode(job, "services");
2890
- if (isMap(services)) for (const service of keysOf(services)) checkContainer(getNode(services, service));
2891
- checkSteps(getNode(job, "steps"), engineFloors.find((entry) => entry.job === key));
2484
+ if (isMap(services)) for (const service of keysOf(services)) checkContainer(getNode(services, service), `jobs.${key}.services.${service}`);
2485
+ checkSteps(getNode(job, "steps"), `jobs.${key}`, engineFloors.find((entry) => entry.job === key));
2892
2486
  }
2893
2487
  } else {
2894
2488
  const runs = getNode(document.contents, "runs");
2895
2489
  const using = getNode(runs, "using");
2896
- if (isScalar(using) && using.value === "composite") checkSteps(getNode(runs, "steps"));
2490
+ if (isScalar(using) && using.value === "composite") checkSteps(getNode(runs, "steps"), "runs");
2897
2491
  if (isScalar(using) && using.value === "docker") {
2898
2492
  const image = getNode(runs, "image");
2899
2493
  const value = isScalar(image) ? image.value : void 0;
@@ -2925,192 +2519,6 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2925
2519
  return diagnostics;
2926
2520
  };
2927
2521
  //#endregion
2928
- //#region src/toolchain-schema.ts
2929
- const packagePinKeys = [
2930
- "oxlint",
2931
- "oxlint-tsgolint",
2932
- "@oxlint/plugins",
2933
- "@stll/oxlint-plugin",
2934
- "oxfmt",
2935
- "lefthook"
2936
- ];
2937
- const valkeyArchitectures = ["amd64", "arm64"];
2938
- const valkeyArtifactPackages = {
2939
- server: "valkey-server",
2940
- tools: "valkey-tools"
2941
- };
2942
- const record$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2943
- const artifactRecord = (value, keys) => {
2944
- if (!record$1(value) || Object.keys(value).some((key) => !keys.includes(key)) || keys.some((key) => !Object.hasOwn(value, key))) throw new Error("valkeyArtifacts requires complete, closed artifact objects");
2945
- return value;
2946
- };
2947
- const parseValkeyArtifacts = (input, version) => {
2948
- const architectures = artifactRecord(input, valkeyArchitectures);
2949
- const line = version.split(".").slice(0, 2).join(".");
2950
- const parseArchitecture = (architecture) => {
2951
- const packages = artifactRecord(architectures[architecture], Object.keys(valkeyArtifactPackages));
2952
- const parseArtifact = (kind) => {
2953
- const artifact = artifactRecord(packages[kind], ["url", "sha256"]);
2954
- const url = artifact["url"];
2955
- const sha256 = artifact["sha256"];
2956
- const expected = `https://download.valkey.io/packaging/valkey-${line}/deb/ubuntu2404/${architecture}/${valkeyArtifactPackages[kind]}_${version}-1.noble_${architecture}.deb`;
2957
- if (typeof url !== "string" || url !== expected) throw new Error("valkeyArtifacts URL must select the exact official versioned Ubuntu artifact");
2958
- if (typeof sha256 !== "string" || !/^[a-f0-9]{64}$/.test(sha256)) throw new Error("valkeyArtifacts requires a lowercase SHA256 pin");
2959
- return {
2960
- url,
2961
- sha256
2962
- };
2963
- };
2964
- return {
2965
- server: parseArtifact("server"),
2966
- tools: parseArtifact("tools")
2967
- };
2968
- };
2969
- return {
2970
- amd64: parseArchitecture("amd64"),
2971
- arm64: parseArchitecture("arm64")
2972
- };
2973
- };
2974
- /** Reject unsupported or incomplete installed policies before inspecting a consumer. */
2975
- const parseToolchainPolicy = (input) => {
2976
- if (!record$1(input) || input["schemaVersion"] !== 1) throw new Error("toolchain.json must use schemaVersion 1");
2977
- const string = (object, key) => {
2978
- const value = object[key];
2979
- if (typeof value !== "string" || value.trim() === "") throw new Error(`toolchain.json requires a nonempty ${key}`);
2980
- return value;
2981
- };
2982
- const packages = {};
2983
- for (const key of packagePinKeys) packages[key] = string(input, key);
2984
- const bun = string(input, "bun");
2985
- const typescript = string(input, "typescript");
2986
- const node = string(input, "node");
2987
- const python = string(input, "python");
2988
- const postgres = string(input, "postgres");
2989
- const valkey = string(input, "valkey");
2990
- const rust = string(input, "rust");
2991
- const rustCompilerDevelopment = string(input, "rustCompilerDevelopment");
2992
- for (const [name, value] of Object.entries({
2993
- ...packages,
2994
- bun,
2995
- typescript
2996
- })) if (valid(value) !== value) throw new Error(`toolchain.json ${name} must be an exact release`);
2997
- if (!/^[1-9]\d*$/.test(postgres) || !Number.isSafeInteger(Number(postgres))) throw new Error("PostgreSQL must be a canonical positive major");
2998
- if (valid(valkey) !== valkey || !/^\d+\.\d+\.\d+$/.test(valkey)) throw new Error("Valkey must be an exact stable release");
2999
- const valkeyArtifacts = parseValkeyArtifacts(input["valkeyArtifacts"], valkey);
3000
- if (!nodePolicyValid(node)) throw new Error("Node must be a canonical major N.x series");
3001
- if (!/^\d+\.\d+(?:\.\d+)?$/.test(python)) throw new Error("Python must be an exact minor or patch release");
3002
- if (!/^\d+\.\d+\.\d+$/.test(rust)) throw new Error("Rust must be an exact stable release");
3003
- if (!/^nightly-\d{4}-\d{2}-\d{2}$/.test(rustCompilerDevelopment)) throw new Error("Rust compiler development must be a dated nightly");
3004
- const compatibility = input["typescript6Compatibility"];
3005
- if (!record$1(compatibility)) throw new Error("missing TypeScript compatibility policy");
3006
- const version = string(compatibility, "version");
3007
- const packageAlias = string(compatibility, "packageAlias");
3008
- if (valid(version) !== version) throw new Error("TypeScript compatibility must be an exact release");
3009
- const layouts = input["typescriptInstallLayouts"];
3010
- if (!Array.isArray(layouts) || layouts.length === 0) throw new Error("missing TypeScript install layouts");
3011
- const typescriptInstallLayouts = layouts.map((layout) => {
3012
- if (!record$1(layout)) throw new Error("invalid TypeScript install layout");
3013
- const compilerPackage = string(layout, "compilerPackage");
3014
- const compilerSpecifier = string(layout, "compilerSpecifier");
3015
- const typecheckCommand = string(layout, "typecheckCommand");
3016
- switch (layout["type"]) {
3017
- case "direct":
3018
- if (compilerSpecifier !== typescript || compilerPackage !== "typescript") throw new Error("direct TypeScript layout differs from shared version");
3019
- return {
3020
- type: "direct",
3021
- compilerPackage,
3022
- compilerSpecifier,
3023
- typecheckCommand
3024
- };
3025
- case "split-compatibility": {
3026
- const compatibilityPackage = string(layout, "compatibilityPackage");
3027
- const compatibilitySpecifier = string(layout, "compatibilitySpecifier");
3028
- if (compilerSpecifier !== `npm:typescript@${typescript}` || compatibilitySpecifier !== version) throw new Error("split TypeScript layout differs from shared versions");
3029
- return {
3030
- type: "split-compatibility",
3031
- compilerPackage,
3032
- compilerSpecifier,
3033
- compatibilityPackage,
3034
- compatibilitySpecifier,
3035
- typecheckCommand
3036
- };
3037
- }
3038
- default: throw new Error("unsupported TypeScript install layout");
3039
- }
3040
- });
3041
- const actionsInput = input["actions"];
3042
- if (!record$1(actionsInput) || Object.keys(actionsInput).length === 0) throw new Error("missing approved actions");
3043
- const actions = {};
3044
- for (const [name, action] of Object.entries(actionsInput)) {
3045
- if (!record$1(action)) throw new Error(`invalid action policy: ${name}`);
3046
- const sha = string(action, "sha");
3047
- const actionVersion = string(action, "version");
3048
- if (!/^[a-f0-9]{40}$/.test(sha) || !/^v\d+(?:\.\d+)*$/.test(actionVersion)) throw new Error(`invalid action SHA/version: ${name}`);
3049
- actions[name] = {
3050
- sha,
3051
- version: actionVersion
3052
- };
3053
- }
3054
- const dependabotInput = input["dependabot"];
3055
- if (!record$1(dependabotInput) || !record$1(dependabotInput["schedule"]) || !record$1(dependabotInput["cooldown"]) || !record$1(dependabotInput["groups"])) throw new Error("missing Dependabot policy");
3056
- const scheduleInput = dependabotInput["schedule"];
3057
- const schedule = {
3058
- interval: string(scheduleInput, "interval"),
3059
- day: string(scheduleInput, "day"),
3060
- time: string(scheduleInput, "time"),
3061
- timezone: string(scheduleInput, "timezone")
3062
- };
3063
- const defaultDays = dependabotInput["cooldown"]["defaultDays"];
3064
- if (typeof defaultDays !== "number" || !Number.isInteger(defaultDays) || defaultDays < 5) throw new Error("Dependabot cooldown must be at least five days");
3065
- const strings = (value) => {
3066
- if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string")) throw new Error("Dependabot group values must be string arrays");
3067
- return value;
3068
- };
3069
- const groups = {};
3070
- for (const [name, group] of Object.entries(dependabotInput["groups"])) {
3071
- if (!record$1(group)) throw new Error(`invalid Dependabot group: ${name}`);
3072
- groups[name] = {
3073
- patterns: strings(group["patterns"]),
3074
- updateTypes: strings(group["updateTypes"])
3075
- };
3076
- }
3077
- return {
3078
- bun,
3079
- typescript,
3080
- node,
3081
- postgres,
3082
- valkey,
3083
- valkeyArtifacts,
3084
- python,
3085
- rust,
3086
- rustCompilerDevelopment,
3087
- packages,
3088
- actions,
3089
- typescriptInstallLayouts,
3090
- typescript6Compatibility: {
3091
- version,
3092
- packageAlias
3093
- },
3094
- dependabot: {
3095
- schedule,
3096
- cooldown: { defaultDays },
3097
- groups,
3098
- ignoredPackages: [.../* @__PURE__ */ new Set([
3099
- ...Object.keys(packages),
3100
- ...typescriptInstallLayouts.flatMap((layout) => layout.type === "direct" ? [layout.compilerPackage] : [layout.compilerPackage, layout.compatibilityPackage]),
3101
- packageAlias,
3102
- "bun-types"
3103
- ])],
3104
- ignoredActions: Object.keys(actions),
3105
- ignoredImages: [
3106
- "node",
3107
- "python",
3108
- "oven/bun"
3109
- ]
3110
- }
3111
- };
3112
- };
3113
- //#endregion
3114
2522
  //#region src/toolchain-guard.ts
3115
2523
  const toolchainRules = [.../* @__PURE__ */ new Set([
3116
2524
  ...packageRules,
@@ -3122,7 +2530,7 @@ const record = (value) => typeof value === "object" && value !== null && !Array.
3122
2530
  const array = (value) => Array.isArray(value);
3123
2531
  /** Repository-owned declarations are explicit; malformed decisions fail closed. */
3124
2532
  const parseToolchainConfiguration = (input) => {
3125
- if (!record(input) || Object.keys(input).some((key) => key !== "optOuts" && key !== "cloud" && key !== "engineFloors") || input["optOuts"] !== void 0 && !array(input["optOuts"])) throw new Error("stll-toolchain.json accepts only optOuts, cloud and engineFloors declarations");
2533
+ if (!record(input) || Object.keys(input).some((key) => key !== "optOuts" && key !== "cloud" && key !== "engineFloors" && key !== "dynamicSelectors" && key !== "consumerChecks") || input["optOuts"] !== void 0 && !array(input["optOuts"])) throw new Error("stll-toolchain.json accepts only optOuts, cloud, engineFloors, dynamicSelectors and consumerChecks declarations");
3126
2534
  const disabled = /* @__PURE__ */ new Set();
3127
2535
  const optOuts = input["optOuts"] ?? [];
3128
2536
  if (!array(optOuts)) throw new Error("optOuts must be an array");
@@ -3135,7 +2543,9 @@ const parseToolchainConfiguration = (input) => {
3135
2543
  return {
3136
2544
  disabled,
3137
2545
  cloud: parseCloudSetup(input["cloud"]),
3138
- engineFloors: parseEngineFloors(input["engineFloors"])
2546
+ engineFloors: parseEngineFloors(input["engineFloors"]),
2547
+ dynamicSelectors: parseDynamicSelectors(input["dynamicSelectors"]),
2548
+ consumerChecks: parseConsumerChecks(input["consumerChecks"])
3139
2549
  };
3140
2550
  };
3141
2551
  /** Read one tracked configuration snapshot for guards and policy generation. */
@@ -3198,13 +2608,12 @@ const checkToolchain = ({ root, policy, onDelegated }) => {
3198
2608
  }).trim();
3199
2609
  repository = /^(?:https?:\/\/github\.com\/|git@github\.com:|ssh:\/\/git@github\.com\/)([^/]+\/[^/]+?)(?:\.git)?\/?$/.exec(origin)?.[1];
3200
2610
  } catch {}
3201
- let disabled = /* @__PURE__ */ new Set();
3202
- let cloud;
2611
+ let configuration = parseToolchainConfiguration({});
3203
2612
  const engineFloors = [];
2613
+ let dynamicSelectors = [];
3204
2614
  if (files["stll-toolchain.json"] !== void 0) try {
3205
- const configuration = parseToolchainConfiguration(JSON.parse(files["stll-toolchain.json"]));
3206
- disabled = configuration.disabled;
3207
- cloud = configuration.cloud;
2615
+ configuration = parseToolchainConfiguration(JSON.parse(files["stll-toolchain.json"]));
2616
+ dynamicSelectors = configuration.dynamicSelectors;
3208
2617
  for (const entry of configuration.engineFloors) engineFloors.push(resolveEngineFloor(entry, files));
3209
2618
  } catch (error) {
3210
2619
  diagnostics.push({
@@ -3214,6 +2623,7 @@ const checkToolchain = ({ root, policy, onDelegated }) => {
3214
2623
  message: error instanceof Error ? error.message : String(error)
3215
2624
  });
3216
2625
  }
2626
+ const cloud = configuration.cloud;
3217
2627
  const script = files[cloudSetupPath];
3218
2628
  if (cloud === void 0 && trackedFiles.has(".agents/cloud-setup.sh")) diagnostics.push({
3219
2629
  rule: "cloud-setup-drift",
@@ -3244,6 +2654,12 @@ const checkToolchain = ({ root, policy, onDelegated }) => {
3244
2654
  }
3245
2655
  const exercisedFloors = /* @__PURE__ */ new Set();
3246
2656
  const mismatchedFloors = /* @__PURE__ */ new Set();
2657
+ const selectorMatches = /* @__PURE__ */ new Map();
2658
+ diagnostics.push(...checkConsumerChecks({
2659
+ declarations: configuration.consumerChecks,
2660
+ files,
2661
+ policy
2662
+ }));
3247
2663
  diagnostics.push(...checkPackageFiles({
3248
2664
  files,
3249
2665
  policy
@@ -3259,6 +2675,8 @@ const checkToolchain = ({ root, policy, onDelegated }) => {
3259
2675
  }
3260
2676
  },
3261
2677
  trackedFiles,
2678
+ dynamicSelectors: dynamicSelectors.filter((entry) => entry.path === file),
2679
+ onDynamicSelector: (entry) => selectorMatches.set(entry, (selectorMatches.get(entry) ?? 0) + 1),
3262
2680
  repository,
3263
2681
  onDelegated,
3264
2682
  engineFloors: engineFloors.filter((entry) => entry.workflow === file),
@@ -3274,13 +2692,19 @@ const checkToolchain = ({ root, policy, onDelegated }) => {
3274
2692
  line: 1,
3275
2693
  message: `engine floor ${entry.workflow}:${entry.job} requires a setup-node exact patch satisfying ${entry.package} engines.node minimum major ${entry.major}`
3276
2694
  });
2695
+ for (const entry of dynamicSelectors) if (selectorMatches.get(entry) !== 1) diagnostics.push({
2696
+ rule: "configuration",
2697
+ path: "stll-toolchain.json",
2698
+ line: 1,
2699
+ message: `dynamic selector ${entry.path}:${entry.at ?? entry.line} must match exactly one unresolved ${entry.kind} selector`
2700
+ });
3277
2701
  diagnostics.push(...checkDependabot({
3278
2702
  files,
3279
2703
  policy: policy.dependabot
3280
2704
  }));
3281
- return diagnostics.filter((diagnostic) => !disabled.has(diagnostic.rule));
2705
+ return diagnostics.filter((diagnostic) => !configuration.disabled.has(diagnostic.rule));
3282
2706
  };
3283
2707
  //#endregion
3284
- export { cloudSetupPath as a, parseToolchainPolicy as i, parseToolchainConfiguration as n, generateCloudSetup as o, readToolchainInputs as r, checkToolchain as t };
2708
+ export { parseToolchainConfiguration as n, readToolchainInputs as r, checkToolchain as t };
3285
2709
 
3286
2710
  //# sourceMappingURL=toolchain-guard.mjs.map