
Contracted
- 2 installs
- Updated March 16, 2026
- validkeys/contracted
Build type-safe TypeScript services with the @validkeys/contracted contract-first pattern using Zod validation, dependency injection, and tagged error handling via neverthrow.
About
Guides contract-first service development with @validkeys/contracted: defining commands, services, and tagged errors with automatic Zod validation and neverthrow Results. A developer uses it when composing type-safe, dependency-injected TypeScript services.
- Workflow: Errors then Commands then Service Contract then Implementations then Factory
- Covers matchError pattern matching, withDependencies injection, and known Zod async limitations
Contracted by the numbers
- 2 all-time installs (skills.sh)
- Ranked #3,765 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Jul 27, 2026 (Skillselion catalog sync)
npx skills add https://github.com/validkeys/contracted --skill contractedAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2 |
|---|---|
| Last updated | March 16, 2026 |
| Repository | validkeys/contracted ↗ |
What it does
Build type-safe TypeScript services with the @validkeys/contracted contract-first pattern using Zod validation, dependency injection, and tagged error handling via neverthrow.
Files
Contracted — @validkeys/contracted
Contract-first TypeScript library for type-safe, composable services with automatic Zod validation and tagged error handling via neverthrow.
Install: pnpm add @validkeys/contracted neverthrow zod
Workflow order: Errors → Commands → Service Contract → Implementations → Service Factory
---
Step 1: Define Errors
import { defineError } from '@validkeys/contracted';
export const UserNotFoundError = defineError<'USER_NOT_FOUND', { userId: string }>(
'USER_NOT_FOUND',
'User not found'
);- Tag must be
SCREAMING_SNAKE_CASEand unique across the system - Data payload carries context needed for error messages
- Group related errors:
export const writeErrors = [UserAlreadyExistsError, ...] as const
---
Step 2: Define Commands (Contracts)
import { z } from 'zod';
import { defineCommand } from '@validkeys/contracted';
// contracts/infrastructure.ts — shared interfaces, no business logic
export interface UserRepository {
findById: (id: string) => Promise<User | null>;
save: (user: User) => Promise<void>;
}
export const getUserCommand = defineCommand({
input: z.object({ userId: z.string().trim() }),
output: z.object({ id: z.string(), name: z.string(), email: z.string() }),
dependencies: {
userRepo: {} as UserRepository, // typed stub — not a real instance
logger: {} as Logger,
},
options: {} as { includeDeleted?: boolean }, // optional per-call flags
errors: [UserNotFoundError] as const, // must be as const
});
export type GetUserInput = typeof getUserCommand.types.Input;
export type GetUserOutput = typeof getUserCommand.types.Output;---
Step 3: Define Service Contract
import { defineService } from '@validkeys/contracted';
export const userServiceContract = defineService({
getUser: getUserCommand,
createUser: createUserCommand,
});
// Types available immediately — before any implementation exists
export type UserService = typeof userServiceContract.types.Service;
export type UserServiceDeps = typeof userServiceContract.types.Dependencies;
export type UserServiceErrors = typeof userServiceContract.types.Errors;Dependencies is an intersection of all commands' deps — the service factory requires every dep from every command.
---
Step 4: Implement Commands
Recommended: implementation() — auto-wrapped
export const getUser = getUserCommand.implementation(async ({ input, deps, options }) => {
// input is already validated and Zod-transformed before this runs
const user = await deps.userRepo.findById(input.userId);
if (!user) throw new UserNotFoundError({ userId: input.userId }); // caught → err()
return user; // validated against output schema → ok()
// ⚠️ Do NOT return ok(user) — the raw value is expected, not a Result
});ValidationErroris automatically added to the error union- Non-
TaggedErrorthrows are re-thrown, not swallowed
To wrap infrastructure exceptions, use the cause argument:
try {
await deps.userRepo.save(user);
} catch (e) {
throw new UserRepositoryError({ op: 'save' }, 'DB write failed', e);
}Alternative: unsafeImplementation() — explicit Results
import { ok, err } from 'neverthrow';
export const getUser = getUserCommand.unsafeImplementation(async ({ input, deps }) => {
const user = await deps.userRepo.findById(input.userId);
if (!user) return err(new UserNotFoundError({ userId: input.userId }));
return ok(user); // explicit Result required; no automatic validation
});Use when: migrating Result-based code, or needing fine-grained validation control.
---
Step 5: Compose the Service
export const createUserService = userServiceContract.implementation({
getUser,
createUser,
// throws at runtime if any command from the contract is missing
});---
Step 6: Use the Service
// Inject all deps (intersection of every command's dependencies)
const userService = createUserService({
userRepo: new UserRepository(),
logger: new Logger(),
});
// Service commands: run(input, options?)
const result = await userService.getUser.run({ userId: '123' });
if (result.isOk()) {
console.log(result.value);
} else {
// result.error = discriminated union of declared errors + ValidationError
}Standalone contract (before service composition) uses a different signature:
// run({ input, deps, options? }) — context object, not positional
const result = await getUser.run({ input: { userId: '123' }, deps: { userRepo } });---
Error Handling
matchError() — exhaustive pattern matching (preferred)
import { matchError } from '@validkeys/contracted';
if (result.isErr()) {
return matchError(result.error, {
VALIDATION_ERROR: (e) => ({ status: 400, errors: e.data.errors }),
USER_NOT_FOUND: (e) => ({ status: 404, message: `No user: ${e.data.userId}` }),
});
}switch on _tag — with exhaustiveness check
if (result.isErr()) {
switch (result.error._tag) {
case 'VALIDATION_ERROR':
// .data.phase = 'input' | 'output'
// .data.errors = simplified array
// .data.zodError = full ZodError (.format(), .flatten(), .issues)
break;
case 'USER_NOT_FOUND': break;
default:
const _: never = result.error;
}
}---
Without Service Contracts (Simple Composition)
import { serviceFrom, serviceFromSimple } from '@validkeys/contracted';
// Full metadata — commands called via .run(input, options?)
const userService = serviceFrom({ getUser, createUser })(deps);
const result = await userService.getUser.run({ userId: '123' });
// Execution only — commands ARE the function, no .run()
const userService = serviceFromSimple({ getUser, createUser })(deps);
const result = await userService.getUser({ userId: '123' }); // ← no .run()---
withDependencies — Pre-inject Dependencies
// Returns CurriedImplementation: (input, options?) => Promise<Result<...>>
const getUserWithDeps = getUser.withDependencies({ userRepo, logger });
const result = await getUserWithDeps({ userId: '123' });Useful for testing individual commands without a full service factory.
---
Type Extraction
// From a command
type Input = typeof myCommand.types.Input;
type Output = typeof myCommand.types.Output;
type Deps = typeof myCommand.types.Dependencies;
// From a service contract
type Service = typeof myServiceContract.types.Service;
type ServiceDeps = typeof myServiceContract.types.Dependencies;
// From serviceFrom collections
import type { ServiceDependencies, ServiceErrors } from '@validkeys/contracted';
type Deps = ServiceDependencies<typeof commandMap>;
type Errors = ServiceErrors<typeof commandMap>;---
Manual Validation
validateInput and validateOutput are available on both standalone contracts and service commands. Both throw ZodError on failure.
const validInput = myCommand.validateInput(rawData);
const validInput = userService.getUser.validateInput(rawData);---
Recommended File Structure
src/
├── contracts/ # No business logic
│ ├── infrastructure.ts # Shared interfaces (Logger, Repository, etc.)
│ └── UserManager/
│ ├── errors.ts # defineError calls
│ ├── contracts.ts # defineCommand calls
│ ├── service.ts # defineService + exported types
│ └── index.ts
└── UserManager/ # Imports from contracts, never the reverse
├── commands/
│ ├── getUser.ts
│ └── createUser.ts
├── service.ts # serviceContract.implementation({...})
└── index.ts---
Known Limitations
- Async Zod refinements are not supported.
z.string().refine(async () => ...)silently passes because the library uses.safeParse()(sync). Validate async invariants inside the implementation body and throw aTaggedError. - `defineContract` is deprecated. Alias for
defineCommand— usedefineCommandin all new code.
---
When NOT to Use
- Simple utility functions with no dependencies or typed errors
- One-off scripts
- Functions that cannot fail and need no dependency injection
{
"$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
Changesets
Hello and welcome! This folder has been automatically generated by @changesets/cli, a build tool that works with multi-package repos, or single-package repos to help you version and publish your code. You can find the full documentation for it in our repository
We have a quick list of common questions to get you started engaging with this project in our documentation
{
"permissions": {
"allow": [
"Bash(pnpm add:*)",
"Bash(pnpm changeset:*)",
"Bash(pnpm build:*)",
"Bash(npm run build:*)",
"Bash(npx tsx:*)",
"Bash(npx tsc:*)",
"Bash(pnpm test:*)",
"Bash(pnpm version:*)",
"Bash(sed:*)",
"Bash(npm test)",
"Bash(git add:*)",
"Bash(git commit:*)"
],
"deny": [],
"ask": []
}
}# Node.js
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
dist/
build/
.env
.env.local
.env.*.local
# Logs
logs
*.log
debug.log
error.log
# OS
.DS_Store
Thumbs.db
# IDEs
.vscode/
.idea/
*.sublime-workspace
*.sublime-project
# Coverage
coverage/
*.lcov
# Misc
*.tgz
.cache/
.parcel-cache/
.next/
out/@validkeys/contracted
4.1.1
Patch Changes
- Fix service composition type to include ValidationError
Fixed a type mismatch in defineService() that prevented service composition from working with implementations created via .implementation(). The ImplementedContractsFrom type now correctly includes ValidationError in the error union to match the documented behavior where .implementation() automatically adds ValidationError for input/output validation.
What Changed:
- Service composition now accepts implementations created with
.implementation()without type errors - The type system now accurately reflects runtime behavior (all implementations can throw
ValidationError) - No workarounds needed - pass implementations directly to
serviceContract.implementation()
Before (Type Error):
const getUser = getUserCommand.implementation(async ({ input, deps }) => {
// Returns ImplementedContract with [...errors, ValidationError]
return user;
});
const createService = userServiceContract.implementation({
getUser, // ❌ Type error: ValidationError not in expected error union
});After (Works Correctly):
const getUser = getUserCommand.implementation(async ({ input, deps }) => {
return user;
});
const createService = userServiceContract.implementation({
getUser, // ✅ Works seamlessly - ValidationError is expected
});Migration Guide: No migration needed - this fix is backward compatible. Code using workarounds (type assertions, unsafeImplementation(), or manually adding ValidationError to contracts) will continue to work. You can now simplify such code to use the standard .implementation() pattern.
4.1.0
Minor Changes
- ad1f5c6: Add full ZodError instance to ValidationError for advanced error handling
ValidationError now includes the complete zodError field alongside the existing simplified errors array. This enhancement provides full access to Zod's rich error information while maintaining backward compatibility.
New Features:
ValidationError.data.zodError: Complete ZodError instance with all Zod methods- Access to
format(),flatten(), andformErrors()methods - Full details for nested errors, union errors, and refinements
- Backward compatible - existing
errorsarray continues to work
Usage:
if (result.isErr() && result.error._tag === "VALIDATION_ERROR") {
// Simplified errors (backward compatible)
result.error.data.errors.forEach((e) => console.log(e.path, e.message));
// Full ZodError with methods
const formatted = result.error.data.zodError.format();
const flattened = result.error.data.zodError.flatten();
}Migration Guide: No migration needed - this is a backward compatible addition. Existing code using result.error.data.errors continues to work unchanged.
4.0.0
Major Changes
- BREAKING CHANGE: Automatic input/output validation in `implementation()` method
The implementation() method now automatically validates inputs and outputs using the defined Zod schemas. This aligns the actual behavior with the documented "automatic validation" feature and ensures runtime type safety.
What Changed
- Input validation: Inputs are validated and transformed BEFORE your implementation runs
- Output validation: Outputs are validated and transformed BEFORE returning success
- ValidationError added: A new
VALIDATION_ERRORtype is automatically added to all contracts usingimplementation() - Zod transformations applied:
.trim(),.default(),.transform(), etc. are now applied automatically - unsafeImplementation unchanged: The
unsafeImplementation()method skips validation (escape hatch)
Migration Guide
Option 1: Embrace automatic validation (Recommended)
// Before (v3.x): Manual validation required
const getUser = getUserCommand.implementation(async ({ input, deps }) => {
const validInput = getUserCommand.validateInput(input); // Manual
const user = await deps.userRepo.findById(validInput.userId);
if (!user) {
throw new UserNotFoundError({ userId: input.userId });
}
return user;
});
// After (v4.0.0): Automatic validation
const getUser = getUserCommand.implementation(async ({ input, deps }) => {
// input is already validated and transformed
const user = await deps.userRepo.findById(input.userId);
if (!user) {
throw new UserNotFoundError({ userId: input.userId });
}
return user;
});
// Handle validation errors
const result = await getUser.run({ input, deps });
if (result.isErr()) {
switch (result.error._tag) {
case "VALIDATION_ERROR":
console.error(
"Invalid:",
result.error.data.phase,
result.error.data.errors
);
break;
case "USER_NOT_FOUND":
console.error("Not found:", result.error.data.userId);
break;
}
}Option 2: Use unsafeImplementation() for full control
// No automatic validation - same behavior as v3.x
const getUser = getUserCommand.unsafeImplementation(
async ({ input, deps }) => {
const validInput = getUserCommand.validateInput(input); // Manual
const user = await deps.userRepo.findById(validInput.userId);
if (!user) {
return err(new UserNotFoundError({ userId: input.userId }));
}
return ok(user);
}
);Breaking Changes
1. ValidationError in error union: All contracts using implementation() now include VALIDATION_ERROR in their error union. Update exhaustive error matching to handle this case.
2. Input transformations applied: Implementations receive Zod-transformed inputs (trimmed strings, coerced numbers, etc.). If you relied on raw untransformed input, switch to unsafeImplementation().
3. Output validation required: Implementations must return valid outputs matching the output schema. Invalid outputs will return VALIDATION_ERROR instead of passing through.
New ValidationError Type
{
_tag: 'VALIDATION_ERROR',
data: {
phase: 'input' | 'output', // Which validation failed
errors: Array<{
path: (string | number)[], // Field path (e.g., ['user', 'email'])
message: string, // Error message
code: string // Zod error code
}>,
message: string // Human-readable summary
}
}Benefits
- ✅ Runtime type safety - invalid data never reaches your implementation
- ✅ Automatic Zod transformations - defaults, trims, coercions work automatically
- ✅ Cleaner code - no manual validation boilerplate
- ✅ Consistent behavior - validation happens in one place
- ✅ Better error messages - detailed validation errors with field paths
3.0.4
Patch Changes
- Fix MergeDependencies type to create intersection instead of union (fixes #5)
The MergeDependencies type was incorrectly creating a union of dependencies from different service commands instead of an intersection. This allowed TypeScript to accept incomplete dependency objects at service initialization, leading to runtime errors that should have been caught at compile time.
What changed:
- Added
UnionToIntersectionutility type - Fixed
MergeDependenciesto properly merge all dependencies using intersection - Added regression test using
expect-typeto prevent this bug from reoccurring
Impact: Services with multiple commands that have different dependency requirements will now correctly require ALL dependencies from ALL commands. Code that was previously compiling with incomplete dependencies will now show TypeScript errors, preventing runtime crashes.
Example:
// Before (incorrect): Union allowed partial deps
type Deps = { db; serviceA; logger } | { db; logger };
// TypeScript accepted: { db, logger } ❌
// After (correct): Intersection requires all deps
type Deps = { db; serviceA; logger } & { db; logger };
// Simplified to: { db, serviceA, logger } ✅3.0.3
Patch Changes
- Fix missing unsafeImplementation method in published types - rebuilt dist folder to include latest TypeScript definitions
3.0.2
Patch Changes
- ad76d70: Fix repository and homepage URLs to point to validkeys organization
3.0.1
Patch Changes
- f014a4f: Add defineContract tests and improve type definitions
3.0.0
Major Changes
- BREAKING CHANGE: Rename
defineContracttodefineCommandfor better naming consistency
- New primary API:
defineCommandreplacesdefineContractfor defining individual operations - Consistent naming:
defineCommand+defineServiceprovides clear distinction between individual operations and service collections - Backwards compatibility:
defineContractis still available as a deprecated alias until v4.0.0 - Updated examples: All documentation and examples now use the new
defineCommandAPI - Migration path: Replace
defineContractimports withdefineCommand- functionality is identical
Before:
import { defineContract, defineService } from "@validkeys/contracted";
const getUserContract = defineContract({
/* ... */
});
const serviceContract = defineService({ getUser: getUserContract });After:
import { defineCommand, defineService } from "@validkeys/contracted";
const getUserCommand = defineCommand({
/* ... */
});
const serviceContract = defineService({ getUser: getUserCommand });This change provides better conceptual clarity: Commands define individual operations, Services define collections of operations.
2.0.1
Patch Changes
- Fix Quick Start example to showcase defineService as primary approach
- Updated Quick Start to use
defineServiceinstead ofserviceFrom - Shows complete service contract definition and implementation pattern
- Highlights immediate type availability after service contract definition
- References
serviceFromas alternative for simple applications
The Quick Start now properly demonstrates the recommended approach for most applications.
2.0.0
Major Changes
- 3e7324f: Initial release of Contracted - a TypeScript library for building type-safe, composable services using a contract-first approach.
Features:
- Contract-first service definition with Zod validation
- Type-safe error handling with tagged errors
- Dependency injection support
- Service composition utilities
- Full TypeScript support with comprehensive type inference
Minor Changes
- Add
defineServicefor service contract definition
- New `defineService` function: Creates service contracts that provide types before implementation exists
- Service Contracts: Enable clean separation between contract definition and implementation across packages
- Consistent API: Follows the same
define → implementationpattern as individual contracts - Cross-Package Types: Service types available in contracts folder for other packages to import
- Full Test Coverage: Comprehensive test suite with Vitest
- Documentation: Service contracts documented as core concept alongside command contracts
This enables type-driven development where service interfaces can be defined in a global contracts folder and implemented separately in packages, maintaining clean boundaries and full type safety.
Example Usage:
// contracts/UserManager/service.ts
const serviceContract = defineService({
createUser: createUserContract,
updateUser: updateUserContract,
});
// Types available immediately
export type UserService = typeof serviceContract.types.Service;
// packages/UserManager/service.ts
export const createUserService = serviceContract.implementation({
createUser: createUserImpl,
updateUser: updateUserImpl,
});Implementation Plan: Automatic Input/Output Validation
Overview
Add automatic schema validation for inputs and outputs in the implementation() method before calling user implementations. This ensures type safety at runtime and aligns the actual behavior with the documented "automatic validation" feature.
Version: 4.0.0 (Breaking Change) Approach: Test-Driven Development (TDD)
Current State
defineCommandcreates contracts with input/output Zod schemasvalidateInput()andvalidateOutput()utility methods exist but must be called manually- Documentation claims "automatic validation" but implementation doesn't validate automatically
- The
run()method passes input directly to implementations without validation
Files affected:
src/core/defineContract.ts:246-277-implementation()methodsrc/core/defineContract.ts:80-83-ImplementedContractinterfacesrc/core/types.ts- Type definitionssrc/core/errors.ts- New validation error type
Design Decisions (Based on User Input)
1. Validation Failure Behavior
Decision: Return err() with ZodError wrapped in the Result type
Rationale:
- Consistent with neverthrow pattern
- Allows type-safe error handling
- Validation errors become part of the contract's error union
2. Output Validation
Decision: Also validate outputs automatically
Rationale:
- Catches implementation bugs early
- Ensures contract compliance
- Provides runtime type safety guarantees
3. Method Coverage
Decision: Only apply to implementation(), skip unsafeImplementation()
Rationale:
unsafeImplementation()is for users who want full control- Keeps advanced escape hatch available
- Clear separation: safe (validated) vs unsafe (manual)
4. Breaking Change Strategy
Decision: Breaking change in v4.0.0
Rationale:
- Aligns behavior with documentation
- Removes inconsistency
- Clear version boundary for migration
Error Type Design
Create a new ValidationError tagged error type:
export const ValidationError = defineError<
'VALIDATION_ERROR',
{
phase: 'input' | 'output';
errors: z.ZodError['errors'];
message: string;
}
>('VALIDATION_ERROR', 'Schema validation failed');Key properties:
phase: Whether input or output validation failederrors: Raw Zod error details for debuggingmessage: Human-readable error summary
Note: This error will be automatically added to all contracts, similar to how other system-level errors work.
TDD Test Plan
Phase 1: Write Failing Tests (Red)
Test Suite 1: Input Validation Tests
File: src/core/defineContract.test.ts
1. Test: should validate input before calling implementation
- Given: Command with strict input schema (e.g., email, min length)
- When: Call
run()with invalid input - Then: Return
err()withVALIDATION_ERRORcontaining Zod details - Assert: Implementation function is never called
2. Test: should pass validated input to implementation when valid
- Given: Command with input schema
- When: Call
run()with valid input - Then: Implementation receives the validated, parsed input
- Assert: Zod transformations are applied (e.g., string trim, coercion)
3. Test: should include detailed Zod errors in validation failure
- Given: Command with multiple validation rules
- When: Call
run()with multiple validation failures - Then: Error includes all Zod error details (field paths, messages)
- Assert: phase === 'input'
Test Suite 2: Output Validation Tests
File: src/core/defineContract.test.ts
4. Test: should validate output after implementation succeeds
- Given: Command with strict output schema
- When: Implementation returns invalid output
- Then: Return
err()withVALIDATION_ERROR(phase: 'output') - Assert: Implementation was called but output rejected
5. Test: should pass valid output through successfully
- Given: Command with output schema
- When: Implementation returns valid output
- Then: Return
ok()with validated output - Assert: Zod transformations are applied to output
6. Test: should validate output even when implementation throws TaggedError
- Given: Command with output schema
- When: Implementation throws a business error (e.g., UserNotFoundError)
- Then: Return
err()with the business error (no output validation) - Assert: Output validation skipped for error paths
Test Suite 3: Method Coverage Tests
File: src/core/defineContract.test.ts
7. Test: unsafeImplementation() should NOT validate input
- Given: Command with strict input schema
- When: Call
unsafeImplementation()with invalid input - Then: Implementation receives raw invalid input
- Assert: No validation error thrown
8. Test: unsafeImplementation() should NOT validate output
- Given: Command with strict output schema
- When:
unsafeImplementation()returns invalid output - Then: Invalid output passes through
- Assert: No validation error
Test Suite 4: Service Integration Tests
File: src/core/defineService.test.ts
9. Test: service commands should validate inputs
- Given: Service with command using
implementation() - When: Call service command with invalid input
- Then: Return validation error
- Assert: Service-level behavior consistent with command-level
10. Test: service commands should validate outputs
- Given: Service with command returning invalid output
- When: Implementation returns malformed data
- Then: Return output validation error
Test Suite 5: Type Safety Tests
File: src/core/defineContract.test.ts
11. Test: ValidationError should be in error union type
- Given: Command with defined errors [UserNotFoundError]
- When: Check types.Error type
- Then: Type includes ValidationError | UserNotFoundError
- Assert: TypeScript compilation with exhaustive matching
12. Test: validateInput() method should still work
- Given: Implemented command
- When: Call
validateInput()manually - Then: Still validates and throws ZodError on failure
- Assert: Backward compatibility for manual validation
Test Suite 6: Edge Cases
File: src/core/defineContract.test.ts
13. Test: should handle async validation errors
- Given: Command with async Zod refinements
- When: Async validation fails
- Then: Return validation error after async check
14. Test: should preserve Zod transformations
- Given: Schema with
.transform()or.default() - When: Run with valid input
- Then: Transformed values passed to implementation
15. Test: should handle nested object validation
- Given: Schema with nested objects and arrays
- When: Nested field is invalid
- Then: Error includes full path (e.g., "user.address.zipCode")
Phase 2: Implement (Green)
After writing all failing tests, implement the feature to make tests pass.
Phase 3: Refactor (Refactor)
Clean up implementation while keeping tests green.
Implementation Steps
Step 1: Create ValidationError Type
File: src/core/errors.ts
/**
* System-level error thrown when input or output validation fails
*/
export const ValidationError = defineError<
'VALIDATION_ERROR',
{
phase: 'input' | 'output';
errors: Array<{
path: (string | number)[];
message: string;
code: string;
}>;
message: string;
}
>('VALIDATION_ERROR', 'Schema validation failed');
// Helper to convert ZodError to ValidationError
export function zodErrorToValidationError(
zodError: z.ZodError,
phase: 'input' | 'output'
): InstanceType<typeof ValidationError> {
return new ValidationError({
phase,
errors: zodError.errors.map(e => ({
path: e.path,
message: e.message,
code: e.code,
})),
message: `${phase} validation failed: ${zodError.errors.map(e => e.message).join(', ')}`,
});
}Step 2: Update ImplementedContract Interface
File: src/core/defineContract.ts:36-84
Update the TErrors type to always include ValidationError:
export interface ImplementedContract<
TInput extends z.ZodType,
TOutput extends z.ZodType,
TDeps extends Record<string, any>,
TOptions extends Record<string, any> = Record<string, never>,
TErrors extends ReadonlyArray<new (...args: any[]) => TaggedError> = []
> {
schemas: {
input: TInput;
output: TOutput;
};
types: {
Input: InferSchema<TInput>;
Output: InferSchema<TOutput>;
Dependencies: TDeps;
Options: TOptions;
// ValidationError is automatically included in all contracts
Error: ErrorUnion<[...TErrors, typeof ValidationError]>;
Implementation: ImplementationFunction<
InferSchema<TInput>,
InferSchema<TOutput>,
TDeps,
TOptions,
ErrorUnion<[...TErrors, typeof ValidationError]>
>;
};
errors: [...TErrors, typeof ValidationError]; // Include ValidationError
run: ImplementationFunction<
InferSchema<TInput>,
InferSchema<TOutput>,
TDeps,
TOptions,
ErrorUnion<[...TErrors, typeof ValidationError]>
>;
// ... rest of interface
}Step 3: Implement Validated Wrapper in implementation()
File: src/core/defineContract.ts:246-277
implementation: (impl) => {
const wrappedImpl: ImplementationFunction<
InferSchema<TInput>,
InferSchema<TOutput>,
TDeps,
TOptions,
ErrorUnion<[...TErrors, typeof ValidationError]>
> = async (context) => {
// STEP 1: Validate input before calling implementation
const inputValidation = params.input.safeParse(context.input);
if (!inputValidation.success) {
return err(zodErrorToValidationError(inputValidation.error, 'input'));
}
// Use validated and transformed input
const validatedContext = {
...context,
input: inputValidation.data,
};
try {
// STEP 2: Call implementation with validated input
const result = await impl(validatedContext);
// STEP 3: Validate output before returning success
const outputValidation = params.output.safeParse(result);
if (!outputValidation.success) {
return err(zodErrorToValidationError(outputValidation.error, 'output'));
}
return ok(outputValidation.data);
} catch (error) {
// If it's already a TaggedError, return it as an error
if (error && typeof error === 'object' && '_tag' in error) {
return err(error as ErrorUnion<[...TErrors, typeof ValidationError]>);
}
// Otherwise, re-throw as this is an unexpected error
throw error;
}
};
const implementedContract: ImplementedContract<TInput, TOutput, TDeps, TOptions, [...TErrors, typeof ValidationError]> = {
...contract,
errors: [...errors, ValidationError] as [...TErrors, typeof ValidationError],
run: wrappedImpl,
withDependencies: (deps: TDeps) => {
return (input, options) => wrappedImpl({ input, deps, options });
},
validateInput: (input: unknown) => params.input.parse(input),
validateOutput: (output: unknown) => params.output.parse(output),
};
return implementedContract;
},Step 4: Keep unsafeImplementation() Unchanged
File: src/core/defineContract.ts:279-290
- No changes to
unsafeImplementation() - Users who want manual validation control use this method
- Document this as the "escape hatch" for full control
Step 5: Update Contract Interface
File: src/core/defineContract.ts:111-159
Update the Contract interface to reflect that implementations will include ValidationError:
export interface Contract<
TInput extends z.ZodType,
TOutput extends z.ZodType,
TDeps extends Record<string, any>,
TOptions extends Record<string, any> = Record<string, never>,
TErrors extends ReadonlyArray<new (...args: any[]) => TaggedError> = []
> {
// ... existing fields ...
/** Method to add an implementation to this contract (automatically wraps in neverthrow with validation) */
implementation: (
impl: UnsafeImplementationFunction<
InferSchema<TInput>,
InferSchema<TOutput>,
TDeps,
TOptions
>
) => ImplementedContract<TInput, TOutput, TDeps, TOptions, [...TErrors, typeof ValidationError]>;
/** Method to add an unsafe implementation that requires explicit Result handling (no automatic validation) */
unsafeImplementation: (
impl: ImplementationFunction<
InferSchema<TInput>,
InferSchema<TOutput>,
TDeps,
TOptions,
ErrorUnion<TErrors>
>
) => ImplementedContract<TInput, TOutput, TDeps, TOptions, TErrors>; // No ValidationError added
}Step 6: Update Service Layer
Files: src/core/defineService.ts, src/core/serviceFrom.ts
- Ensure services properly propagate ValidationError type
- Update type definitions to include ValidationError in merged error unions
- Test that service-level composition handles validation errors
Migration Guide
For Users (v4.0.0 Breaking Changes)
What Changed
The implementation() method now automatically validates inputs and outputs using the defined Zod schemas. Validation failures return err() with a VALIDATION_ERROR instead of passing invalid data to your implementation.
Before (v3.x)
const getUser = getUserCommand.implementation(async ({ input, deps }) => {
// input could be invalid - no validation happened
const user = await deps.userRepo.findById(input.userId);
if (!user) {
throw new UserNotFoundError({ userId: input.userId });
}
return user; // output could be invalid - no validation
});
// Users had to manually validate
try {
const validInput = getUserCommand.validateInput(req.body);
const result = await getUser.run({ input: validInput, deps });
} catch (e) {
// Handle validation error
}After (v4.0.0)
const getUser = getUserCommand.implementation(async ({ input, deps }) => {
// input is guaranteed valid and transformed by Zod
const user = await deps.userRepo.findById(input.userId);
if (!user) {
throw new UserNotFoundError({ userId: input.userId });
}
return user; // output will be validated before returning
});
// Validation happens automatically
const result = await getUser.run({ input: req.body, deps });
if (result.isErr()) {
switch (result.error._tag) {
case 'VALIDATION_ERROR':
// Handle validation failure
console.error(`Validation failed in ${result.error.data.phase}:`, result.error.data.errors);
break;
case 'USER_NOT_FOUND':
// Handle business error
break;
}
}Breaking Changes
1. ValidationError in Error Union
- All contracts using
implementation()now includeVALIDATION_ERRORin their error union - Update exhaustive error matching to handle
VALIDATION_ERROR
2. Input Types
- Implementations now receive Zod-transformed inputs (e.g., trimmed strings, coerced numbers)
- If you relied on raw input, switch to
unsafeImplementation()
3. Output Validation
- Implementations must return valid output matching the output schema
- Invalid outputs return
VALIDATION_ERRORinstead of passing through
Migration Options
Option 1: Embrace automatic validation (Recommended)
- Update error handling to include
VALIDATION_ERRORcases - Remove manual
validateInput()calls - Fix implementations that return invalid outputs
Option 2: Use unsafeImplementation() for full control
- Replace
.implementation()with.unsafeImplementation() - Keep manual validation if needed
- No automatic validation, same behavior as v3.x
// No automatic validation
const getUser = getUserCommand.unsafeImplementation(async ({ input, deps }) => {
// Manually validate if you want
const validInput = getUserCommand.validateInput(input);
const user = await deps.userRepo.findById(validInput.userId);
if (!user) {
return err(new UserNotFoundError({ userId: input.userId }));
}
return ok(user);
});Documentation Updates
Files to Update
1. README.md
- Update "Implementation Methods" section (lines 365-440)
- Add ValidationError to error handling examples (lines 500-550)
- Update "Benefits" section - change "Automatic validation" to explicitly mention it happens automatically
2. CHANGELOG.md
- Add v4.0.0 breaking change entry
- Document new behavior
- Link to migration guide
3. src/core/defineContract.ts
- Update JSDoc comments on
implementation()method - Document ValidationError auto-inclusion
- Add examples showing validation errors
4. API Reference in README
- Update
defineCommandreturn type documentation - Add
ValidationErrorto error types section - Document difference between
implementation()andunsafeImplementation()
Testing Strategy
Test Coverage Requirements
- Unit tests: 100% coverage of validation logic
- Integration tests: Full service validation flow
- Type tests: TypeScript compilation with ValidationError
- Error tests: All validation failure scenarios
- Edge cases: Async validation, transformations, nested schemas
Test Execution Order (TDD)
1. Write all 15 failing tests first 2. Run test suite - confirm all tests fail as expected 3. Implement Step 1 (ValidationError) - some tests may pass 4. Implement Step 2 (Interface updates) - type tests pass 5. Implement Step 3 (Validation logic) - remaining tests pass 6. Run full test suite - all tests green 7. Refactor if needed while keeping tests green
Rollback Plan
If critical issues found after release:
1. Quick fix: Add skipValidation: boolean flag to defineCommand 2. Hotfix release: v4.0.1 with opt-out capability 3. Full rollback: Revert to v3.x behavior in v4.1.0 if needed
Success Criteria
- [ ] All 15 TDD tests pass
- [ ] No regression in existing tests
- [ ] Type safety maintained (TypeScript compiles)
- [ ] Documentation fully updated
- [ ] Migration guide complete
- [ ] Example code updated
- [ ] CHANGELOG entry written
- [ ] 100% test coverage for new validation logic
Timeline Estimate
- Phase 1 (Write tests): 2-3 hours
- Phase 2 (Implementation): 3-4 hours
- Phase 3 (Refactor): 1 hour
- Documentation: 2 hours
- Testing & validation: 1-2 hours
Total: 9-12 hours
Questions Resolved
1. ✅ Validation failure behavior: Return err() with ZodError wrapped 2. ✅ Output validation: Yes, validate automatically 3. ✅ Method coverage: Only implementation(), skip unsafeImplementation() 4. ✅ Breaking change: v4.0.0 with clear migration guide
{
"name": "@validkeys/contracted",
"version": "4.1.1",
"description": "TypeScript library for building type-safe, composable services using a contract-first approach",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "tsc",
"test": "vitest",
"test:run": "vitest run",
"test:ui": "vitest --ui",
"changeset": "changeset",
"version": "changeset version",
"release": "pnpm build && changeset publish",
"prepublishOnly": "pnpm build"
},
"keywords": [
"typescript",
"contracts",
"dependency-injection",
"type-safe",
"composable",
"services",
"error-handling",
"zod",
"neverthrow"
],
"author": "",
"repository": {
"type": "git",
"url": "git+https://github.com/validkeys/contracted.git"
},
"bugs": {
"url": "https://github.com/validkeys/contracted/issues"
},
"homepage": "https://github.com/validkeys/contracted#readme",
"license": "ISC",
"packageManager": "pnpm@9.15.5+sha512.845196026aab1cc3f098a0474b64dfbab2afe7a1b4e91dd86895d8e4aa32a7a6d03049e2d0ad770bbe4de023a7122fb68c1a1d6e0d033c7076085f9d5d4800d4",
"peerDependencies": {
"neverthrow": "^8.0.0",
"zod": "^4.0.0"
},
"devDependencies": {
"@changesets/cli": "^2.29.6",
"@types/node": "^24.3.0",
"@vitest/ui": "^3.2.4",
"expect-type": "^1.2.2",
"neverthrow": "^8.2.0",
"tsx": "^4.20.4",
"typescript": "^5.9.2",
"vitest": "^3.2.4",
"zod": "^4.0.17"
}
}
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
devDependencies:
'@changesets/cli':
specifier: ^2.29.6
version: 2.29.6(@types/node@24.3.0)
'@types/node':
specifier: ^24.3.0
version: 24.3.0
'@vitest/ui':
specifier: ^3.2.4
version: 3.2.4(vitest@3.2.4)
expect-type:
specifier: ^1.2.2
version: 1.2.2
neverthrow:
specifier: ^8.2.0
version: 8.2.0
tsx:
specifier: ^4.20.4
version: 4.20.4
typescript:
specifier: ^5.9.2
version: 5.9.2
vitest:
specifier: ^3.2.4
version: 3.2.4(@types/node@24.3.0)(@vitest/ui@3.2.4)(tsx@4.20.4)
zod:
specifier: ^4.0.17
version: 4.0.17
packages:
'@babel/runtime@7.28.3':
resolution: {integrity: sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==}
engines: {node: '>=6.9.0'}
'@changesets/apply-release-plan@7.0.12':
resolution: {integrity: sha512-EaET7As5CeuhTzvXTQCRZeBUcisoYPDDcXvgTE/2jmmypKp0RC7LxKj/yzqeh/1qFTZI7oDGFcL1PHRuQuketQ==}
'@changesets/assemble-release-plan@6.0.9':
resolution: {integrity: sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==}
'@changesets/changelog-git@0.2.1':
resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==}
'@changesets/cli@2.29.6':
resolution: {integrity: sha512-6qCcVsIG1KQLhpQ5zE8N0PckIx4+9QlHK3z6/lwKnw7Tir71Bjw8BeOZaxA/4Jt00pcgCnCSWZnyuZf5Il05QQ==}
hasBin: true
'@changesets/config@3.1.1':
resolution: {integrity: sha512-bd+3Ap2TKXxljCggI0mKPfzCQKeV/TU4yO2h2C6vAihIo8tzseAn2e7klSuiyYYXvgu53zMN1OeYMIQkaQoWnA==}
'@changesets/errors@0.2.0':
resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==}
'@changesets/get-dependents-graph@2.1.3':
resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==}
'@changesets/get-release-plan@4.0.13':
resolution: {integrity: sha512-DWG1pus72FcNeXkM12tx+xtExyH/c9I1z+2aXlObH3i9YA7+WZEVaiHzHl03thpvAgWTRaH64MpfHxozfF7Dvg==}
'@changesets/get-version-range-type@0.4.0':
resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==}
'@changesets/git@3.0.4':
resolution: {integrity: sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==}
'@changesets/logger@0.1.1':
resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==}
'@changesets/parse@0.4.1':
resolution: {integrity: sha512-iwksMs5Bf/wUItfcg+OXrEpravm5rEd9Bf4oyIPL4kVTmJQ7PNDSd6MDYkpSJR1pn7tz/k8Zf2DhTCqX08Ou+Q==}
'@changesets/pre@2.0.2':
resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==}
'@changesets/read@0.6.5':
resolution: {integrity: sha512-UPzNGhsSjHD3Veb0xO/MwvasGe8eMyNrR/sT9gR8Q3DhOQZirgKhhXv/8hVsI0QpPjR004Z9iFxoJU6in3uGMg==}
'@changesets/should-skip-package@0.1.2':
resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==}
'@changesets/types@4.1.0':
resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==}
'@changesets/types@6.1.0':
resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==}
'@changesets/write@0.4.0':
resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==}
'@esbuild/aix-ppc64@0.25.9':
resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.25.9':
resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.25.9':
resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.25.9':
resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.25.9':
resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.25.9':
resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.25.9':
resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.25.9':
resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.25.9':
resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.25.9':
resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.25.9':
resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.25.9':
resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.25.9':
resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.25.9':
resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.25.9':
resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.25.9':
resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.25.9':
resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
'@esbuild/netbsd-arm64@0.25.9':
resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-x64@0.25.9':
resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
'@esbuild/openbsd-arm64@0.25.9':
resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-x64@0.25.9':
resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
'@esbuild/openharmony-arm64@0.25.9':
resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
'@esbuild/sunos-x64@0.25.9':
resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.25.9':
resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.25.9':
resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.25.9':
resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
'@inquirer/external-editor@1.0.1':
resolution: {integrity: sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==}
engines: {node: '>=18'}
peerDependencies:
'@types/node': '>=18'
peerDependenciesMeta:
'@types/node':
optional: true
'@jridgewell/sourcemap-codec@1.5.5':
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
'@manypkg/find-root@1.1.0':
resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==}
'@manypkg/get-packages@1.1.3':
resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==}
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
'@nodelib/fs.stat@2.0.5':
resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
engines: {node: '>= 8'}
'@nodelib/fs.walk@1.2.8':
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
'@polka/url@1.0.0-next.29':
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
'@rollup/rollup-android-arm-eabi@4.48.1':
resolution: {integrity: sha512-rGmb8qoG/zdmKoYELCBwu7vt+9HxZ7Koos3pD0+sH5fR3u3Wb/jGcpnqxcnWsPEKDUyzeLSqksN8LJtgXjqBYw==}
cpu: [arm]
os: [android]
'@rollup/rollup-android-arm64@4.48.1':
resolution: {integrity: sha512-4e9WtTxrk3gu1DFE+imNJr4WsL13nWbD/Y6wQcyku5qadlKHY3OQ3LJ/INrrjngv2BJIHnIzbqMk1GTAC2P8yQ==}
cpu: [arm64]
os: [android]
'@rollup/rollup-darwin-arm64@4.48.1':
resolution: {integrity: sha512-+XjmyChHfc4TSs6WUQGmVf7Hkg8ferMAE2aNYYWjiLzAS/T62uOsdfnqv+GHRjq7rKRnYh4mwWb4Hz7h/alp8A==}
cpu: [arm64]
os: [darwin]
'@rollup/rollup-darwin-x64@4.48.1':
resolution: {integrity: sha512-upGEY7Ftw8M6BAJyGwnwMw91rSqXTcOKZnnveKrVWsMTF8/k5mleKSuh7D4v4IV1pLxKAk3Tbs0Lo9qYmii5mQ==}
cpu: [x64]
os: [darwin]
'@rollup/rollup-freebsd-arm64@4.48.1':
resolution: {integrity: sha512-P9ViWakdoynYFUOZhqq97vBrhuvRLAbN/p2tAVJvhLb8SvN7rbBnJQcBu8e/rQts42pXGLVhfsAP0k9KXWa3nQ==}
cpu: [arm64]
os: [freebsd]
'@rollup/rollup-freebsd-x64@4.48.1':
resolution: {integrity: sha512-VLKIwIpnBya5/saccM8JshpbxfyJt0Dsli0PjXozHwbSVaHTvWXJH1bbCwPXxnMzU4zVEfgD1HpW3VQHomi2AQ==}
cpu: [x64]
os: [freebsd]
'@rollup/rollup-linux-arm-gnueabihf@4.48.1':
resolution: {integrity: sha512-3zEuZsXfKaw8n/yF7t8N6NNdhyFw3s8xJTqjbTDXlipwrEHo4GtIKcMJr5Ed29leLpB9AugtAQpAHW0jvtKKaQ==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm-musleabihf@4.48.1':
resolution: {integrity: sha512-leo9tOIlKrcBmmEypzunV/2w946JeLbTdDlwEZ7OnnsUyelZ72NMnT4B2vsikSgwQifjnJUbdXzuW4ToN1wV+Q==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm64-gnu@4.48.1':
resolution: {integrity: sha512-Vy/WS4z4jEyvnJm+CnPfExIv5sSKqZrUr98h03hpAMbE2aI0aD2wvK6GiSe8Gx2wGp3eD81cYDpLLBqNb2ydwQ==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-arm64-musl@4.48.1':
resolution: {integrity: sha512-x5Kzn7XTwIssU9UYqWDB9VpLpfHYuXw5c6bJr4Mzv9kIv242vmJHbI5PJJEnmBYitUIfoMCODDhR7KoZLot2VQ==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-loongarch64-gnu@4.48.1':
resolution: {integrity: sha512-yzCaBbwkkWt/EcgJOKDUdUpMHjhiZT/eDktOPWvSRpqrVE04p0Nd6EGV4/g7MARXXeOqstflqsKuXVM3H9wOIQ==}
cpu: [loong64]
os: [linux]
'@rollup/rollup-linux-ppc64-gnu@4.48.1':
resolution: {integrity: sha512-UK0WzWUjMAJccHIeOpPhPcKBqax7QFg47hwZTp6kiMhQHeOYJeaMwzeRZe1q5IiTKsaLnHu9s6toSYVUlZ2QtQ==}
cpu: [ppc64]
os: [linux]
'@rollup/rollup-linux-riscv64-gnu@4.48.1':
resolution: {integrity: sha512-3NADEIlt+aCdCbWVZ7D3tBjBX1lHpXxcvrLt/kdXTiBrOds8APTdtk2yRL2GgmnSVeX4YS1JIf0imFujg78vpw==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-riscv64-musl@4.48.1':
resolution: {integrity: sha512-euuwm/QTXAMOcyiFCcrx0/S2jGvFlKJ2Iro8rsmYL53dlblp3LkUQVFzEidHhvIPPvcIsxDhl2wkBE+I6YVGzA==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-s390x-gnu@4.48.1':
resolution: {integrity: sha512-w8mULUjmPdWLJgmTYJx/W6Qhln1a+yqvgwmGXcQl2vFBkWsKGUBRbtLRuKJUln8Uaimf07zgJNxOhHOvjSQmBQ==}
cpu: [s390x]
os: [linux]
'@rollup/rollup-linux-x64-gnu@4.46.4':
resolution: {integrity: sha512-0Xj1vZE3cbr/wda8d/m+UeuSL+TDpuozzdD4QaSzu/xSOMK0Su5RhIkF7KVHFQsobemUNHPLEcYllL7ZTCP/Cg==}
cpu: [x64]
os: [linux]
'@rollup/rollup-linux-x64-gnu@4.48.1':
resolution: {integrity: sha512-90taWXCWxTbClWuMZD0DKYohY1EovA+W5iytpE89oUPmT5O1HFdf8cuuVIylE6vCbrGdIGv85lVRzTcpTRZ+kA==}
cpu: [x64]
os: [linux]
'@rollup/rollup-linux-x64-musl@4.48.1':
resolution: {integrity: sha512-2Gu29SkFh1FfTRuN1GR1afMuND2GKzlORQUP3mNMJbqdndOg7gNsa81JnORctazHRokiDzQ5+MLE5XYmZW5VWg==}
cpu: [x64]
os: [linux]
'@rollup/rollup-win32-arm64-msvc@4.48.1':
resolution: {integrity: sha512-6kQFR1WuAO50bxkIlAVeIYsz3RUx+xymwhTo9j94dJ+kmHe9ly7muH23sdfWduD0BA8pD9/yhonUvAjxGh34jQ==}
cpu: [arm64]
os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.48.1':
resolution: {integrity: sha512-RUyZZ/mga88lMI3RlXFs4WQ7n3VyU07sPXmMG7/C1NOi8qisUg57Y7LRarqoGoAiopmGmChUhSwfpvQ3H5iGSQ==}
cpu: [ia32]
os: [win32]
'@rollup/rollup-win32-x64-msvc@4.48.1':
resolution: {integrity: sha512-8a/caCUN4vkTChxkaIJcMtwIVcBhi4X2PQRoT+yCK3qRYaZ7cURrmJFL5Ux9H9RaMIXj9RuihckdmkBX3zZsgg==}
cpu: [x64]
os: [win32]
'@types/chai@5.2.2':
resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==}
'@types/deep-eql@4.0.2':
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
'@types/node@12.20.55':
resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==}
'@types/node@24.3.0':
resolution: {integrity: sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==}
'@vitest/expect@3.2.4':
resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==}
'@vitest/mocker@3.2.4':
resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==}
peerDependencies:
msw: ^2.4.9
vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0
peerDependenciesMeta:
msw:
optional: true
vite:
optional: true
'@vitest/pretty-format@3.2.4':
resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==}
'@vitest/runner@3.2.4':
resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==}
'@vitest/snapshot@3.2.4':
resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==}
'@vitest/spy@3.2.4':
resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==}
'@vitest/ui@3.2.4':
resolution: {integrity: sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA==}
peerDependencies:
vitest: 3.2.4
'@vitest/utils@3.2.4':
resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==}
ansi-colors@4.1.3:
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
engines: {node: '>=6'}
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
argparse@1.0.10:
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
array-union@2.1.0:
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
engines: {node: '>=8'}
assertion-error@2.0.1:
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
engines: {node: '>=12'}
better-path-resolve@1.0.0:
resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==}
engines: {node: '>=4'}
braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
cac@6.7.14:
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
engines: {node: '>=8'}
chai@5.3.3:
resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==}
engines: {node: '>=18'}
chardet@2.1.0:
resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==}
check-error@2.1.1:
resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
engines: {node: '>= 16'}
ci-info@3.9.0:
resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==}
engines: {node: '>=8'}
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
debug@4.4.1:
resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
deep-eql@5.0.2:
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
engines: {node: '>=6'}
detect-indent@6.1.0:
resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
engines: {node: '>=8'}
dir-glob@3.0.1:
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
engines: {node: '>=8'}
enquirer@2.4.1:
resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==}
engines: {node: '>=8.6'}
es-module-lexer@1.7.0:
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
esbuild@0.25.9:
resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==}
engines: {node: '>=18'}
hasBin: true
esprima@4.0.1:
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
engines: {node: '>=4'}
hasBin: true
estree-walker@3.0.3:
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
expect-type@1.2.2:
resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==}
engines: {node: '>=12.0.0'}
extendable-error@0.1.7:
resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==}
fast-glob@3.3.3:
resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
engines: {node: '>=8.6.0'}
fastq@1.19.1:
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
fdir@6.5.0:
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
engines: {node: '>=12.0.0'}
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
picomatch:
optional: true
fflate@0.8.2:
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
find-up@4.1.0:
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
engines: {node: '>=8'}
flatted@3.3.3:
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
fs-extra@7.0.1:
resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
engines: {node: '>=6 <7 || >=8'}
fs-extra@8.1.0:
resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
engines: {node: '>=6 <7 || >=8'}
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
get-tsconfig@4.10.1:
resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==}
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
globby@11.1.0:
resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
engines: {node: '>=10'}
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
human-id@4.1.1:
resolution: {integrity: sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg==}
hasBin: true
iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
is-number@7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
is-subdir@1.2.0:
resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==}
engines: {node: '>=4'}
is-windows@1.0.2:
resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==}
engines: {node: '>=0.10.0'}
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
js-tokens@9.0.1:
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
js-yaml@3.14.1:
resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
hasBin: true
jsonfile@4.0.0:
resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
locate-path@5.0.0:
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
engines: {node: '>=8'}
lodash.startcase@4.4.0:
resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==}
loupe@3.2.1:
resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==}
magic-string@0.30.18:
resolution: {integrity: sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==}
merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
micromatch@4.0.8:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
mri@1.2.0:
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
engines: {node: '>=4'}
mrmime@2.0.1:
resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
engines: {node: '>=10'}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
neverthrow@8.2.0:
resolution: {integrity: sha512-kOCT/1MCPAxY5iUV3wytNFUMUolzuwd/VF/1KCx7kf6CutrOsTie+84zTGTpgQycjvfLdBBdvBvFLqFD2c0wkQ==}
engines: {node: '>=18'}
outdent@0.5.0:
resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==}
p-filter@2.1.0:
resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==}
engines: {node: '>=8'}
p-limit@2.3.0:
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
engines: {node: '>=6'}
p-locate@4.1.0:
resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
engines: {node: '>=8'}
p-map@2.1.0:
resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==}
engines: {node: '>=6'}
p-try@2.2.0:
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
engines: {node: '>=6'}
package-manager-detector@0.2.11:
resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==}
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
path-key@3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
path-type@4.0.0:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
pathe@2.0.3:
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
pathval@2.0.1:
resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==}
engines: {node: '>= 14.16'}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
picomatch@4.0.3:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
pify@4.0.1:
resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
engines: {node: '>=6'}
postcss@8.5.6:
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
engines: {node: ^10 || ^12 || >=14}
prettier@2.8.8:
resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
engines: {node: '>=10.13.0'}
hasBin: true
quansync@0.2.11:
resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==}
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
read-yaml-file@1.1.0:
resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==}
engines: {node: '>=6'}
resolve-from@5.0.0:
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
engines: {node: '>=8'}
resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
reusify@1.1.0:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
rollup@4.48.1:
resolution: {integrity: sha512-jVG20NvbhTYDkGAty2/Yh7HK6/q3DGSRH4o8ALKGArmMuaauM9kLfoMZ+WliPwA5+JHr2lTn3g557FxBV87ifg==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
semver@7.7.2:
resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
engines: {node: '>=10'}
hasBin: true
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
shebang-regex@3.0.0:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
siginfo@2.0.0:
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
sirv@3.0.1:
resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==}
engines: {node: '>=18'}
slash@3.0.0:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'}
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
spawndamnit@3.0.1:
resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==}
sprintf-js@1.0.3:
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
stackback@0.0.2:
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
std-env@3.9.0:
resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==}
strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
strip-bom@3.0.0:
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
engines: {node: '>=4'}
strip-literal@3.0.0:
resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==}
term-size@2.2.1:
resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==}
engines: {node: '>=8'}
tinybench@2.9.0:
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
tinyexec@0.3.2:
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
tinyglobby@0.2.14:
resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
engines: {node: '>=12.0.0'}
tinypool@1.1.1:
resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==}
engines: {node: ^18.0.0 || >=20.0.0}
tinyrainbow@2.0.0:
resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
engines: {node: '>=14.0.0'}
tinyspy@4.0.3:
resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==}
engines: {node: '>=14.0.0'}
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
totalist@3.0.1:
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
engines: {node: '>=6'}
tsx@4.20.4:
resolution: {integrity: sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg==}
engines: {node: '>=18.0.0'}
hasBin: true
typescript@5.9.2:
resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
engines: {node: '>=14.17'}
hasBin: true
undici-types@7.10.0:
resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==}
universalify@0.1.2:
resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
engines: {node: '>= 4.0.0'}
vite-node@3.2.4:
resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
vite@7.1.3:
resolution: {integrity: sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
'@types/node': ^20.19.0 || >=22.12.0
jiti: '>=1.21.0'
less: ^4.0.0
lightningcss: ^1.21.0
sass: ^1.70.0
sass-embedded: ^1.70.0
stylus: '>=0.54.8'
sugarss: ^5.0.0
terser: ^5.16.0
tsx: ^4.8.1
yaml: ^2.4.2
peerDependenciesMeta:
'@types/node':
optional: true
jiti:
optional: true
less:
optional: true
lightningcss:
optional: true
sass:
optional: true
sass-embedded:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
tsx:
optional: true
yaml:
optional: true
vitest@3.2.4:
resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
peerDependencies:
'@edge-runtime/vm': '*'
'@types/debug': ^4.1.12
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
'@vitest/browser': 3.2.4
'@vitest/ui': 3.2.4
happy-dom: '*'
jsdom: '*'
peerDependenciesMeta:
'@edge-runtime/vm':
optional: true
'@types/debug':
optional: true
'@types/node':
optional: true
'@vitest/browser':
optional: true
'@vitest/ui':
optional: true
happy-dom:
optional: true
jsdom:
optional: true
which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
hasBin: true
why-is-node-running@2.3.0:
resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
engines: {node: '>=8'}
hasBin: true
zod@4.0.17:
resolution: {integrity: sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ==}
snapshots:
'@babel/runtime@7.28.3': {}
'@changesets/apply-release-plan@7.0.12':
dependencies:
'@changesets/config': 3.1.1
'@changesets/get-version-range-type': 0.4.0
'@changesets/git': 3.0.4
'@changesets/should-skip-package': 0.1.2
'@changesets/types': 6.1.0
'@manypkg/get-packages': 1.1.3
detect-indent: 6.1.0
fs-extra: 7.0.1
lodash.startcase: 4.4.0
outdent: 0.5.0
prettier: 2.8.8
resolve-from: 5.0.0
semver: 7.7.2
'@changesets/assemble-release-plan@6.0.9':
dependencies:
'@changesets/errors': 0.2.0
'@changesets/get-dependents-graph': 2.1.3
'@changesets/should-skip-package': 0.1.2
'@changesets/types': 6.1.0
'@manypkg/get-packages': 1.1.3
semver: 7.7.2
'@changesets/changelog-git@0.2.1':
dependencies:
'@changesets/types': 6.1.0
'@changesets/cli@2.29.6(@types/node@24.3.0)':
dependencies:
'@changesets/apply-release-plan': 7.0.12
'@changesets/assemble-release-plan': 6.0.9
'@changesets/changelog-git': 0.2.1
'@changesets/config': 3.1.1
'@changesets/errors': 0.2.0
'@changesets/get-dependents-graph': 2.1.3
'@changesets/get-release-plan': 4.0.13
'@changesets/git': 3.0.4
'@changesets/logger': 0.1.1
'@changesets/pre': 2.0.2
'@changesets/read': 0.6.5
'@changesets/should-skip-package': 0.1.2
'@changesets/types': 6.1.0
'@changesets/write': 0.4.0
'@inquirer/external-editor': 1.0.1(@types/node@24.3.0)
'@manypkg/get-packages': 1.1.3
ansi-colors: 4.1.3
ci-info: 3.9.0
enquirer: 2.4.1
fs-extra: 7.0.1
mri: 1.2.0
p-limit: 2.3.0
package-manager-detector: 0.2.11
picocolors: 1.1.1
resolve-from: 5.0.0
semver: 7.7.2
spawndamnit: 3.0.1
term-size: 2.2.1
transitivePeerDependencies:
- '@types/node'
'@changesets/config@3.1.1':
dependencies:
'@changesets/errors': 0.2.0
'@changesets/get-dependents-graph': 2.1.3
'@changesets/logger': 0.1.1
'@changesets/types': 6.1.0
'@manypkg/get-packages': 1.1.3
fs-extra: 7.0.1
micromatch: 4.0.8
'@changesets/errors@0.2.0':
dependencies:
extendable-error: 0.1.7
'@changesets/get-dependents-graph@2.1.3':
dependencies:
'@changesets/types': 6.1.0
'@manypkg/get-packages': 1.1.3
picocolors: 1.1.1
semver: 7.7.2
'@changesets/get-release-plan@4.0.13':
dependencies:
'@changesets/assemble-release-plan': 6.0.9
'@changesets/config': 3.1.1
'@changesets/pre': 2.0.2
'@changesets/read': 0.6.5
'@changesets/types': 6.1.0
'@manypkg/get-packages': 1.1.3
'@changesets/get-version-range-type@0.4.0': {}
'@changesets/git@3.0.4':
dependencies:
'@changesets/errors': 0.2.0
'@manypkg/get-packages': 1.1.3
is-subdir: 1.2.0
micromatch: 4.0.8
spawndamnit: 3.0.1
'@changesets/logger@0.1.1':
dependencies:
picocolors: 1.1.1
'@changesets/parse@0.4.1':
dependencies:
'@changesets/types': 6.1.0
js-yaml: 3.14.1
'@changesets/pre@2.0.2':
dependencies:
'@changesets/errors': 0.2.0
'@changesets/types': 6.1.0
'@manypkg/get-packages': 1.1.3
fs-extra: 7.0.1
'@changesets/read@0.6.5':
dependencies:
'@changesets/git': 3.0.4
'@changesets/logger': 0.1.1
'@changesets/parse': 0.4.1
'@changesets/types': 6.1.0
fs-extra: 7.0.1
p-filter: 2.1.0
picocolors: 1.1.1
'@changesets/should-skip-package@0.1.2':
dependencies:
'@changesets/types': 6.1.0
'@manypkg/get-packages': 1.1.3
'@changesets/types@4.1.0': {}
'@changesets/types@6.1.0': {}
'@changesets/write@0.4.0':
dependencies:
'@changesets/types': 6.1.0
fs-extra: 7.0.1
human-id: 4.1.1
prettier: 2.8.8
'@esbuild/aix-ppc64@0.25.9':
optional: true
'@esbuild/android-arm64@0.25.9':
optional: true
'@esbuild/android-arm@0.25.9':
optional: true
'@esbuild/android-x64@0.25.9':
optional: true
'@esbuild/darwin-arm64@0.25.9':
optional: true
'@esbuild/darwin-x64@0.25.9':
optional: true
'@esbuild/freebsd-arm64@0.25.9':
optional: true
'@esbuild/freebsd-x64@0.25.9':
optional: true
'@esbuild/linux-arm64@0.25.9':
optional: true
'@esbuild/linux-arm@0.25.9':
optional: true
'@esbuild/linux-ia32@0.25.9':
optional: true
'@esbuild/linux-loong64@0.25.9':
optional: true
'@esbuild/linux-mips64el@0.25.9':
optional: true
'@esbuild/linux-ppc64@0.25.9':
optional: true
'@esbuild/linux-riscv64@0.25.9':
optional: true
'@esbuild/linux-s390x@0.25.9':
optional: true
'@esbuild/linux-x64@0.25.9':
optional: true
'@esbuild/netbsd-arm64@0.25.9':
optional: true
'@esbuild/netbsd-x64@0.25.9':
optional: true
'@esbuild/openbsd-arm64@0.25.9':
optional: true
'@esbuild/openbsd-x64@0.25.9':
optional: true
'@esbuild/openharmony-arm64@0.25.9':
optional: true
'@esbuild/sunos-x64@0.25.9':
optional: true
'@esbuild/win32-arm64@0.25.9':
optional: true
'@esbuild/win32-ia32@0.25.9':
optional: true
'@esbuild/win32-x64@0.25.9':
optional: true
'@inquirer/external-editor@1.0.1(@types/node@24.3.0)':
dependencies:
chardet: 2.1.0
iconv-lite: 0.6.3
optionalDependencies:
'@types/node': 24.3.0
'@jridgewell/sourcemap-codec@1.5.5': {}
'@manypkg/find-root@1.1.0':
dependencies:
'@babel/runtime': 7.28.3
'@types/node': 12.20.55
find-up: 4.1.0
fs-extra: 8.1.0
'@manypkg/get-packages@1.1.3':
dependencies:
'@babel/runtime': 7.28.3
'@changesets/types': 4.1.0
'@manypkg/find-root': 1.1.0
fs-extra: 8.1.0
globby: 11.1.0
read-yaml-file: 1.1.0
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
run-parallel: 1.2.0
'@nodelib/fs.stat@2.0.5': {}
'@nodelib/fs.walk@1.2.8':
dependencies:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.19.1
'@polka/url@1.0.0-next.29': {}
'@rollup/rollup-android-arm-eabi@4.48.1':
optional: true
'@rollup/rollup-android-arm64@4.48.1':
optional: true
'@rollup/rollup-darwin-arm64@4.48.1':
optional: true
'@rollup/rollup-darwin-x64@4.48.1':
optional: true
'@rollup/rollup-freebsd-arm64@4.48.1':
optional: true
'@rollup/rollup-freebsd-x64@4.48.1':
optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.48.1':
optional: true
'@rollup/rollup-linux-arm-musleabihf@4.48.1':
optional: true
'@rollup/rollup-linux-arm64-gnu@4.48.1':
optional: true
'@rollup/rollup-linux-arm64-musl@4.48.1':
optional: true
'@rollup/rollup-linux-loongarch64-gnu@4.48.1':
optional: true
'@rollup/rollup-linux-ppc64-gnu@4.48.1':
optional: true
'@rollup/rollup-linux-riscv64-gnu@4.48.1':
optional: true
'@rollup/rollup-linux-riscv64-musl@4.48.1':
optional: true
'@rollup/rollup-linux-s390x-gnu@4.48.1':
optional: true
'@rollup/rollup-linux-x64-gnu@4.46.4':
optional: true
'@rollup/rollup-linux-x64-gnu@4.48.1':
optional: true
'@rollup/rollup-linux-x64-musl@4.48.1':
optional: true
'@rollup/rollup-win32-arm64-msvc@4.48.1':
optional: true
'@rollup/rollup-win32-ia32-msvc@4.48.1':
optional: true
'@rollup/rollup-win32-x64-msvc@4.48.1':
optional: true
'@types/chai@5.2.2':
dependencies:
'@types/deep-eql': 4.0.2
'@types/deep-eql@4.0.2': {}
'@types/estree@1.0.8': {}
'@types/node@12.20.55': {}
'@types/node@24.3.0':
dependencies:
undici-types: 7.10.0
'@vitest/expect@3.2.4':
dependencies:
'@types/chai': 5.2.2
'@vitest/spy': 3.2.4
'@vitest/utils': 3.2.4
chai: 5.3.3
tinyrainbow: 2.0.0
'@vitest/mocker@3.2.4(vite@7.1.3(@types/node@24.3.0)(tsx@4.20.4))':
dependencies:
'@vitest/spy': 3.2.4
estree-walker: 3.0.3
magic-string: 0.30.18
optionalDependencies:
vite: 7.1.3(@types/node@24.3.0)(tsx@4.20.4)
'@vitest/pretty-format@3.2.4':
dependencies:
tinyrainbow: 2.0.0
'@vitest/runner@3.2.4':
dependencies:
'@vitest/utils': 3.2.4
pathe: 2.0.3
strip-literal: 3.0.0
'@vitest/snapshot@3.2.4':
dependencies:
'@vitest/pretty-format': 3.2.4
magic-string: 0.30.18
pathe: 2.0.3
'@vitest/spy@3.2.4':
dependencies:
tinyspy: 4.0.3
'@vitest/ui@3.2.4(vitest@3.2.4)':
dependencies:
'@vitest/utils': 3.2.4
fflate: 0.8.2
flatted: 3.3.3
pathe: 2.0.3
sirv: 3.0.1
tinyglobby: 0.2.14
tinyrainbow: 2.0.0
vitest: 3.2.4(@types/node@24.3.0)(@vitest/ui@3.2.4)(tsx@4.20.4)
'@vitest/utils@3.2.4':
dependencies:
'@vitest/pretty-format': 3.2.4
loupe: 3.2.1
tinyrainbow: 2.0.0
ansi-colors@4.1.3: {}
ansi-regex@5.0.1: {}
argparse@1.0.10:
dependencies:
sprintf-js: 1.0.3
array-union@2.1.0: {}
assertion-error@2.0.1: {}
better-path-resolve@1.0.0:
dependencies:
is-windows: 1.0.2
braces@3.0.3:
dependencies:
fill-range: 7.1.1
cac@6.7.14: {}
chai@5.3.3:
dependencies:
assertion-error: 2.0.1
check-error: 2.1.1
deep-eql: 5.0.2
loupe: 3.2.1
pathval: 2.0.1
chardet@2.1.0: {}
check-error@2.1.1: {}
ci-info@3.9.0: {}
cross-spawn@7.0.6:
dependencies:
path-key: 3.1.1
shebang-command: 2.0.0
which: 2.0.2
debug@4.4.1:
dependencies:
ms: 2.1.3
deep-eql@5.0.2: {}
detect-indent@6.1.0: {}
dir-glob@3.0.1:
dependencies:
path-type: 4.0.0
enquirer@2.4.1:
dependencies:
ansi-colors: 4.1.3
strip-ansi: 6.0.1
es-module-lexer@1.7.0: {}
esbuild@0.25.9:
optionalDependencies:
'@esbuild/aix-ppc64': 0.25.9
'@esbuild/android-arm': 0.25.9
'@esbuild/android-arm64': 0.25.9
'@esbuild/android-x64': 0.25.9
'@esbuild/darwin-arm64': 0.25.9
'@esbuild/darwin-x64': 0.25.9
'@esbuild/freebsd-arm64': 0.25.9
'@esbuild/freebsd-x64': 0.25.9
'@esbuild/linux-arm': 0.25.9
'@esbuild/linux-arm64': 0.25.9
'@esbuild/linux-ia32': 0.25.9
'@esbuild/linux-loong64': 0.25.9
'@esbuild/linux-mips64el': 0.25.9
'@esbuild/linux-ppc64': 0.25.9
'@esbuild/linux-riscv64': 0.25.9
'@esbuild/linux-s390x': 0.25.9
'@esbuild/linux-x64': 0.25.9
'@esbuild/netbsd-arm64': 0.25.9
'@esbuild/netbsd-x64': 0.25.9
'@esbuild/openbsd-arm64': 0.25.9
'@esbuild/openbsd-x64': 0.25.9
'@esbuild/openharmony-arm64': 0.25.9
'@esbuild/sunos-x64': 0.25.9
'@esbuild/win32-arm64': 0.25.9
'@esbuild/win32-ia32': 0.25.9
'@esbuild/win32-x64': 0.25.9
esprima@4.0.1: {}
estree-walker@3.0.3:
dependencies:
'@types/estree': 1.0.8
expect-type@1.2.2: {}
extendable-error@0.1.7: {}
fast-glob@3.3.3:
dependencies:
'@nodelib/fs.stat': 2.0.5
'@nodelib/fs.walk': 1.2.8
glob-parent: 5.1.2
merge2: 1.4.1
micromatch: 4.0.8
fastq@1.19.1:
dependencies:
reusify: 1.1.0
fdir@6.5.0(picomatch@4.0.3):
optionalDependencies:
picomatch: 4.0.3
fflate@0.8.2: {}
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
find-up@4.1.0:
dependencies:
locate-path: 5.0.0
path-exists: 4.0.0
flatted@3.3.3: {}
fs-extra@7.0.1:
dependencies:
graceful-fs: 4.2.11
jsonfile: 4.0.0
universalify: 0.1.2
fs-extra@8.1.0:
dependencies:
graceful-fs: 4.2.11
jsonfile: 4.0.0
universalify: 0.1.2
fsevents@2.3.3:
optional: true
get-tsconfig@4.10.1:
dependencies:
resolve-pkg-maps: 1.0.0
glob-parent@5.1.2:
dependencies:
is-glob: 4.0.3
globby@11.1.0:
dependencies:
array-union: 2.1.0
dir-glob: 3.0.1
fast-glob: 3.3.3
ignore: 5.3.2
merge2: 1.4.1
slash: 3.0.0
graceful-fs@4.2.11: {}
human-id@4.1.1: {}
iconv-lite@0.6.3:
dependencies:
safer-buffer: 2.1.2
ignore@5.3.2: {}
is-extglob@2.1.1: {}
is-glob@4.0.3:
dependencies:
is-extglob: 2.1.1
is-number@7.0.0: {}
is-subdir@1.2.0:
dependencies:
better-path-resolve: 1.0.0
is-windows@1.0.2: {}
isexe@2.0.0: {}
js-tokens@9.0.1: {}
js-yaml@3.14.1:
dependencies:
argparse: 1.0.10
esprima: 4.0.1
jsonfile@4.0.0:
optionalDependencies:
graceful-fs: 4.2.11
locate-path@5.0.0:
dependencies:
p-locate: 4.1.0
lodash.startcase@4.4.0: {}
loupe@3.2.1: {}
magic-string@0.30.18:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
merge2@1.4.1: {}
micromatch@4.0.8:
dependencies:
braces: 3.0.3
picomatch: 2.3.1
mri@1.2.0: {}
mrmime@2.0.1: {}
ms@2.1.3: {}
nanoid@3.3.11: {}
neverthrow@8.2.0:
optionalDependencies:
'@rollup/rollup-linux-x64-gnu': 4.46.4
outdent@0.5.0: {}
p-filter@2.1.0:
dependencies:
p-map: 2.1.0
p-limit@2.3.0:
dependencies:
p-try: 2.2.0
p-locate@4.1.0:
dependencies:
p-limit: 2.3.0
p-map@2.1.0: {}
p-try@2.2.0: {}
package-manager-detector@0.2.11:
dependencies:
quansync: 0.2.11
path-exists@4.0.0: {}
path-key@3.1.1: {}
path-type@4.0.0: {}
pathe@2.0.3: {}
pathval@2.0.1: {}
picocolors@1.1.1: {}
picomatch@2.3.1: {}
picomatch@4.0.3: {}
pify@4.0.1: {}
postcss@8.5.6:
dependencies:
nanoid: 3.3.11
picocolors: 1.1.1
source-map-js: 1.2.1
prettier@2.8.8: {}
quansync@0.2.11: {}
queue-microtask@1.2.3: {}
read-yaml-file@1.1.0:
dependencies:
graceful-fs: 4.2.11
js-yaml: 3.14.1
pify: 4.0.1
strip-bom: 3.0.0
resolve-from@5.0.0: {}
resolve-pkg-maps@1.0.0: {}
reusify@1.1.0: {}
rollup@4.48.1:
dependencies:
'@types/estree': 1.0.8
optionalDependencies:
'@rollup/rollup-android-arm-eabi': 4.48.1
'@rollup/rollup-android-arm64': 4.48.1
'@rollup/rollup-darwin-arm64': 4.48.1
'@rollup/rollup-darwin-x64': 4.48.1
'@rollup/rollup-freebsd-arm64': 4.48.1
'@rollup/rollup-freebsd-x64': 4.48.1
'@rollup/rollup-linux-arm-gnueabihf': 4.48.1
'@rollup/rollup-linux-arm-musleabihf': 4.48.1
'@rollup/rollup-linux-arm64-gnu': 4.48.1
'@rollup/rollup-linux-arm64-musl': 4.48.1
'@rollup/rollup-linux-loongarch64-gnu': 4.48.1
'@rollup/rollup-linux-ppc64-gnu': 4.48.1
'@rollup/rollup-linux-riscv64-gnu': 4.48.1
'@rollup/rollup-linux-riscv64-musl': 4.48.1
'@rollup/rollup-linux-s390x-gnu': 4.48.1
'@rollup/rollup-linux-x64-gnu': 4.48.1
'@rollup/rollup-linux-x64-musl': 4.48.1
'@rollup/rollup-win32-arm64-msvc': 4.48.1
'@rollup/rollup-win32-ia32-msvc': 4.48.1
'@rollup/rollup-win32-x64-msvc': 4.48.1
fsevents: 2.3.3
run-parallel@1.2.0:
dependencies:
queue-microtask: 1.2.3
safer-buffer@2.1.2: {}
semver@7.7.2: {}
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
shebang-regex@3.0.0: {}
siginfo@2.0.0: {}
signal-exit@4.1.0: {}
sirv@3.0.1:
dependencies:
'@polka/url': 1.0.0-next.29
mrmime: 2.0.1
totalist: 3.0.1
slash@3.0.0: {}
source-map-js@1.2.1: {}
spawndamnit@3.0.1:
dependencies:
cross-spawn: 7.0.6
signal-exit: 4.1.0
sprintf-js@1.0.3: {}
stackback@0.0.2: {}
std-env@3.9.0: {}
strip-ansi@6.0.1:
dependencies:
ansi-regex: 5.0.1
strip-bom@3.0.0: {}
strip-literal@3.0.0:
dependencies:
js-tokens: 9.0.1
term-size@2.2.1: {}
tinybench@2.9.0: {}
tinyexec@0.3.2: {}
tinyglobby@0.2.14:
dependencies:
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
tinypool@1.1.1: {}
tinyrainbow@2.0.0: {}
tinyspy@4.0.3: {}
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
totalist@3.0.1: {}
tsx@4.20.4:
dependencies:
esbuild: 0.25.9
get-tsconfig: 4.10.1
optionalDependencies:
fsevents: 2.3.3
typescript@5.9.2: {}
undici-types@7.10.0: {}
universalify@0.1.2: {}
vite-node@3.2.4(@types/node@24.3.0)(tsx@4.20.4):
dependencies:
cac: 6.7.14
debug: 4.4.1
es-module-lexer: 1.7.0
pathe: 2.0.3
vite: 7.1.3(@types/node@24.3.0)(tsx@4.20.4)
transitivePeerDependencies:
- '@types/node'
- jiti
- less
- lightningcss
- sass
- sass-embedded
- stylus
- sugarss
- supports-color
- terser
- tsx
- yaml
vite@7.1.3(@types/node@24.3.0)(tsx@4.20.4):
dependencies:
esbuild: 0.25.9
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
postcss: 8.5.6
rollup: 4.48.1
tinyglobby: 0.2.14
optionalDependencies:
'@types/node': 24.3.0
fsevents: 2.3.3
tsx: 4.20.4
vitest@3.2.4(@types/node@24.3.0)(@vitest/ui@3.2.4)(tsx@4.20.4):
dependencies:
'@types/chai': 5.2.2
'@vitest/expect': 3.2.4
'@vitest/mocker': 3.2.4(vite@7.1.3(@types/node@24.3.0)(tsx@4.20.4))
'@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4
'@vitest/spy': 3.2.4
'@vitest/utils': 3.2.4
chai: 5.3.3
debug: 4.4.1
expect-type: 1.2.2
magic-string: 0.30.18
pathe: 2.0.3
picomatch: 4.0.3
std-env: 3.9.0
tinybench: 2.9.0
tinyexec: 0.3.2
tinyglobby: 0.2.14
tinypool: 1.1.1
tinyrainbow: 2.0.0
vite: 7.1.3(@types/node@24.3.0)(tsx@4.20.4)
vite-node: 3.2.4(@types/node@24.3.0)(tsx@4.20.4)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 24.3.0
'@vitest/ui': 3.2.4(vitest@3.2.4)
transitivePeerDependencies:
- jiti
- less
- lightningcss
- msw
- sass
- sass-embedded
- stylus
- sugarss
- supports-color
- terser
- tsx
- yaml
which@2.0.2:
dependencies:
isexe: 2.0.0
why-is-node-running@2.3.0:
dependencies:
siginfo: 2.0.0
stackback: 0.0.2
zod@4.0.17: {}
Contracted
A TypeScript library for building type-safe, composable services using a contract-first approach. Define your operations with clear inputs, outputs, dependencies, and error types, then compose them into services with automatic dependency injection.
Agent Skill
This package ships a SKILL.md that teaches AI coding assistants how to use Contracted correctly. Install it into your project with `npx skills`:
npx skills add @validkeys/contractedTable of Contents
- Installation
- Core Concepts
- Quick Start
- Step-by-Step Guide
- 1. Define Errors
- 2. Define a Command
- 3. Define a Service
- 4. Implement the Command
- 5. Implement the Service
- 6. Use the Service
- Service Contracts
- Error Handling
- Advanced Features
- API Reference
- Examples
Installation
npm install @validkeys/contracted
# or
pnpm add @validkeys/contracted
# or
yarn add @validkeys/contractedContracted has peer dependencies on neverthrow and zod:
npm install neverthrow zod
# or
pnpm add neverthrow zod
# or
yarn add neverthrow zodCore Concepts
The Service Command architecture is built around four key concepts:
🔒 Commands
Define the interface for an operation including:
- Input schema (Zod validation)
- Output schema (Zod validation)
- Dependencies (typed dependency injection)
- Options (optional configuration)
- Errors (typed error conditions)
⚡ Implementations
Implemented commands that contain the actual business logic. Implementations are pure functions that receive validated input and dependencies.
🏗️ Services
Collections of related commands with shared dependencies. Services provide a clean API for executing multiple operations.
🏷️ Tagged Errors
Type-safe error handling using discriminated unions, enabling exhaustive pattern matching and precise error handling.
Quick Start
import { z } from 'zod';
import { defineCommand, defineError, defineService } from '@validkeys/contracted';
// 1. Define errors
const UserNotFoundError = defineError<'USER_NOT_FOUND', { userId: string }>(
'USER_NOT_FOUND',
'User not found'
);
// 2. Define command
const getUserCommand = defineCommand({
input: z.object({ userId: z.string() }),
output: z.object({
id: z.string(),
name: z.string(),
email: z.string()
}),
dependencies: {
userRepo: {} as { findById: (id: string) => Promise<any | null> }
},
errors: [UserNotFoundError] as const
});
// 3. Define service contract
const userServiceContract = defineService({
getUser: getUserCommand
});
// Types available immediately
type UserService = typeof userServiceContract.types.Service;
type UserServiceDeps = typeof userServiceContract.types.Dependencies;
// 4. Implement the service contract (using the new auto-wrapped implementation)
const getUser = getUserCommand.implementation(async ({ input, deps }) => {
const user = await deps.userRepo.findById(input.userId);
if (!user) {
throw new UserNotFoundError({ userId: input.userId });
}
return user; // Automatically wrapped in ok()
});
const createUserService = userServiceContract.implementation({
getUser
});
// 5. Use the service
const userService = createUserService({
userRepo: new UserRepository()
});
const result = await userService.getUser.run({ userId: '123' });Note: For simple applications, you can also use `serviceFrom` to create services directly from implementations without service contracts.
Step-by-Step Guide
1. Define Errors
Start by defining the possible error conditions in your contracts package:
// src/packages/contracts/UserManager/errors.ts
import { defineError } from './core/errors';
// Define specific error types with typed data
export const UserAlreadyExistsError = defineError<
'USER_ALREADY_EXISTS',
{ email: string }
>(
'USER_ALREADY_EXISTS',
'User with this email already exists'
);
export const UserNotFoundError = defineError<
'USER_NOT_FOUND',
{ userId: string }
>(
'USER_NOT_FOUND',
'User not found'
);
export const InvalidUserDataError = defineError<
'INVALID_USER_DATA',
{ field: string; reason: string }
>(
'INVALID_USER_DATA',
'Invalid user data provided'
);
// Group errors by operation
export const createUserErrors = [
UserAlreadyExistsError,
InvalidUserDataError,
] as const;2. Define Commands
Create commands that specify interfaces and dependencies in your contracts package:
// src/packages/contracts/infrastructure.ts
export interface UserRepository {
save: (user: any) => Promise<void>;
findByEmail: (email: string) => Promise<any | null>;
findById: (id: string) => Promise<any | null>;
}
export interface IdGenerator {
generate: () => string;
}
export interface Logger {
info: (message: string, data?: any) => void;
error: (message: string, error: Error) => void;
}
// src/packages/contracts/UserManager/contracts.ts
import { z } from 'zod';
import { defineCommand } from './core/defineCommand';
import { createUserErrors } from './errors';
import { UserRepository, IdGenerator, Logger } from '../infrastructure';
// Define the command
export const createUserCommand = defineCommand({
input: z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
age: z.number().min(18).max(120),
}),
output: z.object({
id: z.string(),
email: z.string(),
name: z.string(),
age: z.number(),
createdAt: z.date(),
}),
dependencies: {
userRepository: {} as UserRepository,
idGenerator: {} as IdGenerator,
logger: {} as Logger,
},
options: {} as {
skipDuplicateCheck?: boolean;
sendWelcomeEmail?: boolean;
},
errors: createUserErrors,
});3. Define a Service
Create a service contract that groups related commands:
// src/packages/contracts/UserManager/service.ts
import { defineService } from '@validkeys/contracted';
import { createUserCommand } from './contracts';
export const userManagerServiceContract = defineService({
createUser: createUserCommand,
// Add other commands here
// updateUser: updateUserCommand,
// deleteUser: deleteUserCommand,
});
// Export types for use in other packages
export type UserManagerService = typeof userManagerServiceContract.types.Service;
export type UserManagerDependencies = typeof userManagerServiceContract.types.Dependencies;
export type UserManagerErrors = typeof userManagerServiceContract.types.Errors;4. Implement the Command
Add the business logic in your implementation package:
// src/packages/UserManager/commands/createUser.ts
import {
createUserCommand,
UserAlreadyExistsError,
InvalidUserDataError,
UserRepositoryError,
} from '../../contracts/UserManager/index';
export const createUser = createUserCommand.implementation(
async ({ input, deps, options }) => {
deps.logger.info('Creating user', { email: input.email });
// Business logic validation - throw errors directly
if (input.name.includes('@')) {
throw new InvalidUserDataError({
field: 'name',
reason: 'Name cannot contain @ symbol'
});
}
// Check for existing user
if (!options?.skipDuplicateCheck) {
try {
const existingUser = await deps.userRepository.findByEmail(input.email);
if (existingUser) {
throw new UserAlreadyExistsError({ email: input.email });
}
} catch (error) {
throw new UserRepositoryError({
operation: 'findByEmail',
details: error?.toString()
});
}
}
// Create new user
const newUser = {
id: deps.idGenerator.generate(),
email: input.email,
name: input.name,
age: input.age,
createdAt: new Date(),
};
// Save to repository
try {
await deps.userRepository.save(newUser);
deps.logger.info('User created successfully', { userId: newUser.id });
// Return the raw output - automatically wrapped in ok()
return newUser;
} catch (error) {
deps.logger.error('Failed to create user', error as Error);
throw new UserRepositoryError({
operation: 'save',
details: error?.toString()
});
}
}
);5. Implement the Service
Create the service implementation using the service contract:
// src/packages/UserManager/service.ts
import { userManagerServiceContract, UserManagerService, UserManagerDependencies } from '../contracts/UserManager/service';
import { createUser } from './commands/createUser';
// Import other command implementations
// import { updateUser } from './commands/updateUser';
// import { deleteUser } from './commands/deleteUser';
export const createUserService = userManagerServiceContract.implementation({
createUser,
// Add other command implementations here
// updateUser,
// deleteUser,
});
// Re-export types for convenience
export type UserService = UserManagerService;
export type { UserManagerDependencies };6. Use the Service
Initialize and use your service:
// src/example/index.ts
import { createUserService, UserManagerDependencies } from './packages/UserManager';
// Initialize with dependencies
const userService = createUserService({
userRepository: new UserRepository(),
idGenerator: new IdGenerator(),
logger: new Logger(),
});
// Execute commands
const result = await userService.createUser.run(
{
email: 'john@example.com',
name: 'John Doe',
age: 30
},
{
sendWelcomeEmail: true
}
);
if (result.isOk()) {
console.log('User created:', result.value);
} else {
console.error('Failed to create user:', result.error);
}Implementation Methods
Contracted provides two ways to implement commands, giving you flexibility in how you handle errors and validation:
1. implementation() - Auto-wrapped with Validation (Recommended)
The default implementation method automatically:
- Validates inputs using your Zod schema before calling your implementation
- Applies Zod transformations (trim, lowercase, defaults, etc.)
- Validates outputs using your Zod schema before returning
- Wraps return values in
ok() - Catches and wraps TaggedErrors in
err() - Adds `ValidationError` to your error union for invalid inputs/outputs
const getUserCommand = defineCommand({
input: z.object({ userId: z.string().trim() }),
output: z.object({ id: z.string(), name: z.string() }),
dependencies: {} as { userRepo: UserRepository },
errors: [UserNotFoundError] as const
});
// Auto-wrapped implementation - inputs are validated and transformed automatically
const getUser = getUserCommand.implementation(async ({ input, deps }) => {
// input.userId is already validated and trimmed
const user = await deps.userRepo.findById(input.userId);
if (!user) {
throw new UserNotFoundError({ userId: input.userId }); // Automatically wrapped in err()
}
return user; // Automatically validated and wrapped in ok()
});
const result = await getUser.run({ input: { userId: '123' }, deps: { userRepo } });
// result is Result<User, UserNotFoundError | ValidationError>
if (result.isErr()) {
switch (result.error._tag) {
case 'VALIDATION_ERROR':
// Invalid input or output
console.error('Validation failed:', result.error.data.phase, result.error.data.errors);
break;
case 'USER_NOT_FOUND':
// Business logic error
console.error('User not found:', result.error.data.userId);
break;
}
}Benefits:
- ✅ Automatic validation - inputs and outputs are validated before/after your code runs
- ✅ Zod transformations applied - defaults, trims, coercions, etc. work automatically
- ✅ Cleaner, more readable code - no manual validation or Result wrapping
- ✅ Standard JavaScript error handling with
throw - ✅ Type-safe error handling - ValidationError is included in error union
- ✅ Unexpected errors are re-thrown for proper error handling
2. unsafeImplementation() - Explicit Results, No Validation
For cases where you need full control over validation and Result handling, use unsafeImplementation:
// Explicit Result handling - NO automatic validation
const getUser = getUserCommand.unsafeImplementation(async ({ input, deps }) => {
// No automatic validation - input might be invalid
// Manually validate if needed:
const validInput = getUserCommand.validateInput(input);
const user = await deps.userRepo.findById(validInput.userId);
if (!user) {
return err(new UserNotFoundError({ userId: input.userId })); // Explicit err()
}
return ok(user); // Explicit ok()
});
const result = await getUser.run({ input: { userId: '123' }, deps: { userRepo } });
// result is Result<User, UserNotFoundError> (no ValidationError)When to use `unsafeImplementation`:
- 🔧 Complex error transformation logic
- 🔧 Fine-grained control over validation timing
- 🔧 Performance-critical paths where you want to skip validation
- 🔧 Custom validation logic beyond what Zod provides
- 🔧 Migrating from existing Result-based code
Important: ValidationError is NOT added to the error union when using unsafeImplementation.
Consistent API
Both methods produce identical ImplementedContract objects with the same API (except for error types):
// Both methods create contracts with identical interfaces
const safeContract = command.implementation(/* ... */); // Includes ValidationError
const unsafeContract = command.unsafeImplementation(/* ... */); // No ValidationError
// Same API available on both:
const result1 = await safeContract.run(context);
const result2 = await unsafeContract.run(context);
const curried1 = safeContract.withDependencies(deps);
const curried2 = unsafeContract.withDependencies(deps);Service Contracts
For large applications with multiple packages, you can define service contracts that provide types before implementation exists. This enables clean separation between contract definition and implementation across package boundaries, following the same define → implementation pattern as individual contracts.
Define Service Contract
// contracts/UserManager/service.ts
import { defineService } from '@validkeys/contracted';
import { createUserCommand, updateUserContract, deleteUserContract } from './contracts';
export const userManagerServiceContract = defineService({
createUser: createUserCommand,
updateUser: updateUserContract,
deleteUser: deleteUserContract
});
// Types available immediately for other packages
export type UserManagerService = typeof userManagerServiceContract.types.Service;
export type UserManagerDependencies = typeof userManagerServiceContract.types.Dependencies;
export type UserManagerErrors = typeof userManagerServiceContract.types.Errors;Implement Service Contract
// packages/UserManager/service.ts
import { userManagerServiceContract } from '../../contracts/UserManager/service';
import { createUser, updateUser, deleteUser } from './commands';
export const createUserManagerService = userManagerServiceContract.implementation({
createUser,
updateUser,
deleteUser
});
// Type matches the contract exactly
export type UserService = typeof userManagerServiceContract.types.Service;Use Service Types Across Packages
// packages/PackageA/handlers.ts
import type { UserManagerService } from '../../contracts/UserManager/service';
// Package A can depend on service type without importing implementation
export class UserHandler {
constructor(private userService: UserManagerService) {}
async handleCreateUser(data: any) {
return this.userService.createUser.run(data);
}
}This pattern enables:
- Type availability before implementation: Service types available in contracts folder
- Clean package boundaries: Packages depend on contracts, not implementations
- Consistent API: Same
define → implementationpattern as contracts - Better architecture: Interface segregation across package boundaries
Error Handling
The architecture provides type-safe error handling through tagged errors. When using implementation(), ValidationError is automatically added to handle schema validation failures.
Built-in ValidationError
When using implementation(), the system automatically validates inputs and outputs. Invalid data returns a ValidationError:
const createUser = createUserCommand.implementation(async ({ input, deps }) => {
// Implementation code
return user;
});
const result = await createUser.run({
input: { email: 'invalid', age: 15 }, // Invalid data
deps
});
if (result.isErr() && result.error._tag === 'VALIDATION_ERROR') {
console.log('Phase:', result.error.data.phase); // 'input' or 'output'
// Access simplified errors array (backward compatible)
console.log('Errors:', result.error.data.errors); // Array of simplified error objects
// Example simplified error structure:
// {
// path: ['email'],
// message: 'Invalid email',
// code: 'invalid_string'
// }
// Access full ZodError for advanced use cases
console.log('ZodError:', result.error.data.zodError);
// Use ZodError methods for detailed formatting
const formatted = result.error.data.zodError.format();
console.log('Formatted errors:', formatted);
// Use flatten() for field-specific errors
const flattened = result.error.data.zodError.flatten();
console.log('Field errors:', flattened.fieldErrors);
// Access all ZodError issues for complete details
result.error.data.zodError.issues.forEach(issue => {
console.log('Issue:', issue.path, issue.message, issue.code);
// Full issue object includes additional properties like:
// - validation, unionErrors, keys, etc. depending on error type
});
}ValidationError Structure
The ValidationError provides both simplified and complete error information:
{
zodError: ZodError; // Complete ZodError instance with all methods
phase: 'input' | 'output'; // Which validation phase failed
errors: Array<{ // Simplified errors (backward compatible)
path: (string | number)[];
message: string;
code: string;
}>;
message: string; // Human-readable summary
}When to use each format:
- `errors` array: Quick access to basic error info, backward compatible
- `zodError`: Advanced formatting, nested errors, union errors, refinement details, or when you need Zod's utility methods (
format(),flatten(),formErrors(), etc.)
Exhaustive Pattern Matching
import { matchError } from '@validkeys/contracted';
if (result.isErr()) {
const response = matchError(result.error, {
VALIDATION_ERROR: (error) => ({
status: 400,
message: `Validation failed for ${error.data.phase}`,
errors: error.data.errors,
}),
USER_ALREADY_EXISTS: (error) => ({
status: 409,
message: `User with email ${error.data.email} already exists`,
}),
INVALID_USER_DATA: (error) => ({
status: 400,
message: `Invalid ${error.data.field}: ${error.data.reason}`,
}),
USER_REPOSITORY_ERROR: (error) => ({
status: 500,
message: 'Database error occurred',
details: error.data.details,
}),
});
console.log('Error response:', response);
}Switch Statement (Type-Safe)
if (result.isErr()) {
switch (result.error._tag) {
case 'VALIDATION_ERROR':
// Handle schema validation errors
console.error('Validation failed:', result.error.data.errors);
break;
case 'USER_ALREADY_EXISTS':
// Handle duplicate user
break;
case 'INVALID_USER_DATA':
// Handle validation error
break;
case 'USER_REPOSITORY_ERROR':
// Handle database error
break;
default:
// TypeScript ensures exhaustiveness
const _exhaustive: never = result.error;
throw new Error(`Unhandled error: ${_exhaustive}`);
}
}Advanced Features
Service Variants
Full Service (with metadata)
const userService = serviceFrom(commands);
// Access to: schemas, types, errors, validateInput, validateOutput, runSimple Service (execution only)
import { serviceFromSimple } from './core/serviceFrom';
const userService = serviceFromSimple(commands);
// Only execution functions availableInput/Output Validation
When using implementation(), validation happens automatically:
const createUser = createUserCommand.implementation(async ({ input, deps }) => {
// input is already validated and transformed by Zod
// Zod transformations like .trim(), .default(), .transform() are applied
return user;
});
// Validation happens automatically
const result = await createUser.run({ input: userData, deps });
if (result.isErr() && result.error._tag === 'VALIDATION_ERROR') {
console.error('Invalid data:', result.error.data.errors);
}For manual validation or with unsafeImplementation():
// Validate input manually
try {
const validInput = userService.createUser.validateInput(req.body);
const result = await userService.createUser.run(validInput);
} catch (validationError) {
// Handle Zod validation error
}
// Validate output (useful for testing)
const validOutput = userService.createUser.validateOutput(result.value);Dependency Type Extraction
import { ServiceDependencies, ServiceErrors } from './core/serviceFrom';
// Extract types from individual contracts
type CreateUserInput = typeof createUserCommand.types.Input;
type CreateUserOutput = typeof createUserCommand.types.Output;
type CreateUserDeps = typeof createUserCommand.types.Dependencies;
// Extract types from service collections
type UserServiceDeps = ServiceDependencies<typeof userCommands>;
type UserServiceErrors = ServiceErrors<typeof userCommands>;HTTP Integration Example
export async function createUserHandler(req: Request, res: Response) {
try {
const input = userService.createUser.validateInput(req.body);
const result = await userService.createUser.run(input, {
sendWelcomeEmail: true,
});
if (result.isErr()) {
const errorResponse = matchError(result.error, {
USER_ALREADY_EXISTS: () => res.status(409).json({
error: 'User already exists',
code: 'DUPLICATE_USER',
}),
USER_REPOSITORY_ERROR: () => res.status(500).json({
error: 'Internal server error',
code: 'DB_ERROR',
}),
INVALID_USER_DATA: (error) => res.status(400).json({
error: 'Invalid input',
field: error.data.field,
reason: error.data.reason,
}),
});
return errorResponse;
}
return res.status(201).json({
status: 'success',
data: result.value,
});
} catch (validationError) {
return res.status(400).json({
error: 'Invalid request body',
details: validationError,
});
}
}API Reference
Core Functions
defineCommand<TInput, TOutput, TDeps, TOptions, TErrors>(params)
Creates a new command definition.
Parameters:
input: z.ZodType- Zod schema for input validationoutput: z.ZodType- Zod schema for output validationdependencies: TDeps- Type definition for dependenciesoptions?: TOptions- Optional configuration typeerrors?: TErrors- Array of error constructors
Returns: Contract with two implementation methods:
implementation(impl)- Auto-wrapped implementation (throws errors, returns raw output)unsafeImplementation(impl)- Explicit Result handling (returnsResult<TOutput, TError>)
defineError<TTag, TData>(tag, defaultMessage?)
Creates a tagged error class.
Parameters:
tag: string- Unique identifier for the errordefaultMessage?: string- Default error message
serviceFrom<T>(commands)
Creates a service factory with full contract metadata.
serviceFromSimple<T>(commands)
Creates a simplified service factory with only execution functions.
defineService<T>(contracts)
Creates a service contract definition from individual contracts.
Parameters:
contracts: Record<string, Contract>- Object mapping command names to contract definitions
Returns: ServiceContract<T> - Service contract with types and implementation method
matchError<TError, TResult>(error, handlers)
Provides exhaustive pattern matching for tagged errors.
Types
Contract<TInput, TOutput, TDeps, TOptions, TErrors>
Base contract interface without implementation.
ImplementedContract<TInput, TOutput, TDeps, TOptions, TErrors>
Contract interface with implementation and execution methods.
ImplementationFunction<TInput, TOutput, TDeps, TOptions, TError>
Type for explicit Result-based implementation functions (used with unsafeImplementation).
UnsafeImplementationFunction<TInput, TOutput, TDeps, TOptions>
Type for auto-wrapped implementation functions (used with implementation).
ServiceContract<T>
Service contract interface that provides types before implementation exists.
TaggedError<TTag>
Base class for all tagged errors.
ErrorUnion<T>
Creates discriminated union from error constructor array.
Examples
The src/example folder contains a complete example showing:
- User Management Service: Creating, updating, and deleting users
- Service Contracts: Using
defineServicefor type-safe service definitions - Error Handling: Comprehensive error scenarios
- Service Composition: Building services from multiple commands
- Cross-Package Types: Type safety across package boundaries
File Structure
src/
├── core/ # Core library code
│ ├── defineCommand.ts # Contract definition
│ ├── defineService.ts # Service contract definition
│ ├── errors.ts # Error handling utilities
│ ├── serviceFrom.ts # Service composition
│ └── types.ts # Type definitions
└── example/ # Usage examples
├── index.ts # Example usage
└── packages/
├── contracts/ # Contract definitions (interfaces)
│ ├── infrastructure.ts # Shared infrastructure interfaces
│ └── UserManager/
│ ├── contracts.ts # Individual contracts
│ ├── service.ts # Service contract definition
│ ├── errors.ts # Error definitions
│ └── index.ts # Package exports
└── UserManager/ # Implementation package
├── commands/
│ └── createUser.ts # Command implementation
├── service.ts # Service composition
└── index.ts # Package exportsPackage Structure
The Service Command architecture uses a clean separation between contracts and implementations:
Contracts Package (contracts/)
- Purpose: Defines interfaces, types, and error definitions
- Contents:
infrastructure.ts: Shared infrastructure interfaces (Logger, Repository, etc.)- Service-specific contracts and error definitions
- Benefits:
- Clear API definitions independent of implementation
- Shared infrastructure interfaces across services
- Easy to share between teams
- Enables contract-first development
- Facilitates testing with mocks
Implementation Packages (UserManager/, etc.)
- Purpose: Provides actual business logic implementations
- Contents: Command implementations, service composition
- Benefits:
- Multiple implementations of same contracts
- Clean separation of concerns
- Easier testing and mocking
- Better code organization
Usage Example
// Import contracts for type definitions
import { createUserCommand } from './contracts/UserManager';
// Import implementation for actual usage
import { createUserService } from './UserManager';
// Access types from the contract
type CreateUserInput = typeof createUserCommand.types.Input;
type CreateUserOutput = typeof createUserCommand.types.Output;
type UserManagerDeps = typeof createUserCommand.types.Dependencies;
// Use with full type safety
const service = createUserService(dependencies);
const result = await service.createUser.run(input);Benefits
✅ Type Safety: Full TypeScript support with compile-time error checking ✅ Dependency Injection: Clean, testable dependency management ✅ Error Handling: Exhaustive, type-safe error handling ✅ Validation: Automatic input/output validation with Zod ✅ Composition: Easy service composition from reusable commands ✅ Testing: Pure functions make testing straightforward ✅ Documentation: Self-documenting contracts with clear interfaces
License
ISC
export * from './core/defineContract';
export * from './core/defineService';
export * from './core/serviceFrom';
export * from './core/types';
export * from './core/errors';