diff --git a/.github/workflows/polyglot-validation/Dockerfile.java b/.github/workflows/polyglot-validation/Dockerfile.java
index 911912a9ae2..b62e3d73c76 100644
--- a/.github/workflows/polyglot-validation/Dockerfile.java
+++ b/.github/workflows/polyglot-validation/Dockerfile.java
@@ -10,7 +10,7 @@
#
# Note: Expects self-extracting binary and NuGet artifacts to be pre-downloaded to /workspace/artifacts/
#
-FROM mcr.microsoft.com/devcontainers/java:17
+FROM mcr.microsoft.com/devcontainers/java:25-trixie
# Ensure Yarn APT repository signing key is available (base image includes Yarn repo)
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | sudo tee /etc/apt/keyrings/yarn-archive-keyring.gpg > /dev/null
@@ -30,7 +30,8 @@
COPY setup-local-cli.sh /scripts/setup-local-cli.sh
COPY test-java.sh /scripts/test-java.sh
-RUN chmod +x /scripts/setup-local-cli.sh /scripts/test-java.sh
+COPY test-java-playground.sh /scripts/test-java-playground.sh
+RUN chmod +x /scripts/setup-local-cli.sh /scripts/test-java.sh /scripts/test-java-playground.sh
# Entrypoint: Set up Aspire CLI and run validation
# Bundle extraction happens lazily on first command that needs the layout
@@ -40,5 +41,6 @@
aspire --nologo config set features:experimentalPolyglot:java true --global && \
echo '' && \
echo '=== Running validation ===' && \
- /scripts/test-java.sh \
+ /scripts/test-java.sh && \
+ /scripts/test-java-playground.sh \
"]
diff --git a/.github/workflows/polyglot-validation/test-java-playground.sh b/.github/workflows/polyglot-validation/test-java-playground.sh
new file mode 100644
index 00000000000..f98571c7a9e
--- /dev/null
+++ b/.github/workflows/polyglot-validation/test-java-playground.sh
@@ -0,0 +1,118 @@
+#!/bin/bash
+# Polyglot SDK Validation - Java Playground Apps
+# Iterates all Java playground apps under playground/polyglot/Java/,
+# runs 'aspire restore' to regenerate the .modules/ SDK, and compiles the
+# compact AppHost plus generated Java SDK sources to verify there are no
+# regressions in the codegen API surface.
+set -euo pipefail
+
+echo "=== Java Playground Codegen Validation ==="
+
+if ! command -v aspire &> /dev/null; then
+ echo "❌ Aspire CLI not found in PATH"
+ exit 1
+fi
+
+if ! command -v javac &> /dev/null; then
+ echo "❌ javac not found in PATH (JDK required)"
+ exit 1
+fi
+
+echo "Aspire CLI version:"
+aspire --version
+
+echo "javac version:"
+javac -version
+
+SCRIPT_SOURCE="${BASH_SOURCE[0]:-$0}"
+SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_SOURCE")" && pwd)"
+if [ -d "/workspace/playground/polyglot/Java" ]; then
+ PLAYGROUND_ROOT="/workspace/playground/polyglot/Java"
+elif [ -d "$PWD/playground/polyglot/Java" ]; then
+ PLAYGROUND_ROOT="$(cd "$PWD/playground/polyglot/Java" && pwd)"
+elif [ -d "$SCRIPT_DIR/../../../playground/polyglot/Java" ]; then
+ PLAYGROUND_ROOT="$(cd "$SCRIPT_DIR/../../../playground/polyglot/Java" && pwd)"
+else
+ echo "❌ Cannot find playground/polyglot/Java directory"
+ exit 1
+fi
+
+echo "Playground root: $PLAYGROUND_ROOT"
+
+APP_DIRS=()
+for integration_dir in "$PLAYGROUND_ROOT"/*/; do
+ if [ -f "$integration_dir/ValidationAppHost/AppHost.java" ]; then
+ APP_DIRS+=("$integration_dir/ValidationAppHost")
+ fi
+done
+
+if [ ${#APP_DIRS[@]} -eq 0 ]; then
+ echo "❌ No Java playground apps found"
+ exit 1
+fi
+
+echo "Found ${#APP_DIRS[@]} Java playground apps:"
+for dir in "${APP_DIRS[@]}"; do
+ echo " - $(basename "$(dirname "$dir")")/$(basename "$dir")"
+done
+echo ""
+
+FAILED=()
+PASSED=()
+
+for app_dir in "${APP_DIRS[@]}"; do
+ app_name="$(basename "$(dirname "$app_dir")")/$(basename "$app_dir")"
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+ echo "Testing: $app_name"
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+
+ cd "$app_dir"
+
+ echo " → aspire restore..."
+ if ! aspire restore --non-interactive 2>&1; then
+ echo " ❌ aspire restore failed for $app_name"
+ FAILED+=("$app_name (aspire restore)")
+ continue
+ fi
+
+ echo " → javac..."
+ build_dir="$app_dir/.java-build"
+ rm -rf "$build_dir"
+ mkdir -p "$build_dir"
+
+ if [ ! -f ".modules/sources.txt" ]; then
+ echo " ❌ No generated Java source list found for $app_name"
+ FAILED+=("$app_name (generated sources missing)")
+ rm -rf "$build_dir"
+ continue
+ fi
+
+ if ! javac --enable-preview --source 25 -d "$build_dir" @.modules/sources.txt AppHost.java 2>&1; then
+ echo " ❌ javac compilation failed for $app_name"
+ FAILED+=("$app_name (javac)")
+ rm -rf "$build_dir"
+ continue
+ fi
+
+ rm -rf "$build_dir"
+ echo " ✅ $app_name passed"
+ PASSED+=("$app_name")
+ echo ""
+done
+
+echo ""
+echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+echo "Results: ${#PASSED[@]} passed, ${#FAILED[@]} failed out of ${#APP_DIRS[@]} apps"
+echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
+
+if [ ${#FAILED[@]} -gt 0 ]; then
+ echo ""
+ echo "❌ Failed apps:"
+ for f in "${FAILED[@]}"; do
+ echo " - $f"
+ done
+ exit 1
+fi
+
+echo "✅ All Java playground apps validated successfully!"
+exit 0
diff --git a/.github/workflows/polyglot-validation/test-java.sh b/.github/workflows/polyglot-validation/test-java.sh
old mode 100755
new mode 100644
index df5d0f68cd0..66fccec5d23
--- a/.github/workflows/polyglot-validation/test-java.sh
+++ b/.github/workflows/polyglot-validation/test-java.sh
@@ -1,11 +1,11 @@
#!/bin/bash
# Polyglot SDK Validation - Java
-# This script validates the Java AppHost SDK with Redis integration
-set -e
+# Creates a Java AppHost via the CLI, adds Redis, and validates that
+# `aspire run` can launch a Redis-backed app non-interactively.
+set -euo pipefail
echo "=== Java AppHost SDK Validation ==="
-# Verify aspire CLI is available
if ! command -v aspire &> /dev/null; then
echo "❌ Aspire CLI not found in PATH"
exit 1
@@ -14,16 +14,24 @@ fi
echo "Aspire CLI version:"
aspire --version
-# Create project directory
-WORK_DIR=$(mktemp -d)
+WORK_DIR="$(mktemp -d)"
+ASPIRE_PID=""
+
+cleanup() {
+ if [ -n "${ASPIRE_PID:-}" ]; then
+ kill "$ASPIRE_PID" 2>/dev/null || true
+ fi
+ rm -rf "$WORK_DIR"
+}
+
+trap cleanup EXIT
+
echo "Working directory: $WORK_DIR"
cd "$WORK_DIR"
-# Initialize Java AppHost
echo "Creating Java apphost project..."
aspire init --language java --non-interactive -d
-# Add Redis integration
echo "Adding Redis integration..."
aspire add Aspire.Hosting.Redis --non-interactive -d 2>&1 || {
echo "aspire add failed, manually updating settings.json..."
@@ -37,23 +45,20 @@ aspire add Aspire.Hosting.Redis --non-interactive -d 2>&1 || {
fi
}
-# Insert Redis code into AppHost.java
echo "Configuring AppHost.java with Redis..."
-if grep -q "builder.build()" AppHost.java; then
- sed -i '/builder.build()/i\ // Add Redis cache resource\n builder.addRedis("cache", null, null).withImageRegistry("netaspireci.azurecr.io");' AppHost.java
+if grep -q "builder.build().run();" AppHost.java; then
+ sed -i '/builder.build().run();/i\ builder.addRedis("cache")\n .withImageRegistry("netaspireci.azurecr.io");' AppHost.java
echo "✅ Redis configuration added to AppHost.java"
fi
echo "=== AppHost.java ==="
cat AppHost.java
-# Run the apphost in background
echo "Starting apphost in background..."
-aspire run -d > aspire.log 2>&1 &
+aspire run -d --non-interactive > aspire.log 2>&1 &
ASPIRE_PID=$!
echo "Aspire PID: $ASPIRE_PID"
-# Poll for Redis container with retries
echo "Polling for Redis container..."
RESULT=1
for i in {1..12}; do
@@ -68,7 +73,7 @@ for i in {1..12}; do
sleep 10
done
-if [ $RESULT -ne 0 ]; then
+if [ "$RESULT" -ne 0 ]; then
echo "❌ FAILURE: Redis container not found after 2 minutes"
echo "=== Docker containers ==="
docker ps
@@ -76,9 +81,4 @@ if [ $RESULT -ne 0 ]; then
cat aspire.log || true
fi
-# Cleanup
-echo "Stopping apphost..."
-kill -9 $ASPIRE_PID 2>/dev/null || true
-rm -rf "$WORK_DIR"
-
-exit $RESULT
+exit "$RESULT"
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index b2295116337..b2e93ab5021 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -218,6 +218,14 @@ jobs:
name: TypeScript SDK Unit Tests
uses: ./.github/workflows/typescript-sdk-tests.yml
+ java_sdk_tests:
+ name: Java SDK Unit Tests
+ uses: ./.github/workflows/run-tests.yml
+ with:
+ testShortName: Java SDK
+ testProjectPath: tests/Aspire.Hosting.CodeGeneration.Java.Tests/Aspire.Hosting.CodeGeneration.Java.Tests.csproj
+ extraTestArgs: --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
+
results:
if: ${{ always() && github.repository_owner == 'microsoft' }}
runs-on: ubuntu-latest
@@ -230,6 +238,7 @@ jobs:
build_cli_archive_macos,
extension_tests_win,
typescript_sdk_tests,
+ java_sdk_tests,
tests_no_nugets,
tests_no_nugets_overflow,
tests_requires_nugets_linux,
@@ -294,8 +303,9 @@ jobs:
contains(needs.*.result, 'cancelled') ||
(github.event_name == 'pull_request' &&
(needs.extension_tests_win.result == 'skipped' ||
- needs.typescript_sdk_tests.result == 'skipped' ||
- needs.tests_no_nugets.result == 'skipped' ||
+ needs.typescript_sdk_tests.result == 'skipped' ||
+ needs.java_sdk_tests.result == 'skipped' ||
+ needs.tests_no_nugets.result == 'skipped' ||
needs.tests_requires_nugets_linux.result == 'skipped' ||
needs.tests_requires_nugets_windows.result == 'skipped' ||
(fromJson(needs.setup_for_tests.outputs.tests_matrix_requires_nugets_macos).include[0] != null &&
@@ -304,8 +314,9 @@ jobs:
needs.polyglot_validation.result == 'skipped')) ||
(github.event_name != 'pull_request' &&
(needs.extension_tests_win.result == 'skipped' ||
- needs.typescript_sdk_tests.result == 'skipped' ||
- needs.tests_no_nugets.result == 'skipped' ||
+ needs.typescript_sdk_tests.result == 'skipped' ||
+ needs.java_sdk_tests.result == 'skipped' ||
+ needs.tests_no_nugets.result == 'skipped' ||
needs.tests_requires_nugets_linux.result == 'skipped' ||
needs.tests_requires_nugets_windows.result == 'skipped' ||
(fromJson(needs.setup_for_tests.outputs.tests_matrix_requires_nugets_macos).include[0] != null &&
diff --git a/.gitignore b/.gitignore
index c1e15bd2e7b..8b31d5af749 100644
--- a/.gitignore
+++ b/.gitignore
@@ -128,6 +128,14 @@ node_modules/
target/
dependency-reduced-pom.xml
+# Java AppHost build artifacts
+playground/**/.java-build/
+playground/**/*.class
+
+# Generated Java AppHost SDK sources
+/playground/JavaAppHost/.modules/
+/playground/polyglot/Java/**/ValidationAppHost/.modules/
+
# Ignore cache created with the Angular CLI.
.angular/
diff --git a/playground/JavaAppHost/AppHost.java b/playground/JavaAppHost/AppHost.java
new file mode 100644
index 00000000000..26858b1ecb7
--- /dev/null
+++ b/playground/JavaAppHost/AppHost.java
@@ -0,0 +1,17 @@
+import aspire.*;
+
+void main(String[] args) throws Exception {
+ IDistributedApplicationBuilder builder = DistributedApplication.CreateBuilder(args);
+
+ NodeAppResource app = builder.addNodeApp("app", "./api", "src/index.ts");
+ app.withHttpEndpoint(new WithHttpEndpointOptions().env("PORT"));
+ app.withExternalHttpEndpoints();
+
+ ViteAppResource frontend = builder.addViteApp("frontend", "./frontend");
+ frontend.withReference(app);
+ frontend.waitFor(app);
+
+ app.publishWithContainerFiles(frontend, "./static");
+
+ builder.build().run();
+ }
diff --git a/playground/JavaAppHost/api/package.json b/playground/JavaAppHost/api/package.json
new file mode 100644
index 00000000000..4c7e48f9f3a
--- /dev/null
+++ b/playground/JavaAppHost/api/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "java-apphost-api",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "start": "tsx src/index.ts"
+ },
+ "dependencies": {
+ "@opentelemetry/auto-instrumentations-node": "^0.71.0",
+ "@opentelemetry/exporter-logs-otlp-grpc": "^0.213.0",
+ "@opentelemetry/exporter-metrics-otlp-grpc": "^0.213.0",
+ "@opentelemetry/exporter-trace-otlp-grpc": "^0.213.0",
+ "@opentelemetry/sdk-logs": "^0.213.0",
+ "@opentelemetry/sdk-metrics": "^2.6.0",
+ "@opentelemetry/sdk-node": "^0.213.0",
+ "express": "^5.1.0"
+ },
+ "devDependencies": {
+ "@types/express": "^5.0.6",
+ "tsx": "^4.21.0"
+ }
+}
diff --git a/playground/JavaAppHost/api/src/index.ts b/playground/JavaAppHost/api/src/index.ts
new file mode 100644
index 00000000000..4d2c25855e1
--- /dev/null
+++ b/playground/JavaAppHost/api/src/index.ts
@@ -0,0 +1,42 @@
+/**
+ * Import the OpenTelemetry instrumentation setup first, before any other modules.
+ * This ensures all subsequent imports are automatically instrumented for
+ * distributed tracing, metrics, and logging in the Aspire dashboard.
+ */
+import "./instrumentation.ts";
+import express from "express";
+import { existsSync } from "fs";
+import { join } from "path";
+
+const app = express();
+const port = process.env.PORT || 5000;
+
+/** Returns a random 5-day weather forecast as JSON. */
+app.get("/api/weatherforecast", (_req, res) => {
+ const summaries = ["Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"];
+ const forecasts = Array.from({ length: 5 }, (_, i) => {
+ const temperatureC = Math.floor(Math.random() * 75) - 20;
+ return {
+ date: new Date(Date.now() + (i + 1) * 86400000).toISOString(),
+ temperatureC,
+ temperatureF: 32 + Math.trunc(temperatureC / 0.5556),
+ summary: summaries[Math.floor(Math.random() * summaries.length)],
+ };
+ });
+ res.json(forecasts);
+});
+
+app.get("/health", (_req, res) => {
+ res.send("Healthy");
+});
+
+// Serve static files from the "static" directory if it exists (used in publish/deploy mode
+// when the frontend's build output is bundled into this container via publishWithContainerFiles)
+const staticDir = join(import.meta.dirname, "..", "static");
+if (existsSync(staticDir)) {
+ app.use(express.static(staticDir));
+}
+
+app.listen(port, () => {
+ console.log(`API server listening on port ${port}`);
+});
diff --git a/playground/JavaAppHost/api/src/instrumentation.ts b/playground/JavaAppHost/api/src/instrumentation.ts
new file mode 100644
index 00000000000..e9282489cdd
--- /dev/null
+++ b/playground/JavaAppHost/api/src/instrumentation.ts
@@ -0,0 +1,44 @@
+/**
+ * OpenTelemetry instrumentation for the app.
+ *
+ * When the OTEL_EXPORTER_OTLP_ENDPOINT environment variable is set (automatically
+ * configured by Aspire), this module initializes the OpenTelemetry Node.js SDK to
+ * collect and export distributed traces, metrics, and logs to the Aspire dashboard.
+ *
+ * This file must be imported before any other modules to ensure all libraries
+ * are automatically instrumented.
+ *
+ * @see https://opentelemetry.io/docs/languages/js/getting-started/nodejs/
+ */
+import { env } from 'node:process';
+import { NodeSDK } from '@opentelemetry/sdk-node';
+import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
+import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';
+import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc';
+import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-grpc';
+import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';
+import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
+
+if (env.OTEL_EXPORTER_OTLP_ENDPOINT) {
+ const sdk = new NodeSDK({
+ traceExporter: new OTLPTraceExporter(),
+ metricReader: new PeriodicExportingMetricReader({
+ exporter: new OTLPMetricExporter(),
+ }),
+ logRecordProcessor: new BatchLogRecordProcessor(
+ new OTLPLogExporter(),
+ ),
+ instrumentations: [getNodeAutoInstrumentations()],
+ });
+
+ sdk.start().catch((error) => {
+ console.error('Failed to start OpenTelemetry NodeSDK:', error);
+ });
+
+ process.on('SIGTERM', () => {
+ sdk.shutdown().finally(() => process.exit(0));
+ });
+ process.on('SIGINT', () => {
+ sdk.shutdown().finally(() => process.exit(0));
+ });
+}
diff --git a/playground/JavaAppHost/api/tsconfig.json b/playground/JavaAppHost/api/tsconfig.json
new file mode 100644
index 00000000000..6f1f8cb7405
--- /dev/null
+++ b/playground/JavaAppHost/api/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./dist"
+ },
+ "include": ["src"]
+}
diff --git a/playground/JavaAppHost/aspire.config.json b/playground/JavaAppHost/aspire.config.json
new file mode 100644
index 00000000000..75fbd923add
--- /dev/null
+++ b/playground/JavaAppHost/aspire.config.json
@@ -0,0 +1,29 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.JavaScript": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:17000;http://localhost:15000",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21000",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22000"
+ }
+ },
+ "http": {
+ "applicationUrl": "http://localhost:15000",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19000",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20000",
+ "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true"
+ }
+ }
+ }
+}
diff --git a/playground/JavaAppHost/frontend/.dockerignore b/playground/JavaAppHost/frontend/.dockerignore
new file mode 100644
index 00000000000..b9470778764
--- /dev/null
+++ b/playground/JavaAppHost/frontend/.dockerignore
@@ -0,0 +1,2 @@
+node_modules/
+dist/
diff --git a/playground/JavaAppHost/frontend/eslint.config.js b/playground/JavaAppHost/frontend/eslint.config.js
new file mode 100644
index 00000000000..2b3e6ccfb16
--- /dev/null
+++ b/playground/JavaAppHost/frontend/eslint.config.js
@@ -0,0 +1,28 @@
+import js from '@eslint/js';
+import globals from 'globals';
+import reactHooks from 'eslint-plugin-react-hooks';
+import reactRefresh from 'eslint-plugin-react-refresh';
+import tseslint from 'typescript-eslint';
+
+export default tseslint.config(
+ { ignores: ['dist'] },
+ {
+ extends: [js.configs.recommended, ...tseslint.configs.recommended],
+ files: ['**/*.{ts,tsx}'],
+ languageOptions: {
+ ecmaVersion: 2020,
+ globals: globals.browser,
+ },
+ plugins: {
+ 'react-hooks': reactHooks,
+ 'react-refresh': reactRefresh,
+ },
+ rules: {
+ ...reactHooks.configs.recommended.rules,
+ 'react-refresh/only-export-components': [
+ 'warn',
+ { allowConstantExport: true },
+ ],
+ },
+ },
+);
diff --git a/playground/JavaAppHost/frontend/index.html b/playground/JavaAppHost/frontend/index.html
new file mode 100644
index 00000000000..472646d2e0f
--- /dev/null
+++ b/playground/JavaAppHost/frontend/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Aspire Starter
+
+
+
+
+
+
diff --git a/playground/JavaAppHost/frontend/package-lock.json b/playground/JavaAppHost/frontend/package-lock.json
new file mode 100644
index 00000000000..cf8c8acaba1
--- /dev/null
+++ b/playground/JavaAppHost/frontend/package-lock.json
@@ -0,0 +1,2430 @@
+{
+ "name": "frontend",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "frontend",
+ "version": "0.0.0",
+ "dependencies": {
+ "react": "^19.2.1",
+ "react-dom": "^19.2.1"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.39.1",
+ "@types/node": "^24.10.1",
+ "@types/react": "^19.2.7",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^6.0.0",
+ "eslint": "^9.39.1",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.24",
+ "globals": "^16.5.0",
+ "typescript": "~5.9.3",
+ "typescript-eslint": "^8.48.1",
+ "vite": "^8.0.0"
+ }
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz",
+ "integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.0",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz",
+ "integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz",
+ "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
+ "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
+ "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.7",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
+ "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.1",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.39.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz",
+ "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.7",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
+ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
+ "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1",
+ "@tybys/wasm-util": "^0.10.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@oxc-project/runtime": {
+ "version": "0.115.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.115.0.tgz",
+ "integrity": "sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@oxc-project/types": {
+ "version": "0.115.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.115.0.tgz",
+ "integrity": "sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.9.tgz",
+ "integrity": "sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.9.tgz",
+ "integrity": "sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.9.tgz",
+ "integrity": "sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.9.tgz",
+ "integrity": "sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.9.tgz",
+ "integrity": "sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.9.tgz",
+ "integrity": "sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.9.tgz",
+ "integrity": "sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.9.tgz",
+ "integrity": "sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.9.tgz",
+ "integrity": "sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.9.tgz",
+ "integrity": "sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.9.tgz",
+ "integrity": "sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.9.tgz",
+ "integrity": "sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.9.tgz",
+ "integrity": "sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.9.tgz",
+ "integrity": "sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.9.tgz",
+ "integrity": "sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-rc.7",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz",
+ "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==",
+ "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/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "24.10.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
+ "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "undici-types": "~7.16.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
+ "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.48.1.tgz",
+ "integrity": "sha512-X63hI1bxl5ohelzr0LY5coufyl0LJNthld+abwxpCoo6Gq+hSqhKwci7MUWkXo67mzgUK6YFByhmaHmUcuBJmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.48.1",
+ "@typescript-eslint/type-utils": "8.48.1",
+ "@typescript-eslint/utils": "8.48.1",
+ "@typescript-eslint/visitor-keys": "8.48.1",
+ "graphemer": "^1.4.0",
+ "ignore": "^7.0.0",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.48.1",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.48.1.tgz",
+ "integrity": "sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.48.1",
+ "@typescript-eslint/types": "8.48.1",
+ "@typescript-eslint/typescript-estree": "8.48.1",
+ "@typescript-eslint/visitor-keys": "8.48.1",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.48.1.tgz",
+ "integrity": "sha512-HQWSicah4s9z2/HifRPQ6b6R7G+SBx64JlFQpgSSHWPKdvCZX57XCbszg/bapbRsOEv42q5tayTYcEFpACcX1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.48.1",
+ "@typescript-eslint/types": "^8.48.1",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.48.1.tgz",
+ "integrity": "sha512-rj4vWQsytQbLxC5Bf4XwZ0/CKd362DkWMUkviT7DCS057SK64D5lH74sSGzhI6PDD2HCEq02xAP9cX68dYyg1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.48.1",
+ "@typescript-eslint/visitor-keys": "8.48.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.48.1.tgz",
+ "integrity": "sha512-k0Jhs4CpEffIBm6wPaCXBAD7jxBtrHjrSgtfCjUvPp9AZ78lXKdTR8fxyZO5y4vWNlOvYXRtngSZNSn+H53Jkw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.48.1.tgz",
+ "integrity": "sha512-1jEop81a3LrJQLTf/1VfPQdhIY4PlGDBc/i67EVWObrtvcziysbLN3oReexHOM6N3jyXgCrkBsZpqwH0hiDOQg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.48.1",
+ "@typescript-eslint/typescript-estree": "8.48.1",
+ "@typescript-eslint/utils": "8.48.1",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.48.1.tgz",
+ "integrity": "sha512-+fZ3LZNeiELGmimrujsDCT4CRIbq5oXdHe7chLiW8qzqyPMnn1puNstCrMNVAqwcl2FdIxkuJ4tOs/RFDBVc/Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.48.1.tgz",
+ "integrity": "sha512-/9wQ4PqaefTK6POVTjJaYS0bynCgzh6ClJHGSBj06XEHjkfylzB+A3qvyaXnErEZSaxhIo4YdyBgq6j4RysxDg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.48.1",
+ "@typescript-eslint/tsconfig-utils": "8.48.1",
+ "@typescript-eslint/types": "8.48.1",
+ "@typescript-eslint/visitor-keys": "8.48.1",
+ "debug": "^4.3.4",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.48.1.tgz",
+ "integrity": "sha512-fAnhLrDjiVfey5wwFRwrweyRlCmdz5ZxXz2G/4cLn0YDLjTapmN4gcCsTBR1N2rWnZSDeWpYtgLDsJt+FpmcwA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.7.0",
+ "@typescript-eslint/scope-manager": "8.48.1",
+ "@typescript-eslint/types": "8.48.1",
+ "@typescript-eslint/typescript-estree": "8.48.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.48.1.tgz",
+ "integrity": "sha512-BmxxndzEWhE4TIEEMBs8lP3MBWN3jFPs/p6gPm/wkv02o41hI6cq9AuSmGAaTTHPtA1FTi2jBre4A9rm5ZmX+Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.48.1",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz",
+ "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rolldown/pluginutils": "1.0.0-rc.7"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0",
+ "babel-plugin-react-compiler": "^1.0.0",
+ "vite": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@rolldown/plugin-babel": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.39.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz",
+ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.1",
+ "@eslint/config-helpers": "^0.4.2",
+ "@eslint/core": "^0.17.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.39.1",
+ "@eslint/plugin-kit": "^0.4.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
+ "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react-refresh": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.24.tgz",
+ "integrity": "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "eslint": ">=8.40"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "16.5.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
+ "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "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/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.8",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
+ "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz",
+ "integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.1.tgz",
+ "integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.1"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/rolldown": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.9.tgz",
+ "integrity": "sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.115.0",
+ "@rolldown/pluginutils": "1.0.0-rc.9"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm64": "1.0.0-rc.9",
+ "@rolldown/binding-darwin-arm64": "1.0.0-rc.9",
+ "@rolldown/binding-darwin-x64": "1.0.0-rc.9",
+ "@rolldown/binding-freebsd-x64": "1.0.0-rc.9",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.9",
+ "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.9",
+ "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.9",
+ "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.9",
+ "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.9",
+ "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.9",
+ "@rolldown/binding-linux-x64-musl": "1.0.0-rc.9",
+ "@rolldown/binding-openharmony-arm64": "1.0.0-rc.9",
+ "@rolldown/binding-wasm32-wasi": "1.0.0-rc.9",
+ "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.9",
+ "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.9"
+ }
+ },
+ "node_modules/rolldown/node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.9.tgz",
+ "integrity": "sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+ "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD",
+ "optional": true
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.48.1.tgz",
+ "integrity": "sha512-FbOKN1fqNoXp1hIl5KYpObVrp0mCn+CLgn479nmu2IsRMrx2vyv74MmsBLVlhg8qVwNFGbXSp8fh1zp8pEoC2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.48.1",
+ "@typescript-eslint/parser": "8.48.1",
+ "@typescript-eslint/typescript-estree": "8.48.1",
+ "@typescript-eslint/utils": "8.48.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.0.tgz",
+ "integrity": "sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@oxc-project/runtime": "0.115.0",
+ "lightningcss": "^1.32.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.8",
+ "rolldown": "1.0.0-rc.9",
+ "tinyglobby": "^0.2.15"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.0.0-alpha.31",
+ "esbuild": "^0.27.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
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/playground/JavaAppHost/frontend/package.json b/playground/JavaAppHost/frontend/package.json
new file mode 100644
index 00000000000..e684fdb3139
--- /dev/null
+++ b/playground/JavaAppHost/frontend/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "frontend",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "react": "^19.2.1",
+ "react-dom": "^19.2.1"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.39.1",
+ "@types/node": "^24.10.1",
+ "@types/react": "^19.2.7",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^6.0.0",
+ "eslint": "^9.39.1",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.24",
+ "globals": "^16.5.0",
+ "typescript": "~5.9.3",
+ "typescript-eslint": "^8.48.1",
+ "vite": "^8.0.0"
+ }
+}
diff --git a/playground/JavaAppHost/frontend/public/Aspire.png b/playground/JavaAppHost/frontend/public/Aspire.png
new file mode 100644
index 00000000000..d4be662a618
Binary files /dev/null and b/playground/JavaAppHost/frontend/public/Aspire.png differ
diff --git a/playground/JavaAppHost/frontend/public/github.svg b/playground/JavaAppHost/frontend/public/github.svg
new file mode 100644
index 00000000000..162a4a43a35
--- /dev/null
+++ b/playground/JavaAppHost/frontend/public/github.svg
@@ -0,0 +1,3 @@
+
diff --git a/playground/JavaAppHost/frontend/src/App.css b/playground/JavaAppHost/frontend/src/App.css
new file mode 100644
index 00000000000..1f690dea729
--- /dev/null
+++ b/playground/JavaAppHost/frontend/src/App.css
@@ -0,0 +1,794 @@
+/* CSS Variables for theming */
+:root {
+ --bg-gradient-start: #1a1a2e;
+ --bg-gradient-end: #16213e;
+ --card-bg: rgba(30, 30, 46, 0.95);
+ --card-hover-shadow: rgba(0, 0, 0, 0.3);
+ --text-primary: #ffffff;
+ --text-secondary: #e2e8f0;
+ --text-tertiary: #cbd5e0;
+ --accent-gradient-start: #7c92f5;
+ --accent-gradient-end: #8b5ecf;
+ --weather-card-bg: rgba(45, 45, 60, 0.8);
+ --weather-card-border: rgba(255, 255, 255, 0.1);
+ --section-title-color: #f7fafc;
+ --date-color: #cbd5e0;
+ --summary-color: #f7fafc;
+ --temp-unit-color: #e2e8f0;
+ --divider-color: #4a5568;
+ --error-bg: rgba(220, 38, 38, 0.1);
+ --error-border: #ef4444;
+ --error-text: #fca5a5;
+ --skeleton-bg-1: rgba(255, 255, 255, 0.05);
+ --skeleton-bg-2: rgba(255, 255, 255, 0.1);
+ --tile-min-height: 120px;
+ --tile-gap: 0.75rem;
+ --cta-height: 3rem;
+ --card-inner-gap: 1rem;
+ --focus-color: #a78bfa;
+}
+
+@media (prefers-color-scheme: light) {
+ :root {
+ --bg-gradient-start: #f0f4ff;
+ --bg-gradient-end: #e0e7ff;
+ --card-bg: rgba(255, 255, 255, 0.98);
+ --card-hover-shadow: rgba(0, 0, 0, 0.15);
+ --text-primary: #1a202c;
+ --text-secondary: #2d3748;
+ --text-tertiary: #4a5568;
+ --accent-gradient-start: #5b6fd8;
+ --accent-gradient-end: #6b46a3;
+ --weather-card-bg: rgba(255, 255, 255, 0.9);
+ --weather-card-border: rgba(102, 126, 234, 0.15);
+ --section-title-color: #1a202c;
+ --date-color: #2d3748;
+ --summary-color: #1a202c;
+ --temp-unit-color: #4a5568;
+ --divider-color: #cbd5e0;
+ --error-bg: #fee;
+ --error-border: #dc2626;
+ --error-text: #991b1b;
+ --skeleton-bg-1: #f0f0f0;
+ --skeleton-bg-2: #e0e0e0;
+ --tile-min-height: 120px;
+ --tile-gap: 0.75rem;
+ --cta-height: 3rem;
+ --card-inner-gap: 1rem;
+ --focus-color: #5b21b6;
+ }
+}
+
+/* Root container */
+#root {
+ width: 100%;
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ overflow-x: hidden;
+}
+
+/* Accessibility utilities */
+.visually-hidden {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
+}
+
+/* App container */
+.app-container {
+ width: 100%;
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ background: linear-gradient(135deg, var(--bg-gradient-start) 0%, var(--bg-gradient-end) 100%);
+ color: var(--text-primary);
+}
+
+/* Header */
+.app-header {
+ padding: 2.5rem 2rem 1.5rem;
+ text-align: center;
+ animation: fadeInDown 0.6s ease-out;
+}
+
+.logo-link {
+ display: inline-block;
+ border-radius: 0.5rem;
+}
+
+.logo-link:focus-visible {
+ outline: 3px solid var(--accent-gradient-end);
+ outline-offset: 8px;
+}
+
+.logo {
+ height: 5rem;
+ width: auto;
+ transition: transform 300ms ease, filter 300ms ease;
+ filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.2));
+}
+
+.logo:hover {
+ transform: scale(1.1) rotate(5deg);
+ filter: drop-shadow(0 8px 16px rgba(0, 0, 0, 0.3));
+}
+
+.app-title {
+ font-size: 2.75rem;
+ font-weight: 700;
+ margin: 1.25rem 0 0.5rem;
+ background: linear-gradient(135deg, var(--text-primary) 0%, var(--text-secondary) 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ letter-spacing: -0.02em;
+}
+
+.app-subtitle {
+ font-size: 1.05rem;
+ color: var(--text-tertiary);
+ margin: 0;
+ font-weight: 300;
+}
+
+/* Main content */
+.main-content {
+ flex: 1;
+ max-width: 1400px;
+ width: 100%;
+ margin: 0 auto;
+ padding: 0rem 2rem 2rem;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+/* Card styles */
+.card {
+ background: var(--card-bg);
+ backdrop-filter: blur(10px);
+ border-radius: 1rem;
+ padding: 1.25rem 1.5rem;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+ color: var(--text-primary);
+ animation: fadeInUp 0.6s ease-out;
+ border: 1px solid var(--weather-card-border);
+ display: flex;
+ flex-direction: column;
+ gap: var(--card-inner-gap);
+}
+
+/* Section styles */
+.demo-section {
+ animation: fadeInUp 0.6s ease-out;
+}
+
+.weather-section {
+ animation: fadeInUp 0.6s ease-out;
+ animation-delay: 0.1s;
+ flex: 1;
+ max-width: 1200px;
+ width: 100%;
+}
+
+.section-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 0;
+ flex-wrap: wrap;
+ gap: 1rem;
+}
+
+.header-actions {
+ display: flex;
+ gap: 0.75rem;
+ align-items: center;
+}
+
+.section-title {
+ font-size: 1.25rem;
+ font-weight: 600;
+ margin: 0;
+ color: var(--section-title-color);
+}
+
+/* Counter area */
+.counter-card .section-header {
+ margin-bottom: 0;
+}
+
+.counter-panel {
+ background: var(--weather-card-bg);
+ border-radius: 0.75rem;
+ border: 1px solid var(--weather-card-border);
+ padding: 1.25rem;
+ display: flex;
+ flex-direction: column;
+ gap: var(--tile-gap);
+ min-height: var(--tile-min-height);
+ backdrop-filter: blur(10px);
+ flex: 1;
+}
+
+.counter-value-group {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 0.25rem;
+ flex: 1;
+ margin-bottom: var(--tile-gap);
+ text-align: center;
+}
+
+.counter-label {
+ font-size: 0.75rem;
+ text-transform: uppercase;
+ letter-spacing: 0.1em;
+ color: var(--text-tertiary);
+ font-weight: 600;
+}
+
+.counter-value {
+ font-size: 2.25rem;
+ font-weight: 700;
+ line-height: 1;
+ background: linear-gradient(135deg, var(--accent-gradient-start) 0%, var(--accent-gradient-end) 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.increment-button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+ background: linear-gradient(135deg, var(--accent-gradient-start) 0%, var(--accent-gradient-end) 100%);
+ color: white;
+ border: none;
+ border-radius: 0.5rem;
+ padding: 0 1.5rem;
+ height: var(--cta-height);
+ font-size: 0.875rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
+ width: 100%;
+ margin-top: auto;
+}
+
+.increment-button:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
+}
+
+.increment-button:active {
+ transform: translateY(0);
+}
+
+.increment-button:focus-visible {
+ outline: 3px solid var(--accent-gradient-end);
+ outline-offset: 2px;
+}
+
+.increment-icon {
+ transition: transform 0.3s ease;
+}
+
+.increment-button:hover .increment-icon {
+ transform: scale(1.1);
+}
+
+/* Toggle switch */
+.toggle-switch {
+ display: flex;
+ background: rgba(255, 255, 255, 0.1);
+ border-radius: 0.5rem;
+ padding: 0.25rem;
+ gap: 0.25rem;
+ border: 1px solid var(--weather-card-border);
+ margin: 0;
+ padding: 0.25rem;
+ min-width: 0;
+}
+
+.toggle-switch legend {
+ padding: 0;
+}
+
+@media (prefers-color-scheme: light) {
+ .toggle-switch {
+ background: rgba(102, 126, 234, 0.08);
+ }
+}
+
+.toggle-option {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: transparent;
+ color: var(--text-secondary);
+ border: none;
+ border-radius: 0.375rem;
+ padding: 0 1rem;
+ height: 2.5rem;
+ font-size: 0.875rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ min-width: 3rem;
+ position: relative;
+}
+
+.toggle-option[aria-pressed="true"] {
+ background: linear-gradient(135deg, var(--accent-gradient-start) 0%, var(--accent-gradient-end) 100%);
+ color: white;
+ box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
+}
+
+.toggle-option[aria-pressed="false"]:hover {
+ background: rgba(255, 255, 255, 0.05);
+ color: var(--text-primary);
+}
+
+@media (prefers-color-scheme: light) {
+ .toggle-option[aria-pressed="false"]:hover {
+ background: rgba(102, 126, 234, 0.1);
+ }
+}
+
+.toggle-option:focus-visible {
+ outline: 3px solid var(--focus-color);
+ outline-offset: 2px;
+ z-index: 1;
+}
+
+/* Refresh button */
+.refresh-button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+ background: linear-gradient(135deg, var(--accent-gradient-start) 0%, var(--accent-gradient-end) 100%);
+ color: white;
+ border: none;
+ border-radius: 0.5rem;
+ padding: 0 1.5rem;
+ height: var(--cta-height);
+ font-size: 0.875rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition: transform 0.3s ease, box-shadow 0.3s ease, opacity 0.3s ease;
+ box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
+ min-width: 140px;
+ white-space: nowrap;
+}
+
+.refresh-button:hover:not(:disabled) {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
+}
+
+.refresh-button:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ transform: none;
+}
+
+.refresh-button:focus-visible {
+ outline: 3px solid var(--focus-color);
+ outline-offset: 3px;
+}
+
+.refresh-icon {
+ transition: transform 0.3s ease;
+}
+
+.refresh-icon.spinning {
+ animation: spin 1s linear infinite;
+}
+
+/* Error message */
+.error-message {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ background-color: var(--error-bg);
+ border-left: 4px solid var(--error-border);
+ color: var(--error-text);
+ padding: 1rem;
+ border-radius: 0.5rem;
+ margin: 1rem 0;
+ animation: slideIn 0.3s ease-out;
+}
+
+/* Loading skeleton */
+.loading-skeleton {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ margin-top: 1rem;
+}
+
+.skeleton-row {
+ height: 80px;
+ background: linear-gradient(90deg, var(--skeleton-bg-1) 25%, var(--skeleton-bg-2) 50%, var(--skeleton-bg-1) 75%);
+ background-size: 200% 100%;
+ animation: shimmer 1.5s infinite;
+ border-radius: 0.5rem;
+}
+
+/* Weather grid */
+.weather-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+ gap: 1rem;
+ margin-top: 0.75rem;
+}
+
+.weather-card {
+ background: var(--weather-card-bg);
+ border-radius: 0.75rem;
+ padding: 1.25rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+ transition: all 0.3s ease;
+ border: 1px solid var(--weather-card-border);
+ backdrop-filter: blur(10px);
+ min-height: var(--tile-min-height);
+}
+
+.weather-card:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
+}
+
+.weather-card:focus-within {
+ outline: 2px solid var(--focus-color);
+ outline-offset: 2px;
+}
+
+.weather-date {
+ font-weight: 600;
+ font-size: 0.875rem;
+ color: var(--date-color);
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ margin: 0;
+}
+
+.weather-summary {
+ font-size: 1.125rem;
+ font-weight: 500;
+ color: var(--summary-color);
+ min-height: 1.5rem;
+ margin: 0;
+}
+
+.weather-temps {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.75rem;
+ margin-top: 0.5rem;
+ padding-top: 0.75rem;
+ border-top: 1px solid var(--weather-card-border);
+}
+
+.temp-group {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 100%;
+}
+
+.temp-value {
+ font-size: 1.5rem;
+ font-weight: 700;
+ background: linear-gradient(135deg, var(--accent-gradient-start) 0%, var(--accent-gradient-end) 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.temp-unit {
+ font-size: 0.75rem;
+ color: var(--temp-unit-color);
+ margin-top: 0.125rem;
+}
+
+/* Responsive design */
+@media (max-width: 1024px) {
+ .main-content {
+ padding: 1rem;
+ }
+}
+.app-footer {
+ padding: 1.5rem;
+ text-align: center;
+ background: rgba(0, 0, 0, 0.2);
+ backdrop-filter: blur(10px);
+}
+
+.app-footer nav {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 1.5rem;
+}
+
+.app-footer a {
+ color: var(--text-secondary);
+ text-decoration: none;
+ font-weight: 500;
+ transition: color 0.3s ease, border-color 0.3s ease;
+ border-bottom: 2px solid transparent;
+ font-size: 0.875rem;
+ padding-bottom: 0.125rem;
+}
+
+.app-footer a:hover {
+ color: var(--text-primary);
+ border-bottom-color: var(--text-primary);
+}
+
+.app-footer a:focus-visible {
+ outline: 3px solid var(--focus-color);
+ outline-offset: 4px;
+ border-radius: 4px;
+}
+
+/* Animations */
+@keyframes fadeInDown {
+ from {
+ opacity: 0;
+ transform: translateY(-20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes slideIn {
+ from {
+ opacity: 0;
+ transform: translateX(-10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+@keyframes spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes shimmer {
+ 0% {
+ background-position: -200% 0;
+ }
+ 100% {
+ background-position: 200% 0;
+ }
+}
+
+/* Responsive design */
+@media (max-width: 1024px) {
+ .main-content {
+ grid-template-columns: 1fr;
+ padding: 1rem;
+ gap: 1rem;
+ }
+}
+
+/* Footer */
+.app-footer {
+ padding: 1.5rem 0;
+ text-align: center;
+ background: rgba(0, 0, 0, 0.2);
+ backdrop-filter: blur(10px);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.app-footer > * {
+ max-width: 1400px;
+ width: 100%;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0 2rem;
+ gap: 1rem;
+}
+
+.app-footer a {
+ color: var(--text-secondary);
+ text-decoration: none;
+ font-weight: 500;
+ transition: color 0.3s ease, transform 0.3s ease;
+ border-bottom: 2px solid transparent;
+ font-size: 0.875rem;
+}
+
+.app-footer a:hover {
+ color: var(--text-primary);
+ border-bottom-color: var(--text-primary);
+}
+
+.app-footer a:focus-visible {
+ outline: 2px solid var(--text-primary);
+ outline-offset: 4px;
+ border-radius: 2px;
+}
+
+.github-link {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.5rem;
+ border-bottom: none !important;
+}
+
+.github-link:focus-visible {
+ outline: 3px solid var(--focus-color);
+ outline-offset: 4px;
+ border-radius: 4px;
+}
+
+.github-link img {
+ transition: transform 0.3s ease, opacity 0.3s ease;
+ filter: brightness(0) invert(1);
+}
+
+@media (prefers-color-scheme: light) {
+ .github-link img {
+ filter: brightness(0) invert(0);
+ opacity: 0.7;
+ }
+
+ .github-link:hover img {
+ opacity: 1;
+ }
+}
+
+.github-link:hover img {
+ transform: scale(1.1);
+}
+
+@media (max-width: 768px) {
+ :root {
+ --cta-height: 2.75rem;
+ }
+
+ .app-header {
+ padding: 1.5rem 1rem 1rem;
+ }
+
+ .logo {
+ height: 3rem;
+ }
+
+ .app-title {
+ font-size: 1.5rem;
+ }
+
+ .app-subtitle {
+ font-size: 0.875rem;
+ }
+
+ .main-content {
+ padding: 0.75rem;
+ }
+
+ .card {
+ padding: 1rem;
+ }
+
+ .section-title {
+ font-size: 1.125rem;
+ }
+
+ .section-header {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 0.75rem;
+ }
+
+ .header-actions {
+ width: 100%;
+ }
+
+ .toggle-switch {
+ flex: 1;
+ }
+
+ .toggle-option {
+ flex: 1;
+ }
+
+ .refresh-button {
+ flex: 1;
+ justify-content: center;
+ padding: 0 1.25rem;
+ }
+
+ .weather-grid {
+ grid-template-columns: 1fr;
+ gap: 0.75rem;
+ }
+
+ .weather-card {
+ padding: 1.25rem;
+ }
+
+ .app-footer {
+ padding: 1rem 0;
+ }
+
+ .app-footer > * {
+ flex-direction: column;
+ padding: 0 1.5rem;
+ }
+
+ .github-link {
+ order: -1;
+ }
+}
+
+/* Reduced motion support */
+@media (prefers-reduced-motion: reduce) {
+ *,
+ *::before,
+ *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ }
+}
+
+/* High contrast mode support */
+@media (prefers-contrast: high) {
+ .card {
+ border: 2px solid currentColor;
+ }
+
+ .weather-card {
+ border: 1px solid currentColor;
+ }
+}
+
+/* Focus visible support for better keyboard navigation */
+*:focus-visible {
+ outline: 3px solid var(--accent-gradient-end);
+ outline-offset: 2px;
+}
+
diff --git a/playground/JavaAppHost/frontend/src/App.tsx b/playground/JavaAppHost/frontend/src/App.tsx
new file mode 100644
index 00000000000..b9b5a1b10ae
--- /dev/null
+++ b/playground/JavaAppHost/frontend/src/App.tsx
@@ -0,0 +1,184 @@
+import { useState, useEffect } from 'react';
+import aspireLogo from '/Aspire.png';
+import './App.css';
+
+interface WeatherForecast {
+ date: string;
+ temperatureC: number;
+ temperatureF: number;
+ summary: string;
+}
+
+function App() {
+ const [weatherData, setWeatherData] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [useCelsius, setUseCelsius] = useState(false);
+
+ const fetchWeatherForecast = async () => {
+ setLoading(true);
+ setError(null);
+
+ try {
+ const response = await fetch('/api/weatherforecast');
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ const data: WeatherForecast[] = await response.json();
+ setWeatherData(data);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : 'Failed to fetch weather data');
+ console.error('Error fetching weather forecast:', err);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ fetchWeatherForecast();
+ }, []);
+
+ const formatDate = (dateString: string) => {
+ return new Date(dateString).toLocaleDateString(undefined, {
+ weekday: 'short',
+ month: 'short',
+ day: 'numeric'
+ });
+ };
+
+ return (
+
+
+
+
+
+
+
+
Weather Forecast
+
+
+
+
+
+
+ {error && (
+
+
+ {error}
+
+ )}
+
+ {loading && weatherData.length === 0 && (
+
+ {[...Array(5)].map((_, i) => (
+
+ ))}
+
Loading weather forecast data...
+
+ )}
+
+ {weatherData.length > 0 && (
+
+ {weatherData.map((forecast, index) => (
+
+
+
+
+ {forecast.summary}
+
+
+
+ {useCelsius ? forecast.temperatureC : forecast.temperatureF}°
+
+ {useCelsius ? 'Celsius' : 'Fahrenheit'}
+
+
+
+ ))}
+
+ )}
+
+
+
+
+
+
+ );
+}
+
+export default App;
diff --git a/playground/JavaAppHost/frontend/src/index.css b/playground/JavaAppHost/frontend/src/index.css
new file mode 100644
index 00000000000..6d1b6557354
--- /dev/null
+++ b/playground/JavaAppHost/frontend/src/index.css
@@ -0,0 +1,55 @@
+:root {
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color-scheme: light dark;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+/* Reset and base styles */
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+
+html {
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ height: 100%;
+ overflow-x: hidden;
+}
+
+body {
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ min-height: 100vh;
+ overflow-x: hidden;
+}
+
+/* Remove default Vite styles that conflict with our design */
+h1 {
+ margin: 0;
+}
+
+button {
+ font-family: inherit;
+ cursor: pointer;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ *,
+ *::before,
+ *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ }
+}
diff --git a/playground/JavaAppHost/frontend/src/main.tsx b/playground/JavaAppHost/frontend/src/main.tsx
new file mode 100644
index 00000000000..2239905c14d
--- /dev/null
+++ b/playground/JavaAppHost/frontend/src/main.tsx
@@ -0,0 +1,10 @@
+import { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import './index.css';
+import App from './App.tsx';
+
+createRoot(document.getElementById('root')!).render(
+
+
+ ,
+);
diff --git a/playground/JavaAppHost/frontend/src/vite-env.d.ts b/playground/JavaAppHost/frontend/src/vite-env.d.ts
new file mode 100644
index 00000000000..11f02fe2a00
--- /dev/null
+++ b/playground/JavaAppHost/frontend/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/playground/JavaAppHost/frontend/tsconfig.app.json b/playground/JavaAppHost/frontend/tsconfig.app.json
new file mode 100644
index 00000000000..c9ccbd4c594
--- /dev/null
+++ b/playground/JavaAppHost/frontend/tsconfig.app.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["src"]
+}
diff --git a/playground/JavaAppHost/frontend/tsconfig.json b/playground/JavaAppHost/frontend/tsconfig.json
new file mode 100644
index 00000000000..1ffef600d95
--- /dev/null
+++ b/playground/JavaAppHost/frontend/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "files": [],
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ]
+}
diff --git a/playground/JavaAppHost/frontend/tsconfig.node.json b/playground/JavaAppHost/frontend/tsconfig.node.json
new file mode 100644
index 00000000000..9728af2d81a
--- /dev/null
+++ b/playground/JavaAppHost/frontend/tsconfig.node.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "ES2022",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/playground/JavaAppHost/frontend/vite.config.ts b/playground/JavaAppHost/frontend/vite.config.ts
new file mode 100644
index 00000000000..22861934212
--- /dev/null
+++ b/playground/JavaAppHost/frontend/vite.config.ts
@@ -0,0 +1,18 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+
+const proxyTarget = process.env.APP_HTTPS || process.env.APP_HTTP || 'http://localhost:5000';
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react()],
+ server: {
+ proxy: {
+ // Proxy API calls to the Express service
+ '/api': {
+ target: proxyTarget,
+ changeOrigin: true
+ }
+ }
+ }
+});
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.AppConfiguration/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.AppConfiguration/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..41b398aa684
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.AppConfiguration/ValidationAppHost/AppHost.java
@@ -0,0 +1,13 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var appConfig = builder.addAzureAppConfiguration("appconfig");
+ appConfig.withAppConfigurationRoleAssignments(appConfig, new AzureAppConfigurationRole[] { AzureAppConfigurationRole.APP_CONFIGURATION_DATA_OWNER, AzureAppConfigurationRole.APP_CONFIGURATION_DATA_READER });
+ appConfig.runAsEmulator((emulator) -> {
+ emulator.withDataBindMount(".aace/appconfig");
+ emulator.withDataVolume("appconfig-data");
+ emulator.withHostPort(8483.0);
+ });
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.AppConfiguration/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.AppConfiguration/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..27292ed17b7
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.AppConfiguration/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.AppConfiguration": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.AppContainers/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.AppContainers/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..4b88466ea59
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.AppContainers/ValidationAppHost/AppHost.java
@@ -0,0 +1,53 @@
+import aspire.*;
+
+void main() throws Exception {
+ // Aspire TypeScript AppHost - Validation for Aspire.Hosting.Azure.AppContainers
+ // For more information, see: https://aspire.dev
+ var builder = DistributedApplication.CreateBuilder();
+ // === Azure Container App Environment ===
+ // Test addAzureContainerAppEnvironment factory method
+ var env = builder.addAzureContainerAppEnvironment("myenv");
+ // Test fluent chaining on AzureContainerAppEnvironmentResource
+ env
+ .withAzdResourceNaming()
+ .withCompactResourceNaming()
+ .withDashboard(true)
+ .withHttpsUpgrade(false);
+ // Test withDashboard with no args (uses default)
+ var env2 = builder.addAzureContainerAppEnvironment("myenv2");
+ env2.withDashboard();
+ // Test withHttpsUpgrade with no args (uses default)
+ env2.withHttpsUpgrade();
+ // === WithAzureLogAnalyticsWorkspace ===
+ // Test withAzureLogAnalyticsWorkspace with a Log Analytics Workspace resource
+ var laws = builder.addAzureLogAnalyticsWorkspace("laws");
+ var env3 = builder.addAzureContainerAppEnvironment("myenv3");
+ env3.withAzureLogAnalyticsWorkspace(laws);
+ // === PublishAsAzureContainerApp ===
+ // Test publishAsAzureContainerApp on a container resource with callback
+ var web = builder.addContainer("web", "myregistry/web:latest");
+ web.publishAsAzureContainerApp((infrastructure, app) -> {
+ // Configure container app via callback
+ });
+ // Test publishAsAzureContainerAppJob on an executable resource
+ var api = builder.addExecutable("api", "dotnet", ".", new String[] { "run" });
+ api.publishAsAzureContainerAppJob();
+ // === PublishAsAzureContainerAppJob ===
+ // Test publishAsAzureContainerAppJob (parameterless - manual trigger)
+ var worker = builder.addContainer("worker", "myregistry/worker:latest");
+ worker.publishAsAzureContainerAppJob();
+ // Test publishAsConfiguredAzureContainerAppJob (with callback)
+ var processor = builder.addContainer("processor", "myregistry/processor:latest");
+ processor.publishAsConfiguredAzureContainerAppJob((infrastructure, job) -> {
+ // Configure the container app job here
+ });
+ // Test publishAsScheduledAzureContainerAppJob (simple - no callback)
+ var scheduler = builder.addContainer("scheduler", "myregistry/scheduler:latest");
+ scheduler.publishAsScheduledAzureContainerAppJob("0 0 * * *");
+ // Test publishAsConfiguredScheduledAzureContainerAppJob (with callback)
+ var reporter = builder.addContainer("reporter", "myregistry/reporter:latest");
+ reporter.publishAsConfiguredScheduledAzureContainerAppJob("0 */6 * * *", (infrastructure, job) -> {
+ // Configure the scheduled job here
+ });
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.AppContainers/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.AppContainers/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..0f8b741f5ec
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.AppContainers/ValidationAppHost/aspire.config.json
@@ -0,0 +1,22 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.AppContainers": "",
+ "Aspire.Hosting.Azure.OperationalInsights": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:49511;http://localhost:51415",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:48887",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:33229"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.AppService/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.AppService/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..17eb325d89d
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.AppService/ValidationAppHost/AppHost.java
@@ -0,0 +1,31 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var applicationInsightsLocation = builder.addParameter("applicationInsightsLocation");
+ var deploymentSlot = builder.addParameter("deploymentSlot");
+ var existingApplicationInsights = builder.addAzureApplicationInsights("existingApplicationInsights");
+ var environment = builder.addAzureAppServiceEnvironment("appservice-environment")
+ .withDashboard()
+ .withDashboard(false)
+ .withAzureApplicationInsights()
+ .withAzureApplicationInsightsLocation("westus")
+ .withAzureApplicationInsightsLocationParameter(applicationInsightsLocation)
+ .withAzureApplicationInsightsResource(existingApplicationInsights)
+ .withDeploymentSlotParameter(deploymentSlot)
+ .withDeploymentSlot("staging");
+ var website = builder.addContainer("frontend", "nginx");
+ website.skipEnvironmentVariableNameChecks();
+ website.publishAsAzureAppServiceWebsite(new PublishAsAzureAppServiceWebsiteOptions().configure((_infrastructure, _appService) -> {}).configureSlot((_infrastructure, _appServiceSlot) -> {}));
+
+ var worker = builder.addExecutable("worker", "dotnet", ".", new String[] { "run" });
+ worker.skipEnvironmentVariableNameChecks();
+ worker.publishAsAzureAppServiceWebsite(new PublishAsAzureAppServiceWebsiteOptions().configure((_infrastructure, _appService) -> {}));
+
+ var api = builder.addProject("api", "../Fake.Api/Fake.Api.csproj", "https");
+ api.skipEnvironmentVariableNameChecks();
+ api.publishAsAzureAppServiceWebsite(new PublishAsAzureAppServiceWebsiteOptions().configureSlot((_infrastructure, _appServiceSlot) -> {}));
+ var _environmentName = environment.getResourceName();
+ var _websiteName = website.getResourceName();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.AppService/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.AppService/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..dfa441ef5c3
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.AppService/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.AppService": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:49511;http://localhost:51415",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:48887",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:33229"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.ApplicationInsights/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.ApplicationInsights/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..de7f6235893
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.ApplicationInsights/ValidationAppHost/AppHost.java
@@ -0,0 +1,16 @@
+import aspire.*;
+
+void main() throws Exception {
+ // Aspire TypeScript AppHost - Azure Application Insights validation
+ // Exercises exported members of Aspire.Hosting.Azure.ApplicationInsights
+ var builder = DistributedApplication.CreateBuilder();
+ // addAzureApplicationInsights - factory method with just a name
+ var appInsights = builder.addAzureApplicationInsights("insights");
+ // addAzureLogAnalyticsWorkspace - from the OperationalInsights dependency
+ var logAnalytics = builder.addAzureLogAnalyticsWorkspace("logs");
+ // withLogAnalyticsWorkspace - fluent method to associate a workspace
+ var appInsightsWithWorkspace = builder
+ .addAzureApplicationInsights("insights-with-workspace")
+ .withLogAnalyticsWorkspace(logAnalytics);
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.ApplicationInsights/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.ApplicationInsights/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..518e59f7b5a
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.ApplicationInsights/ValidationAppHost/aspire.config.json
@@ -0,0 +1,22 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.ApplicationInsights": "",
+ "Aspire.Hosting.Azure.OperationalInsights": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:43145;http://localhost:38016",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:43154",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:41056"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.CognitiveServices/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.CognitiveServices/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..c1c3c1c53f2
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.CognitiveServices/ValidationAppHost/AppHost.java
@@ -0,0 +1,12 @@
+import aspire.*;
+
+void main() throws Exception {
+ // Aspire TypeScript AppHost
+ // For more information, see: https://aspire.dev
+ var builder = DistributedApplication.CreateBuilder();
+ var openai = builder.addAzureOpenAI("openai");
+ openai.addDeployment("chat", "gpt-4o-mini", "2024-07-18");
+ var api = builder.addContainer("api", "redis:latest");
+ api.withCognitiveServicesRoleAssignments(openai, new AzureOpenAIRole[] { AzureOpenAIRole.COGNITIVE_SERVICES_OPEN_AIUSER });
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.CognitiveServices/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.CognitiveServices/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..3a110f0e48a
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.CognitiveServices/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.CognitiveServices": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:55134;http://localhost:18351",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:34251",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:60108"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..d8cb58aba89
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/AppHost.java
@@ -0,0 +1,13 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var registry = builder.addAzureContainerRegistry("containerregistry")
+ .withPurgeTask("0 1 * * *", new WithPurgeTaskOptions().filter("samples:*").ago(7.0).keep(5.0).taskName("purge-samples"));
+ var environment = builder.addAzureContainerAppEnvironment("environment");
+ environment.withAzureContainerRegistry(registry);
+ environment.withContainerRegistryRoleAssignments(registry, new AzureContainerRegistryRole[] { AzureContainerRegistryRole.ACR_PULL, AzureContainerRegistryRole.ACR_PUSH });
+ var registryFromEnvironment = environment.getAzureContainerRegistry();
+ registryFromEnvironment.withPurgeTask("0 2 * * *", new WithPurgeTaskOptions().filter("environment:*").ago(14.0).keep(2.0));
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..05b2a6ff23f
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.ContainerRegistry/ValidationAppHost/aspire.config.json
@@ -0,0 +1,22 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.ContainerRegistry": "",
+ "Aspire.Hosting.Azure.AppContainers": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.CosmosDB/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.CosmosDB/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..c9a470bb048
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.CosmosDB/ValidationAppHost/AppHost.java
@@ -0,0 +1,34 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ // 1) addAzureCosmosDB
+ var cosmos = builder.addAzureCosmosDB("cosmos");
+ // 2) withDefaultAzureSku
+ cosmos.withDefaultAzureSku();
+ // 3) addCosmosDatabase
+ var db = cosmos.addCosmosDatabase("app-db", "appdb");
+ // 4) addContainer (single partition key path)
+ db.addContainer("orders", "/orderId", "orders-container");
+ // 5) addContainerWithPartitionKeyPaths (IEnumerable export)
+ db.addContainerWithPartitionKeyPaths("events", new String[] { "/tenantId", "/eventId" }, "events-container");
+ // 6) withAccessKeyAuthentication
+ cosmos.withAccessKeyAuthentication();
+ // 7) withAccessKeyAuthenticationWithKeyVault
+ var keyVault = builder.addAzureKeyVault("kv");
+ cosmos.withAccessKeyAuthenticationWithKeyVault(keyVault);
+ // 8) runAsEmulator + emulator container configuration methods
+ var cosmosEmulator = builder.addAzureCosmosDB("cosmos-emulator");
+ cosmosEmulator.runAsEmulator((emulator) -> {
+ emulator.withDataVolume("cosmos-emulator-data"); // 9) withDataVolume
+ emulator.withGatewayPort(18081.0); // 10) withGatewayPort
+ emulator.withPartitionCount(25); // 11) withPartitionCount
+ });
+ // 12) runAsPreviewEmulator + 13) withDataExplorer
+ var cosmosPreview = builder.addAzureCosmosDB("cosmos-preview-emulator");
+ cosmosPreview.runAsPreviewEmulator((emulator) -> {
+ emulator.withDataExplorer(11234.0);
+ });
+ var app = builder.build();
+ app.run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.CosmosDB/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.CosmosDB/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..34ccc12e203
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.CosmosDB/ValidationAppHost/aspire.config.json
@@ -0,0 +1,22 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.CosmosDB": "",
+ "Aspire.Hosting.Azure.KeyVault": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:52066;http://localhost:47700",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:62976",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:34207"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.EventHubs/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.EventHubs/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..5fb963795a8
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.EventHubs/ValidationAppHost/AppHost.java
@@ -0,0 +1,23 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var eventHubs = builder.addAzureEventHubs("eventhubs");
+ eventHubs.withEventHubsRoleAssignments(eventHubs, new AzureEventHubsRole[] { AzureEventHubsRole.AZURE_EVENT_HUBS_DATA_OWNER });
+ var hub = eventHubs.addHub("orders", "orders-hub");
+ hub.withProperties((configuredHub) -> {
+ configuredHub.setHubName("orders-hub");
+ var _hubName = configuredHub.hubName();
+ configuredHub.setPartitionCount(2);
+ var _partitionCount = configuredHub.partitionCount();
+ });
+ var consumerGroup = hub.addConsumerGroup("processors", "processor-group");
+ consumerGroup.withEventHubsRoleAssignments(eventHubs, new AzureEventHubsRole[] { AzureEventHubsRole.AZURE_EVENT_HUBS_DATA_RECEIVER });
+ eventHubs.runAsEmulator((emulator) -> {
+ emulator
+ .withHostPort(5673.0)
+ .withConfigurationFile("./eventhubs.config.json")
+ .withEventHubsRoleAssignments(eventHubs, new AzureEventHubsRole[] { AzureEventHubsRole.AZURE_EVENT_HUBS_DATA_SENDER });
+ });
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.EventHubs/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.EventHubs/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..93c44b95c38
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.EventHubs/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.EventHubs": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.Functions/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.Functions/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..9560020455e
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.Functions/ValidationAppHost/AppHost.java
@@ -0,0 +1,25 @@
+import aspire.*;
+
+void main() throws Exception {
+ // Aspire TypeScript AppHost - Azure Functions validation
+ // Exercises every exported member of Aspire.Hosting.Azure.Functions
+ var builder = DistributedApplication.CreateBuilder();
+ // ── 1. addAzureFunctionsProject (path-based overload) ───────────────────────
+ var funcApp = builder.addAzureFunctionsProject(
+ "myfunc",
+ "../MyFunctions/MyFunctions.csproj"
+ );
+ // ── 2. withHostStorage - specify custom Azure Storage for Functions host ────
+ var storage = builder.addAzureStorage("funcstorage");
+ funcApp.withHostStorage(storage);
+ // ── 3. Fluent chaining - verify return types enable chaining ────────────────
+ var chainedFunc = builder
+ .addAzureFunctionsProject("chained-func", "../OtherFunc/OtherFunc.csproj")
+ .withHostStorage(storage);
+ chainedFunc.withEnvironment("MY_KEY", "my-value");
+ chainedFunc.withHttpEndpoint(new WithHttpEndpointOptions().port(7071.0));
+ // ── 4. withReference from base builder - standard resource references ───────
+ var anotherStorage = builder.addAzureStorage("appstorage");
+ funcApp.withReference(anotherStorage);
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.Functions/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.Functions/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..5a76f18d0dc
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.Functions/ValidationAppHost/aspire.config.json
@@ -0,0 +1,22 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.Functions": "",
+ "Aspire.Hosting.Azure.Storage": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:43912;http://localhost:19322",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:30254",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:15931"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.KeyVault/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.KeyVault/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..d58fefaff26
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.KeyVault/ValidationAppHost/AppHost.java
@@ -0,0 +1,33 @@
+import aspire.*;
+
+void main() throws Exception {
+ // Aspire TypeScript AppHost - Azure Key Vault validation
+ // Exercises every exported member of Aspire.Hosting.Azure.KeyVault
+ var builder = DistributedApplication.CreateBuilder();
+ // ── 1. addAzureKeyVault ──────────────────────────────────────────────────────
+ var vault = builder.addAzureKeyVault("vault");
+ // Parameters for secret-based APIs
+ var secretParam = builder.addParameter("secret-param", true);
+ var namedSecretParam = builder.addParameter("named-secret-param", true);
+ // Reference expressions for expression-based APIs
+ var exprSecretValue = ReferenceExpression.refExpr("secret-value-%s", secretParam);
+ var namedExprSecretValue = ReferenceExpression.refExpr("named-secret-value-%s", namedSecretParam);
+ // ── 2. withRoleAssignments ───────────────────────────────────────────────────
+ vault.withKeyVaultRoleAssignments(vault, new AzureKeyVaultRole[] { AzureKeyVaultRole.KEY_VAULT_READER, AzureKeyVaultRole.KEY_VAULT_SECRETS_USER });
+ // ── 3. addSecret ─────────────────────────────────────────────────────────────
+ var secretFromParameter = vault.addSecret("param-secret", secretParam);
+ // ── 4. addSecretFromExpression ───────────────────────────────────────────────
+ var secretFromExpression = vault.addSecretFromExpression("expr-secret", exprSecretValue);
+ // ── 5. addSecretWithName ─────────────────────────────────────────────────────
+ var namedSecretFromParameter = vault.addSecretWithName("secret-resource-param", "named-param-secret", namedSecretParam);
+ // ── 6. addSecretWithNameFromExpression ───────────────────────────────────────
+ var namedSecretFromExpression = vault.addSecretWithNameFromExpression("secret-resource-expr", "named-expr-secret", namedExprSecretValue);
+ // ── 7. getSecret ─────────────────────────────────────────────────────────────
+ var _existingSecretRef = vault.getSecret("param-secret");
+ // Apply role assignments to created secret resources to validate generic coverage.
+ secretFromParameter.withKeyVaultRoleAssignments(vault, new AzureKeyVaultRole[] { AzureKeyVaultRole.KEY_VAULT_SECRETS_USER });
+ secretFromExpression.withKeyVaultRoleAssignments(vault, new AzureKeyVaultRole[] { AzureKeyVaultRole.KEY_VAULT_READER });
+ namedSecretFromParameter.withKeyVaultRoleAssignments(vault, new AzureKeyVaultRole[] { AzureKeyVaultRole.KEY_VAULT_SECRETS_OFFICER });
+ namedSecretFromExpression.withKeyVaultRoleAssignments(vault, new AzureKeyVaultRole[] { AzureKeyVaultRole.KEY_VAULT_READER });
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.KeyVault/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.KeyVault/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..c9677bb0c22
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.KeyVault/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.KeyVault": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:52800;http://localhost:51322",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10120",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:11479"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.Kusto/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.Kusto/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..dde9ff22c0a
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.Kusto/ValidationAppHost/AppHost.java
@@ -0,0 +1,24 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var kusto = builder.addAzureKustoCluster("kusto").runAsEmulator((emulator) -> {
+ emulator.withHostPort(8088.0);
+ });
+ var defaultDatabase = kusto.addReadWriteDatabase("samples");
+ var customDatabase = kusto.addReadWriteDatabase("analytics", "AnalyticsDb");
+ defaultDatabase.withCreationScript(".create database Samples ifnotexists");
+ customDatabase.withCreationScript(".create database AnalyticsDb ifnotexists");
+ var _isEmulator = kusto.isEmulator();
+ var _clusterUri = kusto.uriExpression();
+ var _clusterConnectionString = kusto.connectionStringExpression();
+ var _defaultDatabaseName = defaultDatabase.databaseName();
+ var _defaultDatabaseParent = defaultDatabase.parent();
+ var _defaultDatabaseConnectionString = defaultDatabase.connectionStringExpression();
+ var _defaultDatabaseCreationScript = defaultDatabase.getDatabaseCreationScript();
+ var _customDatabaseName = customDatabase.databaseName();
+ var _customDatabaseParent = customDatabase.parent();
+ var _customDatabaseConnectionString = customDatabase.connectionStringExpression();
+ var _customDatabaseCreationScript = customDatabase.getDatabaseCreationScript();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.Kusto/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.Kusto/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..2852208ec5a
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.Kusto/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.Kusto": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.OperationalInsights/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.OperationalInsights/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..7a9dea707de
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.OperationalInsights/ValidationAppHost/AppHost.java
@@ -0,0 +1,12 @@
+import aspire.*;
+
+void main() throws Exception {
+ // Aspire TypeScript AppHost - Azure Operational Insights validation
+ // Exercises exported members of Aspire.Hosting.Azure.OperationalInsights
+ var builder = DistributedApplication.CreateBuilder();
+ // addAzureLogAnalyticsWorkspace
+ var logAnalytics = builder.addAzureLogAnalyticsWorkspace("logs");
+ // Fluent call on the returned resource builder
+ logAnalytics.withUrl("https://example.local/logs");
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.OperationalInsights/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.OperationalInsights/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..bf0451493b8
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.OperationalInsights/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.OperationalInsights": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:52066;http://localhost:47700",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:62976",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:34207"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.PostgreSQL/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.PostgreSQL/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..ab3c5358286
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.PostgreSQL/ValidationAppHost/AppHost.java
@@ -0,0 +1,22 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ // 1) addAzurePostgresFlexibleServer - main factory method
+ var pg = builder.addAzurePostgresFlexibleServer("pg");
+ // 2) addDatabase - child resource
+ var db = pg.addDatabase("mydb", "appdb");
+ // 3) withPasswordAuthentication - configures password auth (auto KeyVault)
+ var pgAuth = builder.addAzurePostgresFlexibleServer("pg-auth");
+ pgAuth.withPasswordAuthentication();
+ // 4) runAsContainer - run as local PostgreSQL container
+ var pgContainer = builder.addAzurePostgresFlexibleServer("pg-container");
+ pgContainer.runAsContainer((container) -> {
+ // Exercise PostgresServerResource builder methods within the callback
+ container.withLifetime(ContainerLifetime.PERSISTENT);
+ });
+ // 5) addDatabase on container-mode server
+ var dbContainer = pgContainer.addDatabase("containerdb");
+ var app = builder.build();
+ app.run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.PostgreSQL/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.PostgreSQL/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..a62de16f4b0
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.PostgreSQL/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.PostgreSQL": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:52067;http://localhost:47701",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:62977",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:34208"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.Redis/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.Redis/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..7bcb460e1a7
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.Redis/ValidationAppHost/AppHost.java
@@ -0,0 +1,30 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var keyVault = builder.addAzureKeyVault("vault");
+ var cache = builder.addAzureManagedRedis("cache");
+ var accessKeyCache = builder.addAzureManagedRedis("cache-access-key");
+ var containerCache = builder.addAzureManagedRedis("cache-container");
+ accessKeyCache.withAccessKeyAuthentication();
+ accessKeyCache.withAccessKeyAuthenticationWithKeyVault(keyVault);
+ containerCache.runAsContainer((container) -> {
+ container.withVolume("/data");
+ });
+ var _connectionString = cache.connectionStringExpression();
+ var _hostName = cache.hostName();
+ var _port = cache.port();
+ var _uri = cache.uriExpression();
+ var _useAccessKeyAuthentication = cache.useAccessKeyAuthentication();
+ var _accessKeyConnectionString = accessKeyCache.connectionStringExpression();
+ var _accessKeyHostName = accessKeyCache.hostName();
+ var _accessKeyPassword = accessKeyCache.password();
+ var _accessKeyUri = accessKeyCache.uriExpression();
+ var _usesAccessKeyAuthentication = accessKeyCache.useAccessKeyAuthentication();
+ var _containerConnectionString = containerCache.connectionStringExpression();
+ var _containerHostName = containerCache.hostName();
+ var _containerPort = containerCache.port();
+ var _containerPassword = containerCache.password();
+ var _containerUri = containerCache.uriExpression();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.Redis/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.Redis/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..3023a9cac69
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.Redis/ValidationAppHost/aspire.config.json
@@ -0,0 +1,22 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.Redis": "",
+ "Aspire.Hosting.Azure.KeyVault": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.Search/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.Search/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..1991ccc23cd
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.Search/ValidationAppHost/AppHost.java
@@ -0,0 +1,8 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var search = builder.addAzureSearch("search");
+ search.withSearchRoleAssignments(search, new AzureSearchRole[] { AzureSearchRole.SEARCH_SERVICE_CONTRIBUTOR, AzureSearchRole.SEARCH_INDEX_DATA_READER });
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.Search/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.Search/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..b470b9e3c6a
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.Search/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.Search": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.ServiceBus/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.ServiceBus/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..ec4fbbe7ab6
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.ServiceBus/ValidationAppHost/AppHost.java
@@ -0,0 +1,42 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var serviceBus = builder.addAzureServiceBus("messaging");
+ var emulatorBus = builder
+ .addAzureServiceBus("messaging-emulator")
+ .runAsEmulator((emulator) -> { emulator.withConfigurationFile("./servicebus-config.json"); emulator.withHostPort(5672.0); });
+ var queue = serviceBus.addServiceBusQueue("orders", "orders-queue");
+ var topic = serviceBus.addServiceBusTopic("events", "events-topic");
+ var subscription = topic.addServiceBusSubscription("audit", "audit-sub");
+ var filter = new AzureServiceBusCorrelationFilter();
+ filter.setCorrelationId("order-123");
+ filter.setSubject("OrderCreated");
+ filter.setContentType("application/json");
+ filter.setMessageId("msg-001");
+ filter.setReplyTo("reply-queue");
+ filter.setSessionId("session-1");
+ filter.setSendTo("destination");
+
+ var rule = new AzureServiceBusRule();
+ rule.setName("order-filter");
+ rule.setFilterType(AzureServiceBusFilterType.CORRELATION_FILTER);
+ rule.setCorrelationFilter(filter);
+ queue.withProperties((q) -> { q.setDeadLetteringOnMessageExpiration(true); q.setDefaultMessageTimeToLive(36000000000.0); q.setDuplicateDetectionHistoryTimeWindow(6000000000.0); q.setForwardDeadLetteredMessagesTo("dead-letter-queue"); q.setForwardTo("forwarding-queue"); q.setLockDuration(300000000.0); q.setMaxDeliveryCount(10); q.setRequiresDuplicateDetection(true); q.setRequiresSession(false); var _dlq = q.deadLetteringOnMessageExpiration(); var _ttl = q.defaultMessageTimeToLive(); var _fwd = q.forwardTo(); var _maxDel = q.maxDeliveryCount(); });
+ topic.withProperties((t) -> { t.setDefaultMessageTimeToLive(6048000000000.0); t.setDuplicateDetectionHistoryTimeWindow(3000000000.0); t.setRequiresDuplicateDetection(false); var _dupDetect = t.requiresDuplicateDetection(); });
+ subscription.withProperties((s) -> { s.setDeadLetteringOnMessageExpiration(true); s.setDefaultMessageTimeToLive(72000000000.0); s.setForwardDeadLetteredMessagesTo("sub-dlq"); s.setForwardTo("sub-forward"); s.setLockDuration(600000000.0); s.setMaxDeliveryCount(5); s.setRequiresSession(false); var _lock = s.lockDuration(); var _rules = s.rules(); });
+ serviceBus.withServiceBusRoleAssignments(serviceBus, new AzureServiceBusRole[] { AzureServiceBusRole.AZURE_SERVICE_BUS_DATA_OWNER, AzureServiceBusRole.AZURE_SERVICE_BUS_DATA_SENDER, AzureServiceBusRole.AZURE_SERVICE_BUS_DATA_RECEIVER });
+ queue.withServiceBusRoleAssignments(serviceBus, new AzureServiceBusRole[] { AzureServiceBusRole.AZURE_SERVICE_BUS_DATA_RECEIVER });
+ topic.withServiceBusRoleAssignments(serviceBus, new AzureServiceBusRole[] { AzureServiceBusRole.AZURE_SERVICE_BUS_DATA_SENDER });
+ subscription.withServiceBusRoleAssignments(serviceBus, new AzureServiceBusRole[] { AzureServiceBusRole.AZURE_SERVICE_BUS_DATA_RECEIVER });
+ var _sqlFilter = AzureServiceBusFilterType.SQL_FILTER;
+ var _correlationFilter = AzureServiceBusFilterType.CORRELATION_FILTER;
+ serviceBus
+ .addServiceBusQueue("chained-queue")
+ .withProperties((_q) -> { });
+ serviceBus
+ .addServiceBusTopic("chained-topic")
+ .addServiceBusSubscription("chained-sub")
+ .withProperties((_s) -> { });
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.ServiceBus/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.ServiceBus/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..6cf597936a9
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.ServiceBus/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.ServiceBus": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:24190;http://localhost:42530",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:60807",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:16724"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.SignalR/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.SignalR/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..9f7d154cdae
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.SignalR/ValidationAppHost/AppHost.java
@@ -0,0 +1,8 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var signalr = builder.addAzureSignalR("signalr");
+ signalr.runAsEmulator();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.SignalR/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.SignalR/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..30917d54c7f
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.SignalR/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.SignalR": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.Sql/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.Sql/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..bf949c84dda
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.Sql/ValidationAppHost/AppHost.java
@@ -0,0 +1,27 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var storage = builder.addAzureStorage("storage");
+ var sqlServer = builder.addAzureSqlServer("sql");
+ var db = sqlServer.addDatabase("mydb");
+ var db2 = sqlServer.addDatabase("inventory", "inventorydb");
+ db2.withDefaultAzureSku();
+ sqlServer.runAsContainer((container) -> { });
+ sqlServer.withAdminDeploymentScriptStorage(storage);
+ var _db3 = sqlServer.addDatabase("analytics").withDefaultAzureSku();
+ var _hostName = sqlServer.hostName();
+ var _port = sqlServer.port();
+ var _uriExpression = sqlServer.uriExpression();
+ var _connectionStringExpression = sqlServer.connectionStringExpression();
+ var _jdbcConnectionString = sqlServer.jdbcConnectionString();
+ var _isContainer = sqlServer.isContainer();
+ var _databases = sqlServer.databases();
+ var _parent = db.parent();
+ var _dbConnectionStringExpression = db.connectionStringExpression();
+ var _databaseName = db.databaseName();
+ var _dbIsContainer = db.isContainer();
+ var _dbUriExpression = db.uriExpression();
+ var _dbJdbcConnectionString = db.jdbcConnectionString();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.Sql/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.Sql/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..40f4e44f1b9
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.Sql/ValidationAppHost/aspire.config.json
@@ -0,0 +1,22 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.Sql": "",
+ "Aspire.Hosting.Azure.Storage": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:58695;http://localhost:30637",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:44501",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:55515"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.Storage/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.Storage/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..2cd5b5f5706
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.Storage/ValidationAppHost/AppHost.java
@@ -0,0 +1,24 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var storage = builder.addAzureStorage("storage");
+ storage.runAsEmulator();
+ storage.withStorageRoleAssignments(storage, new AzureStorageRole[] { AzureStorageRole.STORAGE_BLOB_DATA_CONTRIBUTOR, AzureStorageRole.STORAGE_QUEUE_DATA_CONTRIBUTOR });
+ // Callbacks are currently not working
+ // storage.runAsEmulator({
+ // configureContainer: (emulator) -> {
+ // emulator.withBlobPort(10000);
+ // emulator.withQueuePort(10001);
+ // emulator.withTablePort(10002);
+ // emulator.withDataVolume();
+ // emulator.withApiVersionCheck(new WithApiVersionCheckOptions().enable(false));
+ // }
+ // });
+ storage.addBlobs("blobs");
+ storage.addTables("tables");
+ storage.addQueues("queues");
+ storage.addQueue("orders");
+ storage.addBlobContainer("images");
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.Storage/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.Storage/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..291d33b2e94
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.Storage/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.Storage": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:52066;http://localhost:47700",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:62976",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:34207"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.WebPubSub/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure.WebPubSub/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..d926ec1f2ff
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.WebPubSub/ValidationAppHost/AppHost.java
@@ -0,0 +1,22 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ // addAzureWebPubSub - factory method
+ var webpubsub = builder.addAzureWebPubSub("webpubsub");
+ // addHub - adds a hub to the Web PubSub resource (with optional hubName)
+ var hub = webpubsub.addHub("myhub");
+ var hubWithName = webpubsub.addHub("hub2", "customhub");
+ // addEventHandler - adds an event handler to a hub
+ hub.addEventHandler(ReferenceExpression.refExpr("https://example.com/handler"));
+ hub.addEventHandler(ReferenceExpression.refExpr("https://example.com/handler2"), new AddEventHandlerOptions().userEventPattern("event1").systemEvents(new String[] { "connect", "connected" }));
+ // withRoleAssignments - assigns roles on a container resource
+ var container = builder.addContainer("mycontainer", "mcr.microsoft.com/dotnet/samples:aspnetapp");
+ container.withWebPubSubRoleAssignments(webpubsub, new AzureWebPubSubRole[] { AzureWebPubSubRole.WEB_PUB_SUB_SERVICE_OWNER, AzureWebPubSubRole.WEB_PUB_SUB_SERVICE_READER, AzureWebPubSubRole.WEB_PUB_SUB_CONTRIBUTOR });
+ // withRoleAssignments - also available directly on AzureWebPubSubResource builder
+ webpubsub.withWebPubSubRoleAssignments(webpubsub, new AzureWebPubSubRole[] { AzureWebPubSubRole.WEB_PUB_SUB_SERVICE_READER });
+ // withReference - generic, works via IResourceWithConnectionString
+ container.withReference(webpubsub);
+ container.withReference(hub);
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure.WebPubSub/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure.WebPubSub/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..bd5ae40cbf5
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure.WebPubSub/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure.WebPubSub": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Azure/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..27e1b57f5a3
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure/ValidationAppHost/AppHost.java
@@ -0,0 +1,83 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ builder.addAzureProvisioning();
+ var location = builder.addParameter("location");
+ var resourceGroup = builder.addParameter("resource-group");
+ var existingName = builder.addParameter("existing-name");
+ var existingResourceGroup = builder.addParameter("existing-resource-group");
+ var connectionString = builder.addConnectionString("azure-validation", "AZURE_VALIDATION_CONNECTION_STRING");
+ var azureEnvironment = builder.addAzureEnvironment();
+ azureEnvironment.withLocation(location).withResourceGroup(resourceGroup);
+ var container = builder.addContainer("api", "mcr.microsoft.com/dotnet/samples:aspnetapp");
+ container.withHttpEndpoint(new WithHttpEndpointOptions().name("http").targetPort(8080.0));
+ var executable = builder.addExecutable("worker", "dotnet", ".", new String[] { "--info" });
+ executable.withHttpEndpoint(new WithHttpEndpointOptions().name("http").targetPort(8081.0));
+ var endpoint = container.getEndpoint("http");
+ var fileBicep = builder.addBicepTemplate("file-bicep", "./validation.bicep");
+ fileBicep.publishAsConnectionString();
+ fileBicep.clearDefaultRoleAssignments();
+ fileBicep.getBicepIdentifier();
+ fileBicep.isExisting();
+ fileBicep.runAsExisting("file-bicep-existing", "rg-bicep");
+ fileBicep.runAsExistingFromParameters(existingName, existingResourceGroup);
+ fileBicep.publishAsExisting("file-bicep-existing", "rg-bicep");
+ fileBicep.publishAsExistingFromParameters(existingName, existingResourceGroup);
+ fileBicep.asExisting(existingName, existingResourceGroup);
+ var inlineBicep = builder.addBicepTemplateString("inline-bicep", """
+ output inlineUrl string = "https://inline.example.com"
+ """);
+ inlineBicep.publishAsConnectionString();
+ inlineBicep.clearDefaultRoleAssignments();
+ inlineBicep.getBicepIdentifier();
+ inlineBicep.isExisting();
+ var infrastructure = builder.addAzureInfrastructure("infra", (infrastructureContext) -> { });
+ var infrastructureOutput = infrastructure.getOutput("serviceUrl");
+ infrastructureOutput.name();
+ infrastructureOutput.value();
+ infrastructureOutput.valueExpression();
+ infrastructure.withParameter("empty");
+ infrastructure.withParameterStringValue("plain", "value");
+ infrastructure.withParameterStringValues("list", new String[] { "one", "two" });
+ infrastructure.withParameterFromParameter("fromParam", existingName);
+ infrastructure.withParameterFromConnectionString("fromConnection", connectionString);
+ infrastructure.withParameterFromOutput("fromOutput", infrastructureOutput);
+ infrastructure.withParameterFromReferenceExpression("fromExpression", ReferenceExpression.refExpr("https://%s", endpoint));
+ infrastructure.withParameterFromEndpoint("fromEndpoint", endpoint);
+ infrastructure.publishAsConnectionString();
+ infrastructure.clearDefaultRoleAssignments();
+ infrastructure.getBicepIdentifier();
+ infrastructure.isExisting();
+ infrastructure.runAsExisting("infra-existing", "rg-infra");
+ infrastructure.runAsExistingFromParameters(existingName, existingResourceGroup);
+ infrastructure.publishAsExisting("infra-existing", "rg-infra");
+ infrastructure.publishAsExistingFromParameters(existingName, existingResourceGroup);
+ infrastructure.asExisting(existingName, existingResourceGroup);
+ var identity = builder.addAzureUserAssignedIdentity("identity");
+ identity.configureInfrastructure((infrastructureContext) -> { });
+ identity.withParameter("identityEmpty");
+ identity.withParameterStringValue("identityPlain", "value");
+ identity.withParameterStringValues("identityList", new String[] { "a", "b" });
+ identity.withParameterFromParameter("identityFromParam", existingName);
+ identity.withParameterFromConnectionString("identityFromConnection", connectionString);
+ identity.withParameterFromOutput("identityFromOutput", infrastructureOutput);
+ identity.withParameterFromReferenceExpression("identityFromExpression", ReferenceExpression.refExpr("%s", location));
+ identity.withParameterFromEndpoint("identityFromEndpoint", endpoint);
+ identity.publishAsConnectionString();
+ identity.clearDefaultRoleAssignments();
+ identity.getBicepIdentifier();
+ identity.isExisting();
+ identity.runAsExisting("identity-existing", "rg-identity");
+ identity.runAsExistingFromParameters(existingName, existingResourceGroup);
+ identity.publishAsExisting("identity-existing", "rg-identity");
+ identity.publishAsExistingFromParameters(existingName, existingResourceGroup);
+ identity.asExisting(existingName, existingResourceGroup);
+ container.withEnvironmentFromOutput("INFRA_URL", infrastructureOutput);
+ container.withEnvironmentFromKeyVaultSecret("SECRET_FROM_IDENTITY", identity);
+ container.withAzureUserAssignedIdentity(identity);
+ executable.withEnvironmentFromOutput("INFRA_URL", infrastructureOutput);
+ executable.withEnvironmentFromKeyVaultSecret("SECRET_FROM_IDENTITY", identity);
+ executable.withAzureUserAssignedIdentity(identity);
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Azure/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Azure/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..2c363f61824
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Azure/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Azure": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.DevTunnels/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.DevTunnels/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..78f83de4b96
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.DevTunnels/ValidationAppHost/AppHost.java
@@ -0,0 +1,45 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ // Test 1: Basic dev tunnel resource creation (addDevTunnel)
+ var tunnel = builder.addDevTunnel("mytunnel");
+ // Test 2: addDevTunnel with tunnelId option
+ var tunnel2 = builder.addDevTunnel("mytunnel2", new AddDevTunnelOptions().tunnelId("custom-tunnel-id"));
+ // Test 3: withAnonymousAccess
+ builder.addDevTunnel("anon-tunnel")
+ .withAnonymousAccess();
+ // Test 4: Add a container to reference its endpoints
+ var web = builder.addContainer("web", "nginx");
+ web.withHttpEndpoint(new WithHttpEndpointOptions().port(80.0));
+ // Test 5: withTunnelReference with EndpointReference (expose a specific endpoint)
+ var webEndpoint = web.getEndpoint("http");
+ tunnel.withTunnelReference(webEndpoint);
+ // Test 6: withTunnelReferenceAnonymous with EndpointReference + allowAnonymous
+ var web2 = builder.addContainer("web2", "nginx");
+ web2.withHttpEndpoint(new WithHttpEndpointOptions().port(8080.0));
+ var web2Endpoint = web2.getEndpoint("http");
+ tunnel2.withTunnelReferenceAnonymous(web2Endpoint, true);
+ // Test 7: withTunnelReferenceAll - expose all endpoints on a resource
+ var tunnel3 = builder.addDevTunnel("all-endpoints-tunnel");
+ var web3 = builder.addContainer("web3", "nginx");
+ web3.withHttpEndpoint(new WithHttpEndpointOptions().port(80.0));
+ tunnel3.withTunnelReferenceAll(web3, false);
+ // Test 8: getTunnelEndpoint - get the public tunnel endpoint for a specific endpoint
+ var web4 = builder.addContainer("web4", "nginx");
+ web4.withHttpEndpoint(new WithHttpEndpointOptions().port(80.0));
+ var web4Endpoint = web4.getEndpoint("http");
+ var tunnel4 = builder.addDevTunnel("get-endpoint-tunnel");
+ tunnel4.withTunnelReference(web4Endpoint);
+ var _tunnelEndpoint = tunnel4.getTunnelEndpoint(web4Endpoint);
+ // Test 9: addDevTunnel with the dedicated polyglot parameters
+ var tunnel5 = builder.addDevTunnel("configured-tunnel", new AddDevTunnelOptions().tunnelId("configured-tunnel-id").allowAnonymous(true).description("Configured by the polyglot validation app").labels(new String[] { "validation", "polyglot" }));
+ var web5 = builder.addContainer("web5", "nginx");
+ web5.withHttpEndpoint(new WithHttpEndpointOptions().port(9090.0));
+ var web5Endpoint = web5.getEndpoint("http");
+ tunnel5.withTunnelReferenceAnonymous(web5Endpoint, true);
+ // Test 10: Chained configuration
+ builder.addDevTunnel("chained-tunnel")
+ .withAnonymousAccess();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.DevTunnels/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.DevTunnels/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..879c0570e1d
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.DevTunnels/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.DevTunnels": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Docker/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Docker/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..5f4d1067162
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Docker/ValidationAppHost/AppHost.java
@@ -0,0 +1,43 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var compose = builder.addDockerComposeEnvironment("compose");
+ var api = builder.addContainer("api", "nginx:alpine");
+ compose.withProperties((environment) -> {
+ environment.setDefaultNetworkName("validation-network");
+ var _defaultNetworkName = environment.defaultNetworkName();
+ environment.setDashboardEnabled(true);
+ var _dashboardEnabled = environment.dashboardEnabled();
+ var _environmentName = environment.name();
+ });
+ compose.withDashboard(false);
+ compose.withDashboard();
+ compose.configureDashboard((dashboard) -> {
+ dashboard.withHostPort(18888.0);
+ dashboard.withForwardedHeaders(true);
+ var _dashboardName = dashboard.name();
+ var primaryEndpoint = dashboard.primaryEndpoint();
+ var _primaryUrl = primaryEndpoint.url();
+ var _primaryHost = primaryEndpoint.host();
+ var _primaryPort = primaryEndpoint.port();
+ var otlpGrpcEndpoint = dashboard.otlpGrpcEndpoint();
+ var _otlpGrpcUrl = otlpGrpcEndpoint.url();
+ var _otlpGrpcPort = otlpGrpcEndpoint.port();
+ });
+ api.publishAsDockerComposeService((composeService, service) -> {
+ service.setContainerName("validation-api");
+ service.setPullPolicy("always");
+ service.setRestart("unless-stopped");
+ var _composeServiceName = composeService.name();
+ var composeEnvironment = composeService.parent();
+ var _composeEnvironmentName = composeEnvironment.name();
+ var _serviceContainerName = service.containerName();
+ var _servicePullPolicy = service.pullPolicy();
+ var _serviceRestart = service.restart();
+ });
+ var _resolvedDefaultNetworkName = compose.defaultNetworkName();
+ var _resolvedDashboardEnabled = compose.dashboardEnabled();
+ var _resolvedName = compose.name();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Docker/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Docker/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..3bfab047c5b
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Docker/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Docker": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29650;http://localhost:28831",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10875",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13219"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Foundry/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Foundry/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..e186e2154a1
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Foundry/ValidationAppHost/AppHost.java
@@ -0,0 +1,99 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+
+ var foundry = builder.addFoundry("foundry");
+
+ var chat = foundry
+ .addDeployment("chat", "Phi-4", "1", "Microsoft")
+ .withProperties((deployment) -> {
+ deployment.setDeploymentName("chat-deployment");
+ deployment.setSkuCapacity(10);
+ var _capacity = deployment.skuCapacity();
+ });
+
+ var model = new FoundryModel();
+ model.setName("gpt-4.1-mini");
+ model.setVersion("1");
+ model.setFormat("OpenAI");
+
+ var _chatFromModel = foundry.addDeploymentFromModel("chat-from-model", model);
+
+ var localFoundry = builder.addFoundry("local-foundry")
+ .runAsFoundryLocal();
+
+ var _localChat = localFoundry.addDeployment("local-chat", "Phi-3.5-mini-instruct", "1", "Microsoft");
+
+ var registry = builder.addAzureContainerRegistry("registry");
+ var keyVault = builder.addAzureKeyVault("vault");
+ var appInsights = builder.addAzureApplicationInsights("insights");
+ var cosmos = builder.addAzureCosmosDB("cosmos");
+ var storage = builder.addAzureStorage("storage");
+
+ var project = foundry.addProject("project");
+ project.withContainerRegistry(registry);
+ project.withKeyVault(keyVault);
+ project.withAppInsights(appInsights);
+
+ var _cosmosConnection = project.addCosmosConnection(cosmos);
+ var _storageConnection = project.addStorageConnection(storage);
+ var _registryConnection = project.addContainerRegistryConnection(registry);
+ var _keyVaultConnection = project.addKeyVaultConnection(keyVault);
+
+ var builderProjectFoundry = builder.addFoundry("builder-project-foundry");
+ var builderProject = builderProjectFoundry.addProject("builder-project");
+ var _builderProjectModel = builderProject.addModelDeployment("builder-project-model", "Phi-4-mini", "1", "Microsoft");
+ var projectModel = project.addModelDeploymentFromModel("project-model", model);
+ var _promptAgent = project.addAndPublishPromptAgent(projectModel, "writer-agent", "Write concise answers.");
+ var hostedAgent = builder.addExecutable(
+ "hosted-agent",
+ "node",
+ ".",
+ new String[] {
+ "-e",
+ """
+const http = require('node:http');
+const port = Number(process.env.DEFAULT_AD_PORT ?? '8088');
+const server = http.createServer((req, res) => {
+ if (req.url === '/liveness' || req.url === '/readiness') {
+ res.writeHead(200, { 'content-type': 'text/plain' });
+ res.end('ok');
+ return;
+ }
+ if (req.url === '/responses') {
+ res.writeHead(200, { 'content-type': 'application/json' });
+ res.end(JSON.stringify({ output: 'hello from validation app host' }));
+ return;
+ }
+ res.writeHead(404);
+ res.end();
+});
+server.listen(port, '127.0.0.1');
+"""
+ });
+
+ hostedAgent.publishAsHostedAgent(new PublishAsHostedAgentOptions()
+ .project(project)
+ .configure((configuration) -> {
+ configuration.setDescription("Validation hosted agent");
+ configuration.setCpu(1);
+ configuration.setMemory(2);
+ configuration.setMetadata(null);
+ configuration.setEnvironmentVariables(null);
+ }));
+
+ var api = builder.addContainer("api", "nginx");
+ api.withRoleAssignments(foundry, new FoundryRole[] {
+ FoundryRole.COGNITIVE_SERVICES_OPEN_AIUSER,
+ FoundryRole.COGNITIVE_SERVICES_USER
+ });
+
+ var _deploymentName = chat.deploymentName();
+ var _modelName = chat.modelName();
+ var _format = chat.format();
+ var _version = chat.modelVersion();
+ var _connectionString = chat.connectionStringExpression();
+
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Foundry/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Foundry/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..3979280e633
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Foundry/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Foundry": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Garnet/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Garnet/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..94b9e10fc16
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Garnet/ValidationAppHost/AppHost.java
@@ -0,0 +1,14 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var cache = builder.addGarnet("cache");
+ // ---- Property access on GarnetResource ----
+ var garnet = cache;
+ var _endpoint = garnet.primaryEndpoint();
+ var _host = garnet.host();
+ var _port = garnet.port();
+ var _uri = garnet.uriExpression();
+ var _cstr = garnet.connectionStringExpression();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Garnet/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Garnet/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..7ff0549f91f
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Garnet/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Garnet": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.GitHub.Models/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.GitHub.Models/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..8a86cc4e3ed
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.GitHub.Models/ValidationAppHost/AppHost.java
@@ -0,0 +1,24 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ // 1) addGitHubModel - using the GitHubModelName enum
+ var githubModel = builder.addGitHubModel("chat", GitHubModelName.OPEN_AIGPT4O);
+ // 2) addGitHubModel - with organization parameter
+ var orgParam = builder.addParameter("gh-org");
+ var githubModelWithOrg = builder.addGitHubModel("chat-org", GitHubModelName.OPEN_AIGPT4O_MINI, orgParam);
+ // 3) addGitHubModelById - using a model identifier string for models not in the enum
+ var customModel = builder.addGitHubModelById("custom-chat", "custom-vendor/custom-model");
+ // 3) withApiKey - configure a custom API key parameter
+ var apiKey = builder.addParameter("gh-api-key", true);
+ githubModel.withApiKey(apiKey);
+ // 4) enableHealthCheck - integration-specific no-args health check
+ githubModel.enableHealthCheck();
+ // 5) withReference - pass GitHubModelResource as a connection string source to a container
+ var container = builder.addContainer("my-service", "mcr.microsoft.com/dotnet/samples:latest");
+ container.withReference(githubModel);
+ // 6) withReference - pass GitHubModelResource as a source to another container with custom connection name
+ container.withReference(githubModelWithOrg, new WithReferenceOptions().connectionName("github-model-org"));
+ var app = builder.build();
+ app.run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.GitHub.Models/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.GitHub.Models/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..7f96ab53a8f
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.GitHub.Models/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.GitHub.Models": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.JavaScript/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.JavaScript/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..fdebcee8d5f
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.JavaScript/ValidationAppHost/AppHost.java
@@ -0,0 +1,20 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var nodeApp = builder.addNodeApp("node-app", "./node-app", "server.js");
+ nodeApp.withNpm(new WithNpmOptions().install(false).installCommand("install").installArgs(new String[] { "--ignore-scripts" }));
+ nodeApp.withBun(new WithBunOptions().install(false).installArgs(new String[] { "--frozen-lockfile" }));
+ nodeApp.withYarn(new WithYarnOptions().install(false).installArgs(new String[] { "--immutable" }));
+ nodeApp.withPnpm(new WithPnpmOptions().install(false).installArgs(new String[] { "--frozen-lockfile" }));
+ nodeApp.withBuildScript("build", new String[] { "--mode", "production" });
+ nodeApp.withRunScript("dev", new String[] { "--host", "0.0.0.0" });
+ var javaScriptApp = builder.addJavaScriptApp("javascript-app", "./javascript-app", "start");
+ javaScriptApp.withEnvironment("NODE_ENV", "development");
+ var viteApp = builder.addViteApp("vite-app", "./vite-app", "dev");
+ viteApp.withViteConfig("./vite.custom.config.ts");
+ viteApp.withPnpm(new WithPnpmOptions().install(false).installArgs(new String[] { "--prod" }));
+ viteApp.withBuildScript("build", new String[] { "--mode", "production" });
+ viteApp.withRunScript("dev", new String[] { "--host" });
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.JavaScript/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.JavaScript/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..f9922f6986f
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.JavaScript/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.JavaScript": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29670;http://localhost:28851",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10895",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13239"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Kafka/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Kafka/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..fc0953afe5e
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Kafka/ValidationAppHost/AppHost.java
@@ -0,0 +1,26 @@
+import aspire.*;
+
+void main() throws Exception {
+ // Aspire TypeScript AppHost - Kafka integration validation
+ // Exercises all [AspireExport] methods for Aspire.Hosting.Kafka
+ var builder = DistributedApplication.CreateBuilder();
+ // addKafka - factory method with optional port
+ var kafka = builder.addKafka("broker");
+ // withKafkaUI - adds Kafka UI management container with callback
+ var kafkaWithUi = kafka.withKafkaUI(new WithKafkaUIOptions().configureContainer((ui) -> {
+ // withHostPort - sets the host port for Kafka UI
+ ui.withHostPort(9000.0);
+ }).containerName("my-kafka-ui"));
+ // withDataVolume - adds a data volume
+ kafkaWithUi.withDataVolume();
+ // withDataBindMount - adds a data bind mount
+ var kafka2 = builder.addKafka("broker2", 19092.0);
+ kafka2.withDataBindMount("/tmp/kafka-data");
+ // ---- Property access on KafkaServerResource ----
+ var _endpoint = kafka.primaryEndpoint();
+ var _host = kafka.host();
+ var _port = kafka.port();
+ var _internal = kafka.internalEndpoint();
+ var _cstr = kafka.connectionStringExpression();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Kafka/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Kafka/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..76090606dc0
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Kafka/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Kafka": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:16114;http://localhost:53304",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:18137",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:39824"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Keycloak/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Keycloak/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..77a9f594d78
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Keycloak/ValidationAppHost/AppHost.java
@@ -0,0 +1,27 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var adminUsername = builder.addParameter("keycloak-admin-user");
+ var adminPassword = builder.addParameter("keycloak-admin-password", true);
+ var keycloak = builder.addKeycloak("keycloak", new AddKeycloakOptions().port(8080.0));
+ keycloak
+ .withDataVolume("keycloak-data")
+ .withRealmImport(".")
+ .withEnabledFeatures(new String[] { "token-exchange", "opentelemetry" })
+ .withDisabledFeatures(new String[] { "admin-fine-grained-authz" })
+ .withOtlpExporter();
+ var keycloak2 = builder.addKeycloak("keycloak2")
+ .withDataBindMount(".")
+ .withRealmImport(".")
+ .withEnabledFeatures(new String[] { "rolling-updates" })
+ .withDisabledFeatures(new String[] { "scripts" })
+ .withOtlpExporterWithProtocol(OtlpProtocol.HTTP_PROTOBUF);
+ var consumer = builder.addContainer("consumer", "nginx");
+ consumer.withReference(keycloak);
+ consumer.withReference(keycloak2);
+ var _keycloakName = keycloak.name();
+ var _keycloakEntrypoint = keycloak.entrypoint();
+ var _keycloakShellExecution = keycloak.shellExecution();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Keycloak/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Keycloak/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..3620ced071a
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Keycloak/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Keycloak": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:44486;http://localhost:17648",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:12771",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:44457"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Kubernetes/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Kubernetes/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..5c2bc9e91d4
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Kubernetes/ValidationAppHost/AppHost.java
@@ -0,0 +1,36 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var kubernetes = builder.addKubernetesEnvironment("kube");
+ kubernetes.withProperties((environment) -> {
+ environment.setHelmChartName("validation-kubernetes");
+ var _configuredHelmChartName = environment.helmChartName();
+ environment.setHelmChartVersion("1.2.3");
+ var _configuredHelmChartVersion = environment.helmChartVersion();
+ environment.setHelmChartDescription("Validation Helm Chart");
+ var _configuredHelmChartDescription = environment.helmChartDescription();
+ environment.setDefaultStorageType("pvc");
+ var _configuredDefaultStorageType = environment.defaultStorageType();
+ environment.setDefaultStorageClassName("fast-storage");
+ var _configuredDefaultStorageClassName = environment.defaultStorageClassName();
+ environment.setDefaultStorageSize("5Gi");
+ var _configuredDefaultStorageSize = environment.defaultStorageSize();
+ environment.setDefaultStorageReadWritePolicy("ReadWriteMany");
+ var _configuredDefaultStorageReadWritePolicy = environment.defaultStorageReadWritePolicy();
+ environment.setDefaultImagePullPolicy("Always");
+ var _configuredDefaultImagePullPolicy = environment.defaultImagePullPolicy();
+ environment.setDefaultServiceType("LoadBalancer");
+ var _configuredDefaultServiceType = environment.defaultServiceType();
+ });
+ var _resolvedHelmChartName = kubernetes.helmChartName();
+ var _resolvedDefaultStorageClassName = kubernetes.defaultStorageClassName();
+ var _resolvedDefaultServiceType = kubernetes.defaultServiceType();
+ var serviceContainer = builder.addContainer("kube-service", "redis:alpine");
+ serviceContainer.publishAsKubernetesService((service) -> {
+ var _serviceName = service.name();
+ var serviceParent = service.parent();
+ var _serviceParentChartName = serviceParent.helmChartName();
+ });
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Kubernetes/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Kubernetes/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..00131712eb8
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Kubernetes/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Kubernetes": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29650;http://localhost:28831",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10875",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13219"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Maui/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Maui/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..bdeb7f0e0e4
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Maui/ValidationAppHost/AppHost.java
@@ -0,0 +1,16 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var maui = builder.addMauiProject(
+ "mauiapp",
+ "../../../../AspireWithMaui/AspireWithMaui.MauiClient/AspireWithMaui.MauiClient.csproj"
+ );
+ maui.addWindowsDevice("mauiapp-windows").withOtlpDevTunnel();
+ maui.addMacCatalystDevice("mauiapp-maccatalyst").withOtlpDevTunnel();
+ maui.addAndroidDevice("mauiapp-android-device", "emulator-5554").withOtlpDevTunnel();
+ maui.addAndroidEmulator("mauiapp-android-emulator", "Pixel_9_API_35").withOtlpDevTunnel();
+ maui.addiOSDevice("mauiapp-ios-device", "00008030-001234567890123A").withOtlpDevTunnel();
+ maui.addiOSSimulator("mauiapp-ios-simulator", "E25BBE37-69BA-4720-B6FD-D54C97791E79").withOtlpDevTunnel();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Maui/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Maui/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..2a75853a712
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Maui/ValidationAppHost/aspire.config.json
@@ -0,0 +1,24 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Maui": ""
+ },
+ "profiles": {
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "https://localhost:17001;http://localhost:17000",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:17003",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17004"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Milvus/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Milvus/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..b4cf4e7e3bb
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Milvus/ValidationAppHost/AppHost.java
@@ -0,0 +1,59 @@
+import aspire.*;
+
+void main() throws Exception {
+ // Aspire TypeScript AppHost - Milvus integration validation
+ // Exercises every exported member of Aspire.Hosting.Milvus
+ var builder = DistributedApplication.CreateBuilder();
+ // ── 1. addMilvus: basic Milvus server resource ─────────────────────────────
+ var milvus = builder.addMilvus("milvus");
+ // ── 2. addMilvus: with custom apiKey parameter ─────────────────────────────
+ var customKey = builder.addParameter("milvus-key", true);
+ var milvus2 = builder.addMilvus("milvus2", new AddMilvusOptions().apiKey(customKey));
+ // ── 3. addMilvus: with explicit gRPC port ──────────────────────────────────
+ builder.addMilvus("milvus3", new AddMilvusOptions().grpcPort(19531.0));
+ // ── 4. addDatabase: add database to Milvus server ──────────────────────────
+ var db = milvus.addDatabase("mydb");
+ // ── 5. addDatabase: with custom database name ──────────────────────────────
+ milvus.addDatabase("db2", "customdb");
+ // ── 6. withAttu: add Attu administration tool ──────────────────────────────
+ milvus.withAttu();
+ // ── 7. withAttu: with container name ────────────────────────────────────────
+ milvus2.withAttu(new WithAttuOptions().containerName("my-attu"));
+ // ── 8. withAttu: with configureContainer callback ──────────────────────────
+ builder.addMilvus("milvus-attu-cfg")
+ .withAttu(new WithAttuOptions().configureContainer((container) -> {
+ container.withHttpEndpoint(new WithHttpEndpointOptions().port(3001.0));
+ }));
+ // ── 9. withDataVolume: persistent data volume ──────────────────────────────
+ milvus.withDataVolume();
+ // ── 10. withDataVolume: with custom name ────────────────────────────────────
+ milvus2.withDataVolume(new WithDataVolumeOptions().name("milvus-data"));
+ // ── 11. withDataBindMount: bind mount for data ─────────────────────────────
+ builder.addMilvus("milvus-bind")
+ .withDataBindMount("./milvus-data");
+ // ── 12. withDataBindMount: with read-only flag ─────────────────────────────
+ builder.addMilvus("milvus-bind-ro")
+ .withDataBindMount("./milvus-data-ro", true);
+ // ── 13. withConfigurationFile: custom milvus.yaml ──────────────────────────
+ builder.addMilvus("milvus-cfg")
+ .withConfigurationFile("./milvus.yaml");
+ // ── 14. Fluent chaining: multiple With* methods ────────────────────────────
+ var milvusChained = builder.addMilvus("milvus-chained");
+ milvusChained.withLifetime(ContainerLifetime.PERSISTENT);
+ milvusChained.withDataVolume(new WithDataVolumeOptions().name("milvus-chained-data"));
+ milvusChained.withAttu();
+ // ── 15. withReference: use Milvus database from a container resource ───────
+ var api = builder.addContainer("api", "myregistry/myapp");
+ api.withReference(db);
+ // ── 16. withReference: use Milvus server directly ──────────────────────────
+ api.withReference(milvus);
+ // ---- Property access on MilvusServerResource ----
+ var _endpoint = milvus.primaryEndpoint();
+ var _host = milvus.host();
+ var _port = milvus.port();
+ var _token = milvus.token();
+ var _uri = milvus.uriExpression();
+ var _cstr = milvus.connectionStringExpression();
+ var _databases = milvus.databases();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Milvus/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Milvus/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..5185498c421
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Milvus/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Milvus": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.MongoDB/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.MongoDB/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..ef7f2095493
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.MongoDB/ValidationAppHost/AppHost.java
@@ -0,0 +1,47 @@
+import aspire.*;
+
+void main() throws Exception {
+ // Aspire TypeScript AppHost
+ // For more information, see: https://aspire.dev
+ var builder = DistributedApplication.CreateBuilder();
+ // Test 1: Basic MongoDB resource creation (addMongoDB)
+ var mongo = builder.addMongoDB("mongo");
+ // Test 2: Add database to MongoDB (addDatabase)
+ mongo.addDatabase("mydb");
+ // Test 3: Add database with custom database name
+ mongo.addDatabase("db2", "customdb2");
+ // Test 4: Test withDataVolume
+ builder.addMongoDB("mongo-volume")
+ .withDataVolume();
+ // Test 5: Test withDataVolume with custom name
+ builder.addMongoDB("mongo-volume-named")
+ .withDataVolume(new WithDataVolumeOptions().name("mongo-data"));
+ // Test 6: Test withHostPort on MongoExpress
+ builder.addMongoDB("mongo-express")
+ .withMongoExpress(new WithMongoExpressOptions().configureContainer((container) -> {
+ container.withHostPort(8082.0);
+ }));
+ // Test 7: Test withMongoExpress with container name
+ builder.addMongoDB("mongo-express-named")
+ .withMongoExpress(new WithMongoExpressOptions().containerName("my-mongo-express"));
+ // Test 8: Custom password parameter with addParameter
+ var customPassword = builder.addParameter("mongo-password", true);
+ builder.addMongoDB("mongo-custom-pass", new AddMongoDBOptions().password(customPassword));
+ // Test 9: Chained configuration - multiple With* methods
+ var mongoChained = builder.addMongoDB("mongo-chained");
+ mongoChained.withLifetime(ContainerLifetime.PERSISTENT);
+ mongoChained.withDataVolume(new WithDataVolumeOptions().name("mongo-chained-data"));
+ // Test 10: Add multiple databases to same server
+ mongoChained.addDatabase("app-db");
+ mongoChained.addDatabase("analytics-db", "analytics");
+ // ---- Property access on MongoDBServerResource ----
+ var _endpoint = mongo.primaryEndpoint();
+ var _host = mongo.host();
+ var _port = mongo.port();
+ var _uri = mongo.uriExpression();
+ var _userName = mongo.userNameReference();
+ // Build and run the app
+ var _cstr = mongo.connectionStringExpression();
+ var _databases = mongo.databases();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.MongoDB/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.MongoDB/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..dc8f5a1e6ff
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.MongoDB/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.MongoDB": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.MySql/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.MySql/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..98d1b8e913d
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.MySql/ValidationAppHost/AppHost.java
@@ -0,0 +1,26 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var rootPassword = builder.addParameter("mysql-root-password", true);
+ var mysql = builder.addMySql("mysql", new AddMySqlOptions().password(rootPassword).port(3306.0));
+ mysql
+ .withPassword(rootPassword)
+ .withDataVolume(new WithDataVolumeOptions().name("mysql-data"))
+ .withDataBindMount(".", true)
+ .withInitFiles(".");
+ mysql.withPhpMyAdmin(new WithPhpMyAdminOptions().containerName("phpmyadmin").configureContainer((container) -> {
+ container.withHostPort(8080.0);
+ }));
+ var db = mysql.addDatabase("appdb", "appdb");
+ db.withCreationScript("CREATE DATABASE IF NOT EXISTS appdb;");
+ // ---- Property access on MySqlServerResource ----
+ var _endpoint = mysql.primaryEndpoint();
+ var _host = mysql.host();
+ var _port = mysql.port();
+ var _uri = mysql.uriExpression();
+ var _jdbc = mysql.jdbcConnectionString();
+ var _cstr = mysql.connectionStringExpression();
+ var _databases = mysql.databases();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.MySql/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.MySql/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..6de80ace28e
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.MySql/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.MySql": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Nats/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Nats/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..3119965b194
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Nats/ValidationAppHost/AppHost.java
@@ -0,0 +1,38 @@
+import aspire.*;
+
+void main() throws Exception {
+ // Aspire TypeScript AppHost - NATS integration validation
+ // Exercises all [AspireExport] methods for Aspire.Hosting.Nats
+ var builder = DistributedApplication.CreateBuilder();
+ // addNats - factory method with default options
+ var nats = builder.addNats("messaging");
+ // withJetStream - enable JetStream support
+ nats.withJetStream();
+ // withDataVolume - add persistent data volume
+ nats.withDataVolume();
+ // withDataVolume - with custom name and readOnly option
+ var nats2 = builder.addNats("messaging2", new AddNatsOptions().port(4223.0))
+ .withJetStream()
+ .withDataVolume(new WithDataVolumeOptions().name("nats-data").isReadOnly(false))
+ .withLifetime(ContainerLifetime.PERSISTENT);
+ // withDataBindMount - bind mount a host directory
+ var nats3 = builder.addNats("messaging3");
+ nats3.withDataBindMount("/tmp/nats-data");
+ // addNats - with custom userName and password parameters
+ var customUser = builder.addParameter("nats-user");
+ var customPass = builder.addParameter("nats-pass", true);
+ var nats4 = builder.addNats("messaging4", new AddNatsOptions().userName(customUser).password(customPass));
+ // withReference - a container referencing a NATS resource (connection string)
+ var consumer = builder.addContainer("consumer", "myimage");
+ consumer.withReference(nats);
+ // withReference - with explicit connection name option
+ consumer.withReference(nats4, new WithReferenceOptions().connectionName("messaging4-connection"));
+ // ---- Property access on NatsServerResource ----
+ var _endpoint = nats.primaryEndpoint();
+ var _host = nats.host();
+ var _port = nats.port();
+ var _uri = nats.uriExpression();
+ var _userName = nats.userNameReference();
+ var _cstr = nats.connectionStringExpression();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Nats/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Nats/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..a0c1f0dd32b
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Nats/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Nats": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.OpenAI/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.OpenAI/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..bad331d1c17
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.OpenAI/ValidationAppHost/AppHost.java
@@ -0,0 +1,11 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var apiKey = builder.addParameter("openai-api-key", true);
+ var openai = builder.addOpenAI("openai")
+ .withEndpoint("https://api.openai.com/v1")
+ .withApiKey(apiKey);
+ openai.addModel("chat-model", "gpt-4o-mini");
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.OpenAI/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.OpenAI/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..8aa47c8ebc9
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.OpenAI/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.OpenAI": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Oracle/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Oracle/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..cfe27d1f183
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Oracle/ValidationAppHost/AppHost.java
@@ -0,0 +1,54 @@
+import aspire.*;
+
+void main() throws Exception {
+ // Aspire TypeScript AppHost - Oracle Integration Validation
+ // Validates all [AspireExport] methods for Aspire.Hosting.Oracle
+ var builder = DistributedApplication.CreateBuilder();
+ // ---- addOracle: factory method with defaults ----
+ var oracle = builder.addOracle("oracledb");
+ // ---- addOracle: factory method with custom password and port ----
+ var customPassword = builder.addParameter("oracle-password", true);
+ var oracle2 = builder.addOracle("oracledb2", new AddOracleOptions().password(customPassword).port(1522.0));
+ // ---- addDatabase: child resource with default databaseName ----
+ var db = oracle.addDatabase("mydb");
+ // ---- addDatabase: child resource with explicit databaseName ----
+ var db2 = oracle.addDatabase("inventory", "inventorydb");
+ // ---- withDataVolume: data persistence (default name) ----
+ oracle.withDataVolume();
+ // ---- withDataVolume: data persistence (custom name) ----
+ oracle2.withDataVolume("oracle-data");
+ // ---- withDataBindMount: bind mount for data ----
+ oracle2.withDataBindMount("./oracle-data");
+ // ---- withInitFiles: initialization scripts ----
+ oracle2.withInitFiles("./init-scripts");
+ // ---- withDbSetupBindMount: DB setup directory ----
+ oracle2.withDbSetupBindMount("./setup-scripts");
+ // ---- withReference: connection string reference (from core) ----
+ var otherOracle = builder.addOracle("other-oracle");
+ var otherDb = otherOracle.addDatabase("otherdb");
+ oracle.withReference(otherDb);
+ // ---- withReference: with connection name option ----
+ oracle.withReference(otherDb, new WithReferenceOptions().connectionName("secondary-db"));
+ // ---- withReference: unified reference to another Oracle server resource ----
+ oracle.withReference(otherOracle);
+ // ---- Fluent chaining: multiple methods chained ----
+ var oracle3 = builder.addOracle("oracledb3");
+ oracle3.withLifetime(ContainerLifetime.PERSISTENT);
+ oracle3.withDataVolume("oracle3-data");
+ oracle3.addDatabase("chaineddb");
+ // ---- Property access on OracleDatabaseServerResource ----
+ var _endpoint = oracle.primaryEndpoint();
+ var _host = oracle.host();
+ var _port = oracle.port();
+ var _userNameRef = oracle.userNameReference();
+ var _uri = oracle.uriExpression();
+ var _jdbc = oracle.jdbcConnectionString();
+ var _cstr = oracle.connectionStringExpression();
+ // ---- Property access on OracleDatabaseResource ----
+ var _dbName = db.databaseName();
+ var _dbUri = db.uriExpression();
+ var _dbJdbc = db.jdbcConnectionString();
+ var _dbParent = db.parent();
+ var _dbCstr = db.connectionStringExpression();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Oracle/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Oracle/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..f4ee67bdd05
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Oracle/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Oracle": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Orleans/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Orleans/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..185a8452a3b
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Orleans/ValidationAppHost/AppHost.java
@@ -0,0 +1,25 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var provider = builder.addConnectionString("provider", "ORLEANS_PROVIDER_CONNECTION_STRING");
+ var orleans = builder.addOrleans("orleans")
+ .withClusterId("cluster-id")
+ .withServiceId("service-id")
+ .withClustering(provider)
+ .withDevelopmentClustering()
+ .withGrainStorage("grain-storage", provider)
+ .withMemoryGrainStorage("memory-grain-storage")
+ .withStreaming("streaming", provider)
+ .withMemoryStreaming("memory-streaming")
+ .withBroadcastChannel("broadcast")
+ .withReminders(provider)
+ .withMemoryReminders()
+ .withGrainDirectory("grain-directory", provider);
+ var orleansClient = orleans.asClient();
+ var silo = builder.addContainer("silo", "redis");
+ silo.withOrleansReference(orleans);
+ var client = builder.addContainer("client", "redis");
+ client.withOrleansClientReference(orleansClient);
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Orleans/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Orleans/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..a302e061f31
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Orleans/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Orleans": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.PostgreSQL/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.PostgreSQL/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..8869ed9c00e
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.PostgreSQL/ValidationAppHost/AppHost.java
@@ -0,0 +1,47 @@
+import aspire.*;
+
+void main() throws Exception {
+ // Aspire TypeScript AppHost - PostgreSQL Integration Validation
+ // Validates all [AspireExport] methods for Aspire.Hosting.PostgreSQL
+ var builder = DistributedApplication.CreateBuilder();
+ // ---- AddPostgres: factory method ----
+ var postgres = builder.addPostgres("pg");
+ // ---- AddDatabase: child resource ----
+ var db = postgres.addDatabase("mydb", "testdb");
+ // ---- WithPgAdmin: management UI ----
+ postgres.withPgAdmin();
+ postgres.withPgAdmin(new WithPgAdminOptions().containerName("mypgadmin"));
+ // ---- WithPgWeb: management UI ----
+ postgres.withPgWeb();
+ postgres.withPgWeb(new WithPgWebOptions().containerName("mypgweb"));
+ // ---- WithDataVolume: data persistence ----
+ postgres.withDataVolume();
+ postgres.withDataVolume(new WithDataVolumeOptions().name("pg-data").isReadOnly(false));
+ // ---- WithDataBindMount: bind mount ----
+ postgres.withDataBindMount("./data");
+ postgres.withDataBindMount("./data2", true);
+ // ---- WithInitFiles: initialization scripts ----
+ postgres.withInitFiles("./init");
+ // ---- WithHostPort: explicit port for PostgreSQL ----
+ postgres.withHostPort(5432.0);
+ // ---- WithCreationScript: custom database creation SQL ----
+ db.withCreationScript("CREATE DATABASE \"testdb\"");
+ // ---- WithPassword / WithUserName: credential configuration ----
+ var customPassword = builder.addParameter("pg-password", true);
+ var customUser = builder.addParameter("pg-user");
+ var pg2 = builder.addPostgres("pg2");
+ pg2.withPassword(customPassword);
+ pg2.withUserName(customUser);
+ // ---- Property access on PostgresServerResource ----
+ var _endpoint = postgres.primaryEndpoint();
+ var _nameRef = postgres.userNameReference();
+ var _uri = postgres.uriExpression();
+ var _jdbc = postgres.jdbcConnectionString();
+ var _cstr = postgres.connectionStringExpression();
+ // ---- Property access on PostgresDatabaseResource ----
+ var _dbName = db.databaseName();
+ var _dbUri = db.uriExpression();
+ var _dbJdbc = db.jdbcConnectionString();
+ var _dbCstr = db.connectionStringExpression();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.PostgreSQL/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.PostgreSQL/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..a64428ed582
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.PostgreSQL/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.PostgreSQL": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:33490;http://localhost:28305",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21575",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17364"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Python/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Python/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..6d0011fd9b9
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Python/ValidationAppHost/AppHost.java
@@ -0,0 +1,15 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ builder.addPythonApp("python-script", ".", "main.py");
+ builder.addPythonModule("python-module", ".", "uvicorn");
+ builder.addPythonExecutable("python-executable", ".", "pytest");
+ var uvicorn = builder.addUvicornApp("python-uvicorn", ".", "main:app");
+ uvicorn.withVirtualEnvironment(".venv", false);
+ uvicorn.withDebugging();
+ uvicorn.withEntrypoint(EntrypointType.MODULE, "uvicorn");
+ uvicorn.withPip(new WithPipOptions().install(true).installArgs(new String[] { "install", "-r", "requirements.txt" }));
+ uvicorn.withUv(new WithUvOptions().install(false).args(new String[] { "sync" }));
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Python/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Python/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..db5811de5c2
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Python/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Python": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Qdrant/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Qdrant/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..3ae21ca5d4d
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Qdrant/ValidationAppHost/AppHost.java
@@ -0,0 +1,22 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var customApiKey = builder.addParameter("qdrant-key", true);
+ builder.addQdrant("qdrant-custom", new AddQdrantOptions().apiKey(customApiKey).grpcPort(16334.0).httpPort(16333.0));
+ var qdrant = builder.addQdrant("qdrant");
+ qdrant.withDataVolume(new WithDataVolumeOptions().name("qdrant-data")).withDataBindMount(".", true);
+ var consumer = builder.addContainer("consumer", "busybox");
+ consumer.withReference(qdrant, new WithReferenceOptions().connectionName("qdrant"));
+ // ---- Property access on QdrantServerResource ----
+ var _endpoint = qdrant.primaryEndpoint();
+ var _grpcHost = qdrant.grpcHost();
+ var _grpcPort = qdrant.grpcPort();
+ var _httpEndpoint = qdrant.httpEndpoint();
+ var _httpHost = qdrant.httpHost();
+ var _httpPort = qdrant.httpPort();
+ var _uri = qdrant.uriExpression();
+ var _httpUri = qdrant.httpUriExpression();
+ var _cstr = qdrant.connectionStringExpression();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Qdrant/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Qdrant/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..e667f37283e
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Qdrant/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Qdrant": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.RabbitMQ/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.RabbitMQ/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..aab4084ab29
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.RabbitMQ/ValidationAppHost/AppHost.java
@@ -0,0 +1,21 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var rabbitmq = builder.addRabbitMQ("messaging");
+ rabbitmq.withDataVolume();
+ rabbitmq.withManagementPlugin();
+ var rabbitmq2 = builder.addRabbitMQ("messaging2");
+ rabbitmq2.withLifetime(ContainerLifetime.PERSISTENT);
+ rabbitmq2.withDataVolume();
+ rabbitmq2.withManagementPluginWithPort(15673.0);
+ // ---- Property access on RabbitMQServerResource ----
+ var _endpoint = rabbitmq.primaryEndpoint();
+ var _mgmtEndpoint = rabbitmq.managementEndpoint();
+ var _host = rabbitmq.host();
+ var _port = rabbitmq.port();
+ var _uri = rabbitmq.uriExpression();
+ var _userName = rabbitmq.userNameReference();
+ var _cstr = rabbitmq.connectionStringExpression();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.RabbitMQ/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.RabbitMQ/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..8984f7abaed
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.RabbitMQ/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.RabbitMQ": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:18076;http://localhost:48822",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:56384",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:64883"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Redis/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Redis/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..ec6d87baa10
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Redis/ValidationAppHost/AppHost.java
@@ -0,0 +1,39 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ // addRedis - full overload with port and password parameter
+ var password = builder.addParameter("redis-password", true);
+ var cache = builder.addRedis("cache", new AddRedisOptions().password(password));
+ // addRedisWithPort - overload with explicit port
+ var cache2 = builder.addRedisWithPort("cache2", 6380.0);
+ // withDataVolume + withPersistence - fluent chaining on RedisResource
+ cache.withDataVolume(new WithDataVolumeOptions().name("redis-data"));
+ cache.withPersistence(new WithPersistenceOptions().interval(600000000.0).keysChangedThreshold(5.0));
+ // withDataBindMount on RedisResource
+ cache2.withDataBindMount("/tmp/redis-data");
+ // withHostPort on RedisResource
+ cache.withHostPort(6379.0);
+ // withPassword on RedisResource
+ var newPassword = builder.addParameter("new-redis-password", true);
+ cache2.withPassword(newPassword);
+ // withRedisCommander - with configureContainer callback exercising withHostPort
+ cache.withRedisCommander(new WithRedisCommanderOptions().configureContainer((commander) -> {
+ commander.withHostPort(8081.0);
+ }).containerName("my-commander"));
+ // withRedisInsight - with configureContainer callback exercising withHostPort, withDataVolume, withDataBindMount
+ cache.withRedisInsight(new WithRedisInsightOptions().configureContainer((insight) -> {
+ insight.withHostPort(5540.0);
+ insight.withDataVolume("insight-data");
+ insight.withDataBindMount("/tmp/insight-data");
+ }).containerName("my-insight"));
+ // ---- Property access on RedisResource (ExposeProperties = true) ----
+ var redis = cache;
+ var _endpoint = redis.primaryEndpoint();
+ var _host = redis.host();
+ var _port = redis.port();
+ var _tlsEnabled = redis.tlsEnabled();
+ var _uri = redis.uriExpression();
+ var _cstr = redis.connectionStringExpression();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Redis/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Redis/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..6c49cdabaac
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Redis/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Redis": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29650;http://localhost:28831",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10875",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13219"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Seq/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Seq/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..617c472577a
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Seq/ValidationAppHost/AppHost.java
@@ -0,0 +1,17 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var adminPassword = builder.addParameter("seq-admin-password", true);
+ var seq = builder.addSeq("seq", adminPassword, 5341.0);
+ seq.withDataVolume();
+ seq.withDataVolume(new WithDataVolumeOptions().name("seq-data").isReadOnly(false));
+ seq.withDataBindMount("./seq-data", true);
+ // ---- Property access on SeqResource ----
+ var _endpoint = seq.primaryEndpoint();
+ var _host = seq.host();
+ var _port = seq.port();
+ var _uri = seq.uriExpression();
+ var _cstr = seq.connectionStringExpression();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Seq/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Seq/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..5db1ee9e93f
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Seq/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Seq": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.SqlServer/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.SqlServer/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..e5f5d86e951
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.SqlServer/ValidationAppHost/AppHost.java
@@ -0,0 +1,37 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ // Test 1: Basic SQL Server resource creation (addSqlServer)
+ var sqlServer = builder.addSqlServer("sql");
+ // Test 2: Add database to SQL Server (addDatabase)
+ sqlServer.addDatabase("mydb");
+ // Test 3: Test withDataVolume
+ builder.addSqlServer("sql-volume")
+ .withDataVolume();
+ // Test 4: Test withHostPort
+ builder.addSqlServer("sql-port")
+ .withHostPort(11433.0);
+ // Test 5: Test password parameter with addParameter
+ var customPassword = builder.addParameter("sql-password", true);
+ builder.addSqlServer("sql-custom-pass", new AddSqlServerOptions().password(customPassword));
+ // Test 6: Chained configuration - multiple With* methods
+ var sqlChained = builder.addSqlServer("sql-chained");
+ sqlChained.withLifetime(ContainerLifetime.PERSISTENT);
+ sqlChained.withDataVolume(new WithDataVolumeOptions().name("sql-chained-data"));
+ sqlChained.withHostPort(12433.0);
+ // Test 7: Add multiple databases to same server
+ sqlChained.addDatabase("db1");
+ sqlChained.addDatabase("db2", "customdb2");
+ // ---- Property access on SqlServerServerResource ----
+ var _endpoint = sqlServer.primaryEndpoint();
+ var _host = sqlServer.host();
+ var _port = sqlServer.port();
+ var _uri = sqlServer.uriExpression();
+ var _jdbc = sqlServer.jdbcConnectionString();
+ var _userName = sqlServer.userNameReference();
+ // Build and run the app
+ var _cstr = sqlServer.connectionStringExpression();
+ var _databases = sqlServer.databases();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.SqlServer/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.SqlServer/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..7fbce57a6be
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.SqlServer/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.SqlServer": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:55686;http://localhost:57768",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:51980",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:28684"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Valkey/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Valkey/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..29dd1ab8cd1
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Valkey/ValidationAppHost/AppHost.java
@@ -0,0 +1,18 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var password = builder.addParameter("valkey-password", true);
+ var valkey = builder.addValkey("cache", new AddValkeyOptions().port(6380.0));
+ valkey
+ .withDataVolume(new WithDataVolumeOptions().name("valkey-data"))
+ .withDataBindMount(".", true)
+ .withPersistence(new WithPersistenceOptions().interval(100000000.0).keysChangedThreshold(1.0));
+ // ---- Property access on ValkeyResource ----
+ var _endpoint = valkey.primaryEndpoint();
+ var _host = valkey.host();
+ var _port = valkey.port();
+ var _uri = valkey.uriExpression();
+ var _cstr = valkey.connectionStringExpression();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Valkey/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Valkey/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..baecdf88964
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Valkey/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Valkey": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting.Yarp/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting.Yarp/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..d89b8ec433b
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Yarp/ValidationAppHost/AppHost.java
@@ -0,0 +1,134 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var buildVersion = builder.addParameterFromConfiguration("buildVersion", "MyConfig:BuildVersion");
+ var buildSecret = builder.addParameterFromConfiguration("buildSecret", "MyConfig:Secret", true);
+ var staticFilesSource = builder.addContainer("static-files-source", "nginx");
+ var backend = builder.addContainer("backend", "nginx");
+ backend.withHttpEndpoint(new WithHttpEndpointOptions().name("http").targetPort(80.0));
+ var externalBackend = builder.addExternalService("external-backend", "https://example.com");
+ var proxy = builder.addYarp("proxy");
+ proxy.withHostPort(8080.0);
+ proxy.withHostHttpsPort(8443.0);
+ proxy.withEndpointProxySupport(true);
+ proxy.withDockerfile("./context");
+ proxy.withImageSHA256("abc123def456");
+ proxy.withContainerNetworkAlias("myalias");
+ proxy.publishAsContainer();
+ proxy.publishWithStaticFiles(staticFilesSource);
+ proxy.withVolume("/data", new WithVolumeOptions().name("proxy-data"));
+ proxy.withBuildArg("BUILD_VERSION", buildVersion);
+ proxy.withBuildSecret("MY_SECRET", buildSecret);
+ proxy.withConfiguration((config) -> {
+ var endpoint = backend.getEndpoint("http");
+ var backendService = backend;
+ var endpointCluster = config.addClusterFromEndpoint(endpoint);
+ var resourceCluster = config.addClusterFromResource(backendService);
+ var externalServiceCluster = config.addClusterFromExternalService(externalBackend);
+ var singleDestinationCluster = config.addClusterWithDestination("single-destination", "https://example.net");
+ var multiDestinationCluster = config.addClusterWithDestinations("multi-destination", new String[] { "https://example.org", "https://example.edu" });
+ var routeFromEndpoint = config.addRouteFromEndpoint("/from-endpoint/{**catchall}", endpoint);
+ var routeFromResource = config.addRouteFromResource("/from-resource/{**catchall}", backendService);
+ var routeFromExternalService = config.addRouteFromExternalService("/from-external/{**catchall}", externalBackend);
+ var catchAllRoute = config.addCatchAllRoute(endpointCluster);
+ var catchAllRouteFromEndpoint = config.addCatchAllRouteFromEndpoint(endpoint);
+ var catchAllRouteFromResource = config.addCatchAllRouteFromResource(backendService);
+ var catchAllRouteFromExternalService = config.addCatchAllRouteFromExternalService(externalBackend);
+
+ var forwarderRequestConfig = new YarpForwarderRequestConfig();
+ forwarderRequestConfig.setActivityTimeout(30_000_000.0);
+ forwarderRequestConfig.setAllowResponseBuffering(true);
+ forwarderRequestConfig.setVersion("2.0");
+ endpointCluster.withForwarderRequestConfig(forwarderRequestConfig);
+
+ var httpClientConfig = new YarpHttpClientConfig();
+ httpClientConfig.setDangerousAcceptAnyServerCertificate(true);
+ httpClientConfig.setEnableMultipleHttp2Connections(true);
+ httpClientConfig.setMaxConnectionsPerServer(10);
+ httpClientConfig.setRequestHeaderEncoding("utf-8");
+ httpClientConfig.setResponseHeaderEncoding("utf-8");
+ endpointCluster.withHttpClientConfig(httpClientConfig);
+
+ var sessionAffinityCookie = new YarpSessionAffinityCookieConfig();
+ sessionAffinityCookie.setDomain("example.com");
+ sessionAffinityCookie.setHttpOnly(true);
+ sessionAffinityCookie.setIsEssential(true);
+ sessionAffinityCookie.setPath("/");
+
+ var sessionAffinity = new YarpSessionAffinityConfig();
+ sessionAffinity.setAffinityKeyName(".Aspire.Affinity");
+ sessionAffinity.setEnabled(true);
+ sessionAffinity.setFailurePolicy("Redistribute");
+ sessionAffinity.setPolicy("Cookie");
+ sessionAffinity.setCookie(sessionAffinityCookie);
+ endpointCluster.withSessionAffinityConfig(sessionAffinity);
+
+ var activeHealthCheck = new YarpActiveHealthCheckConfig();
+ activeHealthCheck.setEnabled(true);
+ activeHealthCheck.setInterval(50_000_000.0);
+ activeHealthCheck.setPath("/health");
+ activeHealthCheck.setPolicy("ConsecutiveFailures");
+ activeHealthCheck.setQuery("probe=1");
+ activeHealthCheck.setTimeout(20_000_000.0);
+
+ var passiveHealthCheck = new YarpPassiveHealthCheckConfig();
+ passiveHealthCheck.setEnabled(true);
+ passiveHealthCheck.setPolicy("TransportFailureRateHealthPolicy");
+ passiveHealthCheck.setReactivationPeriod(100_000_000.0);
+
+ var healthCheck = new YarpHealthCheckConfig();
+ healthCheck.setAvailableDestinationsPolicy("HealthyOrPanic");
+ healthCheck.setActive(activeHealthCheck);
+ healthCheck.setPassive(passiveHealthCheck);
+ endpointCluster.withHealthCheckConfig(healthCheck);
+
+ var defaultRoute = config.addRoute("/{**catchall}", endpointCluster);
+ defaultRoute
+ .withTransformXForwarded()
+ .withTransformForwarded()
+ .withTransformClientCertHeader("X-Client-Cert")
+ .withTransformHttpMethodChange("GET", "POST")
+ .withTransformPathSet("/backend/{**catchall}")
+ .withTransformPathPrefix("/api")
+ .withTransformPathRemovePrefix("/legacy")
+ .withTransformPathRouteValues("/api/{id}")
+ .withTransformQueryValue("source", "apphost")
+ .withTransformQueryRouteValue("routeId", "id")
+ .withTransformQueryRemoveKey("remove")
+ .withTransformCopyRequestHeaders()
+ .withTransformUseOriginalHostHeader()
+ .withTransformRequestHeader("X-Test-Header", "test-value")
+ .withTransformRequestHeaderRouteValue("X-Route-Value", "id")
+ .withTransformRequestHeaderRemove("X-Remove-Request")
+ .withTransformRequestHeadersAllowed(new String[] { "X-Test-Header", "X-Route-Value" })
+ .withTransformCopyResponseHeaders()
+ .withTransformCopyResponseTrailers()
+ .withTransformResponseHeader("X-Response-Header", "response-value")
+ .withTransformResponseHeaderRemove("X-Remove-Response")
+ .withTransformResponseHeadersAllowed(new String[] { "X-Response-Header" })
+ .withTransformResponseTrailer("X-Response-Trailer", "trailer-value")
+ .withTransformResponseTrailerRemove("X-Remove-Trailer")
+ .withTransformResponseTrailersAllowed(new String[] { "X-Response-Trailer" });
+
+ var routeMatch = new YarpRouteMatch();
+ routeMatch.setPath("/from-endpoint/{**catchall}");
+ routeMatch.setMethods(new String[] { "GET", "POST" });
+ routeMatch.setHosts(new String[] { "endpoint.example.com" });
+ routeFromEndpoint.withMatch(routeMatch);
+
+ routeFromEndpoint.withTransform(Map.ofEntries(Map.entry("PathPrefix", "/endpoint"), Map.entry("RequestHeadersCopy", "true")));
+ routeFromResource.withTransform(Map.ofEntries(Map.entry("PathPrefix", "/resource")));
+ routeFromExternalService.withTransform(Map.ofEntries(Map.entry("PathPrefix", "/external")));
+ catchAllRoute.withTransform(Map.ofEntries(Map.entry("PathPrefix", "/cluster")));
+ catchAllRouteFromEndpoint.withTransform(Map.ofEntries(Map.entry("PathPrefix", "/catchall-endpoint")));
+ catchAllRouteFromResource.withTransform(Map.ofEntries(Map.entry("PathPrefix", "/catchall-resource")));
+ catchAllRouteFromExternalService.withTransform(Map.ofEntries(Map.entry("PathPrefix", "/catchall-external")));
+ config.addRoute("/resource/{**catchall}", resourceCluster);
+ config.addRoute("/external/{**catchall}", externalServiceCluster);
+ config.addRoute("/single/{**catchall}", singleDestinationCluster);
+ config.addRoute("/multi/{**catchall}", multiDestinationCluster);
+ });
+ proxy.publishAsConnectionString();
+ builder.build().run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting.Yarp/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting.Yarp/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..4d419a80c14
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting.Yarp/ValidationAppHost/aspire.config.json
@@ -0,0 +1,21 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting.Yarp": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29550;http://localhost:28731",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10775",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13119"
+ }
+ }
+ }
+}
diff --git a/playground/polyglot/Java/Aspire.Hosting/ValidationAppHost/AppHost.java b/playground/polyglot/Java/Aspire.Hosting/ValidationAppHost/AppHost.java
new file mode 100644
index 00000000000..74ce5489d22
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting/ValidationAppHost/AppHost.java
@@ -0,0 +1,100 @@
+import aspire.*;
+
+void main() throws Exception {
+ var builder = DistributedApplication.CreateBuilder();
+ var container = builder.addContainer("mycontainer", "nginx");
+ var dockerContainer = builder.addDockerfile("dockerapp", "./app");
+ var exe = builder.addExecutable("myexe", "echo", ".", new String[] { "hello" });
+ var project = builder.addProject("myproject", "./src/MyProject", "https");
+ var csharpApp = builder.addCSharpApp("csharpapp", "./src/CSharpApp");
+ var cache = builder.addRedis("cache");
+ var tool = builder.addDotnetTool("mytool", "dotnet-ef");
+ var configParam = builder.addParameterFromConfiguration("myconfig", "MyConfig:Key");
+ var secretParam = builder.addParameterFromConfiguration("mysecret", "MyConfig:Secret", true);
+ container.withDockerfileBaseImage(new WithDockerfileBaseImageOptions().buildImage("mcr.microsoft.com/dotnet/sdk:8.0"));
+ container.withContainerRegistry(container);
+ dockerContainer.withHttpEndpoint(new WithHttpEndpointOptions().name("http").targetPort(80.0));
+ var endpoint = dockerContainer.getEndpoint("http");
+ var expr = ReferenceExpression.refExpr("Host=%s", endpoint);
+ var builtConnectionString = builder.addConnectionStringBuilder("customcs", (connectionStringBuilder) -> { var _isEmpty = connectionStringBuilder.isEmpty(); connectionStringBuilder.appendLiteral("Host="); connectionStringBuilder.appendValueProvider(endpoint); connectionStringBuilder.appendLiteral(";Key="); connectionStringBuilder.appendValueProvider(secretParam); var _builtExpression = connectionStringBuilder.build(); });
+ builtConnectionString.withConnectionProperty("Host", expr);
+ builtConnectionString.withConnectionPropertyValue("Mode", "Development");
+ container.withEnvironmentEndpoint("MY_ENDPOINT", endpoint);
+ container.withEnvironmentParameter("MY_PARAM", configParam);
+ container.withEnvironmentConnectionString("MY_CONN", builtConnectionString);
+ builtConnectionString.withConnectionProperty("Endpoint", expr);
+ builtConnectionString.withConnectionPropertyValue("Protocol", "https");
+ container.excludeFromManifest();
+ container.excludeFromMcp();
+ container.waitForCompletion(exe);
+ container.withDeveloperCertificateTrust(true);
+ container.withCertificateTrustScope(CertificateTrustScope.SYSTEM);
+ container.withHttpsDeveloperCertificate();
+ container.withoutHttpsCertificate();
+ container.withChildRelationship(exe);
+ container.withIconName("Database", IconVariant.FILLED);
+ container.withHttpProbe(ProbeType.LIVENESS, new WithHttpProbeOptions().path("/health"));
+ container.withRemoteImageName("myregistry.azurecr.io/myapp");
+ container.withRemoteImageTag("latest");
+ container.withMcpServer(new WithMcpServerOptions().path("/mcp"));
+ container.withRequiredCommand("docker");
+ tool.withToolIgnoreExistingFeeds();
+ tool.withToolIgnoreFailedSources();
+ tool.withToolPackage("dotnet-ef");
+ tool.withToolPrerelease();
+ tool.withToolSource("https://api.nuget.org/v3/index.json");
+ tool.withToolVersion("8.0.0");
+ tool.publishAsDockerFile();
+ container.withPipelineStepFactory("custom-build-step", (stepContext) -> { var pipelineContext = stepContext.pipelineContext(); var pipelineModel = pipelineContext.model(); var _pipelineResources = pipelineModel.getResources(); var _pipelineContainer = pipelineModel.findResourceByName("mycontainer"); var pipelineServices = pipelineContext.services(); var pipelineLoggerFactory = pipelineServices.getLoggerFactory(); var pipelineFactoryLogger = pipelineLoggerFactory.createLogger("ValidationAppHost.PipelineContext"); pipelineFactoryLogger.logInformation("Pipeline factory context logger"); var pipelineLogger = pipelineContext.logger(); pipelineLogger.logDebug("Pipeline context logger"); var pipelineSummary = pipelineContext.summary(); pipelineSummary.add("PipelineContext", "Validated"); pipelineSummary.addMarkdown("PipelineMarkdown", "**Validated**"); var executionContext = stepContext.executionContext(); var _isPublishMode = executionContext.isPublishMode(); var stepServices = stepContext.services(); var stepLogger = stepContext.logger(); stepLogger.logInformation("Pipeline step context logger"); var stepSummary = stepContext.summary(); stepSummary.add("PipelineStepContext", "Validated"); var reportingStep = stepContext.reportingStep(); reportingStep.logStep("information", "Reporting step log"); reportingStep.logStepMarkdown("information", "**Reporting step markdown log**"); var reportingTask = reportingStep.createTask("Task created"); reportingTask.updateTask("Task updated"); reportingTask.updateTaskMarkdown("**Task markdown updated**"); reportingTask.completeTask(new CompleteTaskOptions().completionMessage("Task complete")); var markdownTask = reportingStep.createMarkdownTask("**Markdown task created**"); markdownTask.completeTaskMarkdown("**Markdown task complete**", new CompleteTaskMarkdownOptions().completionState("completed-with-warning")); reportingStep.completeStep("Reporting step complete"); reportingStep.completeStepMarkdown("**Reporting step markdown complete**", new CompleteStepMarkdownOptions().completionState("completed-with-warning")); var stepModel = stepContext.model(); var _stepResources = stepModel.getResources(); var _stepContainer = stepModel.findResourceByName("mycontainer"); var stepLoggerFactory = stepServices.getLoggerFactory(); var stepFactoryLogger = stepLoggerFactory.createLogger("ValidationAppHost.PipelineStepContext"); stepFactoryLogger.logDebug("Pipeline step factory logger"); var cancellationToken = stepContext.cancellationToken(); var cacheUriExpression = cache.uriExpression(); var _cacheUri = cacheUriExpression.getValue(cancellationToken); }, new WithPipelineStepFactoryOptions().dependsOn(new String[] { "build" }).requiredBy(new String[] { "deploy" }).tags(new String[] { "custom-build" }).description("Custom pipeline step"));
+ container.withPipelineConfiguration((configContext) -> { var configServices = configContext.services(); var configModel = configContext.model(); var _configResources = configModel.getResources(); var _configContainer = configModel.findResourceByName("mycontainer"); var configLoggerFactory = configServices.getLoggerFactory(); var configLogger = configLoggerFactory.createLogger("ValidationAppHost.PipelineConfigurationContext"); configLogger.logInformation("Pipeline configuration logger"); var allSteps = configContext.steps(); var taggedSteps = configContext.getStepsByTag("custom-build"); var _stepName = allSteps[0].name(); var _description = allSteps[0].description(); var _tags = allSteps[0].tags(); var _dependsOnSteps = allSteps[0].dependsOnSteps(); var _requiredBySteps = taggedSteps[0].requiredBySteps(); taggedSteps[0].requiredBy("publish"); allSteps[0].dependsOn("build"); });
+ container.withPipelineConfigurationAsync((configContext) -> { var _configServices = configContext.services(); var _configModel = configContext.model(); var _resourceSteps = configContext.steps(); var _taggedSteps = configContext.getStepsByTag("custom-build"); });
+ var _appHostDirectory = builder.appHostDirectory();
+ var hostEnvironment = builder.environment();
+ var _isDevelopment = hostEnvironment.isDevelopment();
+ var _isProduction = hostEnvironment.isProduction();
+ var _isStaging = hostEnvironment.isStaging();
+ var _isSpecificEnvironment = hostEnvironment.isEnvironment("Development");
+ var builderConfiguration = builder.getConfiguration();
+ var _configValue = builderConfiguration.getConfigValue("MyConfig:Key");
+ var _connectionString = builderConfiguration.getConnectionString("customcs");
+ var _configSection = builderConfiguration.getSection("MyConfig");
+ var _configChildren = builderConfiguration.getChildren();
+ var _configExists = builderConfiguration.exists("MyConfig:Key");
+ var builderExecutionContext = builder.executionContext();
+ var executionContextServiceProvider = builderExecutionContext.serviceProvider();
+ var _distributedApplicationModelFromExecutionContext = executionContextServiceProvider.getDistributedApplicationModel();
+ var beforeStartSubscription = builder.subscribeBeforeStart((beforeStartEvent) -> { var beforeStartServices = beforeStartEvent.services(); var beforeStartModel = beforeStartEvent.model(); var _beforeStartResources = beforeStartModel.getResources(); var _beforeStartContainer = beforeStartModel.findResourceByName("mycontainer"); var _beforeStartEventing = beforeStartServices.getEventing(); var beforeStartLoggerFactory = beforeStartServices.getLoggerFactory(); var beforeStartLogger = beforeStartLoggerFactory.createLogger("ValidationAppHost.BeforeStart"); beforeStartLogger.logInformation("BeforeStart information"); beforeStartLogger.logWarning("BeforeStart warning"); beforeStartLogger.logError("BeforeStart error"); beforeStartLogger.logDebug("BeforeStart debug"); beforeStartLogger.log("critical", "BeforeStart critical"); var beforeStartResourceLoggerService = beforeStartServices.getResourceLoggerService(); beforeStartResourceLoggerService.completeLog(container); beforeStartResourceLoggerService.completeLogByName("mycontainer"); var beforeStartNotificationService = beforeStartServices.getResourceNotificationService(); beforeStartNotificationService.waitForResourceState("mycontainer", "Running"); var _matchedResourceState = beforeStartNotificationService.waitForResourceStates("mycontainer", new String[] { "Running", "FailedToStart" }); var _healthyResourceEvent = beforeStartNotificationService.waitForResourceHealthy("mycontainer"); beforeStartNotificationService.waitForDependencies(container); var _currentResourceState = beforeStartNotificationService.tryGetResourceState("mycontainer"); beforeStartNotificationService.publishResourceUpdate(container, new PublishResourceUpdateOptions().state("Validated").stateStyle("info")); var userSecretsManager = beforeStartServices.getUserSecretsManager(); var _userSecretsAvailable = userSecretsManager.isAvailable(); var _userSecretsFilePath = userSecretsManager.filePath(); var _secretSet = userSecretsManager.trySetSecret("Validation:Key", "value"); userSecretsManager.getOrSetSecret(container, "Validation:GeneratedKey", "generated-value"); var _generatedSecretValue = builderConfiguration.getConfigValue("Validation:GeneratedKey"); userSecretsManager.saveStateJson("{\"Validation\":\"Value\"}"); var _modelFromServices = beforeStartServices.getDistributedApplicationModel(); });
+ var afterResourcesCreatedSubscription = builder.subscribeAfterResourcesCreated((afterResourcesCreatedEvent) -> { var afterResourcesCreatedServices = afterResourcesCreatedEvent.services(); var afterResourcesCreatedModel = afterResourcesCreatedEvent.model(); var _afterResources = afterResourcesCreatedModel.getResources(); var _afterResourcesContainer = afterResourcesCreatedModel.findResourceByName("mycontainer"); var afterResourcesCreatedLoggerFactory = afterResourcesCreatedServices.getLoggerFactory(); var afterResourcesCreatedLogger = afterResourcesCreatedLoggerFactory.createLogger("ValidationAppHost.AfterResourcesCreated"); afterResourcesCreatedLogger.logInformation("AfterResourcesCreated"); });
+ var builderEventing = builder.eventing();
+ builderEventing.unsubscribe(beforeStartSubscription);
+ builderEventing.unsubscribe(afterResourcesCreatedSubscription);
+ container.onBeforeResourceStarted((beforeResourceStartedEvent) -> { var _resource = beforeResourceStartedEvent.resource(); var services = beforeResourceStartedEvent.services(); var loggerFactory = services.getLoggerFactory(); var logger = loggerFactory.createLogger("ValidationAppHost.BeforeResourceStarted"); logger.logInformation("BeforeResourceStarted"); });
+ container.onResourceStopped((resourceStoppedEvent) -> { var _resource = resourceStoppedEvent.resource(); var services = resourceStoppedEvent.services(); var loggerFactory = services.getLoggerFactory(); var logger = loggerFactory.createLogger("ValidationAppHost.ResourceStopped"); logger.logWarning("ResourceStopped"); });
+ builtConnectionString.onConnectionStringAvailable((connectionStringAvailableEvent) -> { var _resource = connectionStringAvailableEvent.resource(); var services = connectionStringAvailableEvent.services(); var notifications = services.getResourceNotificationService(); var _connectionState = notifications.tryGetResourceState("customcs"); });
+ container.onInitializeResource((initializeResourceEvent) -> { var _resource = initializeResourceEvent.resource(); var _initializeEventing = initializeResourceEvent.eventing(); var initializeLogger = initializeResourceEvent.logger(); var initializeNotifications = initializeResourceEvent.notifications(); var initializeServices = initializeResourceEvent.services(); initializeLogger.logDebug("InitializeResource"); initializeNotifications.waitForDependencies(container); var _initializeModel = initializeServices.getDistributedApplicationModel(); var _initializeEventingFromServices = initializeServices.getEventing(); });
+ container.onResourceEndpointsAllocated((resourceEndpointsAllocatedEvent) -> { var _resource = resourceEndpointsAllocatedEvent.resource(); var services = resourceEndpointsAllocatedEvent.services(); var loggerFactory = services.getLoggerFactory(); var logger = loggerFactory.createLogger("ValidationAppHost.ResourceEndpointsAllocated"); logger.logInformation("ResourceEndpointsAllocated"); });
+ container.onResourceReady((resourceReadyEvent) -> { var _resource = resourceReadyEvent.resource(); var services = resourceReadyEvent.services(); var loggerFactory = services.getLoggerFactory(); var logger = loggerFactory.createLogger("ValidationAppHost.ResourceReady"); logger.logInformation("ResourceReady"); });
+ container.withEnvironment("MY_VAR", "value");
+ container.withEndpoint();
+ container.withHttpEndpoint();
+ container.withHttpsEndpoint();
+ container.withExternalHttpEndpoints();
+ container.asHttp2Service();
+ container.withArgs(new String[] { "--verbose" });
+ container.withParentRelationship(exe);
+ container.withExplicitStart();
+ container.withUrl("http://localhost:8080");
+ container.withUrlExpression(ReferenceExpression.refExpr("http://%s", endpoint));
+ container.withHealthCheck("mycheck");
+ container.withHttpHealthCheck();
+ container.withCommand("restart", "Restart", (_ctx) -> {
+ var result = new ExecuteCommandResult();
+ result.setSuccess(true);
+ return result;
+ });
+ var app = builder.build();
+ var _distributedAppConnectionString = app.getConnectionString("customcs");
+ var _distributedAppEndpoint = app.getEndpoint("dockerapp", "http");
+ var _distributedAppEndpointForNetwork = app.getEndpointForNetwork("dockerapp", new GetEndpointForNetworkOptions().networkIdentifier("localhost").endpointName("http"));
+ app.run();
+ }
diff --git a/playground/polyglot/Java/Aspire.Hosting/ValidationAppHost/aspire.config.json b/playground/polyglot/Java/Aspire.Hosting/ValidationAppHost/aspire.config.json
new file mode 100644
index 00000000000..b4a5675903a
--- /dev/null
+++ b/playground/polyglot/Java/Aspire.Hosting/ValidationAppHost/aspire.config.json
@@ -0,0 +1,24 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "features": {
+ "experimentalPolyglot:java": true
+ },
+ "packages": {
+ "Aspire.Hosting": "",
+ "Aspire.Hosting.Testing": "",
+ "Aspire.Hosting.Yarp": "",
+ "Aspire.Hosting.Redis": ""
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://localhost:29750;http://localhost:28931",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:10975",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:13319"
+ }
+ }
+ }
+}
diff --git a/src/Aspire.Cli/Resources/TemplatingStrings.Designer.cs b/src/Aspire.Cli/Resources/TemplatingStrings.Designer.cs
index a5712adf0fd..cf820142ea9 100644
--- a/src/Aspire.Cli/Resources/TemplatingStrings.Designer.cs
+++ b/src/Aspire.Cli/Resources/TemplatingStrings.Designer.cs
@@ -150,6 +150,15 @@ public static string AspireJsFrontendStarter_Description {
}
}
+ ///
+ /// Looks up a localized string similar to Java AppHost, Express, and React starter.
+ ///
+ public static string AspireJavaStarter_Description {
+ get {
+ return ResourceManager.GetString("AspireJavaStarter_Description", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to xUnit.
///
diff --git a/src/Aspire.Cli/Resources/TemplatingStrings.resx b/src/Aspire.Cli/Resources/TemplatingStrings.resx
index 9e9488cfa20..4a94f2183b0 100644
--- a/src/Aspire.Cli/Resources/TemplatingStrings.resx
+++ b/src/Aspire.Cli/Resources/TemplatingStrings.resx
@@ -126,6 +126,9 @@
Starter App (ASP.NET Core/React)
+
+ Starter App (Java AppHost/Express/React)
+
AppHost and service defaults
diff --git a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.cs.xlf b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.cs.xlf
index 10e15cf71ed..25bed11cc31 100644
--- a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.cs.xlf
+++ b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.cs.xlf
@@ -17,6 +17,11 @@
AppHost a výchozí nastavení služby
+
+ Starter App (Java AppHost/Express/React)
+ Starter App (Java AppHost/Express/React)
+
+
Starter App (ASP.NET Core/React)
Úvodní aplikace (ASP.NET Core/React)
diff --git a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.de.xlf b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.de.xlf
index d63be5b0c43..5fd2b4766de 100644
--- a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.de.xlf
+++ b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.de.xlf
@@ -17,6 +17,11 @@
AppHost und Dienststandardwerte
+
+ Starter App (Java AppHost/Express/React)
+ Starter App (Java AppHost/Express/React)
+
+
Starter App (ASP.NET Core/React)
Starter-App (ASP.NET Core/React)
diff --git a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.es.xlf b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.es.xlf
index 03995c63882..b623ce45f3a 100644
--- a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.es.xlf
+++ b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.es.xlf
@@ -17,6 +17,11 @@
AppHost y servicios predeterminados
+
+ Starter App (Java AppHost/Express/React)
+ Starter App (Java AppHost/Express/React)
+
+
Starter App (ASP.NET Core/React)
Aplicación de inicio (ASP.NET Core/React)
diff --git a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.fr.xlf b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.fr.xlf
index 12eeb3cf685..a11448a6953 100644
--- a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.fr.xlf
+++ b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.fr.xlf
@@ -17,6 +17,11 @@
AppHost et les paramètres par défaut du service
+
+ Starter App (Java AppHost/Express/React)
+ Starter App (Java AppHost/Express/React)
+
+
Starter App (ASP.NET Core/React)
Application de démarrage (ASP.NET Core/React)
diff --git a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.it.xlf b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.it.xlf
index f7141f0ec30..b7ae26657b3 100644
--- a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.it.xlf
+++ b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.it.xlf
@@ -17,6 +17,11 @@
Impostazioni predefinite di AppHost e del servizio
+
+ Starter App (Java AppHost/Express/React)
+ Starter App (Java AppHost/Express/React)
+
+
Starter App (ASP.NET Core/React)
Starter App (ASP.NET Core/React)
diff --git a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.ja.xlf b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.ja.xlf
index 1d91797d579..c664e10c1fa 100644
--- a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.ja.xlf
+++ b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.ja.xlf
@@ -17,6 +17,11 @@
AppHost およびサービスの既定値
+
+ Starter App (Java AppHost/Express/React)
+ Starter App (Java AppHost/Express/React)
+
+
Starter App (ASP.NET Core/React)
スターター アプリ (ASP.NET Core/React)
diff --git a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.ko.xlf b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.ko.xlf
index a79ace77751..a07a0837d8d 100644
--- a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.ko.xlf
+++ b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.ko.xlf
@@ -17,6 +17,11 @@
AppHost 및 서비스 기본값
+
+ Starter App (Java AppHost/Express/React)
+ Starter App (Java AppHost/Express/React)
+
+
Starter App (ASP.NET Core/React)
시작 앱(ASP.NET Core/React)
diff --git a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.pl.xlf b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.pl.xlf
index d3645708ce9..63c1a26c50a 100644
--- a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.pl.xlf
+++ b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.pl.xlf
@@ -17,6 +17,11 @@
AppHost i ustawienia domyślne usługi
+
+ Starter App (Java AppHost/Express/React)
+ Starter App (Java AppHost/Express/React)
+
+
Starter App (ASP.NET Core/React)
Aplikacja startowa (ASP.NET Core/React)
diff --git a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.pt-BR.xlf b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.pt-BR.xlf
index 8b6ad9efe2e..6e95d101a5d 100644
--- a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.pt-BR.xlf
+++ b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.pt-BR.xlf
@@ -17,6 +17,11 @@
AppHost e padrões de serviço
+
+ Starter App (Java AppHost/Express/React)
+ Starter App (Java AppHost/Express/React)
+
+
Starter App (ASP.NET Core/React)
Aplicativo inicial (ASP.NET Core/React)
diff --git a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.ru.xlf b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.ru.xlf
index f7263021c9f..e79e3e4e656 100644
--- a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.ru.xlf
+++ b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.ru.xlf
@@ -17,6 +17,11 @@
AppHost и службы по умолчанию
+
+ Starter App (Java AppHost/Express/React)
+ Starter App (Java AppHost/Express/React)
+
+
Starter App (ASP.NET Core/React)
Начальное приложение (ASP.NET Core/React)
diff --git a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.tr.xlf b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.tr.xlf
index 2706d8e6108..c128af9d906 100644
--- a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.tr.xlf
+++ b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.tr.xlf
@@ -17,6 +17,11 @@
AppHost ve hizmet varsayılanları
+
+ Starter App (Java AppHost/Express/React)
+ Starter App (Java AppHost/Express/React)
+
+
Starter App (ASP.NET Core/React)
Başlangıç Uygulaması (ASP.NET Core/React)
diff --git a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.zh-Hans.xlf b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.zh-Hans.xlf
index 0230e119447..aedebc47ba6 100644
--- a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.zh-Hans.xlf
+++ b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.zh-Hans.xlf
@@ -17,6 +17,11 @@
AppHost 和服务默认值
+
+ Starter App (Java AppHost/Express/React)
+ Starter App (Java AppHost/Express/React)
+
+
Starter App (ASP.NET Core/React)
入门应用(ASP.NET Core/React)
diff --git a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.zh-Hant.xlf b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.zh-Hant.xlf
index e5a37e45e00..82a9a3c4365 100644
--- a/src/Aspire.Cli/Resources/xlf/TemplatingStrings.zh-Hant.xlf
+++ b/src/Aspire.Cli/Resources/xlf/TemplatingStrings.zh-Hant.xlf
@@ -17,6 +17,11 @@
AppHost 和服務預設值
+
+ Starter App (Java AppHost/Express/React)
+ Starter App (Java AppHost/Express/React)
+
+
Starter App (ASP.NET Core/React)
入門應用程式 (ASP.NET Core/React)
diff --git a/src/Aspire.Cli/Templating/CliTemplateFactory.cs b/src/Aspire.Cli/Templating/CliTemplateFactory.cs
index 41618049ce2..3ef1826fa2a 100644
--- a/src/Aspire.Cli/Templating/CliTemplateFactory.cs
+++ b/src/Aspire.Cli/Templating/CliTemplateFactory.cs
@@ -81,12 +81,12 @@ public Task> GetTemplatesAsync(CancellationToken cancella
public Task> GetInitTemplatesAsync(CancellationToken cancellationToken = default)
{
- return Task.FromResult>([]);
+ return Task.FromResult>(Array.Empty());
}
private IEnumerable GetTemplateDefinitions()
{
- return
+ ITemplate[] templates =
[
new CallbackTemplate(
KnownTemplateId.TypeScriptStarter,
@@ -115,8 +115,30 @@ private IEnumerable GetTemplateDefinitions()
ApplyEmptyAppHostTemplateAsync,
runtime: TemplateRuntime.Cli,
languageId: KnownLanguageId.TypeScript,
+ isEmpty: true),
+
+ new CallbackTemplate(
+ KnownTemplateId.JavaEmptyAppHost,
+ "Empty (Java AppHost)",
+ projectName => $"./{projectName}",
+ cmd => AddOptionIfMissing(cmd, _localhostTldOption),
+ ApplyEmptyAppHostTemplateAsync,
+ runtime: TemplateRuntime.Cli,
+ languageId: KnownLanguageId.Java,
isEmpty: true)
];
+
+ return templates.Where(IsTemplateAvailable);
+ }
+
+ private bool IsTemplateAvailable(ITemplate template)
+ {
+ if (string.IsNullOrWhiteSpace(template.LanguageId))
+ {
+ return true;
+ }
+
+ return _languageDiscovery.GetLanguageById(new LanguageId(template.LanguageId)) is not null;
}
private static string ApplyTokens(string content, string projectName, string projectNameLower, string aspireVersion, TemplatePorts ports, string hostName = "localhost")
diff --git a/src/Aspire.Cli/Templating/KnownTemplateId.cs b/src/Aspire.Cli/Templating/KnownTemplateId.cs
index 59d90eeb078..36216035a26 100644
--- a/src/Aspire.Cli/Templating/KnownTemplateId.cs
+++ b/src/Aspire.Cli/Templating/KnownTemplateId.cs
@@ -27,4 +27,9 @@ internal static class KnownTemplateId
/// The template ID for the TypeScript starter template.
///
public const string TypeScriptStarter = "aspire-ts-starter";
+
+ ///
+ /// The template ID for the CLI Java empty AppHost template.
+ ///
+ public const string JavaEmptyAppHost = "aspire-java-empty";
}
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/AppHost.java b/src/Aspire.Cli/Templating/Templates/java-starter/AppHost.java
new file mode 100644
index 00000000000..31bbe44c623
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/AppHost.java
@@ -0,0 +1,19 @@
+package aspire;
+
+final class AppHost {
+ void main() throws Exception {
+ IDistributedApplicationBuilder builder = DistributedApplication.CreateBuilder();
+
+ NodeAppResource app = builder.addNodeApp("app", "./api", "src/index.ts");
+ app.withHttpEndpoint(new WithHttpEndpointOptions().env("PORT"));
+ app.withExternalHttpEndpoints();
+
+ ViteAppResource frontend = builder.addViteApp("frontend", "./frontend");
+ frontend.withReference(app);
+ frontend.waitFor(app);
+
+ app.publishWithContainerFiles(frontend, "./static");
+
+ builder.build().run();
+ }
+}
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/api/package.json b/src/Aspire.Cli/Templating/Templates/java-starter/api/package.json
new file mode 100644
index 00000000000..296e3af621c
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/api/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "{{projectNameLower}}-api",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "start": "tsx src/index.ts"
+ },
+ "dependencies": {
+ "@opentelemetry/auto-instrumentations-node": "^0.71.0",
+ "@opentelemetry/exporter-logs-otlp-grpc": "^0.213.0",
+ "@opentelemetry/exporter-metrics-otlp-grpc": "^0.213.0",
+ "@opentelemetry/exporter-trace-otlp-grpc": "^0.213.0",
+ "@opentelemetry/sdk-logs": "^0.213.0",
+ "@opentelemetry/sdk-metrics": "^2.6.0",
+ "@opentelemetry/sdk-node": "^0.213.0",
+ "express": "^5.1.0"
+ },
+ "devDependencies": {
+ "@types/express": "^5.0.6",
+ "tsx": "^4.21.0"
+ }
+}
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/api/src/index.ts b/src/Aspire.Cli/Templating/Templates/java-starter/api/src/index.ts
new file mode 100644
index 00000000000..4d2c25855e1
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/api/src/index.ts
@@ -0,0 +1,42 @@
+/**
+ * Import the OpenTelemetry instrumentation setup first, before any other modules.
+ * This ensures all subsequent imports are automatically instrumented for
+ * distributed tracing, metrics, and logging in the Aspire dashboard.
+ */
+import "./instrumentation.ts";
+import express from "express";
+import { existsSync } from "fs";
+import { join } from "path";
+
+const app = express();
+const port = process.env.PORT || 5000;
+
+/** Returns a random 5-day weather forecast as JSON. */
+app.get("/api/weatherforecast", (_req, res) => {
+ const summaries = ["Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"];
+ const forecasts = Array.from({ length: 5 }, (_, i) => {
+ const temperatureC = Math.floor(Math.random() * 75) - 20;
+ return {
+ date: new Date(Date.now() + (i + 1) * 86400000).toISOString(),
+ temperatureC,
+ temperatureF: 32 + Math.trunc(temperatureC / 0.5556),
+ summary: summaries[Math.floor(Math.random() * summaries.length)],
+ };
+ });
+ res.json(forecasts);
+});
+
+app.get("/health", (_req, res) => {
+ res.send("Healthy");
+});
+
+// Serve static files from the "static" directory if it exists (used in publish/deploy mode
+// when the frontend's build output is bundled into this container via publishWithContainerFiles)
+const staticDir = join(import.meta.dirname, "..", "static");
+if (existsSync(staticDir)) {
+ app.use(express.static(staticDir));
+}
+
+app.listen(port, () => {
+ console.log(`API server listening on port ${port}`);
+});
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/api/src/instrumentation.ts b/src/Aspire.Cli/Templating/Templates/java-starter/api/src/instrumentation.ts
new file mode 100644
index 00000000000..c19e073d172
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/api/src/instrumentation.ts
@@ -0,0 +1,42 @@
+/**
+ * OpenTelemetry instrumentation for the app.
+ *
+ * When the OTEL_EXPORTER_OTLP_ENDPOINT environment variable is set (automatically
+ * configured by Aspire), this module initializes the OpenTelemetry Node.js SDK to
+ * collect and export distributed traces, metrics, and logs to the Aspire dashboard.
+ *
+ * This file must be imported before any other modules to ensure all libraries
+ * are automatically instrumented.
+ *
+ * @see https://opentelemetry.io/docs/languages/js/getting-started/nodejs/
+ */
+import { env } from 'node:process';
+import { NodeSDK } from '@opentelemetry/sdk-node';
+import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
+import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';
+import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc';
+import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-grpc';
+import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';
+import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
+
+if (env.OTEL_EXPORTER_OTLP_ENDPOINT) {
+ const sdk = new NodeSDK({
+ traceExporter: new OTLPTraceExporter(),
+ metricReader: new PeriodicExportingMetricReader({
+ exporter: new OTLPMetricExporter(),
+ }),
+ logRecordProcessor: new BatchLogRecordProcessor(
+ new OTLPLogExporter(),
+ ),
+ instrumentations: [getNodeAutoInstrumentations()],
+ });
+
+ sdk.start();
+
+ process.on('SIGTERM', () => {
+ sdk.shutdown().finally(() => process.exit(0));
+ });
+ process.on('SIGINT', () => {
+ sdk.shutdown().finally(() => process.exit(0));
+ });
+}
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/api/tsconfig.json b/src/Aspire.Cli/Templating/Templates/java-starter/api/tsconfig.json
new file mode 100644
index 00000000000..6f1f8cb7405
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/api/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./dist"
+ },
+ "include": ["src"]
+}
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/aspire.config.json b/src/Aspire.Cli/Templating/Templates/java-starter/aspire.config.json
new file mode 100644
index 00000000000..ec9a8f30d23
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/aspire.config.json
@@ -0,0 +1,26 @@
+{
+ "appHost": {
+ "path": "AppHost.java",
+ "language": "java"
+ },
+ "packages": {
+ "Aspire.Hosting.JavaScript": "{{aspireVersion}}"
+ },
+ "profiles": {
+ "https": {
+ "applicationUrl": "https://{{hostName}}:{{httpsPort}};http://{{hostName}}:{{httpPort}}",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://{{hostName}}:{{otlpHttpsPort}}",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://{{hostName}}:{{resourceHttpsPort}}"
+ }
+ },
+ "http": {
+ "applicationUrl": "http://{{hostName}}:{{httpPort}}",
+ "environmentVariables": {
+ "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://{{hostName}}:{{otlpHttpPort}}",
+ "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://{{hostName}}:{{resourceHttpPort}}",
+ "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true"
+ }
+ }
+ }
+}
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/frontend/.dockerignore b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/.dockerignore
new file mode 100644
index 00000000000..b9470778764
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/.dockerignore
@@ -0,0 +1,2 @@
+node_modules/
+dist/
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/frontend/.gitignore b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/.gitignore
new file mode 100644
index 00000000000..a547bf36d8d
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/frontend/eslint.config.js b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/eslint.config.js
new file mode 100644
index 00000000000..2b3e6ccfb16
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/eslint.config.js
@@ -0,0 +1,28 @@
+import js from '@eslint/js';
+import globals from 'globals';
+import reactHooks from 'eslint-plugin-react-hooks';
+import reactRefresh from 'eslint-plugin-react-refresh';
+import tseslint from 'typescript-eslint';
+
+export default tseslint.config(
+ { ignores: ['dist'] },
+ {
+ extends: [js.configs.recommended, ...tseslint.configs.recommended],
+ files: ['**/*.{ts,tsx}'],
+ languageOptions: {
+ ecmaVersion: 2020,
+ globals: globals.browser,
+ },
+ plugins: {
+ 'react-hooks': reactHooks,
+ 'react-refresh': reactRefresh,
+ },
+ rules: {
+ ...reactHooks.configs.recommended.rules,
+ 'react-refresh/only-export-components': [
+ 'warn',
+ { allowConstantExport: true },
+ ],
+ },
+ },
+);
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/frontend/index.html b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/index.html
new file mode 100644
index 00000000000..472646d2e0f
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Aspire Starter
+
+
+
+
+
+
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/frontend/package-lock.json b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/package-lock.json
new file mode 100644
index 00000000000..cf8c8acaba1
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/package-lock.json
@@ -0,0 +1,2430 @@
+{
+ "name": "frontend",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "frontend",
+ "version": "0.0.0",
+ "dependencies": {
+ "react": "^19.2.1",
+ "react-dom": "^19.2.1"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.39.1",
+ "@types/node": "^24.10.1",
+ "@types/react": "^19.2.7",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^6.0.0",
+ "eslint": "^9.39.1",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.24",
+ "globals": "^16.5.0",
+ "typescript": "~5.9.3",
+ "typescript-eslint": "^8.48.1",
+ "vite": "^8.0.0"
+ }
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz",
+ "integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.0",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz",
+ "integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz",
+ "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
+ "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
+ "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.7",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
+ "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.1",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.39.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz",
+ "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.7",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
+ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
+ "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1",
+ "@tybys/wasm-util": "^0.10.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@oxc-project/runtime": {
+ "version": "0.115.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.115.0.tgz",
+ "integrity": "sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@oxc-project/types": {
+ "version": "0.115.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.115.0.tgz",
+ "integrity": "sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.9.tgz",
+ "integrity": "sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.9.tgz",
+ "integrity": "sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.9.tgz",
+ "integrity": "sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.9.tgz",
+ "integrity": "sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.9.tgz",
+ "integrity": "sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.9.tgz",
+ "integrity": "sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.9.tgz",
+ "integrity": "sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.9.tgz",
+ "integrity": "sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.9.tgz",
+ "integrity": "sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.9.tgz",
+ "integrity": "sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.9.tgz",
+ "integrity": "sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.9.tgz",
+ "integrity": "sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.9.tgz",
+ "integrity": "sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.9.tgz",
+ "integrity": "sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.9.tgz",
+ "integrity": "sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-rc.7",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz",
+ "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==",
+ "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/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "24.10.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
+ "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "undici-types": "~7.16.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
+ "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.48.1.tgz",
+ "integrity": "sha512-X63hI1bxl5ohelzr0LY5coufyl0LJNthld+abwxpCoo6Gq+hSqhKwci7MUWkXo67mzgUK6YFByhmaHmUcuBJmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.48.1",
+ "@typescript-eslint/type-utils": "8.48.1",
+ "@typescript-eslint/utils": "8.48.1",
+ "@typescript-eslint/visitor-keys": "8.48.1",
+ "graphemer": "^1.4.0",
+ "ignore": "^7.0.0",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.48.1",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.48.1.tgz",
+ "integrity": "sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.48.1",
+ "@typescript-eslint/types": "8.48.1",
+ "@typescript-eslint/typescript-estree": "8.48.1",
+ "@typescript-eslint/visitor-keys": "8.48.1",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.48.1.tgz",
+ "integrity": "sha512-HQWSicah4s9z2/HifRPQ6b6R7G+SBx64JlFQpgSSHWPKdvCZX57XCbszg/bapbRsOEv42q5tayTYcEFpACcX1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.48.1",
+ "@typescript-eslint/types": "^8.48.1",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.48.1.tgz",
+ "integrity": "sha512-rj4vWQsytQbLxC5Bf4XwZ0/CKd362DkWMUkviT7DCS057SK64D5lH74sSGzhI6PDD2HCEq02xAP9cX68dYyg1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.48.1",
+ "@typescript-eslint/visitor-keys": "8.48.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.48.1.tgz",
+ "integrity": "sha512-k0Jhs4CpEffIBm6wPaCXBAD7jxBtrHjrSgtfCjUvPp9AZ78lXKdTR8fxyZO5y4vWNlOvYXRtngSZNSn+H53Jkw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.48.1.tgz",
+ "integrity": "sha512-1jEop81a3LrJQLTf/1VfPQdhIY4PlGDBc/i67EVWObrtvcziysbLN3oReexHOM6N3jyXgCrkBsZpqwH0hiDOQg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.48.1",
+ "@typescript-eslint/typescript-estree": "8.48.1",
+ "@typescript-eslint/utils": "8.48.1",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.48.1.tgz",
+ "integrity": "sha512-+fZ3LZNeiELGmimrujsDCT4CRIbq5oXdHe7chLiW8qzqyPMnn1puNstCrMNVAqwcl2FdIxkuJ4tOs/RFDBVc/Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.48.1.tgz",
+ "integrity": "sha512-/9wQ4PqaefTK6POVTjJaYS0bynCgzh6ClJHGSBj06XEHjkfylzB+A3qvyaXnErEZSaxhIo4YdyBgq6j4RysxDg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.48.1",
+ "@typescript-eslint/tsconfig-utils": "8.48.1",
+ "@typescript-eslint/types": "8.48.1",
+ "@typescript-eslint/visitor-keys": "8.48.1",
+ "debug": "^4.3.4",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.48.1.tgz",
+ "integrity": "sha512-fAnhLrDjiVfey5wwFRwrweyRlCmdz5ZxXz2G/4cLn0YDLjTapmN4gcCsTBR1N2rWnZSDeWpYtgLDsJt+FpmcwA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.7.0",
+ "@typescript-eslint/scope-manager": "8.48.1",
+ "@typescript-eslint/types": "8.48.1",
+ "@typescript-eslint/typescript-estree": "8.48.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.48.1.tgz",
+ "integrity": "sha512-BmxxndzEWhE4TIEEMBs8lP3MBWN3jFPs/p6gPm/wkv02o41hI6cq9AuSmGAaTTHPtA1FTi2jBre4A9rm5ZmX+Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.48.1",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz",
+ "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rolldown/pluginutils": "1.0.0-rc.7"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0",
+ "babel-plugin-react-compiler": "^1.0.0",
+ "vite": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@rolldown/plugin-babel": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.39.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz",
+ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.1",
+ "@eslint/config-helpers": "^0.4.2",
+ "@eslint/core": "^0.17.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.39.1",
+ "@eslint/plugin-kit": "^0.4.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
+ "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react-refresh": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.24.tgz",
+ "integrity": "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "eslint": ">=8.40"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "16.5.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
+ "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "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/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.8",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
+ "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz",
+ "integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.1.tgz",
+ "integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.1"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/rolldown": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.9.tgz",
+ "integrity": "sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.115.0",
+ "@rolldown/pluginutils": "1.0.0-rc.9"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm64": "1.0.0-rc.9",
+ "@rolldown/binding-darwin-arm64": "1.0.0-rc.9",
+ "@rolldown/binding-darwin-x64": "1.0.0-rc.9",
+ "@rolldown/binding-freebsd-x64": "1.0.0-rc.9",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.9",
+ "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.9",
+ "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.9",
+ "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.9",
+ "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.9",
+ "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.9",
+ "@rolldown/binding-linux-x64-musl": "1.0.0-rc.9",
+ "@rolldown/binding-openharmony-arm64": "1.0.0-rc.9",
+ "@rolldown/binding-wasm32-wasi": "1.0.0-rc.9",
+ "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.9",
+ "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.9"
+ }
+ },
+ "node_modules/rolldown/node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-rc.9",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.9.tgz",
+ "integrity": "sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+ "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD",
+ "optional": true
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.48.1",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.48.1.tgz",
+ "integrity": "sha512-FbOKN1fqNoXp1hIl5KYpObVrp0mCn+CLgn479nmu2IsRMrx2vyv74MmsBLVlhg8qVwNFGbXSp8fh1zp8pEoC2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.48.1",
+ "@typescript-eslint/parser": "8.48.1",
+ "@typescript-eslint/typescript-estree": "8.48.1",
+ "@typescript-eslint/utils": "8.48.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.0.tgz",
+ "integrity": "sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@oxc-project/runtime": "0.115.0",
+ "lightningcss": "^1.32.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.8",
+ "rolldown": "1.0.0-rc.9",
+ "tinyglobby": "^0.2.15"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.0.0-alpha.31",
+ "esbuild": "^0.27.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
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/frontend/package.json b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/package.json
new file mode 100644
index 00000000000..e684fdb3139
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "frontend",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "react": "^19.2.1",
+ "react-dom": "^19.2.1"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.39.1",
+ "@types/node": "^24.10.1",
+ "@types/react": "^19.2.7",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^6.0.0",
+ "eslint": "^9.39.1",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.24",
+ "globals": "^16.5.0",
+ "typescript": "~5.9.3",
+ "typescript-eslint": "^8.48.1",
+ "vite": "^8.0.0"
+ }
+}
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/frontend/public/Aspire.png b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/public/Aspire.png
new file mode 100644
index 00000000000..d4be662a618
Binary files /dev/null and b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/public/Aspire.png differ
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/frontend/public/github.svg b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/public/github.svg
new file mode 100644
index 00000000000..162a4a43a35
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/public/github.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/frontend/src/App.css b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/src/App.css
new file mode 100644
index 00000000000..1f690dea729
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/src/App.css
@@ -0,0 +1,794 @@
+/* CSS Variables for theming */
+:root {
+ --bg-gradient-start: #1a1a2e;
+ --bg-gradient-end: #16213e;
+ --card-bg: rgba(30, 30, 46, 0.95);
+ --card-hover-shadow: rgba(0, 0, 0, 0.3);
+ --text-primary: #ffffff;
+ --text-secondary: #e2e8f0;
+ --text-tertiary: #cbd5e0;
+ --accent-gradient-start: #7c92f5;
+ --accent-gradient-end: #8b5ecf;
+ --weather-card-bg: rgba(45, 45, 60, 0.8);
+ --weather-card-border: rgba(255, 255, 255, 0.1);
+ --section-title-color: #f7fafc;
+ --date-color: #cbd5e0;
+ --summary-color: #f7fafc;
+ --temp-unit-color: #e2e8f0;
+ --divider-color: #4a5568;
+ --error-bg: rgba(220, 38, 38, 0.1);
+ --error-border: #ef4444;
+ --error-text: #fca5a5;
+ --skeleton-bg-1: rgba(255, 255, 255, 0.05);
+ --skeleton-bg-2: rgba(255, 255, 255, 0.1);
+ --tile-min-height: 120px;
+ --tile-gap: 0.75rem;
+ --cta-height: 3rem;
+ --card-inner-gap: 1rem;
+ --focus-color: #a78bfa;
+}
+
+@media (prefers-color-scheme: light) {
+ :root {
+ --bg-gradient-start: #f0f4ff;
+ --bg-gradient-end: #e0e7ff;
+ --card-bg: rgba(255, 255, 255, 0.98);
+ --card-hover-shadow: rgba(0, 0, 0, 0.15);
+ --text-primary: #1a202c;
+ --text-secondary: #2d3748;
+ --text-tertiary: #4a5568;
+ --accent-gradient-start: #5b6fd8;
+ --accent-gradient-end: #6b46a3;
+ --weather-card-bg: rgba(255, 255, 255, 0.9);
+ --weather-card-border: rgba(102, 126, 234, 0.15);
+ --section-title-color: #1a202c;
+ --date-color: #2d3748;
+ --summary-color: #1a202c;
+ --temp-unit-color: #4a5568;
+ --divider-color: #cbd5e0;
+ --error-bg: #fee;
+ --error-border: #dc2626;
+ --error-text: #991b1b;
+ --skeleton-bg-1: #f0f0f0;
+ --skeleton-bg-2: #e0e0e0;
+ --tile-min-height: 120px;
+ --tile-gap: 0.75rem;
+ --cta-height: 3rem;
+ --card-inner-gap: 1rem;
+ --focus-color: #5b21b6;
+ }
+}
+
+/* Root container */
+#root {
+ width: 100%;
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ overflow-x: hidden;
+}
+
+/* Accessibility utilities */
+.visually-hidden {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
+}
+
+/* App container */
+.app-container {
+ width: 100%;
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ background: linear-gradient(135deg, var(--bg-gradient-start) 0%, var(--bg-gradient-end) 100%);
+ color: var(--text-primary);
+}
+
+/* Header */
+.app-header {
+ padding: 2.5rem 2rem 1.5rem;
+ text-align: center;
+ animation: fadeInDown 0.6s ease-out;
+}
+
+.logo-link {
+ display: inline-block;
+ border-radius: 0.5rem;
+}
+
+.logo-link:focus-visible {
+ outline: 3px solid var(--accent-gradient-end);
+ outline-offset: 8px;
+}
+
+.logo {
+ height: 5rem;
+ width: auto;
+ transition: transform 300ms ease, filter 300ms ease;
+ filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.2));
+}
+
+.logo:hover {
+ transform: scale(1.1) rotate(5deg);
+ filter: drop-shadow(0 8px 16px rgba(0, 0, 0, 0.3));
+}
+
+.app-title {
+ font-size: 2.75rem;
+ font-weight: 700;
+ margin: 1.25rem 0 0.5rem;
+ background: linear-gradient(135deg, var(--text-primary) 0%, var(--text-secondary) 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ letter-spacing: -0.02em;
+}
+
+.app-subtitle {
+ font-size: 1.05rem;
+ color: var(--text-tertiary);
+ margin: 0;
+ font-weight: 300;
+}
+
+/* Main content */
+.main-content {
+ flex: 1;
+ max-width: 1400px;
+ width: 100%;
+ margin: 0 auto;
+ padding: 0rem 2rem 2rem;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+/* Card styles */
+.card {
+ background: var(--card-bg);
+ backdrop-filter: blur(10px);
+ border-radius: 1rem;
+ padding: 1.25rem 1.5rem;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+ color: var(--text-primary);
+ animation: fadeInUp 0.6s ease-out;
+ border: 1px solid var(--weather-card-border);
+ display: flex;
+ flex-direction: column;
+ gap: var(--card-inner-gap);
+}
+
+/* Section styles */
+.demo-section {
+ animation: fadeInUp 0.6s ease-out;
+}
+
+.weather-section {
+ animation: fadeInUp 0.6s ease-out;
+ animation-delay: 0.1s;
+ flex: 1;
+ max-width: 1200px;
+ width: 100%;
+}
+
+.section-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 0;
+ flex-wrap: wrap;
+ gap: 1rem;
+}
+
+.header-actions {
+ display: flex;
+ gap: 0.75rem;
+ align-items: center;
+}
+
+.section-title {
+ font-size: 1.25rem;
+ font-weight: 600;
+ margin: 0;
+ color: var(--section-title-color);
+}
+
+/* Counter area */
+.counter-card .section-header {
+ margin-bottom: 0;
+}
+
+.counter-panel {
+ background: var(--weather-card-bg);
+ border-radius: 0.75rem;
+ border: 1px solid var(--weather-card-border);
+ padding: 1.25rem;
+ display: flex;
+ flex-direction: column;
+ gap: var(--tile-gap);
+ min-height: var(--tile-min-height);
+ backdrop-filter: blur(10px);
+ flex: 1;
+}
+
+.counter-value-group {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 0.25rem;
+ flex: 1;
+ margin-bottom: var(--tile-gap);
+ text-align: center;
+}
+
+.counter-label {
+ font-size: 0.75rem;
+ text-transform: uppercase;
+ letter-spacing: 0.1em;
+ color: var(--text-tertiary);
+ font-weight: 600;
+}
+
+.counter-value {
+ font-size: 2.25rem;
+ font-weight: 700;
+ line-height: 1;
+ background: linear-gradient(135deg, var(--accent-gradient-start) 0%, var(--accent-gradient-end) 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.increment-button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+ background: linear-gradient(135deg, var(--accent-gradient-start) 0%, var(--accent-gradient-end) 100%);
+ color: white;
+ border: none;
+ border-radius: 0.5rem;
+ padding: 0 1.5rem;
+ height: var(--cta-height);
+ font-size: 0.875rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
+ width: 100%;
+ margin-top: auto;
+}
+
+.increment-button:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
+}
+
+.increment-button:active {
+ transform: translateY(0);
+}
+
+.increment-button:focus-visible {
+ outline: 3px solid var(--accent-gradient-end);
+ outline-offset: 2px;
+}
+
+.increment-icon {
+ transition: transform 0.3s ease;
+}
+
+.increment-button:hover .increment-icon {
+ transform: scale(1.1);
+}
+
+/* Toggle switch */
+.toggle-switch {
+ display: flex;
+ background: rgba(255, 255, 255, 0.1);
+ border-radius: 0.5rem;
+ padding: 0.25rem;
+ gap: 0.25rem;
+ border: 1px solid var(--weather-card-border);
+ margin: 0;
+ padding: 0.25rem;
+ min-width: 0;
+}
+
+.toggle-switch legend {
+ padding: 0;
+}
+
+@media (prefers-color-scheme: light) {
+ .toggle-switch {
+ background: rgba(102, 126, 234, 0.08);
+ }
+}
+
+.toggle-option {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: transparent;
+ color: var(--text-secondary);
+ border: none;
+ border-radius: 0.375rem;
+ padding: 0 1rem;
+ height: 2.5rem;
+ font-size: 0.875rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ min-width: 3rem;
+ position: relative;
+}
+
+.toggle-option[aria-pressed="true"] {
+ background: linear-gradient(135deg, var(--accent-gradient-start) 0%, var(--accent-gradient-end) 100%);
+ color: white;
+ box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
+}
+
+.toggle-option[aria-pressed="false"]:hover {
+ background: rgba(255, 255, 255, 0.05);
+ color: var(--text-primary);
+}
+
+@media (prefers-color-scheme: light) {
+ .toggle-option[aria-pressed="false"]:hover {
+ background: rgba(102, 126, 234, 0.1);
+ }
+}
+
+.toggle-option:focus-visible {
+ outline: 3px solid var(--focus-color);
+ outline-offset: 2px;
+ z-index: 1;
+}
+
+/* Refresh button */
+.refresh-button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+ background: linear-gradient(135deg, var(--accent-gradient-start) 0%, var(--accent-gradient-end) 100%);
+ color: white;
+ border: none;
+ border-radius: 0.5rem;
+ padding: 0 1.5rem;
+ height: var(--cta-height);
+ font-size: 0.875rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition: transform 0.3s ease, box-shadow 0.3s ease, opacity 0.3s ease;
+ box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
+ min-width: 140px;
+ white-space: nowrap;
+}
+
+.refresh-button:hover:not(:disabled) {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
+}
+
+.refresh-button:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ transform: none;
+}
+
+.refresh-button:focus-visible {
+ outline: 3px solid var(--focus-color);
+ outline-offset: 3px;
+}
+
+.refresh-icon {
+ transition: transform 0.3s ease;
+}
+
+.refresh-icon.spinning {
+ animation: spin 1s linear infinite;
+}
+
+/* Error message */
+.error-message {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ background-color: var(--error-bg);
+ border-left: 4px solid var(--error-border);
+ color: var(--error-text);
+ padding: 1rem;
+ border-radius: 0.5rem;
+ margin: 1rem 0;
+ animation: slideIn 0.3s ease-out;
+}
+
+/* Loading skeleton */
+.loading-skeleton {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ margin-top: 1rem;
+}
+
+.skeleton-row {
+ height: 80px;
+ background: linear-gradient(90deg, var(--skeleton-bg-1) 25%, var(--skeleton-bg-2) 50%, var(--skeleton-bg-1) 75%);
+ background-size: 200% 100%;
+ animation: shimmer 1.5s infinite;
+ border-radius: 0.5rem;
+}
+
+/* Weather grid */
+.weather-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+ gap: 1rem;
+ margin-top: 0.75rem;
+}
+
+.weather-card {
+ background: var(--weather-card-bg);
+ border-radius: 0.75rem;
+ padding: 1.25rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+ transition: all 0.3s ease;
+ border: 1px solid var(--weather-card-border);
+ backdrop-filter: blur(10px);
+ min-height: var(--tile-min-height);
+}
+
+.weather-card:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
+}
+
+.weather-card:focus-within {
+ outline: 2px solid var(--focus-color);
+ outline-offset: 2px;
+}
+
+.weather-date {
+ font-weight: 600;
+ font-size: 0.875rem;
+ color: var(--date-color);
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ margin: 0;
+}
+
+.weather-summary {
+ font-size: 1.125rem;
+ font-weight: 500;
+ color: var(--summary-color);
+ min-height: 1.5rem;
+ margin: 0;
+}
+
+.weather-temps {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.75rem;
+ margin-top: 0.5rem;
+ padding-top: 0.75rem;
+ border-top: 1px solid var(--weather-card-border);
+}
+
+.temp-group {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 100%;
+}
+
+.temp-value {
+ font-size: 1.5rem;
+ font-weight: 700;
+ background: linear-gradient(135deg, var(--accent-gradient-start) 0%, var(--accent-gradient-end) 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.temp-unit {
+ font-size: 0.75rem;
+ color: var(--temp-unit-color);
+ margin-top: 0.125rem;
+}
+
+/* Responsive design */
+@media (max-width: 1024px) {
+ .main-content {
+ padding: 1rem;
+ }
+}
+.app-footer {
+ padding: 1.5rem;
+ text-align: center;
+ background: rgba(0, 0, 0, 0.2);
+ backdrop-filter: blur(10px);
+}
+
+.app-footer nav {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 1.5rem;
+}
+
+.app-footer a {
+ color: var(--text-secondary);
+ text-decoration: none;
+ font-weight: 500;
+ transition: color 0.3s ease, border-color 0.3s ease;
+ border-bottom: 2px solid transparent;
+ font-size: 0.875rem;
+ padding-bottom: 0.125rem;
+}
+
+.app-footer a:hover {
+ color: var(--text-primary);
+ border-bottom-color: var(--text-primary);
+}
+
+.app-footer a:focus-visible {
+ outline: 3px solid var(--focus-color);
+ outline-offset: 4px;
+ border-radius: 4px;
+}
+
+/* Animations */
+@keyframes fadeInDown {
+ from {
+ opacity: 0;
+ transform: translateY(-20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes slideIn {
+ from {
+ opacity: 0;
+ transform: translateX(-10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+@keyframes spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes shimmer {
+ 0% {
+ background-position: -200% 0;
+ }
+ 100% {
+ background-position: 200% 0;
+ }
+}
+
+/* Responsive design */
+@media (max-width: 1024px) {
+ .main-content {
+ grid-template-columns: 1fr;
+ padding: 1rem;
+ gap: 1rem;
+ }
+}
+
+/* Footer */
+.app-footer {
+ padding: 1.5rem 0;
+ text-align: center;
+ background: rgba(0, 0, 0, 0.2);
+ backdrop-filter: blur(10px);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.app-footer > * {
+ max-width: 1400px;
+ width: 100%;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0 2rem;
+ gap: 1rem;
+}
+
+.app-footer a {
+ color: var(--text-secondary);
+ text-decoration: none;
+ font-weight: 500;
+ transition: color 0.3s ease, transform 0.3s ease;
+ border-bottom: 2px solid transparent;
+ font-size: 0.875rem;
+}
+
+.app-footer a:hover {
+ color: var(--text-primary);
+ border-bottom-color: var(--text-primary);
+}
+
+.app-footer a:focus-visible {
+ outline: 2px solid var(--text-primary);
+ outline-offset: 4px;
+ border-radius: 2px;
+}
+
+.github-link {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.5rem;
+ border-bottom: none !important;
+}
+
+.github-link:focus-visible {
+ outline: 3px solid var(--focus-color);
+ outline-offset: 4px;
+ border-radius: 4px;
+}
+
+.github-link img {
+ transition: transform 0.3s ease, opacity 0.3s ease;
+ filter: brightness(0) invert(1);
+}
+
+@media (prefers-color-scheme: light) {
+ .github-link img {
+ filter: brightness(0) invert(0);
+ opacity: 0.7;
+ }
+
+ .github-link:hover img {
+ opacity: 1;
+ }
+}
+
+.github-link:hover img {
+ transform: scale(1.1);
+}
+
+@media (max-width: 768px) {
+ :root {
+ --cta-height: 2.75rem;
+ }
+
+ .app-header {
+ padding: 1.5rem 1rem 1rem;
+ }
+
+ .logo {
+ height: 3rem;
+ }
+
+ .app-title {
+ font-size: 1.5rem;
+ }
+
+ .app-subtitle {
+ font-size: 0.875rem;
+ }
+
+ .main-content {
+ padding: 0.75rem;
+ }
+
+ .card {
+ padding: 1rem;
+ }
+
+ .section-title {
+ font-size: 1.125rem;
+ }
+
+ .section-header {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 0.75rem;
+ }
+
+ .header-actions {
+ width: 100%;
+ }
+
+ .toggle-switch {
+ flex: 1;
+ }
+
+ .toggle-option {
+ flex: 1;
+ }
+
+ .refresh-button {
+ flex: 1;
+ justify-content: center;
+ padding: 0 1.25rem;
+ }
+
+ .weather-grid {
+ grid-template-columns: 1fr;
+ gap: 0.75rem;
+ }
+
+ .weather-card {
+ padding: 1.25rem;
+ }
+
+ .app-footer {
+ padding: 1rem 0;
+ }
+
+ .app-footer > * {
+ flex-direction: column;
+ padding: 0 1.5rem;
+ }
+
+ .github-link {
+ order: -1;
+ }
+}
+
+/* Reduced motion support */
+@media (prefers-reduced-motion: reduce) {
+ *,
+ *::before,
+ *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ }
+}
+
+/* High contrast mode support */
+@media (prefers-contrast: high) {
+ .card {
+ border: 2px solid currentColor;
+ }
+
+ .weather-card {
+ border: 1px solid currentColor;
+ }
+}
+
+/* Focus visible support for better keyboard navigation */
+*:focus-visible {
+ outline: 3px solid var(--accent-gradient-end);
+ outline-offset: 2px;
+}
+
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/frontend/src/App.tsx b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/src/App.tsx
new file mode 100644
index 00000000000..b9b5a1b10ae
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/src/App.tsx
@@ -0,0 +1,184 @@
+import { useState, useEffect } from 'react';
+import aspireLogo from '/Aspire.png';
+import './App.css';
+
+interface WeatherForecast {
+ date: string;
+ temperatureC: number;
+ temperatureF: number;
+ summary: string;
+}
+
+function App() {
+ const [weatherData, setWeatherData] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [useCelsius, setUseCelsius] = useState(false);
+
+ const fetchWeatherForecast = async () => {
+ setLoading(true);
+ setError(null);
+
+ try {
+ const response = await fetch('/api/weatherforecast');
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ const data: WeatherForecast[] = await response.json();
+ setWeatherData(data);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : 'Failed to fetch weather data');
+ console.error('Error fetching weather forecast:', err);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ fetchWeatherForecast();
+ }, []);
+
+ const formatDate = (dateString: string) => {
+ return new Date(dateString).toLocaleDateString(undefined, {
+ weekday: 'short',
+ month: 'short',
+ day: 'numeric'
+ });
+ };
+
+ return (
+
+
+
+
+
+
+
+
Weather Forecast
+
+
+
+
+
+
+ {error && (
+
+
+ {error}
+
+ )}
+
+ {loading && weatherData.length === 0 && (
+
+ {[...Array(5)].map((_, i) => (
+
+ ))}
+
Loading weather forecast data...
+
+ )}
+
+ {weatherData.length > 0 && (
+
+ {weatherData.map((forecast, index) => (
+
+
+
+
+ {forecast.summary}
+
+
+
+ {useCelsius ? forecast.temperatureC : forecast.temperatureF}°
+
+ {useCelsius ? 'Celsius' : 'Fahrenheit'}
+
+
+
+ ))}
+
+ )}
+
+
+
+
+
+
+ );
+}
+
+export default App;
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/frontend/src/index.css b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/src/index.css
new file mode 100644
index 00000000000..6d1b6557354
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/src/index.css
@@ -0,0 +1,55 @@
+:root {
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color-scheme: light dark;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+/* Reset and base styles */
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+
+html {
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ height: 100%;
+ overflow-x: hidden;
+}
+
+body {
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ min-height: 100vh;
+ overflow-x: hidden;
+}
+
+/* Remove default Vite styles that conflict with our design */
+h1 {
+ margin: 0;
+}
+
+button {
+ font-family: inherit;
+ cursor: pointer;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ *,
+ *::before,
+ *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ }
+}
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/frontend/src/main.tsx b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/src/main.tsx
new file mode 100644
index 00000000000..2239905c14d
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/src/main.tsx
@@ -0,0 +1,10 @@
+import { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import './index.css';
+import App from './App.tsx';
+
+createRoot(document.getElementById('root')!).render(
+
+
+ ,
+);
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/frontend/src/vite-env.d.ts b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/src/vite-env.d.ts
new file mode 100644
index 00000000000..11f02fe2a00
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/frontend/tsconfig.app.json b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/tsconfig.app.json
new file mode 100644
index 00000000000..c9ccbd4c594
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/tsconfig.app.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["src"]
+}
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/frontend/tsconfig.json b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/tsconfig.json
new file mode 100644
index 00000000000..1ffef600d95
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "files": [],
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ]
+}
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/frontend/tsconfig.node.json b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/tsconfig.node.json
new file mode 100644
index 00000000000..9728af2d81a
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/tsconfig.node.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "ES2022",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/src/Aspire.Cli/Templating/Templates/java-starter/frontend/vite.config.ts b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/vite.config.ts
new file mode 100644
index 00000000000..a11dd97e3bf
--- /dev/null
+++ b/src/Aspire.Cli/Templating/Templates/java-starter/frontend/vite.config.ts
@@ -0,0 +1,16 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react()],
+ server: {
+ proxy: {
+ // Proxy API calls to the Express service
+ '/api': {
+ target: process.env.APP_HTTPS || process.env.APP_HTTP,
+ changeOrigin: true
+ }
+ }
+ }
+});
diff --git a/src/Aspire.Hosting.CodeGeneration.Java/AtsJavaCodeGenerator.cs b/src/Aspire.Hosting.CodeGeneration.Java/AtsJavaCodeGenerator.cs
index a117af08ff5..3c1b88482b8 100644
--- a/src/Aspire.Hosting.CodeGeneration.Java/AtsJavaCodeGenerator.cs
+++ b/src/Aspire.Hosting.CodeGeneration.Java/AtsJavaCodeGenerator.cs
@@ -29,6 +29,9 @@ public sealed class AtsJavaCodeGenerator : ICodeGenerator
private readonly Dictionary _classNames = new(StringComparer.Ordinal);
private readonly Dictionary _dtoNames = new(StringComparer.Ordinal);
private readonly Dictionary _enumNames = new(StringComparer.Ordinal);
+ private readonly Dictionary> _optionsClassesToGenerate = new(StringComparer.Ordinal);
+ private readonly Dictionary _capabilityOptionsClassMap = new(StringComparer.Ordinal);
+ private readonly HashSet _resourceBuilderHandleClasses = new(StringComparer.Ordinal);
///
public string Language => "Java";
@@ -36,12 +39,20 @@ public sealed class AtsJavaCodeGenerator : ICodeGenerator
///
public Dictionary GenerateDistributedApplication(AtsContext context)
{
- return new Dictionary(StringComparer.Ordinal)
- {
- ["Transport.java"] = GetEmbeddedResource("Transport.java"),
- ["Base.java"] = GetEmbeddedResource("Base.java"),
- ["Aspire.java"] = GenerateAspireSdk(context)
- };
+ var files = new Dictionary(StringComparer.Ordinal);
+
+ AddSplitJavaSourceFiles(files, GetEmbeddedResource("Transport.java"));
+ AddSplitJavaSourceFiles(files, GetEmbeddedResource("Base.java"));
+ AddSplitJavaSourceFiles(files, GenerateAspireSdk(context));
+
+ files["sources.txt"] = string.Join(
+ '\n',
+ files.Keys
+ .Where(static key => key.EndsWith(".java", StringComparison.Ordinal))
+ .OrderBy(static key => key, StringComparer.Ordinal)
+ .Select(static key => $".modules/{key}")) + '\n';
+
+ return files;
}
private static string GetEmbeddedResource(string name)
@@ -55,6 +66,327 @@ private static string GetEmbeddedResource(string name)
return reader.ReadToEnd();
}
+ private static void AddSplitJavaSourceFiles(Dictionary files, string source)
+ {
+ foreach (var (fileName, content) in SplitJavaSourceFiles(source))
+ {
+ files.Add(fileName, content);
+ }
+ }
+
+ private static Dictionary SplitJavaSourceFiles(string source)
+ {
+ var packageLine = string.Empty;
+ var importLines = new List();
+ var declarations = new Dictionary(StringComparer.Ordinal);
+
+ var lines = source.Replace("\r\n", "\n", StringComparison.Ordinal).Split('\n');
+ var bodyStartIndex = 0;
+
+ for (var i = 0; i < lines.Length; i++)
+ {
+ var trimmed = lines[i].Trim();
+ if (trimmed.StartsWith("package ", StringComparison.Ordinal))
+ {
+ packageLine = trimmed;
+ continue;
+ }
+
+ if (string.IsNullOrEmpty(packageLine))
+ {
+ continue;
+ }
+
+ if (trimmed.StartsWith("import ", StringComparison.Ordinal))
+ {
+ importLines.Add(trimmed);
+ continue;
+ }
+
+ if (string.IsNullOrWhiteSpace(trimmed) || trimmed.StartsWith("//", StringComparison.Ordinal))
+ {
+ continue;
+ }
+
+ bodyStartIndex = i;
+ break;
+ }
+
+ List? currentDeclaration = null;
+ List? pendingLines = [];
+ string? currentTypeName = null;
+ var braceDepth = 0;
+ var inBlockComment = false;
+
+ for (var i = bodyStartIndex; i < lines.Length; i++)
+ {
+ var line = lines[i];
+ var trimmed = line.Trim();
+
+ if (currentDeclaration is null)
+ {
+ if (TryGetTopLevelDeclarationName(trimmed, out var declarationName))
+ {
+ currentTypeName = declarationName;
+ currentDeclaration = [];
+
+ if (pendingLines.Count > 0)
+ {
+ currentDeclaration.AddRange(pendingLines);
+ pendingLines.Clear();
+ }
+
+ currentDeclaration.Add(PromoteTopLevelDeclaration(line));
+ braceDepth = CountBraceDelta(line, ref inBlockComment);
+ continue;
+ }
+
+ if (ShouldPreserveTopLevelLine(trimmed))
+ {
+ pendingLines.Add(line);
+ }
+ else if (pendingLines.Count > 0 && string.IsNullOrWhiteSpace(trimmed))
+ {
+ pendingLines.Add(line);
+ }
+ else
+ {
+ pendingLines.Clear();
+ }
+
+ continue;
+ }
+
+ currentDeclaration.Add(line);
+ braceDepth += CountBraceDelta(line, ref inBlockComment);
+
+ if (braceDepth == 0)
+ {
+ declarations.Add(
+ $"{currentTypeName}.java",
+ CreateJavaSourceFile($"{currentTypeName}.java", packageLine, importLines, currentDeclaration));
+
+ currentDeclaration = null;
+ currentTypeName = null;
+ pendingLines = [];
+ }
+ }
+
+ return declarations;
+ }
+
+ private static bool TryGetTopLevelDeclarationName(string trimmedLine, out string? declarationName)
+ {
+ declarationName = null;
+
+ if (string.IsNullOrWhiteSpace(trimmedLine))
+ {
+ return false;
+ }
+
+ if (ShouldPreserveTopLevelLine(trimmedLine) || trimmedLine.StartsWith("//", StringComparison.Ordinal))
+ {
+ return false;
+ }
+
+ var declarationLine = trimmedLine;
+ while (true)
+ {
+ var updated = declarationLine switch
+ {
+ _ when declarationLine.StartsWith("public ", StringComparison.Ordinal) => declarationLine["public ".Length..].TrimStart(),
+ _ when declarationLine.StartsWith("final ", StringComparison.Ordinal) => declarationLine["final ".Length..].TrimStart(),
+ _ when declarationLine.StartsWith("abstract ", StringComparison.Ordinal) => declarationLine["abstract ".Length..].TrimStart(),
+ _ when declarationLine.StartsWith("static ", StringComparison.Ordinal) => declarationLine["static ".Length..].TrimStart(),
+ _ => declarationLine
+ };
+
+ if (ReferenceEquals(updated, declarationLine) || updated == declarationLine)
+ {
+ break;
+ }
+
+ declarationLine = updated;
+ }
+
+ foreach (var kind in new[] { "class", "interface", "enum", "record" })
+ {
+ var kindPrefix = kind + " ";
+ if (!declarationLine.StartsWith(kindPrefix, StringComparison.Ordinal))
+ {
+ continue;
+ }
+
+ declarationName = declarationLine[kindPrefix.Length..]
+ .Split([' ', '\t', '<'], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)[0];
+ return true;
+ }
+
+ return false;
+ }
+
+ private static bool ShouldPreserveTopLevelLine(string trimmedLine) =>
+ trimmedLine.StartsWith("/**", StringComparison.Ordinal)
+ || trimmedLine.StartsWith("/*", StringComparison.Ordinal)
+ || trimmedLine.StartsWith("*", StringComparison.Ordinal)
+ || trimmedLine.StartsWith("*/", StringComparison.Ordinal)
+ || trimmedLine.StartsWith("@", StringComparison.Ordinal);
+
+ private static string PromoteTopLevelDeclaration(string line)
+ {
+ var trimmed = line.TrimStart();
+ if (trimmed.StartsWith("public ", StringComparison.Ordinal))
+ {
+ return line;
+ }
+
+ var leadingWhitespaceLength = line.Length - trimmed.Length;
+ var leadingWhitespace = line[..leadingWhitespaceLength];
+
+ foreach (var declarationPrefix in new[]
+ {
+ "final class ",
+ "abstract class ",
+ "static class ",
+ "class ",
+ "interface ",
+ "enum ",
+ "record "
+ })
+ {
+ if (trimmed.StartsWith(declarationPrefix, StringComparison.Ordinal))
+ {
+ return $"{leadingWhitespace}public {trimmed}";
+ }
+ }
+
+ return line;
+ }
+
+ private static int CountBraceDelta(string line, ref bool inBlockComment)
+ {
+ var delta = 0;
+ var inString = false;
+ var inChar = false;
+ var escaped = false;
+
+ for (var i = 0; i < line.Length; i++)
+ {
+ var ch = line[i];
+ var next = i + 1 < line.Length ? line[i + 1] : '\0';
+
+ if (inBlockComment)
+ {
+ if (ch == '*' && next == '/')
+ {
+ inBlockComment = false;
+ i++;
+ }
+ continue;
+ }
+
+ if (!inString && !inChar)
+ {
+ if (ch == '/' && next == '/')
+ {
+ break;
+ }
+
+ if (ch == '/' && next == '*')
+ {
+ inBlockComment = true;
+ i++;
+ continue;
+ }
+ }
+
+ if (escaped)
+ {
+ escaped = false;
+ continue;
+ }
+
+ if (inString)
+ {
+ if (ch == '\\')
+ {
+ escaped = true;
+ }
+ else if (ch == '"')
+ {
+ inString = false;
+ }
+
+ continue;
+ }
+
+ if (inChar)
+ {
+ if (ch == '\\')
+ {
+ escaped = true;
+ }
+ else if (ch == '\'')
+ {
+ inChar = false;
+ }
+
+ continue;
+ }
+
+ if (ch == '"')
+ {
+ inString = true;
+ continue;
+ }
+
+ if (ch == '\'')
+ {
+ inChar = true;
+ continue;
+ }
+
+ if (ch == '{')
+ {
+ delta++;
+ }
+ else if (ch == '}')
+ {
+ delta--;
+ }
+ }
+
+ return delta;
+ }
+
+ private static string CreateJavaSourceFile(string fileName, string packageLine, List importLines, List declarationLines)
+ {
+ var builder = new StringBuilder();
+ builder.Append("// ");
+ builder.Append(fileName);
+ builder.AppendLine(" - GENERATED CODE - DO NOT EDIT");
+ builder.AppendLine();
+ builder.AppendLine(packageLine);
+ builder.AppendLine();
+
+ foreach (var importLine in importLines)
+ {
+ builder.AppendLine(importLine);
+ }
+
+ if (importLines.Count > 0)
+ {
+ builder.AppendLine();
+ }
+
+ foreach (var line in declarationLines)
+ {
+ builder.AppendLine(line);
+ }
+
+ return builder.ToString();
+ }
+
private string GenerateAspireSdk(AtsContext context)
{
using var stringWriter = new StringWriter(CultureInfo.InvariantCulture);
@@ -76,6 +408,10 @@ private string GenerateAspireSdk(AtsContext context)
_dtoNames[dto.TypeId] = SanitizeIdentifier(dto.Name);
}
+ _optionsClassesToGenerate.Clear();
+ _capabilityOptionsClassMap.Clear();
+ CollectOptionsClasses(capabilities);
+
var handleTypes = BuildHandleTypes(context);
var capabilitiesByTarget = GroupCapabilitiesByTarget(capabilities);
var collectionTypes = CollectListAndDictTypeIds(capabilities);
@@ -83,6 +419,7 @@ private string GenerateAspireSdk(AtsContext context)
WriteHeader();
GenerateEnumTypes(enumTypes);
GenerateDtoTypes(dtoTypes);
+ GenerateOptionTypes();
GenerateHandleTypes(handleTypes, capabilitiesByTarget);
GenerateHandleWrapperRegistrations(handleTypes, collectionTypes);
GenerateConnectionHelpers();
@@ -129,7 +466,7 @@ private void GenerateEnumTypes(IReadOnlyList enumTypes)
var enumName = _enumNames[enumType.TypeId];
WriteLine($"/** {enumType.Name} enum. */");
- WriteLine($"enum {enumName} {{");
+ WriteLine($"enum {enumName} implements WireValueEnum {{");
var members = Enum.GetNames(enumType.ClrType);
for (var i = 0; i < members.Length; i++)
{
@@ -208,17 +545,309 @@ private void GenerateDtoTypes(IReadOnlyList dtoTypes)
WriteLine(" Map map = new HashMap<>();");
foreach (var property in dto.Properties)
{
- var fieldName = ToCamelCase(property.Name);
- WriteLine($" map.put(\"{property.Name}\", AspireClient.serializeValue({fieldName}));");
+ var fieldName = ToCamelCase(property.Name);
+ WriteLine($" map.put(\"{property.Name}\", AspireClient.serializeValue({fieldName}));");
+ }
+ WriteLine(" return map;");
+ WriteLine(" }");
+
+ WriteLine("}");
+ WriteLine();
+ }
+ }
+
+ private void CollectOptionsClasses(IReadOnlyList capabilities)
+ {
+ foreach (var capability in capabilities)
+ {
+ var targetParamName = capability.TargetParameterName ?? "builder";
+ var parameters = capability.Parameters
+ .Where(p => !string.Equals(p.Name, targetParamName, StringComparison.Ordinal))
+ .ToList();
+ var (_, optionalParameters) = SeparateParameters(parameters);
+ if (optionalParameters.Count > 1)
+ {
+ RegisterOptionsClass(capability.CapabilityId, capability.MethodName, optionalParameters);
+ }
+ }
+ }
+
+ private void RegisterOptionsClass(string capabilityId, string methodName, List optionalParameters)
+ {
+ var baseClassName = GetOptionsClassName(methodName);
+ if (_optionsClassesToGenerate.TryGetValue(baseClassName, out var existingParameters))
+ {
+ if (AreOptionsCompatible(existingParameters, optionalParameters))
+ {
+ _capabilityOptionsClassMap[capabilityId] = baseClassName;
+ return;
+ }
+
+ for (var suffix = 1; ; suffix++)
+ {
+ var suffixedName = GetOptionsClassName($"{methodName}{suffix}");
+ if (!_optionsClassesToGenerate.TryGetValue(suffixedName, out var suffixedParameters))
+ {
+ _optionsClassesToGenerate[suffixedName] = [.. optionalParameters];
+ _capabilityOptionsClassMap[capabilityId] = suffixedName;
+ return;
+ }
+
+ if (AreOptionsCompatible(suffixedParameters, optionalParameters))
+ {
+ _capabilityOptionsClassMap[capabilityId] = suffixedName;
+ return;
+ }
+ }
+ }
+
+ _optionsClassesToGenerate[baseClassName] = [.. optionalParameters];
+ _capabilityOptionsClassMap[capabilityId] = baseClassName;
+ }
+
+ private static bool AreOptionsCompatible(List existing, List candidate)
+ {
+ if (existing.Count != candidate.Count)
+ {
+ return false;
+ }
+
+ for (var i = 0; i < existing.Count; i++)
+ {
+ if (!AreParameterTypesEqual(existing[i], candidate[i]) || !string.Equals(existing[i].Name, candidate[i].Name, StringComparison.Ordinal))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static bool AreParameterTypesEqual(AtsParameterInfo left, AtsParameterInfo right)
+ {
+ if (!string.Equals(left.Type?.TypeId, right.Type?.TypeId, StringComparison.Ordinal))
+ {
+ return false;
+ }
+
+ if (left.IsCallback != right.IsCallback)
+ {
+ return false;
+ }
+
+ if (!left.IsCallback)
+ {
+ return true;
+ }
+
+ var leftCallbackParameters = left.CallbackParameters ?? [];
+ var rightCallbackParameters = right.CallbackParameters ?? [];
+ if (leftCallbackParameters.Count != rightCallbackParameters.Count)
+ {
+ return false;
+ }
+
+ for (var i = 0; i < leftCallbackParameters.Count; i++)
+ {
+ if (!string.Equals(leftCallbackParameters[i].Type.TypeId, rightCallbackParameters[i].Type.TypeId, StringComparison.Ordinal))
+ {
+ return false;
+ }
+ }
+
+ return string.Equals(left.CallbackReturnType?.TypeId, right.CallbackReturnType?.TypeId, StringComparison.Ordinal);
+ }
+
+ private void GenerateOptionTypes()
+ {
+ if (_optionsClassesToGenerate.Count == 0)
+ {
+ return;
+ }
+
+ WriteLine("// ============================================================================");
+ WriteLine("// Options Types");
+ WriteLine("// ============================================================================");
+ WriteLine();
+
+ foreach (var (className, optionalParameters) in _optionsClassesToGenerate.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))
+ {
+ WriteLine($"/** Options for {className[..^"Options".Length]}. */");
+ WriteLine($"final class {className} {{");
+ foreach (var parameter in optionalParameters)
+ {
+ var parameterName = ToCamelCase(parameter.Name);
+ WriteLine($" private {MapParameterToJava(parameter)} {parameterName};");
+ }
+ WriteLine();
+
+ foreach (var parameter in optionalParameters)
+ {
+ var parameterName = ToCamelCase(parameter.Name);
+ var parameterType = MapParameterToJava(parameter);
+ WriteLine($" public {parameterType} {GetOptionGetterName(parameter)}() {{ return {parameterName}; }}");
+ WriteLine($" public {className} {parameterName}({parameterType} value) {{");
+ WriteLine($" this.{parameterName} = value;");
+ WriteLine(" return this;");
+ WriteLine(" }");
+ WriteLine();
+ }
+
+ WriteLine("}");
+ WriteLine();
+ }
+ }
+
+ private static (List Required, List Optional) SeparateParameters(IEnumerable parameters)
+ {
+ var required = new List();
+ var optional = new List();
+
+ foreach (var parameter in parameters)
+ {
+ if (parameter.IsOptional || parameter.IsNullable)
+ {
+ optional.Add(parameter);
+ }
+ else
+ {
+ required.Add(parameter);
+ }
+ }
+
+ return (required, optional);
+ }
+
+ private string? ResolveOptionsClassName(AtsCapabilityInfo capability) =>
+ _capabilityOptionsClassMap.TryGetValue(capability.CapabilityId, out var className) ? className : null;
+
+ private static string GetOptionsClassName(string methodName) =>
+ SanitizeIdentifier($"{ToPascalCase(methodName)}Options");
+
+ private static string AppendArgumentList(IEnumerable arguments, string trailingArgument)
+ {
+ var argumentList = arguments.ToList();
+ argumentList.Add(trailingArgument);
+ return string.Join(", ", argumentList);
+ }
+
+ private List CreateMethodParameters(IEnumerable parameters)
+ {
+ var result = new List();
+
+ foreach (var parameter in parameters)
+ {
+ var (resourceWrapperType, resourceWrapperParameterType) = GetResourceBuilderWrapperType(parameter);
+ result.Add(new JavaMethodParameter(
+ MapParameterToJava(parameter),
+ ToCamelCase(parameter.Name),
+ resourceWrapperType,
+ resourceWrapperParameterType));
+ }
+
+ return result;
+ }
+
+ private (string? ResourceWrapperType, string? ResourceWrapperParameterType) GetResourceBuilderWrapperType(AtsParameterInfo parameter)
+ {
+ if (parameter.IsCallback || parameter.Type?.Category != AtsTypeCategory.Handle)
+ {
+ return (null, null);
+ }
+
+ var wrapperType = MapInputTypeToJava(parameter.Type, parameter.IsOptional || parameter.IsNullable);
+ if (!wrapperType.StartsWith("I", StringComparison.Ordinal))
+ {
+ return (null, null);
+ }
+
+ return _resourceBuilderHandleClasses.Contains(wrapperType)
+ ? (wrapperType, "ResourceBuilderBase")
+ : (wrapperType, "HandleWrapperBase");
+ }
+
+ private void GenerateResourceBuilderOverloads(
+ string returnType,
+ string methodName,
+ IReadOnlyList parameters,
+ bool hasReturn)
+ {
+ if (parameters.Count == 0)
+ {
+ return;
+ }
+
+ var convertibleParameters = parameters
+ .Select((parameter, index) => new { Parameter = parameter, Index = index })
+ .Where(x => x.Parameter.ResourceWrapperType is not null)
+ .ToList();
+
+ if (convertibleParameters.Count == 0)
+ {
+ return;
+ }
+
+ var seenSignatures = new HashSet(StringComparer.Ordinal);
+ var combinationCount = 1 << convertibleParameters.Count;
+
+ for (var mask = 1; mask < combinationCount; mask++)
+ {
+ var selectedIndexes = new HashSet(
+ convertibleParameters
+ .Where((_, bitIndex) => (mask & (1 << bitIndex)) != 0)
+ .Select(x => x.Index));
+
+ var overloadParameters = new List(parameters.Count);
+ var callArguments = new List(parameters.Count);
+
+ for (var i = 0; i < parameters.Count; i++)
+ {
+ var parameter = parameters[i];
+ if (selectedIndexes.Contains(i))
+ {
+ overloadParameters.Add($"{parameter.ResourceWrapperParameterType} {parameter.Name}");
+ callArguments.Add($"new {parameter.ResourceWrapperType}({parameter.Name}.getHandle(), {parameter.Name}.getClient())");
+ }
+ else
+ {
+ overloadParameters.Add($"{parameter.Type} {parameter.Name}");
+ callArguments.Add(parameter.Name);
+ }
+ }
+
+ var signature = string.Join(", ", overloadParameters);
+ if (!seenSignatures.Add(signature))
+ {
+ continue;
+ }
+
+ WriteLine($" public {returnType} {methodName}({signature}) {{");
+ if (hasReturn)
+ {
+ WriteLine($" return {methodName}({string.Join(", ", callArguments)});");
+ }
+ else
+ {
+ WriteLine($" {methodName}({string.Join(", ", callArguments)});");
}
- WriteLine(" return map;");
WriteLine(" }");
-
- WriteLine("}");
WriteLine();
}
}
+ private static string GetOptionGetterName(AtsParameterInfo parameter)
+ {
+ var parameterName = ToCamelCase(parameter.Name);
+ if (parameterName.StartsWith("is", StringComparison.Ordinal) &&
+ parameterName.Length > 2 &&
+ char.IsUpper(parameterName[2]))
+ {
+ return parameterName;
+ }
+
+ return $"get{ToPascalCase(parameterName)}";
+ }
+
private void GenerateHandleTypes(
IReadOnlyList handleTypes,
Dictionary> capabilitiesByTarget)
@@ -247,34 +876,327 @@ private void GenerateHandleTypes(
{
foreach (var method in methods)
{
- GenerateCapabilityMethod(method);
+ GenerateCapabilityMethod(handleType, method);
}
}
+ if (string.Equals(handleType.ClassName, "DistributedApplication", StringComparison.Ordinal))
+ {
+ GenerateDistributedApplicationBuilderHelpers();
+ }
+
WriteLine("}");
WriteLine();
}
}
- private void GenerateCapabilityMethod(AtsCapabilityInfo capability)
+ private void GenerateDistributedApplicationBuilderHelpers()
+ {
+ var builderClassName = _classNames.TryGetValue(AtsConstants.BuilderTypeId, out var name)
+ ? name
+ : "DistributedApplicationBuilder";
+
+ WriteLine(" /** Create a new distributed application builder. */");
+ WriteLine($" public static {builderClassName} CreateBuilder() throws Exception {{");
+ WriteLine(" return CreateBuilder((String[]) null);");
+ WriteLine(" }");
+ WriteLine();
+ WriteLine(" /** Create a new distributed application builder. */");
+ WriteLine($" public static {builderClassName} CreateBuilder(String[] args) throws Exception {{");
+ WriteLine(" CreateBuilderOptions options = new CreateBuilderOptions();");
+ WriteLine(" if (args != null) {");
+ WriteLine(" options.setArgs(args);");
+ WriteLine(" }");
+ WriteLine(" return CreateBuilder(options);");
+ WriteLine(" }");
+ WriteLine();
+ WriteLine(" /** Create a new distributed application builder. */");
+ WriteLine($" public static {builderClassName} CreateBuilder(CreateBuilderOptions options) throws Exception {{");
+ WriteLine(" return Aspire.createBuilder(options);");
+ WriteLine(" }");
+ WriteLine();
+ }
+
+ private void GenerateCapabilityMethod(JavaHandleType handleType, AtsCapabilityInfo capability)
{
var targetParamName = capability.TargetParameterName ?? "builder";
var methodName = ToCamelCase(capability.MethodName);
var parameters = capability.Parameters
.Where(p => !string.Equals(p.Name, targetParamName, StringComparison.Ordinal))
.ToList();
+ var (requiredParameters, optionalParameters) = SeparateParameters(parameters);
+ var optionsClassName = ResolveOptionsClassName(capability);
+ var useOptionsClass = optionsClassName is not null;
+ var returnInfo = GetMethodReturnInfo(handleType, capability);
- // Check if this is a List/Dict property getter (no parameters, returns List/Dict)
if (parameters.Count == 0 && IsListOrDictPropertyGetter(capability.ReturnType))
{
GenerateListOrDictProperty(capability, methodName);
return;
}
- var returnType = MapTypeRefToJava(capability.ReturnType, false);
- var hasReturn = capability.ReturnType.TypeId != AtsConstants.Void;
+ if (useOptionsClass)
+ {
+ var implementationMethodName = $"{methodName}Impl";
+ GenerateUnionOverloadsWithOptions(returnInfo, methodName, requiredParameters, optionsClassName!);
+ GenerateOptionsOverloads(capability, returnInfo, methodName, implementationMethodName, requiredParameters, optionalParameters, optionsClassName!);
+ GenerateCapabilityMethodImplementation(capability, returnInfo, implementationMethodName, targetParamName, parameters, isPublic: false);
+ }
+ else
+ {
+ GenerateUnionOverloads(returnInfo, methodName, parameters);
+ GenerateOptionalOverloads(returnInfo, methodName, parameters);
+ GenerateCapabilityMethodImplementation(capability, returnInfo, methodName, targetParamName, parameters, isPublic: true);
+ }
+ }
+
+ private void GenerateUnionOverloads(JavaCapabilityReturnInfo returnInfo, string methodName, List parameters)
+ {
+ var unionParameters = parameters.Where(p => IsUnionType(p.Type)).ToList();
+ if (unionParameters.Count != 1)
+ {
+ return;
+ }
+
+ var unionParameter = unionParameters[0];
+ var unionTypes = unionParameter.Type?.UnionTypes;
+ if (unionTypes is null || unionTypes.Count == 0)
+ {
+ return;
+ }
+
+ var unionParamName = ToCamelCase(unionParameter.Name);
+
+ foreach (var unionType in unionTypes
+ .Select(type => new { Type = type, JavaType = MapInputTypeToJava(type, unionParameter.IsOptional || unionParameter.IsNullable) })
+ .DistinctBy(x => x.JavaType, StringComparer.Ordinal)
+ .Select(x => x.Type))
+ {
+ var overloadParameters = new StringBuilder();
+ foreach (var parameter in parameters)
+ {
+ if (overloadParameters.Length > 0)
+ {
+ overloadParameters.Append(", ");
+ }
+
+ var parameterType = ReferenceEquals(parameter, unionParameter)
+ ? MapInputTypeToJava(unionType, unionParameter.IsOptional || unionParameter.IsNullable)
+ : MapParameterToJava(parameter);
+ overloadParameters.Append(CultureInfo.InvariantCulture, $"{parameterType} {ToCamelCase(parameter.Name)}");
+ }
+
+ WriteLine($" public {returnInfo.ReturnType} {methodName}({overloadParameters}) {{");
+ var callArguments = string.Join(", ", parameters.Select(parameter =>
+ ReferenceEquals(parameter, unionParameter)
+ ? $"AspireUnion.of({unionParamName})"
+ : ToCamelCase(parameter.Name)));
+ if (returnInfo.HasReturn)
+ {
+ WriteLine($" return {methodName}({callArguments});");
+ }
+ else
+ {
+ WriteLine($" {methodName}({callArguments});");
+ }
+ WriteLine(" }");
+ WriteLine();
+ }
+ }
+
+ private void GenerateOptionalOverloads(JavaCapabilityReturnInfo returnInfo, string methodName, List parameters)
+ {
+ var trailingOptionalCount = parameters.AsEnumerable().Reverse().TakeWhile(IsOmittableParameter).Count();
+ if (trailingOptionalCount == 0)
+ {
+ return;
+ }
+
+ for (var omitCount = trailingOptionalCount; omitCount >= 1; omitCount--)
+ {
+ var visibleParameters = parameters.Take(parameters.Count - omitCount).ToList();
+ var parameterList = string.Join(", ", visibleParameters.Select(parameter => $"{MapParameterToJava(parameter)} {ToCamelCase(parameter.Name)}"));
+ WriteLine($" public {returnInfo.ReturnType} {methodName}({parameterList}) {{");
+
+ var callArguments = new List(parameters.Count);
+ foreach (var parameter in parameters)
+ {
+ if (visibleParameters.Contains(parameter))
+ {
+ callArguments.Add(ToCamelCase(parameter.Name));
+ }
+ else
+ {
+ callArguments.Add("null");
+ }
+ }
+
+ if (returnInfo.HasReturn)
+ {
+ WriteLine($" return {methodName}({string.Join(", ", callArguments)});");
+ }
+ else
+ {
+ WriteLine($" {methodName}({string.Join(", ", callArguments)});");
+ }
+ WriteLine(" }");
+ WriteLine();
+
+ GenerateResourceBuilderOverloads(
+ returnInfo.ReturnType,
+ methodName,
+ CreateMethodParameters(visibleParameters),
+ returnInfo.HasReturn);
+ }
+ }
+
+ private void GenerateUnionOverloadsWithOptions(
+ JavaCapabilityReturnInfo returnInfo,
+ string methodName,
+ List requiredParameters,
+ string optionsClassName)
+ {
+ var unionParameters = requiredParameters.Where(p => IsUnionType(p.Type)).ToList();
+ if (unionParameters.Count != 1)
+ {
+ return;
+ }
+
+ var unionParameter = unionParameters[0];
+ var unionTypes = unionParameter.Type?.UnionTypes;
+ if (unionTypes is null || unionTypes.Count == 0)
+ {
+ return;
+ }
+
+ var unionParamName = ToCamelCase(unionParameter.Name);
+
+ foreach (var unionType in unionTypes
+ .Select(type => new { Type = type, JavaType = MapInputTypeToJava(type, unionParameter.IsOptional || unionParameter.IsNullable) })
+ .DistinctBy(x => x.JavaType, StringComparer.Ordinal)
+ .Select(x => x.Type))
+ {
+ var overloadParameters = new StringBuilder();
+ foreach (var parameter in requiredParameters)
+ {
+ if (overloadParameters.Length > 0)
+ {
+ overloadParameters.Append(", ");
+ }
+
+ var parameterType = ReferenceEquals(parameter, unionParameter)
+ ? MapInputTypeToJava(unionType, unionParameter.IsOptional || unionParameter.IsNullable)
+ : MapParameterToJava(parameter);
+ overloadParameters.Append(CultureInfo.InvariantCulture, $"{parameterType} {ToCamelCase(parameter.Name)}");
+ }
+
+ if (overloadParameters.Length > 0)
+ {
+ overloadParameters.Append(", ");
+ }
+ overloadParameters.Append(CultureInfo.InvariantCulture, $"{optionsClassName} options");
+
+ WriteLine($" public {returnInfo.ReturnType} {methodName}({overloadParameters}) {{");
+ var callArguments = string.Join(", ", requiredParameters.Select(parameter =>
+ ReferenceEquals(parameter, unionParameter)
+ ? $"AspireUnion.of({unionParamName})"
+ : ToCamelCase(parameter.Name)));
+ if (returnInfo.HasReturn)
+ {
+ WriteLine($" return {methodName}({callArguments}, options);");
+ }
+ else
+ {
+ WriteLine($" {methodName}({callArguments}, options);");
+ }
+ WriteLine(" }");
+ WriteLine();
+
+ WriteLine($" public {returnInfo.ReturnType} {methodName}({string.Join(", ", requiredParameters.Select(parameter => ReferenceEquals(parameter, unionParameter) ? $"{MapInputTypeToJava(unionType, unionParameter.IsOptional || unionParameter.IsNullable)} {ToCamelCase(parameter.Name)}" : $"{MapParameterToJava(parameter)} {ToCamelCase(parameter.Name)}"))}) {{");
+ if (returnInfo.HasReturn)
+ {
+ WriteLine($" return {methodName}({callArguments});");
+ }
+ else
+ {
+ WriteLine($" {methodName}({callArguments});");
+ }
+ WriteLine(" }");
+ WriteLine();
+ }
+ }
+
+ private void GenerateOptionsOverloads(
+ AtsCapabilityInfo capability,
+ JavaCapabilityReturnInfo returnInfo,
+ string methodName,
+ string implementationMethodName,
+ List requiredParameters,
+ List optionalParameters,
+ string optionsClassName)
+ {
+ var requiredParameterList = string.Join(", ", requiredParameters.Select(parameter => $"{MapParameterToJava(parameter)} {ToCamelCase(parameter.Name)}"));
+ var publicParameterList = string.IsNullOrEmpty(requiredParameterList)
+ ? $"{optionsClassName} options"
+ : $"{requiredParameterList}, {optionsClassName} options";
+
+ if (!string.IsNullOrEmpty(capability.Description))
+ {
+ WriteLine($" /** {capability.Description} */");
+ }
+
+ WriteLine($" public {returnInfo.ReturnType} {methodName}({publicParameterList}) {{");
+ foreach (var parameter in optionalParameters)
+ {
+ var paramName = ToCamelCase(parameter.Name);
+ WriteLine($" var {paramName} = options == null ? null : options.{GetOptionGetterName(parameter)}();");
+ }
+
+ var implementationArguments = requiredParameters
+ .Select(parameter => ToCamelCase(parameter.Name))
+ .Concat(optionalParameters.Select(parameter => ToCamelCase(parameter.Name)))
+ .ToList();
+
+ if (returnInfo.HasReturn)
+ {
+ WriteLine($" return {implementationMethodName}({string.Join(", ", implementationArguments)});");
+ }
+ else
+ {
+ WriteLine($" {implementationMethodName}({string.Join(", ", implementationArguments)});");
+ }
+ WriteLine(" }");
+ WriteLine();
+
+ var optionsParameters = CreateMethodParameters(requiredParameters);
+ optionsParameters.Add(new JavaMethodParameter(optionsClassName, "options"));
+ GenerateResourceBuilderOverloads(
+ returnInfo.ReturnType,
+ methodName,
+ optionsParameters,
+ returnInfo.HasReturn);
+
+ WriteLine($" public {returnInfo.ReturnType} {methodName}({requiredParameterList}) {{");
+ if (returnInfo.HasReturn)
+ {
+ WriteLine($" return {methodName}({AppendArgumentList(requiredParameters.Select(parameter => ToCamelCase(parameter.Name)), "null")});");
+ }
+ else
+ {
+ WriteLine($" {methodName}({AppendArgumentList(requiredParameters.Select(parameter => ToCamelCase(parameter.Name)), "null")});");
+ }
+ WriteLine(" }");
+ WriteLine();
+
+ GenerateResourceBuilderOverloads(
+ returnInfo.ReturnType,
+ methodName,
+ CreateMethodParameters(requiredParameters),
+ returnInfo.HasReturn);
+ }
- // Build parameter list
+ private void GenerateCapabilityMethodImplementation(AtsCapabilityInfo capability, JavaCapabilityReturnInfo returnInfo, string methodName, string targetParamName, List parameters, bool isPublic)
+ {
var paramList = new StringBuilder();
foreach (var parameter in parameters)
{
@@ -282,22 +1204,16 @@ private void GenerateCapabilityMethod(AtsCapabilityInfo capability)
{
paramList.Append(", ");
}
- var paramName = ToCamelCase(parameter.Name);
- var paramType = parameter.IsCallback
- ? "Function