@stll/oxlint-config 0.12.2 → 0.15.0

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 (88) hide show
  1. package/README.md +261 -1
  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/index.cjs +50 -40
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +46 -41
  19. package/dist/index.d.mts +46 -41
  20. package/dist/index.mjs +50 -41
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/publish-build-target-nuxt-helper.cjs +71 -0
  23. package/dist/publish-build-target-nuxt-helper.cjs.map +1 -0
  24. package/dist/publish-build-target-nuxt-helper.d.cts +10 -0
  25. package/dist/publish-build-target-nuxt-helper.d.mts +10 -0
  26. package/dist/publish-build-target-nuxt-helper.mjs +68 -0
  27. package/dist/publish-build-target-nuxt-helper.mjs.map +1 -0
  28. package/dist/publish-build-target-vite-dts-helper.cjs +3 -0
  29. package/dist/publish-build-target-vite-dts-helper.d.cts +24 -0
  30. package/dist/publish-build-target-vite-dts-helper.d.mts +24 -0
  31. package/dist/publish-build-target-vite-dts-helper.mjs +2 -0
  32. package/dist/publish-build-target-vite-dts-helper2.cjs +372 -0
  33. package/dist/publish-build-target-vite-dts-helper2.cjs.map +1 -0
  34. package/dist/publish-build-target-vite-dts-helper2.mjs +359 -0
  35. package/dist/publish-build-target-vite-dts-helper2.mjs.map +1 -0
  36. package/dist/publish-contract-cli.cjs +983 -0
  37. package/dist/publish-contract-cli.cjs.map +1 -0
  38. package/dist/publish-contract-cli.d.cts +1 -0
  39. package/dist/publish-contract-cli.d.mts +1 -0
  40. package/dist/publish-contract-cli.mjs +981 -0
  41. package/dist/publish-contract-cli.mjs.map +1 -0
  42. package/dist/publish-contract.cjs +431 -0
  43. package/dist/publish-contract.cjs.map +1 -0
  44. package/dist/publish-contract.mjs +396 -0
  45. package/dist/publish-contract.mjs.map +1 -0
  46. package/dist/rolldown-runtime.cjs +28 -0
  47. package/dist/toolchain-changed-cli.cjs +34 -0
  48. package/dist/toolchain-changed-cli.cjs.map +1 -0
  49. package/dist/toolchain-changed-cli.d.cts +1 -0
  50. package/dist/toolchain-changed-cli.d.mts +1 -0
  51. package/dist/toolchain-changed-cli.mjs +34 -0
  52. package/dist/toolchain-changed-cli.mjs.map +1 -0
  53. package/dist/toolchain-changed.cjs +1470 -0
  54. package/dist/toolchain-changed.cjs.map +1 -0
  55. package/dist/toolchain-changed.mjs +1463 -0
  56. package/dist/toolchain-changed.mjs.map +1 -0
  57. package/dist/toolchain-check-cli.cjs +2 -1
  58. package/dist/toolchain-check-cli.cjs.map +1 -1
  59. package/dist/toolchain-check-cli.mjs +2 -1
  60. package/dist/toolchain-check-cli.mjs.map +1 -1
  61. package/dist/toolchain-guard.cjs +360 -1161
  62. package/dist/toolchain-guard.cjs.map +1 -1
  63. package/dist/toolchain-guard.mjs +338 -1100
  64. package/dist/toolchain-guard.mjs.map +1 -1
  65. package/dist/toolchain-inputs.cjs +926 -0
  66. package/dist/toolchain-inputs.cjs.map +1 -0
  67. package/dist/toolchain-inputs.mjs +816 -0
  68. package/dist/toolchain-inputs.mjs.map +1 -0
  69. package/dist/toolchain-schema.cjs +702 -0
  70. package/dist/toolchain-schema.cjs.map +1 -0
  71. package/dist/toolchain-schema.mjs +580 -0
  72. package/dist/toolchain-schema.mjs.map +1 -0
  73. package/dist/typecheck-parity-cli.cjs +38 -898
  74. package/dist/typecheck-parity-cli.cjs.map +1 -1
  75. package/dist/typecheck-parity-cli.mjs +39 -899
  76. package/dist/typecheck-parity-cli.mjs.map +1 -1
  77. package/dist/typecheck-parity.cjs +905 -0
  78. package/dist/typecheck-parity.cjs.map +1 -0
  79. package/dist/typecheck-parity.mjs +894 -0
  80. package/dist/typecheck-parity.mjs.map +1 -0
  81. package/dist/typecheck-probe-cli.cjs +176 -0
  82. package/dist/typecheck-probe-cli.cjs.map +1 -0
  83. package/dist/typecheck-probe-cli.d.cts +1 -0
  84. package/dist/typecheck-probe-cli.d.mts +1 -0
  85. package/dist/typecheck-probe-cli.mjs +174 -0
  86. package/dist/typecheck-probe-cli.mjs.map +1 -0
  87. package/package.json +43 -5
  88. 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.15.0";
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$7 = (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$7(document)) return void 0;
720
- const services = document["services"];
721
- if (isComposeDefinitionPath(file) && record$7(services)) {
722
- const images = [];
723
- for (const [name, service] of Object.entries(services)) if (record$7(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$7(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$7(spec) ? spec[key] : void 0;
755
- if (!record$7(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$7(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,69 +260,9 @@ 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
- const record$6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
265
+ const record$5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
867
266
  const strings = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
868
267
  const sameStrings = (value, expected) => {
869
268
  if (!strings(value) || value.length !== expected.length) return false;
@@ -885,8 +284,8 @@ const workspaceTable = (parsed, ecosystem) => {
885
284
  case "cargo": return parsed["workspace"];
886
285
  case "uv": {
887
286
  const tool = parsed["tool"];
888
- const uv = record$6(tool) ? tool["uv"] : void 0;
889
- return record$6(uv) ? uv["workspace"] : void 0;
287
+ const uv = record$5(tool) ? tool["uv"] : void 0;
288
+ return record$5(uv) ? uv["workspace"] : void 0;
890
289
  }
891
290
  default: throw new Error("unknown workspace ecosystem", { cause: ecosystem });
892
291
  }
@@ -906,7 +305,7 @@ const workspaceRoots = (files, ecosystem) => {
906
305
  const document = (0, yaml.parseDocument)(text);
907
306
  if (document.errors.length > 0) throw new Error(`${workspaceFile}:1: invalid pnpm workspace YAML`);
908
307
  const parsed = document.toJS({ maxAliasCount: 100 });
909
- if (!record$6(parsed)) throw new Error(`${workspaceFile}:1: pnpm workspace must contain an object`);
308
+ if (!record$5(parsed)) throw new Error(`${workspaceFile}:1: pnpm workspace must contain an object`);
910
309
  const patterns = parsed["packages"] ?? [];
911
310
  if (!strings(patterns)) throw new Error(`${workspaceFile}:1: pnpm packages must be a string array`);
912
311
  workspaces.set(directory, patterns.map((pattern) => pattern.startsWith("!") ? `!${node_path.default.posix.normalize(pattern.slice(1))}` : node_path.default.posix.normalize(pattern)));
@@ -918,13 +317,13 @@ const workspaceRoots = (files, ecosystem) => {
918
317
  }
919
318
  try {
920
319
  const parsed = ecosystem === "npm" ? JSON.parse(files[file] ?? "") : (0, smol_toml.parse)(files[file] ?? "");
921
- if (!record$6(parsed)) continue;
320
+ if (!record$5(parsed)) continue;
922
321
  if (ecosystem === "npm" && typeof parsed["packageManager"] === "string" && parsed["packageManager"].startsWith("pnpm@")) continue;
923
322
  const value = workspaceTable(parsed, ecosystem);
924
- const patterns = record$6(value) ? value[ecosystem === "npm" ? "packages" : "members"] : value;
925
- const members = ecosystem === "cargo" && record$6(value) && patterns === void 0 ? [] : patterns;
323
+ const patterns = record$5(value) ? value[ecosystem === "npm" ? "packages" : "members"] : value;
324
+ const members = ecosystem === "cargo" && record$5(value) && patterns === void 0 ? [] : patterns;
926
325
  if (strings(members)) {
927
- const excluded = ecosystem !== "npm" && record$6(value) && strings(value["exclude"]) ? value["exclude"].map((pattern) => `!${pattern}`) : [];
326
+ const excluded = ecosystem !== "npm" && record$5(value) && strings(value["exclude"]) ? value["exclude"].map((pattern) => `!${pattern}`) : [];
928
327
  workspaces.set(node_path.default.posix.dirname(file), [...members, ...excluded]);
929
328
  }
930
329
  } catch {}
@@ -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) {
@@ -975,11 +374,11 @@ const workspaceRoots = (files, ecosystem) => {
975
374
  for (const directory of memberDirectories) try {
976
375
  const parsed = (0, smol_toml.parse)(files[node_path.default.posix.join(directory, "Cargo.toml")] ?? "");
977
376
  const workspace = (0, smol_toml.parse)(files[node_path.default.posix.join(root, "Cargo.toml")] ?? "")["workspace"];
978
- const inherited = record$6(workspace) ? workspace["dependencies"] : void 0;
377
+ const inherited = record$5(workspace) ? workspace["dependencies"] : void 0;
979
378
  const dependencyOwners = [parsed];
980
379
  const targets = parsed["target"];
981
- if (record$6(targets)) {
982
- for (const target of Object.values(targets)) if (record$6(target)) dependencyOwners.push(target);
380
+ if (record$5(targets)) {
381
+ for (const target of Object.values(targets)) if (record$5(target)) dependencyOwners.push(target);
983
382
  }
984
383
  for (const owner of dependencyOwners) for (const key of [
985
384
  "dependencies",
@@ -987,12 +386,12 @@ const workspaceRoots = (files, ecosystem) => {
987
386
  "build-dependencies"
988
387
  ]) {
989
388
  const dependencies = owner[key];
990
- if (!record$6(dependencies)) continue;
389
+ if (!record$5(dependencies)) continue;
991
390
  for (const [name, dependency] of Object.entries(dependencies)) {
992
- if (!record$6(dependency)) continue;
391
+ if (!record$5(dependency)) continue;
993
392
  const isInherited = dependency["workspace"] === true;
994
- const resolved = isInherited && record$6(inherited) ? inherited[name] : dependency;
995
- if (!record$6(resolved) || typeof resolved["path"] !== "string") continue;
393
+ const resolved = isInherited && record$5(inherited) ? inherited[name] : dependency;
394
+ if (!record$5(resolved) || typeof resolved["path"] !== "string") continue;
996
395
  if (node_path.default.posix.isAbsolute(resolved["path"]) || /\\|\$|^[A-Za-z]:/.test(resolved["path"])) continue;
997
396
  const target = node_path.default.posix.normalize(node_path.default.posix.join(isInherited ? root : directory, resolved["path"]));
998
397
  const relative = node_path.default.posix.relative(root, target);
@@ -1020,19 +419,19 @@ const javascriptEcosystem = (directory, files) => {
1020
419
  if (files[node_path.default.posix.join(root, "bun.lock")] !== void 0) return "bun";
1021
420
  try {
1022
421
  const manifest = JSON.parse(files[node_path.default.posix.join(root, "package.json")] ?? "{}");
1023
- if (record$6(manifest) && typeof manifest["packageManager"] === "string" && manifest["packageManager"].startsWith("bun@")) return "bun";
422
+ if (record$5(manifest) && typeof manifest["packageManager"] === "string" && manifest["packageManager"].startsWith("bun@")) return "bun";
1024
423
  } catch {}
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";
1032
431
  const pyproject = files[node_path.default.posix.join(root, "pyproject.toml")];
1033
432
  if (pyproject !== void 0) try {
1034
433
  const tool = (0, smol_toml.parse)(pyproject)["tool"];
1035
- if (record$6(tool) && record$6(tool["uv"])) return "uv";
434
+ if (record$5(tool) && record$5(tool["uv"])) return "uv";
1036
435
  } catch {}
1037
436
  return "pip";
1038
437
  };
@@ -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
  };
@@ -1103,7 +502,7 @@ const checkDependabot = ({ files, policy }) => {
1103
502
  add("cannot resolve Dependabot YAML aliases");
1104
503
  return diagnostics;
1105
504
  }
1106
- if (!record$6(parsed) || parsed["version"] !== 2 || !Array.isArray(parsed["updates"])) {
505
+ if (!record$5(parsed) || parsed["version"] !== 2 || !Array.isArray(parsed["updates"])) {
1107
506
  add("Dependabot must declare version: 2 and an updates array");
1108
507
  return diagnostics;
1109
508
  }
@@ -1116,7 +515,7 @@ const checkDependabot = ({ files, policy }) => {
1116
515
  });
1117
516
  if (legacyDocument.errors.length === 0) legacy = legacyDocument.toJS({ maxAliasCount: 100 });
1118
517
  } catch {}
1119
- const legacyUpdates = record$6(legacy) && Array.isArray(legacy["updates"]) ? legacy["updates"] : [];
518
+ const legacyUpdates = record$5(legacy) && Array.isArray(legacy["updates"]) ? legacy["updates"] : [];
1120
519
  const rootClaims = /* @__PURE__ */ new Map();
1121
520
  const directoryClaims = /* @__PURE__ */ new Map();
1122
521
  for (const [index, update] of parsed["updates"].entries()) {
@@ -1130,7 +529,7 @@ const checkDependabot = ({ files, policy }) => {
1130
529
  return offset === void 0 ? 1 : lineCounter.linePos(offset).line;
1131
530
  };
1132
531
  const report = (key, message) => add(message, lineOf(key));
1133
- if (!record$6(update) || typeof update["package-ecosystem"] !== "string") {
532
+ if (!record$5(update) || typeof update["package-ecosystem"] !== "string") {
1134
533
  report("package-ecosystem", "each Dependabot update must declare a package ecosystem");
1135
534
  continue;
1136
535
  }
@@ -1164,21 +563,21 @@ const checkDependabot = ({ files, policy }) => {
1164
563
  covered.set(ecosystem, matches);
1165
564
  const schedule = update["schedule"];
1166
565
  const legacyUpdate = legacyUpdates.at(index);
1167
- const legacySchedule = record$6(legacyUpdate) ? legacyUpdate["schedule"] : void 0;
1168
- if (record$6(schedule) && schedule["time"] === policy.schedule.time && (!record$6(legacySchedule) || legacySchedule["time"] !== policy.schedule.time)) report("schedule", `${ecosystem}: quote schedule time to preserve its string value in YAML 1.1`);
1169
- if (!record$6(schedule) || Object.keys(schedule).length !== Object.keys(policy.schedule).length || Object.entries(policy.schedule).some(([key, value]) => schedule[key] !== value)) report("schedule", `${ecosystem}: schedule must match the shared policy`);
566
+ const legacySchedule = record$5(legacyUpdate) ? legacyUpdate["schedule"] : void 0;
567
+ if (record$5(schedule) && schedule["time"] === policy.schedule.time && (!record$5(legacySchedule) || legacySchedule["time"] !== policy.schedule.time)) report("schedule", `${ecosystem}: quote schedule time to preserve its string value in YAML 1.1`);
568
+ if (!record$5(schedule) || Object.keys(schedule).length !== Object.keys(policy.schedule).length || Object.entries(policy.schedule).some(([key, value]) => schedule[key] !== value)) report("schedule", `${ecosystem}: schedule must match the shared policy`);
1170
569
  const cooldown = update["cooldown"];
1171
- if (!record$6(cooldown) || Object.keys(cooldown).length !== 1 || cooldown["default-days"] !== policy.cooldown.defaultDays) report("cooldown", `${ecosystem}: cooldown must contain only default-days: ${policy.cooldown.defaultDays}`);
570
+ if (!record$5(cooldown) || Object.keys(cooldown).length !== 1 || cooldown["default-days"] !== policy.cooldown.defaultDays) report("cooldown", `${ecosystem}: cooldown must contain only default-days: ${policy.cooldown.defaultDays}`);
1172
571
  const groups = update["groups"];
1173
- if (!record$6(groups) || Object.keys(groups).length !== Object.keys(policy.groups).length || Object.entries(policy.groups).some(([name, expected]) => {
572
+ if (!record$5(groups) || Object.keys(groups).length !== Object.keys(policy.groups).length || Object.entries(policy.groups).some(([name, expected]) => {
1174
573
  const group = groups[name];
1175
- return !record$6(group) || Object.keys(group).length !== 2 || !sameStrings(group["patterns"], expected.patterns) || !sameStrings(group["update-types"], expected.updateTypes);
574
+ return !record$5(group) || Object.keys(group).length !== 2 || !sameStrings(group["patterns"], expected.patterns) || !sameStrings(group["update-types"], expected.updateTypes);
1176
575
  })) report("groups", `${ecosystem}: groups must match the shared policy`);
1177
576
  const ignored = ignoredFor(ecosystem, policy);
1178
577
  const ignore = update["ignore"];
1179
578
  if (ignore !== void 0 && (!Array.isArray(ignore) || ignore.length === 0)) report("ignore", `${ecosystem}: omit ignore or provide a nonempty array`);
1180
- if (Array.isArray(ignore) && ignore.some((entry) => !record$6(entry) || typeof entry["dependency-name"] !== "string" || !ignored.includes(entry["dependency-name"]))) report("ignore", `${ecosystem}: ignore only packages owned by the shared policy`);
1181
- for (const name of ignored) if (!Array.isArray(ignore) || !ignore.some((entry) => record$6(entry) && entry["dependency-name"] === name && Object.keys(entry).length === 1)) report("ignore", `${ecosystem}: ignore all updates for shared pin ${name}`);
579
+ if (Array.isArray(ignore) && ignore.some((entry) => !record$5(entry) || typeof entry["dependency-name"] !== "string" || !ignored.includes(entry["dependency-name"]))) report("ignore", `${ecosystem}: ignore only packages owned by the shared policy`);
580
+ for (const name of ignored) if (!Array.isArray(ignore) || !ignore.some((entry) => record$5(entry) && entry["dependency-name"] === name && Object.keys(entry).length === 1)) report("ignore", `${ecosystem}: ignore all updates for shared pin ${name}`);
1182
581
  }
1183
582
  for (const [ecosystem, directories] of roots) for (const directory of directories) if (!covered.get(ecosystem)?.has(directory)) add(`add a ${ecosystem} update entry covering ${directory}`);
1184
583
  return diagnostics;
@@ -1190,7 +589,7 @@ const locatorValidators = {
1190
589
  "bun-source": /^(?:jobs\.[\w-]+|runs)\.steps\.[\w-]+$/
1191
590
  };
1192
591
  const knownKind = (value) => typeof value === "string" && Object.hasOwn(locatorValidators, value);
1193
- const record$5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
592
+ const record$4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1194
593
  /** One reviewed declaration identifies one unresolved selector, never a rule. */
1195
594
  const parseDynamicSelectors = (input) => {
1196
595
  if (input === void 0) return [];
@@ -1198,7 +597,7 @@ const parseDynamicSelectors = (input) => {
1198
597
  const result = [];
1199
598
  const keys = /* @__PURE__ */ new Set();
1200
599
  for (const entry of input) {
1201
- if (!record$5(entry) || Object.keys(entry).some((key) => ![
600
+ if (!record$4(entry) || Object.keys(entry).some((key) => ![
1202
601
  "path",
1203
602
  "kind",
1204
603
  "reason",
@@ -1234,7 +633,7 @@ const parseDynamicSelectors = (input) => {
1234
633
  };
1235
634
  //#endregion
1236
635
  //#region src/toolchain-engine-floors.ts
1237
- const record$4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
636
+ const record$3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1238
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 !== "..");
1239
638
  const parseEngineFloors = (input) => {
1240
639
  if (input === void 0) return [];
@@ -1242,7 +641,7 @@ const parseEngineFloors = (input) => {
1242
641
  const result = [];
1243
642
  const jobs = /* @__PURE__ */ new Set();
1244
643
  for (const entry of input) {
1245
- if (!record$4(entry) || Object.keys(entry).some((key) => ![
644
+ if (!record$3(entry) || Object.keys(entry).some((key) => ![
1246
645
  "package",
1247
646
  "workflow",
1248
647
  "job"
@@ -1263,16 +662,16 @@ const resolveEngineFloor = (entry, files) => {
1263
662
  const workflowText = files[entry.workflow];
1264
663
  if (manifestText === void 0 || workflowText === void 0) throw new Error("engine floor package and workflow must be tracked");
1265
664
  const manifest = JSON.parse(manifestText);
1266
- 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");
1267
666
  const range = manifest["engines"]["node"];
1268
667
  const minimum = (0, semver.validRange)(range) === null ? null : (0, semver.minVersion)(range);
1269
668
  if (minimum === null || minimum.prerelease.length !== 0) throw new Error("engine floor engines.node must declare a stable minimum");
1270
669
  const document = (0, yaml.parseDocument)(workflowText, { merge: true });
1271
670
  if (document.errors.length !== 0) throw new Error("engine floor workflow must be valid YAML");
1272
671
  const workflow = document.toJS({ maxAliasCount: 100 });
1273
- 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");
1274
673
  const job = workflow["jobs"][entry.job];
1275
- 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");
1276
675
  if (!Array.isArray(job["steps"]) || Object.hasOwn(job, "uses")) throw new Error("engine floor job must declare steps and cannot reuse a workflow");
1277
676
  return {
1278
677
  ...entry,
@@ -1292,7 +691,7 @@ const packageRules = [
1292
691
  "typescript-layout",
1293
692
  "node-engine"
1294
693
  ];
1295
- 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);
1296
695
  const shellSubstitutions = (command) => {
1297
696
  const frames = [{
1298
697
  kind: "root",
@@ -1357,7 +756,7 @@ const compilerCommandSegments = (command) => {
1357
756
  const segments = [[]];
1358
757
  for (const source of [command, ...shellSubstitutions(command)]) {
1359
758
  segments.push([]);
1360
- const shellWords = source.match(/(?:[^\s"'\\;&|()]|\\[^\n]|"(?:[^"\\]|\\.)*"|'[^']*')+|&&|\|\||[();&|\n]/g);
759
+ const shellWords = source.match(/(?:[^\s"'\\;&|()]|\\[\s\S]|"(?:[^"\\]|\\[\s\S])*"|'[^']*')+|&&|\|\||[();&|\n]/g);
1361
760
  for (const word of shellWords ?? []) if (/^(?:&&|\|\||[;&|\n])$/.test(word)) segments.push([]);
1362
761
  else segments.at(-1)?.push(word);
1363
762
  }
@@ -1444,29 +843,29 @@ const launcherValueOptions = new Map(Object.entries({
1444
843
  "--input-type"
1445
844
  ]
1446
845
  }).map(([launcher, options]) => [launcher, new Set(options)]));
1447
- const invokesCompiler = (words) => {
1448
- const literal = (word) => {
1449
- let result = "";
1450
- let quote;
1451
- for (let index = 0; index < word.length; index += 1) {
1452
- const character = word.at(index);
1453
- if (character === quote) {
1454
- quote = void 0;
1455
- continue;
1456
- }
1457
- if (quote === void 0 && (character === "'" || character === "\"")) {
1458
- quote = character;
1459
- continue;
1460
- }
1461
- const next = word.at(index + 1);
1462
- if (character === "\\" && next !== void 0 && quote !== "'" && (quote === void 0 || /[$`"\\\n]/.test(next))) {
1463
- result += next;
1464
- index += 1;
1465
- } 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;
1466
854
  }
1467
- return result;
1468
- };
1469
- 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);
1470
869
  const compiler = (word) => /(?:^|\/)(?:tsc|tsgo)(?:\.js)?$/.test(word);
1471
870
  let start = 0;
1472
871
  const skipOptions = (index, launcher) => {
@@ -1523,10 +922,10 @@ const checkPackageFiles = ({ files, policy }) => {
1523
922
  const document = pnpm ? (0, yaml.parseDocument)(text, { lineCounter }) : void 0;
1524
923
  if (document !== void 0 && document.errors.length > 0) throw new Error("invalid YAML");
1525
924
  const json = document === void 0 ? JSON.parse(text) : document.toJS();
1526
- 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");
1527
926
  if (pnpm) {
1528
- if (json["catalog"] !== void 0 && !record$3(json["catalog"])) throw new Error("catalog must be a mapping");
1529
- 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");
1530
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");
1531
930
  }
1532
931
  manifests.set(file, json);
@@ -1540,13 +939,13 @@ const checkPackageFiles = ({ files, policy }) => {
1540
939
  catalog: json["overrides"],
1541
940
  prefix: ["overrides"]
1542
941
  },
1543
- ...record$3(json["catalogs"]) ? Object.entries(json["catalogs"]).map(([name, catalog]) => ({
942
+ ...record$2(json["catalogs"]) ? Object.entries(json["catalogs"]).map(([name, catalog]) => ({
1544
943
  catalog,
1545
944
  prefix: ["catalogs", name]
1546
945
  })) : []
1547
946
  ];
1548
947
  for (const { catalog, prefix } of catalogEntries) {
1549
- if (!record$3(catalog)) continue;
948
+ if (!record$2(catalog)) continue;
1550
949
  for (const [name, value] of Object.entries(catalog)) {
1551
950
  const node = document.getIn([...prefix, name], true);
1552
951
  if (!(0, yaml.isNode)(node) || node.range === void 0 || node.range === null) continue;
@@ -1565,7 +964,7 @@ const checkPackageFiles = ({ files, policy }) => {
1565
964
  });
1566
965
  }
1567
966
  }
1568
- const catalogSources = (json) => [json, ...record$3(json["workspaces"]) ? [json["workspaces"]] : []];
967
+ const catalogSources = (json) => [json, ...record$2(json["workspaces"]) ? [json["workspaces"]] : []];
1569
968
  const pnpmWorkspaceFor = (file) => {
1570
969
  let directory = node_path.default.posix.dirname(file);
1571
970
  while (true) {
@@ -1582,14 +981,14 @@ const checkPackageFiles = ({ files, policy }) => {
1582
981
  const owner = pnpmWorkspaceFor(file);
1583
982
  if (owner !== void 0) {
1584
983
  const { directory, workspace } = owner;
1585
- if (node_path.default.posix.basename(file) === "package.json" && !workspaceContains({
984
+ if (node_path.default.posix.basename(file) === "package.json" && !require_toolchain_inputs.workspaceContains({
1586
985
  directory,
1587
986
  file,
1588
987
  patterns: workspace["packages"],
1589
988
  rootMembership: "implicit"
1590
989
  })) return void 0;
1591
- if (name === "" || name === "default") return workspace["catalog"] ?? (record$3(workspace["catalogs"]) ? workspace["catalogs"]["default"] : void 0);
1592
- 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;
1593
992
  }
1594
993
  let directory = node_path.default.posix.dirname(file);
1595
994
  while (true) {
@@ -1597,7 +996,7 @@ const checkPackageFiles = ({ files, policy }) => {
1597
996
  if (manifest !== void 0) {
1598
997
  for (const source of catalogSources(manifest)) {
1599
998
  if (name === "" && source["catalog"] !== void 0) return source["catalog"];
1600
- 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];
1601
1000
  }
1602
1001
  if (manifest["workspaces"] !== void 0) return void 0;
1603
1002
  }
@@ -1611,7 +1010,7 @@ const checkPackageFiles = ({ files, policy }) => {
1611
1010
  if (visited.has(value)) return void 0;
1612
1011
  visited.add(value);
1613
1012
  const catalog = catalogFor(file, value.slice(8));
1614
- value = record$3(catalog) ? catalog[name] : void 0;
1013
+ value = record$2(catalog) ? catalog[name] : void 0;
1615
1014
  }
1616
1015
  return value;
1617
1016
  };
@@ -1627,7 +1026,7 @@ const checkPackageFiles = ({ files, policy }) => {
1627
1026
  const workspaces = manifests.get(node_path.default.posix.join(directory, "package.json"))?.["workspaces"];
1628
1027
  if (workspaces !== void 0) return {
1629
1028
  directory,
1630
- patterns: record$3(workspaces) ? workspaces["packages"] : workspaces,
1029
+ patterns: record$2(workspaces) ? workspaces["packages"] : workspaces,
1631
1030
  source: "json"
1632
1031
  };
1633
1032
  if (directory === ".") return void 0;
@@ -1639,13 +1038,13 @@ const checkPackageFiles = ({ files, policy }) => {
1639
1038
  if (owner === void 0) return "nonmember";
1640
1039
  const { directory, patterns, source } = owner;
1641
1040
  if (source === "json" && !Array.isArray(patterns)) return "mismatch";
1642
- if (!workspaceContains({
1041
+ if (!require_toolchain_inputs.workspaceContains({
1643
1042
  directory,
1644
1043
  file,
1645
1044
  patterns,
1646
1045
  rootMembership: "implicit"
1647
1046
  })) return "nonmember";
1648
- 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({
1649
1048
  directory,
1650
1049
  file: candidate,
1651
1050
  patterns,
@@ -1683,7 +1082,7 @@ const checkPackageFiles = ({ files, policy }) => {
1683
1082
  "peerDependencies"
1684
1083
  ]) {
1685
1084
  const dependencies = json[section];
1686
- if (!record$3(dependencies)) continue;
1085
+ if (!record$2(dependencies)) continue;
1687
1086
  for (const [name, value] of Object.entries(dependencies)) {
1688
1087
  if (!tsSpecifiers.has(name)) continue;
1689
1088
  const resolved = resolve(file, name, value);
@@ -1709,7 +1108,7 @@ const checkPackageFiles = ({ files, policy }) => {
1709
1108
  const workspaceLayouts = /* @__PURE__ */ new Map();
1710
1109
  const workspaceLayoutFor = (file) => {
1711
1110
  const owner = workspaceOwnerFor(file);
1712
- if (owner === void 0 || !workspaceContains({
1111
+ if (owner === void 0 || !require_toolchain_inputs.workspaceContains({
1713
1112
  ...owner,
1714
1113
  file,
1715
1114
  rootMembership: "implicit"
@@ -1721,7 +1120,7 @@ const checkPackageFiles = ({ files, policy }) => {
1721
1120
  for (const [candidate, member] of manifests) {
1722
1121
  if (node_path.default.posix.basename(candidate) !== "package.json") continue;
1723
1122
  const memberOwner = workspaceOwnerFor(candidate);
1724
- if (memberOwner?.directory !== owner.directory || memberOwner.source !== owner.source || !workspaceContains({
1123
+ if (memberOwner?.directory !== owner.directory || memberOwner.source !== owner.source || !require_toolchain_inputs.workspaceContains({
1725
1124
  ...owner,
1726
1125
  file: candidate,
1727
1126
  rootMembership: "implicit"
@@ -1745,7 +1144,7 @@ const checkPackageFiles = ({ files, policy }) => {
1745
1144
  message: `packageManager Bun version must be ${policy.bun}, found ${JSON.stringify(manager)}`
1746
1145
  });
1747
1146
  const checkEntries = (entries, location) => {
1748
- if (!record$3(entries)) return;
1147
+ if (!record$2(entries)) return;
1749
1148
  for (const [name, raw] of Object.entries(entries)) {
1750
1149
  const value = resolve(file, name, raw);
1751
1150
  const tsAllowed = tsSpecifiers.get(name);
@@ -1782,12 +1181,12 @@ const checkPackageFiles = ({ files, policy }) => {
1782
1181
  "devDependencies",
1783
1182
  "optionalDependencies"
1784
1183
  ]) checkEntries(json[key]);
1785
- if (record$3(json["peerDependencies"])) {
1184
+ if (record$2(json["peerDependencies"])) {
1786
1185
  for (const [name, value] of Object.entries(json["peerDependencies"])) if (tsSpecifiers.has(name) && !peerRange(resolve(file, name, value))) checkEntries({ [name]: value });
1787
1186
  }
1788
1187
  for (const source of catalogSources(json)) {
1789
1188
  checkEntries(source["catalog"]);
1790
- if (!record$3(source["catalogs"])) continue;
1189
+ if (!record$2(source["catalogs"])) continue;
1791
1190
  for (const catalog of Object.values(source["catalogs"])) checkEntries(catalog);
1792
1191
  }
1793
1192
  const ownedNames = /* @__PURE__ */ new Set([...pins.keys(), ...tsSpecifiers.keys()]);
@@ -1799,7 +1198,7 @@ const checkPackageFiles = ({ files, policy }) => {
1799
1198
  "optionalDependencies"
1800
1199
  ]) {
1801
1200
  const entries = resolutionRoot[section];
1802
- if (!record$3(entries)) continue;
1201
+ if (!record$2(entries)) continue;
1803
1202
  for (const [name, value] of Object.entries(entries)) resolutionDependencies[name] = resolve(file, name, value);
1804
1203
  }
1805
1204
  const affectedSpecifiers = /* @__PURE__ */ new Map();
@@ -1807,7 +1206,7 @@ const checkPackageFiles = ({ files, policy }) => {
1807
1206
  for (const [candidate, member] of manifests) {
1808
1207
  if (node_path.default.posix.basename(candidate) !== "package.json") continue;
1809
1208
  const memberOwner = workspaceOwnerFor(candidate);
1810
- 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({
1811
1210
  directory: owner.directory,
1812
1211
  file: candidate,
1813
1212
  patterns: owner.patterns,
@@ -1819,7 +1218,7 @@ const checkPackageFiles = ({ files, policy }) => {
1819
1218
  "optionalDependencies"
1820
1219
  ]) {
1821
1220
  const entries = member[section];
1822
- if (!record$3(entries)) continue;
1221
+ if (!record$2(entries)) continue;
1823
1222
  for (const [name, value] of Object.entries(entries)) {
1824
1223
  if (!tsSpecifiers.has(name)) continue;
1825
1224
  const values = affectedSpecifiers.get(name) ?? /* @__PURE__ */ new Set();
@@ -1829,7 +1228,7 @@ const checkPackageFiles = ({ files, policy }) => {
1829
1228
  }
1830
1229
  }
1831
1230
  const checkResolutionMap = (entries, parent) => {
1832
- if (!record$3(entries)) return;
1231
+ if (!record$2(entries)) return;
1833
1232
  for (const [selector, replacement] of Object.entries(entries)) {
1834
1233
  const target = selector === "." ? parent : selector;
1835
1234
  if (target === void 0) continue;
@@ -1852,7 +1251,7 @@ const checkPackageFiles = ({ files, policy }) => {
1852
1251
  });
1853
1252
  continue;
1854
1253
  }
1855
- if (record$3(replacement)) {
1254
+ if (record$2(replacement)) {
1856
1255
  checkResolutionMap(replacement, target);
1857
1256
  continue;
1858
1257
  }
@@ -1886,7 +1285,7 @@ const checkPackageFiles = ({ files, policy }) => {
1886
1285
  };
1887
1286
  checkResolutionMap(json["overrides"]);
1888
1287
  checkResolutionMap(json["resolutions"]);
1889
- if (record$3(json["pnpm"])) checkResolutionMap(json["pnpm"]["overrides"]);
1288
+ if (record$2(json["pnpm"])) checkResolutionMap(json["pnpm"]["overrides"]);
1890
1289
  const localEntries = toolchainEntries(file, json);
1891
1290
  const usesCompiler = policy.typescriptInstallLayouts.some((layout) => localEntries.some(({ name }) => name === layout.compilerPackage));
1892
1291
  let selectedLayout = layoutFor(specifierMap(localEntries));
@@ -1910,7 +1309,7 @@ const checkPackageFiles = ({ files, policy }) => {
1910
1309
  key: "dependencies",
1911
1310
  message: "TypeScript compiler and compatibility packages must match one complete declared install layout"
1912
1311
  });
1913
- const command = record$3(json["scripts"]) ? json["scripts"]["typecheck"] : void 0;
1312
+ const command = record$2(json["scripts"]) ? json["scripts"]["typecheck"] : void 0;
1914
1313
  if (selectedLayout !== void 0 && typeof command === "string") {
1915
1314
  const normalizedExpected = selectedLayout.typecheckCommand.replace(/\s+/g, " ").trim();
1916
1315
  for (const words of compilerCommandSegments(command)) {
@@ -1928,7 +1327,7 @@ const checkPackageFiles = ({ files, policy }) => {
1928
1327
  }
1929
1328
  }
1930
1329
  }
1931
- if (record$3(json["peerDependencies"])) {
1330
+ if (record$2(json["peerDependencies"])) {
1932
1331
  const peerLayout = selectedLayout ?? workspaceLayoutFor(file)?.layout;
1933
1332
  for (const [name, raw] of Object.entries(json["peerDependencies"])) {
1934
1333
  if (!tsSpecifiers.has(name)) continue;
@@ -1952,7 +1351,7 @@ const checkPackageFiles = ({ files, policy }) => {
1952
1351
  });
1953
1352
  }
1954
1353
  }
1955
- 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({
1956
1355
  file,
1957
1356
  rule: "node-engine",
1958
1357
  key: "node",
@@ -1974,7 +1373,7 @@ const runtimeRules = [
1974
1373
  "runtime-workflow",
1975
1374
  "action-pins"
1976
1375
  ];
1977
- 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);
1978
1377
  const pythonSelectorMatches = (value, expected) => value === expected || typeof value === "string" && /^\d+\.\d+$/.test(expected) && value.startsWith(`${expected}.`) && /^\d+\.\d+\.\d+$/.test(value);
1979
1378
  const compareRelease = (left, right) => {
1980
1379
  for (let index = 0; index < Math.max(left.length, right.length); index++) {
@@ -2069,7 +1468,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2069
1468
  const lineOf = (key) => Math.max(1, lines.findIndex((line) => line.includes(key)) + 1);
2070
1469
  const pin = ({ rule, value, expected, label, line = 1, selector }) => {
2071
1470
  let matches = value === expected;
2072
- if (selector === "node") matches = nodeSelectorMatches(value, expected);
1471
+ if (selector === "node") matches = require_toolchain_inputs.nodeSelectorMatches(value, expected);
2073
1472
  else if (label.includes("python")) matches = pythonSelectorMatches(value, expected);
2074
1473
  if (!matches) add({
2075
1474
  rule,
@@ -2138,11 +1537,11 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2138
1537
  const parsed = toml("rust-version");
2139
1538
  if (parsed !== void 0) {
2140
1539
  const toolchain = parsed["toolchain"];
2141
- const components = record$2(toolchain) ? toolchain["components"] : void 0;
1540
+ const components = record$1(toolchain) ? toolchain["components"] : void 0;
2142
1541
  const expected = Array.isArray(components) && components.includes("rustc-dev") ? policy.rustCompilerDevelopment : policy.rust;
2143
1542
  pin({
2144
1543
  rule: "rust-version",
2145
- value: record$2(toolchain) ? toolchain["channel"] : void 0,
1544
+ value: record$1(toolchain) ? toolchain["channel"] : void 0,
2146
1545
  expected,
2147
1546
  label: "Rust channel",
2148
1547
  line: lineOf("channel")
@@ -2170,14 +1569,14 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2170
1569
  };
2171
1570
  if (parsed !== void 0) {
2172
1571
  const workspace = parsed["workspace"];
2173
- const defaults = record$2(workspace) ? workspace["package"] : void 0;
2174
- 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"]);
2175
1574
  const pkg = parsed["package"];
2176
- const version = record$2(pkg) ? pkg["rust-version"] : void 0;
2177
- 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);
2178
1577
  else {
2179
1578
  let inherited;
2180
- if (version["workspace"] === true && record$2(pkg)) {
1579
+ if (version["workspace"] === true && record$1(pkg)) {
2181
1580
  const explicit = pkg["workspace"];
2182
1581
  let directory = node_path.default.posix.dirname(file);
2183
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;
@@ -2185,9 +1584,9 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2185
1584
  if (explicitPath !== void 0) directory = explicitPath;
2186
1585
  while (true) {
2187
1586
  const table = readManifest(node_path.default.posix.join(directory, "Cargo.toml"))?.["workspace"];
2188
- if (record$2(table)) {
1587
+ if (record$1(table)) {
2189
1588
  const fields = table["package"];
2190
- inherited = record$2(fields) ? fields["rust-version"] : void 0;
1589
+ inherited = record$1(fields) ? fields["rust-version"] : void 0;
2191
1590
  break;
2192
1591
  }
2193
1592
  if (explicit !== void 0 || directory === ".") break;
@@ -2201,7 +1600,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2201
1600
  }
2202
1601
  if (name === "Pipfile") {
2203
1602
  const requires = toml("python-version")?.["requires"];
2204
- if (record$2(requires)) {
1603
+ if (record$1(requires)) {
2205
1604
  const minor = requires["python_version"];
2206
1605
  if (minor !== void 0 && minor !== policy.python.split(".").slice(0, 2).join(".")) add({
2207
1606
  rule: "python-version",
@@ -2220,7 +1619,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2220
1619
  const parsed = toml("python-version");
2221
1620
  if (parsed !== void 0) {
2222
1621
  const project = parsed["project"];
2223
- if (record$2(project) && project["requires-python"] !== void 0) {
1622
+ if (record$1(project) && project["requires-python"] !== void 0) {
2224
1623
  if (!pythonRequirementMatches(project["requires-python"], policy.python)) add({
2225
1624
  rule: "python-version",
2226
1625
  line: lineOf("requires-python"),
@@ -2228,9 +1627,9 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2228
1627
  });
2229
1628
  }
2230
1629
  const tool = parsed["tool"];
2231
- const configuredUv = record$2(tool) ? tool["uv"] : void 0;
1630
+ const configuredUv = record$1(tool) ? tool["uv"] : void 0;
2232
1631
  const uv = name === "uv.toml" ? parsed : configuredUv;
2233
- if (record$2(uv) && uv["python"] !== void 0) pin({
1632
+ if (record$1(uv) && uv["python"] !== void 0) pin({
2234
1633
  rule: "python-version",
2235
1634
  value: uv["python"],
2236
1635
  expected: policy.python,
@@ -2239,13 +1638,13 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2239
1638
  });
2240
1639
  }
2241
1640
  }
2242
- if (isMiseConfigPath(file)) {
1641
+ if (require_toolchain_inputs.isMiseConfigPath(file)) {
2243
1642
  const tools = toml("runtime-manager")?.["tools"];
2244
- 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)) {
2245
1644
  const configured = tools[tool];
2246
1645
  if (configured !== void 0) pin({
2247
1646
  rule: "runtime-manager",
2248
- value: record$2(configured) ? configured["version"] : configured,
1647
+ value: record$1(configured) ? configured["version"] : configured,
2249
1648
  expected,
2250
1649
  selector: nodeTools.has(tool) ? "node" : void 0,
2251
1650
  label: `mise ${tool}`,
@@ -2343,7 +1742,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2343
1742
  line
2344
1743
  });
2345
1744
  };
2346
- if (isDockerDefinitionPath(file)) {
1745
+ if (require_toolchain_inputs.isDockerDefinitionPath(file)) {
2347
1746
  const variables = /* @__PURE__ */ new Map();
2348
1747
  const escape = /^\s*#\s*escape\s*=\s*([\\`])\s*$/m.exec(text)?.[1] ?? "\\";
2349
1748
  const continuation = new RegExp(`${escape === "`" ? "`" : "\\\\"}\\s*$`);
@@ -2476,7 +1875,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2476
1875
  message: `unterminated Docker heredoc ${heredoc.delimiter}`
2477
1876
  });
2478
1877
  }
2479
- if (isComposeDefinitionPath(file) || isKubernetesDefinitionPath(file)) {
1878
+ if (require_toolchain_inputs.isComposeDefinitionPath(file) || require_toolchain_inputs.isKubernetesDefinitionPath(file)) {
2480
1879
  const lineCounter = new yaml.LineCounter();
2481
1880
  for (const containerDocument of (0, yaml.parseAllDocuments)(text, {
2482
1881
  lineCounter,
@@ -2485,7 +1884,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2485
1884
  })) {
2486
1885
  const contents = containerDocument.contents;
2487
1886
  const declaredResource = (0, yaml.isMap)(contents) && (0, yaml.isScalar)(contents.get("apiVersion", true)) && (0, yaml.isScalar)(contents.get("kind", true));
2488
- const recognizedContainer = isComposeDefinitionPath(file) || declaredResource;
1887
+ const recognizedContainer = require_toolchain_inputs.isComposeDefinitionPath(file) || declaredResource;
2489
1888
  if (containerDocument.errors.length > 0) {
2490
1889
  if (recognizedContainer) add({
2491
1890
  rule: "runtime-docker",
@@ -2495,7 +1894,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2495
1894
  continue;
2496
1895
  }
2497
1896
  try {
2498
- const classified = containerDocumentImages(containerDocument.toJS({ maxAliasCount: 100 }), file);
1897
+ const classified = require_toolchain_inputs.containerDocumentImages(containerDocument.toJS({ maxAliasCount: 100 }), file);
2499
1898
  for (const entry of classified?.images ?? []) {
2500
1899
  const node = containerDocument.getIn(entry.path, true);
2501
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;
@@ -2515,7 +1914,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2515
1914
  }
2516
1915
  }
2517
1916
  }
2518
- const automationKind = githubAutomationFileKind(file);
1917
+ const automationKind = require_toolchain_inputs.githubAutomationFileKind(file);
2519
1918
  if (automationKind === void 0) return diagnostics;
2520
1919
  const document = (0, yaml.parseDocument)(text, {
2521
1920
  uniqueKeys: true,
@@ -2530,7 +1929,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2530
1929
  return diagnostics;
2531
1930
  }
2532
1931
  const nodeLine = (node) => {
2533
- if (!record$2(node) || !Array.isArray(node["range"])) return 1;
1932
+ if (!record$1(node) || !Array.isArray(node["range"])) return 1;
2534
1933
  const offset = node["range"].at(0);
2535
1934
  return typeof offset === "number" ? text.slice(0, offset).split("\n").length : 1;
2536
1935
  };
@@ -2644,7 +2043,7 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
2644
2043
  const manifest = JSON.parse(content ?? "");
2645
2044
  pin({
2646
2045
  rule,
2647
- value: record$2(manifest) ? manifest["packageManager"] : void 0,
2046
+ value: record$1(manifest) ? manifest["packageManager"] : void 0,
2648
2047
  expected: `bun@${policy.bun}`,
2649
2048
  label: "setup-bun version file packageManager",
2650
2049
  line
@@ -3123,192 +2522,6 @@ const checkRuntimeFile = ({ file, text, policy, trackedFiles, readFile, reposito
3123
2522
  return diagnostics;
3124
2523
  };
3125
2524
  //#endregion
3126
- //#region src/toolchain-schema.ts
3127
- const packagePinKeys = [
3128
- "oxlint",
3129
- "oxlint-tsgolint",
3130
- "@oxlint/plugins",
3131
- "@stll/oxlint-plugin",
3132
- "oxfmt",
3133
- "lefthook"
3134
- ];
3135
- const valkeyArchitectures = ["amd64", "arm64"];
3136
- const valkeyArtifactPackages = {
3137
- server: "valkey-server",
3138
- tools: "valkey-tools"
3139
- };
3140
- const record$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3141
- const artifactRecord = (value, keys) => {
3142
- 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");
3143
- return value;
3144
- };
3145
- const parseValkeyArtifacts = (input, version) => {
3146
- const architectures = artifactRecord(input, valkeyArchitectures);
3147
- const line = version.split(".").slice(0, 2).join(".");
3148
- const parseArchitecture = (architecture) => {
3149
- const packages = artifactRecord(architectures[architecture], Object.keys(valkeyArtifactPackages));
3150
- const parseArtifact = (kind) => {
3151
- const artifact = artifactRecord(packages[kind], ["url", "sha256"]);
3152
- const url = artifact["url"];
3153
- const sha256 = artifact["sha256"];
3154
- const expected = `https://download.valkey.io/packaging/valkey-${line}/deb/ubuntu2404/${architecture}/${valkeyArtifactPackages[kind]}_${version}-1.noble_${architecture}.deb`;
3155
- if (typeof url !== "string" || url !== expected) throw new Error("valkeyArtifacts URL must select the exact official versioned Ubuntu artifact");
3156
- if (typeof sha256 !== "string" || !/^[a-f0-9]{64}$/.test(sha256)) throw new Error("valkeyArtifacts requires a lowercase SHA256 pin");
3157
- return {
3158
- url,
3159
- sha256
3160
- };
3161
- };
3162
- return {
3163
- server: parseArtifact("server"),
3164
- tools: parseArtifact("tools")
3165
- };
3166
- };
3167
- return {
3168
- amd64: parseArchitecture("amd64"),
3169
- arm64: parseArchitecture("arm64")
3170
- };
3171
- };
3172
- /** Reject unsupported or incomplete installed policies before inspecting a consumer. */
3173
- const parseToolchainPolicy = (input) => {
3174
- if (!record$1(input) || input["schemaVersion"] !== 1) throw new Error("toolchain.json must use schemaVersion 1");
3175
- const string = (object, key) => {
3176
- const value = object[key];
3177
- if (typeof value !== "string" || value.trim() === "") throw new Error(`toolchain.json requires a nonempty ${key}`);
3178
- return value;
3179
- };
3180
- const packages = {};
3181
- for (const key of packagePinKeys) packages[key] = string(input, key);
3182
- const bun = string(input, "bun");
3183
- const typescript = string(input, "typescript");
3184
- const node = string(input, "node");
3185
- const python = string(input, "python");
3186
- const postgres = string(input, "postgres");
3187
- const valkey = string(input, "valkey");
3188
- const rust = string(input, "rust");
3189
- const rustCompilerDevelopment = string(input, "rustCompilerDevelopment");
3190
- for (const [name, value] of Object.entries({
3191
- ...packages,
3192
- bun,
3193
- typescript
3194
- })) if ((0, semver.valid)(value) !== value) throw new Error(`toolchain.json ${name} must be an exact release`);
3195
- if (!/^[1-9]\d*$/.test(postgres) || !Number.isSafeInteger(Number(postgres))) throw new Error("PostgreSQL must be a canonical positive major");
3196
- if ((0, semver.valid)(valkey) !== valkey || !/^\d+\.\d+\.\d+$/.test(valkey)) throw new Error("Valkey must be an exact stable release");
3197
- const valkeyArtifacts = parseValkeyArtifacts(input["valkeyArtifacts"], valkey);
3198
- if (!nodePolicyValid(node)) throw new Error("Node must be a canonical major N.x series");
3199
- if (!/^\d+\.\d+(?:\.\d+)?$/.test(python)) throw new Error("Python must be an exact minor or patch release");
3200
- if (!/^\d+\.\d+\.\d+$/.test(rust)) throw new Error("Rust must be an exact stable release");
3201
- if (!/^nightly-\d{4}-\d{2}-\d{2}$/.test(rustCompilerDevelopment)) throw new Error("Rust compiler development must be a dated nightly");
3202
- const compatibility = input["typescript6Compatibility"];
3203
- if (!record$1(compatibility)) throw new Error("missing TypeScript compatibility policy");
3204
- const version = string(compatibility, "version");
3205
- const packageAlias = string(compatibility, "packageAlias");
3206
- if ((0, semver.valid)(version) !== version) throw new Error("TypeScript compatibility must be an exact release");
3207
- const layouts = input["typescriptInstallLayouts"];
3208
- if (!Array.isArray(layouts) || layouts.length === 0) throw new Error("missing TypeScript install layouts");
3209
- const typescriptInstallLayouts = layouts.map((layout) => {
3210
- if (!record$1(layout)) throw new Error("invalid TypeScript install layout");
3211
- const compilerPackage = string(layout, "compilerPackage");
3212
- const compilerSpecifier = string(layout, "compilerSpecifier");
3213
- const typecheckCommand = string(layout, "typecheckCommand");
3214
- switch (layout["type"]) {
3215
- case "direct":
3216
- if (compilerSpecifier !== typescript || compilerPackage !== "typescript") throw new Error("direct TypeScript layout differs from shared version");
3217
- return {
3218
- type: "direct",
3219
- compilerPackage,
3220
- compilerSpecifier,
3221
- typecheckCommand
3222
- };
3223
- case "split-compatibility": {
3224
- const compatibilityPackage = string(layout, "compatibilityPackage");
3225
- const compatibilitySpecifier = string(layout, "compatibilitySpecifier");
3226
- if (compilerSpecifier !== `npm:typescript@${typescript}` || compatibilitySpecifier !== version) throw new Error("split TypeScript layout differs from shared versions");
3227
- return {
3228
- type: "split-compatibility",
3229
- compilerPackage,
3230
- compilerSpecifier,
3231
- compatibilityPackage,
3232
- compatibilitySpecifier,
3233
- typecheckCommand
3234
- };
3235
- }
3236
- default: throw new Error("unsupported TypeScript install layout");
3237
- }
3238
- });
3239
- const actionsInput = input["actions"];
3240
- if (!record$1(actionsInput) || Object.keys(actionsInput).length === 0) throw new Error("missing approved actions");
3241
- const actions = {};
3242
- for (const [name, action] of Object.entries(actionsInput)) {
3243
- if (!record$1(action)) throw new Error(`invalid action policy: ${name}`);
3244
- const sha = string(action, "sha");
3245
- const actionVersion = string(action, "version");
3246
- if (!/^[a-f0-9]{40}$/.test(sha) || !/^v\d+(?:\.\d+)*$/.test(actionVersion)) throw new Error(`invalid action SHA/version: ${name}`);
3247
- actions[name] = {
3248
- sha,
3249
- version: actionVersion
3250
- };
3251
- }
3252
- const dependabotInput = input["dependabot"];
3253
- if (!record$1(dependabotInput) || !record$1(dependabotInput["schedule"]) || !record$1(dependabotInput["cooldown"]) || !record$1(dependabotInput["groups"])) throw new Error("missing Dependabot policy");
3254
- const scheduleInput = dependabotInput["schedule"];
3255
- const schedule = {
3256
- interval: string(scheduleInput, "interval"),
3257
- day: string(scheduleInput, "day"),
3258
- time: string(scheduleInput, "time"),
3259
- timezone: string(scheduleInput, "timezone")
3260
- };
3261
- const defaultDays = dependabotInput["cooldown"]["defaultDays"];
3262
- if (typeof defaultDays !== "number" || !Number.isInteger(defaultDays) || defaultDays < 5) throw new Error("Dependabot cooldown must be at least five days");
3263
- const strings = (value) => {
3264
- if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string")) throw new Error("Dependabot group values must be string arrays");
3265
- return value;
3266
- };
3267
- const groups = {};
3268
- for (const [name, group] of Object.entries(dependabotInput["groups"])) {
3269
- if (!record$1(group)) throw new Error(`invalid Dependabot group: ${name}`);
3270
- groups[name] = {
3271
- patterns: strings(group["patterns"]),
3272
- updateTypes: strings(group["updateTypes"])
3273
- };
3274
- }
3275
- return {
3276
- bun,
3277
- typescript,
3278
- node,
3279
- postgres,
3280
- valkey,
3281
- valkeyArtifacts,
3282
- python,
3283
- rust,
3284
- rustCompilerDevelopment,
3285
- packages,
3286
- actions,
3287
- typescriptInstallLayouts,
3288
- typescript6Compatibility: {
3289
- version,
3290
- packageAlias
3291
- },
3292
- dependabot: {
3293
- schedule,
3294
- cooldown: { defaultDays },
3295
- groups,
3296
- ignoredPackages: [.../* @__PURE__ */ new Set([
3297
- ...Object.keys(packages),
3298
- ...typescriptInstallLayouts.flatMap((layout) => layout.type === "direct" ? [layout.compilerPackage] : [layout.compilerPackage, layout.compatibilityPackage]),
3299
- packageAlias,
3300
- "bun-types"
3301
- ])],
3302
- ignoredActions: Object.keys(actions),
3303
- ignoredImages: [
3304
- "node",
3305
- "python",
3306
- "oven/bun"
3307
- ]
3308
- }
3309
- };
3310
- };
3311
- //#endregion
3312
2525
  //#region src/toolchain-guard.ts
3313
2526
  const toolchainRules = [.../* @__PURE__ */ new Set([
3314
2527
  ...packageRules,
@@ -3320,7 +2533,7 @@ const record = (value) => typeof value === "object" && value !== null && !Array.
3320
2533
  const array = (value) => Array.isArray(value);
3321
2534
  /** Repository-owned declarations are explicit; malformed decisions fail closed. */
3322
2535
  const parseToolchainConfiguration = (input) => {
3323
- if (!record(input) || Object.keys(input).some((key) => key !== "optOuts" && key !== "cloud" && key !== "engineFloors" && key !== "dynamicSelectors") || input["optOuts"] !== void 0 && !array(input["optOuts"])) throw new Error("stll-toolchain.json accepts only optOuts, cloud, engineFloors and dynamicSelectors 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");
3324
2537
  const disabled = /* @__PURE__ */ new Set();
3325
2538
  const optOuts = input["optOuts"] ?? [];
3326
2539
  if (!array(optOuts)) throw new Error("optOuts must be an array");
@@ -3332,9 +2545,10 @@ const parseToolchainConfiguration = (input) => {
3332
2545
  }
3333
2546
  return {
3334
2547
  disabled,
3335
- cloud: parseCloudSetup(input["cloud"]),
2548
+ cloud: require_toolchain_inputs.parseCloudSetup(input["cloud"]),
3336
2549
  engineFloors: parseEngineFloors(input["engineFloors"]),
3337
- dynamicSelectors: parseDynamicSelectors(input["dynamicSelectors"])
2550
+ dynamicSelectors: parseDynamicSelectors(input["dynamicSelectors"]),
2551
+ consumerChecks: parseConsumerChecks(input["consumerChecks"])
3338
2552
  };
3339
2553
  };
3340
2554
  /** Read one tracked configuration snapshot for guards and policy generation. */
@@ -3352,7 +2566,7 @@ const readToolchainInputs = (root) => {
3352
2566
  const trackedFiles = new Set(tracked);
3353
2567
  const resolvedRoot = (0, node_fs.realpathSync)(root);
3354
2568
  for (const file of tracked) {
3355
- const kind = toolchainInputKind(file);
2569
+ const kind = require_toolchain_inputs.toolchainInputKind(file);
3356
2570
  if (kind === void 0) continue;
3357
2571
  if (kind === "presence") {
3358
2572
  files[file] = "";
@@ -3397,14 +2611,11 @@ const checkToolchain = ({ root, policy, onDelegated }) => {
3397
2611
  }).trim();
3398
2612
  repository = /^(?:https?:\/\/github\.com\/|git@github\.com:|ssh:\/\/git@github\.com\/)([^/]+\/[^/]+?)(?:\.git)?\/?$/.exec(origin)?.[1];
3399
2613
  } catch {}
3400
- let disabled = /* @__PURE__ */ new Set();
3401
- let cloud;
2614
+ let configuration = parseToolchainConfiguration({});
3402
2615
  const engineFloors = [];
3403
2616
  let dynamicSelectors = [];
3404
2617
  if (files["stll-toolchain.json"] !== void 0) try {
3405
- const configuration = parseToolchainConfiguration(JSON.parse(files["stll-toolchain.json"]));
3406
- disabled = configuration.disabled;
3407
- cloud = configuration.cloud;
2618
+ configuration = parseToolchainConfiguration(JSON.parse(files["stll-toolchain.json"]));
3408
2619
  dynamicSelectors = configuration.dynamicSelectors;
3409
2620
  for (const entry of configuration.engineFloors) engineFloors.push(resolveEngineFloor(entry, files));
3410
2621
  } catch (error) {
@@ -3415,30 +2626,31 @@ const checkToolchain = ({ root, policy, onDelegated }) => {
3415
2626
  message: error instanceof Error ? error.message : String(error)
3416
2627
  });
3417
2628
  }
3418
- const script = files[cloudSetupPath];
2629
+ const cloud = configuration.cloud;
2630
+ const script = files[require_toolchain_inputs.cloudSetupPath];
3419
2631
  if (cloud === void 0 && trackedFiles.has(".agents/cloud-setup.sh")) diagnostics.push({
3420
2632
  rule: "cloud-setup-drift",
3421
- path: cloudSetupPath,
2633
+ path: require_toolchain_inputs.cloudSetupPath,
3422
2634
  line: 1,
3423
2635
  message: "cloud setup script requires an explicit cloud declaration in stll-toolchain.json"
3424
2636
  });
3425
2637
  if (cloud !== void 0) try {
3426
2638
  const nodeVersion = files[".node-version"];
3427
2639
  if (nodeVersion === void 0) throw new Error("cloud setup requires a tracked root .node-version with an exact stable release");
3428
- if (script !== generateCloudSetup({
2640
+ if (script !== require_toolchain_inputs.generateCloudSetup({
3429
2641
  policy,
3430
2642
  cloud,
3431
2643
  nodeVersion: nodeVersion.trim()
3432
2644
  })) diagnostics.push({
3433
2645
  rule: "cloud-setup-drift",
3434
- path: cloudSetupPath,
2646
+ path: require_toolchain_inputs.cloudSetupPath,
3435
2647
  line: 1,
3436
2648
  message: "cloud setup script is missing or differs from shared policy; run stll-cloud-setup"
3437
2649
  });
3438
2650
  } catch (error) {
3439
2651
  diagnostics.push({
3440
2652
  rule: "cloud-setup-drift",
3441
- path: cloudSetupPath,
2653
+ path: require_toolchain_inputs.cloudSetupPath,
3442
2654
  line: 1,
3443
2655
  message: error instanceof Error ? error.message : String(error)
3444
2656
  });
@@ -3446,6 +2658,11 @@ const checkToolchain = ({ root, policy, onDelegated }) => {
3446
2658
  const exercisedFloors = /* @__PURE__ */ new Set();
3447
2659
  const mismatchedFloors = /* @__PURE__ */ new Set();
3448
2660
  const selectorMatches = /* @__PURE__ */ new Map();
2661
+ diagnostics.push(...checkConsumerChecks({
2662
+ declarations: configuration.consumerChecks,
2663
+ files,
2664
+ policy
2665
+ }));
3449
2666
  diagnostics.push(...checkPackageFiles({
3450
2667
  files,
3451
2668
  policy
@@ -3488,7 +2705,7 @@ const checkToolchain = ({ root, policy, onDelegated }) => {
3488
2705
  files,
3489
2706
  policy: policy.dependabot
3490
2707
  }));
3491
- return diagnostics.filter((diagnostic) => !disabled.has(diagnostic.rule));
2708
+ return diagnostics.filter((diagnostic) => !configuration.disabled.has(diagnostic.rule));
3492
2709
  };
3493
2710
  //#endregion
3494
2711
  Object.defineProperty(exports, "checkToolchain", {
@@ -3497,30 +2714,12 @@ Object.defineProperty(exports, "checkToolchain", {
3497
2714
  return checkToolchain;
3498
2715
  }
3499
2716
  });
3500
- Object.defineProperty(exports, "cloudSetupPath", {
3501
- enumerable: true,
3502
- get: function() {
3503
- return cloudSetupPath;
3504
- }
3505
- });
3506
- Object.defineProperty(exports, "generateCloudSetup", {
3507
- enumerable: true,
3508
- get: function() {
3509
- return generateCloudSetup;
3510
- }
3511
- });
3512
2717
  Object.defineProperty(exports, "parseToolchainConfiguration", {
3513
2718
  enumerable: true,
3514
2719
  get: function() {
3515
2720
  return parseToolchainConfiguration;
3516
2721
  }
3517
2722
  });
3518
- Object.defineProperty(exports, "parseToolchainPolicy", {
3519
- enumerable: true,
3520
- get: function() {
3521
- return parseToolchainPolicy;
3522
- }
3523
- });
3524
2723
  Object.defineProperty(exports, "readToolchainInputs", {
3525
2724
  enumerable: true,
3526
2725
  get: function() {