results-js
v1.0.1
Published
A library offering functional and object-oriented programming tools for state and error management, compatible with both TypeScript and JavaScript projects.
Downloads
17
Maintainers
Keywords
Readme
Results-js
Overview
Results-js is a cutting-edge tool designed to inject Functional Programming principles into an Object-Oriented Programming environment. Its primary objective is to encapsulate the outcome of operations, clearly differentiating between successful results and failures. This unique approach enhances error handling, boosts code readability, and fosters robust, maintainable code structures.
Core Concept
The cornerstone of Results-js is the Result
class. This class functions as a type-safe union, capable of holding either a success value or an error object, but never both. This structure allows methods to return a Result
object that communicates the outcome of an operation.
Key Features
Type Safety
Ensuring type safety is paramount, minimizing common pitfalls in error handling and result processing.
Immutable Design
Result
instances are immutable upon creation, echoing the Functional Programming's emphasis on reliability and predictability.
Error Handling
Diverging from traditional Object-Oriented Programming's reliances on exceptions, Result
treats errors as regular data types called an Issue
, streamlining the error management process within the program's normal flow.
Enhanced Debugging and Maintenance
Outcomes encapsulated in Result
objects simplify code tracing and debugging, making data and error flows transparent and predictable.
Advantages Over Traditional Object-Oriented Programming
Explicit Error Handling
Errors are integral to the method's contract, ensuring clarity and explicitness in handling.
Reduced Side-Effects
The library's design, focusing on immutability and data-centric error handling, diminishes common Object-Oriented Programming side effects.
Improved Code Clarity
The distinct separation between success and failure outcomes enhances code readability and comprehension.
Facilitates Testability
The predictable nature of Result
objects streamlines unit testing, enabling easy simulation and verification of outcomes.
Components
Modules Overview
- Action: The
action.ts
module facilitates the execution of operations, returningResult
objects. - Result: The
result.ts
module is the backbone of the library, providing theResult
class. It encapsulates outcomes as either success or failure, maintaining type safety and immutability. - Issue: Defined in
issue.ts
, this module manages errors and failures, allowing for structured and comprehensible error representation. - Store: As seen in
store.ts
, this module handles state management within the OOP context, integrating FP principles.
Action
The Action
class is a foundational component of Results-js, designed to encapsulate and manage executable actions within applications. By offering a structured approach to executing operations and managing their effects on application state, the Action
class leads to creating more predictable, maintainable, and testable codebases.
Role and Usage
- Executable Actions Representation: The
Action
class serves as a blueprint for actions that can be executed within an application. Each instance is uniquely identified and carries with it a set of parameters, a name, and an execution logic that defines its behavior. - State and Content Management: Through its execution logic, an
Action
can produce effects that alter the application's state in a controlled manner. These effects are encapsulated in theIEffect<S, C>
interface, linking actions directly to state transitions and content generation. - Unique Identification: Every action instance is assigned a unique identifier (
id
) upon creation, facilitating traceability and management of actions within complex systems. - Correlation Support: Actions can optionally include a correlation identifier (
correlationId
), allowing them to be associated with other related actions. This feature is particularly useful in scenarios involving coordinated operations or transactions. - Immutable Design: Upon instantiation, an
Action
's properties are immutable, echoing the principles of functional programming and enhancing the predictability and reliability of the system. - Dynamic Execution Logic Attachment: The class allows for the dynamic attachment of execution logic (
exec
) to actions, providing flexibility in defining or altering an action's behavior post-creation. - Serialization and Deserialization:
Action
instances can be serialized into a JSON format for persistence or transmission and subsequently rehydrated back into fully functional objects. This process includes generating a new unique identifier for the rehydrated action to ensure system-wide uniqueness.
Methods
create
: A factory method for instantiating newAction
objects with specified names, parameters, and execution logic.fromJSON
: Reconstructs anAction
instance from a JSON object, facilitating the restoration of actions from persisted or transmitted data.toJSON
: Serializes anAction
into a JSON-friendly format, capturing its state at a given moment.attach
: Allows for the dynamic attachment of execution logic to an existing action.execute
: Triggers the execution of an action, applying its logic to the given state and producing an effect that encapsulates the outcome and any state transformations.
Result
The Result
class is a critical component of Results-js, engineered to elegantly handle the outcomes of operations within applications. It provides a robust framework for distinguishing between successful outcomes and failures, thereby facilitating error handling, debugging, and operational transparency.
Role and Usage
- Outcome Representation: The
Result
class acts as a container for the outcomes of operations, categorizing them into successes and failures. This distinction aids in explicit error handling and decision-making based on operation results. - Type Safety and Flexibility: It supports generic typing for both success and failure outcomes, ensuring type safety while offering the flexibility to accommodate various data structures and error types.
- Immutability and Predictability: Instances of the
Result
class are immutable once created, reinforcing functional programming principles. This immutability contributes to the predictability and reliability of state management within applications. - Error Handling and Debugging: By encapsulating errors as first-class citizens, the
Result
class simplifies error handling and debugging processes. It allows applications to gracefully manage failures without resorting to exceptions, leading to cleaner and more maintainable code. - Integration with Application Logic:
Result
objects can seamlessly integrate with the broader application logic, including actions and state transitions managed by theAction
class. This integration ensures a cohesive approach to handling operation outcomes and their effects on the application state.
Methods
success
: Creates aResult
instance representing a successful outcome, encapsulating the success value.failure
: Creates aResult
instance representing a failure, encapsulating the error or failure reason.isSuccess
: Checks if aResult
instance represents a success, facilitating conditional logic based on the outcome.isFailure
: Checks if aResult
instance represents a failure, aiding in error handling and recovery strategies.fromJSON
: Deserializes a JSON object into aResult
instance, reconstructing the state of aResult
based on its serialized representation. This static method is crucial for restoringResult
objects from persisted data, enabling the reattachment or identification of the associated action through other means, given the limitation that function references cannot be serialized directly. It employs hooks (before-deserialize-state
,after-deserialize-state
) to provide additional control over the deserialization process, ensuring that the transition states are correctly handled. This method throws an error if the JSON structure is invalid or missing essential properties, ensuring the integrity of the reconstructedResult
.toJSON
: Serializes the current state of theResult
into a JSON-friendly format, returning a plain object that encapsulates theResult
's serializable properties. This method is instrumental for logging, storing, or transmittingResult
instances as JSON strings, capturing essential information such as success status, content, errors, associated action details, and state transitions. The serialization process preserves the critical aspects of theResult
, facilitating subsequent deserialization or analysis.map
: Transforms the successful content of aResult
instance using a specified function. If the result is successful, the transformation function is applied to its content. If the result is a failure, the original errors are preserved, maintaining the failure state without altering the error information.bind
: Applies a function to the successful content of thisResult
, returning a newResult
instance. This method is essential for chaining operations that return aResult
, facilitating the composition of result-producing functions. Even if the content isnull
, the function is applied, allowing for comprehensive handling of all cases.fold
: Executes one of two provided functions based on theResult
's success or failure status. This branching operation enables distinct handling for both outcomes, applying the appropriate function to the content in case of success or to the errors in case of failure, and producing a new value of a potentially different type.recover
: Offers a mechanism to convert a failure into a success, applying a function to the errors to produce new successful content. If theResult
is already successful, it remains unchanged. This method is particularly useful for error recovery strategies, allowing for graceful handling and correction of failures.orElse
: Provides an alternativeResult
in case the current one is a failure. If the currentResult
is successful, it is returned as is. This method is beneficial for providing fallback values or alternative paths in the face of failures, ensuring that the application can continue to operate smoothly.generateDiff
: Calculates the differences between the previous and next states associated with theResult
. This method is valuable for understanding the state changes induced by an action, offering insights into how an operation has transformed the application state. The method returns an array of diff objects representing these changes, orundefined
if no difference can be computed.
Issue
The Issue
class is an integral part of Results-js, designed to represent and manage errors or problems encountered during the execution of operations within applications. It acts as a sophisticated mechanism for error handling, offering a structured approach to capturing and dealing with issues that arise, thereby enhancing application robustness and maintainability.
Role and Usage
- Error Representation: The
Issue
class provides a unified structure for representing errors or failures, enabling detailed error information to be encapsulated within instances. This structured approach aids in precise error handling and categorization. - Type Safety and Detailed Context: It supports detailed typing and context for errors, allowing for the inclusion of error codes, messages, and additional metadata. This ensures that errors are not only captured but also fully described, facilitating easier diagnosis and resolution.
- Immutability and Traceability: Like the
Result
class, instances of theIssue
class are immutable, ensuring that error information remains consistent and unaltered once captured. This immutability is crucial for reliable error logging and auditing processes. - Integration with Operation Outcomes:
Issue
objects can be seamlessly integrated with theResult
class, allowing for a cohesive approach to handling both successful outcomes and failures. This integration ensures that errors are handled as first-class citizens within the application logic. - Enhanced Debugging and Maintenance: By providing clear and detailed error information, the
Issue
class simplifies debugging and maintenance tasks. Developers can quickly identify the source and nature of issues, leading to more efficient problem resolution.
Here's the Methods
section for the Issue
class, formatted to match the structure provided for the Result
class:
Methods
fromAction
: Creates anIssue
instance related to a specific action and its resulting error. This method encapsulates both the error and the context (action) in which it occurred, providing a detailed account of the failure. It is particularly useful for tracing errors back to the actions that triggered them, enhancing error analysis and debugging processes.fromJSON
: Deserializes a JSON object into anIssue
instance, effectively rehydrating the serialized state of anIssue
. This static method facilitates the reconstruction ofIssue
objects from their serialized forms, assuming the associated action can be identified or reattached. It ensures thatIssue
instances can be persisted, transmitted, and then accurately restored, maintaining the integrity of error information.toJSON
: Serializes theIssue
into a JSON-friendly format, providing a representation of the issue's state that is suitable for logging, storage, or transmission. This method extracts the serializable properties of theIssue
, including error messages, associated action details, and other relevant metadata, ensuring that essential information is preserved and readily accessible in a standardized format.
Store
The Store
class is a pivotal component of Results-js, designed to serve as the central hub for state management within applications. It facilitates a cohesive and predictable approach to managing application state, leveraging the principles of immutability and functional programming to ensure reliable and efficient state transitions.
Role and Usage
- Centralized State Management: The
Store
class acts as the single source of truth for the application's state, consolidating state management in one place. This centralized approach simplifies state access, manipulation, and observation across the entire application. - Immutable State Transitions: Adhering to functional programming principles, the
Store
ensures that state transitions are immutable. State changes are made through well-defined actions, promoting predictability and reducing the likelihood of state-related bugs. - Action-Driven State Changes: State changes within the
Store
are initiated by actions, consistent with theAction
class's methodology. This alignment ensures that all state transitions are the result of explicit actions, enhancing traceability and maintainability. - Integration with
Result
andIssue
Classes: TheStore
seamlessly integrates with theResult
andIssue
classes, allowing for a unified approach to handling operation outcomes, errors, and state changes. This integration facilitates comprehensive application logic handling, from action execution to state management and error processing.
Methods
create
: Initializes a newStore
instance with a specified initial state and operating mode. This static method sets up the store, ready for state management, and optionally accepts metadata to provide additional context or configuration. Themode
parameter dictates how the store behaves, particularly in terms of enforcing immutability and logging, while themetadata
can contain any extra information relevant to the store's operation.apply
: Executes an action against the store's current state, potentially updating it based on the action's outcome. This method orchestrates the application of actions, ensuring that state changes are the result of successful actions. It integrates with theHooks
system to provide lifecycle events (before-state-change
,after-state-change
,after-action-cleanup
) for additional control and side-effects management. The method returns aPromise
that resolves to either aResult
or anIssue
, reflecting the success or failure of the action's application and its effect on the state.hydrate
: Restores the store's state from a serialized version, commonly retrieved from an external source like local storage or a server. This asynchronous method parses the serialized state and updates the store, surrounded by a series of hooks (before-hydrate
,state-validation
,after-hydrate
,hydrate-error
,after-hydration-cleanup
) for validating, processing, and handling the hydration process. The method aims to ensure that the store can be accurately reinitialized with a previous state, facilitating features like state persistence and recovery.
Given the structure and intent of your previous class documentations, without direct access to the content of the hooks.ts
file but assuming it provides a mechanism for lifecycle hooks within Results-js, here's a conceptual documentation for the Hooks
class:
Hooks
The Hooks
class is an essential component of Results-js, designed to facilitate event-driven programming by allowing developers to register and trigger custom callback functions (hooks) at specific points within the application flow. This class enhances the library's extensibility and flexibility, enabling custom integrations and behaviors tailored to specific application requirements.
Role and Usage
- Event Registration and Management: The
Hooks
class provides a straightforward interface for registering and managing hooks associated with various events within the application lifecycle, such as before and after state changes, action executions, and error handling processes. - Flexible Hook Invocation: It supports the invocation of registered hooks with relevant context and parameters, allowing for dynamic responses to application events. This feature is crucial for implementing custom logic, such as logging, state validation, or error transformations, in response to specific triggers.
- Decoupled Architecture: By decoupling hook management from the core application logic, the
Hooks
class promotes a clean and modular design. This separation of concerns ensures that the core application functionality remains uncluttered by auxiliary behaviors, which are instead encapsulated within hooks. - Enhanced Application Insights: Utilizing hooks for events such as state transitions and action applications provides valuable insights into the application's runtime behavior, aiding in debugging, performance monitoring, and compliance tracking.
Methods
register
: This method allows for the registration of a hook function for a specified event name, ensuring uniqueness by preventing duplicate registrations for the same event. It validates the input types to ensure the event name is a string and the hook is a function, returningtrue
if the hook is successfully registered andfalse
if a duplicate is detected.unregister
: Removes a specific hook function associated with a given event name. This method facilitates fine-grained control over the hooks, allowing for the removal of individual hooks without affecting others registered for the same event. It returnstrue
if the hook was found and successfully removed, orfalse
if the hook did not exist for the event.has
: Checks for the existence of any registered hooks for a specific event name, offering a quick way to determine if any hooks are set to respond to an event. This method returnstrue
if at least one hook is registered for the event, otherwisefalse
, providing insight into the hooks' setup.invoke
: Triggers the execution of all hooks registered for a particular event name, passing any provided arguments to each hook function. This asynchronous method executes hooks in the order they were registered, handling both synchronous and asynchronous hooks seamlessly. It logs a warning if no hooks are registered for the event, ensuring awareness of potentially unhandled events.clear
: Removes all hooks registered for a specific event name. This method is useful for event-specific cleanup or when needing to refresh the hooks associated with an event, allowing for dynamic adjustment of event handling as application needs evolve.clearAll
: Clears all hooks registered across all events within theHooks
class. This method provides a complete reset of the hooks mechanism, useful for teardown processes, testing, or reinitializing the application state related to hooks.
Examples
(Detailed "usage examples" for each component, including scenarios demonstrating their application and integration within systems, are coming soon.)
Contributing
Fork the Repository: Start by forking the repository to your own GitHub account. This is your workspace where you can make changes without affecting the original project.
Clone Your Fork: Clone your fork to your local machine, so you have a working copy of the codebase.
git clone https://github.com/your-username/project-name.git
Create a Branch: For each new feature or fix, create a new branch. Naming your branch something descriptive can help track work and discussions.
git checkout -b feature/your-feature-name
Make Your Changes: Implement your changes, additions, or fixes. Keep your code clean and well-commented where necessary.
Commit Your Changes: Commit your changes with a clear and descriptive commit message. This message should communicate the changes you've made and why.
git commit -m "Add a brief description of your changes"
Push to Your Fork: Push your changes to your fork on GitHub.
git push origin feature/your-feature-name
Submit a Pull Request: Go to the original repository on GitHub, and you'll see a prompt to submit a pull request from your new branch. Fill in the pull request with details about your changes and why they should be included.
Code Review: Wait for a review from the project maintainers. There might be discussions, suggestions, or requests for changes to your code. Please be patient and receptive to feedback.
Guidelines
- Follow the Project's Code Style: Ensure your code adheres to the existing code style of the project to maintain consistency.
- Write Tests: If adding new features, include tests that cover your changes.
- Update Documentation: If your changes require it, update the documentation to reflect your contributions accurately.
- Check for Existing Issues: Before submitting a new issue, check if it has already been reported. If you find an existing issue similar to yours, contribute to the ongoing discussion.
Testing
Testing is a critical part of maintaining the project's reliability and stability. We encourage contributors to write tests for new code and run existing tests before submitting pull requests. Here's how you can run the tests for this project:
Install Dependencies: Ensure all necessary dependencies are installed by running:
npm install
or if you're using Yarn:
yarn install
Run the Tests: Execute the test suite using the command:
npm test
or with Yarn:
yarn test
This will run the entire suite of automated tests and output the results. If any tests fail, please address the failures before submitting your pull request.
Writing Tests
When adding new features or fixing bugs, include tests that cover your changes. Good tests help ensure that changes don't break existing functionality and make the codebase more maintainable.
- Unit Tests: Write unit tests for individual functions or components to ensure they behave as expected under various conditions.
- Integration Tests: If your changes involve interactions between multiple components, consider adding integration tests to verify that these interactions work correctly.
By contributing tests and actively participating in the testing process, you help ensure the project's long-term quality and success.
These sections aim to foster a collaborative and quality-focused development environment. Feel free to adjust the content to better match your project's specific contribution process and testing frameworks.---
License
This project is licensed under the MIT License - see the LICENSE file for details.
The MIT License is a permissive license that is short and to the point. It lets people do anything they want with your code as long as they provide attribution back to you and don’t hold you liable.
MIT License Summary:
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the Software.
By using this library, you agree to the terms of the MIT License as stated. Contributions to this library are also accepted under the same license.