diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
new file mode 100644
index 0000000..a45d404
--- /dev/null
+++ b/.github/copilot-instructions.md
@@ -0,0 +1,102 @@
+# Copilot Instructions for PSFluentObjectValidation
+
+## Project Overview
+PSFluentObjectValidation is a PowerShell module that provides fluent syntax for validating complex object structures using dot notation with validation operators. The core functionality is implemented as a C# class embedded in PowerShell, supporting deep object traversal, array indexing, and wildcard validation.
+
+## Architecture
+
+### Core Components
+- **C# Implementation**: `PSFluentObjectValidation/Private/PSFluentObjectValidation.ps1` contains the main logic as embedded C# code using `Add-Type`
+- **Public Functions**: Thin PowerShell wrappers around the C# class
+ - `Test-Exist`: Safe validation that returns boolean
+ - `Assert-Exist`: Throws exceptions with detailed error messages
+- **Module Structure**: Standard PowerShell module with Public/Private folder separation
+
+### Key Design Patterns
+
+#### Validation Syntax
+The module uses a fluent dot notation with special operators:
+- `property.nested` - Basic navigation
+- `property!` - Non-empty validation (rejects null/empty/whitespace)
+- `property?` - Existence validation (allows null values)
+- `array[0]` - Array indexing
+- `array[*]` - Wildcard validation (all elements must pass)
+
+#### Error Handling Strategy
+- `Test-Exist` wraps `Assert-Exist` in try/catch, never throws
+- `Assert-Exist` provides detailed error messages with context
+- C# implementation uses regex patterns for parsing validation operators
+
+## Development Workflows
+
+### Build System (psake + PowerShellBuild)
+```powershell
+# Bootstrap dependencies first (one-time setup)
+./build.ps1 -Bootstrap
+
+# Standard development workflow
+./build.ps1 -Task Build # Compiles and validates module
+./build.ps1 -Task Test # Runs Pester tests + analysis
+./build.ps1 -Task Clean # Cleans output directory
+```
+
+The build uses PowerShellBuild tasks defined in `psakeFile.ps1`. The `requirements.psd1` manages all build dependencies including Pester 5.4.0, PSScriptAnalyzer, and psake.
+
+### Testing Strategy
+Tests live in `tests/` directory following these patterns:
+- `Manifest.tests.ps1` - Module manifest validation
+- `Meta.tests.ps1` - Code quality and PSScriptAnalyzer rules
+- `Help.tests.ps1` - Documentation validation
+- Use `ScriptAnalyzerSettings.psd1` for custom analysis rules
+
+### Module Compilation
+The module uses **non-monolithic** compilation (`$PSBPreference.Build.CompileModule = $false`), preserving individual Public/Private .ps1 files in the output rather than combining into a single .psm1.
+
+## Critical Implementation Details
+
+### C# Embedded Code Patterns
+When modifying the C# implementation:
+- Use `Add-Type` with `ReferencedAssemblies` for System.Management.Automation
+- Regex patterns are compiled for performance: `PropertyWithValidation`, `ArrayIndexPattern`
+- Support multiple object types: Hashtables, PSObjects, .NET objects, Arrays, IList, IEnumerable
+
+### Array Processing
+The `WildcardArrayWrapper` class enables wildcard validation by:
+1. Wrapping array objects during `[*]` processing
+2. Validating properties exist on ALL elements
+3. Returning first element's value for continued navigation
+
+### Property Resolution Order
+1. Check for array indexing pattern `property[index]`
+2. Check for validation suffixes `property!` or `property?`
+3. Handle wildcard array wrapper context
+4. Fall back to regular property navigation
+
+## Common Patterns
+
+### Adding New Validation Operators
+1. Update regex patterns in C# code
+2. Add case handling in `ProcessPropertyWithValidation`
+3. Add validation logic in `ValidatePropertyValue`
+4. Update documentation and examples
+
+### Testing Complex Object Structures
+Use the fluent syntax patterns from README.md:
+```powershell
+# Deep nesting with validation
+Test-Exist -In $data -With "users[0].profile.settings.theme!"
+
+# Wildcard array validation
+Test-Exist -In $data -With "users[*].email!"
+
+# Mixed indexing and wildcards
+Test-Exist -In $data -With "orders[1].items[*].price"
+```
+
+### Error Message Conventions
+- Include property path context: `"Property 'user.name' does not exist"`
+- For arrays: `"Array index [10] is out of bounds for 'users' (length: 5)"`
+- For wildcards: `"Property 'email' in element [2] is empty"`
+
+## Cross-Platform Considerations
+The module targets PowerShell 5.1+ and supports Windows/Linux/macOS. The CI pipeline tests on all three platforms using GitHub Actions with the psmodulecache action for dependency management.
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index d318c20..25c15f8 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -7,7 +7,7 @@
"windows": {
"options": {
"shell": {
- "executable": "powershell.exe",
+ "executable": "pwsh.exe",
"args": [ "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command" ]
}
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 386a34a..bd78831 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,12 +3,32 @@
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
-and this project adheres to [Semantic Versioning](http://semver.org/).
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## [1.0.1] Released
+## [1.0.2] - 2025-09-24
+
+### Added
+
+- Added test cases
+
+### Fixed
+
+- Fixed a typo in Assert-Exists where the With alias was called Width
+- Fixed an issue with multi level array validation tests[*].users[1] was failing to properly validate before
+
+### Changed
+
+- Updated README
+- Updated CHANGELOG
+
+## [1.0.1] - 2025-09-23
+
+### Fixed
- Fixing issue with powershell 5.1 compiling the c# code.
-## [1.0.0] Released
+## [1.0.0] - 2025-09-23
+
+### Added
- Initial release
diff --git a/PSFluentObjectValidation/PSFluentObjectValidation.psd1 b/PSFluentObjectValidation/PSFluentObjectValidation.psd1
index 9cb9232..0b1541c 100644
--- a/PSFluentObjectValidation/PSFluentObjectValidation.psd1
+++ b/PSFluentObjectValidation/PSFluentObjectValidation.psd1
@@ -1,22 +1,22 @@
@{
RootModule = 'PSFluentObjectValidation.psm1'
- ModuleVersion = '1.0.1'
+ ModuleVersion = '1.0.2'
GUID = '90ac3c83-3bd9-4da5-8705-7b82b21963c8'
Author = 'Joshua Wilson'
CompanyName = 'PwshDevs'
Copyright = '(c) 2025 PwshDevs. All rights reserved.'
Description = 'Contains a helper class and functions to validate objects.'
PowerShellVersion = '5.1'
- FunctionsToExport = @('Test-Exists', 'Assert-Exists')
+ FunctionsToExport = @('Test-Exist', 'Assert-Exist')
CmdletsToExport = @()
VariablesToExport = '*'
AliasesToExport = @('exists', 'asserts', 'tests')
PrivateData = @{
PSData = @{
- Tags = @('Validation', 'Object', 'Fluent', 'Helper', 'Assert', 'Test', 'Exists')
+ Tags = @('PSEdition_Desktop', 'PSEdition_Core', 'Windows', 'Linux', 'MacOS', 'Validation', 'Object', 'Fluent', 'Helper', 'Assert', 'Test', 'Exists')
LicenseUri = 'https://github.com/pwshdevs/PSFluentObjectValidation/blob/main/LICENSE'
ProjectUri = 'https://github.com/pwshdevs/PSFluentObjectValidation'
- ReleaseNotes = ''
+ ReleaseNotes = 'https://github.com/pwshdevs/PSFluentObjectValidation/blob/main/CHANGELOG.md'
}
}
}
diff --git a/PSFluentObjectValidation/Private/PSFluentObjectValidation.ps1 b/PSFluentObjectValidation/Private/PSFluentObjectValidation.ps1
index 39bfc7e..742c7a6 100644
--- a/PSFluentObjectValidation/Private/PSFluentObjectValidation.ps1
+++ b/PSFluentObjectValidation/Private/PSFluentObjectValidation.ps1
@@ -28,7 +28,7 @@ public static class PSFluentObjectValidation
if (inputObject == null)
throw new ArgumentException("InputObject cannot be null");
- if (string.IsNullOrEmpty(key))
+ if (String.IsNullOrEmpty(key))
throw new ArgumentException("Key cannot be null or empty");
string[] keyParts = key.Split('.');
@@ -42,6 +42,13 @@ public static class PSFluentObjectValidation
private static object ProcessKeyPart(object currentObject, string part)
{
+ // Handle wildcard array wrapper specially
+ if (currentObject is WildcardArrayWrapper)
+ {
+ WildcardArrayWrapper wrapper = (WildcardArrayWrapper)currentObject;
+ return ProcessWildcardPropertyAccess(wrapper.ArrayObject, part);
+ }
+
// Check for array indexing: property[index] or property[*]
Match arrayMatch = ArrayIndexPattern.Match(part);
if (arrayMatch.Success)
@@ -225,6 +232,112 @@ public static class PSFluentObjectValidation
private static object ProcessWildcardPropertyAccess(object arrayObject, string propertyName)
{
+ // First check if this is an array indexing pattern: property[index] or property[*]
+ Match arrayMatch = ArrayIndexPattern.Match(propertyName);
+ if (arrayMatch.Success)
+ {
+ string basePropertyName = arrayMatch.Groups[1].Value;
+ string indexStr = arrayMatch.Groups[2].Value;
+
+ // Handle array indexing after wildcard: items[0], tags[*], etc.
+ if (arrayObject is Array)
+ {
+ Array array = (Array)arrayObject;
+ for (int i = 0; i < array.Length; i++)
+ {
+ object element = array.GetValue(i);
+ if (element == null)
+ throw new InvalidOperationException(String.Format("Array element [{0}] is null", i));
+
+ if (!HasProperty(element, basePropertyName))
+ throw new InvalidOperationException(String.Format("Array element [{0}] does not have property '{1}'", i, basePropertyName));
+
+ object propertyValue = GetProperty(element, basePropertyName);
+ if (propertyValue == null)
+ throw new InvalidOperationException(String.Format("Property '{0}' in element [{1}] is null", basePropertyName, i));
+ if (!IsArrayLike(propertyValue))
+ throw new InvalidOperationException(String.Format("Property '{0}' in element [{1}] is not an array", basePropertyName, i));
+ }
+
+ // All elements are valid, now handle the indexing
+ object firstElement = array.GetValue(0);
+ object firstPropertyValue = GetProperty(firstElement, basePropertyName);
+
+ if (indexStr == "*")
+ {
+ return new WildcardArrayWrapper(firstPropertyValue);
+ }
+ else
+ {
+ int index = int.Parse(indexStr);
+ int count = GetCount(firstPropertyValue);
+ if (index < 0 || index >= count)
+ throw new InvalidOperationException(String.Format("Array index [{0}] is out of bounds for property '{1}' (length: {2})", index, basePropertyName, count));
+
+ if (firstPropertyValue is Array)
+ {
+ Array firstArray = (Array)firstPropertyValue;
+ return firstArray.GetValue(index);
+ }
+ if (firstPropertyValue is IList)
+ {
+ IList firstList = (IList)firstPropertyValue;
+ return firstList[index];
+ }
+ }
+ }
+
+ if (arrayObject is IList)
+ {
+ IList list = (IList)arrayObject;
+ for (int i = 0; i < list.Count; i++)
+ {
+ object element = list[i];
+ if (element == null)
+ throw new InvalidOperationException(String.Format("Array element [{0}] is null", i));
+ if (!HasProperty(element, basePropertyName))
+ throw new InvalidOperationException(String.Format("Array element [{0}] does not have property '{1}'", i, basePropertyName));
+
+ object propertyValue = GetProperty(element, basePropertyName);
+ if (propertyValue == null)
+ throw new InvalidOperationException(String.Format("Property '{0}' in element [{1}] is null", basePropertyName, i));
+ if (!IsArrayLike(propertyValue))
+ throw new InvalidOperationException(String.Format("Property '{0}' in element [{1}] is not an array", basePropertyName, i));
+ }
+
+ // All elements are valid, now handle the indexing
+ object firstElement = list[0];
+ object firstPropertyValue = GetProperty(firstElement, basePropertyName);
+
+ if (indexStr == "*")
+ {
+ return new WildcardArrayWrapper(firstPropertyValue);
+ }
+ else
+ {
+ int index = int.Parse(indexStr);
+ int count = GetCount(firstPropertyValue);
+ if (index < 0 || index >= count)
+ throw new InvalidOperationException(String.Format("Array index [{0}] is out of bounds for property '{1}' (length: {2})", index, basePropertyName, count));
+
+ if (firstPropertyValue is Array)
+ {
+ Array firstArray = (Array)firstPropertyValue;
+ return firstArray.GetValue(index);
+ }
+ if (firstPropertyValue is IList)
+ {
+ IList firstList = (IList)firstPropertyValue;
+ return firstList[index];
+ }
+ }
+ }
+
+ throw new InvalidOperationException(String.Format("Cannot process wildcard array indexing on type {0}", arrayObject.GetType().Name));
+ }
+
+
+
// Parse validation suffix if present
Match validationMatch = PropertyWithValidation.Match(propertyName);
string actualPropertyName = propertyName;
diff --git a/PSFluentObjectValidation/Public/Assert-Exist.ps1 b/PSFluentObjectValidation/Public/Assert-Exist.ps1
index e634d39..e6a2380 100644
--- a/PSFluentObjectValidation/Public/Assert-Exist.ps1
+++ b/PSFluentObjectValidation/Public/Assert-Exist.ps1
@@ -1,14 +1,54 @@
+<#
+.SYNOPSIS
+Asserts the existence and validity of a property within an object.
+
+.DESCRIPTION
+The `Assert-Exist` function validates the existence of a property within an object and ensures it meets the specified validation criteria. If the validation fails, it throws a detailed exception, making it suitable for scenarios where strict validation is required.
+
+.PARAMETER InputObject
+The object to validate. This can be a hashtable, PSObject, .NET object, or any other object type.
+
+.PARAMETER Key
+The property path to validate. Supports fluent syntax with validation operators:
+- `property.nested` - Basic navigation
+- `property!` - Non-empty validation (rejects null/empty/whitespace)
+- `property?` - Existence validation (allows null values)
+- `array[0]` - Array indexing
+- `array[*]` - Wildcard validation (all elements must pass)
+
+.EXAMPLE
+Assert-Exist -InputObject $data -Key "user.name!"
+
+Asserts that the `user.name` property exists and is non-empty
+.EXAMPLE
+Assert-Exist -InputObject $data -Key "users[*].email!"
+
+Asserts that all users in the array have a non-empty email
+.EXAMPLE
+Assert-Exist -InputObject $data -Key "settings.theme"
+
+Asserts that the `settings.theme` property exists
+.NOTES
+Throws an exception if the validation fails. Use `Test-Exist` for a non-throwing alternative.
+
+.LINK
+https://www.pwshdevs.com/
+#>
function Assert-Exist {
param(
[Parameter(Mandatory=$true)]
[Alias('In')]
$InputObject,
[Parameter(Mandatory=$true, ValueFromPipeline = $true)]
- [Alias('Width', 'Test')]
+ [Alias('With', 'Test')]
[string]$Key
)
- [PSFluentObjectValidation]::AssertExists($InputObject, $Key)
+ Begin { }
+
+ Process {
+ [PSFluentObjectValidation]::AssertExists($InputObject, $Key)
+ }
}
New-Alias -Name asserts -Value Assert-Exist
diff --git a/PSFluentObjectValidation/Public/Test-Exist.ps1 b/PSFluentObjectValidation/Public/Test-Exist.ps1
index 96540e4..ea80bca 100644
--- a/PSFluentObjectValidation/Public/Test-Exist.ps1
+++ b/PSFluentObjectValidation/Public/Test-Exist.ps1
@@ -1,3 +1,39 @@
+<#
+.SYNOPSIS
+Tests the existence and validity of a property within an object.
+
+.DESCRIPTION
+The `Test-Exist` function validates the existence of a property within an object and ensures it meets the specified validation criteria. Unlike `Assert-Exist`, this function does not throw exceptions; instead, it returns a boolean value indicating whether the validation passed.
+
+.PARAMETER InputObject
+The object to validate. This can be a hashtable, PSObject, .NET object, or any other object type.
+
+.PARAMETER Key
+The property path to validate. Supports fluent syntax with validation operators:
+- `property.nested` - Basic navigation
+- `property!` - Non-empty validation (rejects null/empty/whitespace)
+- `property?` - Existence validation (allows null values)
+- `array[0]` - Array indexing
+- `array[*]` - Wildcard validation (all elements must pass)
+
+.EXAMPLE
+Test-Exist -InputObject $data -Key "user.name!"
+
+Tests that the `user.name` property exists and is non-empty.
+.EXAMPLE
+Test-Exist -InputObject $data -Key "users[*].email!"
+
+Tests that all users in the array have a non-empty email.
+.EXAMPLE
+Test-Exist -InputObject $data -Key "settings.theme"
+
+Tests that the `settings.theme` property exists.
+.NOTES
+Returns `$true` if the validation passes, `$false` otherwise. Use `Assert-Exist` for a throwing alternative.
+
+.LINK
+https://www.pwshdevs.com/
+#>
Function Test-Exist {
param(
[Parameter(Mandatory=$true)]
@@ -8,7 +44,10 @@ Function Test-Exist {
[string]$Key
)
- return [PSFluentObjectValidation]::TestExists($InputObject, $Key)
+ Begin { }
+ Process {
+ return [PSFluentObjectValidation]::TestExists($InputObject, $Key)
+ }
}
New-Alias -Name exists -Value Test-Exist
diff --git a/README.md b/README.md
index fcb5f50..6a224b7 100644
--- a/README.md
+++ b/README.md
@@ -6,29 +6,27 @@
**PSGallery**
-[![PowerShell Gallery][psgallery-badge]][psgallery] [![PSGallery Version][psgallery-version-badge]][psgallery]
+[![PowerShell Gallery][psgallery-badge]][psgallery] [![PSGallery Version][psgallery-version-badge]][psgallery] [![PSGallery Playform][psgallery-platform-badge]][psgallery] [![PSGallery Playform][ps-desktop-badge]][psgallery]
## General Overview
-The PSFluentObjectValidation represents a comprehensive evolution from simple property validation to a high-performance, feature-rich validation engine with advanced array indexing capabilities.
-
## Installation
```powershell
Install-PSResource -Name PSFluentObjectValidation
-# or
+```
+
+or
+
+```powershell
Install-Module -Name PSFluentObjectValidation
+```
-# Verify installation (long style)
-Test-Exists -in @{ test = "value" } -with "test!"
-# (short hand)
-exists -with "test!" -in @{ test = "value" }
-# or alternative
-tests -with "test!" -in @{ test = "value" }
-# or if you'd rather have thrown errors
-Assert-Exists -in @{ test = "value" } -with "test!"
-# or you can use a short hand version
-asserts -with "test!" -in @{ test = "value" }
+Once installed, you can run a couple of tests to verify it works as expected:
+
+```powershell
+# Verify installation
+Test-Exist -In @{ test = "value" } -With "test!"
# Should return: True
```
@@ -37,18 +35,23 @@ asserts -with "test!" -in @{ test = "value" }
### Basic Property Access
```powershell
-exists -with "user.name" -in $data # Simple property
-exists -with "user.profile.age" -in $data # Nested objects
-exists -with "config.db.host" -in $data # Deep nesting
+Test-Exist -With "user.name" -In $data # Simple property
+Test-Exist -With "user.profile.age" -In $data # Nested objects
+Test-Exist -With "config.db.host" -In $data # Deep nesting
```
-As an example, you would normally test this like so
+As an example, you would normally test this like so:
+
```powershell
-if(-not $data.user -or -not $data.user.name -or -not $data.user.profile -or -not $data.user.profile.age) {
+if(-not $data.user -or -not $data.user.name -or [string]::IsNullOrWhitespace($data.user.name) -or -not $data.user.profile -or -not $data.user.profile.age) {
# do something when any of these fields do not exist
}
-# vs. PSFluentObjectValidation
-if(-not (exists -with "user.name" -in $data) -or -not (exists -with "user.profile.age" -in $data)) {
+```
+
+vs. PSFluentObjectValidation
+
+```powershell
+if(-not (Test-Exist -With "user.name!" -In $data) -or -not (Test-Exist -With "user.profile.age" -In $data)) {
# do something when any of these fields do not exist
}
```
@@ -57,33 +60,33 @@ if(-not (exists -with "user.name" -in $data) -or -not (exists -with "user.profil
### Validation Suffixes
```powershell
-exists -with "user.email!" -in $data # Non-empty validation
-exists -with "user.profile?" -in $data # Object existence
-exists -with "settings.theme!" -in $data # Non-empty string
+Test-Exist -With "user.email!" -In $data # Non-empty validation
+Test-Exist -With "user.profile?" -In $data # Object existence
+Test-Exist -With "settings.theme!" -In $data # Non-empty string
```
### Array Indexing
```powershell
-exists -with "users[0].name" -in $data # First element
-exists -with "users[1].email" -in $data # Second element
-exists -with "products[2].title" -in $data # Third element
+Test-Exist -With "users[0].name" -In $data # First element
+Test-Exist -With "users[1].email" -In $data # Second element
+Test-Exist -With "products[2].title" -In $data # Third element
```
### Wildcard Array Validation
```powershell
-exists -with "users[*].name" -in $data # All users have names
-exists -with "users[*].email!" -in $data # All users have non-empty emails
-exists -with "products[*].active" -in $data # All products have active property
+Test-Exist -With "users[*].name" -In $data # All users have names
+Test-Exist -With "users[*].email!" -In $data # All users have non-empty emails
+Test-Exist -With "products[*].active" -In $data # All products have active property
```
### Advanced Combinations
```powershell
-exists -with "users[0].profile.settings.theme!" -in $data # Deep + validation
-exists -with "products[*].category.name!" -in $data # Wildcard + deep + validation
-exists -with "orders[1].items[*].price" -in $data # Nested array access
+Test-Exist -With "users[0].profile.settings.theme!" -In $data # Deep + validation
+Test-Exist -With "products[*].category.name!" -In $data # Wildcard + deep + validation
+Test-Exist -With "orders[1].items[*].price" -In $data # Nested array access
```
## Usage Examples
@@ -105,13 +108,13 @@ $apiResponse = @{
}
# Validate all users have required fields
-exists -in $apiResponse -with "users[*].id" # All users have IDs
-exists -in $apiResponse -with "users[*].name!" # All users have non-empty names
-exists -in $apiResponse -with "users[*].email!" # All users have non-empty emails
+Test-Exist -In $apiResponse -With "users[*].id" # All users have IDs
+Test-Exist -In $apiResponse -With "users[*].name!" # All users have non-empty names
+Test-Exist -In $apiResponse -With "users[*].email!" # All users have non-empty emails
# Validate specific user data
-exists -in $apiResponse -with "users[0].active" # First user has active status
-exists -in $apiResponse -with "metadata.total!" # Metadata has non-empty total
+Test-Exist -In $apiResponse -With "users[0].active" # First user has active status
+Test-Exist -In $apiResponse -With "metadata.total!" # Metadata has non-empty total
```
#### Configuration Validation
@@ -133,10 +136,10 @@ $config = @{
}
# Validate critical configuration
-exists -in $config -with "database.host!" # Non-empty host
-exists -in $config -with "database.credentials.password!" # Non-empty password
-exists -in $config -with "servers[*].name!" # All servers have names
-exists -in $config -with "servers[*].ip!" # All servers have IPs
+Test-Exist -In $config -With "database.host!" # Non-empty host
+Test-Exist -In $config -With "database.credentials.password!" # Non-empty password
+Test-Exist -In $config -With "servers[*].name!" # All servers have names
+Test-Exist -In $config -With "servers[*].ip!" # All servers have IPs
```
#### E-commerce Data Validation
@@ -155,11 +158,11 @@ $order = @{
}
# Comprehensive order validation
-exists -in $order -with "id!" # Order has ID
-exists -in $order -with "customer.email!" # Customer has email
-exists -in $order -with "items[*].sku!" # All items have SKUs
-exists -in $order -with "items[*].price" # All items have prices
-exists -in $order -with "items[0].quantity" # First item has quantity
+Test-Exist -In $order -With "id!" # Order has ID
+Test-Exist -In $order -With "customer.email!" # Customer has email
+Test-Exist -In $order -With "items[*].sku!" # All items have SKUs
+Test-Exist -In $order -With "items[*].price" # All items have prices
+Test-Exist -In $order -With "items[0].quantity" # First item has quantity
```
## Error Handling & Edge Cases
@@ -170,108 +173,43 @@ exists -in $order -with "items[0].quantity" # First item has quantity
```powershell
# Array bounds checking
-tests -in $data -with "users[10].name" # Returns false for out-of-bounds
-asserts -in $data -with "users[10].name" # Throws: "Array index [10] is out of bounds"
+Test-Exist -In $data -With "users[10].name" # Returns false for out-of-bounds
+Assert-Exist -In $data -With "users[10].name" # Throws: "Array index [10] is out of bounds"
# Null safety
-tests -in $data -with "user.profile.settings" # Handles null intermediate objects
-asserts -in $data -with "missing.property" # Throws: "Property 'missing' does not exist"
+Test-Exist -In $data -With "user.profile.settings" # Handles null intermediate objects
+Assert-Exist -In $data -With "missing.property" # Throws: "Property 'missing' does not exist"
# Type validation
-tests -in $data -with "config.port[0]" # Throws: "Property 'port' is not an array"
+Test-Exist -In $data -With "config.port[0]" # Throws: "Property 'port' is not an array"
```
#### Wildcard Validation Error Handling
```powershell
# Empty array validation
-Test-Exists @{ users = @() } "users[*].name" # Throws: "Array 'users' is empty"
+Test-Exist -In @{ users = @() } -With "users[*].name" # Throws: "Array 'users' is empty"
# Partial validation failures
-Test-Exists $data "users[*].email!" # Validates ALL users have non-empty emails
-Assert-Exists $data "users[*].phone!" # Throws if ANY user lacks phone
+Test-Exist -In $data -With "users[*].email!" # Validates ALL users have non-empty emails
+Assert-Exist -In $data -With "users[*].phone!" # Throws if ANY user lacks phone
```
### Function Reference
-#### `Test-Exists` or `tests` or `exists`
-
-**Purpose**: Safely test property existence and validation
-**Syntax**: `Test-Exists $object $propertyPath`
-**Returns**: `$true` if validation passes, `$false` otherwise
-**Error Handling**: Never throws exceptions
-
-#### `Assert-Exists` or `asserts`
-
-**Purpose**: Assert property existence with detailed error reporting
-**Syntax**: `Assert-Exists $object $propertyPath`
-**Returns**: `void` (throws on failure)
-**Error Handling**: Throws descriptive exceptions for debugging
-
-### Advanced Configuration
-
-#### Performance Tuning
-
-- **Warmup Iterations**: Recommended 1000+ for consistent measurements
-- **Test Iterations**: 10,000+ for statistical significance
-- **Memory Management**: Automatic garbage collection handling
-
-#### Debugging Support
-
-- **Verbose Error Messages**: Detailed exception information
-- **Stack Trace Preservation**: Full error context maintenance
-- **Development Mode**: Additional validation checks available
-
-## Future Roadmap
-
-### Potential Enhancements
-
-1. **Dynamic Array Slicing**: `users[1:3].name` syntax
-2. **Conditional Validation**: `users[active=true].email!` filtering
-3. **Performance Profiling**: Built-in benchmarking tools
-4. **Visual Studio Code Extension**: IntelliSense support for validation syntax
-
-### Performance Targets
-
-- **Sub-10μs Operations**: Further C# optimization
-- **Parallel Validation**: Multi-threaded wildcard processing
-- **Memory Optimization**: Zero-allocation validation paths
-
-## Performance Results Overview
-
-### Performance Comparison
-
-Originally this module was written in pure powershell but comparing it against just standard manual testing showed it was very slow (in micro seconds, still fairly quick overall). The the module was rewritten to use a different format for testing, which improved performance but it was still relatively slow compared to just manual validation. The module was then implemented in C# and saw significate performance improvements over even manual processing. The module was updated a 4th time to handle arrays as there are times when its better to test before looping with | ForEach-Object. Below is a series of tests written against all 5 methods.
-
-Based on 10,000 iterations with corrected V3-compatible scenario testing:
-
-| Version | Average Performance | vs Manual | Technology | Array Support |
-|---------|-------------------|-----------|------------|---------------|
-| **Manual Validation** | 59.68 μs | ±0% | Native PowerShell | Limited |
-| **V1 (PowerShell)** | 352.48 μs | +491% | Pure PowerShell | ❌ |
-| **V2 (PowerShell)** | 318.71 μs | +434% | Pure PowerShell | ❌ |
-| **V3 (C# Basic)** | **13.06 μs** | **-78.1%** | C# Compiled | ❌ |
-| **V4 (C# Current)** | **13.86 μs** | **-76.8%** | C# + Array Logic | ✅ |
-
-### Performance Highlights
+#### `Test-Exist`
-- **V3**: **78.1% faster** than manual validation - C# performance breakthrough
-- **V4**: **76.8% faster** than manual validation with revolutionary array features
-- **V4 vs V3**: Only **6.1% slower** - excellent trade-off for massive feature expansion
-- **Both C# versions**: **27x faster** than V1, **23x faster** than V2
+> **Purpose Safely**: test property existence and validation
+> **Syntax**: `Test-Exist -In $object -With $propertyPath`
+> **Returns**: `$true` if validation passes, `$false` otherwise
+> **Error Handling**: Never throws exceptions
-## Features & Capabilities Matrix
+#### `Assert-Exist`
-| Feature | V1 | V2 | V3 | V4 | Description |
-|---------|----|----|----|----|-------------|
-| **Basic Properties** | ✅ | ✅ | ✅ | ✅ | `user.name`, `config.host` |
-| **Validation Suffixes** | ✅ | ✅ | ✅ | ✅ | `property!` (non-empty), `property?` (exists) |
-| **Nested Navigation** | ✅ | ✅ | ✅ | ✅ | `user.profile.settings.theme` |
-| **Array Indexing** | ❌ | ❌ | ❌ | ✅ | `users[0].name`, `products[1].title` |
-| **Wildcard Arrays** | ❌ | ❌ | ❌ | ✅ | `users[*].name` (all elements) |
-| **Array + Validation** | ❌ | ❌ | ❌ | ✅ | `users[*].email!` (all non-empty) |
-| **Deep Array Access** | ❌ | ❌ | ❌ | ✅ | `products[0].category.name` |
-| **Error Handling** | Basic | Basic | Enhanced | Advanced | Bounds checking, null safety |
+> **Purpose**: Assert property existence with detailed error reporting
+> **Syntax**: `Assert-Exist -In $object -With $propertyPath`
+> **Returns**: `void` (throws on failure)
+> **Error Handling**: Throws descriptive exceptions for debugging
[github-actions-badge]: https://img.shields.io/github/actions/workflow/status/pwshdevs/PSFluentObjectValidation/CI.yaml?label=build&style=for-the-badge
[github-actions-badge-publish]: https://img.shields.io/github/actions/workflow/status/pwshdevs/PSFluentObjectValidation/publish.yaml?label=publish&style=for-the-badge
@@ -285,3 +223,6 @@ Based on 10,000 iterations with corrected V3-compatible scenario testing:
[github-closed-issues-badge]: https://img.shields.io/github/issues-closed/pwshdevs/PSFluentObjectValidation?style=for-the-badge
[github-closed-issues]: https://github.com/pwshdevs/PSFluentObjectValidation/issues?q=is%3Aissue%20state%3Aclosed
[github-open-issues]: https://github.com/pwshdevs/PSFluentObjectValidation/issues
+[psgallery-platform-badge]: https://img.shields.io/powershellgallery/p/PSFluentObjectValidation?style=for-the-badge
+[ps-desktop-badge]: https://img.shields.io/badge/powershell-5.1,_7.0+-blue?style=for-the-badge
+[ps-core-badge]: https://img.shields.io/badge/powershell-5.1,_7.0+-blue?style=for-the-badge
diff --git a/docs/en-US/Assert-Exist.md b/docs/en-US/Assert-Exist.md
new file mode 100644
index 0000000..c162cdd
--- /dev/null
+++ b/docs/en-US/Assert-Exist.md
@@ -0,0 +1,114 @@
+---
+external help file: PSFluentObjectValidation-help.xml
+Module Name: PSFluentObjectValidation
+online version: https://www.pwshdevs.com/
+schema: 2.0.0
+---
+
+# Assert-Exist
+
+## SYNOPSIS
+Asserts the existence and validity of a property within an object.
+
+## SYNTAX
+
+```
+Assert-Exist [-InputObject]