@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,782 +1,241 @@
1
- //#region \0rolldown/runtime.js
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
- key = keys[i];
11
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
- get: ((k) => from[k]).bind(null, key),
13
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
- });
15
- }
16
- return to;
17
- };
18
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
- value: mod,
20
- enumerable: true
21
- }) : target, mod));
22
- //#endregion
1
+ const require_rolldown_runtime = require("./rolldown-runtime.cjs");
2
+ const require_toolchain_inputs = require("./toolchain-inputs.cjs");
3
+ const require_toolchain_schema = require("./toolchain-schema.cjs");
23
4
  let node_fs = require("node:fs");
24
5
  let node_child_process = require("node:child_process");
25
6
  let node_path = require("node:path");
26
- node_path = __toESM(node_path, 1);
7
+ node_path = require_rolldown_runtime.__toESM(node_path, 1);
27
8
  let semver = require("semver");
9
+ let yaml = require("yaml");
28
10
  let picomatch = require("picomatch");
29
- picomatch = __toESM(picomatch, 1);
11
+ picomatch = require_rolldown_runtime.__toESM(picomatch, 1);
30
12
  let smol_toml = require("smol-toml");
31
- let yaml = require("yaml");
32
- //#region src/cloud-setup-install.ts
33
- const packageInstall = String.raw`
34
- # Prevent package maintainer scripts from starting unmanaged system services.
35
- apt_install() (
36
- # EXIT runs after function locals expire; this subshell owns the trap state.
37
- backup=''
38
- policy_state=untouched
39
- [[ ! -L /var/lock/stll-cloud-apt.lock ]] || fail 'Invalid package install lock'
40
- exec 8>/var/lock/stll-cloud-apt.lock
41
- flock -x -w 120 8 || fail 'Another package installation is active'
42
- [[ ! -L /usr/sbin/policy-rc.d ]] || fail 'Unsupported service-start policy symlink'
43
- [[ ! -e /usr/sbin/policy-rc.d || -f /usr/sbin/policy-rc.d ]] || fail 'Unsupported service-start policy file'
44
- backup="$(mktemp -d)"
45
- cleanup_package_policy() {
46
- local status=$?
47
- trap - EXIT HUP INT TERM
48
- if [[ "$policy_state" == installed ]]; then
49
- if [[ -f "$backup/original" ]]; then
50
- if ! cp -p "$backup/original" /usr/sbin/policy-rc.d; then status=1; fi
51
- else
52
- if ! rm -f /usr/sbin/policy-rc.d; then status=1; fi
53
- fi
54
- fi
55
- rm -rf -- "$backup"
56
- exit "$status"
57
- }
58
- trap cleanup_package_policy EXIT
59
- trap 'exit 129' HUP
60
- trap 'exit 130' INT
61
- trap 'exit 143' TERM
62
- if [[ -e /usr/sbin/policy-rc.d ]]; then cp -p /usr/sbin/policy-rc.d "$backup/original"; fi
63
- policy_state=installed
64
- printf '%s\n' '#!/bin/sh' 'exit 101' > /usr/sbin/policy-rc.d
65
- chmod 755 /usr/sbin/policy-rc.d
66
- DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "$@"
67
- )
68
-
69
- `;
70
- const postgresSourceLine = "deb [signed-by=/usr/share/keyrings/stll-pgdg.asc] https://apt.postgresql.org/pub/repos/apt noble-pgdg main";
71
- const postgresSigningFingerprint = "B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8";
72
- const postgresSources = String.raw`
73
- pgdg_key_fingerprint() {
74
- 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 }'
75
- }
76
- pgdg_sources_ready() {
77
- local managed="$1" file mode records suite keyring override fingerprint selected_keyring=''
78
- shift
79
- PGDG_SOURCE_STATE=missing
80
- [[ ! -L "$managed" ]] || fail "Managed PGDG source must not be a symlink: $managed"
81
- for file in "$@"; do
82
- [[ -e "$file" ]] || continue
83
- [[ -f "$file" ]] || fail "APT source must be a regular file: $file"
84
- case "$file" in *.sources) mode=sources ;; *) mode=list ;; esac
85
- records="$(awk -v mode="$mode" '
86
- function trim(value) { gsub(/^[[:space:]]+|[[:space:]]+$/, "", value); return value }
87
- function pgdg_uri(uri) {
88
- uri = tolower(uri)
89
- if (uri !~ /^[a-z][a-z0-9+.-]*:\/\//) return 0
90
- sub(/^[a-z][a-z0-9+.-]*:\/\//, "", uri)
91
- sub(/[\/?#].*$/, "", uri)
92
- sub(/^.*@/, "", uri)
93
- sub(/:.*$/, "", uri)
94
- return uri == "apt.postgresql.org" || uri == "apt.postgresql.org."
95
- }
96
- function contains_pgdg(value, tokens, count, token_index) {
97
- count = split(value, tokens, /[[:space:]]+/)
98
- for (token_index = 1; token_index <= count; token_index++) if (pgdg_uri(tokens[token_index])) return 1
99
- return 0
100
- }
101
- function unsafe_field(name) {
102
- return name ~ /^(trusted|allow-insecure|allow-weak|allow-downgrade-to-insecure)$/
103
- }
104
- function disabled_override(value) {
105
- value = tolower(trim(value))
106
- gsub(/^["\047]|["\047]$/, "", value)
107
- return value ~ /^(no|false|without|off|disable|0)$/
108
- }
109
- function stanza(override_index) {
110
- override = 0
111
- for (override_index = 1; override_index <= override_count; override_index++) {
112
- if (!disabled_override(override_values[override_index])) override = 1
113
- delete override_values[override_index]
114
- }
115
- if (tolower(trim(enabled)) != "no" && pgdg) print (trim(suites) == "" ? "-" : trim(suites)) "\t" (trim(keyring) == "" ? "-" : trim(keyring)) "\t" (override ? "unsafe" : "safe")
116
- enabled = ""; pgdg = 0; field = ""; suites = ""; keyring = ""; override = 0; override_count = 0
117
- }
118
- /^[[:space:]]*#/ { if (mode == "list" || $0 ~ /^#/) next }
119
- {
120
- line = $0
121
- if (mode == "list") {
122
- sub(/[[:space:]]*#.*$/, "", line)
123
- if (line !~ /^[[:space:]]*deb(-src)?[[:space:]]/ || !contains_pgdg(line)) next
124
- keyring = ""; override = 0
125
- if (match(line, /\[[^]]*\]/)) {
126
- options = substr(line, RSTART + 1, RLENGTH - 2)
127
- count = split(options, tokens, /[[:space:]]+/)
128
- for (token_index = 1; token_index <= count; token_index++) {
129
- if (tolower(tokens[token_index]) ~ /^signed-by=/) { keyring = tokens[token_index]; sub(/^[^=]*=/, "", keyring) }
130
- option_name = tolower(tokens[token_index]); sub(/=.*/, "", option_name)
131
- option_value = tokens[token_index]; sub(/^[^=]*=/, "", option_value)
132
- if (unsafe_field(option_name) && !disabled_override(option_value)) override = 1
133
- }
134
- sub(/\[[^]]*\]/, "", line)
135
- }
136
- count = split(line, tokens, /[[:space:]]+/)
137
- 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")
138
- next
139
- }
140
- if (line ~ /^[[:space:]]*$/) { stanza(); next }
141
- if (line ~ /^[[:space:]]/) {
142
- if (field == "uris" && contains_pgdg(line)) pgdg = 1
143
- if (field == "enabled") enabled = enabled " " line
144
- if (field == "suites") suites = suites " " line
145
- if (field == "signed-by") keyring = keyring " " line
146
- if (unsafe_field(field)) override_values[override_count] = override_values[override_count] " " line
147
- next
148
- }
149
- field = tolower(line)
150
- sub(/:.*/, "", field)
151
- value = line
152
- sub(/^[^:]*:[[:space:]]*/, "", value)
153
- if (field == "uris" && contains_pgdg(value)) pgdg = 1
154
- if (field == "enabled") enabled = value
155
- if (field == "suites") suites = value
156
- if (field == "signed-by") keyring = value
157
- if (unsafe_field(field)) override_values[++override_count] = value
158
- }
159
- END { if (mode == "sources") stanza() }
160
- ' "$file")" || fail "Cannot inspect APT source: $file"
161
- if [[ "$file" == "$managed" && -z "$records" ]]; then
162
- fail "Managed PGDG source has no active PGDG entry: $file"
163
- fi
164
- while IFS=$'\t' read -r suite keyring override; do
165
- [[ -n "$suite" ]] || continue
166
- [[ "$override" == safe ]] || fail "PGDG source enables an unsafe trust override: $file"
167
- [[ "$suite" == noble-pgdg && "$keyring" == /* && ! "$keyring" =~ [[:space:],] ]] || fail "PGDG source requires noble-pgdg and one Signed-By keyring: $file"
168
- [[ -z "$selected_keyring" || "$selected_keyring" == "$keyring" ]] || fail "Conflicting PGDG Signed-By keyrings: $file"
169
- fingerprint="$(pgdg_key_fingerprint "$keyring")" || fail "Cannot verify PGDG Signed-By keyring: $file"
170
- [[ "$fingerprint" == @PGDG_FINGERPRINT@ ]] || fail "PGDG Signed-By fingerprint mismatch: $file"
171
- selected_keyring="$keyring"
172
- PGDG_SOURCE_STATE=reuse
173
- done <<< "$records"
174
- done
175
- }
176
- `;
177
- const postgresInstall = String.raw`
178
- postgres_ready() {
179
- local binary
180
- for binary in postgres initdb pg_ctl psql createdb; do
181
- [[ -x "/usr/lib/postgresql/@POSTGRES@/bin/$binary" ]] || return 1
182
- done
183
- /usr/lib/postgresql/@POSTGRES@/bin/postgres --version | grep -Eq '^postgres \(PostgreSQL\) @POSTGRES@\.'
184
- }
185
- if ! postgres_ready; then
186
- 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
187
- if [[ "$PGDG_SOURCE_STATE" == missing ]]; then
188
- local key key_fingerprint
189
- key="$(mktemp)"
190
- curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 https://www.postgresql.org/media/keys/ACCC4CF8.asc -o "$key"
191
- key_fingerprint="$(pgdg_key_fingerprint "$key")"
192
- [[ "$key_fingerprint" == @PGDG_FINGERPRINT@ ]] || fail 'PostgreSQL signing key mismatch'
193
- install -d -m 755 /usr/share/keyrings
194
- install -m 644 "$key" /usr/share/keyrings/stll-pgdg.asc
195
- rm -- "$key"
196
- printf '%s\n' '@PGDG_SOURCE@' > /etc/apt/sources.list.d/stll-pgdg.list
197
- fi
198
- apt-get update
199
- apt_install postgresql-@POSTGRES@ postgresql-client-@POSTGRES@
200
- fi
201
- postgres_ready || fail 'PostgreSQL toolchain is incomplete or differs from the policy major'
202
- `;
203
- const valkeyInstall = String.raw`
204
- 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
205
- local scratch server_url server_sha tools_url tools_sha package
206
- scratch="$(mktemp -d)"
207
- chmod 755 "$scratch"
208
- case "$ARCH" in
209
- amd64) server_url='@AMD64_SERVER_URL@'; server_sha='@AMD64_SERVER_SHA@'; tools_url='@AMD64_TOOLS_URL@'; tools_sha='@AMD64_TOOLS_SHA@' ;;
210
- arm64) server_url='@ARM64_SERVER_URL@'; server_sha='@ARM64_SERVER_SHA@'; tools_url='@ARM64_TOOLS_URL@'; tools_sha='@ARM64_TOOLS_SHA@' ;;
211
- *) fail 'Unsupported Valkey architecture' ;;
212
- esac
213
- curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "$server_url" -o "$scratch/valkey-server.deb"
214
- curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 "$tools_url" -o "$scratch/valkey-tools.deb"
215
- (cd "$scratch"; printf '%s %s\n' "$server_sha" valkey-server.deb "$tools_sha" valkey-tools.deb | sha256sum --strict -c -)
216
- for package in valkey-server valkey-tools; do
217
- [[ "$(dpkg-deb -f "$scratch/$package.deb" Package)" == "$package" ]] || fail 'Valkey artifact package mismatch'
218
- [[ "$(dpkg-deb -f "$scratch/$package.deb" Version)" == '@VALKEY@-1.noble' ]] || fail 'Valkey artifact version mismatch'
219
- [[ "$(dpkg-deb -f "$scratch/$package.deb" Architecture)" == "$ARCH" ]] || fail 'Valkey artifact architecture mismatch'
220
- done
221
- apt-get update
222
- apt_install "$scratch/valkey-server.deb" "$scratch/valkey-tools.deb"
223
- rm -rf -- "$scratch"
224
- fi
225
- [[ "$(valkey-server --version | sed -nE 's/.*v=([0-9.]+).*/\1/p')" == '@VALKEY@' ]] || fail 'Valkey version differs from policy'
226
- [[ "$(valkey-cli --version)" == 'valkey-cli @VALKEY@' ]] || fail 'Valkey client version differs from policy'
227
- `;
228
- const state = String.raw`
229
- service_account_ready() {
230
- local uid gid shell
231
- uid="$(id -u stll-cloud)" || fail 'Service user is unavailable; run install'
232
- gid="$(id -g stll-cloud)"
233
- shell="$(getent passwd stll-cloud | cut -d: -f7)"
234
- [[ "$uid" =~ ^[1-9][0-9]*$ && "$gid" =~ ^[1-9][0-9]*$ && "$shell" == /usr/sbin/nologin ]] || fail 'Service user must be unprivileged with no login shell'
235
- }
236
- prepare_state() {
237
- service_account_ready
238
- [[ ! -L /var/lib/stll-cloud ]] || fail 'Service state root must not be a symlink'
239
- if [[ -e /var/lib/stll-cloud ]]; then
240
- [[ -d /var/lib/stll-cloud && "$(stat -c '%u:%a' /var/lib/stll-cloud)" == 0:755 ]] || fail 'Invalid service state root'
241
- else
242
- install -d -m 755 -o root -g root /var/lib/stll-cloud
243
- fi
244
- STATE="/var/lib/stll-cloud/$(printf '%s' "$REPO_ROOT" | sha256sum | cut -d' ' -f1)"
245
- [[ ! -L "$STATE" ]] || fail 'Service state must not be a symlink'
246
- if [[ -e "$STATE" ]]; then
247
- [[ -d "$STATE" && "$(stat -c '%u:%a' "$STATE")" == 0:710 && "$(stat -c '%g' "$STATE")" == "$(id -g stll-cloud)" ]] || fail 'Invalid service state ownership'
248
- else
249
- install -d -m 710 -o root -g stll-cloud "$STATE"
250
- fi
251
- [[ ! -L "$STATE/lock" ]] || fail 'Service lock must not be a symlink'
252
- exec 9>"$STATE/lock"
253
- flock -x -w 10 9 || fail 'Another setup process owns the service state'
254
- }
255
- `;
256
- const renderCloudServiceInstall = ({ services, postgres, valkey, valkeyArtifacts }) => {
257
- const parts = services.map((service) => {
258
- switch (service) {
259
- case "postgres": return postgresInstall.replaceAll("@POSTGRES@", postgres).replaceAll("@PGDG_SOURCE@", postgresSourceLine).replaceAll("@PGDG_FINGERPRINT@", postgresSigningFingerprint);
260
- case "valkey": {
261
- const replacements = /* @__PURE__ */ new Map([
262
- ["@VALKEY@", valkey],
263
- ["@AMD64_SERVER_URL@", valkeyArtifacts.amd64.server.url],
264
- ["@AMD64_SERVER_SHA@", valkeyArtifacts.amd64.server.sha256],
265
- ["@AMD64_TOOLS_URL@", valkeyArtifacts.amd64.tools.url],
266
- ["@AMD64_TOOLS_SHA@", valkeyArtifacts.amd64.tools.sha256],
267
- ["@ARM64_SERVER_URL@", valkeyArtifacts.arm64.server.url],
268
- ["@ARM64_SERVER_SHA@", valkeyArtifacts.arm64.server.sha256],
269
- ["@ARM64_TOOLS_URL@", valkeyArtifacts.arm64.tools.url],
270
- ["@ARM64_TOOLS_SHA@", valkeyArtifacts.arm64.tools.sha256]
271
- ]);
272
- return valkeyInstall.replace(/@[A-Z0-9_]+@/g, (token) => {
273
- const value = replacements.get(token);
274
- if (value === void 0) throw new Error(`Missing Valkey artifact field: ${token}`);
275
- return value;
276
- });
277
- }
278
- default: throw new Error(`Unsupported service: ${String(service)}`);
279
- }
280
- });
281
- 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";
282
- 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`;
283
- };
284
13
  //#endregion
285
- //#region src/cloud-setup-schema.ts
286
- const cloudServices = ["postgres", "valkey"];
287
- const cloudInstallCommand = "bun install --frozen-lockfile";
288
- const cloudService = (value) => cloudServices.some((service) => value === service);
289
- /** Parse an explicit declaration without assuming services for undeclared repositories. */
290
- const parseCloudSetup = (input) => {
291
- if (input === void 0) return void 0;
292
- if (typeof input !== "object" || input === null || Array.isArray(input)) throw new Error("cloud must be an object");
293
- for (const key of Object.keys(input)) if (key !== "services" && key !== "install" && key !== "envFile") throw new Error(`unsupported cloud key: ${key}`);
294
- const selected = "services" in input ? input.services : [];
295
- if (!Array.isArray(selected)) throw new Error("cloud.services must be an array when provided");
296
- if (!("install" in input) || input.install !== "bun install --frozen-lockfile") throw new Error(`cloud.install must be exactly ${cloudInstallCommand}`);
297
- const seen = /* @__PURE__ */ new Set();
298
- const services = Array.from(selected, (service) => {
299
- if (!cloudService(service)) throw new Error("cloud.services contains an unsupported service");
300
- if (seen.has(service)) throw new Error(`cloud.services contains duplicate service: ${service}`);
301
- seen.add(service);
302
- return service;
303
- });
304
- if (services.length === 0) {
305
- if ("envFile" in input) throw new Error("cloud.envFile is not allowed without services");
306
- return {
307
- services,
308
- install: cloudInstallCommand
309
- };
14
+ //#region src/toolchain-consumer-checks.ts
15
+ const consumerRunnerVersion = "0.14.1";
16
+ const consumerCheckWorkflow = "stella/.github/.github/workflows/package-consumer-compat.yml";
17
+ const record$6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
18
+ const repositoryDirectory = (value) => typeof value === "string" && value !== "" && !value.includes("\\") && !value.includes("${{") && !value.split("").some((character) => {
19
+ const code = character.charCodeAt(0);
20
+ return code < 32 || code === 127;
21
+ }) && !/^[A-Za-z]:/.test(value) && !node_path.default.posix.isAbsolute(value) && (value === "." || value.split("/").every((part) => part !== "" && part !== "." && part !== ".."));
22
+ const packageDirectories = (value) => Array.isArray(value) && value.length > 0 && value.every(repositoryDirectory) && new Set(value).size === value.length;
23
+ const cronFields = [
24
+ {
25
+ minimum: 0,
26
+ maximum: 59
27
+ },
28
+ {
29
+ minimum: 0,
30
+ maximum: 23
31
+ },
32
+ {
33
+ minimum: 1,
34
+ maximum: 31
35
+ },
36
+ {
37
+ minimum: 1,
38
+ maximum: 12,
39
+ names: Object.fromEntries([
40
+ "JAN",
41
+ "FEB",
42
+ "MAR",
43
+ "APR",
44
+ "MAY",
45
+ "JUN",
46
+ "JUL",
47
+ "AUG",
48
+ "SEP",
49
+ "OCT",
50
+ "NOV",
51
+ "DEC"
52
+ ].map((name, index) => [name, index + 1]))
53
+ },
54
+ {
55
+ minimum: 0,
56
+ maximum: 6,
57
+ names: Object.fromEntries([
58
+ "SUN",
59
+ "MON",
60
+ "TUE",
61
+ "WED",
62
+ "THU",
63
+ "FRI",
64
+ "SAT"
65
+ ].map((name, index) => [name, index]))
310
66
  }
311
- if (!("envFile" in input) || typeof input.envFile !== "string") throw new Error("cloud.envFile must be a canonical repository-relative path");
312
- const envFile = input.envFile;
313
- 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");
314
- return {
315
- services,
316
- install: cloudInstallCommand,
317
- envFile
67
+ ];
68
+ const cronFieldValues = (text, field) => {
69
+ const value = (token) => {
70
+ const number = /^\d+$/.test(token) ? Number(token) : field.names?.[token.toUpperCase()];
71
+ return number !== void 0 && Number.isSafeInteger(number) && number >= field.minimum && number <= field.maximum ? number : void 0;
318
72
  };
319
- };
320
- //#endregion
321
- //#region src/cloud-setup-services.ts
322
- const common = String.raw`
323
- service_file() {
324
- local file="$1" owner="$2"
325
- [[ ! -L "$file" ]] || fail "Service state must not contain symbolic links"
326
- if [[ -e "$file" ]]; then
327
- [[ -f "$file" && "$(stat -c '%u:%a' "$file")" == "$owner:600" ]] || fail "Invalid service state file"
328
- fi
329
- }
330
- service_directory() {
331
- local directory="$1"
332
- [[ ! -L "$directory" ]] || fail "Service directory must not be a symbolic link"
333
- if [[ -e "$directory" ]]; then
334
- [[ -d "$directory" && "$(stat -c '%u:%a' "$directory")" == "$SERVICE_UID:700" ]] || fail "Invalid service directory"
335
- else
336
- root install -d -m 700 -o stll-cloud -g stll-cloud "$directory"
337
- fi
338
- }
339
- service_password() {
340
- local file="$1"
341
- service_file "$file" 0
342
- if [[ ! -e "$file" ]]; then
343
- (umask 077; openssl rand -hex 32 > "$file")
344
- root chown root:root "$file"
345
- fi
346
- [[ "$(cat "$file")" =~ ^[0-9a-f]{64}$ ]] || fail "Invalid service credential state"
347
- }
348
- service_process_matches() {
349
- local pid="$1" executable="$2" owner actual expected
350
- [[ "$pid" =~ ^[1-9][0-9]*$ && -d "/proc/$pid" ]] || return 1
351
- owner="$(stat -c '%u' "/proc/$pid" 2>/dev/null)" || return 2
352
- [[ "$owner" =~ ^[0-9]+$ ]] || return 2
353
- [[ "$owner" == "$SERVICE_UID" ]] || return 1
354
- actual="$(readlink -f "/proc/$pid/exe" 2>/dev/null)" || return 2
355
- expected="$(readlink -f "$executable")" || return 2
356
- [[ -n "$actual" && -n "$expected" && "$actual" != *' (deleted)' ]] || return 2
357
- [[ "$actual" == "$expected" ]]
358
- }
359
- service_process() {
360
- service_process_matches "$1" "$2" || fail "Managed service process identity differs from the recorded state"
361
- }
362
- service_prune_pid() {
363
- local file="$1" executable="$2" service="$3" pid identity
364
- [[ -e "$file" ]] || return 0
365
- pid="$(head -n 1 "$file")"
366
- # PostgreSQL encodes a standalone backend with a negative PID.
367
- if [[ "$service" == postgres ]]; then pid="$(printf '%s' "$pid" | sed 's/^-//')"; fi
368
- if service_process_matches "$pid" "$executable"; then return 0; else identity=$?; fi
369
- [[ "$identity" != 2 ]] || fail "Cannot verify recorded live service process; retaining its PID file"
370
- # Let PostgreSQL check orphan shared memory before removing a dead-PID lock.
371
- if [[ "$service" == postgres && "$pid" =~ ^[1-9][0-9]*$ && ! -d "/proc/$pid" ]]; then return 0; fi
372
- if [[ "$service" == postgres ]]; then
373
- [[ "$pid" =~ ^[1-9][0-9]*$ && "$SERVICE_UID" != 0 ]] || fail "Cannot safely recover PostgreSQL PID state"
374
- # The root controller is not our nonroot postgres. Preserve the remaining
375
- # lock rows so PostgreSQL's EPERM/ancestor path still checks orphan memory.
376
- root sed -i "1s/.*/$$/" "$file"
377
- return 0
378
- fi
379
- # Restored state can name an unrelated live process; never signal that PID.
380
- root rm -- "$file"
381
- }
382
- `;
383
- const postgresStart = String.raw`
384
- local pg_bin='/usr/lib/postgresql/@POSTGRES@/bin' pg_data="$STATE/postgres" pg_password pg_details pg_pid pg_version
385
- service_password "$STATE/postgres.password"
386
- pg_password="$(cat "$STATE/postgres.password")"
387
- service_directory "$pg_data"
388
- for file in PG_VERSION postmaster.pid postgresql.conf pg_hba.conf pg_ident.conf postgresql.auto.conf; do
389
- service_file "$pg_data/$file" "$SERVICE_UID"
390
- done
391
- if [[ ! -e "$pg_data/PG_VERSION" ]]; then
392
- [[ -z "$(ls -A "$pg_data")" ]] || fail "PostgreSQL data directory is not initialized"
393
- service_file "$STATE/postgres.init-password" "$SERVICE_UID"
394
- (umask 077; printf '%s\n' "$pg_password" > "$STATE/postgres.init-password")
395
- root chown stll-cloud:stll-cloud "$STATE/postgres.init-password"
396
- 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"
397
- root rm "$STATE/postgres.init-password"
398
- fi
399
- [[ -f "$pg_data/PG_VERSION" && "$(cat "$pg_data/PG_VERSION")" == '@POSTGRES@' ]] || fail "PostgreSQL data has a different major version"
400
- service_prune_pid "$pg_data/postmaster.pid" "$pg_bin/postgres" postgres
401
- 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
402
- 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"
403
- fi
404
- 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"
405
- pg_version="$(printf '%s\n' "$pg_details" | cut -d '|' -f4)"
406
- [[ "$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"
407
- (( pg_version / 10000 == @POSTGRES@ )) || fail "PostgreSQL endpoint has a different major version"
408
- pg_pid="$(head -n 1 "$pg_data/postmaster.pid")"
409
- service_process "$pg_pid" "$pg_bin/postgres"
410
- [[ "$(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"
411
- DATABASE_URL="postgresql://stll_cloud:$pg_password@127.0.0.1:55432/cloud_test"
412
- `;
413
- const valkeyStart = String.raw`
414
- local vk_data="$STATE/valkey" vk_password vk_config vk_info vk_pid vk_expected ready=0
415
- service_password "$STATE/valkey.password"
416
- vk_password="$(cat "$STATE/valkey.password")"
417
- service_directory "$vk_data"
418
- vk_config="$vk_data/valkey.conf"
419
- service_file "$vk_config" "$SERVICE_UID"
420
- for file in valkey.pid valkey.log dump.rdb; do
421
- [[ ! -L "$vk_data/$file" ]] || fail "Valkey state must not contain symbolic links"
422
- if [[ -e "$vk_data/$file" ]]; then
423
- [[ -f "$vk_data/$file" && "$(stat -c '%u' "$vk_data/$file")" == "$SERVICE_UID" ]] || fail "Invalid Valkey state file"
424
- fi
425
- done
426
- 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')"
427
- if [[ ! -e "$vk_config" ]]; then
428
- (umask 077; printf '%s\n' "$vk_expected" > "$vk_config")
429
- root chown stll-cloud:stll-cloud "$vk_config"
430
- fi
431
- [[ "$(cat "$vk_config")" == "$vk_expected" ]] || fail "Valkey configuration differs from managed settings"
432
- service_prune_pid "$vk_data/valkey.pid" "$(command -v valkey-server)" valkey
433
- if [[ ! -e "$vk_data/valkey.pid" ]]; then
434
- service_run valkey-server "$vk_config" || fail "Valkey start failed; port 56379 must be available"
435
- fi
436
- for ((attempt=0; attempt<20; attempt++)); do
437
- 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
438
- if [[ "$vk_info" == *$'valkey_version:@VALKEY@\r\n'* && "$vk_info" == *"config_file:$vk_config"$'\r\n'* ]]; then ready=1; break; fi
439
- fi
440
- sleep 0.25
441
- done
442
- [[ "$ready" == 1 ]] || fail "Valkey authenticated readiness or managed version/configuration check failed"
443
- [[ -f "$vk_data/valkey.pid" ]] || fail "Valkey managed process identifier is unavailable"
444
- vk_pid="$(cat "$vk_data/valkey.pid")"
445
- service_process "$vk_pid" "$(command -v valkey-server)"
446
- [[ "$vk_info" == *"process_id:$vk_pid"$'\r\n'* ]] || fail "Valkey endpoint has a different process identifier"
447
- [[ "$(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"
448
- [[ "$(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"
449
- REDIS_URL="redis://:$vk_password@127.0.0.1:56379/0"
450
- `;
451
- /** Render only declared services; installation and environment-file ownership stay with the caller. */
452
- const renderCloudServiceStart = ({ services, postgres, valkey }) => {
453
- if (!/^[1-9][0-9]*$/.test(postgres) || !/^\d+\.\d+\.\d+$/.test(valkey)) throw new Error("Invalid cloud service runtime policy");
454
- if (services.length === 0) return "start_services() { :; }\n";
455
- const bodies = services.map((service) => {
456
- switch (service) {
457
- case "postgres": return postgresStart.replaceAll("@POSTGRES@", postgres);
458
- case "valkey": return valkeyStart.replaceAll("@VALKEY@", valkey);
459
- default: throw new Error(`Unsupported cloud service: ${String(service)}`);
460
- }
461
- });
462
- 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`;
463
- };
464
- //#endregion
465
- //#region src/toolchain-node.ts
466
- const numericComponent = "(?:0|[1-9]\\d*)";
467
- const policyPattern = new RegExp(`^${numericComponent}\\.x$`);
468
- const selectorPattern = new RegExp(`^(${numericComponent})(?:\\.(?:x|${numericComponent}(?:\\.${numericComponent})?))?$`);
469
- /** A Node policy selects the complete stable release series of one major. */
470
- const nodePolicyValid = (value) => policyPattern.test(value) && (0, semver.validRange)(value) !== null;
471
- /** Static selectors may narrow that series, but cannot float across majors. */
472
- const nodeSelectorMatches = (value, policy) => {
473
- if (!nodePolicyValid(policy) || typeof value !== "string") return false;
474
- const major = selectorPattern.exec(value)?.[1];
475
- return major !== void 0 && major === policy.slice(0, -2) && (0, semver.validRange)(value) !== null;
476
- };
477
- /** Engines must support every stable release in the approved major series. */
478
- const nodeSupportRangeMatches = (policy, range) => {
479
- if (!nodePolicyValid(policy)) return false;
480
- try {
481
- return (0, semver.subset)(policy, range);
482
- } catch {
483
- return false;
73
+ const values = /* @__PURE__ */ new Set();
74
+ for (const part of text.split(",")) {
75
+ const split = part.split("/");
76
+ if (split.length > 2) return void 0;
77
+ const [base, stepText] = split;
78
+ const step = stepText === void 0 ? 1 : Number(stepText);
79
+ if (stepText !== void 0 && !/^\d+$/.test(stepText)) return void 0;
80
+ if (!Number.isSafeInteger(step) || step <= 0 || base === void 0) return void 0;
81
+ const bounds = base.split("-");
82
+ if (bounds.length > 2) return void 0;
83
+ const lower = bounds.at(0);
84
+ if (lower === void 0) return void 0;
85
+ const start = base === "*" ? field.minimum : value(lower);
86
+ const upper = bounds.at(1);
87
+ let end = start;
88
+ if (base === "*" || bounds.length === 1 && stepText !== void 0) end = field.maximum;
89
+ else if (upper !== void 0) end = value(upper);
90
+ if (start === void 0 || end === void 0 || start > end) return void 0;
91
+ for (let selected = start; selected <= end; selected += step) values.add(selected);
484
92
  }
93
+ return values;
485
94
  };
486
- //#endregion
487
- //#region src/cloud-setup.ts
488
- const cloudSetupPath = ".agents/cloud-setup.sh";
489
- const cloudEnvironmentMarker = "# Generated by stll-cloud-setup; disposable local test services.";
490
- const script = String.raw`#!/usr/bin/env bash
491
- # Generated by stll-cloud-setup. Regenerate with stll-cloud-setup.
492
- # Networked installation and offline service startup are separate lifecycle steps.
493
- # Cached filesystems may not preserve processes; run start at every session start.
494
- # Supported platform: Ubuntu 24.04 (noble), amd64/arm64, root or passwordless sudo.
495
- set -euo pipefail
496
-
497
- usage() { echo 'Usage: cloud-setup.sh install|start' >&2; exit 2; }
498
- [[ "$#" == 1 ]] || usage
499
- case "$1" in install|start) MODE="$1" ;; *) usage ;; esac
500
- fail() { echo "cloud-setup: $*" >&2; exit 1; }
501
- trap 'echo "cloud-setup: command failed at line $LINENO" >&2' ERR
502
- [[ -f /etc/os-release ]] || fail 'Ubuntu 24.04 is required'
503
- . /etc/os-release
504
- [[ "$ID" == ubuntu && "$VERSION_ID" == 24.04 && "$VERSION_CODENAME" == noble ]] || fail 'Only Ubuntu 24.04 (noble) is supported'
505
- SCRIPT="$(realpath -- "$0")"
506
- REPO_ROOT="$(cd -- "$(dirname -- "$SCRIPT")/.." && pwd -P)"
507
- [[ "$SCRIPT" == "$REPO_ROOT/.agents/cloud-setup.sh" ]] || fail 'Run the generated repository script'
508
- if [[ "$(id -u)" != 0 ]]; then
509
- command -v sudo >/dev/null && sudo -n true || fail 'Root or passwordless sudo is required'
510
- exec sudo -n -- bash "$SCRIPT" "$MODE"
511
- fi
512
- OUTPUT_UID="$(printenv SUDO_UID || id -u)"
513
- [[ "$OUTPUT_UID" =~ ^[0-9]+$ ]] || fail 'Invalid invoking user'
514
- OUTPUT_GID="$(getent passwd "$OUTPUT_UID" | cut -d: -f4)"
515
- [[ "$OUTPUT_GID" =~ ^[0-9]+$ ]] || fail 'Invoking user is unavailable'
516
- OUTPUT_USER="$(getent passwd "$OUTPUT_UID" | cut -d: -f1)"
517
- OUTPUT_HOME="$(getent passwd "$OUTPUT_UID" | cut -d: -f6)"
518
- [[ -n "$OUTPUT_USER" && "$OUTPUT_HOME" == /* && -d "$OUTPUT_HOME" ]] || fail 'Invoking user requires an existing home directory'
519
- root() { "$@"; }
520
- service_run() { runuser -u stll-cloud -- "$@" 9>&-; }
521
- BUN_VERSION='@BUN@'
522
- NODE_VERSION='@NODE@'
523
- NODE_DIR="/opt/stll-cloud/node/$NODE_VERSION"
524
- BUN_DIR="/opt/stll-cloud/bun/$BUN_VERSION"
525
- export PATH="$NODE_DIR/bin:$BUN_DIR:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
526
- BUN_CACHE="/var/cache/stll-cloud/bun/$OUTPUT_UID"
527
- @ENV_ASSIGNMENTS@
528
- [[ -f "$REPO_ROOT/.node-version" && ! -L "$REPO_ROOT/.node-version" ]] || fail 'A regular root .node-version is required'
529
- [[ "$(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'
530
- cd -- "$REPO_ROOT"
531
- case "$(uname -m)" in
532
- x86_64) ARCH=amd64; NODE_ARCH=x64; BUN_ARCH=x64 ;;
533
- aarch64|arm64) ARCH=arm64; NODE_ARCH=arm64; BUN_ARCH=aarch64 ;;
534
- *) fail 'Only amd64 and arm64 are supported' ;;
535
- esac
536
-
537
- runtime_ready() {
538
- [[ -x "$NODE_DIR/bin/node" && "$($NODE_DIR/bin/node --version)" == "v$NODE_VERSION" ]] || fail 'Pinned Node is unavailable; run install'
539
- [[ -x "$BUN_DIR/bun" && "$($BUN_DIR/bun --version)" == "$BUN_VERSION" ]] || fail 'Pinned Bun is unavailable; run install'
540
- }
541
- install_runtimes() {
542
- 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
543
- apt-get update
544
- DEBIAN_FRONTEND=noninteractive apt_install ca-certificates curl unzip xz-utils openssl gnupg util-linux
545
- fi
546
- local scratch node_archive bun_archive bun_folder
547
- scratch="$(mktemp -d)"
548
- trap 'rm -rf -- "$scratch"' RETURN
549
- if [[ ! -x "$NODE_DIR/bin/node" ]] || [[ "$($NODE_DIR/bin/node --version)" != "v$NODE_VERSION" ]]; then
550
- node_archive="node-v$NODE_VERSION-linux-$NODE_ARCH.tar.xz"
551
- 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"
552
- 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"
553
- (cd "$scratch"; awk -v name="$node_archive" '$2 == name { print; count++ } END { if (count != 1) exit 1 }' node-sums | sha256sum --strict -c -)
554
- install -d -m 755 "$NODE_DIR"
555
- tar -xJf "$scratch/$node_archive" --strip-components=1 -C "$NODE_DIR"
556
- fi
557
- if [[ ! -x "$BUN_DIR/bun" ]] || [[ "$($BUN_DIR/bun --version)" != "$BUN_VERSION" ]]; then
558
- bun_folder="bun-linux-$BUN_ARCH"
559
- bun_archive="$bun_folder.zip"
560
- 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"
561
- 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"
562
- (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 -)
563
- unzip -q "$scratch/$bun_archive" -d "$scratch/bun"
564
- install -d -m 755 "$BUN_DIR"
565
- install -m 755 "$scratch/bun/$bun_folder/bun" "$BUN_DIR/bun"
566
- fi
567
- runtime_ready
568
- ln -sfnT -- "$NODE_DIR/bin/node" /usr/local/bin/node
569
- ln -sfnT -- "$NODE_DIR/bin/npm" /usr/local/bin/npm
570
- ln -sfnT -- "$NODE_DIR/bin/npx" /usr/local/bin/npx
571
- ln -sfnT -- "$BUN_DIR/bun" /usr/local/bin/bun
572
- rm -rf -- "$scratch"
573
- trap - RETURN
574
- }
575
-
576
- dependency_install() {
577
- local directory
578
- for directory in /var/cache/stll-cloud /var/cache/stll-cloud/bun; do
579
- [[ ! -L "$directory" ]] || fail 'Dependency cache parent must not be a symlink'
580
- if [[ -e "$directory" ]]; then
581
- [[ -d "$directory" && "$(stat -c '%u' "$directory")" == 0 ]] || fail 'Dependency cache parent must be root-owned'
582
- fi
583
- install -d -m 755 -o root -g root "$directory"
584
- done
585
- [[ ! -L "$BUN_CACHE" ]] || fail 'Dependency cache must not be a symlink'
586
- if [[ -e "$BUN_CACHE" ]]; then
587
- [[ -d "$BUN_CACHE" && "$(stat -c '%u' "$BUN_CACHE")" == "$OUTPUT_UID" ]] || fail 'Dependency cache ownership differs from the invoking user'
588
- fi
589
- install -d -m 700 -o "$OUTPUT_UID" -g "$OUTPUT_GID" "$BUN_CACHE"
590
- runuser -u "$OUTPUT_USER" -- env HOME="$OUTPUT_HOME" BUN_INSTALL_CACHE_DIR="$BUN_CACHE" PATH="$PATH" "$BUN_DIR/bun" install --frozen-lockfile
591
- }
592
-
593
- @SERVICE_INSTALL@
594
- @SERVICE_START@
595
-
596
- @ENV_FUNCTIONS@
597
-
598
- if [[ "$MODE" == install ]]; then
599
- install_runtimes
600
- install_services
601
- dependency_install
602
- exit 0
603
- fi
604
- runtime_ready
605
- @ENV_VALIDATE@
606
- prepare_state
607
- start_services
608
- @ENV_WRITE@
609
- `;
610
- const environmentFunctions = String.raw`
611
- validate_environment() {
612
- local part target="$REPO_ROOT" env_path="$REPO_ROOT/$ENV_FILE" first_line temporary
613
- while IFS= read -r part; do
614
- target="$target/$part"
615
- [[ ! -L "$target" ]] || fail 'Environment path must not contain symbolic links'
616
- done < <(printf '%s\n' "$ENV_FILE" | tr '/' '\n')
617
- [[ -d "$(dirname -- "$env_path")" ]] || fail 'Environment parent directory is missing'
618
- if [[ -e "$env_path" ]]; then
619
- [[ -f "$env_path" ]] || fail 'Environment target must be a regular file'
620
- IFS= read -r first_line < "$env_path" || fail 'Refusing to overwrite an unowned environment file'
621
- [[ "$first_line" == "$ENV_MARKER" ]] || fail 'Refusing to overwrite an unowned environment file'
622
- fi
623
- }
624
- write_environment() {
625
- validate_environment
626
- local env_path="$REPO_ROOT/$ENV_FILE" temporary
627
- temporary="$(mktemp "$(dirname -- "$env_path")/.stll-cloud-env.XXXXXX")"
628
- chmod 600 "$temporary"
629
- {
630
- printf '%s\n' "$ENV_MARKER" 'NODE_ENV=test'
631
- if [[ -n "$DATABASE_URL" ]]; then printf 'DATABASE_URL=%s\n' "$DATABASE_URL"; fi
632
- if [[ -n "$REDIS_URL" ]]; then printf 'REDIS_URL=%s\n' "$REDIS_URL"; fi
633
- } > "$temporary"
634
- chown "$OUTPUT_UID:$OUTPUT_GID" "$temporary"
635
- mv -f -- "$temporary" "$env_path"
636
- }
637
- `;
638
- const shellQuote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
639
- const generateCloudSetup = ({ policy, cloud, nodeVersion }) => {
640
- const selected = parseCloudSetup(cloud);
641
- if (selected === void 0) throw new Error("cloud declaration is required");
642
- const exactNode = nodeVersion.trim();
643
- if ((0, semver.valid)(exactNode) !== exactNode || !nodeSelectorMatches(exactNode, policy.node)) throw new Error("cloud requires a tracked exact .node-version within the shared Node series");
644
- const services = cloudServices.filter((service) => selected.services.includes(service));
645
- const replacements = /* @__PURE__ */ new Map([
646
- ["@BUN@", policy.bun],
647
- ["@NODE@", exactNode],
648
- ["@ENV_ASSIGNMENTS@", selected.envFile === void 0 ? "" : `ENV_FILE=${shellQuote(selected.envFile)}\nENV_MARKER=${shellQuote(cloudEnvironmentMarker)}`],
649
- ["@ENV_FUNCTIONS@", selected.envFile === void 0 ? "" : environmentFunctions],
650
- ["@ENV_VALIDATE@", selected.envFile === void 0 ? "" : "DATABASE_URL=''\nREDIS_URL=''\nvalidate_environment"],
651
- ["@ENV_WRITE@", selected.envFile === void 0 ? "" : "write_environment"],
652
- ["@SERVICE_INSTALL@", renderCloudServiceInstall({
653
- services,
654
- postgres: policy.postgres,
655
- valkey: policy.valkey,
656
- valkeyArtifacts: policy.valkeyArtifacts
657
- })],
658
- ["@SERVICE_START@", renderCloudServiceStart({
659
- services,
660
- postgres: policy.postgres,
661
- valkey: policy.valkey
662
- })]
663
- ]);
664
- return script.replace(/@[A-Z0-9_]+@/g, (token) => {
665
- const value = replacements.get(token);
666
- if (value === void 0) throw new Error(`Missing cloud setup field: ${token}`);
667
- return value;
95
+ const validConsumerCron = (text) => {
96
+ const fields = text.trim().split(/\s+/);
97
+ return fields.length === cronFields.length && cronFields.every((field, index) => {
98
+ const fieldText = fields.at(index);
99
+ return fieldText !== void 0 && cronFieldValues(fieldText, field) !== void 0;
668
100
  });
669
101
  };
670
- //#endregion
671
- //#region src/toolchain-container-inputs.ts
672
- const record$6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
673
- /** Filename class recognized by Dependabot's Docker Compose fetcher. */
674
- const isComposeDefinitionPath = (file) => /^\.?(?:docker-)?compose(?:-[\w]+)?(?:\.[\w-]+)?\.ya?ml$/i.test(file.split("/").at(-1) ?? "");
675
- /** Candidate YAML files are classified by their document shape after reading. */
676
- const isKubernetesDefinitionPath = (file) => /^[^.].*\.ya?ml$/i.test(file.split("/").at(-1) ?? "");
677
- const kubernetesPodSpecPaths = {
678
- Pod: ["spec"],
679
- Deployment: [
680
- "spec",
681
- "template",
682
- "spec"
683
- ],
684
- DaemonSet: [
685
- "spec",
686
- "template",
687
- "spec"
688
- ],
689
- StatefulSet: [
690
- "spec",
691
- "template",
692
- "spec"
693
- ],
694
- ReplicaSet: [
695
- "spec",
696
- "template",
697
- "spec"
698
- ],
699
- ReplicationController: [
700
- "spec",
701
- "template",
702
- "spec"
703
- ],
704
- Job: [
705
- "spec",
706
- "template",
707
- "spec"
708
- ],
709
- CronJob: [
710
- "spec",
711
- "jobTemplate",
712
- "spec",
713
- "template",
714
- "spec"
715
- ]
716
- };
717
- /** Shared semantic image enumeration for runtime checks and update-root generation. */
718
- const containerDocumentImages = (document, file) => {
719
- if (!record$6(document)) return void 0;
720
- const services = document["services"];
721
- if (isComposeDefinitionPath(file) && record$6(services)) {
722
- const images = [];
723
- for (const [name, service] of Object.entries(services)) if (record$6(service) && service["image"] !== void 0) images.push({
724
- image: service["image"],
725
- path: [
726
- "services",
727
- name,
728
- "image"
729
- ]
102
+ const nightlySchedule = (value) => {
103
+ if (!record$6(value)) return false;
104
+ const schedule = value["schedule"];
105
+ if (!Array.isArray(schedule) || schedule.length === 0) return false;
106
+ const coveredDays = /* @__PURE__ */ new Set();
107
+ for (const entry of schedule) {
108
+ if (!record$6(entry) || typeof entry["cron"] !== "string" || !validConsumerCron(entry["cron"])) return false;
109
+ const fields = entry["cron"].trim().split(/\s+/);
110
+ const selections = cronFields.map((field, index) => {
111
+ const text = fields.at(index);
112
+ return text === void 0 ? void 0 : cronFieldValues(text, field);
730
113
  });
114
+ if (selections.at(2)?.size !== 31 || selections.at(3)?.size !== 12) continue;
115
+ for (const day of selections.at(4) ?? []) coveredDays.add(day);
116
+ }
117
+ return coveredDays.size === 7;
118
+ };
119
+ const parseConsumerChecks = (input) => {
120
+ if (input === void 0) return [];
121
+ if (!Array.isArray(input)) throw new Error("consumerChecks must be an array");
122
+ const targets = /* @__PURE__ */ new Set();
123
+ return input.map((entry) => {
124
+ if (!record$6(entry) || Object.keys(entry).some((key) => ![
125
+ "workflow",
126
+ "job",
127
+ "packages",
128
+ "toolingVersion",
129
+ "fixturePath"
130
+ ].includes(key)) || typeof entry["workflow"] !== "string" || require_toolchain_inputs.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");
131
+ const target = `${entry["workflow"]}:${entry["job"]}`;
132
+ if (targets.has(target)) throw new Error("consumerChecks targets must be unique");
133
+ targets.add(target);
731
134
  return {
732
- ecosystem: "docker-compose",
733
- images
135
+ workflow: entry["workflow"],
136
+ job: entry["job"],
137
+ packages: entry["packages"],
138
+ toolingVersion: entry["toolingVersion"],
139
+ fixturePath: entry["fixturePath"]
734
140
  };
735
- }
736
- if (typeof document["apiVersion"] !== "string" || typeof document["kind"] !== "string") return void 0;
737
- const images = [];
738
- const visited = /* @__PURE__ */ new Set();
739
- const walkResource = (resource, prefix) => {
740
- if (visited.has(resource)) return;
741
- visited.add(resource);
742
- if (typeof resource["apiVersion"] !== "string" || typeof resource["kind"] !== "string") return;
743
- if (resource["kind"] === "List" && Array.isArray(resource["items"])) {
744
- for (const [index, item] of resource["items"].entries()) if (record$6(item)) walkResource(item, [
745
- ...prefix,
746
- "items",
747
- index
748
- ]);
749
- return;
750
- }
751
- const specPath = Object.entries(kubernetesPodSpecPaths).find(([kind]) => kind === resource["kind"])?.[1];
752
- if (specPath === void 0) return;
753
- let spec = resource;
754
- for (const key of specPath) spec = record$6(spec) ? spec[key] : void 0;
755
- if (!record$6(spec)) return;
756
- for (const key of [
757
- "containers",
758
- "initContainers",
759
- "ephemeralContainers"
760
- ]) {
761
- const containers = spec[key];
762
- if (!Array.isArray(containers)) continue;
763
- for (const [index, container] of containers.entries()) if (record$6(container) && container["image"] !== void 0) images.push({
764
- image: container["image"],
765
- path: [
766
- ...prefix,
767
- ...specPath,
768
- key,
769
- index,
770
- "image"
771
- ]
141
+ });
142
+ };
143
+ /** Consumer checks are scoped declarations, never general runtime exceptions. */
144
+ const checkConsumerChecks = ({ declarations, files, policy }) => {
145
+ const diagnostics = [];
146
+ const exercised = /* @__PURE__ */ new Set();
147
+ const add = (message) => diagnostics.push({
148
+ rule: "configuration",
149
+ path: "stll-toolchain.json",
150
+ line: 1,
151
+ message
152
+ });
153
+ for (const [workflow, text] of Object.entries(files)) {
154
+ if (require_toolchain_inputs.githubAutomationFileKind(workflow) !== "workflow") continue;
155
+ try {
156
+ const document = (0, yaml.parseDocument)(text, {
157
+ uniqueKeys: true,
158
+ merge: true
772
159
  });
160
+ if (document.errors.length > 0) throw new Error("invalid workflow YAML");
161
+ const source = document.toJS({ maxAliasCount: 100 });
162
+ if (!record$6(source) || !record$6(source["jobs"])) continue;
163
+ for (const [jobName, job] of Object.entries(source["jobs"])) {
164
+ if (!record$6(job) || typeof job["uses"] !== "string") continue;
165
+ const [workflowPath, ref] = job["uses"].split("@");
166
+ if (workflowPath?.toLowerCase() !== "stella/.github/.github/workflows/package-consumer-compat.yml") continue;
167
+ const declaration = declarations.find((entry) => entry.workflow === workflow && entry.job === jobName);
168
+ if (declaration === void 0) {
169
+ add(`${workflow}:${jobName} requires a consumerChecks declaration`);
170
+ continue;
171
+ }
172
+ if (!nightlySchedule(source["on"])) throw new Error(`${workflow} consumer checks require a nonempty valid on.schedule covering every day`);
173
+ 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`);
174
+ exercised.add(declaration);
175
+ const approved = Object.entries(policy.actions).find(([name]) => name.toLowerCase() === consumerCheckWorkflow)?.[1];
176
+ 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`);
177
+ const inputs = job["with"];
178
+ if (!record$6(inputs)) throw new Error(`${workflow}:${jobName} requires consumer inputs`);
179
+ const names = /* @__PURE__ */ new Set();
180
+ for (const key of Object.keys(inputs)) {
181
+ const normalized = key.toLowerCase();
182
+ if (names.has(normalized) || ![
183
+ "packages",
184
+ "consumer-node",
185
+ "tooling-version",
186
+ "fixture-path"
187
+ ].includes(normalized) || normalized !== key) throw new Error(`${workflow}:${jobName} requires unique canonical consumer input names`);
188
+ names.add(normalized);
189
+ }
190
+ if (inputs["consumer-node"] !== policy.consumerNode) throw new Error(`${workflow}:${jobName} consumer-node must be ${policy.consumerNode}`);
191
+ if (declaration.toolingVersion !== consumerRunnerVersion) throw new Error(`${workflow}:${jobName} toolingVersion must match installed @stll/oxlint-config ${consumerRunnerVersion}`);
192
+ if (inputs["tooling-version"] !== declaration.toolingVersion) throw new Error(`${workflow}:${jobName} tooling-version must match consumerChecks.toolingVersion`);
193
+ if (inputs["fixture-path"] !== declaration.fixturePath) throw new Error(`${workflow}:${jobName} fixture-path must match consumerChecks.fixturePath`);
194
+ const fixtureManifest = node_path.default.posix.join(declaration.fixturePath, "consumer-compat.json");
195
+ const fixtureSource = files[fixtureManifest];
196
+ if (fixtureSource === void 0) throw new Error(`consumerChecks fixture configuration must be tracked: ${fixtureManifest}`);
197
+ const fixtures = require_toolchain_schema.parseConsumerFixtures(JSON.parse(fixtureSource));
198
+ require_toolchain_schema.assertConsumerFixtureSelection({
199
+ selected: declaration.packages,
200
+ fixtures
201
+ });
202
+ const rawPackages = inputs["packages"];
203
+ if (typeof rawPackages !== "string") throw new Error(`${workflow}:${jobName} packages must be a static JSON array`);
204
+ const selected = JSON.parse(rawPackages);
205
+ 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`);
206
+ const workspacePackages = require_toolchain_schema.discoverConsumerPackages(files);
207
+ const packagesByDirectory = new Map([...workspacePackages.values()].map((entry) => [entry.directory, entry]));
208
+ for (const directory of declaration.packages) {
209
+ const manifestPath = node_path.default.posix.join(directory, "package.json");
210
+ if (files[manifestPath] === void 0) throw new Error(`consumerChecks package manifest must be tracked: ${manifestPath}`);
211
+ const selectedPackage = packagesByDirectory.get(directory);
212
+ if (selectedPackage === void 0) throw new Error(`consumerChecks package must be a discovered workspace member: ${manifestPath}`);
213
+ const fixture = fixtures.find((candidate) => candidate.package === directory);
214
+ if (fixture === void 0) throw new Error(`missing declared consumer fixture: ${directory}`);
215
+ require_toolchain_schema.assertConsumerFixtureKind({
216
+ fixture,
217
+ pkg: selectedPackage
218
+ });
219
+ const manifest = selectedPackage.manifest;
220
+ if (manifest["private"] === true || typeof manifest["name"] !== "string" || manifest["name"].trim() === "") throw new Error(`consumerChecks requires a named published package: ${manifestPath}`);
221
+ const closure = require_toolchain_schema.consumerPackageClosure({
222
+ selected: selectedPackage,
223
+ packages: workspacePackages,
224
+ files
225
+ });
226
+ for (const member of closure.values()) require_toolchain_schema.assertConsumerNodeSupport({
227
+ manifest: member.manifest,
228
+ node: policy.consumerNode,
229
+ label: node_path.default.posix.join(member.directory, "package.json")
230
+ });
231
+ }
232
+ }
233
+ } catch (error) {
234
+ 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));
773
235
  }
774
- };
775
- walkResource(document, []);
776
- return images.length === 0 ? void 0 : {
777
- ecosystem: "docker",
778
- images
779
- };
236
+ }
237
+ for (const declaration of declarations) if (!exercised.has(declaration)) add(`${declaration.workflow}:${declaration.job} consumerChecks declaration has no matching reusable invocation`);
238
+ return diagnostics;
780
239
  };
781
240
  //#endregion
782
241
  //#region src/toolchain-images.ts
@@ -801,66 +260,6 @@ const canonicalDockerRuntime = (image) => {
801
260
  return (registry === void 0 ? image : image.slice(registry.length + 1)).replace(/^library\//i, "");
802
261
  };
803
262
  //#endregion
804
- //#region src/toolchain-inputs.ts
805
- const excludedInputPath = (file) => file === "" || file.split("/").some((part) => part === "node_modules" || part === "vendor");
806
- const javascriptDependencyLockfiles = [
807
- "bun.lock",
808
- "package-lock.json",
809
- "yarn.lock",
810
- "pnpm-lock.yaml"
811
- ];
812
- /** Match project, environment, local, and fragment filenames recognized by mise. */
813
- const isMiseConfigPath = (file) => !excludedInputPath(file) && (/(?:^|\/)\.?mise(?:\.[^./][^/]*)?\.toml$/.test(file) || /(?:^|\/)(?:\.?mise|\.config\/mise)\/config(?:\.[^./][^/]*)?\.toml$/.test(file) || /(?:^|\/)(?:\.?mise|\.config\/mise)\/conf\.d\/[^./][^/]*\.toml$/.test(file));
814
- /** GitHub action metadata may live at the repository root or in any directory. */
815
- const githubAutomationFileKind = (file) => {
816
- if (file === "") return void 0;
817
- if (/^\.github\/workflows\/[^/]+\.ya?ml$/.test(file)) return "workflow";
818
- if (/(?:^|\/)action\.ya?ml$/.test(file)) return "action";
819
- };
820
- /** Dependabot updates workflows and repository-root action metadata. */
821
- const isDependabotGithubActionsPath = (file) => {
822
- const kind = githubAutomationFileKind(file);
823
- return kind === "workflow" || kind === "action" && !file.includes("/");
824
- };
825
- /** Use one filename discriminator for reader selection and Python ecosystem decisions. */
826
- const pythonDependencyManifestKind = (file) => {
827
- if (excludedInputPath(file)) return void 0;
828
- const name = file.split("/").at(-1);
829
- if (name === "pyproject.toml") return "project";
830
- if (name === "Pipfile") return "pipfile";
831
- if (name === "Pipfile.lock") return "pipfile-lock";
832
- if (name === "setup.py" || name === "setup.cfg") return "setup";
833
- if (name !== void 0 && /^requirements[^/]*\.(?:txt|in)$/.test(name)) return "requirements";
834
- };
835
- const isPythonDependencyManifest = (file) => pythonDependencyManifestKind(file) !== void 0;
836
- /** Docker definition names use dot-delimited variants, excluding source and documentation. */
837
- const isDockerDefinitionPath = (file) => {
838
- if (excludedInputPath(file)) return false;
839
- const name = file.split("/").at(-1) ?? "";
840
- 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;
841
- return /^(?:[^./]+\.)*(?:dockerfile|containerfile)(?:\.[^./]+)*$/i.test(name);
842
- };
843
- /** Lockfiles establish ecosystem presence without reading their dependency graphs. */
844
- const toolchainInputKind = (file) => {
845
- if (file === ".agents/cloud-setup.sh") return "config";
846
- if (githubAutomationFileKind(file) !== void 0) return "config";
847
- if (excludedInputPath(file)) return void 0;
848
- if (pythonDependencyManifestKind(file) === "pipfile-lock" || javascriptDependencyLockfiles.some((name) => file.split("/").at(-1) === name) || /(?:^|\/)uv\.lock$/.test(file)) return "presence";
849
- 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";
850
- };
851
- //#endregion
852
- //#region src/toolchain-workspaces.ts
853
- /** Match manifest directories with the same positive and negative workspace patterns. */
854
- const workspaceContains = ({ directory, file, patterns, rootMembership }) => {
855
- const relative = node_path.default.posix.relative(directory, node_path.default.posix.dirname(file));
856
- if (relative === "" && rootMembership === "implicit") return true;
857
- if (relative === ".." || relative.startsWith("../")) return false;
858
- if (!Array.isArray(patterns)) return false;
859
- const normalize = (pattern) => node_path.default.posix.normalize(pattern);
860
- const selected = patterns.filter((value) => typeof value === "string");
861
- return (0, picomatch.default)(selected.filter((pattern) => !pattern.startsWith("!")).map(normalize))(relative) && !(0, picomatch.default)(selected.filter((pattern) => pattern.startsWith("!")).map((pattern) => normalize(pattern.slice(1))))(relative);
862
- };
863
- //#endregion
864
263
  //#region src/toolchain-dependabot.ts
865
264
  const dependabotRules = ["dependabot-policy"];
866
265
  const record$5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -933,7 +332,7 @@ const workspaceRoots = (files, ecosystem) => {
933
332
  const declaredMember = (root, patterns, directory) => {
934
333
  const relative = node_path.default.posix.relative(root, directory);
935
334
  if (relative === "" || relative === ".." || relative.startsWith("../")) return false;
936
- return workspaceContains({
335
+ return require_toolchain_inputs.workspaceContains({
937
336
  directory: root,
938
337
  file: node_path.default.posix.join(directory, "package.json"),
939
338
  patterns,
@@ -944,7 +343,7 @@ const workspaceRoots = (files, ecosystem) => {
944
343
  if (ecosystem === "npm") {
945
344
  for (const file of manifests) {
946
345
  const directory = node_path.default.posix.dirname(file);
947
- if (javascriptDependencyLockfiles.some((name) => files[node_path.default.posix.join(directory, name)] !== void 0)) installable.add(directory);
346
+ if (require_toolchain_inputs.javascriptDependencyLockfiles.some((name) => files[node_path.default.posix.join(directory, name)] !== void 0)) installable.add(directory);
948
347
  }
949
348
  let changed = true;
950
349
  while (changed) {
@@ -1025,7 +424,7 @@ const javascriptEcosystem = (directory, files) => {
1025
424
  return "npm";
1026
425
  };
1027
426
  const pythonEcosystem = (file, files) => {
1028
- const manifestKind = pythonDependencyManifestKind(file);
427
+ const manifestKind = require_toolchain_inputs.pythonDependencyManifestKind(file);
1029
428
  if (manifestKind === "pipfile" || manifestKind === "pipfile-lock" || manifestKind === "setup") return "pip";
1030
429
  const root = node_path.default.posix.dirname(file);
1031
430
  if (files[node_path.default.posix.join(root, "uv.lock")] !== void 0 || files[node_path.default.posix.join(root, "uv.toml")] !== void 0) return "uv";
@@ -1050,17 +449,17 @@ const ecosystemRoots = (files) => {
1050
449
  requireRoot(pythonEcosystem(node_path.default.posix.join(root, "pyproject.toml"), files), directory);
1051
450
  }
1052
451
  for (const file of Object.keys(files)) {
1053
- if (isDependabotGithubActionsPath(file)) requireRoot("github-actions", "/");
452
+ if (require_toolchain_inputs.isDependabotGithubActionsPath(file)) requireRoot("github-actions", "/");
1054
453
  const name = node_path.default.posix.basename(file);
1055
- if (isDockerDefinitionPath(file)) requireRoot("docker", directoryOf(file));
1056
- if (isComposeDefinitionPath(file) || isKubernetesDefinitionPath(file)) for (const document of (0, yaml.parseAllDocuments)(files[file] ?? "", { merge: true })) {
454
+ if (require_toolchain_inputs.isDockerDefinitionPath(file)) requireRoot("docker", directoryOf(file));
455
+ if (require_toolchain_inputs.isComposeDefinitionPath(file) || require_toolchain_inputs.isKubernetesDefinitionPath(file)) for (const document of (0, yaml.parseAllDocuments)(files[file] ?? "", { merge: true })) {
1057
456
  if (document.errors.length > 0) continue;
1058
457
  try {
1059
- const classified = containerDocumentImages(document.toJS({ maxAliasCount: 100 }), file);
458
+ const classified = require_toolchain_inputs.containerDocumentImages(document.toJS({ maxAliasCount: 100 }), file);
1060
459
  if (classified !== void 0) requireRoot(classified.ecosystem, directoryOf(file));
1061
460
  } catch {}
1062
461
  }
1063
- if (name === "uv.lock" || name === "uv.toml" || name !== "pyproject.toml" && isPythonDependencyManifest(file)) requireRoot(pythonEcosystem(file, files), directoryOf(file));
462
+ if (name === "uv.lock" || name === "uv.toml" || name !== "pyproject.toml" && require_toolchain_inputs.isPythonDependencyManifest(file)) requireRoot(pythonEcosystem(file, files), directoryOf(file));
1064
463
  }
1065
464
  return roots;
1066
465
  };
@@ -1184,8 +583,57 @@ const checkDependabot = ({ files, policy }) => {
1184
583
  return diagnostics;
1185
584
  };
1186
585
  //#endregion
1187
- //#region src/toolchain-engine-floors.ts
586
+ //#region src/toolchain-dynamic-selectors.ts
587
+ const locatorValidators = {
588
+ image: /^(?:jobs\.[\w-]+\.(?:container|services\.[\w-]+)|services\.[\w.-]+\.image)$/,
589
+ "bun-source": /^(?:jobs\.[\w-]+|runs)\.steps\.[\w-]+$/
590
+ };
591
+ const knownKind = (value) => typeof value === "string" && Object.hasOwn(locatorValidators, value);
1188
592
  const record$4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
593
+ /** One reviewed declaration identifies one unresolved selector, never a rule. */
594
+ const parseDynamicSelectors = (input) => {
595
+ if (input === void 0) return [];
596
+ if (!Array.isArray(input)) throw new Error("dynamicSelectors must be an array");
597
+ const result = [];
598
+ const keys = /* @__PURE__ */ new Set();
599
+ for (const entry of input) {
600
+ if (!record$4(entry) || Object.keys(entry).some((key) => ![
601
+ "path",
602
+ "kind",
603
+ "reason",
604
+ "at",
605
+ "line"
606
+ ].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");
607
+ const kind = entry["kind"];
608
+ if (!knownKind(kind)) throw new Error("unknown dynamic selector kind");
609
+ let selector;
610
+ if (Object.hasOwn(entry, "line") && !Object.hasOwn(entry, "at") && typeof entry["line"] === "number" && Number.isSafeInteger(entry["line"]) && entry["line"] > 0) selector = {
611
+ path: entry["path"],
612
+ kind,
613
+ reason: entry["reason"],
614
+ line: entry["line"]
615
+ };
616
+ else if (!Object.hasOwn(entry, "line") && typeof entry["at"] === "string" && locatorValidators[kind].test(entry["at"])) selector = {
617
+ path: entry["path"],
618
+ kind,
619
+ reason: entry["reason"],
620
+ at: entry["at"]
621
+ };
622
+ else throw new Error("dynamic selector requires one positive line or a kind-specific job/service/step ID locator");
623
+ const key = JSON.stringify([
624
+ selector.path,
625
+ selector.kind,
626
+ selector.at ?? selector.line
627
+ ]);
628
+ if (keys.has(key)) throw new Error("dynamic selector locations must be unique");
629
+ keys.add(key);
630
+ result.push(selector);
631
+ }
632
+ return result;
633
+ };
634
+ //#endregion
635
+ //#region src/toolchain-engine-floors.ts
636
+ const record$3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1189
637
  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 !== "..");
1190
638
  const parseEngineFloors = (input) => {
1191
639
  if (input === void 0) return [];
@@ -1193,7 +641,7 @@ const parseEngineFloors = (input) => {
1193
641
  const result = [];
1194
642
  const jobs = /* @__PURE__ */ new Set();
1195
643
  for (const entry of input) {
1196
- if (!record$4(entry) || Object.keys(entry).some((key) => ![
644
+ if (!record$3(entry) || Object.keys(entry).some((key) => ![
1197
645
  "package",
1198
646
  "workflow",
1199
647
  "job"
@@ -1214,16 +662,16 @@ const resolveEngineFloor = (entry, files) => {
1214
662
  const workflowText = files[entry.workflow];
1215
663
  if (manifestText === void 0 || workflowText === void 0) throw new Error("engine floor package and workflow must be tracked");
1216
664
  const manifest = JSON.parse(manifestText);
1217
- 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");
665
+ 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");
1218
666
  const range = manifest["engines"]["node"];
1219
667
  const minimum = (0, semver.validRange)(range) === null ? null : (0, semver.minVersion)(range);
1220
668
  if (minimum === null || minimum.prerelease.length !== 0) throw new Error("engine floor engines.node must declare a stable minimum");
1221
669
  const document = (0, yaml.parseDocument)(workflowText, { merge: true });
1222
670
  if (document.errors.length !== 0) throw new Error("engine floor workflow must be valid YAML");
1223
671
  const workflow = document.toJS({ maxAliasCount: 100 });
1224
- if (!record$4(workflow) || !record$4(workflow["jobs"])) throw new Error("engine floor job must exist in its tracked workflow");
672
+ if (!record$3(workflow) || !record$3(workflow["jobs"])) throw new Error("engine floor job must exist in its tracked workflow");
1225
673
  const job = workflow["jobs"][entry.job];
1226
- if (!record$4(job)) throw new Error("engine floor job must exist in its tracked workflow");
674
+ if (!record$3(job)) throw new Error("engine floor job must exist in its tracked workflow");
1227
675
  if (!Array.isArray(job["steps"]) || Object.hasOwn(job, "uses")) throw new Error("engine floor job must declare steps and cannot reuse a workflow");
1228
676
  return {
1229
677
  ...entry,
@@ -1243,7 +691,7 @@ const packageRules = [
1243
691
  "typescript-layout",
1244
692
  "node-engine"
1245
693
  ];
1246
- const record$3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
694
+ const record$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1247
695
  const shellSubstitutions = (command) => {
1248
696
  const frames = [{
1249
697
  kind: "root",
@@ -1308,7 +756,7 @@ const compilerCommandSegments = (command) => {
1308
756
  const segments = [[]];
1309
757
  for (const source of [command, ...shellSubstitutions(command)]) {
1310
758
  segments.push([]);
1311
- const shellWords = source.match(/(?:[^\s"'\\;&|()]|\\[^\n]|"(?:[^"\\]|\\.)*"|'[^']*')+|&&|\|\||[();&|\n]/g);
759
+ const shellWords = source.match(/(?:[^\s"'\\;&|()]|\\[\s\S]|"(?:[^"\\]|\\[\s\S])*"|'[^']*')+|&&|\|\||[();&|\n]/g);
1312
760
  for (const word of shellWords ?? []) if (/^(?:&&|\|\||[;&|\n])$/.test(word)) segments.push([]);
1313
761
  else segments.at(-1)?.push(word);
1314
762
  }
@@ -1395,29 +843,29 @@ const launcherValueOptions = new Map(Object.entries({
1395
843
  "--input-type"
1396
844
  ]
1397
845
  }).map(([launcher, options]) => [launcher, new Set(options)]));
1398
- const invokesCompiler = (words) => {
1399
- const literal = (word) => {
1400
- let result = "";
1401
- let quote;
1402
- for (let index = 0; index < word.length; index += 1) {
1403
- const character = word.at(index);
1404
- if (character === quote) {
1405
- quote = void 0;
1406
- continue;
1407
- }
1408
- if (quote === void 0 && (character === "'" || character === "\"")) {
1409
- quote = character;
1410
- continue;
1411
- }
1412
- const next = word.at(index + 1);
1413
- if (character === "\\" && next !== void 0 && quote !== "'" && (quote === void 0 || /[$`"\\\n]/.test(next))) {
1414
- result += next;
1415
- index += 1;
1416
- } else result += character;
846
+ const decodeShellWord = (word) => {
847
+ let result = "";
848
+ let quote;
849
+ for (let index = 0; index < word.length; index += 1) {
850
+ const character = word.at(index);
851
+ if (character === quote) {
852
+ quote = void 0;
853
+ continue;
1417
854
  }
1418
- return result;
1419
- };
1420
- const tokens = words.map(literal);
855
+ if (quote === void 0 && (character === "'" || character === "\"")) {
856
+ quote = character;
857
+ continue;
858
+ }
859
+ const next = word.at(index + 1);
860
+ if (character === "\\" && next !== void 0 && quote !== "'" && (quote === void 0 || /[$`"\\\n]/.test(next))) {
861
+ if (next !== "\n") result += next;
862
+ index += 1;
863
+ } else result += character;
864
+ }
865
+ return result;
866
+ };
867
+ const invokesCompiler = (words) => {
868
+ const tokens = words.map(decodeShellWord);
1421
869
  const compiler = (word) => /(?:^|\/)(?:tsc|tsgo)(?:\.js)?$/.test(word);
1422
870
  let start = 0;
1423
871
  const skipOptions = (index, launcher) => {
@@ -1474,10 +922,10 @@ const checkPackageFiles = ({ files, policy }) => {
1474
922
  const document = pnpm ? (0, yaml.parseDocument)(text, { lineCounter }) : void 0;
1475
923
  if (document !== void 0 && document.errors.length > 0) throw new Error("invalid YAML");
1476
924
  const json = document === void 0 ? JSON.parse(text) : document.toJS();
1477
- if (!record$3(json)) throw new Error("manifest must be an object");
925
+ if (!record$2(json)) throw new Error("manifest must be an object");
1478
926
  if (pnpm) {
1479
- if (json["catalog"] !== void 0 && !record$3(json["catalog"])) throw new Error("catalog must be a mapping");
1480
- 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");
927
+ if (json["catalog"] !== void 0 && !record$2(json["catalog"])) throw new Error("catalog must be a mapping");
928
+ 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");
1481
929
  if (json["packages"] !== void 0 && (!Array.isArray(json["packages"]) || !json["packages"].every((pattern) => typeof pattern === "string"))) throw new Error("workspace packages must be patterns");
1482
930
  }
1483
931
  manifests.set(file, json);
@@ -1491,13 +939,13 @@ const checkPackageFiles = ({ files, policy }) => {
1491
939
  catalog: json["overrides"],
1492
940
  prefix: ["overrides"]
1493
941
  },
1494
- ...record$3(json["catalogs"]) ? Object.entries(json["catalogs"]).map(([name, catalog]) => ({
942
+ ...record$2(json["catalogs"]) ? Object.entries(json["catalogs"]).map(([name, catalog]) => ({
1495
943
  catalog,
1496
944
  prefix: ["catalogs", name]
1497
945
  })) : []
1498
946
  ];
1499
947
  for (const { catalog, prefix } of catalogEntries) {
1500
- if (!record$3(catalog)) continue;
948
+ if (!record$2(catalog)) continue;
1501
949
  for (const [name, value] of Object.entries(catalog)) {
1502
950
  const node = document.getIn([...prefix, name], true);
1503
951
  if (!(0, yaml.isNode)(node) || node.range === void 0 || node.range === null) continue;
@@ -1516,7 +964,7 @@ const checkPackageFiles = ({ files, policy }) => {
1516
964
  });
1517
965
  }
1518
966
  }
1519
- const catalogSources = (json) => [json, ...record$3(json["workspaces"]) ? [json["workspaces"]] : []];
967
+ const catalogSources = (json) => [json, ...record$2(json["workspaces"]) ? [json["workspaces"]] : []];
1520
968
  const pnpmWorkspaceFor = (file) => {
1521
969
  let directory = node_path.default.posix.dirname(file);
1522
970
  while (true) {
@@ -1533,14 +981,14 @@ const checkPackageFiles = ({ files, policy }) => {
1533
981
  const owner = pnpmWorkspaceFor(file);
1534
982
  if (owner !== void 0) {
1535
983
  const { directory, workspace } = owner;
1536
- if (node_path.default.posix.basename(file) === "package.json" && !workspaceContains({
984
+ if (node_path.default.posix.basename(file) === "package.json" && !require_toolchain_inputs.workspaceContains({
1537
985
  directory,
1538
986
  file,
1539
987
  patterns: workspace["packages"],
1540
988
  rootMembership: "implicit"
1541
989
  })) return void 0;
1542
- if (name === "" || name === "default") return workspace["catalog"] ?? (record$3(workspace["catalogs"]) ? workspace["catalogs"]["default"] : void 0);
1543
- return record$3(workspace["catalogs"]) ? workspace["catalogs"][name] : void 0;
990
+ if (name === "" || name === "default") return workspace["catalog"] ?? (record$2(workspace["catalogs"]) ? workspace["catalogs"]["default"] : void 0);
991
+ return record$2(workspace["catalogs"]) ? workspace["catalogs"][name] : void 0;
1544
992
  }
1545
993
  let directory = node_path.default.posix.dirname(file);
1546
994
  while (true) {
@@ -1548,7 +996,7 @@ const checkPackageFiles = ({ files, policy }) => {
1548
996
  if (manifest !== void 0) {
1549
997
  for (const source of catalogSources(manifest)) {
1550
998
  if (name === "" && source["catalog"] !== void 0) return source["catalog"];
1551
- if (record$3(source["catalogs"]) && source["catalogs"][name] !== void 0) return source["catalogs"][name];
999
+ if (record$2(source["catalogs"]) && source["catalogs"][name] !== void 0) return source["catalogs"][name];
1552
1000
  }
1553
1001
  if (manifest["workspaces"] !== void 0) return void 0;
1554
1002
  }
@@ -1562,7 +1010,7 @@ const checkPackageFiles = ({ files, policy }) => {
1562
1010
  if (visited.has(value)) return void 0;
1563
1011
  visited.add(value);
1564
1012
  const catalog = catalogFor(file, value.slice(8));
1565
- value = record$3(catalog) ? catalog[name] : void 0;
1013
+ value = record$2(catalog) ? catalog[name] : void 0;
1566
1014
  }
1567
1015
  return value;
1568
1016
  };
@@ -1578,7 +1026,7 @@ const checkPackageFiles = ({ files, policy }) => {
1578
1026
  const workspaces = manifests.get(node_path.default.posix.join(directory, "package.json"))?.["workspaces"];
1579
1027
  if (workspaces !== void 0) return {
1580
1028
  directory,
1581
- patterns: record$3(workspaces) ? workspaces["packages"] : workspaces,
1029
+ patterns: record$2(workspaces) ? workspaces["packages"] : workspaces,
1582
1030
  source: "json"
1583
1031
  };
1584
1032
  if (directory === ".") return void 0;
@@ -1590,13 +1038,13 @@ const checkPackageFiles = ({ files, policy }) => {
1590
1038
  if (owner === void 0) return "nonmember";
1591
1039
  const { directory, patterns, source } = owner;
1592
1040
  if (source === "json" && !Array.isArray(patterns)) return "mismatch";
1593
- if (!workspaceContains({
1041
+ if (!require_toolchain_inputs.workspaceContains({
1594
1042
  directory,
1595
1043
  file,
1596
1044
  patterns,
1597
1045
  rootMembership: "implicit"
1598
1046
  })) return "nonmember";
1599
- const candidates = [...manifests.entries()].filter(([candidate, json]) => node_path.default.posix.basename(candidate) === "package.json" && json["name"] === name && workspaceContains({
1047
+ const candidates = [...manifests.entries()].filter(([candidate, json]) => node_path.default.posix.basename(candidate) === "package.json" && json["name"] === name && require_toolchain_inputs.workspaceContains({
1600
1048
  directory,
1601
1049
  file: candidate,
1602
1050
  patterns,
@@ -1634,7 +1082,7 @@ const checkPackageFiles = ({ files, policy }) => {
1634
1082
  "peerDependencies"
1635
1083
  ]) {
1636
1084
  const dependencies = json[section];
1637
- if (!record$3(dependencies)) continue;
1085
+ if (!record$2(dependencies)) continue;
1638
1086
  for (const [name, value] of Object.entries(dependencies)) {
1639
1087
  if (!tsSpecifiers.has(name)) continue;
1640
1088
  const resolved = resolve(file, name, value);
@@ -1660,7 +1108,7 @@ const checkPackageFiles = ({ files, policy }) => {
1660
1108
  const workspaceLayouts = /* @__PURE__ */ new Map();
1661
1109
  const workspaceLayoutFor = (file) => {
1662
1110
  const owner = workspaceOwnerFor(file);
1663
- if (owner === void 0 || !workspaceContains({
1111
+ if (owner === void 0 || !require_toolchain_inputs.workspaceContains({
1664
1112
  ...owner,
1665
1113
  file,
1666
1114
  rootMembership: "implicit"
@@ -1672,7 +1120,7 @@ const checkPackageFiles = ({ files, policy }) => {
1672
1120
  for (const [candidate, member] of manifests) {
1673
1121
  if (node_path.default.posix.basename(candidate) !== "package.json") continue;
1674
1122
  const memberOwner = workspaceOwnerFor(candidate);
1675
- if (memberOwner?.directory !== owner.directory || memberOwner.source !== owner.source || !workspaceContains({
1123
+ if (memberOwner?.directory !== owner.directory || memberOwner.source !== owner.source || !require_toolchain_inputs.workspaceContains({
1676
1124
  ...owner,
1677
1125
  file: candidate,
1678
1126
  rootMembership: "implicit"
@@ -1696,7 +1144,7 @@ const checkPackageFiles = ({ files, policy }) => {
1696
1144
  message: `packageManager Bun version must be ${policy.bun}, found ${JSON.stringify(manager)}`
1697
1145
  });
1698
1146
  const checkEntries = (entries, location) => {
1699
- if (!record$3(entries)) return;
1147
+ if (!record$2(entries)) return;
1700
1148
  for (const [name, raw] of Object.entries(entries)) {
1701
1149
  const value = resolve(file, name, raw);
1702
1150
  const tsAllowed = tsSpecifiers.get(name);
@@ -1733,12 +1181,12 @@ const checkPackageFiles = ({ files, policy }) => {
1733
1181
  "devDependencies",
1734
1182
  "optionalDependencies"
1735
1183
  ]) checkEntries(json[key]);
1736
- if (record$3(json["peerDependencies"])) {
1184
+ if (record$2(json["peerDependencies"])) {
1737
1185
  for (const [name, value] of Object.entries(json["peerDependencies"])) if (tsSpecifiers.has(name) && !peerRange(resolve(file, name, value))) checkEntries({ [name]: value });
1738
1186
  }
1739
1187
  for (const source of catalogSources(json)) {
1740
1188
  checkEntries(source["catalog"]);
1741
- if (!record$3(source["catalogs"])) continue;
1189
+ if (!record$2(source["catalogs"])) continue;
1742
1190
  for (const catalog of Object.values(source["catalogs"])) checkEntries(catalog);
1743
1191
  }
1744
1192
  const ownedNames = /* @__PURE__ */ new Set([...pins.keys(), ...tsSpecifiers.keys()]);
@@ -1750,7 +1198,7 @@ const checkPackageFiles = ({ files, policy }) => {
1750
1198
  "optionalDependencies"
1751
1199
  ]) {
1752
1200
  const entries = resolutionRoot[section];
1753
- if (!record$3(entries)) continue;
1201
+ if (!record$2(entries)) continue;
1754
1202
  for (const [name, value] of Object.entries(entries)) resolutionDependencies[name] = resolve(file, name, value);
1755
1203
  }
1756
1204
  const affectedSpecifiers = /* @__PURE__ */ new Map();
@@ -1758,7 +1206,7 @@ const checkPackageFiles = ({ files, policy }) => {
1758
1206
  for (const [candidate, member] of manifests) {
1759
1207
  if (node_path.default.posix.basename(candidate) !== "package.json") continue;
1760
1208
  const memberOwner = workspaceOwnerFor(candidate);
1761
- if (!(candidate === node_path.default.posix.join(node_path.default.posix.dirname(file), "package.json")) && !(owner !== void 0 && owner.directory === node_path.default.posix.dirname(file) && memberOwner?.directory === owner.directory && memberOwner.source === owner.source && workspaceContains({
1209
+ if (!(candidate === node_path.default.posix.join(node_path.default.posix.dirname(file), "package.json")) && !(owner !== void 0 && owner.directory === node_path.default.posix.dirname(file) && memberOwner?.directory === owner.directory && memberOwner.source === owner.source && require_toolchain_inputs.workspaceContains({
1762
1210
  directory: owner.directory,
1763
1211
  file: candidate,
1764
1212
  patterns: owner.patterns,
@@ -1770,7 +1218,7 @@ const checkPackageFiles = ({ files, policy }) => {
1770
1218
  "optionalDependencies"
1771
1219
  ]) {
1772
1220
  const entries = member[section];
1773
- if (!record$3(entries)) continue;
1221
+ if (!record$2(entries)) continue;
1774
1222
  for (const [name, value] of Object.entries(entries)) {
1775
1223
  if (!tsSpecifiers.has(name)) continue;
1776
1224
  const values = affectedSpecifiers.get(name) ?? /* @__PURE__ */ new Set();
@@ -1780,7 +1228,7 @@ const checkPackageFiles = ({ files, policy }) => {
1780
1228
  }
1781
1229
  }
1782
1230
  const checkResolutionMap = (entries, parent) => {
1783
- if (!record$3(entries)) return;
1231
+ if (!record$2(entries)) return;
1784
1232
  for (const [selector, replacement] of Object.entries(entries)) {
1785
1233
  const target = selector === "." ? parent : selector;
1786
1234
  if (target === void 0) continue;
@@ -1803,7 +1251,7 @@ const checkPackageFiles = ({ files, policy }) => {
1803
1251
  });
1804
1252
  continue;
1805
1253
  }
1806
- if (record$3(replacement)) {
1254
+ if (record$2(replacement)) {
1807
1255
  checkResolutionMap(replacement, target);
1808
1256
  continue;
1809
1257
  }
@@ -1837,7 +1285,7 @@ const checkPackageFiles = ({ files, policy }) => {
1837
1285
  };
1838
1286
  checkResolutionMap(json["overrides"]);
1839
1287
  checkResolutionMap(json["resolutions"]);
1840
- if (record$3(json["pnpm"])) checkResolutionMap(json["pnpm"]["overrides"]);
1288
+ if (record$2(json["pnpm"])) checkResolutionMap(json["pnpm"]["overrides"]);
1841
1289
  const localEntries = toolchainEntries(file, json);
1842
1290
  const usesCompiler = policy.typescriptInstallLayouts.some((layout) => localEntries.some(({ name }) => name === layout.compilerPackage));
1843
1291
  let selectedLayout = layoutFor(specifierMap(localEntries));
@@ -1861,7 +1309,7 @@ const checkPackageFiles = ({ files, policy }) => {
1861
1309
  key: "dependencies",
1862
1310
  message: "TypeScript compiler and compatibility packages must match one complete declared install layout"
1863
1311
  });
1864
- const command = record$3(json["scripts"]) ? json["scripts"]["typecheck"] : void 0;
1312
+ const command = record$2(json["scripts"]) ? json["scripts"]["typecheck"] : void 0;
1865
1313
  if (selectedLayout !== void 0 && typeof command === "string") {
1866
1314
  const normalizedExpected = selectedLayout.typecheckCommand.replace(/\s+/g, " ").trim();
1867
1315
  for (const words of compilerCommandSegments(command)) {
@@ -1879,7 +1327,7 @@ const checkPackageFiles = ({ files, policy }) => {
1879
1327
  }
1880
1328
  }
1881
1329
  }
1882
- if (record$3(json["peerDependencies"])) {
1330
+ if (record$2(json["peerDependencies"])) {
1883
1331
  const peerLayout = selectedLayout ?? workspaceLayoutFor(file)?.layout;
1884
1332
  for (const [name, raw] of Object.entries(json["peerDependencies"])) {
1885
1333
  if (!tsSpecifiers.has(name)) continue;
@@ -1903,7 +1351,7 @@ const checkPackageFiles = ({ files, policy }) => {
1903
1351
  });
1904
1352
  }
1905
1353
  }
1906
- if (record$3(json["engines"]) && json["engines"]["node"] !== void 0 && (typeof json["engines"]["node"] !== "string" || !nodeSupportRangeMatches(policy.node, json["engines"]["node"]))) add({
1354
+ if (record$2(json["engines"]) && json["engines"]["node"] !== void 0 && (typeof json["engines"]["node"] !== "string" || !require_toolchain_inputs.nodeSupportRangeMatches(policy.node, json["engines"]["node"]))) add({
1907
1355
  file,
1908
1356
  rule: "node-engine",
1909
1357
  key: "node",
@@ -1925,7 +1373,7 @@ const runtimeRules = [
1925
1373
  "runtime-workflow",
1926
1374
  "action-pins"
1927
1375
  ];
1928
- const record$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1376
+ const record$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1929
1377
  const pythonSelectorMatches = (value, expected) => value === expected || typeof value === "string" && /^\d+\.\d+$/.test(expected) && value.startsWith(`${expected}.`) && /^\d+\.\d+\.\d+$/.test(value);
1930
1378
  const compareRelease = (left, right) => {
1931
1379
  for (let index = 0; index < Math.max(left.length, right.length); index++) {
@@ -1995,7 +1443,7 @@ const pythonRequirementMatches = (requirement, version) => {
1995
1443
  });
1996
1444
  };
1997
1445
  /** Check runtime declarations only in the files that declare or configure them. */
1998
- const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, repository, onDelegated, engineFloors = [], onEngineFloor }) => {
1446
+ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, repository, onDelegated, dynamicSelectors = [], onDynamicSelector, engineFloors = [], onEngineFloor }) => {
1999
1447
  const diagnostics = [];
2000
1448
  const name = node_path.default.posix.basename(file);
2001
1449
  const lines = text.split(/\r?\n/);
@@ -2007,10 +1455,20 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2007
1455
  message
2008
1456
  });
2009
1457
  };
1458
+ let currentLocation;
1459
+ const dynamic = (value) => typeof value === "string" && value.includes("$");
1460
+ const authorizeDynamic = (kind, line, at = currentLocation) => {
1461
+ const matches = dynamicSelectors.filter((entry) => entry.path === file && entry.kind === kind && (entry.at !== void 0 ? entry.at === at : entry.line === line));
1462
+ if (matches.length !== 1) return false;
1463
+ const entry = matches.at(0);
1464
+ if (entry === void 0) return false;
1465
+ onDynamicSelector?.(entry);
1466
+ return true;
1467
+ };
2010
1468
  const lineOf = (key) => Math.max(1, lines.findIndex((line) => line.includes(key)) + 1);
2011
1469
  const pin = ({ rule, value, expected, label, line = 1, selector }) => {
2012
1470
  let matches = value === expected;
2013
- if (selector === "node") matches = nodeSelectorMatches(value, expected);
1471
+ if (selector === "node") matches = require_toolchain_inputs.nodeSelectorMatches(value, expected);
2014
1472
  else if (label.includes("python")) matches = pythonSelectorMatches(value, expected);
2015
1473
  if (!matches) add({
2016
1474
  rule,
@@ -2079,11 +1537,11 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2079
1537
  const parsed = toml("rust-version");
2080
1538
  if (parsed !== void 0) {
2081
1539
  const toolchain = parsed["toolchain"];
2082
- const components = record$2(toolchain) ? toolchain["components"] : void 0;
1540
+ const components = record$1(toolchain) ? toolchain["components"] : void 0;
2083
1541
  const expected = Array.isArray(components) && components.includes("rustc-dev") ? policy.rustCompilerDevelopment : policy.rust;
2084
1542
  pin({
2085
1543
  rule: "rust-version",
2086
- value: record$2(toolchain) ? toolchain["channel"] : void 0,
1544
+ value: record$1(toolchain) ? toolchain["channel"] : void 0,
2087
1545
  expected,
2088
1546
  label: "Rust channel",
2089
1547
  line: lineOf("channel")
@@ -2111,14 +1569,14 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2111
1569
  };
2112
1570
  if (parsed !== void 0) {
2113
1571
  const workspace = parsed["workspace"];
2114
- const defaults = record$2(workspace) ? workspace["package"] : void 0;
2115
- if (record$2(defaults) && defaults["rust-version"] !== void 0) rustFloor(defaults["rust-version"]);
1572
+ const defaults = record$1(workspace) ? workspace["package"] : void 0;
1573
+ if (record$1(defaults) && defaults["rust-version"] !== void 0) rustFloor(defaults["rust-version"]);
2116
1574
  const pkg = parsed["package"];
2117
- const version = record$2(pkg) ? pkg["rust-version"] : void 0;
2118
- if (version !== void 0) if (!record$2(version)) rustFloor(version);
1575
+ const version = record$1(pkg) ? pkg["rust-version"] : void 0;
1576
+ if (version !== void 0) if (!record$1(version)) rustFloor(version);
2119
1577
  else {
2120
1578
  let inherited;
2121
- if (version["workspace"] === true && record$2(pkg)) {
1579
+ if (version["workspace"] === true && record$1(pkg)) {
2122
1580
  const explicit = pkg["workspace"];
2123
1581
  let directory = node_path.default.posix.dirname(file);
2124
1582
  const explicitPath = typeof explicit === "string" && !explicit.includes("\\") && !explicit.includes(":") && !explicit.includes("${{") && !node_path.default.posix.isAbsolute(explicit) ? node_path.default.posix.normalize(node_path.default.posix.join(directory, explicit)) : void 0;
@@ -2126,9 +1584,9 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2126
1584
  if (explicitPath !== void 0) directory = explicitPath;
2127
1585
  while (true) {
2128
1586
  const table = readManifest(node_path.default.posix.join(directory, "Cargo.toml"))?.["workspace"];
2129
- if (record$2(table)) {
1587
+ if (record$1(table)) {
2130
1588
  const fields = table["package"];
2131
- inherited = record$2(fields) ? fields["rust-version"] : void 0;
1589
+ inherited = record$1(fields) ? fields["rust-version"] : void 0;
2132
1590
  break;
2133
1591
  }
2134
1592
  if (explicit !== void 0 || directory === ".") break;
@@ -2142,7 +1600,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2142
1600
  }
2143
1601
  if (name === "Pipfile") {
2144
1602
  const requires = toml("python-version")?.["requires"];
2145
- if (record$2(requires)) {
1603
+ if (record$1(requires)) {
2146
1604
  const minor = requires["python_version"];
2147
1605
  if (minor !== void 0 && minor !== policy.python.split(".").slice(0, 2).join(".")) add({
2148
1606
  rule: "python-version",
@@ -2161,7 +1619,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2161
1619
  const parsed = toml("python-version");
2162
1620
  if (parsed !== void 0) {
2163
1621
  const project = parsed["project"];
2164
- if (record$2(project) && project["requires-python"] !== void 0) {
1622
+ if (record$1(project) && project["requires-python"] !== void 0) {
2165
1623
  if (!pythonRequirementMatches(project["requires-python"], policy.python)) add({
2166
1624
  rule: "python-version",
2167
1625
  line: lineOf("requires-python"),
@@ -2169,9 +1627,9 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2169
1627
  });
2170
1628
  }
2171
1629
  const tool = parsed["tool"];
2172
- const configuredUv = record$2(tool) ? tool["uv"] : void 0;
1630
+ const configuredUv = record$1(tool) ? tool["uv"] : void 0;
2173
1631
  const uv = name === "uv.toml" ? parsed : configuredUv;
2174
- if (record$2(uv) && uv["python"] !== void 0) pin({
1632
+ if (record$1(uv) && uv["python"] !== void 0) pin({
2175
1633
  rule: "python-version",
2176
1634
  value: uv["python"],
2177
1635
  expected: policy.python,
@@ -2180,13 +1638,13 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2180
1638
  });
2181
1639
  }
2182
1640
  }
2183
- if (isMiseConfigPath(file)) {
1641
+ if (require_toolchain_inputs.isMiseConfigPath(file)) {
2184
1642
  const tools = toml("runtime-manager")?.["tools"];
2185
- if (record$2(tools)) for (const [tool, expected] of Object.entries(versions)) {
1643
+ if (record$1(tools)) for (const [tool, expected] of Object.entries(versions)) {
2186
1644
  const configured = tools[tool];
2187
1645
  if (configured !== void 0) pin({
2188
1646
  rule: "runtime-manager",
2189
- value: record$2(configured) ? configured["version"] : configured,
1647
+ value: record$1(configured) ? configured["version"] : configured,
2190
1648
  expected,
2191
1649
  selector: nodeTools.has(tool) ? "node" : void 0,
2192
1650
  label: `mise ${tool}`,
@@ -2208,12 +1666,13 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2208
1666
  line: index + 1
2209
1667
  });
2210
1668
  });
2211
- const checkRuntimeImage = ({ image, line, label }) => {
1669
+ const checkRuntimeImage = ({ image, line, label, at }) => {
2212
1670
  const unknownRuntime = () => {
1671
+ if (dynamic(image) && authorizeDynamic("image", line, at)) return;
2213
1672
  add({
2214
1673
  rule: "runtime-docker",
2215
1674
  line,
2216
- message: `cannot determine ${label} runtime; use a static image or a reasoned runtime-docker opt-out`
1675
+ message: `cannot determine ${label} runtime; use a static image or a scoped dynamicSelectors declaration`
2217
1676
  });
2218
1677
  };
2219
1678
  if (typeof image !== "string") {
@@ -2283,7 +1742,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2283
1742
  line
2284
1743
  });
2285
1744
  };
2286
- if (isDockerDefinitionPath(file)) {
1745
+ if (require_toolchain_inputs.isDockerDefinitionPath(file)) {
2287
1746
  const variables = /* @__PURE__ */ new Map();
2288
1747
  const escape = /^\s*#\s*escape\s*=\s*([\\`])\s*$/m.exec(text)?.[1] ?? "\\";
2289
1748
  const continuation = new RegExp(`${escape === "`" ? "`" : "\\\\"}\\s*$`);
@@ -2416,7 +1875,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2416
1875
  message: `unterminated Docker heredoc ${heredoc.delimiter}`
2417
1876
  });
2418
1877
  }
2419
- if (isComposeDefinitionPath(file) || isKubernetesDefinitionPath(file)) {
1878
+ if (require_toolchain_inputs.isComposeDefinitionPath(file) || require_toolchain_inputs.isKubernetesDefinitionPath(file)) {
2420
1879
  const lineCounter = new yaml.LineCounter();
2421
1880
  for (const containerDocument of (0, yaml.parseAllDocuments)(text, {
2422
1881
  lineCounter,
@@ -2425,7 +1884,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2425
1884
  })) {
2426
1885
  const contents = containerDocument.contents;
2427
1886
  const declaredResource = (0, yaml.isMap)(contents) && (0, yaml.isScalar)(contents.get("apiVersion", true)) && (0, yaml.isScalar)(contents.get("kind", true));
2428
- const recognizedContainer = isComposeDefinitionPath(file) || declaredResource;
1887
+ const recognizedContainer = require_toolchain_inputs.isComposeDefinitionPath(file) || declaredResource;
2429
1888
  if (containerDocument.errors.length > 0) {
2430
1889
  if (recognizedContainer) add({
2431
1890
  rule: "runtime-docker",
@@ -2435,13 +1894,14 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2435
1894
  continue;
2436
1895
  }
2437
1896
  try {
2438
- const classified = containerDocumentImages(containerDocument.toJS({ maxAliasCount: 100 }), file);
1897
+ const classified = require_toolchain_inputs.containerDocumentImages(containerDocument.toJS({ maxAliasCount: 100 }), file);
2439
1898
  for (const entry of classified?.images ?? []) {
2440
1899
  const node = containerDocument.getIn(entry.path, true);
2441
1900
  const line = ((0, yaml.isMap)(node) || (0, yaml.isScalar)(node) || (0, yaml.isSeq)(node) || (0, yaml.isAlias)(node)) && node.range !== void 0 && node.range !== null ? lineCounter.linePos(node.range[0]).line : 1;
2442
1901
  checkRuntimeImage({
2443
1902
  image: entry.image,
2444
1903
  line,
1904
+ at: entry.path.join("."),
2445
1905
  label: classified?.ecosystem === "docker-compose" ? "Compose image" : "Kubernetes image"
2446
1906
  });
2447
1907
  }
@@ -2454,7 +1914,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2454
1914
  }
2455
1915
  }
2456
1916
  }
2457
- const automationKind = githubAutomationFileKind(file);
1917
+ const automationKind = require_toolchain_inputs.githubAutomationFileKind(file);
2458
1918
  if (automationKind === void 0) return diagnostics;
2459
1919
  const document = (0, yaml.parseDocument)(text, {
2460
1920
  uniqueKeys: true,
@@ -2469,15 +1929,29 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2469
1929
  return diagnostics;
2470
1930
  }
2471
1931
  const nodeLine = (node) => {
2472
- if (!record$2(node) || !Array.isArray(node["range"])) return 1;
1932
+ if (!record$1(node) || !Array.isArray(node["range"])) return 1;
2473
1933
  const offset = node["range"].at(0);
2474
1934
  return typeof offset === "number" ? text.slice(0, offset).split("\n").length : 1;
2475
1935
  };
2476
1936
  let checkoutBindings = [];
2477
1937
  let checkoutDestinations = /* @__PURE__ */ new Map();
2478
1938
  let unknownCheckoutDestination = false;
1939
+ let unreviewableCheckoutSource = false;
2479
1940
  const staticRepositoryPath = (value) => typeof value === "string" && value !== "" && !value.includes("${{") && !value.includes("\\") && !value.includes(":") && !node_path.default.posix.isAbsolute(value) && !value.split("/").includes("..");
2480
1941
  const normalizeCheckoutPath = (prefix) => node_path.default.posix.normalize(prefix).replace(/\/$/, "") || ".";
1942
+ const checkoutProvidersFor = (selector) => {
1943
+ if (selector === void 0) return checkoutBindings;
1944
+ let deepest;
1945
+ for (const entry of checkoutBindings) {
1946
+ if (entry.path !== "." && selector !== entry.path && !selector.startsWith(`${entry.path}/`)) continue;
1947
+ if (deepest === void 0 || entry.path !== "." && (deepest.path === "." || entry.path.length > deepest.path.length)) deepest = entry;
1948
+ }
1949
+ return checkoutBindings.filter((entry) => entry.path === deepest?.path);
1950
+ };
1951
+ const hasAmbiguousCheckoutWriters = (selector, providers) => {
1952
+ if (providers.length > 0) return providers.some((entry) => (checkoutDestinations.get(entry.path) ?? 0) > 1);
1953
+ return [...checkoutDestinations].some(([prefix, count]) => count > 1 && (selector === void 0 || prefix === "." || selector === prefix || selector.startsWith(`${prefix}/`)));
1954
+ };
2481
1955
  const checkReference = ({ value, tool, line }) => {
2482
1956
  const allowed = {
2483
1957
  node: [".node-version", ".nvmrc"],
@@ -2486,8 +1960,28 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2486
1960
  }[tool];
2487
1961
  const rule = tool === "bun" ? "bun-pins" : "runtime-workflow";
2488
1962
  const selector = staticRepositoryPath(value) ? node_path.default.posix.normalize(value) : void 0;
2489
- const binding = selector === void 0 ? void 0 : checkoutBindings.findLast((entry) => entry.path === "." || selector === entry.path || selector.startsWith(`${entry.path}/`));
1963
+ const providers = checkoutProvidersFor(selector);
1964
+ const binding = selector === void 0 ? void 0 : providers.at(-1);
1965
+ const dynamicSource = providers.some((entry) => entry.dynamicSource);
1966
+ const sparseFailure = providers.find((entry) => {
1967
+ if (entry.sparse.mode === "invalid") return true;
1968
+ if (entry.sparse.mode !== "files" && entry.sparse.mode !== "mixed") return false;
1969
+ if (selector === void 0) return true;
1970
+ const selectedFile = entry.path === "." ? selector : node_path.default.posix.relative(entry.path, selector);
1971
+ return !entry.sparse.paths.has(selectedFile);
1972
+ });
1973
+ if (sparseFailure !== void 0) {
1974
+ let missingFile = "the selected version file";
1975
+ if (selector !== void 0) missingFile = sparseFailure.path === "." ? selector : node_path.default.posix.relative(sparseFailure.path, selector);
1976
+ add({
1977
+ rule,
1978
+ line,
1979
+ message: `checkout sparse-checkout must explicitly list ${missingFile} without invalid literal patterns`
1980
+ });
1981
+ return;
1982
+ }
2490
1983
  if (selector === void 0) {
1984
+ if (tool === "bun" && dynamic(value) && !unreviewableCheckoutSource && authorizeDynamic("bun-source", line)) return;
2491
1985
  add({
2492
1986
  rule,
2493
1987
  line,
@@ -2495,7 +1989,8 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2495
1989
  });
2496
1990
  return;
2497
1991
  }
2498
- if (unknownCheckoutDestination || [...checkoutDestinations].some(([prefix, count]) => count > 1 && (prefix === "." || selector === prefix || selector.startsWith(`${prefix}/`)))) {
1992
+ if (unknownCheckoutDestination || hasAmbiguousCheckoutWriters(selector, providers)) {
1993
+ if (tool === "bun" && dynamicSource && !unreviewableCheckoutSource && authorizeDynamic("bun-source", line)) return;
2499
1994
  add({
2500
1995
  rule,
2501
1996
  line,
@@ -2504,6 +1999,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2504
1999
  return;
2505
2000
  }
2506
2001
  if (binding?.source === "untrusted") {
2002
+ if (tool === "bun" && dynamicSource && !unreviewableCheckoutSource && authorizeDynamic("bun-source", line)) return;
2507
2003
  add({
2508
2004
  rule,
2509
2005
  line: binding.line,
@@ -2520,13 +2016,15 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2520
2016
  });
2521
2017
  return;
2522
2018
  }
2523
- if (binding !== void 0 && (binding.sparse.mode === "invalid" || binding.sparse.mode === "files" && !binding.sparse.paths.has(target))) {
2524
- add({
2525
- rule,
2526
- line,
2527
- message: `checkout sparse-checkout must explicitly list ${target} without dynamic, glob, or negation patterns`
2528
- });
2529
- return;
2019
+ if (binding !== void 0 && (binding.sparse.mode === "dynamic" || binding.sparse.mode === "mixed")) {
2020
+ if (tool !== "bun" || unreviewableCheckoutSource || !authorizeDynamic("bun-source", line)) {
2021
+ add({
2022
+ rule,
2023
+ line,
2024
+ message: `checkout sparse-checkout must explicitly list ${target} without dynamic, glob, or negation patterns`
2025
+ });
2026
+ return;
2027
+ }
2530
2028
  }
2531
2029
  if (binding?.source === "delegated") {
2532
2030
  onDelegated?.({
@@ -2545,7 +2043,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2545
2043
  const manifest = JSON.parse(content ?? "");
2546
2044
  pin({
2547
2045
  rule,
2548
- value: record$2(manifest) ? manifest["packageManager"] : void 0,
2046
+ value: record$1(manifest) ? manifest["packageManager"] : void 0,
2549
2047
  expected: `bun@${policy.bun}`,
2550
2048
  label: "setup-bun version file packageManager",
2551
2049
  line
@@ -2648,10 +2146,10 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2648
2146
  return inputs;
2649
2147
  };
2650
2148
  const getInput = (node, key) => inputsOf(node).get(key.toUpperCase());
2651
- const dynamicRefBody = (value) => {
2652
- if (typeof value !== "string" || !value.startsWith("${{")) return void 0;
2149
+ const expressionAt = (value, start) => {
2150
+ if (!value.startsWith("${{", start)) return void 0;
2653
2151
  let quote;
2654
- for (let index = 3; index < value.length; index++) {
2152
+ for (let index = start + 3; index < value.length; index++) {
2655
2153
  const character = value.at(index);
2656
2154
  if (quote !== void 0) {
2657
2155
  if (character === quote) if (quote === "'" && value.at(index + 1) === "'") index++;
@@ -2661,9 +2159,33 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2661
2159
  }
2662
2160
  if (character === "'" || character === "\"") quote = character;
2663
2161
  else if (value.startsWith("${{", index)) return void 0;
2664
- else if (character === "}" && value.at(index + 1) === "}") return index === value.length - 2 ? value.slice(3, index).trim() : void 0;
2162
+ else if (character === "}" && value.at(index + 1) === "}") return {
2163
+ body: value.slice(start + 3, index).trim(),
2164
+ end: index + 2
2165
+ };
2665
2166
  }
2666
2167
  };
2168
+ const dynamicRefBody = (value) => {
2169
+ if (typeof value !== "string") return void 0;
2170
+ const expression = expressionAt(value, 0);
2171
+ return expression?.end === value.length ? expression.body : void 0;
2172
+ };
2173
+ const sparseEntryIsValid = (entry) => {
2174
+ let literalPath = "";
2175
+ let position = 0;
2176
+ while (position < entry.length) {
2177
+ const start = entry.indexOf("${{", position);
2178
+ const literal = entry.slice(position, start < 0 ? entry.length : start);
2179
+ if (/[!$*?[\]\\:]/.test(literal) || literal.split("/").includes("..")) return false;
2180
+ literalPath += literal;
2181
+ if (start < 0) break;
2182
+ const expression = expressionAt(entry, start);
2183
+ if (expression === void 0 || expression.body === "") return false;
2184
+ literalPath += "expression";
2185
+ position = expression.end;
2186
+ }
2187
+ return staticRepositoryPath(literalPath);
2188
+ };
2667
2189
  const checkoutPrefixOf = (node) => {
2668
2190
  const destination = getInput(node, "path");
2669
2191
  if (destination === void 0) return ".";
@@ -2676,11 +2198,19 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2676
2198
  const value = getInput(node, "sparse-checkout");
2677
2199
  const cone = getInput(node, "sparse-checkout-cone-mode");
2678
2200
  if (!(0, yaml.isScalar)(value) || typeof value.value !== "string" || cone !== void 0 && (!(0, yaml.isScalar)(cone) || cone.value !== true && cone.value !== false && cone.value !== "true" && cone.value !== "false")) return { mode: "invalid" };
2679
- const paths = value.value.split(/\r?\n/).map((entry) => entry.trim()).filter((entry) => entry !== "");
2680
- if (paths.length === 0 || paths.some((entry) => !staticRepositoryPath(entry) || entry.startsWith("!") || /[*?[\]]/.test(entry))) return { mode: "invalid" };
2201
+ const paths = value.value.split(/\r?\n/).map((entry) => entry.trim().replace(/^\//, "")).filter((entry) => entry !== "");
2202
+ if (paths.length === 0 || paths.some((entry) => !sparseEntryIsValid(entry))) return { mode: "invalid" };
2203
+ const literalPaths = new Set(paths.filter((entry) => !dynamic(entry)));
2204
+ if (paths.some(dynamic)) {
2205
+ if (literalPaths.size === 0) return { mode: "dynamic" };
2206
+ return {
2207
+ mode: "mixed",
2208
+ paths: literalPaths
2209
+ };
2210
+ }
2681
2211
  return {
2682
2212
  mode: "files",
2683
- paths: new Set(paths)
2213
+ paths: literalPaths
2684
2214
  };
2685
2215
  };
2686
2216
  let currentEngineFloor;
@@ -2710,8 +2240,9 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2710
2240
  }
2711
2241
  if (value.startsWith("./")) {
2712
2242
  const selector = staticRepositoryPath(value) ? node_path.default.posix.normalize(value) : void 0;
2713
- const binding = selector === void 0 ? void 0 : checkoutBindings.findLast((entry) => entry.path === "." || selector === entry.path || selector.startsWith(`${entry.path}/`));
2714
- if (selector === void 0 || unknownCheckoutDestination || binding !== void 0 && binding.source !== "tracked" || [...checkoutDestinations].some(([prefix, count]) => count > 1 && (prefix === "." || selector === prefix || selector.startsWith(`${prefix}/`)))) add({
2243
+ const providers = checkoutProvidersFor(selector);
2244
+ const binding = selector === void 0 ? void 0 : providers.at(-1);
2245
+ if (selector === void 0 || unknownCheckoutDestination || binding !== void 0 && binding.source !== "tracked" || hasAmbiguousCheckoutWriters(selector, providers)) add({
2715
2246
  rule: "action-pins",
2716
2247
  line,
2717
2248
  message: "local actions require an unambiguous checkout of the current source snapshot"
@@ -2767,24 +2298,55 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2767
2298
  const checkoutRepository = getInput(node, "repository");
2768
2299
  const checkoutRef = getInput(node, "ref");
2769
2300
  const prefix = checkoutPrefixOf(node);
2301
+ const clean = getInput(node, "clean");
2302
+ const cleanIsResolved = clean === void 0 || (0, yaml.isScalar)(clean) && !dynamic(clean.value);
2770
2303
  let repo;
2771
2304
  if (checkoutRepository !== void 0) repo = (0, yaml.isScalar)(checkoutRepository) ? checkoutRepository.value : null;
2772
2305
  const self = repo === void 0 || repo === "${{ github.repository }}" || typeof repo === "string" && repository !== void 0 && repo.toLowerCase() === repository.toLowerCase();
2773
2306
  const workflowSource = repo === "${{ job.workflow_repository }}" && (0, yaml.isScalar)(checkoutRef) && checkoutRef.value === "${{ job.workflow_sha }}";
2774
- const currentSource = self && checkoutRef === void 0;
2775
- const refValue = (0, yaml.isScalar)(checkoutRef) ? checkoutRef.value : void 0;
2307
+ const triggers = getNode(document.contents, "on");
2308
+ const callerContext = (() => {
2309
+ if ((0, yaml.isMap)(triggers)) return [...keysOf(triggers)];
2310
+ if ((0, yaml.isSeq)(triggers)) return triggers.items.map((event) => (0, yaml.isScalar)(event) ? event.value : void 0);
2311
+ if ((0, yaml.isScalar)(triggers)) return [triggers.value];
2312
+ return [];
2313
+ })().includes("workflow_call");
2314
+ let refValue = (0, yaml.isScalar)(checkoutRef) ? checkoutRef.value : void 0;
2315
+ if (callerContext && (repo === void 0 || repo === "${{ github.repository }}") && checkoutRef === void 0) refValue = "${{ github.sha }}";
2316
+ const currentRef = dynamicRefBody(refValue);
2317
+ const currentSource = self && !callerContext && (checkoutRef === void 0 || currentRef === "github.sha");
2776
2318
  const dynamicRef = dynamicRefBody(refValue);
2777
- 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);
2319
+ 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");
2320
+ const validSource = cleanIsResolved && ref === approved?.sha && sourceMap({
2321
+ node,
2322
+ key: "if"
2323
+ }) === void 0 && sourceMap({
2324
+ node,
2325
+ key: "continue-on-error"
2326
+ }) === void 0 && (currentSource || workflowSource || delegatedSource);
2327
+ if (!validSource) unreviewableCheckoutSource = true;
2778
2328
  if (!staticRepositoryPath(prefix)) checkoutBindings.push({
2779
2329
  path: ".",
2330
+ dynamicSource: validSource && dynamic(prefix),
2780
2331
  source: "untrusted",
2781
2332
  reason: "checkout destination must be a static repository path",
2782
2333
  line,
2783
- sparse: { mode: "invalid" }
2334
+ sparse: sparseCheckoutOf(node)
2784
2335
  });
2785
2336
  else {
2786
2337
  const destination = normalizeCheckoutPath(prefix);
2787
- const trustedWriter = ref === approved?.sha && sourceMap({
2338
+ checkoutBindings = checkoutBindings.map((entry) => {
2339
+ if (entry.path === destination || destination !== "." && !entry.path.startsWith(`${destination}/`)) return entry;
2340
+ return {
2341
+ path: entry.path,
2342
+ sparse: { mode: "all" },
2343
+ dynamicSource: false,
2344
+ source: "untrusted",
2345
+ reason: "a later ancestor checkout invalidates this descendant destination",
2346
+ line
2347
+ };
2348
+ });
2349
+ const trustedWriter = cleanIsResolved && ref === approved?.sha && sourceMap({
2788
2350
  node,
2789
2351
  key: "if"
2790
2352
  }) === void 0 && sourceMap({
@@ -2793,18 +2355,21 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2793
2355
  }) === void 0 && checkoutDestinations.get(destination) === 1 && !unknownCheckoutDestination;
2794
2356
  if (trustedWriter && delegatedSource && typeof refValue === "string") checkoutBindings.push({
2795
2357
  path: destination,
2358
+ dynamicSource: validSource && delegatedSource,
2796
2359
  sparse: sparseCheckoutOf(node),
2797
2360
  source: "delegated",
2798
2361
  ref: refValue
2799
2362
  });
2800
2363
  else if (trustedWriter && (workflowSource || currentSource)) checkoutBindings.push({
2801
2364
  path: destination,
2365
+ dynamicSource: validSource && delegatedSource,
2802
2366
  sparse: sparseCheckoutOf(node),
2803
2367
  source: "tracked"
2804
2368
  });
2805
2369
  else {
2806
2370
  let reason = "checkout does not select a trusted repository source snapshot";
2807
- if (ref !== approved?.sha) reason = "actions/checkout must use the approved action SHA";
2371
+ if (!cleanIsResolved) reason = "checkout clean must be statically resolved";
2372
+ else if (ref !== approved?.sha) reason = "actions/checkout must use the approved action SHA";
2808
2373
  else if (sourceMap({
2809
2374
  node,
2810
2375
  key: "if"
@@ -2815,6 +2380,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2815
2380
  else if (checkoutDestinations.get(destination) !== 1 || unknownCheckoutDestination) reason = "checkout destination must have exactly one static writer";
2816
2381
  checkoutBindings.push({
2817
2382
  path: destination,
2383
+ dynamicSource: validSource && delegatedSource,
2818
2384
  sparse: sparseCheckoutOf(node),
2819
2385
  source: "untrusted",
2820
2386
  reason,
@@ -2863,21 +2429,23 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2863
2429
  line: reference === void 0 ? line : nodeLine(reference)
2864
2430
  });
2865
2431
  };
2866
- const checkContainer = (node) => {
2432
+ const checkContainer = (node, at) => {
2867
2433
  node = resolveNode(node);
2868
2434
  if (node === void 0) return;
2869
2435
  const image = (0, yaml.isMap)(node) ? getNode(node, "image") : node;
2870
2436
  checkRuntimeImage({
2871
2437
  image: (0, yaml.isScalar)(image) ? image.value : void 0,
2872
2438
  line: nodeLine(image ?? node),
2873
- label: "container image"
2439
+ label: "container image",
2440
+ at
2874
2441
  });
2875
2442
  };
2876
- const checkSteps = (node, floor) => {
2443
+ const checkSteps = (node, locationPrefix, floor) => {
2877
2444
  currentEngineFloor = void 0;
2878
2445
  checkoutBindings = [];
2879
2446
  checkoutDestinations = /* @__PURE__ */ new Map();
2880
2447
  unknownCheckoutDestination = false;
2448
+ unreviewableCheckoutSource = false;
2881
2449
  node = resolveNode(node);
2882
2450
  if (!(0, yaml.isSeq)(node)) return;
2883
2451
  for (const step of node.items) {
@@ -2893,9 +2461,14 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2893
2461
  }
2894
2462
  currentEngineFloor = floor;
2895
2463
  try {
2896
- for (const step of node.items) checkAction(step);
2464
+ for (const step of node.items) {
2465
+ const id = getNode(step, "id");
2466
+ currentLocation = (0, yaml.isScalar)(id) && typeof id.value === "string" ? `${locationPrefix}.steps.${id.value}` : void 0;
2467
+ checkAction(step);
2468
+ }
2897
2469
  } finally {
2898
2470
  currentEngineFloor = void 0;
2471
+ currentLocation = void 0;
2899
2472
  }
2900
2473
  };
2901
2474
  try {
@@ -2909,15 +2482,15 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2909
2482
  currentEngineFloor = void 0;
2910
2483
  const job = getNode(jobs, key);
2911
2484
  checkAction(job);
2912
- checkContainer(getNode(job, "container"));
2485
+ checkContainer(getNode(job, "container"), `jobs.${key}.container`);
2913
2486
  const services = getNode(job, "services");
2914
- if ((0, yaml.isMap)(services)) for (const service of keysOf(services)) checkContainer(getNode(services, service));
2915
- checkSteps(getNode(job, "steps"), engineFloors.find((entry) => entry.job === key));
2487
+ if ((0, yaml.isMap)(services)) for (const service of keysOf(services)) checkContainer(getNode(services, service), `jobs.${key}.services.${service}`);
2488
+ checkSteps(getNode(job, "steps"), `jobs.${key}`, engineFloors.find((entry) => entry.job === key));
2916
2489
  }
2917
2490
  } else {
2918
2491
  const runs = getNode(document.contents, "runs");
2919
2492
  const using = getNode(runs, "using");
2920
- if ((0, yaml.isScalar)(using) && using.value === "composite") checkSteps(getNode(runs, "steps"));
2493
+ if ((0, yaml.isScalar)(using) && using.value === "composite") checkSteps(getNode(runs, "steps"), "runs");
2921
2494
  if ((0, yaml.isScalar)(using) && using.value === "docker") {
2922
2495
  const image = getNode(runs, "image");
2923
2496
  const value = (0, yaml.isScalar)(image) ? image.value : void 0;
@@ -2949,192 +2522,6 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2949
2522
  return diagnostics;
2950
2523
  };
2951
2524
  //#endregion
2952
- //#region src/toolchain-schema.ts
2953
- const packagePinKeys = [
2954
- "oxlint",
2955
- "oxlint-tsgolint",
2956
- "@oxlint/plugins",
2957
- "@stll/oxlint-plugin",
2958
- "oxfmt",
2959
- "lefthook"
2960
- ];
2961
- const valkeyArchitectures = ["amd64", "arm64"];
2962
- const valkeyArtifactPackages = {
2963
- server: "valkey-server",
2964
- tools: "valkey-tools"
2965
- };
2966
- const record$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2967
- const artifactRecord = (value, keys) => {
2968
- 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");
2969
- return value;
2970
- };
2971
- const parseValkeyArtifacts = (input, version) => {
2972
- const architectures = artifactRecord(input, valkeyArchitectures);
2973
- const line = version.split(".").slice(0, 2).join(".");
2974
- const parseArchitecture = (architecture) => {
2975
- const packages = artifactRecord(architectures[architecture], Object.keys(valkeyArtifactPackages));
2976
- const parseArtifact = (kind) => {
2977
- const artifact = artifactRecord(packages[kind], ["url", "sha256"]);
2978
- const url = artifact["url"];
2979
- const sha256 = artifact["sha256"];
2980
- const expected = `https://download.valkey.io/packaging/valkey-${line}/deb/ubuntu2404/${architecture}/${valkeyArtifactPackages[kind]}_${version}-1.noble_${architecture}.deb`;
2981
- if (typeof url !== "string" || url !== expected) throw new Error("valkeyArtifacts URL must select the exact official versioned Ubuntu artifact");
2982
- if (typeof sha256 !== "string" || !/^[a-f0-9]{64}$/.test(sha256)) throw new Error("valkeyArtifacts requires a lowercase SHA256 pin");
2983
- return {
2984
- url,
2985
- sha256
2986
- };
2987
- };
2988
- return {
2989
- server: parseArtifact("server"),
2990
- tools: parseArtifact("tools")
2991
- };
2992
- };
2993
- return {
2994
- amd64: parseArchitecture("amd64"),
2995
- arm64: parseArchitecture("arm64")
2996
- };
2997
- };
2998
- /** Reject unsupported or incomplete installed policies before inspecting a consumer. */
2999
- const parseToolchainPolicy = (input) => {
3000
- if (!record$1(input) || input["schemaVersion"] !== 1) throw new Error("toolchain.json must use schemaVersion 1");
3001
- const string = (object, key) => {
3002
- const value = object[key];
3003
- if (typeof value !== "string" || value.trim() === "") throw new Error(`toolchain.json requires a nonempty ${key}`);
3004
- return value;
3005
- };
3006
- const packages = {};
3007
- for (const key of packagePinKeys) packages[key] = string(input, key);
3008
- const bun = string(input, "bun");
3009
- const typescript = string(input, "typescript");
3010
- const node = string(input, "node");
3011
- const python = string(input, "python");
3012
- const postgres = string(input, "postgres");
3013
- const valkey = string(input, "valkey");
3014
- const rust = string(input, "rust");
3015
- const rustCompilerDevelopment = string(input, "rustCompilerDevelopment");
3016
- for (const [name, value] of Object.entries({
3017
- ...packages,
3018
- bun,
3019
- typescript
3020
- })) if ((0, semver.valid)(value) !== value) throw new Error(`toolchain.json ${name} must be an exact release`);
3021
- if (!/^[1-9]\d*$/.test(postgres) || !Number.isSafeInteger(Number(postgres))) throw new Error("PostgreSQL must be a canonical positive major");
3022
- if ((0, semver.valid)(valkey) !== valkey || !/^\d+\.\d+\.\d+$/.test(valkey)) throw new Error("Valkey must be an exact stable release");
3023
- const valkeyArtifacts = parseValkeyArtifacts(input["valkeyArtifacts"], valkey);
3024
- if (!nodePolicyValid(node)) throw new Error("Node must be a canonical major N.x series");
3025
- if (!/^\d+\.\d+(?:\.\d+)?$/.test(python)) throw new Error("Python must be an exact minor or patch release");
3026
- if (!/^\d+\.\d+\.\d+$/.test(rust)) throw new Error("Rust must be an exact stable release");
3027
- if (!/^nightly-\d{4}-\d{2}-\d{2}$/.test(rustCompilerDevelopment)) throw new Error("Rust compiler development must be a dated nightly");
3028
- const compatibility = input["typescript6Compatibility"];
3029
- if (!record$1(compatibility)) throw new Error("missing TypeScript compatibility policy");
3030
- const version = string(compatibility, "version");
3031
- const packageAlias = string(compatibility, "packageAlias");
3032
- if ((0, semver.valid)(version) !== version) throw new Error("TypeScript compatibility must be an exact release");
3033
- const layouts = input["typescriptInstallLayouts"];
3034
- if (!Array.isArray(layouts) || layouts.length === 0) throw new Error("missing TypeScript install layouts");
3035
- const typescriptInstallLayouts = layouts.map((layout) => {
3036
- if (!record$1(layout)) throw new Error("invalid TypeScript install layout");
3037
- const compilerPackage = string(layout, "compilerPackage");
3038
- const compilerSpecifier = string(layout, "compilerSpecifier");
3039
- const typecheckCommand = string(layout, "typecheckCommand");
3040
- switch (layout["type"]) {
3041
- case "direct":
3042
- if (compilerSpecifier !== typescript || compilerPackage !== "typescript") throw new Error("direct TypeScript layout differs from shared version");
3043
- return {
3044
- type: "direct",
3045
- compilerPackage,
3046
- compilerSpecifier,
3047
- typecheckCommand
3048
- };
3049
- case "split-compatibility": {
3050
- const compatibilityPackage = string(layout, "compatibilityPackage");
3051
- const compatibilitySpecifier = string(layout, "compatibilitySpecifier");
3052
- if (compilerSpecifier !== `npm:typescript@${typescript}` || compatibilitySpecifier !== version) throw new Error("split TypeScript layout differs from shared versions");
3053
- return {
3054
- type: "split-compatibility",
3055
- compilerPackage,
3056
- compilerSpecifier,
3057
- compatibilityPackage,
3058
- compatibilitySpecifier,
3059
- typecheckCommand
3060
- };
3061
- }
3062
- default: throw new Error("unsupported TypeScript install layout");
3063
- }
3064
- });
3065
- const actionsInput = input["actions"];
3066
- if (!record$1(actionsInput) || Object.keys(actionsInput).length === 0) throw new Error("missing approved actions");
3067
- const actions = {};
3068
- for (const [name, action] of Object.entries(actionsInput)) {
3069
- if (!record$1(action)) throw new Error(`invalid action policy: ${name}`);
3070
- const sha = string(action, "sha");
3071
- const actionVersion = string(action, "version");
3072
- if (!/^[a-f0-9]{40}$/.test(sha) || !/^v\d+(?:\.\d+)*$/.test(actionVersion)) throw new Error(`invalid action SHA/version: ${name}`);
3073
- actions[name] = {
3074
- sha,
3075
- version: actionVersion
3076
- };
3077
- }
3078
- const dependabotInput = input["dependabot"];
3079
- if (!record$1(dependabotInput) || !record$1(dependabotInput["schedule"]) || !record$1(dependabotInput["cooldown"]) || !record$1(dependabotInput["groups"])) throw new Error("missing Dependabot policy");
3080
- const scheduleInput = dependabotInput["schedule"];
3081
- const schedule = {
3082
- interval: string(scheduleInput, "interval"),
3083
- day: string(scheduleInput, "day"),
3084
- time: string(scheduleInput, "time"),
3085
- timezone: string(scheduleInput, "timezone")
3086
- };
3087
- const defaultDays = dependabotInput["cooldown"]["defaultDays"];
3088
- if (typeof defaultDays !== "number" || !Number.isInteger(defaultDays) || defaultDays < 5) throw new Error("Dependabot cooldown must be at least five days");
3089
- const strings = (value) => {
3090
- if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string")) throw new Error("Dependabot group values must be string arrays");
3091
- return value;
3092
- };
3093
- const groups = {};
3094
- for (const [name, group] of Object.entries(dependabotInput["groups"])) {
3095
- if (!record$1(group)) throw new Error(`invalid Dependabot group: ${name}`);
3096
- groups[name] = {
3097
- patterns: strings(group["patterns"]),
3098
- updateTypes: strings(group["updateTypes"])
3099
- };
3100
- }
3101
- return {
3102
- bun,
3103
- typescript,
3104
- node,
3105
- postgres,
3106
- valkey,
3107
- valkeyArtifacts,
3108
- python,
3109
- rust,
3110
- rustCompilerDevelopment,
3111
- packages,
3112
- actions,
3113
- typescriptInstallLayouts,
3114
- typescript6Compatibility: {
3115
- version,
3116
- packageAlias
3117
- },
3118
- dependabot: {
3119
- schedule,
3120
- cooldown: { defaultDays },
3121
- groups,
3122
- ignoredPackages: [.../* @__PURE__ */ new Set([
3123
- ...Object.keys(packages),
3124
- ...typescriptInstallLayouts.flatMap((layout) => layout.type === "direct" ? [layout.compilerPackage] : [layout.compilerPackage, layout.compatibilityPackage]),
3125
- packageAlias,
3126
- "bun-types"
3127
- ])],
3128
- ignoredActions: Object.keys(actions),
3129
- ignoredImages: [
3130
- "node",
3131
- "python",
3132
- "oven/bun"
3133
- ]
3134
- }
3135
- };
3136
- };
3137
- //#endregion
3138
2525
  //#region src/toolchain-guard.ts
3139
2526
  const toolchainRules = [.../* @__PURE__ */ new Set([
3140
2527
  ...packageRules,
@@ -3146,7 +2533,7 @@ const record = (value) => typeof value === "object" && value !== null && !Array.
3146
2533
  const array = (value) => Array.isArray(value);
3147
2534
  /** Repository-owned declarations are explicit; malformed decisions fail closed. */
3148
2535
  const parseToolchainConfiguration = (input) => {
3149
- 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");
2536
+ 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");
3150
2537
  const disabled = /* @__PURE__ */ new Set();
3151
2538
  const optOuts = input["optOuts"] ?? [];
3152
2539
  if (!array(optOuts)) throw new Error("optOuts must be an array");
@@ -3158,8 +2545,10 @@ const parseToolchainConfiguration = (input) => {
3158
2545
  }
3159
2546
  return {
3160
2547
  disabled,
3161
- cloud: parseCloudSetup(input["cloud"]),
3162
- engineFloors: parseEngineFloors(input["engineFloors"])
2548
+ cloud: require_toolchain_inputs.parseCloudSetup(input["cloud"]),
2549
+ engineFloors: parseEngineFloors(input["engineFloors"]),
2550
+ dynamicSelectors: parseDynamicSelectors(input["dynamicSelectors"]),
2551
+ consumerChecks: parseConsumerChecks(input["consumerChecks"])
3163
2552
  };
3164
2553
  };
3165
2554
  /** Read one tracked configuration snapshot for guards and policy generation. */
@@ -3177,7 +2566,7 @@ const readToolchainInputs = (root) => {
3177
2566
  const trackedFiles = new Set(tracked);
3178
2567
  const resolvedRoot = (0, node_fs.realpathSync)(root);
3179
2568
  for (const file of tracked) {
3180
- const kind = toolchainInputKind(file);
2569
+ const kind = require_toolchain_inputs.toolchainInputKind(file);
3181
2570
  if (kind === void 0) continue;
3182
2571
  if (kind === "presence") {
3183
2572
  files[file] = "";
@@ -3222,13 +2611,12 @@ const checkToolchain = ({ root, policy, onDelegated }) => {
3222
2611
  }).trim();
3223
2612
  repository = /^(?:https?:\/\/github\.com\/|git@github\.com:|ssh:\/\/git@github\.com\/)([^/]+\/[^/]+?)(?:\.git)?\/?$/.exec(origin)?.[1];
3224
2613
  } catch {}
3225
- let disabled = /* @__PURE__ */ new Set();
3226
- let cloud;
2614
+ let configuration = parseToolchainConfiguration({});
3227
2615
  const engineFloors = [];
2616
+ let dynamicSelectors = [];
3228
2617
  if (files["stll-toolchain.json"] !== void 0) try {
3229
- const configuration = parseToolchainConfiguration(JSON.parse(files["stll-toolchain.json"]));
3230
- disabled = configuration.disabled;
3231
- cloud = configuration.cloud;
2618
+ configuration = parseToolchainConfiguration(JSON.parse(files["stll-toolchain.json"]));
2619
+ dynamicSelectors = configuration.dynamicSelectors;
3232
2620
  for (const entry of configuration.engineFloors) engineFloors.push(resolveEngineFloor(entry, files));
3233
2621
  } catch (error) {
3234
2622
  diagnostics.push({
@@ -3238,36 +2626,43 @@ const checkToolchain = ({ root, policy, onDelegated }) => {
3238
2626
  message: error instanceof Error ? error.message : String(error)
3239
2627
  });
3240
2628
  }
3241
- const script = files[cloudSetupPath];
2629
+ const cloud = configuration.cloud;
2630
+ const script = files[require_toolchain_inputs.cloudSetupPath];
3242
2631
  if (cloud === void 0 && trackedFiles.has(".agents/cloud-setup.sh")) diagnostics.push({
3243
2632
  rule: "cloud-setup-drift",
3244
- path: cloudSetupPath,
2633
+ path: require_toolchain_inputs.cloudSetupPath,
3245
2634
  line: 1,
3246
2635
  message: "cloud setup script requires an explicit cloud declaration in stll-toolchain.json"
3247
2636
  });
3248
2637
  if (cloud !== void 0) try {
3249
2638
  const nodeVersion = files[".node-version"];
3250
2639
  if (nodeVersion === void 0) throw new Error("cloud setup requires a tracked root .node-version with an exact stable release");
3251
- if (script !== generateCloudSetup({
2640
+ if (script !== require_toolchain_inputs.generateCloudSetup({
3252
2641
  policy,
3253
2642
  cloud,
3254
2643
  nodeVersion: nodeVersion.trim()
3255
2644
  })) diagnostics.push({
3256
2645
  rule: "cloud-setup-drift",
3257
- path: cloudSetupPath,
2646
+ path: require_toolchain_inputs.cloudSetupPath,
3258
2647
  line: 1,
3259
2648
  message: "cloud setup script is missing or differs from shared policy; run stll-cloud-setup"
3260
2649
  });
3261
2650
  } catch (error) {
3262
2651
  diagnostics.push({
3263
2652
  rule: "cloud-setup-drift",
3264
- path: cloudSetupPath,
2653
+ path: require_toolchain_inputs.cloudSetupPath,
3265
2654
  line: 1,
3266
2655
  message: error instanceof Error ? error.message : String(error)
3267
2656
  });
3268
2657
  }
3269
2658
  const exercisedFloors = /* @__PURE__ */ new Set();
3270
2659
  const mismatchedFloors = /* @__PURE__ */ new Set();
2660
+ const selectorMatches = /* @__PURE__ */ new Map();
2661
+ diagnostics.push(...checkConsumerChecks({
2662
+ declarations: configuration.consumerChecks,
2663
+ files,
2664
+ policy
2665
+ }));
3271
2666
  diagnostics.push(...checkPackageFiles({
3272
2667
  files,
3273
2668
  policy
@@ -3283,6 +2678,8 @@ const checkToolchain = ({ root, policy, onDelegated }) => {
3283
2678
  }
3284
2679
  },
3285
2680
  trackedFiles,
2681
+ dynamicSelectors: dynamicSelectors.filter((entry) => entry.path === file),
2682
+ onDynamicSelector: (entry) => selectorMatches.set(entry, (selectorMatches.get(entry) ?? 0) + 1),
3286
2683
  repository,
3287
2684
  onDelegated,
3288
2685
  engineFloors: engineFloors.filter((entry) => entry.workflow === file),
@@ -3298,11 +2695,17 @@ const checkToolchain = ({ root, policy, onDelegated }) => {
3298
2695
  line: 1,
3299
2696
  message: `engine floor ${entry.workflow}:${entry.job} requires a setup-node exact patch satisfying ${entry.package} engines.node minimum major ${entry.major}`
3300
2697
  });
2698
+ for (const entry of dynamicSelectors) if (selectorMatches.get(entry) !== 1) diagnostics.push({
2699
+ rule: "configuration",
2700
+ path: "stll-toolchain.json",
2701
+ line: 1,
2702
+ message: `dynamic selector ${entry.path}:${entry.at ?? entry.line} must match exactly one unresolved ${entry.kind} selector`
2703
+ });
3301
2704
  diagnostics.push(...checkDependabot({
3302
2705
  files,
3303
2706
  policy: policy.dependabot
3304
2707
  }));
3305
- return diagnostics.filter((diagnostic) => !disabled.has(diagnostic.rule));
2708
+ return diagnostics.filter((diagnostic) => !configuration.disabled.has(diagnostic.rule));
3306
2709
  };
3307
2710
  //#endregion
3308
2711
  Object.defineProperty(exports, "checkToolchain", {
@@ -3311,30 +2714,12 @@ Object.defineProperty(exports, "checkToolchain", {
3311
2714
  return checkToolchain;
3312
2715
  }
3313
2716
  });
3314
- Object.defineProperty(exports, "cloudSetupPath", {
3315
- enumerable: true,
3316
- get: function() {
3317
- return cloudSetupPath;
3318
- }
3319
- });
3320
- Object.defineProperty(exports, "generateCloudSetup", {
3321
- enumerable: true,
3322
- get: function() {
3323
- return generateCloudSetup;
3324
- }
3325
- });
3326
2717
  Object.defineProperty(exports, "parseToolchainConfiguration", {
3327
2718
  enumerable: true,
3328
2719
  get: function() {
3329
2720
  return parseToolchainConfiguration;
3330
2721
  }
3331
2722
  });
3332
- Object.defineProperty(exports, "parseToolchainPolicy", {
3333
- enumerable: true,
3334
- get: function() {
3335
- return parseToolchainPolicy;
3336
- }
3337
- });
3338
2723
  Object.defineProperty(exports, "readToolchainInputs", {
3339
2724
  enumerable: true,
3340
2725
  get: function() {