diff --git a/config/owners.config b/config/owners.config new file mode 100644 index 0000000..6777998 --- /dev/null +++ b/config/owners.config @@ -0,0 +1,27 @@ +# Git Scripts β€” Owner Allowlist +# +# These scripts must NEVER touch repositories owned by anyone outside this +# list. The guard refuses to operate (and exits non-zero) if a target repo's +# GitHub owner is not present here. +# +# Add to this list: +# - your own GitHub username +# - any family members you maintain repos for (e.g. your son) +# - any organisations you control +# +# The owner check is case-insensitive. One owner per line in the array. +# +# Override at runtime without editing this file by exporting: +# GIT_SCRIPTS_ALLOWED_OWNERS="ownerA ownerB ownerC" +# (space- or comma-separated). + +ALLOWED_OWNERS=( + "hyperpolymath" +) + +if [[ -n "${GIT_SCRIPTS_ALLOWED_OWNERS:-}" ]]; then + # Replace the array entirely from the env var + IFS=', ' read -r -a ALLOWED_OWNERS <<< "${GIT_SCRIPTS_ALLOWED_OWNERS}" +fi + +export ALLOWED_OWNERS diff --git a/lib/script_manager/dependency_fixer.ex b/lib/script_manager/dependency_fixer.ex index b4ae215..797dcc4 100644 --- a/lib/script_manager/dependency_fixer.ex +++ b/lib/script_manager/dependency_fixer.ex @@ -11,37 +11,66 @@ defmodule ScriptManager.DependencyFixer do def run do IO.puts("\nπŸ”§ DEPENDENCY FIXER (Hardened Mode)") IO.puts("===================================") - + fix_lithoglyph() fix_rgtv() - + IO.puts("\nβœ… Dependency fixing complete!") :ok end + # Walk up to the enclosing git working tree and check the owner allowlist. + # Returns true if the directory has no enclosing repo (we can't tell, so allow + # the explicit per-path edits to proceed inside our own filesystem layout). + @spec safe_to_edit?(String.t()) :: boolean() + defp safe_to_edit?(path) do + case System.cmd("git", ["-C", path, "rev-parse", "--show-toplevel"], stderr_to_stdout: true) do + {toplevel, 0} -> + ScriptManager.OwnershipGuard.repo_allowed?(String.trim(toplevel)) + + _ -> + # Not inside a git repo: nothing remote to push to, no owner to violate. + true + end + end + @spec fix_lithoglyph() :: :ok defp fix_lithoglyph do path = "/var/mnt/eclipse/repos/nextgen-databases/lithoglyph/core-zig" IO.puts("Fixing Lithoglyph in #{path}...") - + try do - if File.dir?(path) do - build_zig = Path.join(path, "build.zig") - if File.exists?(build_zig) do - content = File.read!(build_zig) - new_content = String.replace(content, "const crypto_tests = b.addTest(.{", "const _crypto_tests = b.addTest(.{") - File.write!(build_zig, new_content) - IO.puts(" βœ“ build.zig patched") - - IO.puts(" Running tests...") - System.cmd("zig", ["build", "test"], cd: path, into: IO.stream(:stdio, :line)) - end - else - IO.puts(" ⚠ Lithoglyph directory not found") + cond do + not File.dir?(path) -> + IO.puts(" ⚠ Lithoglyph directory not found") + + not safe_to_edit?(path) -> + IO.puts(" πŸ›‘ Skipping: enclosing repo is outside the owner allowlist.") + + true -> + do_fix_lithoglyph(path) end rescue e -> IO.puts(" ❌ Failed to fix Lithoglyph: #{inspect(e)}") end + + :ok + end + + @spec do_fix_lithoglyph(String.t()) :: :ok + defp do_fix_lithoglyph(path) do + build_zig = Path.join(path, "build.zig") + + if File.exists?(build_zig) do + content = File.read!(build_zig) + new_content = String.replace(content, "const crypto_tests = b.addTest(.{", "const _crypto_tests = b.addTest(.{") + File.write!(build_zig, new_content) + IO.puts(" βœ“ build.zig patched") + + IO.puts(" Running tests...") + System.cmd("zig", ["build", "test"], cd: path, into: IO.stream(:stdio, :line)) + end + :ok end @@ -49,33 +78,48 @@ defmodule ScriptManager.DependencyFixer do defp fix_rgtv do path = "/var/mnt/eclipse/repos/reasonably-good-token-vault/vault-core" IO.puts("Fixing RGTV in #{path}...") - + try do - if File.dir?(path) do - primes_rs = Path.join([path, "src", "primes.rs"]) - if File.exists?(primes_rs) do - content = File.read!(primes_rs) - new_content = String.replace(content, "use num_bigint::{BigUint, RandBigInt, ToBigUint};", "use num_bigint::{BigUint, ToBigUint};") - File.write!(primes_rs, new_content) - IO.puts(" βœ“ src/primes.rs patched") - end - - crypto_rs = Path.join([path, "src", "crypto.rs"]) - if File.exists?(crypto_rs) do - content = File.read!(crypto_rs) - new_content = String.replace(content, "use ed448_goldilocks::EdwardsPoint::generator()", "use ed448_goldilocks::edwards::EdwardsPoint::generator()") - File.write!(crypto_rs, new_content) - IO.puts(" βœ“ src/crypto.rs patched") - end - - IO.puts(" Running tests...") - System.cmd("cargo", ["test", "--lib"], cd: path, into: IO.stream(:stdio, :line)) - else - IO.puts(" ⚠ RGTV directory not found") + cond do + not File.dir?(path) -> + IO.puts(" ⚠ RGTV directory not found") + + not safe_to_edit?(path) -> + IO.puts(" πŸ›‘ Skipping: enclosing repo is outside the owner allowlist.") + + true -> + do_fix_rgtv(path) end rescue e -> IO.puts(" ❌ Failed to fix RGTV: #{inspect(e)}") end + + :ok + end + + @spec do_fix_rgtv(String.t()) :: :ok + defp do_fix_rgtv(path) do + primes_rs = Path.join([path, "src", "primes.rs"]) + + if File.exists?(primes_rs) do + content = File.read!(primes_rs) + new_content = String.replace(content, "use num_bigint::{BigUint, RandBigInt, ToBigUint};", "use num_bigint::{BigUint, ToBigUint};") + File.write!(primes_rs, new_content) + IO.puts(" βœ“ src/primes.rs patched") + end + + crypto_rs = Path.join([path, "src", "crypto.rs"]) + + if File.exists?(crypto_rs) do + content = File.read!(crypto_rs) + new_content = String.replace(content, "use ed448_goldilocks::EdwardsPoint::generator()", "use ed448_goldilocks::edwards::EdwardsPoint::generator()") + File.write!(crypto_rs, new_content) + IO.puts(" βœ“ src/crypto.rs patched") + end + + IO.puts(" Running tests...") + System.cmd("cargo", ["test", "--lib"], cd: path, into: IO.stream(:stdio, :line)) + :ok end end diff --git a/lib/script_manager/estate_deployer.ex b/lib/script_manager/estate_deployer.ex index 24bd83b..88024ef 100644 --- a/lib/script_manager/estate_deployer.ex +++ b/lib/script_manager/estate_deployer.ex @@ -2,6 +2,7 @@ defmodule ScriptManager.EstateDeployer do @moduledoc "Estate deployment logic generalized for all repositories" alias ScriptManager.RepoHelper + alias ScriptManager.OwnershipGuard @contractile_types ["must", "trust", "dust", "lust", "adjust", "intend"] @standards_dir "/var/mnt/eclipse/repos/standards" @@ -65,9 +66,12 @@ defmodule ScriptManager.EstateDeployer do end defp deploy_by_paths(repo_paths, phases) do + # Ownership guard: refuse to deploy into repos outside the allowlist. + repo_paths = OwnershipGuard.filter_allowed_verbose(repo_paths) + total = length(repo_paths) IO.puts("Processing #{total} repositories...") - + repo_paths |> Enum.with_index(1) |> Enum.each(fn {path, index} -> diff --git a/lib/script_manager/git_syncer.ex b/lib/script_manager/git_syncer.ex index 96b9b5b..9c6f357 100644 --- a/lib/script_manager/git_syncer.ex +++ b/lib/script_manager/git_syncer.ex @@ -5,6 +5,7 @@ defmodule ScriptManager.GitSyncer do """ alias ScriptManager.RepoHelper + alias ScriptManager.OwnershipGuard @type sync_status :: String.t() @type merge_status :: String.t() @@ -16,9 +17,12 @@ defmodule ScriptManager.GitSyncer do def run do IO.puts("\n🌐 GLOBAL GIT SYNC (Concurrent Strict Mode)") IO.puts("============================================") - - all_repos = RepoHelper.find_all_repos() - + + # Ownership guard: never push to repos outside the allowlist. + all_repos = + RepoHelper.find_all_repos() + |> OwnershipGuard.filter_allowed_verbose() + header = "| Repository | Sync Status | Merge Status | Push Status |" separator = "| :--- | :--- | :--- | :--- |" diff --git a/lib/script_manager/ownership_guard.ex b/lib/script_manager/ownership_guard.ex new file mode 100644 index 0000000..ac53384 --- /dev/null +++ b/lib/script_manager/ownership_guard.ex @@ -0,0 +1,184 @@ +defmodule ScriptManager.OwnershipGuard do + @moduledoc """ + Owner allowlist gate for any script that talks to GitHub or pushes to remotes. + + Refuses to act on repositories owned by anyone outside the configured + allowlist. Mirrors `scripts/lib/ownership_guard.sh` so bash scripts and + Elixir modules behave the same way. + + Allowlist sources (first non-empty wins): + 1. `GIT_SCRIPTS_ALLOWED_OWNERS` env var (space- or comma-separated). + 2. `config/owners.config` (parsed for the `ALLOWED_OWNERS=( … )` array). + 3. The default `["hyperpolymath"]`. + """ + + @config_paths [ + "config/owners.config", + "/var/mnt/eclipse/repos/git-scripts/config/owners.config" + ] + + @default_owners ["hyperpolymath"] + + @doc "Return the configured list of allowed owners (lowercase)." + @spec allowed_owners() :: [String.t()] + def allowed_owners do + raw = + case System.get_env("GIT_SCRIPTS_ALLOWED_OWNERS") do + nil -> from_config_file() || @default_owners + "" -> from_config_file() || @default_owners + env -> parse_list(env) + end + + Enum.map(raw, &String.downcase/1) + end + + @doc "True when an owner string is in the allowlist." + @spec owner_allowed?(String.t() | nil) :: boolean() + def owner_allowed?(nil), do: false + def owner_allowed?(""), do: false + def owner_allowed?(owner) when is_binary(owner) do + String.downcase(owner) in allowed_owners() + end + + @doc "Get the GitHub owner from a local repo's `origin` remote, or nil." + @spec repo_owner(String.t()) :: String.t() | nil + def repo_owner(repo_path) do + case System.cmd("git", ["-C", repo_path, "config", "--get", "remote.origin.url"], + stderr_to_stdout: true + ) do + {url, 0} -> url |> String.trim() |> parse_owner_from_url() + _ -> nil + end + rescue + _ -> nil + end + + @doc "True when the local repo's GitHub owner is in the allowlist." + @spec repo_allowed?(String.t()) :: boolean() + def repo_allowed?(repo_path) do + case repo_owner(repo_path) do + nil -> false + owner -> owner_allowed?(owner) + end + end + + @doc """ + Filter a list of repo paths down to those whose origin owner is allowed. + Repos with no GitHub origin are excluded. + """ + @spec filter_allowed([String.t()]) :: [String.t()] + def filter_allowed(paths) when is_list(paths) do + Enum.filter(paths, &repo_allowed?/1) + end + + @doc """ + Like `filter_allowed/1` but also prints a one-line summary of how many + repos were rejected, so the user can see the guard at work. + """ + @spec filter_allowed_verbose([String.t()]) :: [String.t()] + def filter_allowed_verbose(paths) when is_list(paths) do + {allowed, rejected} = Enum.split_with(paths, &repo_allowed?/1) + + if rejected != [] do + IO.puts( + "πŸ›‘ Ownership guard: skipping #{length(rejected)} repo(s) outside allowlist " <> + "(#{Enum.join(allowed_owners(), ", ")})." + ) + end + + allowed + end + + @doc """ + Hard guard. Aborts the running script with a clear message if `owner` + is not in the allowlist. Use at the top of any operation that targets + a single org/user (e.g. mass PR labelling). + """ + @spec assert_owner_allowed!(String.t()) :: :ok | no_return() + def assert_owner_allowed!(owner) do + if owner_allowed?(owner) do + :ok + else + IO.puts(:stderr, """ + + ❌ REFUSING to operate on owner '#{owner}'. + This owner is not in the git-scripts allowlist. + Allowed owners: #{Enum.join(allowed_owners(), ", ")} + + Edit config/owners.config or set GIT_SCRIPTS_ALLOWED_OWNERS=\"owner1 owner2\" + to change this. + """) + + System.halt(78) + end + end + + # ------------------------------------------------------------------ + # Internals + # ------------------------------------------------------------------ + + # Host-agnostic owner parser. Handles SSH-style (git@host:path) and URL-style + # (proto://[creds@]host[:port]/path) and treats the second-to-last path + # segment as the owner β€” works for GitHub, GitLab, Bitbucket, Gitea, + # codeberg, self-hosted servers, and so on. + defp parse_owner_from_url(url) do + url = String.trim_trailing(url, ".git") + + path_part = + cond do + # SSH-style: [user@]host:path + m = Regex.run(~r{^[^/\s@]+@[^:]+:(.+)$}, url) -> Enum.at(m, 1) + # URL-style: proto://[creds@]host[:port]/path + m = Regex.run(~r{^[a-zA-Z]+://[^/]+(/.+)$}, url) -> Enum.at(m, 1) + true -> nil + end + + case path_part do + nil -> nil + "" -> nil + pp -> + pp = pp |> String.trim_leading("/") |> String.trim_trailing("/") + case String.split(pp, "/") do + segments when length(segments) >= 2 -> Enum.at(segments, length(segments) - 2) + _ -> nil + end + end + end + + defp parse_list(str) do + str + |> String.split([",", " ", "\n", "\t"], trim: true) + |> Enum.map(&String.trim/1) + |> Enum.reject(&(&1 == "")) + end + + defp from_config_file do + @config_paths + |> Enum.find_value(fn path -> + if File.exists?(path), do: parse_config_file(path), else: nil + end) + end + + defp parse_config_file(path) do + case File.read(path) do + {:ok, contents} -> + case Regex.run(~r/ALLOWED_OWNERS=\(([^)]*)\)/s, contents) do + [_, body] -> + body + |> String.split([",", " ", "\n", "\t"], trim: true) + |> Enum.map(&String.trim(&1, "\"")) + |> Enum.map(&String.trim(&1, "'")) + |> Enum.reject(&(&1 == "")) + |> Enum.reject(&String.starts_with?(&1, "#")) + |> case do + [] -> nil + owners -> owners + end + + _ -> nil + end + + _ -> nil + end + end +end diff --git a/lib/script_manager/pr_processor.ex b/lib/script_manager/pr_processor.ex index 4f428a0..d653eb6 100644 --- a/lib/script_manager/pr_processor.ex +++ b/lib/script_manager/pr_processor.ex @@ -9,8 +9,12 @@ defmodule ScriptManager.PRProcessor do def process_all(org, action) when action in [:add_reviewers, :request_changes, :add_labels, :add_comments, :request_reviews, :close_stale] do IO.puts("\nπŸ”„ MASS PR PROCESSOR") IO.puts("====================") + + # Ownership guard: refuse to run against orgs/users outside the allowlist. + ScriptManager.OwnershipGuard.assert_owner_allowed!(org) + IO.puts("Processing all open PRs with action: #{action}") - + prs = ScriptManager.GitHubAPI.get_open_prs(org) if length(prs) == 0 do @@ -78,7 +82,10 @@ defmodule ScriptManager.PRProcessor do def add_standard_comment(org, comment) do IO.puts("\nπŸ“ ADD STANDARD COMMENT") IO.puts("======================") - + + # Ownership guard: refuse to comment on PRs in orgs outside the allowlist. + ScriptManager.OwnershipGuard.assert_owner_allowed!(org) + prs = ScriptManager.GitHubAPI.get_open_prs(org) Enum.each(prs, fn pr -> diff --git a/lib/script_manager/repo_cleanup.ex b/lib/script_manager/repo_cleanup.ex index 0a4a96f..d6cfacf 100644 --- a/lib/script_manager/repo_cleanup.ex +++ b/lib/script_manager/repo_cleanup.ex @@ -6,6 +6,13 @@ defmodule ScriptManager.RepoCleanup do IO.puts("🧹 REPOSITORY CLEANUP") IO.puts("====================") IO.puts("") + IO.puts( + "⚠️ These cleanup operations shell out to external scripts in" <> + " /var/mnt/eclipse/cleanup_scripts/ which iterate every local repo and" <> + " are NOT bound by config/owners.config. Review those scripts before" <> + " running them." + ) + IO.puts("") IO.puts("Select cleanup operation:") IO.puts("[1] Run comprehensive cleanup (all 280+ repos)") IO.puts("[2] Run targeted cleanup (10 key repos)") diff --git a/lib/script_manager/tui.ex b/lib/script_manager/tui.ex index 73b0cef..ccaecb0 100644 --- a/lib/script_manager/tui.ex +++ b/lib/script_manager/tui.ex @@ -1,59 +1,172 @@ defmodule ScriptManager.TUI do @moduledoc """ - Enhanced Elixir TUI for managing reusable scripts and functions - + Two-tier TUI for managing reusable scripts and functions. + + Top level groups related operations into categories ([A]–[F]); each + category opens a sub-menu of numbered items. Item names have been + rewritten to describe what they actually do at a glance. + Features: - Self-healing: Automatic recovery from common errors - Fault tolerant: Graceful handling of failures - Self-diagnostic: Pre-execution validation - Help system: Detailed information for each function - User negotiation: Confirmations for critical operations + - Ownership guard: Refuses to operate on repos outside the allowlist """ + # ---------------------------------------------------------------------- + # Menu definition (single source of truth) + # ---------------------------------------------------------------------- + + # Each category has a key (letter), a title, and a list of items. + # Each item has: a sub-key (number), a display name, the action, and an + # optional one-line help blurb. + # + # Action shapes: + # {:fun, fun} β€” call fun.() + # {:fun, fun, args} β€” apply(fun, args) + # {:fun_confirm, fun, prompt} β€” confirm, then call fun.() + # {:nyi, message} β€” print "coming soon" message + defp categories do + [ + {"A", "Audits & Reports", + [ + {"1", "Audit Wiki Status", + {:fun, &ScriptManager.WikiAudit.run/0}, + "Audit GitHub wiki status across allowed repos."}, + {"2", "Audit Project Metadata (About)", + {:fun, &ScriptManager.ProjectTabsAudit.run/0}, + "Audit description, homepage URL, and topics on each repo."}, + {"3", "Audit Contractile Implementation", + {:fun, &ScriptManager.ContractileAuditor.run/0}, + "Check must/trust/dust/lust/adjust/intend contractiles + K9-SVC."}, + {"4", "Security Audit (Secrets & Dependabot)", + {:fun, &ScriptManager.ScriptAuditor.run/0}, + "Scan for secrets (gitleaks) and Dependabot Critical/High alerts."}, + {"5", "Repository Health Dashboard", + {:fun, &ScriptManager.HealthDashboard.generate_report/0}, + "Generate a health-score report for repositories."}, + {"6", "Verify Local-vs-Remote Sync", + {:fun, &ScriptManager.Verifier.run/0}, + "Compare each repo's local HEAD message to its remote."} + ]}, + {"B", "Repository Maintenance", + [ + {"1", "Update Repos (Sync, Commit, Push)", + {:fun, &ScriptManager.RepoUpdater.run/0}, + "Pull, rebase, commit, and push the configured repo set."}, + {"2", "Global Git Sync (All Allowed Repos)", + {:fun, &ScriptManager.GitSyncer.run/0}, + "Concurrent sync/merge/push across every allowed local repo."}, + {"3", "Standardize README Format", + {:fun, &ScriptManager.ReadmeStandardizer.run/0}, + "Convert and consolidate README files to README.adoc."}, + {"4", "Convert Markdown to AsciiDoc", + {:fun, &ScriptManager.MDConverter.run/0}, + "Bulk-convert lingering README.md files to AsciiDoc."}, + {"5", "Clean Hidden Unicode in Files", + {:fun, &__MODULE__.run_clean_unicode/0}, + "Strip hidden / bidi Unicode characters from tracked files."}, + {"6", "Repository Cleanup Operations", + {:fun_confirm, &ScriptManager.RepoCleanup.run/0, + "This may delete files. Continue?"}, + "Run gitignore updates, workflow commits, or full cleanup."}, + {"7", "Fix Known Dependency Issues", + {:fun, &ScriptManager.DependencyFixer.run/0}, + "Apply hard-coded patches for Lithoglyph and RGTV builds."} + ]}, + {"C", "GitHub Operations", + [ + {"1", "Apply Branch Protection Rulesets", + {:fun_confirm, &ScriptManager.BranchProtection.run/0, + "This will modify repository settings. Continue?"}, + "Push the standard ruleset (signed commits, linear history…)."}, + {"2", "Mass PR Processor (Labels/Comments)", + {:fun, &ScriptManager.PRProcessor.process_all/2, + ["hyperpolymath", :add_labels]}, + "Apply labels in bulk to open PRs across the allowed org."}, + {"3", "GitHub CLI Helper", + {:fun, &ScriptManager.GHCLI.run/0}, + "Print useful gh commands and verify gh auth status."} + ]}, + {"D", "Estate-Wide Deployment", + [ + {"1", "Deploy Estate Standards", + {:fun, &ScriptManager.EstateDeployer.run/0}, + "Deploy contractiles, K9-SVC, accessibility, VPAT, pre-commit hook."}, + {"2", "Link Language Toolchains", + {:fun, &ScriptManager.ToolchainLinker.run/0}, + "Symlink built compiler/runtime binaries into ~/.local/bin."}, + {"3", "Find Media Repositories (rclone)", + {:fun, &ScriptManager.MediaFinder.run/0}, + "Scan rclone remotes for directories that look like media repos."} + ]}, + {"E", "External Tools", + [ + {"1", "Launch NQC (Database Query)", + {:fun, &__MODULE__.launch_nqc/0}, + "Open the NextGen Query Client web UI for VQL/GQL/KQL."}, + {"2", "Launch Invariant Path (Code Analysis)", + {:fun, &__MODULE__.launch_invariant_path/0}, + "Open the Invariant Path code-analysis tool."} + ]}, + {"F", "Coming Soon", + [ + {"1", "Dependency Updater", + {:nyi, "πŸ“¦ Dependency Updater - Coming Soon!"}, + "Cross-language dependency upgrade orchestration (planned)."}, + {"2", "Release Manager", + {:nyi, "πŸŽ‰ Release Manager - Coming Soon!"}, + "Tagging, changelog, and GitHub release automation (planned)."} + ]} + ] + end + + # ---------------------------------------------------------------------- + # Lifecycle + # ---------------------------------------------------------------------- + @doc "Main TUI loop" def run do - # Set up error handling Process.flag(:trap_exit, true) - - # Show welcome banner + show_banner() - - # Check system health check_system_health() - - # Start main menu - menu() + show_owner_allowlist() + + main_loop() rescue - error -> + error -> IO.puts("\n❌ Critical error: #{inspect(error)}") IO.puts("Restarting TUI...") run() end defp show_banner do - IO.puts("\nπŸ”§ ELIXIR SCRIPT MANAGER v2.0") + IO.puts("\nπŸ”§ ELIXIR SCRIPT MANAGER v2.1") IO.puts("==============================") IO.puts("Self-Healing, Fault-Tolerant TUI") + IO.puts("Two-tier menu | Ownership-guarded operations") IO.puts("Type 'h' for help, '0' to exit") IO.puts("") end defp check_system_health do - # Check if required commands are available required_commands = ["bash", "git", "gh"] - + missing = Enum.filter(required_commands, fn cmd -> - System.cmd("which", [cmd], [stderr_to_stdout: true]) |> elem(0) != 0 + System.cmd("which", [cmd], stderr_to_stdout: true) |> elem(0) != 0 end) - + if missing != [] do IO.puts("⚠️ Missing required commands: #{inspect(missing)}") IO.puts("Some functions may not work properly.") IO.puts("") end - - # Check if scripts directory exists + scripts_dir = "scripts" + if !File.exists?(scripts_dir) do IO.puts("⚠️ Scripts directory not found: #{scripts_dir}") IO.puts("Script-based functions will not work.") @@ -61,121 +174,136 @@ defmodule ScriptManager.TUI do end end - defp menu do - loop() + defp show_owner_allowlist do + owners = ScriptManager.OwnershipGuard.allowed_owners() + + IO.puts("πŸ›‘ Ownership allowlist: #{Enum.join(owners, ", ")}") + IO.puts(" (Edit config/owners.config to add your son's or other owners.)") + IO.puts("") end - - defp loop do + + # ---------------------------------------------------------------------- + # Top-level menu + # ---------------------------------------------------------------------- + + defp main_loop do IO.puts("\n" <> String.duplicate("=", 50)) IO.puts("MAIN MENU") IO.puts(String.duplicate("=", 50)) - - IO.puts("\n[1] Wiki Audit") - IO.puts("[2] Project Tabs Audit") - IO.puts("[3] Branch Protection Apply") - IO.puts("[4] MD to ADOC Converter") - IO.puts("[5] Standardize READMEs") - IO.puts("[6] Update Repos") - IO.puts("[7] Audit Scripts") - IO.puts("[8] Verify") - IO.puts("[9] Use GH CLI") - IO.puts("[10] Mass PR Processor") - IO.puts("[11] Health Dashboard") - IO.puts("[12] Repository Cleanup") - IO.puts("[13] Clean Unicode") - IO.puts("[14] Dependency Updater") - IO.puts("[15] Release Manager") - IO.puts("[16] Contractile Audit") - IO.puts("[17] Estate Deployer") - IO.puts("[18] Global Git Sync") - IO.puts("[19] Media Finder") - IO.puts("[20] Dependency Fixer") - IO.puts("[21] Toolchain Linker") - IO.puts("[22] Launch NQC (Database Query)") - IO.puts("[23] Launch Invariant Path (Code Analysis)") - IO.puts("\n[h] Help - Detailed information") + + Enum.each(categories(), fn {key, title, items} -> + IO.puts("[#{key}] #{title} (#{length(items)})") + end) + + IO.puts("") + IO.puts("[h] Help - Detailed information") IO.puts("[s] System Status") IO.puts("[0] Exit") - - IO.write("\nSelect option: ") - choice = - try do - input = IO.gets("") - cond do - input == nil -> "0" # Handle EOF as exit - input == :eof -> "0" # Handle EOF as exit - true -> String.trim(input) - end - rescue - _ -> "0" # Any error, default to exit - end - + + IO.write("\nSelect category: ") + choice = read_choice() |> String.upcase() + case choice do - "1" -> safe_execute(&ScriptManager.WikiAudit.run/0, "Wiki Audit") - "2" -> safe_execute(&ScriptManager.ProjectTabsAudit.run/0, "Project Tabs Audit") - "3" -> safe_execute_with_confirm(&ScriptManager.BranchProtection.run/0, "Branch Protection Apply", "This will modify repository settings. Continue?") - "4" -> safe_execute(&ScriptManager.MDConverter.run/0, "MD to ADOC Converter") - "5" -> safe_execute(&ScriptManager.ReadmeStandardizer.run/0, "Standardize READMEs") - "6" -> safe_execute(&ScriptManager.RepoUpdater.run/0, "Update Repos") - "7" -> safe_execute(&ScriptManager.ScriptAuditor.run/0, "Audit Scripts") - "8" -> safe_execute(&ScriptManager.Verifier.run/0, "Verify") - "9" -> safe_execute(&ScriptManager.GHCLI.run/0, "GH CLI") - "10" -> safe_execute(&ScriptManager.PRProcessor.process_all/2, "Mass PR Processor", ["hyperpolymath", :add_labels]) - "11" -> safe_execute(&ScriptManager.HealthDashboard.generate_report/0, "Health Dashboard") - "12" -> safe_execute_with_confirm(&ScriptManager.RepoCleanup.run/0, "Repository Cleanup", "This may delete files. Continue?") - "13" -> safe_execute(&run_clean_unicode/0, "Clean Unicode") - "14" -> IO.puts("\nπŸ“¦ Dependency Updater - Coming Soon!") - "15" -> IO.puts("\nπŸŽ‰ Release Manager - Coming Soon!") - "16" -> safe_execute(&ScriptManager.ContractileAuditor.run/0, "Contractile Audit") - "17" -> safe_execute(&ScriptManager.EstateDeployer.run/0, "Estate Deployer") - "18" -> safe_execute(&ScriptManager.GitSyncer.run/0, "Global Git Sync") - "19" -> safe_execute(&ScriptManager.MediaFinder.run/0, "Media Finder") - "20" -> safe_execute(&ScriptManager.DependencyFixer.run/0, "Dependency Fixer") - "21" -> safe_execute(&ScriptManager.ToolchainLinker.run/0, "Toolchain Linker") - "22" -> launch_nqc() - "23" -> launch_invariant_path() - "h" -> show_help() - "s" -> show_system_status() - "0" -> IO.puts("\nπŸ‘‹ Goodbye!") - _ -> - IO.puts("\n❌ Invalid choice, please try again") - loop() - end - - # Continue loop unless exiting - if choice != "0" do - loop() + "H" -> + show_help() + main_loop() + + "S" -> + show_system_status() + main_loop() + + "0" -> + IO.puts("\nπŸ‘‹ Goodbye!") + + key -> + case Enum.find(categories(), fn {k, _, _} -> k == key end) do + nil -> + IO.puts("\n❌ Invalid choice, please try again") + main_loop() + + category -> + sub_loop(category) + main_loop() + end end end - defp safe_execute(func, name) do - safe_execute(func, name, []) + # ---------------------------------------------------------------------- + # Sub-menu (per category) + # ---------------------------------------------------------------------- + + defp sub_loop({key, title, items}) do + IO.puts("\n" <> String.duplicate("-", 50)) + IO.puts("[#{key}] #{title}") + IO.puts(String.duplicate("-", 50)) + + Enum.each(items, fn {num, name, _action, _help} -> + IO.puts(" [#{num}] #{name}") + end) + + IO.puts("") + IO.puts(" [b] Back to main menu") + IO.puts(" [0] Exit") + + IO.write("\nSelect item: ") + choice = read_choice() |> String.downcase() + + case choice do + "b" -> + :ok + + "0" -> + IO.puts("\nπŸ‘‹ Goodbye!") + System.halt(0) + + num -> + case Enum.find(items, fn {n, _, _, _} -> n == num end) do + nil -> + IO.puts("\n❌ Invalid choice, please try again") + sub_loop({key, title, items}) + + {_, name, action, _help} -> + invoke(action, name) + sub_loop({key, title, items}) + end + end end + # ---------------------------------------------------------------------- + # Action dispatch + # ---------------------------------------------------------------------- + + defp invoke({:fun, fun}, name), do: safe_execute(fun, name, []) + defp invoke({:fun, fun, args}, name), do: safe_execute(fun, name, args) + + defp invoke({:fun_confirm, fun, prompt}, name), + do: safe_execute_with_confirm(fun, name, prompt) + + defp invoke({:nyi, message}, _name), do: IO.puts("\n" <> message) + defp safe_execute(func, name, args) do IO.puts("\nπŸ”„ Starting: #{name}") IO.puts("=" <> String.duplicate("=", String.length(name) + 1)) - + try do start_time = System.system_time(:millisecond) result = apply(func, args) end_time = System.system_time(:millisecond) - + elapsed_ms = end_time - start_time elapsed_s = elapsed_ms / 1000.0 - + IO.puts("\nβœ… #{name} completed in #{Float.round(elapsed_s, 2)} seconds") result rescue error in [FunctionClauseError, UndefinedFunctionError] -> IO.puts("\n❌ Function not available: #{inspect(error)}") IO.puts("This feature may not be implemented yet.") - + error -> IO.puts("\n❌ Error in #{name}: #{inspect(error)}") IO.puts("Attempting recovery...") - - # Try to recover by reloading modules + try do Code.ensure_loaded?(ScriptManager.TUI) IO.puts("βœ… Recovered successfully") @@ -189,70 +317,110 @@ defmodule ScriptManager.TUI do IO.puts("\n⚠️ #{name}") IO.puts("This operation may make changes to your repositories.") IO.write("\n#{confirm_msg} (y/N): ") - + response = String.trim(IO.gets("") || "n") - + if String.downcase(response) == "y" do - safe_execute(func, name) + safe_execute(func, name, []) else IO.puts("❌ Operation cancelled by user") end end + # ---------------------------------------------------------------------- + # System status / help + # ---------------------------------------------------------------------- + defp show_system_status do IO.puts("\n" <> String.duplicate("=", 60)) IO.puts("SYSTEM STATUS") IO.puts(String.duplicate("=", 60)) - - # Check required commands + required_commands = ["bash", "git", "gh", "jq"] - + IO.puts("\nRequired Commands:") + Enum.each(required_commands, fn cmd -> - if System.cmd("which", [cmd], [stderr_to_stdout: true]) |> elem(0) == 0 do + if System.cmd("which", [cmd], stderr_to_stdout: true) |> elem(0) == 0 do IO.puts(" βœ… #{cmd}") else IO.puts(" ❌ #{cmd} (missing)") end end) - - # Check scripts directory + scripts_dir = "scripts" + if File.exists?(scripts_dir) do script_count = File.ls!(scripts_dir) |> Enum.count() - IO.puts("\nScripts Directory: βœ… #{script_count} scripts found") + IO.puts("\nScripts Directory: βœ… #{script_count} entries found") else IO.puts("\nScripts Directory: ❌ Not found") end - - # Check GitHub CLI authentication + IO.puts("\nGitHub CLI Status:") - case System.cmd("gh", ["auth", "status"], [stderr_to_stdout: true]) do + + case System.cmd("gh", ["auth", "status"], stderr_to_stdout: true) do {0, output} -> IO.puts(" βœ… Authenticated: #{String.trim(output)}") {_, _} -> IO.puts(" ❌ Not authenticated or error") end - + + IO.puts("\nOwnership Allowlist:") + Enum.each(ScriptManager.OwnershipGuard.allowed_owners(), fn o -> + IO.puts(" β€’ #{o}") + end) + IO.puts("\n" <> String.duplicate("-", 60)) IO.puts("Press Enter to return to main menu...") IO.gets("") end - defp run_clean_unicode do + defp show_help do + IO.puts("\n" <> String.duplicate("=", 60)) + IO.puts("HELP β€” Detailed Function Information") + IO.puts(String.duplicate("=", 60)) + + Enum.each(categories(), fn {key, title, items} -> + IO.puts("\n[#{key}] #{title}") + IO.puts(String.duplicate("-", 60)) + + Enum.each(items, fn {num, name, _action, blurb} -> + IO.puts(" [#{key}#{num}] #{name}") + if blurb && blurb != "", do: IO.puts(" #{blurb}") + end) + end) + + IO.puts("\n" <> String.duplicate("-", 60)) + IO.puts("Ownership safety:") + IO.puts( + " Operations that touch repositories check the allowlist in" <> + " config/owners.config (or the GIT_SCRIPTS_ALLOWED_OWNERS env var)." + ) + IO.puts(" Add yourself, your son, and any organisations you control there.") + + IO.puts("\nPress Enter to return to main menu...") + IO.gets("") + end + + # ---------------------------------------------------------------------- + # External launchers (kept public so the menu definition can reference them) + # ---------------------------------------------------------------------- + + @doc false + def run_clean_unicode do IO.puts("\n🧼 CLEAN UNICODE") IO.puts("Cleaning hidden/bidirectional Unicode characters from files...") - - # Check if script exists + script_path = "/var/mnt/eclipse/scripts/clean-unicode.sh" - + if File.exists?(script_path) do IO.puts("Running: #{script_path}") - - # Execute with error handling + case System.cmd(script_path, []) do - {0, output} -> + {output, 0} -> IO.puts("βœ… Unicode cleaning complete!") IO.puts(output) - {status, error} -> + + {error, status} -> IO.puts("❌ Unicode cleaning failed (exit #{status}):") IO.puts(error) end @@ -260,20 +428,21 @@ defmodule ScriptManager.TUI do IO.puts("❌ Script not found: #{script_path}") IO.puts("Cannot perform Unicode cleaning") end - + :ok end - defp launch_nqc do + @doc false + def launch_nqc do IO.puts("\nπŸš€ Launching NextGen Query Client...") - nqc_launcher = "/var/mnt/eclipse/repos/nextgen-databases/nqc/nqc-enhanced-launcher.sh" - + if File.exists?(nqc_launcher) do IO.puts("Starting NQC web interface...") + case System.cmd(nqc_launcher, ["--auto"]) do - {0, _} -> IO.puts("βœ… NQC launched successfully") - {status, error} -> IO.puts("❌ Failed to launch NQC (exit #{status}): #{error}") + {_, 0} -> IO.puts("βœ… NQC launched successfully") + {error, status} -> IO.puts("❌ Failed to launch NQC (exit #{status}): #{error}") end else IO.puts("❌ NQC launcher not found: #{nqc_launcher}") @@ -281,16 +450,18 @@ defmodule ScriptManager.TUI do end end - defp launch_invariant_path do + @doc false + def launch_invariant_path do IO.puts("\nπŸ” Launching Invariant Path...") - ip_launcher = "/var/mnt/eclipse/repos/invariant-path/invariant-path-launcher" - + if File.exists?(ip_launcher) do IO.puts("Starting Invariant Path analysis tool...") + case System.cmd(ip_launcher, ["--auto"]) do - {0, _} -> IO.puts("βœ… Invariant Path launched successfully") - {status, error} -> IO.puts("❌ Failed to launch Invariant Path (exit #{status}): #{error}") + {_, 0} -> IO.puts("βœ… Invariant Path launched successfully") + {error, status} -> + IO.puts("❌ Failed to launch Invariant Path (exit #{status}): #{error}") end else IO.puts("❌ Invariant Path launcher not found: #{ip_launcher}") @@ -298,96 +469,19 @@ defmodule ScriptManager.TUI do end end - defp show_help do - IO.puts("\n" <> String.duplicate("=", 60)) - IO.puts("HELP SYSTEM - Detailed Function Information") - IO.puts(String.duplicate("=", 60)) - - help_items = Map.merge(%{ - "1" => { - "Wiki Audit", - "Audits GitHub wiki status across repositories", - "Checks wiki enabled/disabled, content status, page count", - "Use to identify repos needing wiki setup or cleanup" - }, - "2" => { - "Project Tabs Audit", - "Audits repository project tabs/configuration", - "Checks for proper tab setup and configuration", - "Use to ensure consistent project organization" - }, - "3" => { - "Branch Protection Apply", - "Applies strict branch protection rules to repositories", - "Enforces signed commits, linear history, blocks force pushes", - "WARNING: Modifies repository settings - use with caution" - }, - "4" => { - "MD to ADOC Converter", - "Converts Markdown files to AsciiDoc format", - "Preserves formatting and metadata", - "Use for documentation standardization" - }, - "5" => { - "Standardize READMEs", - "Applies consistent README formatting across repositories", - "Ensures proper structure and content", - "Use to maintain documentation standards" - }, - "6" => { - "Update Repos", - "Updates all repositories to latest versions", - "Pulls latest changes and updates dependencies", - "Use to keep repositories synchronized" - }, - "7" => { - "Audit Scripts", - "Audits the script collection for issues", - "Checks for syntax errors, best practices, security issues", - "Use to maintain script quality" - }, - "8" => { - "Verify", - "Verifies system and repository health", - "Checks for common issues and configuration problems", - "Use for troubleshooting and maintenance" - }, - "9" => { - "Use GH CLI", - "GitHub CLI helper functions", - "Provides convenient GitHub operations", - "Use for GitHub repository management" - }, - "10" => { - "Mass PR Processor", - "Processes pull requests across repositories", - "Can add labels, review, or perform other batch operations", - "Use for bulk PR management" - }}, - %{ - "22" => { - "Launch NQC", - "Launches NextGen Query Client for database operations", - "Provides web interface for VQL/GQL/KQL queries", - "Use for database exploration and querying" - }, - "23" => { - "Launch Invariant Path", - "Launches Invariant Path for code analysis", - "Analyzes claim transitions and invariant preservation", - "Use for code documentation and invariant checking" - } - }) - - Enum.each(help_items, fn {num, {name, desc, details, usage}} -> - IO.puts("\n[#{num}] #{name}") - IO.puts(" Description: #{desc}") - IO.puts(" Details: #{details}") - IO.puts(" Usage: #{usage}") - end) - - IO.puts("\n" <> String.duplicate("-", 60)) - IO.puts("Press Enter to return to main menu...") - IO.gets("") + # ---------------------------------------------------------------------- + # Input helper + # ---------------------------------------------------------------------- + + defp read_choice do + try do + case IO.gets("") do + nil -> "0" + :eof -> "0" + input -> String.trim(input) + end + rescue + _ -> "0" + end end -end \ No newline at end of file +end diff --git a/scripts/audit_script.sh b/scripts/audit_script.sh index 1d81579..8b58dec 100755 --- a/scripts/audit_script.sh +++ b/scripts/audit_script.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -uo pipefail TOKEN="${GITHUB_TOKEN:-}" if [ -z "$TOKEN" ]; then echo "GITHUB_TOKEN is required" >&2 @@ -8,33 +9,47 @@ REPOS_DIR="${REPOS_DIR:-/var/mnt/eclipse/repos}" CONFIG_FILE="$REPOS_DIR/gitleaks_config.toml" GLOBAL_IGNORE="$REPOS_DIR/global_gitleaksignore" +# --- Ownership safety guard --- +_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=lib/ownership_guard.sh +source "${_SCRIPT_DIR}/lib/ownership_guard.sh" + # Create global ignore from boj-server cp "$REPOS_DIR/boj-server/.gitleaksignore" "$GLOBAL_IGNORE" 2>/dev/null || touch "$GLOBAL_IGNORE" -echo "| Repo | Gitleaks Findings | Dependabot Alerts (Crit/High) | Status |" -echo "| --- | --- | --- | --- |" +echo "| Repo | Owner | Gitleaks Findings | Dependabot Alerts (Crit/High) | Status |" +echo "| --- | --- | --- | --- | --- |" # Filter directories and iterate for repo_path in "$REPOS_DIR"/*/; do repo_name=$(basename "$repo_path") - + [ -d "$repo_path" ] || continue [ "$repo_name" = ".git" ] && continue [ "$repo_name" = ".gemini" ] && continue [ "$repo_name" = ".claude" ] && continue [ "$repo_name" = "scripts" ] && continue [ "$repo_name" = "audit_script.sh" ] && continue - + + # --- Ownership filter --- + # Determine the owner from the repo's origin remote and skip anything + # outside the configured allowlist. + repo_owner="$(repo_owner_from_remote "$repo_path" 2>/dev/null || true)" + if [ -z "${repo_owner}" ] || ! owner_allowed "${repo_owner}"; then + echo "| $repo_name | ${repo_owner:-unknown} | - | - | SKIPPED (owner not allowed) |" + continue + fi + # Gitleaks Scan REPORT_FILE=$(mktemp) # Using the user requested flags: --source . --no-git --verbose # We add config and ignore path gitleaks detect --source "$repo_path" --no-git --config "$CONFIG_FILE" --gitleaks-ignore-path "$GLOBAL_IGNORE" --report-path "$REPORT_FILE" --report-format json > /dev/null 2>&1 GITLEAKS_COUNT=$(grep -c "Fingerprint" "$REPORT_FILE" || echo 0) - - # Dependabot Audit + + # Dependabot Audit (uses the actual owner derived from the repo) ENCODED_NAME=$(echo "$repo_name" | sed 's/ /%20/g') - ALERTS_JSON=$(curl -s -H "Authorization: token $TOKEN" "https://api.github.com/repos/hyperpolymath/$ENCODED_NAME/dependabot/alerts?state=open") + ALERTS_JSON=$(curl -s -H "Authorization: token $TOKEN" "https://api.github.com/repos/${repo_owner}/$ENCODED_NAME/dependabot/alerts?state=open") if echo "$ALERTS_JSON" | jq -e '.message == "Not Found" or .message == "Moved Permanently" or .message == "Bad credentials"' > /dev/null 2>&1 || [ -z "$ALERTS_JSON" ]; then DEPENDABOT_COUNT="N/A" @@ -59,7 +74,7 @@ for repo_path in "$REPOS_DIR"/*/; do fi fi - echo "| $repo_name | $GITLEAKS_COUNT | $DEPENDABOT_COUNT | $STATUS |" - + echo "| $repo_name | $repo_owner | $GITLEAKS_COUNT | $DEPENDABOT_COUNT | $STATUS |" + rm "$REPORT_FILE" done diff --git a/scripts/branch-protection-apply.sh b/scripts/branch-protection-apply.sh index a354ea9..2cd2b56 100755 --- a/scripts/branch-protection-apply.sh +++ b/scripts/branch-protection-apply.sh @@ -34,6 +34,15 @@ RULESET_NAME="Base" DRY_RUN=false LIMIT=600 +# --------------------------------------------------------------------------- +# Ownership safety guard +# --------------------------------------------------------------------------- + +_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=lib/ownership_guard.sh +source "${_SCRIPT_DIR}/lib/ownership_guard.sh" +assert_owner_allowed "${OWNER}" + if [[ "${1:-}" == "--dry-run" ]]; then DRY_RUN=true echo "[DRY RUN] No changes will be made." diff --git a/scripts/lib/ownership_guard.sh b/scripts/lib/ownership_guard.sh new file mode 100644 index 0000000..acc5c4c --- /dev/null +++ b/scripts/lib/ownership_guard.sh @@ -0,0 +1,135 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: PMPL-1.0-or-later +# +# ownership_guard.sh β€” refuse to operate on repositories owned by anyone +# outside the configured allowlist. Source this from any script that +# touches GitHub or pushes to remotes. +# +# Public functions: +# owner_allowed β€” return 0 if allowed, 1 otherwise +# assert_owner_allowed β€” exit 78 if owner is not allowed +# repo_owner_from_remote β€” print the GitHub owner of a local repo +# repo_allowed β€” return 0 if a local repo's owner is allowed +# +# Configuration is loaded from the first existing file: +# $(dirname this)/../../config/owners.config +# /var/mnt/eclipse/repos/git-scripts/config/owners.config +# falling back to a hard-coded ["hyperpolymath"]. + +# Idempotent: only load once per shell. +if [[ "${_OWNERSHIP_GUARD_LOADED:-0}" == "1" ]]; then + return 0 2>/dev/null || true +fi +_OWNERSHIP_GUARD_LOADED=1 + +_GUARD_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +_OWNERS_CONFIG_CANDIDATES=( + "${_GUARD_DIR}/../../config/owners.config" + "/var/mnt/eclipse/repos/git-scripts/config/owners.config" +) + +_loaded_owners_config="" +for _candidate in "${_OWNERS_CONFIG_CANDIDATES[@]}"; do + if [[ -f "${_candidate}" ]]; then + # shellcheck disable=SC1090 + source "${_candidate}" + _loaded_owners_config="${_candidate}" + break + fi +done + +if [[ -z "${_loaded_owners_config}" ]]; then + ALLOWED_OWNERS=("hyperpolymath") +fi + +# Lowercase a string (portable; no `${var,,}` to keep bash 3 compat). +_lc() { + printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]' +} + +# Return 0 if $1 is in ALLOWED_OWNERS (case-insensitive). +owner_allowed() { + local needle + needle="$(_lc "${1:-}")" + [[ -z "${needle}" ]] && return 1 + local allowed + for allowed in "${ALLOWED_OWNERS[@]}"; do + if [[ "${needle}" == "$(_lc "${allowed}")" ]]; then + return 0 + fi + done + return 1 +} + +# Print the owner of a local git repo, derived from `origin` URL. +# Host-agnostic: works for GitHub, GitLab, Bitbucket, Gitea, codeberg, +# self-hosted servers, and SSH-style URLs. The owner is taken as the +# second-to-last path segment (after stripping a trailing .git). +# Returns 1 (and prints nothing) if no owner can be parsed. +repo_owner_from_remote() { + local repo_path="${1:-.}" + local url + url=$(git -C "${repo_path}" config --get remote.origin.url 2>/dev/null) || return 1 + [[ -z "${url}" ]] && return 1 + + # Strip a trailing .git for clean splitting. + url="${url%.git}" + + local path_part="" + + if [[ "${url}" =~ ^[^[:space:]/@]+@[^:]+:(.+)$ ]]; then + # SSH-style: [user@]host:path + path_part="${BASH_REMATCH[1]}" + elif [[ "${url}" =~ ^[a-zA-Z]+://[^/]+(/.+)$ ]]; then + # URL-style: proto://[creds@]host[:port]/path + path_part="${BASH_REMATCH[1]}" + else + return 1 + fi + + # Trim leading/trailing slashes, then take the segment before the last. + path_part="${path_part#/}" + path_part="${path_part%/}" + [[ -z "${path_part}" ]] && return 1 + + local owner_dir owner + owner_dir="$(dirname "${path_part}")" + [[ "${owner_dir}" == "." || "${owner_dir}" == "/" ]] && return 1 + + owner="$(basename "${owner_dir}")" + [[ -z "${owner}" ]] && return 1 + + printf '%s\n' "${owner}" +} + +# Soft check: returns 0 if the local repo's owner is allowed. +repo_allowed() { + local owner + owner="$(repo_owner_from_remote "${1:-.}")" || return 1 + owner_allowed "${owner}" +} + +# Hard guard: print an explanation and exit if the owner is not allowed. +# Use at the top of any script that targets a single org/user. +assert_owner_allowed() { + local owner="${1:-}" + if owner_allowed "${owner}"; then + return 0 + fi + { + echo "" + echo "❌ REFUSING to operate on owner '${owner}'." + echo " This owner is not in the allowlist for git-scripts." + echo " Allowed owners: ${ALLOWED_OWNERS[*]}" + echo "" + echo " To allow it, edit:" + if [[ -n "${_loaded_owners_config}" ]]; then + echo " ${_loaded_owners_config}" + else + echo " config/owners.config" + fi + echo " …or set GIT_SCRIPTS_ALLOWED_OWNERS=\"owner1 owner2\" in the environment." + echo "" + } >&2 + exit 78 # EX_CONFIG +} diff --git a/scripts/md_to_adoc_converter.sh b/scripts/md_to_adoc_converter.sh index a2a3418..10ac605 100755 --- a/scripts/md_to_adoc_converter.sh +++ b/scripts/md_to_adoc_converter.sh @@ -3,6 +3,11 @@ # Simple Markdown to Asciidoc Converter # Handles basic markdown elements for README conversion +# --- Ownership safety guard --- +_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=lib/ownership_guard.sh +source "${_SCRIPT_DIR}/lib/ownership_guard.sh" + convert_markdown_to_adoc() { local md_file="$1" local adoc_file="$2" @@ -33,10 +38,18 @@ REPOS_DIR="${REPOS_DIR:-/var/mnt/eclipse/repos}" find "$REPOS_DIR" -maxdepth 2 -name "README.md" -type f | while read md_file; do repo_dir=$(dirname "$md_file") adoc_file="$repo_dir/README.adoc" - + + # --- Ownership filter (only convert files in repos we own) --- + if [[ -d "$repo_dir/.git" ]]; then + if ! repo_allowed "$repo_dir"; then + echo "Skipping: $md_file (owner not in allowlist)" + continue + fi + fi + echo "Converting: $md_file" convert_markdown_to_adoc "$md_file" "$adoc_file" - + # Remove the original markdown file rm "$md_file" echo " β†’ Created: $adoc_file" diff --git a/scripts/project-tabs-audit.sh b/scripts/project-tabs-audit.sh index 99ede55..7aac499 100755 --- a/scripts/project-tabs-audit.sh +++ b/scripts/project-tabs-audit.sh @@ -24,6 +24,15 @@ set -euo pipefail OWNER="hyperpolymath" +# --------------------------------------------------------------------------- +# Ownership safety guard +# --------------------------------------------------------------------------- + +_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=lib/ownership_guard.sh +source "${_SCRIPT_DIR}/lib/ownership_guard.sh" +assert_owner_allowed "${OWNER}" + # Mandatory topics β€” every repo SHOULD have these MANDATORY_TOPICS=("hyperpolymath" "palimpsest") diff --git a/scripts/standardize_readmes.sh b/scripts/standardize_readmes.sh index 5330dc1..2fe1f15 100644 --- a/scripts/standardize_readmes.sh +++ b/scripts/standardize_readmes.sh @@ -7,6 +7,11 @@ REPOS_DIR="${REPOS_DIR:-/var/mnt/eclipse/repos}" LOG_FILE="$HOME/Desktop/readme_standardization.log" BACKUP_DIR="$HOME/Desktop/readme_backups" +# --- Ownership safety guard --- +_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=lib/ownership_guard.sh +source "${_SCRIPT_DIR}/lib/ownership_guard.sh" + if ! command -v pandoc >/dev/null 2>&1; then echo "Error: pandoc is not installed. Please install it to use this script." exit 1 @@ -36,8 +41,15 @@ find "$REPOS_DIR"/*/ -maxdepth 0 -type d | while read repo; do if [[ ! -d "$repo/.git" ]]; then continue fi - + repo_name=$(basename "$repo") + + # --- Ownership filter --- + if ! repo_allowed "$repo"; then + echo "Skipping: $repo_name (owner not in allowlist)" >> "$LOG_FILE" + continue + fi + echo "Processing: $repo_name" >> "$LOG_FILE" # Check what README files exist diff --git a/scripts/update_repos.sh b/scripts/update_repos.sh index 07a225b..9c07c67 100755 --- a/scripts/update_repos.sh +++ b/scripts/update_repos.sh @@ -8,6 +8,11 @@ else BASE_DIR="${REPOS_DIR:-/var/mnt/eclipse/repos}" fi +# --- Ownership safety guard --- +_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=lib/ownership_guard.sh +source "${_SCRIPT_DIR}/lib/ownership_guard.sh" + if [[ -z "${REPOS:-}" ]]; then echo "Warning: REPOS list is empty or not loaded." # Attempt to find all repos if list is empty @@ -15,17 +20,31 @@ if [[ -z "${REPOS:-}" ]]; then fi FAILURES=() +SKIPPED_OWNERSHIP=() for REPO in "${REPOS[@]}"; do REPO_PATH="$BASE_DIR/$REPO" echo "Processing $REPO..." - + if [ ! -d "$REPO_PATH/.git" ]; then echo "Error: $REPO_PATH is not a git repository." FAILURES+=("$REPO (not a git repo)") continue fi - + + # --- Per-repo ownership filter (refuse to push to foreign owners) --- + repo_owner="$(repo_owner_from_remote "$REPO_PATH" 2>/dev/null || true)" + if [ -z "${repo_owner}" ]; then + echo "Skipping $REPO: no GitHub origin remote (cannot verify owner)." + SKIPPED_OWNERSHIP+=("$REPO (no github origin)") + continue + fi + if ! owner_allowed "${repo_owner}"; then + echo "Skipping $REPO: owner '${repo_owner}' is not in the allowlist." + SKIPPED_OWNERSHIP+=("$REPO (owner=${repo_owner})") + continue + fi + cd "$REPO_PATH" || continue # 0. Sync from remote first (Hiccup prevention) @@ -96,3 +115,11 @@ echo "Persistent Failures:" for FAIL in "${FAILURES[@]}"; do echo "- $FAIL" done + +if [ "${#SKIPPED_OWNERSHIP[@]}" -gt 0 ]; then + echo "" + echo "Skipped (ownership guard):" + for SKIP in "${SKIPPED_OWNERSHIP[@]}"; do + echo "- $SKIP" + done +fi diff --git a/scripts/wiki-audit.sh b/scripts/wiki-audit.sh index f04e0e9..417f284 100755 --- a/scripts/wiki-audit.sh +++ b/scripts/wiki-audit.sh @@ -18,6 +18,12 @@ set -euo pipefail OWNER="hyperpolymath" TMPDIR="" +# --- Ownership safety guard --- +_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=lib/ownership_guard.sh +source "${_SCRIPT_DIR}/lib/ownership_guard.sh" +assert_owner_allowed "${OWNER}" + # Key repos to always check (subset for quick audits) KEY_REPOS=( boj-server proven echidna gossamer typed-wasm ephapax diff --git a/ui/package-lock.json b/ui/package-lock.json index c16a4b2..7fc32ac 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -15,7 +15,7 @@ "devDependencies": { "concurrently": "^9.0.1", "rescript": "^12.0.0", - "vite": "^5.4.10" + "vite": "^8.0.8" } }, "../../rescript-tea": { @@ -26,395 +26,67 @@ "rescript": "^12.0.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], + "node_modules/@emnapi/core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], + "node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" + "dependencies": { + "tslib": "^2.4.0" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" + "dependencies": { + "tslib": "^2.4.0" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], + "node_modules/@oxc-project/types": { + "version": "0.124.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz", + "integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" + "funding": { + "url": "https://github.com/sponsors/Boshen" } }, "node_modules/@rescript/core": { @@ -512,24 +184,10 @@ "node": ">=20.11.0" } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", - "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", - "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==", "cpu": [ "arm64" ], @@ -538,12 +196,15 @@ "optional": true, "os": [ "android" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", - "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==", "cpu": [ "arm64" ], @@ -552,12 +213,15 @@ "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", - "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz", + "integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==", "cpu": [ "x64" ], @@ -566,26 +230,15 @@ "optional": true, "os": [ "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", - "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", - "cpu": [ - "arm64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", - "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz", + "integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==", "cpu": [ "x64" ], @@ -594,26 +247,15 @@ "optional": true, "os": [ "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", - "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", - "cpu": [ - "arm" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", - "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz", + "integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==", "cpu": [ "arm" ], @@ -622,26 +264,15 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", - "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", - "cpu": [ - "arm64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", - "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==", "cpu": [ "arm64" ], @@ -650,54 +281,32 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", - "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", - "cpu": [ - "loong64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", - "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz", + "integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==", "cpu": [ - "loong64" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", - "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", - "cpu": [ - "ppc64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", - "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==", "cpu": [ "ppc64" ], @@ -706,40 +315,15 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", - "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", - "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", - "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==", "cpu": [ "s390x" ], @@ -748,12 +332,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", - "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==", "cpu": [ "x64" ], @@ -762,12 +349,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", - "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz", + "integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==", "cpu": [ "x64" ], @@ -776,26 +366,15 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", - "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", - "cpu": [ - "x64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", - "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==", "cpu": [ "arm64" ], @@ -804,54 +383,51 @@ "optional": true, "os": [ "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", - "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", - "cpu": [ - "arm64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", - "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz", + "integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==", "cpu": [ - "ia32" + "wasm32" ], "dev": true, "license": "MIT", "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "@emnapi/core": "1.9.2", + "@emnapi/runtime": "1.9.2", + "@napi-rs/wasm-runtime": "^1.1.3" + }, + "engines": { + "node": ">=14.0.0" + } }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", - "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz", + "integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==", "cpu": [ - "x64" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", - "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz", + "integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==", "cpu": [ "x64" ], @@ -860,15 +436,29 @@ "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz", + "integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==", "dev": true, "license": "MIT" }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -985,6 +575,16 @@ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -992,45 +592,6 @@ "dev": true, "license": "MIT" }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -1041,6 +602,24 @@ "node": ">=6" } }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1086,6 +665,267 @@ "node": ">=8" } }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -1112,6 +952,19 @@ "dev": true, "license": "ISC" }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/postcss": { "version": "8.5.9", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", @@ -1192,49 +1045,38 @@ "resolved": "../../rescript-tea", "link": true }, - "node_modules/rollup": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", - "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "node_modules/rolldown": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz", + "integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "@oxc-project/types": "=0.124.0", + "@rolldown/pluginutils": "1.0.0-rc.15" }, "bin": { - "rollup": "dist/bin/rollup" + "rolldown": "bin/cli.mjs" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.1", - "@rollup/rollup-android-arm64": "4.60.1", - "@rollup/rollup-darwin-arm64": "4.60.1", - "@rollup/rollup-darwin-x64": "4.60.1", - "@rollup/rollup-freebsd-arm64": "4.60.1", - "@rollup/rollup-freebsd-x64": "4.60.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", - "@rollup/rollup-linux-arm-musleabihf": "4.60.1", - "@rollup/rollup-linux-arm64-gnu": "4.60.1", - "@rollup/rollup-linux-arm64-musl": "4.60.1", - "@rollup/rollup-linux-loong64-gnu": "4.60.1", - "@rollup/rollup-linux-loong64-musl": "4.60.1", - "@rollup/rollup-linux-ppc64-gnu": "4.60.1", - "@rollup/rollup-linux-ppc64-musl": "4.60.1", - "@rollup/rollup-linux-riscv64-gnu": "4.60.1", - "@rollup/rollup-linux-riscv64-musl": "4.60.1", - "@rollup/rollup-linux-s390x-gnu": "4.60.1", - "@rollup/rollup-linux-x64-gnu": "4.60.1", - "@rollup/rollup-linux-x64-musl": "4.60.1", - "@rollup/rollup-openbsd-x64": "4.60.1", - "@rollup/rollup-openharmony-arm64": "4.60.1", - "@rollup/rollup-win32-arm64-msvc": "4.60.1", - "@rollup/rollup-win32-ia32-msvc": "4.60.1", - "@rollup/rollup-win32-x64-gnu": "4.60.1", - "@rollup/rollup-win32-x64-msvc": "4.60.1", - "fsevents": "~2.3.2" + "@rolldown/binding-android-arm64": "1.0.0-rc.15", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", + "@rolldown/binding-darwin-x64": "1.0.0-rc.15", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" } }, "node_modules/rxjs": { @@ -1314,6 +1156,23 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -1332,21 +1191,23 @@ "license": "0BSD" }, "node_modules/vite": { - "version": "5.4.21", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", - "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz", + "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.15", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -1355,23 +1216,33 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, - "less": { + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { "optional": true }, - "lightningcss": { + "less": { "optional": true }, "sass": { @@ -1388,6 +1259,12 @@ }, "terser": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } } }, diff --git a/ui/package.json b/ui/package.json index 002eb3a..31349e7 100644 --- a/ui/package.json +++ b/ui/package.json @@ -12,13 +12,13 @@ "preview": "vite preview" }, "dependencies": { - "@rescript/runtime": "^12.0.0", "@rescript/core": "^1.6.1", + "@rescript/runtime": "^12.0.0", "rescript-tea": "file:../../rescript-tea" }, "devDependencies": { "concurrently": "^9.0.1", "rescript": "^12.0.0", - "vite": "^5.4.10" + "vite": "^8.0.8" } }