diff --git a/.gitignore b/.gitignore
index 207bc8a7..0837e3f7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,7 @@
# Qodo
/.qodo
+
+# Gradle
+/.gradle
+/build
diff --git a/.gradle-docs/MIGRATION.md b/.gradle-docs/MIGRATION.md
new file mode 100644
index 00000000..50d99164
--- /dev/null
+++ b/.gradle-docs/MIGRATION.md
@@ -0,0 +1,365 @@
+# Migration Guide: Ant to Gradle
+
+This document provides guidance for migrating from the legacy Ant build system to the new Gradle build system.
+
+## Overview
+
+The Bearsampp Module Node.js project has been fully migrated from Apache Ant to Gradle. The Gradle build system provides better performance, modern tooling, and improved maintainability.
+
+## What Changed
+
+### Removed Files
+
+The following files have been **removed** from the repository:
+
+| File | Status | Notes |
+|-------------------------------|-----------|------------------------------------------|
+| `build.xml` | ✗ Removed | Replaced by `build.gradle` |
+| `module-nodejs.RELEASE.launch`| ✗ Removed | Eclipse Ant launcher, no longer needed |
+
+**Note**: These legacy Ant build files have been completely removed. The project now uses pure Gradle for all build operations.
+
+### New Files
+
+| File | Purpose |
+|-------------------------------|----------------------------------------------|
+| `build.gradle` | Main Gradle build script |
+| `settings.gradle` | Gradle project settings |
+| `gradle.properties` | Gradle configuration properties |
+| `.gradle-docs/` | Gradle build documentation directory |
+| `.gradle-docs/README.md` | Main build documentation |
+| `.gradle-docs/PACKAGING.md` | Packaging and archive structure guide |
+| `.gradle-docs/MIGRATION.md` | This migration guide |
+
+## Command Mapping
+
+### Basic Commands
+
+| Ant Command | Gradle Command | Notes |
+|--------------------------------------|---------------------------------------------|------------------------------------------|
+| `ant release` | `gradle release` | Interactive mode (prompts for version) |
+| `ant release -Dinput.bundle=24.6.0` | `gradle release -PbundleVersion=24.6.0` | Non-interactive mode |
+| `ant clean` | `gradle clean` | Clean build artifacts |
+
+### New Commands
+
+Gradle provides additional commands not available in Ant:
+
+| Command | Description |
+|-------------------------------|----------------------------------------------|
+| `gradle info` | Display build configuration information |
+| `gradle tasks` | List all available tasks |
+| `gradle verify` | Verify build environment and dependencies |
+| `gradle listVersions` | List available versions in bin/ |
+| `gradle listReleases` | List releases from modules-untouched |
+| `gradle releaseAll` | Build all versions in bin/ |
+| `gradle validateProperties` | Validate build.properties configuration |
+| `gradle checkModulesUntouched`| Check modules-untouched integration |
+
+## Key Differences
+
+### 1. Build File Format
+
+**Ant (build.xml):**
+```xml
+
+
+
+
+
+
+
+```
+
+**Gradle (build.gradle):**
+```groovy
+tasks.register('release') {
+ group = 'build'
+ description = 'Build release package'
+
+ doLast {
+ copy {
+ from nodeExtractPath
+ into bundlePrepPath
+ }
+ }
+}
+```
+
+### 2. Property Handling
+
+**Ant:**
+```xml
+
+
+```
+
+**Gradle:**
+```groovy
+def buildProps = new Properties()
+file('build.properties').withInputStream { buildProps.load(it) }
+
+ext {
+ bundleName = buildProps.getProperty('bundle.name', 'nodejs')
+}
+```
+
+### 3. Task Execution
+
+**Ant:**
+- Tasks defined as `` elements
+- Dependencies specified with `depends` attribute
+- Sequential execution by default
+
+**Gradle:**
+- Tasks defined with `tasks.register()`
+- Dependencies specified with `dependsOn`
+- Parallel execution possible
+- Incremental builds supported
+
+### 4. Dependency Management
+
+**Ant:**
+- Manual download and extraction
+- No built-in dependency management
+- Custom scripts required
+
+**Gradle:**
+- Built-in dependency management
+- Automatic download and caching
+- Repository support (Maven, etc.)
+
+### 5. IDE Integration
+
+**Ant:**
+- Limited IDE support
+- Manual configuration required
+- Eclipse `.launch` files needed
+
+**Gradle:**
+- Excellent IDE support (IntelliJ IDEA, Eclipse, VS Code)
+- Automatic project import
+- No manual configuration needed
+
+## Migration Steps
+
+If you're migrating from Ant to Gradle in your own project:
+
+### Step 1: Install Gradle
+
+```bash
+# Windows (Chocolatey)
+choco install gradle
+
+# macOS (Homebrew)
+brew install gradle
+
+# Linux (SDKMAN!)
+sdk install gradle
+
+# Verify installation
+gradle --version
+```
+
+### Step 2: Create build.gradle
+
+Create a new `build.gradle` file based on the current implementation. See [build.gradle](../build.gradle) for reference.
+
+### Step 3: Create settings.gradle
+
+```groovy
+rootProject.name = 'module-nodejs'
+```
+
+### Step 4: Test the Build
+
+```bash
+# Verify environment
+gradle verify
+
+# List available tasks
+gradle tasks
+
+# Test a build
+gradle release -PbundleVersion=24.6.0
+```
+
+### Step 5: Update Documentation
+
+Update your README.md and other documentation to reference Gradle commands instead of Ant.
+
+### Step 6: Deprecate Ant Files
+
+Mark `build.xml` and related files as deprecated. You can keep them for reference but note they're no longer used.
+
+## Troubleshooting Migration Issues
+
+### Issue: "Cannot find dev project"
+
+**Ant Behavior:**
+```xml
+
+
+```
+
+**Gradle Behavior:**
+```groovy
+ext {
+ devPath = file("${rootDir}/dev").absolutePath
+}
+
+if (!file(ext.devPath).exists()) {
+ throw new GradleException("Dev path not found: ${ext.devPath}")
+}
+```
+
+**Solution:**
+Ensure the `dev` project exists in the parent directory, or modify the build script if your structure is different.
+
+### Issue: "Property not found"
+
+**Ant Behavior:**
+Properties loaded from `build.properties` are automatically available.
+
+**Gradle Behavior:**
+Properties must be explicitly loaded:
+
+```groovy
+def buildProps = new Properties()
+file('build.properties').withInputStream { buildProps.load(it) }
+```
+
+**Solution:**
+Check that `build.properties` exists and properties are loaded correctly.
+
+### Issue: "Task not found"
+
+**Ant Behavior:**
+```bash
+ant release
+```
+
+**Gradle Behavior:**
+```bash
+gradle release
+```
+
+**Solution:**
+Use `gradle tasks` to list all available tasks and verify the task name.
+
+## Benefits of Gradle
+
+### 1. Performance
+
+- **Incremental builds**: Only rebuild what changed
+- **Build cache**: Reuse outputs from previous builds
+- **Parallel execution**: Run tasks in parallel when possible
+
+### 2. Maintainability
+
+- **Groovy DSL**: More readable and maintainable than XML
+- **Modular**: Easy to split build logic into multiple files
+- **Reusable**: Share build logic across projects
+
+### 3. Tooling
+
+- **IDE integration**: Excellent support in IntelliJ IDEA, Eclipse, VS Code
+- **Build scans**: Detailed build performance analysis
+- **Dependency insight**: Understand dependency trees
+
+### 4. Ecosystem
+
+- **Plugins**: Thousands of plugins available
+- **Community**: Large and active community
+- **Documentation**: Comprehensive official documentation
+
+## Best Practices
+
+### 1. Use Gradle Wrapper (Optional)
+
+While this project doesn't include the Gradle Wrapper, you can add it:
+
+```bash
+gradle wrapper --gradle-version 8.5
+```
+
+This creates:
+- `gradlew` (Unix)
+- `gradlew.bat` (Windows)
+- `gradle/wrapper/` directory
+
+Benefits:
+- Ensures consistent Gradle version across team
+- No need to install Gradle separately
+
+### 2. Organize Build Logic
+
+Keep build logic organized:
+- Main build logic in `build.gradle`
+- Configuration in `build.properties`
+- Documentation in `.gradle-docs/`
+
+### 3. Use Task Groups
+
+Organize tasks into logical groups:
+
+```groovy
+tasks.register('myTask') {
+ group = 'build' // or 'verification', 'help', etc.
+ description = 'My task description'
+}
+```
+
+### 4. Leverage Gradle Features
+
+- Use `ext` for shared properties
+- Use `doFirst` and `doLast` for task actions
+- Use `dependsOn` for task dependencies
+- Use `mustRunAfter` for task ordering
+
+## Additional Resources
+
+### Official Documentation
+
+- [Gradle User Manual](https://docs.gradle.org/current/userguide/userguide.html)
+- [Gradle DSL Reference](https://docs.gradle.org/current/dsl/)
+- [Gradle Build Language Reference](https://docs.gradle.org/current/userguide/writing_build_scripts.html)
+
+### Tutorials
+
+- [Getting Started with Gradle](https://docs.gradle.org/current/userguide/getting_started.html)
+- [Migrating from Ant to Gradle](https://docs.gradle.org/current/userguide/migrating_from_ant.html)
+- [Gradle Best Practices](https://docs.gradle.org/current/userguide/authoring_maintainable_build_scripts.html)
+
+### Bearsampp Resources
+
+- [Bearsampp Project](https://github.com/bearsampp/bearsampp)
+- [Module PHP (Gradle)](https://github.com/Bearsampp/module-php/tree/gradle-convert)
+- [modules-untouched Repository](https://github.com/Bearsampp/modules-untouched)
+
+## Support
+
+If you encounter issues during migration:
+
+1. Check this migration guide
+2. Review the [main documentation](README.md)
+3. Check [troubleshooting guide](README.md#troubleshooting)
+4. Open an issue on [GitHub](https://github.com/bearsampp/module-nodejs/issues)
+
+---
+
+**Last Updated**: 2025-01-31
+**Version**: 2025.8.21
+**Build System**: Pure Gradle
+
+## Summary
+
+The migration from Ant to Gradle provides:
+
+✅ **Better Performance** - Incremental builds and caching
+✅ **Modern Tooling** - Excellent IDE integration
+✅ **Easier Maintenance** - Readable Groovy DSL
+✅ **More Features** - Rich plugin ecosystem
+✅ **Better Documentation** - Comprehensive guides
+
+The Gradle build system is now the primary and recommended way to build Bearsampp Node.js modules.
diff --git a/.gradle-docs/PACKAGING.md b/.gradle-docs/PACKAGING.md
new file mode 100644
index 00000000..ebcc99b1
--- /dev/null
+++ b/.gradle-docs/PACKAGING.md
@@ -0,0 +1,384 @@
+# Packaging and Archive Layout
+
+This document describes how the Bearsampp Node.js module packages releases and the structure of the resulting archives.
+
+## Overview
+
+The Gradle build produces archives that include the top-level version folder, matching the behavior used across all Bearsampp modules (PHP, MySQL, etc.). This ensures consistency and proper extraction behavior.
+
+## Archive Structure
+
+### Example for Node.js 24.6.0
+
+```
+bearsampp-nodejs-24.6.0-2025.8.21.7z
+└── nodejs24.6.0/ ← Version folder at root
+ ├── node.exe ← Node.js executable
+ ├── npm ← NPM script
+ ├── npm.cmd ← NPM Windows command
+ ├── npx ← NPX script
+ ├── npx.cmd ← NPX Windows command
+ ├── node_modules/ ← Node modules directory
+ │ ├── npm/
+ │ └── ...
+ ├── LICENSE
+ ├── README.md
+ └── ...
+```
+
+### Key Points
+
+1. **Top-level folder**: The archive contains `nodejs{version}/` as the root directory
+2. **Version naming**: Format is `nodejs` + version number (e.g., `nodejs24.6.0`)
+3. **Complete structure**: All Node.js files and directories are inside the version folder
+4. **Consistency**: Matches the pattern used by other Bearsampp modules
+
+## Implementation Details
+
+### Compression Process
+
+The build system uses two different approaches depending on the archive format:
+
+#### 7-Zip Compression
+
+```groovy
+// Run from parent directory and include folder name explicitly
+def parentDir = prepPath.parentFile
+def folderName = prepPath.name // e.g., "nodejs24.6.0"
+
+ProcessBuilder([
+ sevenZipExe,
+ 'a', // Add to archive
+ '-t7z', // 7z format
+ archiveFile.absolutePath, // Output archive
+ folderName // Folder to include
+])
+.directory(parentDir) // Run from parent directory
+.start()
+```
+
+**Why this works:**
+- Running from the parent directory ensures the folder name is included in the archive
+- Passing the folder name (not path) as the argument includes it at the root level
+- Result: Archive contains `nodejs24.6.0/` at the root
+
+#### ZIP Compression
+
+```groovy
+task("zipArchive_${bundleVersion}", type: Zip) {
+ from(prepPath.parentFile) {
+ include "${prepPath.name}/**"
+ }
+ destinationDirectory = archiveFile.parentFile
+ archiveFileName = archiveFile.name
+}
+```
+
+**Why this works:**
+- `from(prepPath.parentFile)` starts from the parent directory
+- `include "${prepPath.name}/**"` includes the folder and all its contents
+- Result: Archive contains `nodejs24.6.0/` at the root
+
+## Archive Naming Convention
+
+Archives follow this naming pattern:
+
+```
+bearsampp-{bundle.name}-{version}-{bundle.release}.{format}
+```
+
+**Example:**
+```
+bearsampp-nodejs-24.6.0-2025.8.21.7z
+```
+
+**Components:**
+- `bearsampp` - Project prefix
+- `nodejs` - Bundle name (from build.properties)
+- `24.6.0` - Node.js version
+- `2025.8.21` - Bundle release date (from build.properties)
+- `7z` - Archive format (from build.properties)
+
+## Hash Files
+
+Each archive is accompanied by multiple hash files for integrity verification:
+
+```
+bearsampp-nodejs-24.6.0-2025.8.21.7z
+bearsampp-nodejs-24.6.0-2025.8.21.7z.md5 ← MD5 checksum
+bearsampp-nodejs-24.6.0-2025.8.21.7z.sha1 ← SHA-1 checksum
+bearsampp-nodejs-24.6.0-2025.8.21.7z.sha256 ← SHA-256 checksum
+bearsampp-nodejs-24.6.0-2025.8.21.7z.sha512 ← SHA-512 checksum
+```
+
+### Hash File Format
+
+Each hash file contains:
+```
+{hash_value} {filename}
+```
+
+**Example (MD5):**
+```
+a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6 bearsampp-nodejs-24.6.0-2025.8.21.7z
+```
+
+### Verification
+
+To verify archive integrity:
+
+```bash
+# Windows (PowerShell)
+# MD5
+Get-FileHash bearsampp-nodejs-24.6.0-2025.8.21.7z -Algorithm MD5
+
+# SHA256
+Get-FileHash bearsampp-nodejs-24.6.0-2025.8.21.7z -Algorithm SHA256
+
+# Linux/macOS
+# MD5
+md5sum bearsampp-nodejs-24.6.0-2025.8.21.7z
+
+# SHA256
+sha256sum bearsampp-nodejs-24.6.0-2025.8.21.7z
+```
+
+## Output Locations
+
+### Default Structure
+
+```
+bearsampp-build/
+├── tmp/ # Temporary build files
+│ ├── bundles_prep/bins/nodejs/ # Prepared bundles
+│ │ └── nodejs24.6.0/ # Prepared version
+│ ├── bundles_build/bins/nodejs/ # Build staging
+│ │ └── nodejs24.6.0/ # Built version (non-archived)
+│ ├── downloads/nodejs/ # Downloaded archives
+│ │ └── nodejs-24.6.0-win-x64.7z
+│ └── extract/nodejs/ # Extracted archives
+│ └── 24.6.0/
+│ └── node-v24.6.0-win-x64/
+└── bins/nodejs/ # Final packaged archives
+ └── 2025.8.21/ # Release version
+ ├── bearsampp-nodejs-24.6.0-2025.8.21.7z
+ ├── bearsampp-nodejs-24.6.0-2025.8.21.7z.md5
+ ├── bearsampp-nodejs-24.6.0-2025.8.21.7z.sha1
+ ├── bearsampp-nodejs-24.6.0-2025.8.21.7z.sha256
+ └── bearsampp-nodejs-24.6.0-2025.8.21.7z.sha512
+```
+
+### Customizing Output Location
+
+You can override the default output location in two ways:
+
+#### 1. build.properties
+
+```properties
+build.path = C:/Custom-Build-Path
+```
+
+#### 2. Environment Variable
+
+```bash
+# Windows (PowerShell)
+$env:BEARSAMPP_BUILD_PATH = "C:\Custom-Build-Path"
+
+# Windows (Command Prompt)
+set BEARSAMPP_BUILD_PATH=C:\Custom-Build-Path
+
+# Linux/macOS
+export BEARSAMPP_BUILD_PATH=/path/to/custom/build
+```
+
+**Priority:**
+1. `build.properties` `build.path` property (highest)
+2. `BEARSAMPP_BUILD_PATH` environment variable
+3. Default: `{repo-parent}/bearsampp-build` (lowest)
+
+## Verification Guide
+
+### How to Verify Archive Structure
+
+After building a release, verify the archive structure:
+
+#### Method 1: Using 7-Zip Command Line
+
+```bash
+# List archive contents
+7z l bearsampp-nodejs-24.6.0-2025.8.21.7z
+
+# Filter for the version folder
+7z l bearsampp-nodejs-24.6.0-2025.8.21.7z | findstr nodejs24.6.0
+
+# Expected output should show entries like:
+# nodejs24.6.0\node.exe
+# nodejs24.6.0\npm
+# nodejs24.6.0\node_modules\...
+```
+
+#### Method 2: Using PowerShell (ZIP)
+
+```powershell
+# Extract to temporary directory
+Expand-Archive -Path bearsampp-nodejs-24.6.0-2025.8.21.zip -DestinationPath .\_inspect
+
+# List contents
+Get-ChildItem .\_inspect
+
+# Expected output:
+# Directory: E:\...\\_inspect
+# Mode LastWriteTime Length Name
+# ---- ------------- ------ ----
+# d----- 1/31/2025 ... nodejs24.6.0
+
+# List files inside version folder
+Get-ChildItem .\_inspect\nodejs24.6.0
+
+# Clean up
+Remove-Item .\_inspect -Recurse -Force
+```
+
+#### Method 3: Using 7-Zip GUI
+
+1. Open the archive in 7-Zip
+2. The first entry should be a folder named `nodejs{version}/`
+3. All files should be inside this folder
+
+### Quick Verification Script
+
+```bash
+# Build a release
+gradle release -PbundleVersion=24.6.0
+
+# Verify the archive
+7z l bearsampp-build/bins/nodejs/2025.8.21/bearsampp-nodejs-24.6.0-2025.8.21.7z | findstr /C:"nodejs24.6.0"
+
+# If successful, you should see multiple lines starting with "nodejs24.6.0\"
+```
+
+## Build Process Flow
+
+### Step-by-Step Packaging
+
+1. **Preparation Phase**
+ ```
+ tmp/bundles_prep/bins/nodejs/nodejs24.6.0/
+ ```
+ - Copy Node.js files from source (downloaded or local)
+ - Overlay custom files from bin/nodejs24.6.0/
+ - Result: Complete Node.js installation ready for packaging
+
+2. **Build Phase**
+ ```
+ tmp/bundles_build/bins/nodejs/nodejs24.6.0/
+ ```
+ - Copy prepared files to build directory
+ - This is the non-archived version
+ - Can be used for testing before packaging
+
+3. **Packaging Phase**
+ ```
+ bins/nodejs/2025.8.21/bearsampp-nodejs-24.6.0-2025.8.21.7z
+ ```
+ - Compress from parent directory
+ - Include version folder at root
+ - Generate hash files
+
+## Troubleshooting
+
+### Issue: Archive doesn't contain version folder
+
+**Symptom:**
+```
+Archive contains files directly at root instead of nodejs24.6.0/ folder
+```
+
+**Solution:**
+This should not happen with the current build system. If it does:
+1. Check that you're using the latest build.gradle
+2. Verify the compression command is running from the parent directory
+3. Check the Gradle output for any errors during packaging
+
+### Issue: Wrong folder name in archive
+
+**Symptom:**
+```
+Archive contains "node-v24.6.0-win-x64" instead of "nodejs24.6.0"
+```
+
+**Solution:**
+This indicates the preparation phase didn't complete correctly:
+1. Check that the prep directory was created: `tmp/bundles_prep/bins/nodejs/nodejs24.6.0/`
+2. Verify files were copied to the prep directory
+3. Run with `--info` flag to see detailed output: `gradle release -PbundleVersion=24.6.0 --info`
+
+### Issue: Hash files not generated
+
+**Symptom:**
+```
+Archive created but no .md5, .sha1, etc. files
+```
+
+**Solution:**
+1. Check Gradle output for hash generation errors
+2. Verify Java has permissions to write to the output directory
+3. Try running with `--stacktrace` to see detailed error: `gradle release -PbundleVersion=24.6.0 --stacktrace`
+
+## Best Practices
+
+### 1. Always Verify After Build
+
+After building a release, always verify the archive structure:
+
+```bash
+gradle release -PbundleVersion=24.6.0
+7z l bearsampp-build/bins/nodejs/2025.8.21/bearsampp-nodejs-24.6.0-2025.8.21.7z | findstr nodejs24.6.0
+```
+
+### 2. Test Extraction
+
+Before distributing, test that the archive extracts correctly:
+
+```bash
+# Create test directory
+mkdir test-extract
+cd test-extract
+
+# Extract archive
+7z x ../bearsampp-build/bins/nodejs/2025.8.21/bearsampp-nodejs-24.6.0-2025.8.21.7z
+
+# Verify structure
+dir nodejs24.6.0
+
+# Test Node.js
+nodejs24.6.0\node.exe --version
+
+# Clean up
+cd ..
+rmdir /s /q test-extract
+```
+
+### 3. Verify Hash Files
+
+Always verify hash files are generated and correct:
+
+```bash
+# Check hash file exists
+dir bearsampp-build\bins\nodejs\2025.8.21\*.md5
+
+# Verify hash matches
+Get-FileHash bearsampp-build/bins/nodejs/2025.8.21/bearsampp-nodejs-24.6.0-2025.8.21.7z -Algorithm MD5
+type bearsampp-build\bins\nodejs\2025.8.21\bearsampp-nodejs-24.6.0-2025.8.21.7z.md5
+```
+
+## Related Documentation
+
+- [Main Build Documentation](README.md) - Complete build system documentation
+- [build.gradle](../build.gradle) - Build script implementation
+- [build.properties](../build.properties) - Build configuration
+
+---
+
+**Last Updated**: 2025-01-31
+**Version**: 2025.8.21
diff --git a/.gradle-docs/README.md b/.gradle-docs/README.md
new file mode 100644
index 00000000..9a8e97dc
--- /dev/null
+++ b/.gradle-docs/README.md
@@ -0,0 +1,498 @@
+# Bearsampp Module Node.js - Gradle Build Documentation
+
+## Table of Contents
+
+- [Overview](#overview)
+- [Quick Start](#quick-start)
+- [Installation](#installation)
+- [Build Tasks](#build-tasks)
+- [Configuration](#configuration)
+- [Architecture](#architecture)
+- [Troubleshooting](#troubleshooting)
+- [Additional Documentation](#additional-documentation)
+
+---
+
+## Overview
+
+The Bearsampp Module Node.js project has been converted to a **pure Gradle build system**, replacing the legacy Ant build configuration. This provides:
+
+- **Modern Build System** - Native Gradle tasks and conventions
+- **Better Performance** - Incremental builds and caching
+- **Simplified Maintenance** - Pure Groovy/Gradle DSL
+- **Enhanced Tooling** - IDE integration and dependency management
+- **Cross-Platform Support** - Works on Windows, Linux, and macOS
+
+### Project Information
+
+| Property | Value |
+|-------------------|------------------------------------------|
+| **Project Name** | module-nodejs |
+| **Group** | com.bearsampp.modules |
+| **Type** | Node.js Module Builder |
+| **Build Tool** | Gradle 8.x+ |
+| **Language** | Groovy (Gradle DSL) |
+
+---
+
+## Quick Start
+
+### Prerequisites
+
+| Requirement | Version | Purpose |
+|-------------------|---------------|------------------------------------------|
+| **Java** | 8+ | Required for Gradle execution |
+| **Gradle** | 8.0+ | Build automation tool |
+| **7-Zip** | Latest | Archive extraction and creation |
+
+### Basic Commands
+
+```bash
+# Display build information
+gradle info
+
+# List all available tasks
+gradle tasks
+
+# Verify build environment
+gradle verify
+
+# Build a release (interactive)
+gradle release
+
+# Build a specific version (non-interactive)
+gradle release -PbundleVersion=24.6.0
+
+# Clean build artifacts
+gradle clean
+```
+
+---
+
+## Installation
+
+### 1. Clone the Repository
+
+```bash
+git clone https://github.com/bearsampp/module-nodejs.git
+cd module-nodejs
+```
+
+### 2. Verify Environment
+
+```bash
+gradle verify
+```
+
+This will check:
+- Java version (8+)
+- Required files (build.properties)
+- Directory structure (bin/)
+- Build dependencies
+
+### 3. List Available Versions
+
+```bash
+gradle listVersions
+```
+
+### 4. Build Your First Release
+
+```bash
+# Interactive mode (prompts for version)
+gradle release
+
+# Or specify version directly
+gradle release -PbundleVersion=24.6.0
+```
+
+---
+
+## Build Tasks
+
+### Core Build Tasks
+
+| Task | Description | Example |
+|-----------------------|--------------------------------------------------|------------------------------------------|
+| `release` | Build and package release (interactive/non-interactive) | `gradle release -PbundleVersion=24.6.0` |
+| `releaseAll` | Build all available versions in bin/ | `gradle releaseAll` |
+| `clean` | Clean build artifacts and temporary files | `gradle clean` |
+
+### Verification Tasks
+
+| Task | Description | Example |
+|---------------------------|----------------------------------------------|----------------------------------------------|
+| `verify` | Verify build environment and dependencies | `gradle verify` |
+| `validateProperties` | Validate build.properties configuration | `gradle validateProperties` |
+
+### Information Tasks
+
+| Task | Description | Example |
+|---------------------|--------------------------------------------------|----------------------------|
+| `info` | Display build configuration information | `gradle info` |
+| `listVersions` | List available bundle versions in bin/ | `gradle listVersions` |
+| `listReleases` | List all available releases from modules-untouched | `gradle listReleases` |
+| `checkModulesUntouched` | Check modules-untouched integration | `gradle checkModulesUntouched` |
+
+### Task Groups
+
+| Group | Purpose |
+|------------------|--------------------------------------------------|
+| **build** | Build and package tasks |
+| **verification** | Verification and validation tasks |
+| **help** | Help and information tasks |
+
+---
+
+## Configuration
+
+### build.properties
+
+The main configuration file for the build:
+
+```properties
+bundle.name = nodejs
+bundle.release = 2025.8.21
+bundle.type = bins
+bundle.format = 7z
+```
+
+| Property | Description | Example Value |
+|-------------------|--------------------------------------|----------------|
+| `bundle.name` | Name of the bundle | `nodejs` |
+| `bundle.release` | Release version | `2025.8.21` |
+| `bundle.type` | Type of bundle | `bins` |
+| `bundle.format` | Archive format | `7z` |
+
+### gradle.properties
+
+Gradle-specific configuration:
+
+```properties
+# Gradle daemon configuration
+org.gradle.daemon=true
+org.gradle.parallel=true
+org.gradle.caching=true
+
+# JVM settings
+org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m
+```
+
+### Directory Structure
+
+```
+module-nodejs/
+├── .gradle-docs/ # Gradle documentation
+│ ├── README.md # Main documentation
+│ └── PACKAGING.md # Packaging guide
+├── bin/ # Node.js version bundles
+│ ├── nodejs24.6.0/
+│ ├── nodejs22.11.0/
+│ ├── archived/ # Archived versions
+│ └── ...
+├── img/ # Images and assets
+│ └── Bearsampp-logo.svg
+├── build.gradle # Main Gradle build script
+├── settings.gradle # Gradle settings
+├── build.properties # Build configuration
+├── releases.properties # Available Node.js releases
+└── README.md # Main project README
+
+bearsampp-build/ # External build directory (outside repo)
+├── tmp/ # Temporary build files
+│ ├── bundles_prep/bins/nodejs/ # Prepared bundles
+│ ├── bundles_build/bins/nodejs/ # Build staging
+│ ├── downloads/nodejs/ # Downloaded dependencies
+│ └── extract/nodejs/ # Extracted archives
+└── bins/nodejs/ # Final packaged archives
+ └── 2025.8.21/ # Release version
+ ├── bearsampp-nodejs-24.6.0-2025.8.21.7z
+ ├── bearsampp-nodejs-24.6.0-2025.8.21.7z.md5
+ └── ...
+```
+
+---
+
+## Architecture
+
+### Build Process Flow
+
+```
+1. User runs: gradle release -PbundleVersion=24.6.0
+ ↓
+2. Validate environment and version
+ ↓
+3. Check if Node.js binaries exist in bin/nodejs24.6.0/
+ ↓
+4. If not found, download from modules-untouched
+ - Check nodejs.properties for version URL
+ - Fallback to standard URL format
+ - Extract to tmp/extract/
+ ↓
+5. Create preparation directory (tmp/bundles_prep/)
+ ↓
+6. Copy Node.js files to prep directory
+ ↓
+7. Overlay any custom files from bin/nodejs24.6.0/
+ ↓
+8. Copy to bundles_build directory
+ ↓
+9. Package prepared folder into archive
+ - Archive includes top-level folder: nodejs24.6.0/
+ - Generate hash files (MD5, SHA1, SHA256, SHA512)
+ ↓
+10. Output to bearsampp-build/bins/nodejs/{bundle.release}/
+```
+
+### Packaging Details
+
+- **Archive name format**: `bearsampp-nodejs-{version}-{bundle.release}.{7z|zip}`
+- **Location**: `bearsampp-build/bins/nodejs/{bundle.release}/`
+ - Example: `bearsampp-build/bins/nodejs/2025.8.21/bearsampp-nodejs-24.6.0-2025.8.21.7z`
+- **Content root**: The top-level folder inside the archive is `nodejs{version}/` (e.g., `nodejs24.6.0/`)
+- **Structure**: The archive contains the Node.js version folder at the root with all files inside
+
+**Archive Structure Example**:
+```
+bearsampp-nodejs-24.6.0-2025.8.21.7z
+└── nodejs24.6.0/ ← Version folder at root
+ ├── node.exe
+ ├── npm
+ ├── npm.cmd
+ ├── npx
+ ├── npx.cmd
+ ├── node_modules/
+ └── ...
+```
+
+**Verification Commands**:
+
+```bash
+# List archive contents with 7z
+7z l bearsampp-build/bins/nodejs/2025.8.21/bearsampp-nodejs-24.6.0-2025.8.21.7z | more
+
+# You should see entries beginning with:
+# nodejs24.6.0/node.exe
+# nodejs24.6.0/npm
+# nodejs24.6.0/node_modules/
+# nodejs24.6.0/...
+
+# Extract and inspect with PowerShell (zip example)
+Expand-Archive -Path bearsampp-build/bins/nodejs/2025.8.21/bearsampp-nodejs-24.6.0-2025.8.21.zip -DestinationPath .\_inspect
+Get-ChildItem .\_inspect\nodejs24.6.0 | Select-Object Name
+
+# Expected output:
+# node.exe
+# npm
+# npm.cmd
+# node_modules/
+# ...
+```
+
+**Note**: This archive structure matches other Bearsampp modules (PHP, MySQL) where archives contain `{module}{version}/` at the root. This ensures consistency across all Bearsampp modules.
+
+**Hash Files**: Each archive is accompanied by hash sidecar files:
+- `.md5` - MD5 checksum
+- `.sha1` - SHA-1 checksum
+- `.sha256` - SHA-256 checksum
+- `.sha512` - SHA-512 checksum
+
+Example:
+```
+bearsampp-build/bins/nodejs/2025.8.21/
+├── bearsampp-nodejs-24.6.0-2025.8.21.7z
+├── bearsampp-nodejs-24.6.0-2025.8.21.7z.md5
+├── bearsampp-nodejs-24.6.0-2025.8.21.7z.sha1
+├── bearsampp-nodejs-24.6.0-2025.8.21.7z.sha256
+└── bearsampp-nodejs-24.6.0-2025.8.21.7z.sha512
+```
+
+### Version Resolution Strategy
+
+The build system uses a two-tier strategy to locate Node.js binaries:
+
+1. **modules-untouched nodejs.properties** (Primary)
+ - URL: `https://github.com/Bearsampp/modules-untouched/blob/main/modules/nodejs.properties`
+ - Contains version-to-URL mappings
+ - Example: `24.6.0=https://github.com/Bearsampp/modules-untouched/releases/download/nodejs-24.6.0/nodejs-24.6.0-win-x64.7z`
+
+2. **Standard URL Format** (Fallback)
+ - Format: `https://github.com/Bearsampp/modules-untouched/releases/download/nodejs-{version}/nodejs-{version}-win-x64.7z`
+ - Used when version not found in nodejs.properties
+ - Assumes standard naming convention
+
+---
+
+## Troubleshooting
+
+### Common Issues
+
+#### Issue: "Dev path not found"
+
+**Symptom:**
+```
+Dev path not found: E:/Bearsampp-development/dev
+```
+
+**Solution:**
+This is a warning only. The dev path is optional for most tasks. If you need it, ensure the `dev` project exists in the parent directory.
+
+---
+
+#### Issue: "Bundle version not found"
+
+**Symptom:**
+```
+Bundle version not found in bin/ or bin/archived/
+```
+
+**Solution:**
+1. List available versions: `gradle listVersions`
+2. Use an existing version: `gradle release -PbundleVersion=24.6.0`
+3. Or the build will automatically download from modules-untouched
+
+---
+
+#### Issue: "Failed to download from modules-untouched"
+
+**Symptom:**
+```
+Failed to download from modules-untouched: Connection refused
+```
+
+**Solution:**
+1. Check internet connectivity
+2. Verify version exists: `gradle listReleases`
+3. Check modules-untouched repository is accessible
+4. Try again later if repository is temporarily unavailable
+
+---
+
+#### Issue: "Java version too old"
+
+**Symptom:**
+```
+Java 8+ required
+```
+
+**Solution:**
+1. Check Java version: `java -version`
+2. Install Java 8 or higher
+3. Update JAVA_HOME environment variable
+
+---
+
+#### Issue: "7-Zip not found"
+
+**Symptom:**
+```
+7-Zip not found. Please install 7-Zip or set 7Z_HOME environment variable.
+```
+
+**Solution:**
+1. Install 7-Zip from https://www.7-zip.org/
+2. Or set `7Z_HOME` environment variable to 7-Zip installation directory
+3. Or change `bundle.format` to `zip` in build.properties
+
+---
+
+### Debug Mode
+
+Run Gradle with debug output:
+
+```bash
+gradle release -PbundleVersion=24.6.0 --info
+gradle release -PbundleVersion=24.6.0 --debug
+```
+
+### Clean Build
+
+If you encounter issues, try a clean build:
+
+```bash
+gradle clean
+gradle release -PbundleVersion=24.6.0
+```
+
+---
+
+## Migration Guide
+
+### From Ant to Gradle
+
+The project has been fully migrated from Ant to Gradle. Here's what changed:
+
+#### Removed Files
+
+| File | Status | Replacement |
+|-------------------|-----------|----------------------------|
+| `build.xml` | ✗ Removed | `build.gradle` |
+
+#### Command Mapping
+
+| Ant Command | Gradle Command |
+|--------------------------------------|---------------------------------------------|
+| `ant release` | `gradle release` |
+| `ant release -Dinput.bundle=24.6.0` | `gradle release -PbundleVersion=24.6.0` |
+| `ant clean` | `gradle clean` |
+
+#### Key Differences
+
+| Aspect | Ant | Gradle |
+|---------------------|------------------------------|----------------------------------|
+| **Build File** | XML (build.xml) | Groovy DSL (build.gradle) |
+| **Task Definition** | `` | `tasks.register('...')` |
+| **Properties** | `` | `ext { ... }` |
+| **Dependencies** | Manual downloads | Automatic with repositories |
+| **Caching** | None | Built-in incremental builds |
+| **IDE Support** | Limited | Excellent (IntelliJ, Eclipse) |
+
+---
+
+## Additional Documentation
+
+This documentation is part of a comprehensive guide for the Bearsampp Node.js module build system:
+
+### Documentation Files
+
+| Document | Description |
+|---------------------------|--------------------------------------------------|
+| [README.md](README.md) | Main build documentation (this file) |
+| [PACKAGING.md](PACKAGING.md) | Packaging and archive structure guide |
+| [MIGRATION.md](MIGRATION.md) | Migration guide from Ant to Gradle |
+
+### Quick Links
+
+- **Main Project README**: [../README.md](../README.md)
+- **Build Script**: [../build.gradle](../build.gradle)
+- **Build Configuration**: [../build.properties](../build.properties)
+- **Gradle Settings**: [../settings.gradle](../settings.gradle)
+
+### External Resources
+
+- [Gradle Documentation](https://docs.gradle.org/)
+- [Bearsampp Project](https://github.com/bearsampp/bearsampp)
+- [Node.js Downloads](https://nodejs.org/en/download/)
+- [modules-untouched Repository](https://github.com/Bearsampp/modules-untouched)
+
+---
+
+## Support
+
+For issues and questions:
+
+- **GitHub Issues**: https://github.com/bearsampp/module-nodejs/issues
+- **Bearsampp Issues**: https://github.com/bearsampp/bearsampp/issues
+- **Documentation**: https://bearsampp.com/module/nodejs
+
+---
+
+**Last Updated**: 2025-01-31
+**Version**: 2025.8.21
+**Build System**: Pure Gradle (no wrapper, no Ant)
+
+### Notes
+
+- This project deliberately does not ship the Gradle Wrapper. Install Gradle 8+ locally and run with `gradle ...`.
+- Legacy Ant files have been removed. The project now uses pure Gradle for all build operations.
+- See [MIGRATION.md](MIGRATION.md) for details on the Ant to Gradle migration.
diff --git a/README.md b/README.md
index 3015d9b7..0ec08f1f 100644
--- a/README.md
+++ b/README.md
@@ -5,10 +5,65 @@
This is a module of [Bearsampp project](https://github.com/bearsampp/bearsampp) involving Node.js.
-## Documentation and downloads
+## Build System
-https://bearsampp.com/module/nodejs
+This project uses **Gradle** as its build system. The legacy Ant build has been fully replaced with a modern, pure Gradle implementation.
+
+### Quick Start
+
+```bash
+# Display build information
+gradle info
+
+# List all available tasks
+gradle tasks
+
+# Verify build environment
+gradle verify
+
+# Build a release (interactive)
+gradle release
+
+# Build a specific version (non-interactive)
+gradle release -PbundleVersion=24.6.0
+
+# Clean build artifacts
+gradle clean
+```
+
+### Prerequisites
+
+| Requirement | Version | Purpose |
+|-------------------|---------------|------------------------------------------|
+| **Java** | 8+ | Required for Gradle execution |
+| **Gradle** | 8.0+ | Build automation tool |
+| **7-Zip** | Latest | Archive creation (optional for zip) |
+
+### Available Tasks
+
+| Task | Description |
+|-----------------------|--------------------------------------------------|
+| `release` | Build release package (interactive/non-interactive) |
+| `releaseAll` | Build all versions in bin/ directory |
+| `clean` | Clean build artifacts and temporary files |
+| `verify` | Verify build environment and dependencies |
+| `info` | Display build configuration information |
+| `listVersions` | List available bundle versions in bin/ |
+| `listReleases` | List releases from modules-untouched |
+| `validateProperties` | Validate build.properties configuration |
+| `checkModulesUntouched` | Check modules-untouched integration |
+
+For complete documentation, see [.gradle-docs/README.md](.gradle-docs/README.md)
+
+## Documentation
+
+- **Build Documentation**: [.gradle-docs/README.md](.gradle-docs/README.md)
+- **Module Downloads**: https://bearsampp.com/module/nodejs
## Issues
Issues must be reported on [Bearsampp repository](https://github.com/bearsampp/bearsampp/issues).
+
+## Statistics
+
+
diff --git a/bin/nodejs22.17.0/bearsampp.conf b/bin/archived/nodejs22.17.0/bearsampp.conf
similarity index 100%
rename from bin/nodejs22.17.0/bearsampp.conf
rename to bin/archived/nodejs22.17.0/bearsampp.conf
diff --git a/bin/nodejs22.17.0/etc/npmrc b/bin/archived/nodejs22.17.0/etc/npmrc
similarity index 100%
rename from bin/nodejs22.17.0/etc/npmrc
rename to bin/archived/nodejs22.17.0/etc/npmrc
diff --git a/bin/nodejs22.17.0/etc/npmrc.ber b/bin/archived/nodejs22.17.0/etc/npmrc.ber
similarity index 100%
rename from bin/nodejs22.17.0/etc/npmrc.ber
rename to bin/archived/nodejs22.17.0/etc/npmrc.ber
diff --git a/bin/nodejs22.17.0/launch.bat b/bin/archived/nodejs22.17.0/launch.bat
similarity index 100%
rename from bin/nodejs22.17.0/launch.bat
rename to bin/archived/nodejs22.17.0/launch.bat
diff --git a/bin/nodejs22.17.0/node_modules/npm/npmrc b/bin/archived/nodejs22.17.0/node_modules/npm/npmrc
similarity index 100%
rename from bin/nodejs22.17.0/node_modules/npm/npmrc
rename to bin/archived/nodejs22.17.0/node_modules/npm/npmrc
diff --git a/bin/nodejs22.17.0/node_modules/npm/npmrc.ber b/bin/archived/nodejs22.17.0/node_modules/npm/npmrc.ber
similarity index 100%
rename from bin/nodejs22.17.0/node_modules/npm/npmrc.ber
rename to bin/archived/nodejs22.17.0/node_modules/npm/npmrc.ber
diff --git a/bin/nodejs22.18.0/bearsampp.conf b/bin/archived/nodejs22.18.0/bearsampp.conf
similarity index 100%
rename from bin/nodejs22.18.0/bearsampp.conf
rename to bin/archived/nodejs22.18.0/bearsampp.conf
diff --git a/bin/nodejs22.18.0/etc/npmrc b/bin/archived/nodejs22.18.0/etc/npmrc
similarity index 100%
rename from bin/nodejs22.18.0/etc/npmrc
rename to bin/archived/nodejs22.18.0/etc/npmrc
diff --git a/bin/nodejs22.18.0/etc/npmrc.ber b/bin/archived/nodejs22.18.0/etc/npmrc.ber
similarity index 100%
rename from bin/nodejs22.18.0/etc/npmrc.ber
rename to bin/archived/nodejs22.18.0/etc/npmrc.ber
diff --git a/bin/nodejs22.18.0/launch.bat b/bin/archived/nodejs22.18.0/launch.bat
similarity index 100%
rename from bin/nodejs22.18.0/launch.bat
rename to bin/archived/nodejs22.18.0/launch.bat
diff --git a/bin/nodejs22.18.0/node_modules/npm/npmrc b/bin/archived/nodejs22.18.0/node_modules/npm/npmrc
similarity index 100%
rename from bin/nodejs22.18.0/node_modules/npm/npmrc
rename to bin/archived/nodejs22.18.0/node_modules/npm/npmrc
diff --git a/bin/nodejs22.18.0/node_modules/npm/npmrc.ber b/bin/archived/nodejs22.18.0/node_modules/npm/npmrc.ber
similarity index 100%
rename from bin/nodejs22.18.0/node_modules/npm/npmrc.ber
rename to bin/archived/nodejs22.18.0/node_modules/npm/npmrc.ber
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 00000000..a9b92e0e
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,955 @@
+/*
+ * Bearsampp Module Node.js - Gradle Build (Bruno-style)
+ *
+ * This is a 100% Gradle build configuration for the Node.js module.
+ * It handles downloading, extracting, and packaging Node.js releases.
+ *
+ * VERSION RESOLUTION STRATEGY:
+ * 1. Remote modules-untouched nodejs.properties (primary source)
+ * URL: https://github.com/Bearsampp/modules-untouched/blob/main/modules/nodejs.properties
+ * 2. Standard URL format construction (fallback)
+ *
+ * DOCUMENTATION:
+ * All build documentation is located in /.gradle-docs/
+ * See /.gradle-docs/README.md for complete documentation index
+ *
+ * Usage:
+ * gradle tasks - List all available tasks
+ * gradle release -PbundleVersion=24.6.0 - Build release for specific version
+ * gradle releaseAll - Build all versions
+ * gradle clean - Clean build artifacts
+ * gradle info - Display build information
+ * gradle verify - Verify build environment
+ * gradle listVersions - List available versions
+ * gradle listReleases - List releases from modules-untouched
+ * gradle checkModulesUntouched - Check modules-untouched integration
+ */
+
+plugins {
+ id 'base'
+}
+
+// ============================================================================
+// PROJECT CONFIGURATION
+// ============================================================================
+
+// Load build properties
+def buildProps = new Properties()
+file('build.properties').withInputStream { buildProps.load(it) }
+
+// Project information
+group = 'com.bearsampp.modules'
+version = buildProps.getProperty('bundle.release', '1.0.0')
+description = "Bearsampp Module - ${buildProps.getProperty('bundle.name', 'nodejs')}"
+
+// Define project paths
+ext {
+ projectBasedir = projectDir.absolutePath
+ rootDir = projectDir.parent
+ devPath = file("${rootDir}/dev").absolutePath
+
+ // Bundle properties from build.properties
+ bundleName = buildProps.getProperty('bundle.name', 'nodejs')
+ bundleRelease = buildProps.getProperty('bundle.release', '1.0.0')
+ bundleType = buildProps.getProperty('bundle.type', 'bins')
+ bundleFormat = buildProps.getProperty('bundle.format', '7z')
+
+ // Build paths - with configurable base path
+ def buildPathFromProps = buildProps.getProperty('build.path', '').trim()
+ def buildPathFromEnv = System.getenv('BEARSAMPP_BUILD_PATH') ?: ''
+ def defaultBuildPath = "${rootDir}/bearsampp-build"
+
+ buildBasePath = buildPathFromProps ?: (buildPathFromEnv ?: defaultBuildPath)
+
+ // Shared bearsampp-build/tmp structure
+ buildTmpPath = file("${buildBasePath}/tmp").absolutePath
+ bundleTmpBuildPath = file("${buildTmpPath}/bundles_build/${bundleType}/${bundleName}").absolutePath
+ bundleTmpPrepPath = file("${buildTmpPath}/bundles_prep/${bundleType}/${bundleName}").absolutePath
+ bundleTmpSrcPath = file("${buildTmpPath}/bundles_src").absolutePath
+
+ // Download and extract paths
+ bundleTmpDownloadPath = file("${buildTmpPath}/downloads/${bundleName}").absolutePath
+ bundleTmpExtractPath = file("${buildTmpPath}/extract/${bundleName}").absolutePath
+}
+
+// Align with Bruno: place Gradle's own buildDir under the shared bearsampp-build tmp tree
+// This prevents creating a local ./build folder in the repository
+buildDir = file("${buildBasePath}/tmp/gradle/${bundleName}")
+
+// Verify dev path exists
+if (!file(ext.devPath).exists()) {
+ throw new GradleException("Dev path not found: ${ext.devPath}. Please ensure the 'dev' project exists in ${ext.rootDir}")
+}
+
+// Repositories
+repositories {
+ mavenCentral()
+}
+
+// ============================================================================
+// HELPER FUNCTIONS
+// ============================================================================
+
+// Function to fetch nodejs.properties from modules-untouched repository
+def fetchModulesUntouchedProperties() {
+ def propsUrl = "https://raw.githubusercontent.com/Bearsampp/modules-untouched/main/modules/nodejs.properties"
+
+ println "Fetching nodejs.properties from modules-untouched repository..."
+ println " URL: ${propsUrl}"
+
+ def tempFile = file("${bundleTmpDownloadPath}/nodejs-untouched.properties")
+ tempFile.parentFile.mkdirs()
+
+ try {
+ new URL(propsUrl).withInputStream { input ->
+ tempFile.withOutputStream { output ->
+ output << input
+ }
+ }
+
+ def props = new Properties()
+ tempFile.withInputStream { props.load(it) }
+
+ println " ✓ Successfully loaded ${props.size()} versions from modules-untouched"
+ return props
+ } catch (Exception e) {
+ println " ✗ Warning: Could not fetch nodejs.properties from modules-untouched: ${e.message}"
+ println " Will fall back to standard URL format if needed"
+ return null
+ }
+}
+
+// Function to download from modules-untouched repository (or fallback)
+def downloadFromModulesUntouched(String version, File destDir) {
+ println "Checking modules-untouched repository..."
+
+ def untouchedProps = fetchModulesUntouchedProperties()
+ def untouchedUrl = null
+
+ if (untouchedProps) {
+ untouchedUrl = untouchedProps.getProperty(version)
+ if (untouchedUrl) {
+ println "Found version ${version} in modules-untouched nodejs.properties"
+ println "Downloading from:"
+ println " ${untouchedUrl}"
+ } else {
+ println "Version ${version} not found in modules-untouched nodejs.properties"
+ println "Attempting to construct URL based on standard format..."
+ // Windows x64 standard URL (fallback) — aligns with Bearsampp packaging
+ untouchedUrl = "https://github.com/Bearsampp/modules-untouched/releases/download/nodejs-${version}/nodejs-${version}-win-x64.7z"
+ println " ${untouchedUrl}"
+ }
+ } else {
+ println "Could not fetch nodejs.properties, using standard URL format..."
+ untouchedUrl = "https://github.com/Bearsampp/modules-untouched/releases/download/nodejs-${version}/nodejs-${version}-win-x64.7z"
+ println " ${untouchedUrl}"
+ }
+
+ // Determine filename and ensure download dir
+ def filename = untouchedUrl.substring(untouchedUrl.lastIndexOf('/') + 1)
+ def downloadDir = file(bundleTmpDownloadPath)
+ downloadDir.mkdirs()
+
+ def downloadedFile = file("${downloadDir}/${filename}")
+
+ if (!downloadedFile.exists()) {
+ println " Downloading to: ${downloadedFile}"
+ try {
+ new URL(untouchedUrl).withInputStream { input ->
+ downloadedFile.withOutputStream { output ->
+ def buffer = new byte[8192]
+ def bytesRead
+ while ((bytesRead = input.read(buffer)) != -1) {
+ output.write(buffer, 0, bytesRead)
+ }
+ }
+ }
+ println " Download complete from modules-untouched"
+ } catch (Exception e) {
+ throw new GradleException("""
+ Failed to download from modules-untouched: ${e.message}
+
+ Tried URL: ${untouchedUrl}
+
+ Please verify:
+ 1. Version ${version} exists in modules-untouched repository
+ 2. The URL is correct in nodejs.properties or matches format: nodejs-{version}/nodejs-{version}-win-x64.7z
+ 3. You have internet connectivity
+ """.stripIndent())
+ }
+ } else {
+ println " Using cached file: ${downloadedFile}"
+ }
+
+ return downloadedFile
+}
+
+// Download and extract Node.js binaries
+def downloadAndExtractNode(String version, File destDir) {
+ def downloadedFile = downloadFromModulesUntouched(version, destDir)
+
+ def extractDir = file(bundleTmpExtractPath)
+ extractDir.mkdirs()
+ println " Extracting archive..."
+ def extractPath = file("${extractDir}/${version}")
+ if (extractPath.exists()) { delete extractPath }
+ extractPath.mkdirs()
+
+ def filename = downloadedFile.name
+
+ if (filename.endsWith('.7z')) {
+ def sevenZipPath = find7ZipExecutable()
+ if (sevenZipPath) {
+ def command = [
+ sevenZipPath.toString(),
+ 'x',
+ downloadedFile.absolutePath.toString(),
+ "-o${extractPath.absolutePath}".toString(),
+ '-y'
+ ]
+ def process = new ProcessBuilder(command as String[])
+ .directory(extractPath)
+ .redirectErrorStream(true)
+ .start()
+
+ process.inputStream.eachLine { line -> if (line.trim()) println " ${line}" }
+ def exitCode = process.waitFor()
+ if (exitCode != 0) {
+ throw new GradleException("7zip extraction failed with exit code: ${exitCode}")
+ }
+ } else {
+ throw new GradleException("7zip not found. Please install 7zip or extract manually.")
+ }
+ } else if (filename.endsWith('.zip')) {
+ copy { from zipTree(downloadedFile); into extractPath }
+ } else {
+ throw new GradleException("Unsupported archive format: ${filename}")
+ }
+
+ println " Extraction complete"
+
+ def nodeDir = findNodeDirectory(extractPath)
+ if (!nodeDir) {
+ throw new GradleException("Could not find Node.js directory in extracted files")
+ }
+
+ println " Found Node.js directory: ${nodeDir.name}"
+ println ""
+ println "NOTE: Version ${version} was sourced from modules-untouched (or fallback URL)."
+
+ return nodeDir
+}
+
+// Find Node.js directory (looks for node.exe)
+def findNodeDirectory(File extractPath) {
+ def hasNodeExe = { File dir ->
+ new File(dir, 'node.exe').exists() || new File(dir, 'Node.exe').exists()
+ }
+
+ if (!extractPath?.exists()) { return null }
+ if (hasNodeExe(extractPath)) { return extractPath }
+
+ File found = null
+ def stack = new ArrayDeque()
+ extractPath.listFiles()?.findAll { it.isDirectory() }?.each { stack.push(it) }
+ while (!stack.isEmpty() && found == null) {
+ def dir = stack.pop()
+ if (hasNodeExe(dir)) { found = dir; break }
+ dir.listFiles()?.findAll { it.isDirectory() }?.each { stack.push(it) }
+ }
+ return found
+}
+
+// ============================================================================
+// GRADLE TASKS
+// ============================================================================
+
+// Info task
+tasks.register('info') {
+ group = 'help'
+ description = 'Display build configuration information'
+
+ def projectName = project.name
+ def projectVersion = project.version
+ def projectDescription = project.description
+ def projectBasedirValue = projectBasedir
+ def rootDirValue = rootDir
+ def devPathValue = devPath
+ def bundleNameValue = bundleName
+ def bundleReleaseValue = bundleRelease
+ def bundleTypeValue = bundleType
+ def bundleFormatValue = bundleFormat
+ def buildBasePathValue = buildBasePath
+ def buildTmpPathValue = buildTmpPath
+ def bundleTmpPrepPathValue = bundleTmpPrepPath
+ def bundleTmpBuildPathValue = bundleTmpBuildPath
+ def bundleTmpSrcPathValue = bundleTmpSrcPath
+ def bundleTmpDownloadPathValue = bundleTmpDownloadPath
+ def bundleTmpExtractPathValue = bundleTmpExtractPath
+ def javaVersion = JavaVersion.current()
+ def javaHome = System.getProperty('java.home')
+ def gradleVersion = gradle.gradleVersion
+ def gradleHome = gradle.gradleHomeDir
+
+ doLast {
+ println """
+ ================================================================
+ Bearsampp Module Node.js - Build Info
+ ================================================================
+
+ Project: ${projectName}
+ Version: ${projectVersion}
+ Description: ${projectDescription}
+
+ Bundle Properties:
+ Name: ${bundleNameValue}
+ Release: ${bundleReleaseValue}
+ Type: ${bundleTypeValue}
+ Format: ${bundleFormatValue}
+
+ Paths:
+ Project Dir: ${projectBasedirValue}
+ Root Dir: ${rootDirValue}
+ Dev Path: ${devPathValue}
+ Build Base: ${buildBasePathValue}
+ Build Tmp: ${buildTmpPathValue}
+ Tmp Prep: ${bundleTmpPrepPathValue}
+ Tmp Build: ${bundleTmpBuildPathValue}
+ Tmp Src: ${bundleTmpSrcPathValue}
+ Tmp Download: ${bundleTmpDownloadPathValue}
+ Tmp Extract: ${bundleTmpExtractPathValue}
+
+ Java:
+ Version: ${javaVersion}
+ Home: ${javaHome}
+
+ Gradle:
+ Version: ${gradleVersion}
+ Home: ${gradleHome}
+
+ Available Task Groups:
+ * build - Build and package tasks
+ * help - Help and information tasks
+ * verification - Verification tasks
+
+ Quick Start:
+ gradle tasks - List all available tasks
+ gradle info - Show this information
+ gradle release -PbundleVersion=24.6.0 - Build specific version
+ gradle releaseAll - Build all versions
+ gradle clean - Clean build artifacts
+ gradle verify - Verify build environment
+ """.stripIndent()
+ }
+}
+
+// Main release task - build a specific version
+tasks.register('release') {
+ group = 'build'
+ description = 'Build release package for a specific version (use -PbundleVersion=X.X.X or run interactively)'
+
+ def versionProperty = project.findProperty('bundleVersion')
+
+ doLast {
+ def versionToBuild = versionProperty
+
+ if (!versionToBuild) {
+ // Interactive prompt
+ def availableVersions = getAvailableVersions()
+ if (availableVersions.isEmpty()) {
+ throw new GradleException("No versions found in bin/ directory")
+ }
+
+ println ""
+ println "=".multiply(70)
+ println "Interactive Release Mode"
+ println "=".multiply(70)
+ println ""
+ println "Available versions:"
+
+ def binDir = file("${projectDir}/bin")
+ def archivedDir = file("${projectDir}/bin/archived")
+
+ availableVersions.eachWithIndex { version, index ->
+ def location = ""
+ if (binDir.exists() && file("${binDir}/${bundleName}${version}").exists()) {
+ location = "[bin]"
+ } else if (archivedDir.exists() && file("${archivedDir}/${bundleName}${version}").exists()) {
+ location = "[bin/archived]"
+ }
+ println " ${(index + 1).toString().padLeft(2)}. ${version.padRight(15)} ${location}"
+ }
+ println ""
+ println "Enter version to build (index or version string):"
+ println ""
+
+ def input = null
+ try {
+ def reader = new BufferedReader(new InputStreamReader(System.in))
+ input = reader.readLine()
+ } catch (Exception e) {
+ throw new GradleException("""
+ Failed to read input. Please use non-interactive mode:
+ gradle release -PbundleVersion=X.X.X
+
+ Available versions: ${availableVersions.join(', ')}
+ """.stripIndent())
+ }
+
+ if (!input || input.trim().isEmpty()) {
+ throw new GradleException("""
+ No version selected. Please use non-interactive mode:
+ gradle release -PbundleVersion=X.X.X
+
+ Available versions: ${availableVersions.join(', ')}
+ """.stripIndent())
+ }
+
+ def cleaned = input.trim()
+ if (cleaned.isInteger()) {
+ def idx = cleaned.toInteger()
+ if (idx < 1 || idx > availableVersions.size()) {
+ throw new GradleException("""
+ Invalid selection index: ${cleaned}
+
+ Please choose a number between 1 and ${availableVersions.size()} or enter a version string.
+ """.stripIndent())
+ }
+ versionToBuild = availableVersions[idx - 1]
+ } else {
+ versionToBuild = cleaned
+ if (!availableVersions.contains(versionToBuild)) {
+ throw new GradleException("""
+ Invalid version: ${versionToBuild}
+
+ Please choose from available versions:
+ ${availableVersions.collect { " - ${it}" }.join('\n')}
+ """.stripIndent())
+ }
+ }
+
+ println ""
+ println "Selected version: ${versionToBuild}"
+ }
+
+ println ""
+ println "=".multiply(70)
+ println "Building ${bundleName} ${versionToBuild}"
+ println "=".multiply(70)
+ println ""
+
+ // Validate version exists in bin or archived
+ def bundlePath = file("${projectDir}/bin/${bundleName}${versionToBuild}")
+ if (!bundlePath.exists()) {
+ bundlePath = file("${projectDir}/bin/archived/${bundleName}${versionToBuild}")
+ if (!bundlePath.exists()) {
+ throw new GradleException("Bundle version not found in bin/ or bin/archived/\n\nAvailable versions:\n${getAvailableVersions().collect { " - ${it}" }.join('\n')}")
+ }
+ }
+
+ println "Bundle path: ${bundlePath}"
+ println ""
+
+ def bundleFolder = bundlePath.name
+ def bundleVersion = bundleFolder.replace(bundleName, '')
+
+ def bundleSrcDest = bundlePath
+ def bundleSrcFinal = bundleSrcDest
+
+ def nodeExe = file("${bundleSrcFinal}/node.exe")
+ if (!nodeExe.exists()) {
+ // Try cached extract first
+ def tmpExtractPath = file("${bundleTmpExtractPath}/${bundleVersion}")
+ def tmpNodeDir = findNodeDirectory(tmpExtractPath)
+
+ if (tmpNodeDir && tmpNodeDir.exists()) {
+ println "Using cached Node.js binaries from bearsampp-build/tmp"
+ bundleSrcFinal = tmpNodeDir
+ } else {
+ println ""
+ println "Node.js binaries not found"
+ println "Downloading Node.js ${bundleVersion}..."
+ println ""
+
+ try {
+ bundleSrcFinal = downloadAndExtractNode(bundleVersion, file(bundleTmpExtractPath))
+ } catch (Exception e) {
+ throw new GradleException("""
+ Failed to download Node.js binaries: ${e.message}
+
+ You can manually download and extract Node.js binaries to:
+ ${bundleSrcDest}/
+
+ Or check that version ${bundleVersion} exists in modules-untouched nodejs.properties
+ """.stripIndent())
+ }
+ }
+ }
+
+ nodeExe = file("${bundleSrcFinal}/node.exe")
+ if (!nodeExe.exists()) {
+ throw new GradleException("node.exe not found at ${nodeExe}")
+ }
+
+ println "Source folder: ${bundleSrcFinal}"
+ println ""
+
+ // Prep directory
+ def prepPath = file("${bundleTmpPrepPath}/${bundleName}${bundleVersion}")
+ if (prepPath.exists()) { delete prepPath }
+ prepPath.mkdirs()
+
+ // Copy Node.js files from extracted/downloaded location
+ println "Copying Node.js files..."
+ copy { from bundleSrcFinal; into prepPath }
+
+ // Overlay files from bin bundle directory
+ println "Overlaying bundle files from bin directory..."
+ copy { from bundleSrcDest; into prepPath }
+
+ println ""
+ println "Copying to bundles_build directory..."
+ def buildPathOut = file("${bundleTmpBuildPath}/${bundleName}${bundleVersion}")
+ if (buildPathOut.exists()) { delete buildPathOut }
+ buildPathOut.mkdirs()
+ copy { from prepPath; into buildPathOut }
+ println "Non-zip version available at: ${buildPathOut}"
+
+ println ""
+ println "Preparing archive..."
+
+ // Output path bearsampp-build/{type}/{name}/{release}
+ def buildPath = file(buildBasePath)
+ def buildBinsPath = file("${buildPath}/${bundleType}/${bundleName}/${bundleRelease}")
+ buildBinsPath.mkdirs()
+
+ def destFile = file("${buildBinsPath}/bearsampp-${bundleName}-${bundleVersion}-${bundleRelease}")
+
+ if (bundleFormat == '7z') {
+ def archiveFile = file("${destFile}.7z")
+ if (archiveFile.exists()) { delete archiveFile }
+
+ println "Compressing (including top-level folder) ${bundleName}${bundleVersion} to ${archiveFile.name}..."
+
+ def sevenZipExe = find7ZipExecutable()
+ if (!sevenZipExe) { throw new GradleException("7-Zip not found. Please install 7-Zip or set 7Z_HOME environment variable.") }
+ println "Using 7-Zip: ${sevenZipExe}"
+
+ // IMPORTANT: run from the parent directory and add the folder name explicitly
+ def parentDir = prepPath.parentFile
+ def folderName = prepPath.name
+ def command = [ sevenZipExe, 'a', '-t7z', archiveFile.absolutePath.toString(), folderName ]
+ def process = new ProcessBuilder(command as String[])
+ .directory(parentDir)
+ .redirectErrorStream(true)
+ .start()
+ process.inputStream.eachLine { line -> if (line.trim()) println " ${line}" }
+ def exitCode = process.waitFor()
+ if (exitCode != 0) { throw new GradleException("7zip compression failed with exit code: ${exitCode}") }
+
+ println "Archive created: ${archiveFile}"
+ println "Generating hash files..."
+ generateHashFiles(archiveFile)
+ } else {
+ def archiveFile = file("${destFile}.zip")
+ if (archiveFile.exists()) { delete archiveFile }
+
+ println "Compressing (including top-level folder) ${bundleName}${bundleVersion} to ${archiveFile.name}..."
+ // Use Zip task but include the folder itself so the archive contains nodejs{version}/...
+ task("zipArchive_${bundleVersion}", type: Zip) {
+ from(prepPath.parentFile) {
+ include "${prepPath.name}/**"
+ }
+ destinationDirectory = archiveFile.parentFile
+ archiveFileName = archiveFile.name
+ }.execute()
+
+ println "Archive created: ${archiveFile}"
+ println "Generating hash files..."
+ generateHashFiles(archiveFile)
+ }
+
+ println ""
+ println "=".multiply(70)
+ println "[SUCCESS] Release build completed successfully for version ${versionToBuild}"
+ println "Output directory: ${buildPathOut}"
+ println "Archive: ${destFile}.${bundleFormat}"
+ println "=".multiply(70)
+ }
+}
+
+// Find 7-Zip executable
+def find7ZipExecutable() {
+ def sevenZipHome = System.getenv('7Z_HOME')
+ if (sevenZipHome) {
+ def exe = file("${sevenZipHome}/7z.exe")
+ if (exe.exists()) { return exe.absolutePath }
+ }
+ def commonPaths = [
+ 'C:/Program Files/7-Zip/7z.exe',
+ 'C:/Program Files (x86)/7-Zip/7z.exe',
+ 'D:/Program Files/7-Zip/7z.exe',
+ 'D:/Program Files (x86)/7-Zip/7z.exe'
+ ]
+ for (path in commonPaths) {
+ def exe = file(path)
+ if (exe.exists()) { return exe.absolutePath }
+ }
+ try {
+ def process = ['where', '7z.exe'].execute()
+ process.waitFor()
+ if (process.exitValue() == 0) {
+ def output = process.text.trim()
+ if (output) { return output.split('\n')[0].trim() }
+ }
+ } catch (Exception e) { }
+ return null
+}
+
+// Generate hash files for an archive
+def generateHashFiles(File file) {
+ if (!file.exists()) { throw new GradleException("File not found for hashing: ${file}") }
+
+ def md5File = new File("${file.absolutePath}.md5")
+ def md5Hash = calculateHash(file, 'MD5')
+ md5File.text = "${md5Hash} ${file.name}\n"
+ println " Created: ${md5File.name}"
+
+ def sha1File = new File("${file.absolutePath}.sha1")
+ def sha1Hash = calculateHash(file, 'SHA-1')
+ sha1File.text = "${sha1Hash} ${file.name}\n"
+ println " Created: ${sha1File.name}"
+
+ def sha256File = new File("${file.absolutePath}.sha256")
+ def sha256Hash = calculateHash(file, 'SHA-256')
+ sha256File.text = "${sha256Hash} ${file.name}\n"
+ println " Created: ${sha256File.name}"
+
+ def sha512File = new File("${file.absolutePath}.sha512")
+ def sha512Hash = calculateHash(file, 'SHA-512')
+ sha512File.text = "${sha512Hash} ${file.name}\n"
+ println " Created: ${sha512File.name}"
+}
+
+// Calculate hash
+def calculateHash(File file, String algorithm) {
+ def digest = java.security.MessageDigest.getInstance(algorithm)
+ file.withInputStream { stream ->
+ def buffer = new byte[8192]
+ def bytesRead
+ while ((bytesRead = stream.read(buffer)) != -1) {
+ digest.update(buffer, 0, bytesRead)
+ }
+ }
+ return digest.digest().collect { String.format('%02x', it) }.join('')
+}
+
+// Get available versions from bin and bin/archived
+def getAvailableVersions() {
+ def versions = []
+
+ def binDir = file("${projectDir}/bin")
+ if (binDir.exists()) {
+ def binVersions = binDir.listFiles()
+ ?.findAll { it.isDirectory() && it.name.startsWith(bundleName) && it.name != 'archived' }
+ ?.collect { it.name.replace(bundleName, '') } ?: []
+ versions.addAll(binVersions)
+ }
+
+ def archivedDir = file("${projectDir}/bin/archived")
+ if (archivedDir.exists()) {
+ def archivedVersions = archivedDir.listFiles()
+ ?.findAll { it.isDirectory() && it.name.startsWith(bundleName) }
+ ?.collect { it.name.replace(bundleName, '') } ?: []
+ versions.addAll(archivedVersions)
+ }
+
+ return versions.unique().sort()
+}
+
+// Build all available versions (prep only)
+tasks.register('releaseAll') {
+ group = 'build'
+ description = 'Build release packages for all available versions in bin/ directory'
+
+ doLast {
+ def binDir = file("${projectDir}/bin")
+ if (!binDir.exists()) { throw new GradleException("bin/ directory not found") }
+
+ def versions = getAvailableVersions()
+ if (versions.isEmpty()) { throw new GradleException("No versions found in bin/ directory") }
+
+ println ""
+ println "=".multiply(70)
+ println "Building releases for ${versions.size()} ${bundleName} versions"
+ println "=".multiply(70)
+ println ""
+
+ def successCount = 0
+ def failedVersions = []
+
+ versions.each { version ->
+ println "=".multiply(70)
+ println "[${successCount + 1}/${versions.size()}] Building ${bundleName} ${version}..."
+ println "=".multiply(70)
+
+ try {
+ def bundlePath = file("${projectDir}/bin/${bundleName}${version}")
+ if (!bundlePath.exists()) { throw new GradleException("Bundle path not found: ${bundlePath}") }
+
+ println "Bundle path: ${bundlePath}"
+ println ""
+
+ def bundleFolder = bundlePath.name
+ def bundleVersion = bundleFolder.replace(bundleName, '')
+ def bundleSrcDest = bundlePath
+
+ def nodeExe = file("${bundleSrcDest}/node.exe")
+ if (!nodeExe.exists()) { throw new GradleException("node.exe not found at ${nodeExe}") }
+
+ println "Source folder: ${bundleSrcDest}"
+ println ""
+
+ def prepPath = file("${bundleTmpPrepPath}/${bundleName}${bundleVersion}")
+ if (prepPath.exists()) { delete prepPath }
+ prepPath.mkdirs()
+
+ println "Copying Node.js files..."
+ copy { from bundleSrcDest; into prepPath }
+
+ println ""
+ println "[SUCCESS] ${bundleName} ${version} completed"
+ println "Output: ${prepPath}"
+ successCount++
+
+ } catch (Exception e) {
+ println ""
+ println "[FAILED] ${bundleName} ${version}: ${e.message}"
+ failedVersions.add(version)
+ }
+
+ println ""
+ }
+
+ println "=".multiply(70)
+ println "Build Summary"
+ println "=".multiply(70)
+ println "Total versions: ${versions.size()}"
+ println "Successful: ${successCount}"
+ println "Failed: ${failedVersions.size()}"
+
+ if (!failedVersions.isEmpty()) {
+ println ""
+ println "Failed versions:"
+ failedVersions.each { v -> println " - ${v}" }
+ }
+
+ println "=".multiply(70)
+
+ if (failedVersions.isEmpty()) {
+ println "[SUCCESS] All versions built successfully!"
+ } else {
+ throw new GradleException("${failedVersions.size()} version(s) failed to build")
+ }
+ }
+}
+
+// Clean task enhancement (only Gradle artifacts for this script)
+tasks.named('clean') {
+ group = 'build'
+ description = 'Clean build artifacts and temporary files'
+ doLast {
+ def buildDir = file("${projectDir}/build")
+ if (buildDir.exists()) { delete buildDir }
+ println "[SUCCESS] Build artifacts cleaned"
+ }
+}
+
+// Verify environment
+tasks.register('verify') {
+ group = 'verification'
+ description = 'Verify build environment and dependencies'
+ doLast {
+ println "Verifying build environment for module-nodejs..."
+ def checks = [:]
+ def javaVersion = JavaVersion.current()
+ checks['Java 8+'] = javaVersion >= JavaVersion.VERSION_1_8
+ checks['build.properties'] = file('build.properties').exists()
+ checks['dev directory'] = file(devPath).exists()
+ checks['bin directory'] = file("${projectDir}/bin").exists()
+ if (bundleFormat == '7z') { checks['7-Zip'] = find7ZipExecutable() != null }
+
+ println "\nEnvironment Check Results:"
+ println "-".multiply(60)
+ checks.each { name, passed ->
+ def status = passed ? "[PASS]" : "[FAIL]"
+ println " ${status.padRight(10)} ${name}"
+ }
+ println "-".multiply(60)
+
+ def allPassed = checks.values().every { it }
+ if (allPassed) {
+ println "\n[SUCCESS] All checks passed! Build environment is ready."
+ println "\nYou can now run:"
+ println " gradle release -PbundleVersion=24.6.0 - Build release for version"
+ println " gradle listVersions - List available versions"
+ } else {
+ println "\n[WARNING] Some checks failed. Please review the requirements."
+ throw new GradleException("Build environment verification failed")
+ }
+ }
+}
+
+// List releases from modules-untouched
+tasks.register('listReleases') {
+ group = 'help'
+ description = 'List all available releases from modules-untouched nodejs.properties'
+ doLast {
+ def props = fetchModulesUntouchedProperties()
+ if (!props) {
+ println "\n[WARNING] Could not fetch modules-untouched nodejs.properties."
+ println "No release information available."
+ return
+ }
+ println "\nAvailable Node.js Releases (modules-untouched):"
+ println "-".multiply(80)
+ props.sort { a, b -> a.key <=> b.key }.each { version, url -> println " ${version.padRight(10)} -> ${url}" }
+ println "-".multiply(80)
+ println "Total releases: ${props.size()}"
+ }
+}
+
+// List versions available locally
+tasks.register('listVersions') {
+ group = 'help'
+ description = 'List all available bundle versions in bin/ and bin/archived/ directories'
+ doLast {
+ def versions = getAvailableVersions()
+ if (versions.isEmpty()) { println "\nNo versions found in bin/ or bin/archived/ directories"; return }
+
+ println "\nAvailable ${bundleName} versions:"
+ println "-".multiply(60)
+
+ def binDir = file("${projectDir}/bin")
+ def archivedDir = file("${projectDir}/bin/archived")
+
+ versions.each { version ->
+ def location = ""
+ if (binDir.exists() && file("${binDir}/${bundleName}${version}").exists()) { location = "[bin]" }
+ else if (archivedDir.exists() && file("${archivedDir}/${bundleName}${version}").exists()) { location = "[bin/archived]" }
+ println " ${version.padRight(15)} ${location}"
+ }
+ println "-".multiply(60)
+ println "Total versions: ${versions.size()}"
+ if (!versions.isEmpty()) {
+ println "\nTo build a specific version:"
+ println " gradle release -PbundleVersion=${versions.last()}"
+ }
+ }
+}
+
+// Validate build.properties
+tasks.register('validateProperties') {
+ group = 'verification'
+ description = 'Validate build.properties configuration'
+ doLast {
+ println "Validating build.properties..."
+ def required = ['bundle.name', 'bundle.release', 'bundle.type', 'bundle.format']
+ def missing = []
+ required.each { prop -> if (!buildProps.containsKey(prop) || buildProps.getProperty(prop).trim().isEmpty()) { missing.add(prop) } }
+ if (missing.isEmpty()) {
+ println "[SUCCESS] All required properties are present:"
+ required.each { prop -> println " ${prop} = ${buildProps.getProperty(prop)}" }
+ } else {
+ println "[ERROR] Missing required properties:"
+ missing.each { prop -> println " - ${prop}" }
+ throw new GradleException("build.properties validation failed")
+ }
+ }
+}
+
+// Check modules-untouched integration
+tasks.register('checkModulesUntouched') {
+ group = 'verification'
+ description = 'Check modules-untouched repository integration and available versions'
+ doLast {
+ println ""
+ println "=".multiply(70)
+ println "Modules-Untouched Integration Check"
+ println "=".multiply(70)
+ println ""
+
+ def propsUrl = "https://raw.githubusercontent.com/Bearsampp/modules-untouched/main/modules/nodejs.properties"
+ println "Repository URL:"
+ println " ${propsUrl}"
+ println ""
+
+ println "Fetching nodejs.properties from modules-untouched..."
+ def untouchedProps = fetchModulesUntouchedProperties()
+
+ if (untouchedProps) {
+ println ""
+ println "=".multiply(70)
+ println "Available Versions in modules-untouched"
+ println "=".multiply(70)
+
+ def sortedVersions = untouchedProps.sort { a, b ->
+ def aParts = a.key.tokenize('.')
+ def bParts = b.key.tokenize('.')
+ for (int i = 0; i < Math.min(aParts.size(), bParts.size()); i++) {
+ def aNum = aParts[i].toInteger()
+ def bNum = bParts[i].toInteger()
+ if (aNum != bNum) return aNum <=> bNum
+ }
+ return aParts.size() <=> bParts.size()
+ }
+
+ sortedVersions.each { version, url -> println " ${version.padRight(10)}" }
+
+ println "=".multiply(70)
+ println "Total versions: ${untouchedProps.size()}"
+ println ""
+ println ""
+ println "=".multiply(70)
+ println "[SUCCESS] modules-untouched integration is working"
+ println "=".multiply(70)
+ println ""
+ println "Version Resolution Strategy:"
+ println " 1. Check modules-untouched nodejs.properties (remote)"
+ println " 2. Construct standard URL format (fallback)"
+ println ""
+ println "Documentation: /.gradle-docs/MODULES_UNTOUCHED_INTEGRATION.md"
+ } else {
+ println ""
+ println "=".multiply(70)
+ println "[WARNING] Could not fetch nodejs.properties from modules-untouched"
+ println "=".multiply(70)
+ println ""
+ println "This may be due to:"
+ println " - Network connectivity issues"
+ println " - Repository access problems"
+ println " - File not available at expected location"
+ println ""
+ println "The build system will fall back to:"
+ println " 1. Standard URL format construction"
+ }
+ }
+}
+
+// ============================================================================
+// BUILD LIFECYCLE HOOKS
+// ============================================================================
+
+gradle.taskGraph.whenReady { graph ->
+ println """
+ ================================================================
+ Bearsampp Module Node.js - Gradle Build
+ ================================================================
+ """.stripIndent()
+}
+
+// ============================================================================
+// DEFAULT TASK
+// ============================================================================
+
+defaultTasks 'info'
diff --git a/build.xml b/build.xml
deleted file mode 100644
index 11933307..00000000
--- a/build.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 00000000..eab38d9d
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,19 @@
+# Gradle Build Properties for Bearsampp Module Node.js
+
+# Gradle daemon configuration
+org.gradle.daemon=true
+org.gradle.parallel=true
+org.gradle.caching=true
+
+# JVM settings for Gradle
+org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError
+
+# Configure console output
+org.gradle.console=auto
+org.gradle.warning.mode=all
+
+# Build performance
+org.gradle.configureondemand=false
+
+# Gradle version compatibility
+# This project is compatible with Gradle 7.0+
diff --git a/module-nodejs.RELEASE.launch b/module-nodejs.RELEASE.launch
deleted file mode 100644
index d6908ad3..00000000
--- a/module-nodejs.RELEASE.launch
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 00000000..53e2de93
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,25 @@
+/*
+ * Bearsampp Module Node.js - Gradle Settings
+ */
+
+rootProject.name = 'module-nodejs'
+
+// Enable Gradle features for better performance
+enableFeaturePreview('STABLE_CONFIGURATION_CACHE')
+
+// Configure build cache for faster builds
+buildCache {
+ local {
+ enabled = true
+ directory = file("${rootDir}/.gradle/build-cache")
+ }
+}
+
+// Display initialization message
+gradle.rootProject {
+ println """
+ ================================================================
+ Initializing Bearsampp Module Node.js Build
+ ================================================================
+ """.stripIndent()
+}