chore: bazelrc presets (#3865)

diff --git a/.aspect/workflows/config.yaml b/.aspect/workflows/config.yaml
index b3fca42..9ff3138 100644
--- a/.aspect/workflows/config.yaml
+++ b/.aspect/workflows/config.yaml
@@ -1,5 +1,7 @@
 # See https://docs.aspect.build/workflows/configuration
 tasks:
   - test:
+    flags:
+      - --config=ci
 notifications:
   github: {}
diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml
index 9e49c0c..97bb93c 100644
--- a/.bazelci/presubmit.yml
+++ b/.bazelci/presubmit.yml
@@ -1,5 +1,5 @@
 ---
-bazel: 7.1.1
+bazel: 7.3.1
 # Note, this will tell the user to do the wrong thing (manually run buildifer)
 # See https://github.com/bazelbuild/continuous-integration/issues/1161
 buildifier:
diff --git a/.bazelrc b/.bazelrc
index 05a1149..8685e03 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -1,44 +1,8 @@
-# TODO: migrate all root WORKSPACE dependencies to MODULE.bazel
-# https://github.com/bazel-contrib/rules_nodejs/issues/3695
-common --noenable_bzlmod
-common --enable_workspace
+import %workspace%/tools/preset.bazelrc
 
-# Specifies desired output mode for running tests.
-# Valid values are
-#   'summary' to output only test status summary
-#   'errors' to also print test logs for failed tests
-#   'all' to print logs for all tests
-#   'streamed' to output logs for all tests in real time
-#     (this will force tests to be executed locally one at a time regardless of --test_strategy value).
-common --test_output=errors
-
-# Turn on --incompatible_strict_action_env which was on by default
-# in Bazel 0.21.0 but turned off again in 0.22.0. Follow
-# https://github.com/bazelbuild/bazel/issues/7026 for more details.
-# This flag is needed to so that the bazel cache is not invalidated
-# when running bazel via `yarn bazel`.
-# See https://github.com/angular/angular/issues/27514.
-common --incompatible_strict_action_env
-
-# Turn off legacy external runfiles
-# This prevents accidentally depending on this feature, which Bazel will remove.
-common --nolegacy_external_runfiles
-
-# Don’t want to push a rules author to update their deps if not needed.
-# https://bazel.build/reference/command-line-reference#flag--check_direct_dependencies
-# https://bazelbuild.slack.com/archives/C014RARENH0/p1691158021917459?thread_ts=1691156601.420349&cid=C014RARENH0
-common --check_direct_dependencies=off
+common --config=ruleset
 
 # In the root MODULE.bazel file we don't set include_headers on the nodejs toolchain
 # so the `//nodejs/headers:current_node_cc_headers`` target will not build. This target
 # is instead tested in `e2e/headers``
 common --deleted_packages=nodejs/headers
-
-# Load any settings specific to the current user.
-# .bazelrc.user should appear in .gitignore so that settings are not shared with team members
-# This needs to be last statement in this
-# config, as the user configuration should be able to overwrite flags from this file.
-# See https://docs.bazel.build/versions/master/best-practices.html#bazelrc
-# (Note that we use .bazelrc.user so the file appears next to .bazelrc in directory listing,
-# rather than user.bazelrc as suggested in the Bazel docs)
-try-import %workspace%/.bazelrc.user
diff --git a/MODULE.bazel b/MODULE.bazel
index 37314a2..e7fba8f 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -8,6 +8,9 @@
 # Lower-bounds (minimum) versions for direct runtime dependencies
 bazel_dep(name = "bazel_lib", version = "3.0.0-beta.1")
 bazel_dep(name = "bazel_skylib", version = "1.4.1")
+
+bazel_dep(name = "bazelrc-preset.bzl", version = "1.2.0", dev_dependency = True)
+
 bazel_dep(name = "platforms", version = "0.0.5")
 
 # workaround for https://github.com/bazelbuild/bazel/issues/25124
@@ -17,7 +20,9 @@
 
 # Note, this gets the default version of Node.js from
 # https://github.com/bazel-contrib/rules_nodejs/blob/main/nodejs/repositories.bzl#L11
-node.toolchain()
+node.toolchain(
+    node_version = "16.5.0",
+)
 use_repo(node, "nodejs_toolchains")
 
 # Toolchain registration under bzlmod should match the order of WORKSPACE registration
diff --git a/tools/BUILD b/tools/BUILD
new file mode 100644
index 0000000..f77de08
--- /dev/null
+++ b/tools/BUILD
@@ -0,0 +1,6 @@
+load("@bazelrc-preset.bzl", "bazelrc_preset")
+
+bazelrc_preset(
+    name = "preset",
+    strict = True,  # Enable this to opt-in to flags that are flipped in the upcoming major release
+)
diff --git a/tools/preset.bazelrc b/tools/preset.bazelrc
new file mode 100644
index 0000000..249448f
--- /dev/null
+++ b/tools/preset.bazelrc
@@ -0,0 +1,237 @@
+# Generated by bazelrc-preset.bzl
+# To update this file, run:
+#   bazel run @@//tools:preset.update
+
+# On CI, announce all announces command options read from the bazelrc file(s) when starting up at the
+# beginning of each Bazel invocation. This is very useful on CI to be able to inspect which flags
+# are being applied on each run based on the order of overrides.
+common:ci --announce_rc
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=announce_rc
+
+# Avoid creating a runfiles tree for binaries or tests until it is needed.
+# See https://github.com/bazelbuild/bazel/issues/6627
+# This may break local workflows that `build` a binary target, then run the resulting program outside of `bazel run`.
+# In those cases, the script will need to call `bazel build --build_runfile_links //my/binary:target` and then execute the resulting program.
+common --nobuild_runfile_links
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=build_runfile_links
+
+# See https://github.com/bazelbuild/bazel/issues/20577
+coverage --build_runfile_links
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=build_runfile_links
+
+# Always run tests even if they have cached results.
+# This ensures tests are executed fresh each time, useful for debugging and ensuring test reliability.
+common:debug --nocache_test_results
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=cache_test_results
+
+# Don’t encourage a rules author to update their deps if not needed.
+# These bazel_dep calls should indicate the minimum version constraint of the ruleset.
+# If the author instead updates to the newest of any of their transitives, as this flag would suggest,
+# then they'll also force their dependents to a newer version.
+# Context:
+# https://bazelbuild.slack.com/archives/C014RARENH0/p1691158021917459?thread_ts=1691156601.420349&cid=C014RARENH0
+common:ruleset --check_direct_dependencies="off"
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=check_direct_dependencies
+
+# On CI, use colors to highlight output on the screen. Set to `no` if your CI does not display colors.
+common:ci --color="yes"
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=color
+
+# On CI, use cursor controls in screen output.
+common:ci --curses="yes"
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=curses
+
+# Bazel picks up host-OS-specific config lines from bazelrc files. For example, if the host OS is
+# Linux and you run bazel build, Bazel picks up lines starting with build:linux. Supported OS
+# identifiers are `linux`, `macos`, `windows`, `freebsd`, and `openbsd`. Enabling this flag is
+# equivalent to using `--config=linux` on Linux, `--config=windows` on Windows, etc.
+common --enable_platform_specific_config
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=enable_platform_specific_config
+
+# Speed up all builds by not checking if external repository files have been modified.
+# For reference: https://github.com/bazelbuild/bazel/blob/1af61b21df99edc2fc66939cdf14449c2661f873/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryOptions.java#L244
+common --noexperimental_check_external_repository_files
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=experimental_check_external_repository_files
+
+# Always download coverage files for tests from the remote cache. By default, coverage files are not
+# downloaded on test result cache hits when --remote_download_minimal is enabled, making it impossible
+# to generate a full coverage report.
+common --experimental_fetch_all_coverage_outputs
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=experimental_fetch_all_coverage_outputs
+
+# This flag was added in Bazel 6.2.0 with a default of zero:
+# https://github.com/bazelbuild/bazel/commit/24b45890c431de98d586fdfe5777031612049135
+# For Bazel 8.0.0rc1 the default was changed to 5:
+# https://github.com/bazelbuild/bazel/commit/739e37de66f4913bec1a55b2f2a162e7db6f2d0f
+# Back-port the updated flag default value to older Bazel versions.
+common --experimental_remote_cache_eviction_retries=5
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=experimental_remote_cache_eviction_retries
+
+# This flag was added in Bazel 5.0.0 with a default of zero:
+# https://github.com/bazelbuild/bazel/commit/a1137ec1338d9549fd34a9a74502ffa58c286a8e
+# For Bazel 8.0.0 the default was changed to 5:
+# https://github.com/bazelbuild/bazel/commit/9335cf989ee6a678ca10bc4da72214634cef0a57
+# Back-port the updated flag default value to older Bazel versions.
+common --experimental_repository_downloader_retries=5
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=experimental_repository_downloader_retries
+
+# Set this flag to enable re-tries of failed tests on CI.
+# When any test target fails, try one or more times. This applies regardless of whether the "flaky"
+# tag appears on the target definition.
+# This is a tradeoff: legitimately failing tests will take longer to report,
+# but we can "paper over" flaky tests that pass most of the time.
+#
+# An alternative is to mark every flaky test with the `flaky = True` attribute, but this requires
+# the buildcop to make frequent code edits.
+# This flag is not recommended for local builds: flakiness is more likely to get fixed if it is
+# observed during development.
+#
+# Note that when passing after the first attempt, Bazel will give a special "FLAKY" status rather than "PASSED".
+test:ci --flaky_test_attempts=2
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=flaky_test_attempts
+
+# Fixes builds hanging on CI that get the TCP connection closed without sending RST packets.
+common:ci --grpc_keepalive_time="30s"
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=grpc_keepalive_time
+
+# Output a heap dump if an OOM is thrown during a Bazel invocation
+# (including OOMs due to `--experimental_oom_more_eagerly_threshold`).
+# The dump will be written to `<output_base>/<invocation_id>.heapdump.hprof`.
+# You should configure CI to upload this artifact for later inspection.
+common --heap_dump_on_oom
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=heap_dump_on_oom
+
+# Allow the Bazel server to check directory sources for changes. Ensures that the Bazel server
+# notices when a directory changes, if you have a directory listed in the srcs of some target.
+# Recommended when using [copy_directory](https://github.com/bazel-contrib/bazel-lib/blob/main/docs/copy_directory.md)
+# and [rules_js](https://github.com/aspect-build/rules_js) since npm package are source directories inputs to copy_directory actions.
+startup --host_jvm_args="-DBAZEL_TRACK_SOURCE_DIRECTORIES=1"
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=host_jvm_args
+
+# By default, Bazel automatically creates __init__.py files for py_binary and py_test targets.
+# From https://github.com/bazelbuild/bazel/issues/10076:
+# > It is magic at a distance.
+# > Python programmers are already used to creating __init__.py files in their source trees,
+# > so doing it behind their backs introduces confusion and changes the semantics of imports
+common --incompatible_default_to_explicit_init_py
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=incompatible_default_to_explicit_init_py
+
+# Disallow empty glob patterns.
+# The glob() function tends to be error-prone, because any typo in a path will silently return an empty list.
+# This flag was added in Bazel 0.27 and flipped in Bazel 8: https://github.com/bazelbuild/bazel/issues/8195
+common --incompatible_disallow_empty_glob
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=incompatible_disallow_empty_glob
+
+# Make builds more reproducible by using a static value for PATH and not inheriting LD_LIBRARY_PATH.
+# Use `--action_env=ENV_VARIABLE` if you want to inherit specific variables from the environment where Bazel runs.
+# Note that doing so can prevent cross-user caching if a shared cache is used.
+# See https://github.com/bazelbuild/bazel/issues/2574 for more details.
+common --incompatible_strict_action_env
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=incompatible_strict_action_env
+
+# Performance improvement: avoid laying out a second copy of the runfiles tree.
+# See https://github.com/bazelbuild/bazel/issues/23574.
+# This flag was flipped for Bazel 8.
+common --nolegacy_external_runfiles
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=legacy_external_runfiles
+
+# On CI, don't download remote outputs to the local machine.
+# Most CI pipelines don't need to access the files and they can remain at rest on the remote cache.
+# Significant time can be spent on needless downloads, which is especially noticeable on fully-cached builds.
+#
+# If you do need to download files, the fastest options are:
+# - (preferred) Use `remote_download_regex` to specify the files to download.
+# - Use the Remote Output Service (https://blog.bazel.build/2024/07/23/remote-output-service.html)
+# to lazy-materialize specific files after the build completes.
+# - Perform a second bazel command with specific targets and override this flag with the `toplevel` value.
+# - To copy executable targets, you can use `bazel run --run_under=cp //some:binary_target <destination path>`.
+common:ci --remote_download_outputs="minimal"
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=remote_download_outputs
+
+# On CI, fall back to standalone local execution strategy if remote execution fails.
+# Otherwise, when a grpc remote cache connection fails, it would fail the build.
+common:ci --remote_local_fallback
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=remote_local_fallback
+
+# On CI, extend the maximum amount of time to wait for remote execution and cache calls.
+common:ci --remote_timeout=3600
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=remote_timeout
+
+# Do not upload locally executed action results to the remote cache.
+# This should be the default for local builds so local builds cannot poison the remote cache.
+#
+# Note that this flag is flipped to True under --config=ci, see below.
+common --noremote_upload_local_results
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=remote_upload_local_results
+
+# On CI, upload locally executed action results to the remote cache.
+common:ci --remote_upload_local_results
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=remote_upload_local_results
+
+# Repository rules, such as rules_jvm_external: put Bazel's JDK on the path.
+# Avoids non-hermeticity from dependency on a JAVA_HOME pointing at a system JDK
+# see https://github.com/bazelbuild/rules_jvm_external/issues/445
+common --repo_env="JAVA_HOME=../bazel_tools/jdk"
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=repo_env
+
+# Reuse sandbox directories between invocations.
+# Directories used by sandboxed non-worker execution may be reused to avoid unnecessary setup costs.
+# Saves time on sandbox creation and deletion when many of the same kind of action is spawned during the build.
+common --reuse_sandbox_directories
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=reuse_sandbox_directories
+
+# Don't allow network access for build actions in the sandbox by default.
+# Avoids accidental non-hermeticity in actions/tests which depend on remote services.
+# Developers should tag targets with `tags=["requires-network"]` to be explicit that they need network access.
+# Note that the sandbox cannot print a message to the console if it denies network access,
+# so failures under this flag appear as application errors in the networking layer.
+common --nosandbox_default_allow_network
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=sandbox_default_allow_network
+
+# Only show progress every 60 seconds on CI.
+# We want to find a compromise between printing often enough to show that the build isn't stuck,
+# but not so often that we produce a long log file that requires a lot of scrolling.
+common:ci --show_progress_rate_limit=60
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=show_progress_rate_limit
+
+# The printed files are convenient strings for copy+pasting to the shell, to execute them.
+# This option requires an integer argument, which is the threshold number of targets above which result information is not printed.
+# Show the output files created by builds that requested more than one target.
+# This helps users locate the build outputs in more cases.
+common --show_result=20
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=show_result
+
+# On CI, add a timestamp to each message generated by Bazel specifying the time at which the message was displayed.
+# This makes it easier to reason about what were the slowest steps on CI.
+common:ci --show_timestamps
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=show_timestamps
+
+# The terminal width in columns. Configure this to override the default value based on what your CI system renders.
+common:ci --terminal_columns=143
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=terminal_columns
+
+# Output test errors to stderr so users don't have to `cat` or open test failure log files when test fail.
+# This makes the log noisier in exchange for reducing the time-to-feedback on test failures for users.
+common --test_output="errors"
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=test_output
+
+# Stream stdout/stderr output from each test in real-time.
+# This provides immediate feedback during test execution, useful for debugging test failures.
+common:debug --test_output="streamed"
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=test_output
+
+# Run one test at a time in exclusive mode.
+# This prevents test interference and provides clearer output when debugging test issues.
+common:debug --test_strategy="exclusive"
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=test_strategy
+
+# The default test_summary ("short") prints a result for every test target that was executed.
+# In a large repo this amounts to hundreds of lines of additional log output when testing a broad wildcard pattern like //...
+# This value means to print information only about unsuccessful tests that were run.
+test:ci --test_summary="terse"
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=test_summary
+
+# Prevent long running tests from timing out.
+# Set to a high value to allow tests to complete even if they take longer than expected.
+common:debug --test_timeout=9999
+# Docs: https://registry.build/flag/bazel@7.3.1?filter=test_timeout