diff --git a/CHANGELOG.md b/CHANGELOG.md
index 80d2a852..6387e586 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,18 +9,48 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
-* Add upgrade guide from v0.7.x- to v0.10.x.
+* Service Container package (`@aedart/container`).
+* `Facade` abstraction, in `@aedart/support/facades`.
+* `DEPENDENCIES` symbol and `Identifier` type in `@aedart/contracts/container`.
+* `dependsOn()`, `dependencies()`, `hasDependencies()`, and `getDependencies()`, in `@aedart/support/container`.
+* `isBindingIdentifier`, in `@aedart/support/container`.
+* `ClassMethodName` and `ClassMethodReference` type aliases in `@aedart/contracts`.
+* `isMethod()` util in `@aedart/support/reflections`.
+* `ConstructorLike` and `Callback` type aliases, in `@aedart/constracts`.
+* `CallbackWrapper` util class, in `@aedart/support`.
+* `isCallbackWrapper` util, in `@aedart/support`.
+* `ArbitraryData` concern, in `@aedart/support`.
+* `arrayMergeOptions` in object `merge()`.
+* Add upgrade guide for "v0.7.x- to v0.10.x".
### Changed
+**Breaking**
+
+* Added `hasAny()` method in `TargetRepository` interface, in `@aedart/contracts/meta`.
+* Default generic for `defaultValue` changed to `undefined`, for `get()` methods in meta `Repository` and `TargetRepository`.
+
+**Non-breaking Changes**
+
* Root package Typescript dependency changed to `^5.4.2`.
* `@typescript-eslint/eslint-plugin` upgraded to `^7.1.1`, in root package.
-* Decorator return types for `meta()`, `targetMeta()`, and `inheritTargetMeta()` (_continued to cause TS1270 and TS1238 errors_). [#8](https://github.com/aedart/ion/pull/8), [#9](https://github.com/aedart/ion/pull/9).
+* Refactored all classes' fields, changed from private to protected visibility (_see [private is not inherited](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain) for details in_).
+* Removed decorator return types for `use()`, `meta()`, `targetMeta()`, and `inheritTargetMeta()` (_continued to cause TS1270 and TS1238 errors_). [#8](https://github.com/aedart/ion/pull/8), [#9](https://github.com/aedart/ion/pull/9).
+* Refactored `hasAllMethods()` to use new `isMethod()` internally, in `@aedart/support/reflections`.
+* Refactored all components that used deprecated `ConstructorOrAbstractConstructor` to use new `ConstructorLike` type alias.
+* Marked `isClassConstructor()` and `isCallable()` as stable, in `@aedart/support/reflections`.
+* Refactored / redesigned the array `merge()` to use a new `ArrayMerger` component, that allows custom merge callback and options.
### Fixed
* Decorator types aliases (_TS1270 and TS1238 issues when applying the various decorator and decorator result types_). [#8](https://github.com/aedart/ion/pull/8).
* Broken link in docs for `isArrayLike`.
+* Missing `tslib` as peer dependency for `@aedart/support` package.
+* Unable to merge arrays containing functions, in `MetaRepository`.
+
+### Deprecated
+
+* `ConstructorOrAbstractConstructor` type alias. It has been replaced with the new `ConstructorLike` type., in `@aedart/constracts`.
## [0.10.0] - 2024-03-07
diff --git a/NOTICE b/NOTICE
index 50a07f35..f7871245 100644
--- a/NOTICE
+++ b/NOTICE
@@ -2,8 +2,8 @@ NOTICES AND INFORMATION
Please do not translate or Localize.
Parts of the herein provided software are considered an "adaptation", or "derivative work", of 3rd party software.
-Below you will find general information about which parts are affected, or where you may find additional information
-such, along with original license(s), terms and conditions as provided by the 3rd party software.
+Below you will find general information about which parts are affected, or where you may find additional information,
+along with original license(s), terms and conditions as provided by the 3rd party software.
3rd party software that are included as dependencies by this software is NOT covered by this NOTICE file, unless
explicitly required by 3rd party software license(s). You can find original license(s), terms and
diff --git a/aliases.js b/aliases.js
index b5f9cc7b..2034e3f5 100644
--- a/aliases.js
+++ b/aliases.js
@@ -18,10 +18,15 @@ module.exports = {
// conditionNames: ['require', 'import'],
alias: {
+ // container
+ '@aedart/container': path.resolve(__dirname, './packages/container/src'),
+
// contracts
+ '@aedart/contracts/container': path.resolve(__dirname, './packages/contracts/container'),
'@aedart/contracts/support/arrays': path.resolve(__dirname, './packages/contracts/support/arrays'),
'@aedart/contracts/support/concerns': path.resolve(__dirname, './packages/contracts/support/concerns'),
'@aedart/contracts/support/exceptions': path.resolve(__dirname, './packages/contracts/support/exceptions'),
+ '@aedart/contracts/support/facades': path.resolve(__dirname, './packages/contracts/support/facades'),
'@aedart/contracts/support/meta': path.resolve(__dirname, './packages/contracts/support/meta'),
'@aedart/contracts/support/mixins': path.resolve(__dirname, './packages/contracts/support/mixins'),
'@aedart/contracts/support/objects': path.resolve(__dirname, './packages/contracts/support/objects'),
diff --git a/docs/.vuepress/archive/Version0x.ts b/docs/.vuepress/archive/Version0x.ts
index 93955b40..b77c55c6 100644
--- a/docs/.vuepress/archive/Version0x.ts
+++ b/docs/.vuepress/archive/Version0x.ts
@@ -22,6 +22,20 @@ export default PagesCollection.make('v0.x', '/v0x', [
collapsible: true,
children: [
'packages/',
+ {
+ text: 'Container',
+ collapsible: true,
+ children: [
+ 'packages/container/',
+ 'packages/container/prerequisites',
+ 'packages/container/install',
+ 'packages/container/container-instance',
+ 'packages/container/bindings',
+ 'packages/container/dependencies',
+ 'packages/container/resolving',
+ 'packages/container/contextual-bindings',
+ ]
+ },
{
text: 'Contracts',
collapsible: true,
@@ -77,6 +91,13 @@ export default PagesCollection.make('v0.x', '/v0x', [
'packages/support/exceptions/customErrors',
]
},
+ {
+ text: 'Facades',
+ collapsible: true,
+ children: [
+ 'packages/support/facades/',
+ ]
+ },
{
text: 'Meta',
collapsible: true,
@@ -141,9 +162,13 @@ export default PagesCollection.make('v0.x', '/v0x', [
'packages/support/reflections/hasAllMethods',
'packages/support/reflections/hasMethod',
'packages/support/reflections/hasPrototypeProperty',
+ 'packages/support/reflections/isCallable',
+ 'packages/support/reflections/isClassConstructor',
+ 'packages/support/reflections/isClassMethodReference',
'packages/support/reflections/isConstructor',
'packages/support/reflections/isKeySafe',
'packages/support/reflections/isKeyUnsafe',
+ 'packages/support/reflections/isMethod',
'packages/support/reflections/isSubclass',
'packages/support/reflections/isSubclassOrLooksLike',
'packages/support/reflections/isWeakKind',
@@ -164,6 +189,7 @@ export default PagesCollection.make('v0.x', '/v0x', [
'packages/support/misc/toWeakRef',
]
},
+ 'packages/support/CallbackWrapper',
]
},
{
diff --git a/docs/archive/current/README.md b/docs/archive/current/README.md
index d57681aa..dc353931 100644
--- a/docs/archive/current/README.md
+++ b/docs/archive/current/README.md
@@ -29,6 +29,46 @@ _TBD: "To be decided"._
## `v0.x` Highlights
+### Service Container
+
+An adaptation of Laravel's Service Container that offers a way to with powerful tool to manage dependencies and perform
+dependency injection.
+
+```js
+import { Container } from "@aedart/container";
+
+container.bind('storage', () => {
+ return new CloudService('s3');
+});
+
+// Later in your application.
+const storage = container.make('storage');
+```
+
+For additional examples, see the [Service Container documentation](./packages/container/README.md).
+
+### Facades
+
+Adaptation of Laravel's Facade component. It acts as an interface or gateway to an underlying object that is resolved
+from the Service Container.
+
+```js
+import { Facade } from "@aedart/support/facades";
+
+export default class ApiFacade extends Facade
+{
+ static getIdentifier()
+ {
+ return 'api_client';
+ }
+}
+
+// Later in your application
+const promise = ApiFacade.obtain().fetch('https://acme.com/api/users');
+```
+
+See the [Facades documentation](./packages/support/facades/README.md) for additional details.
+
### Concerns
Intended as an alternative to mixins, the [Concerns](./packages/support/concerns/README.md) submodule offers a different
diff --git a/docs/archive/current/packages/container/README.md b/docs/archive/current/packages/container/README.md
new file mode 100644
index 00000000..ee026d19
--- /dev/null
+++ b/docs/archive/current/packages/container/README.md
@@ -0,0 +1,83 @@
+---
+title: Introduction
+description: Ion Service Container package
+sidebarDepth: 0
+---
+
+# Introduction
+
+The `@aedart/container` package offers an adaptation of [Laravel's Service Container](https://laravel.com/docs/11.x/container)
+(_originally licensed under [MIT](https://github.com/laravel/framework/blob/11.x/src/Illuminate/Container/LICENSE.md)_).
+
+The tools provided by this package give you a way to:
+* Manage class dependencies
+* Perform [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection)
+
+## Example
+
+### Bindings
+
+Imagine that you have an Api client (_or any component for that matter_). Whenever it is needed, you want it to be
+injected into components that depend on it.
+
+```js
+export default class ApiClient
+{
+ // ...implementation not shown...
+}
+```
+
+To ensure that dependency injection can be performed, you must first bind the component in the service container.
+Each binding requires a unique identifier, e.g. a string, symbol, number...etc.
+
+```js
+import { Container } from "@aedart/container";
+import { ApiClient } from "@acme/api";
+
+const container = Container.getInstance();
+
+// Bind 'my_api_client' to the ApiClient component...
+container.bind('my_api_client', ApiClient);
+```
+
+### Define Dependencies
+
+To define the dependencies of a component, use the `dependencies()` decorator.
+By itself, the decorator does not do anything more than to associate a component with one or more dependencies
+(_binding identifiers_). In other words, the decorator _**does not automatically inject**_ anything into your class.
+It only registers the dependencies as [metadata](../support/meta) onto a class.
+
+```js
+import { dependencies } from "@aedart/support/container";
+
+@dependencies('my_api_client')
+export default class BookService
+{
+ apiClient;
+
+ constructor(client) {
+ this.apiClient = client;
+ }
+
+ // ...remaining not shown...
+}
+```
+
+### Resolve
+
+When you want to resolve a component, with all of its dependencies injected into it, use the service container's `make()`
+method.
+
+```js
+import { Container } from "@aedart/container";
+import { BookService } from "@acme/app/services";
+
+const bookService = Container.getInstance().make(BookService);
+
+console.log(bookService.apiClient); // ApiClient
+```
+
+### Onward
+
+The above shown example illustrates the most basic usage of the service container. Throughout the remaining of this
+package's documentation, more examples and use-cases are covered.
\ No newline at end of file
diff --git a/docs/archive/current/packages/container/bindings.md b/docs/archive/current/packages/container/bindings.md
new file mode 100644
index 00000000..67b38c94
--- /dev/null
+++ b/docs/archive/current/packages/container/bindings.md
@@ -0,0 +1,184 @@
+---
+description: Service Container Bindings
+sidebarDepth: 0
+---
+
+# Bindings
+
+[[TOC]]
+
+## Basics
+
+The `bind()` method is used to register bindings in the Service Container. It accepts three arguments:
+
+* `identifier: Identifier` - (_see [Identifiers](#identifiers)_).
+* `concrete: FactoryCallback | Constructor` - The value to be resolved from the container.
+* `shared: boolean = false` - (_optional - see [Singletons](#singletons)_).
+
+```js
+import { CookieStorage } from "@acme/storage";
+
+container.bind('storage', CookieStorage);
+```
+
+When the binding is [resolved](./resolving.md) from the Service Container, the `concrete` value is returned. Either as a new class
+instance (_see [Constructors](#constructors)_), or the value returned from a callback (_see [Factory Callbacks](#factory-callbacks)_).
+
+You can also use `bindIf()` to register a binding. The method will _ONLY_ register the binding, if one has not already
+been registered for the given identifier.
+
+```js
+container.bindIf('storage', CookieStorage);
+```
+
+### Singletons
+
+If you wish to register a "shared" binding, use the `singleton()` method. It ensures that the binding is only resolved
+once. This means that the same object instance or value is returned, each time that it is requested resolved.
+Invoking the `singleton()` is the equivalent to invoking `bind()` with the `shared` argument set to `true`.
+
+```js
+import { ApiClient } from "@acme/api";
+
+container.singleton('api_client', ApiClient);
+```
+
+The `singletonIf()` method is similar to `bindIf()`. It will only register a "shared" binding, if one has not already
+been registered.
+
+```js
+container.singletonIf('api_client', ApiClient);
+```
+
+### Instances
+
+You can also register existing object instances in the Service Container. This is done via the `instance()` method.
+Whenever the binding is requested resolved, the same instance is returned each time.
+
+```js
+import { ApiClient } from "@acme/api";
+
+const client = new ApiClient();
+
+container.instance('api_client', client);
+```
+
+## Identifiers
+
+To ensure that the Service Container is able to resolve the correct object instances or values, the binding identifiers
+must be unique. The following types are supported as binding identifiers:
+
+* `string`
+* `symbol`
+* `number`
+* `object` (_not `null`_)
+* Class Constructor
+* Callback
+
+::: tip
+
+To ensure that identifiers are truly unique, you _should_ use [symbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol) or
+[class constructors](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) as binding identifiers.
+
+```js
+// Somewhere in your application
+export const STORAGE = Symbol('app_storage');
+```
+
+```js
+import { CookieStorage } from "@acme/storage";
+import { STORAGE } from "@acme/services";
+
+container.bind(STORAGE, CookieStorage);
+```
+
+:::
+
+## `concrete` Types
+
+The `concrete` argument for the `bind()`, `bindIf()`, `singleton()` and `singletonIf()` methods accepts
+the following types:
+
+* Class constructor
+* "Factory" callback
+
+### Constructors
+
+When registering a binding using a class constructor as the `concrete` argument value, a new instance of that class is
+instantiated and returned, when requested [resolved](./resolving.md).
+
+```js
+class TextRecorder {}
+
+container.bind('recorder', TextRecorder);
+
+// Later in your application
+const recorder = container.make('recorder');
+
+console.log(recorder instanceof TextRecorder); // true
+```
+
+### Factory Callbacks
+
+If you need more advanced resolve logic, then you can specify a callback as the `concrete` argument value.
+When requested resolved, the callback is invoked and the Service Container instance is passed as argument to the callback.
+This allows you to perform other kinds of resolve logic.
+
+```js
+class TextRecorder {
+ constructor(config) {
+ this.config = config;
+ }
+}
+
+container.bind('recorder', (container) => {
+ const config = container.make('my_recorder_config');
+
+ return new TextRecorder(config);
+});
+```
+
+Although the above example shows an object instance being returned by the factory callback, any kind of value can be returned
+by the callback.
+
+```js
+container.bind('my_message', () => 'Hi there...');
+
+// Later in your application
+const msg = container.make('my_message'); // Hi there...
+```
+
+#### Arguments
+
+The factory callback is also provided with any arguments that are passed on to the [`make()` method](./resolving.md#the-make-method).
+
+```js
+class User {
+ constructor(name) {
+ this.name = name;
+ }
+}
+
+container.bind('user', (container, ...args) => {
+ return new User(...args);
+});
+
+// Later in your application
+const user = container.make('user', 'Maya');
+
+console.log(user.name); // Maya
+```
+
+## Extend Bindings
+
+The `extend()` method can be used to decorate or configure object instances that have been resolved.
+The method accepts the following arguments:
+
+* `identifier: Identifier` - the target binding identifier.
+* `callback: ExtendCallback` - callback that is responsible for modifying the resolved instance.
+
+```js
+container.extend('user', (resolved, container) => {
+ return DecoratedUser(resolved);
+});
+```
\ No newline at end of file
diff --git a/docs/archive/current/packages/container/container-instance.md b/docs/archive/current/packages/container/container-instance.md
new file mode 100644
index 00000000..53df8252
--- /dev/null
+++ b/docs/archive/current/packages/container/container-instance.md
@@ -0,0 +1,34 @@
+---
+description: How to obtain Service Container instance
+sidebarDepth: 0
+---
+
+# Container Instance
+
+The Service Container can be instantiated like any other regular class. This allows you to use the container in
+isolation, without application-wide side effects.
+
+```js
+import { Container } from "@aedart/container";
+
+const container = new Container();
+```
+
+However, if you want the use the same Service Container instance across your entire application, then you can obtain a [singleton](https://en.wikipedia.org/wiki/Singleton_pattern)
+instance, via the static method `getInstance()`.
+
+The `getInstance()` method will automatically create a new Service Container instance and store a static reference to it, if no previous instance
+was created. Otherwise, the method will return the existing instance.
+
+```js
+const container = Container.getInstance();
+```
+
+## Destroy Existing Instance
+
+In situations when you need to destroy the existing singleton instance, call the static `setInstance()` method
+with `null` as argument.
+
+```js
+Container.setInstance(null); // Existing singleton instance is now lost...
+```
\ No newline at end of file
diff --git a/docs/archive/current/packages/container/contextual-bindings.md b/docs/archive/current/packages/container/contextual-bindings.md
new file mode 100644
index 00000000..59314608
--- /dev/null
+++ b/docs/archive/current/packages/container/contextual-bindings.md
@@ -0,0 +1,70 @@
+---
+description: Define Contextual Bindings
+sidebarDepth: 0
+---
+
+# Contextual Bindings
+
+In situations when multiple classes make use of the same dependency, but you wish to inject a different component or
+value on some of those classes, then you can make use of "context binding".
+The `when()` method allows you to specify (_overwrite_) the implementation to be resolved and injected, for a given
+target class.
+
+```js
+container.when(ApiService)
+ .needs('storage')
+ .give(CookieStorage);
+
+container.when(UsersRepository, BooksRepository)
+ .needs('api_client')
+ .give(() => {
+ return new AcmeApiClient();
+ });
+```
+
+To illustrate the usefulness of contextual binding a bit further, consider the following example:
+
+```js
+@dependency('storage')
+class A {
+ // ...not shown...
+}
+
+@dependency('storage')
+class B {
+ // ...not shown...
+}
+
+@dependency('storage')
+class C {
+ // ...not shown...
+}
+
+@dependency('storage')
+class D {
+ // ...not shown...
+}
+
+// Register "default" storage binding
+container.singleton('storage', CookieStorage);
+
+// Register contextual binding for C and D
+container.when(C, D)
+ .needs('storage')
+ .give(() => {
+ return new CloudStorage('s3');
+ });
+```
+
+In the above shown example, all classes define the same binding identifier (_"storage"_) as a dependency.
+By default, a "storage" binding is registered in the Service Container, which ensures that when the classes are resolved,
+a `CookieStorage` component instance is injected into each target class instance.
+
+However, classes `C` and `D` require a different implementation, than the one offered by the "storage" binding.
+To achieve this, and without overwriting the default "storage" binding, a new contextual binding is registered that affects
+only classes `C` and `D`. When they are resolved, a different implementation of injected into the target classes.
+
+```js
+const c = container.make(C);
+console.log(c.storage); // CloudStorage
+```
\ No newline at end of file
diff --git a/docs/archive/current/packages/container/dependencies.md b/docs/archive/current/packages/container/dependencies.md
new file mode 100644
index 00000000..ea19a97e
--- /dev/null
+++ b/docs/archive/current/packages/container/dependencies.md
@@ -0,0 +1,63 @@
+---
+description: How to define dependencies
+sidebarDepth: 0
+---
+
+# Dependencies
+
+In order for the Service Container to be able to automatically inject dependencies, when [resolving](./resolving.md)
+components, you must first define them on a target class. The `dependencies()` decorator is used for this purpose.
+
+```js
+import { dependencies } from "@aedart/support/container";
+
+@dependencies('engine')
+export default class Car
+{
+ engine = undefined;
+
+ constructor(engine) {
+ this.engine = engine;
+ }
+}
+```
+
+::: tip No automatic injection
+The `dependencies()` decorator _**does not automatically inject**_ anything into your class.
+It will only associate binding identifiers with the target class, as [metadata](../support/meta).
+This means that you can instantiate a new instance of the class, without any side effects (_dependencies must
+be manually given as arguments to the target class_).
+
+```js
+const car = new Car();
+console.log(car.engine); // undefined
+```
+
+The Service Container's [`make()` method](./resolving.md#the-make-method) is responsible for reading the defined
+dependencies, resolve them, and inject them into the target class.
+:::
+
+## Multiple Dependencies
+
+The `dependencies()` decorator accepts an arbitrary amount of binding identifiers. This allows you to define multiple
+dependencies in a single call.
+
+```js
+@dependencies(
+ 'warehouse_manager',
+ 'api_client',
+ 'events'
+)
+export default class Warehouse
+{
+ manager = undefined;
+ apiClient = undefined;
+ eventDispatcher = undefined;
+
+ constructor(manager, apiClient, dispatcher) {
+ this.manager = manager;
+ this.apiClient = apiClient;
+ this.eventDispatcher = dispatcher;
+ }
+}
+```
\ No newline at end of file
diff --git a/docs/archive/current/packages/container/install.md b/docs/archive/current/packages/container/install.md
new file mode 100644
index 00000000..a54a770e
--- /dev/null
+++ b/docs/archive/current/packages/container/install.md
@@ -0,0 +1,24 @@
+---
+description: How to install Ion Service Container package
+sidebarDepth: 0
+---
+
+# How to install
+
+## npm
+
+```bash:no-line-numbers
+npm install --save-peer @aedart/container
+```
+
+## yarn
+
+```bash:no-line-numbers
+yarn add --peer @aedart/container
+```
+
+## pnpm
+
+```bash:no-line-numbers
+pnpm add --save-peer @aedart/container
+```
\ No newline at end of file
diff --git a/docs/archive/current/packages/container/prerequisites.md b/docs/archive/current/packages/container/prerequisites.md
new file mode 100644
index 00000000..b1bf596a
--- /dev/null
+++ b/docs/archive/current/packages/container/prerequisites.md
@@ -0,0 +1,10 @@
+---
+title: Prerequisites
+description: Prerequisites for using service container.
+sidebarDepth: 0
+---
+
+# Prerequisites
+
+At the time of this writing, [decorators](https://github.com/tc39/proposal-decorators) are still in a proposal phase.
+To use the service container, you must either use [@babel/plugin-proposal-decorators](https://babeljs.io/docs/babel-plugin-proposal-decorators), or use [TypeScript 5 decorators](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-0.html#decorators).
\ No newline at end of file
diff --git a/docs/archive/current/packages/container/resolving.md b/docs/archive/current/packages/container/resolving.md
new file mode 100644
index 00000000..5fa701be
--- /dev/null
+++ b/docs/archive/current/packages/container/resolving.md
@@ -0,0 +1,223 @@
+---
+description: Resolving Dependencies
+sidebarDepth: 0
+---
+
+# Resolving
+
+[[TOC]]
+
+## The `make()` method
+
+To resolve component instances or values from the Service Container, use the `make()` method.
+It accepts the following arguments:
+
+* `identifier: Identifier` - Target [binding identifier](./bindings.md#identifiers).
+* `args: any[] = []` - (_optional_) Eventual arguments to be passed on to [class constructor](./bindings.md#constructors) or ["factory" callback](./bindings.md#factory-callbacks).
+
+```js
+const recorder = container.make('recorder');
+```
+
+When specifying a class constructor as the `identifier` argument, the `make()` method will automatically attempt to
+create a new instance of the given class, even if no binding was registered for it.
+
+```js
+class AudioPlayer
+{
+ // ...not shown...
+}
+
+const audio = container.make(AudioPlayer); // new AudioPlayer instance
+```
+
+### Dependencies
+
+If the target that must be resolved is a class that has [dependencies defined](./dependencies.md) as [metadata](../support/meta),
+then the `make()` method will automatically resolve them, and inject them into the target class.
+
+```js
+import { dependencies } from "@aedart/support/container";
+
+@dependencies('storage')
+class TextRecorder
+{
+ storage = undeinfed;
+
+ constructor(storage) {
+ this.storage = storage;
+ }
+}
+
+// Register binding in the Service Container
+container.singleton('storage', () => {
+ return new CookieStorage();
+});
+
+
+// ...Later in your application
+const recorder = container.make(TextRecorder);
+console.log(recorder.storage); // CookieStorage
+```
+
+### The `args` Argument
+
+You can also manually specify what arguments a class constructor or "factory" callback should receive, via the `args` argument.
+
+```js
+const recorder = container.make(TextRecorder, [ new CloudStorage() ]);
+console.log(recorder.storage); // CloudStorage
+```
+
+::: warning
+When specifying the `args` argument for `make()`, any defined dependencies are **overwritten** by the values
+in the `args` array, if a class constructor is requested resolved!
+In other words, the binding identifiers defined via the [`dependencies` decorator](./dependencies.md) are ignored.
+:::
+
+## The `call()` method
+
+The Service Container can also be used to invoke class methods or callbacks. This allows you to resolve a method's dependencies
+and inject them.
+The `call()` method accepts the following arguments:
+
+* `method: Callback | CallbackWrapper | ClassMethodReference` - The target callback or class method to invoke.
+* `args: any[] = []` - (_optional_) Eventual arguments to be passed on to class method or callback.
+
+```js
+class UsersRepository
+{
+ @dependencies('users_api_service')
+ fetchUser(usersService)
+ {
+ // ...not shown...
+ }
+}
+
+// Later in your application
+const promise = container.call([UsersRepository, 'fetchUser']);
+```
+
+### Class Method Reference
+
+A "class method reference" is an array that holds two values:
+
+* A class constructor or object instance.
+* The name of the method to be invoked in the target class.
+
+```js
+const reference = [AudioPlayer, 'play'];
+```
+
+When given as the `method` argument, for `call()`, the target class constructor is automatically resolved (_instantiated with eventual dependencies injected_).
+The method is thereafter invoked and output is returned.
+If the class method has any dependencies defined, then those will be resolved and injected into the method as arguments.
+
+```js
+class AudioPlayer
+{
+ @dependencies('audio_processor', 'my_song')
+ play(processor, song) {
+ // ...play logic not shown...
+ return this;
+ }
+}
+
+const player = container.call([AudioPlayer, 'play']);
+```
+
+::: warning
+If you specify the `args` argument for `call()`, then eventual defined dependencies are **overwritten** with the values
+provided in the `args` array. Thus, the dependencies of the class method are ignored.
+
+```js
+const player = container.call(
+ [AudioPlayer, 'play'],
+
+ // Arguments passed on to "play" method.
+ [
+ new AudioProcessor(),
+ new FavouriteSong()
+ ]
+);
+```
+:::
+
+### Callback Wrapper
+
+When specifying a [callback wrapper](../support/CallbackWrapper.md) as target for `call()`, then the callback will be
+invoked and eventual output is returned. If the wrapper has arguments specified, then they will automatically be applied,
+the underlying callback is invoked.
+
+::: warning
+Providing the `args` argument for `call()` will **overwrite** eventual arguments set in the callback wrapper!
+
+```js
+import { CallbackWrapper } from "@aedart/support";
+
+const wrapped = CallbackWrapper.make((firstname, lastname) => {
+ return `Hi ${firstname} ${lastname}`;
+}, 'Brian', 'Jackson');
+
+const result = container.call(wrapped, [ 'James', 'Brown' ]);
+console.log(result); // Hi James Brown
+```
+:::
+
+To define dependencies for a callback wrapper, you must use the wrapper's `set()` method and specify an array of target
+binding identifiers for the `DEPENDENCIES` symbol as key.
+
+```js
+import { DEPENDENCIES } from "@aedart/contracts/container";
+
+const wrapped = CallbackWrapper.make((apiClient) => {
+ // ...fetch user logic not shown...
+
+ return promise;
+}).set(DEPENDENCIES, [ 'api_client' ]);
+
+const promise = container.call(wrapped); // Api Client injected into callback...
+```
+
+### Callback
+
+The `call()` can also be used for invoking a regular callback. Any `args` argument given to `call()` are passed on to
+the callback, and eventual output value is returned.
+
+```js
+const result = container.call((x) => {
+ return x * 2;
+}, 4);
+
+console.log(result); // 8
+```
+
+::: warning Limitation
+At the moment, it is not possible to associate dependencies with a native callback directly.
+Please use a [callback wrapper](#callback-wrapper) instead, if you need to inject dependencies into a callback.
+:::
+
+## Hooks
+
+If you need to react to components or values that are being resolved from the Service Container, then you can use the
+`before()` and `after()` hook methods.
+
+### `before()`
+
+The `before()` method registers a callback to be invoked before a binding is resolved.
+
+```js
+container.before('user', (identifier, args, container) => {
+ // ...not shown...
+});
+```
+
+### `after()`
+
+The `after()` method registers a callback to be invoked after a binding has been resolved
+
+```js
+container.after('user', (identifier, resolved, container) => {
+ // ...not shown...
+});
+```
\ No newline at end of file
diff --git a/docs/archive/current/packages/support/CallbackWrapper.md b/docs/archive/current/packages/support/CallbackWrapper.md
new file mode 100644
index 00000000..310a1de3
--- /dev/null
+++ b/docs/archive/current/packages/support/CallbackWrapper.md
@@ -0,0 +1,169 @@
+---
+title: Callback Wrapper
+description: Wrapper object for a callback
+sidebarDepth: 0
+---
+
+# Callback Wrapper
+
+The `CallbackWrapper` objects offers a convenient way to wrap a callable function.
+
+```js
+import { CallbackWrapper } from "@aedart/support";
+
+const wrapped = CallbackWrapper.make(() => {
+ return 'Hi there...';
+});
+
+// Later in your application
+wrapped.call(); // Hi there...
+```
+
+[[TOC]]
+
+## Call
+
+The `call()` method invokes the wrapped callback and returns its eventual output.
+
+```js
+const wrapped = CallbackWrapper.make(() => {
+ return true;
+});
+
+wrapped.call(); // true
+```
+
+## Arguments
+
+There are several ways to specify arguments that must be applied for the wrapped callback, when `call()` is invoked.
+
+### Via `make()`
+
+The static `make()` method allows you to specify arguments right away.
+This is useful, if you already know the arguments.
+
+```js
+const wrapped = CallbackWrapper.make((firstname, lastname) => {
+ return `Hi ${firstname} ${lastname}`;
+}, 'Timmy', 'Jackson');
+
+wrapped.call(); // Hi Timmy Jackson
+```
+
+### Via `with()`
+
+In situations when you must add additional arguments, e.g. because you might not know all arguments up front, then you
+can use the `with()` method.
+
+```js
+const wrapped = CallbackWrapper.make((firstname, lastname) => {
+ return `Hi ${firstname} ${lastname}`;
+}, 'Siw');
+
+wrapped
+ .with('Orion')
+ .call(); // Hi Siw Orion
+```
+
+### Via `arguments`
+
+Lastly, in situations when you must completely overwrite all arguments, then you can specify them via the `arguments`
+property.
+
+```js
+const wrapped = CallbackWrapper.make((firstname, lastname) => {
+ return `Hi ${firstname} ${lastname}`;
+});
+
+wrapped.arguments = [ 'Alpha', 'Zero' ];
+wrapped
+ .call(); // Hi Alpha Zero
+```
+
+## Binding
+
+Use `bind()` to specify the callback's [`this` value](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
+
+```js
+class A {
+ sayHi(name) {
+ return `Hi ${name}`;
+ }
+}
+const instance = new A();
+
+const wrapped = CallbackWrapper.make(function(name) {
+ return this.sayHi(name);
+});
+
+wrapped
+ .bind(instance)
+ .with('Akari')
+ .call(); // Hi Akari
+```
+
+### Binding vs. Arrow Function
+
+::: warning
+It is not possible to apply a binding on an arrow function callback. Doing so can result in a `TypeError` or other unexpected behaviour.
+See [Mozilla's documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) for additional information.
+
+**_:x:_**
+
+```js
+// Callback Wrapper for arrow function...
+const wrapped = CallbackWrapper.make(() => {
+ // ...not shown ...
+});
+
+wrapped
+ .bind(myObject)
+ .call(); // TypeError
+```
+
+**_:heavy_check_mark:_**
+
+```js
+// Callback Wrapper for normal function...
+const wrapped = CallbackWrapper.make(function () {
+ // ...not shown ...
+});
+
+wrapped
+ .bind(myObject)
+ .call();
+```
+:::
+
+## Misc.
+
+If you need to determine if a value is a "callback wrapper" object, then you can use the `isCallbackWrapper()` util.
+
+```js
+import { isCallbackWrapper, CallbackWrapper } from "@aedart/support";
+
+isCallbackWrapper(() => true); // false
+isCallbackWrapper(CallbackWrapper.make(() => true)); // true
+```
+
+### Custom Callback Wrapper
+
+`isCallbackWrapper()` can also accept custom implementation of a callback wrapper.
+
+```js
+// Custom implementation of a callback wrapper
+const custom = {
+ 'callback': function() { /* not shown */ },
+ 'binding': undefined,
+ 'arguments': [],
+ 'with': function() { /* not shown */ },
+ 'hasArguments': function() { /* not shown */ },
+ 'bind': function() { /* not shown */ },
+ 'hasBinding': function() { /* not shown */ },
+ 'call': function() { /* not shown */ },
+};
+
+isCallbackWrapper(custom); // true
+```
+
+_See the source code of `isCallbackWrapper()` for additional details._
\ No newline at end of file
diff --git a/docs/archive/current/packages/support/arrays/merge.md b/docs/archive/current/packages/support/arrays/merge.md
index c7a54b64..17055106 100644
--- a/docs/archive/current/packages/support/arrays/merge.md
+++ b/docs/archive/current/packages/support/arrays/merge.md
@@ -6,6 +6,8 @@ sidebarDepth: 0
# `merge`
+[[TOC]]
+
Merges arrays into a new array.
This function attempts to deep copy values, using [`structuredClone`](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone).
@@ -21,7 +23,7 @@ merge(a, b, c); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
## Deep Copy Objects
-Simple (_or "plain"_) objects [deep copied](https://developer.mozilla.org/en-US/docs/Glossary/Deep_copy).
+Simple (_or "plain"_) objects are [deep copied](https://developer.mozilla.org/en-US/docs/Glossary/Deep_copy).
This means that new objects are returned in the resulting array.
See [Mozilla's documentation](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) for additional
@@ -48,4 +50,61 @@ const a = [ 1, 2, 3 ];
const b = [ function() {} ]; // A function cannot be deep copied...
merge(a, b); // ArrayMergeError
-```
\ No newline at end of file
+```
+
+_See [merge options](#merge-options) for details on how to deal with functions._
+
+## Merge Options
+
+`merge()` supports a number of options. To specify thom, use the `using()` method.
+
+```js
+merge()
+ .using({ /** option: value */ })
+ .of(arrayA, arrayB, arrayC);
+```
+
+::: tip Note
+When invoking `merge()` without any arguments, an underlying array `Merger` instance is returned.
+:::
+
+### `transferFunctions`
+
+By default, functions are not transferred (_not copied_). When encountered an `ArrayMergeError` is thrown, because
+the underlying [`structuredClone`](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) is not able to
+duplicate functions. To change this behaviour, you can set the `transferFunctions` setting to `true`. Function are then
+"transferred" into the resulting array.
+
+```js
+const foo = () => true;
+const bar = () => false;
+
+merge()
+ .using({ transferFunctions: true })
+ .of([ foo ], [ bar ]) // [ foo, bar ]
+```
+
+### `callback`
+
+If you require more advanced duplication logic of the array values, then you can specify a callback that can process and
+return the value in question.
+
+```js
+const a = [ 1, 2 ];
+const b = [ 3, 4 ];
+
+const result = merge()
+ .using({
+ callback: (element, index, array, options) => {
+ return element * 2;
+ }
+ })
+ .of(a, b); // [ 2, 4, 6, 8 ]
+```
+
+#### Arguments
+
+* `element: any` - The current element being processed in the array.
+* `index: number` - The index of the current element being processed in the array.
+* `array: any[]` - The concatenated array this callback was called upon.
+* `options: Readonly` - The merge options to be applied.
\ No newline at end of file
diff --git a/docs/archive/current/packages/support/facades/README.md b/docs/archive/current/packages/support/facades/README.md
new file mode 100644
index 00000000..2047bad9
--- /dev/null
+++ b/docs/archive/current/packages/support/facades/README.md
@@ -0,0 +1,194 @@
+---
+title: About Facades
+description: A static interface to classes
+sidebarDepth: 0
+---
+
+# Introduction
+
+The `@aedart/support/facades` package is an adaptation of [Laravel's Facades](https://laravel.com/docs/11.x/facades)
+(_originally licensed under [MIT](https://github.com/laravel/framework/blob/11.x/src/Illuminate/Container/LICENSE.md)_). In this context, a [Facade](https://en.wikipedia.org/wiki/Facade_pattern) acts as an interface (_or gateway_) to an
+underlying object instance, resolved from the [Service Container](../../container/README.md).
+
+```js
+import { Container } from "@aedart/support/facades";
+
+const service = Container.obtain().make('api_service');
+```
+
+[[TOC]]
+
+## Setup Facade's Service Container instance
+
+Before you can make use of facades, you must ensure that the `Facade` abstraction has a service container instance set.
+This can be done via the static `setContainer()` method.
+
+```js
+import { Container } from "@aedart/container";
+import { Facade } from "@aedart/support/facades";
+
+// Somewhere in your application's setup or boot logic...
+Facade.setContainer(Container.getInstance());
+```
+
+Consequently, if you need to unset the Service Container instance and make sure that the `Facade` abstraction is cleared
+of any previously resolved object instances, invoke the static `destroy()` method.
+
+```js
+Facade.destroy();
+```
+
+## Define a Facade
+
+To define your own Facade, extend the abstract `Facade` class, and specify the target [binding identifier](../../container/bindings.md#identifiers).
+
+```js
+import { Facade } from "@aedart/support/facades";
+
+export default class ApiFacade extends Facade
+{
+ static getIdentifier()
+ {
+ return 'api_client';
+ }
+}
+```
+
+If you are using TypeScript, then you can also specify the return type of the `obtain()` method, by declaring the
+underlying resolved object's type, for the internal `type` property (_`type` property is not used for any other purpose_).
+
+```ts
+import type { Identifier } from "@aedart/contracts/container";
+import { Facade } from "@aedart/support/facades";
+import type { AcmeApiClient } from "@acme/contracts/api";
+
+export default class ApiFacade extends Facade
+{
+ protected static type: AcmeApiClient;
+
+ public static getIdentifier(): Identifier
+ {
+ return 'api_client';
+ }
+}
+```
+
+## The `obtain()` method
+
+The `obtain()` is used to obtain the Facade's underlying object instance. Typically, you do not need to do anything more
+than to implement the `getIdentifier()` method in your concrete facade class.
+But, in some situations you might need to resolve a binding differently. Or, perhaps perform some kind of additional
+post-resolve logic, in order to make easier / simpler to work with the resolved object.
+
+```js
+export default class LimitedApiFacade extends Facade
+{
+ static getIdentifier()
+ {
+ return 'api_client';
+ }
+
+ /**
+ * @return {import('@acme/contracts/api').AcmeApiClient}
+ */
+ static obtain()
+ {
+ const client = this.resolve(this.getIdentifier());
+ client.error_response_thresshold = 3;
+ client.ttl = 350;
+
+ return client;
+ }
+}
+```
+
+```js
+const promise = LimitedApiFacade.obtain().fetch('https://acme.com/api/users');
+```
+
+## Testing
+
+When you need to test components that rely on Facades, you can register a "spy" (_mocked object_), via the static
+method `spy()`. Consider, for instance, that you have a users repository component that relies on a custom Api facade.
+
+```js
+import { ApiFacade } from "@acme/facades";
+
+class UsersRepository {
+
+ fetch() {
+ return ApiFacade.obtain().fetch('https://acme.com/api/users');
+ }
+
+ // ...remaining not shown...
+}
+```
+
+In your testing environment, you can specify a callback that can be used to create a fake object (_mocked object_) that
+must behave in a certain way, via the `spy()` method. The callback must return either of the following:
+
+* The Facade's underlying resolved object.
+* Or, a fake object that behaves as desired (_in the context of your test_).
+
+```js
+ApiFacade.spy((container, identifier) => {
+ // ...mocking not shown ...
+
+ return myResolvedObject; // resolved or mocked object
+});
+```
+
+All subsequent calls to the facade's underlying object will be made to the registered "spy" object instead.
+
+The following example uses [Jasmine](https://jasmine.github.io/) as testing framework.
+However, the `spy()` method is not tied to any specific testing or object mocking framework. Feel free to use whatever
+testing tools or frameworks fits your purpose best.
+
+```js
+import { ApiFacade } from "@acme/facades";
+import { UsersRepository } from "@app";
+
+// E.g. testing via Jasmine Framework...
+describe('@acme/api', () => {
+
+ // Test setup not shown in this example...
+
+ afterEach(() => {
+ Facade.destroy();
+ });
+
+ it('can obtain users', () => {
+
+ let mocked = null;
+ ApiFacade.spy((container, identifier) => {
+ const apiClient = container.get(identifier);
+
+ mocked = spyOn(apiClient, 'fetch')
+ .and
+ .returnValue([
+ { id: 12, name: 'Jackie' },
+ { id: 14, name: 'Lana' },
+ // ...etc
+ ]);
+
+ // return the resolved api client...
+ return apiClient;
+ });
+
+ const repo = new UsersRepository();
+ const users = repo.fetch();
+
+ expect(users)
+ .not
+ .toBeUndefined();
+
+ expect(mocked)
+ .toHaveBeenCalled();
+ });
+});
+```
+
+## Onward
+
+Please consider reading Laravel's ["When to Utilize Facades"](https://laravel.com/docs/11.x/facades#when-to-use-facades),
+to gain an idea of when using Facades can be good, and when not.
\ No newline at end of file
diff --git a/docs/archive/current/packages/support/objects/merge.md b/docs/archive/current/packages/support/objects/merge.md
index bc3e00b4..aaddcce1 100644
--- a/docs/archive/current/packages/support/objects/merge.md
+++ b/docs/archive/current/packages/support/objects/merge.md
@@ -297,6 +297,10 @@ merge()
Behind the scene, the [array merge](../arrays/merge.md) utility is used for merging arrays.
+### `arrayMergeOptions`
+
+_See [Array Merge Options](../arrays/merge.md#merge-options)._
+
### `callback`
In situations when you need more advanced merge logic, you may specify a custom callback.
diff --git a/docs/archive/current/packages/support/reflections/hasAllMethods.md b/docs/archive/current/packages/support/reflections/hasAllMethods.md
index c8cc4d71..35b1c4aa 100644
--- a/docs/archive/current/packages/support/reflections/hasAllMethods.md
+++ b/docs/archive/current/packages/support/reflections/hasAllMethods.md
@@ -23,4 +23,6 @@ const a = {
hasAllMethods(a, 'foo', 'bar'); // true
hasAllMethods(a, 'foo', 'bar', 'zar'); // false
-```
\ No newline at end of file
+```
+
+See also [`hasMethod()`](./hasMethod.md).
\ No newline at end of file
diff --git a/docs/archive/current/packages/support/reflections/hasMethod.md b/docs/archive/current/packages/support/reflections/hasMethod.md
index 5aec48d3..85242af8 100644
--- a/docs/archive/current/packages/support/reflections/hasMethod.md
+++ b/docs/archive/current/packages/support/reflections/hasMethod.md
@@ -24,4 +24,6 @@ const a = {
hasMethod(a, 'foo'); // true
hasMethod(a, 'bar'); // true
hasMethod(a, 'zar'); // false
-```
\ No newline at end of file
+```
+
+See also [`isMethod()`](./isMethod.md).
\ No newline at end of file
diff --git a/docs/archive/current/packages/support/reflections/isCallable.md b/docs/archive/current/packages/support/reflections/isCallable.md
new file mode 100644
index 00000000..6dd29509
--- /dev/null
+++ b/docs/archive/current/packages/support/reflections/isCallable.md
@@ -0,0 +1,29 @@
+---
+title: Is Callable
+description: Determine if value is callable.
+sidebarDepth: 0
+---
+
+# `isCallable`
+
+Determine if a value is "callable" - a function that is not a [class constructor](./isClassConstructor.md).
+
+```js
+import { isCallable } from "@aedart/support/reflections";
+
+isCallable(null); // false
+isCallable({}); // false
+isCallable([]); // false
+isCallable(class {}); // false
+
+isCallable(function() {}); // true
+isCallable(() => {}); // true
+isCallable(Array); // true
+
+```
+
+**Acknowledgement**
+
+The source code of the above shown methods is heavily inspired by Denis Pushkarev's Core-js implementation of the [Function.isCallable / Function.isConstructor](https://github.com/zloirock/core-js#function-iscallable-isconstructor-) proposal (_License MIT_).
+
+See also [`isClassConstructor()`](./isClassConstructor.md).
\ No newline at end of file
diff --git a/docs/archive/current/packages/support/reflections/isClassConstructor.md b/docs/archive/current/packages/support/reflections/isClassConstructor.md
new file mode 100644
index 00000000..01b46eee
--- /dev/null
+++ b/docs/archive/current/packages/support/reflections/isClassConstructor.md
@@ -0,0 +1,39 @@
+---
+title: Is Class Constructor
+description: Determine if value is a class constructor.
+sidebarDepth: 0
+---
+
+# `isClassConstructor`
+
+The `isClassConstructor()` is able to determine if a value is a class constructor.
+
+::: warning Caution
+`isClassConstructor()` will only be able to return `true` for classes that are defined using the `class` keyword. See [ES6 classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) for additional information.
+:::
+
+::: warning Built-in Classes
+This util is **NOT** able to detect [built-in classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects).
+Use [`isConstructor()`](./isConstructor.md) if you wish to test for "constructable" functions / classes, including built-in classes.
+:::
+
+```js
+import { isClassConstructor } from "@aedart/support/reflections";
+
+isClassConstructor(null); // false
+isClassConstructor({}); // false
+isClassConstructor([]); // false
+isClassConstructor(function() {}); // false
+isClassConstructor(() => {}); // false
+isClassConstructor(Array); // false
+
+class A {}
+isClassConstructor(A); // true
+isClassConstructor(class {}); // true
+```
+
+**Acknowledgement**
+
+The source code of the above shown methods is heavily inspired by Denis Pushkarev's Core-js implementation of the [Function.isCallable / Function.isConstructor](https://github.com/zloirock/core-js#function-iscallable-isconstructor-) proposal (_License MIT_).
+
+See also [`isConstructor()`](./isConstructor.md).
diff --git a/docs/archive/current/packages/support/reflections/isClassMethodReference.md b/docs/archive/current/packages/support/reflections/isClassMethodReference.md
new file mode 100644
index 00000000..fe3ef0ff
--- /dev/null
+++ b/docs/archive/current/packages/support/reflections/isClassMethodReference.md
@@ -0,0 +1,31 @@
+---
+title: Is Class Method Reference
+description: Determine if value is a class method reference
+sidebarDepth: 0
+---
+
+# `isClassMethodReference`
+
+Determine if value is a "_class method reference_".
+A class method reference is an `array` with two values:
+
+- `0 = Constructor | object` Target class constructor or class instance
+- `1 = PropertyKey` Name of method (_property key in target_).
+
+```js
+import { isClassMethodReference } from '@aedart/support/reflections';
+
+class A {
+ age = 23;
+
+ foo: () => { /* ...not shown... */ }
+}
+
+const instance = new A();
+
+isClassMethodReference([ A, 'age' ]); // false
+isClassMethodReference([ instance, 'age' ]); // false
+
+isClassMethodReference([ A, 'foo' ]); // true
+isClassMethodReference([ instance, 'foo' ]); // true
+```
\ No newline at end of file
diff --git a/docs/archive/current/packages/support/reflections/isConstructor.md b/docs/archive/current/packages/support/reflections/isConstructor.md
index ae7b7ff4..acd9080e 100644
--- a/docs/archive/current/packages/support/reflections/isConstructor.md
+++ b/docs/archive/current/packages/support/reflections/isConstructor.md
@@ -6,20 +6,31 @@ sidebarDepth: 0
# `isConstructor`
-Based on the [TC39 `Function.isCallable() / Function.isConstructor()`](https://github.com/caitp/TC39-Proposals/blob/trunk/tc39-reflect-isconstructor-iscallable.md) proposal, the `isConstructor()` can determine if given argument is a constructor.
+Based on the [TC39 `Function.isCallable() / Function.isConstructor()`](https://github.com/caitp/TC39-Proposals/blob/trunk/tc39-reflect-isconstructor-iscallable.md) proposal, the `isConstructor()` can determine if value is a constructor.
-```js{6,8-9}
+```js
import { isConstructor } from "@aedart/support/reflections";
isConstructor(null); // false
isConstructor({}); // false
isConstructor([]); // false
-isConstructor(function() {}); // true
isConstructor(() => {}); // false
-isConstructor(Array); // true
+
+isConstructor(function() {}); // true
isConstructor(class {}); // true
+
+// Built-in objects
+isConstructor(Array); // true
+isConstructor(String); // true
+isConstructor(Number); // true
+isConstructor(Date); // true
+isConstructor(Map); // true
+isConstructor(Set); // true
+// ...etc
```
**Acknowledgement**
The source code of the above shown methods is heavily inspired by Denis Pushkarev's Core-js implementation of the [Function.isCallable / Function.isConstructor](https://github.com/zloirock/core-js#function-iscallable-isconstructor-) proposal (_License MIT_).
+
+See also [`isClassConstructor()`](./isClassConstructor.md).
\ No newline at end of file
diff --git a/docs/archive/current/packages/support/reflections/isMethod.md b/docs/archive/current/packages/support/reflections/isMethod.md
new file mode 100644
index 00000000..8718b41a
--- /dev/null
+++ b/docs/archive/current/packages/support/reflections/isMethod.md
@@ -0,0 +1,43 @@
+---
+title: Is Method
+description: Determine if property is a method in target
+sidebarDepth: 0
+---
+
+# `isMethod`
+
+Determine if property (_name_) is a method in given target object.
+
+It accepts the following arguments:
+
+- `target: object` - The target.
+- `property: PropertyKey` - Name of property.
+
+```js
+import { isMethod } from '@aedart/support/reflections';
+
+class A {
+ age = 23;
+
+ #title = 'AAA';
+ get title() {
+ return this.#title;
+ }
+
+ #job = 'AAA';
+ set job(v) {
+ this.#job = v;
+ }
+
+ foo: () => { /* ...not shown... */ }
+}
+
+const a = new A();
+
+isMethod(a, 'age'); // false
+isMethod(a, 'title'); // false
+isMethod(a, 'job'); // false
+isMethod(a, 'foo'); // true
+```
+
+See also [`hasMethod()`](./hasMethod.md).
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 77c5ef1f..116a26f3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,13 +10,14 @@
"packages/*"
],
"devDependencies": {
- "@babel/cli": "^7.23.4",
- "@babel/core": "^7.23.7",
+ "@babel/cli": "^7.23.9",
+ "@babel/core": "^7.24.0",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-proposal-decorators": "^7.23.7",
"@babel/plugin-proposal-private-methods": "^7.18.6",
"@babel/plugin-transform-class-static-block": "^7.23.4",
"@babel/preset-env": "^7.23.8",
+ "@babel/runtime": "^7.24.0",
"@lerna-lite/changed": "^3.2.1",
"@lerna-lite/cli": "^3.2.1",
"@lerna-lite/list": "^3.2.1",
@@ -77,6 +78,10 @@
"node": ">=0.10.0"
}
},
+ "node_modules/@aedart/container": {
+ "resolved": "packages/container",
+ "link": true
+ },
"node_modules/@aedart/contracts": {
"resolved": "packages/contracts",
"link": true
@@ -282,9 +287,9 @@
}
},
"node_modules/@babel/helper-define-polyfill-provider": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz",
- "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==",
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.0.tgz",
+ "integrity": "sha512-efwOM90nCG6YeT8o3PCyBVSxRfmILxCNL+TNI8CGQl7a62M0Wd9VkV+XHwIlkOz1r4b+lxu6gBjdWiOMdUCrCQ==",
"dev": true,
"dependencies": {
"@babel/helper-compilation-targets": "^7.22.6",
@@ -2520,12 +2525,12 @@
}
},
"node_modules/@jridgewell/source-map": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
- "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+ "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
"dependencies": {
- "@jridgewell/gen-mapping": "^0.3.0",
- "@jridgewell/trace-mapping": "^0.3.9"
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
@@ -3038,12 +3043,12 @@
"dev": true
},
"node_modules/@ljharb/through": {
- "version": "2.3.12",
- "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.12.tgz",
- "integrity": "sha512-ajo/heTlG3QgC8EGP6APIejksVAYt4ayz4tqoP3MolFELzcH1x1fzwEYRJTPO0IELutZ5HQ0c26/GqAYy79u3g==",
+ "version": "2.3.13",
+ "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz",
+ "integrity": "sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==",
"dev": true,
"dependencies": {
- "call-bind": "^1.0.5"
+ "call-bind": "^1.0.7"
},
"engines": {
"node": ">= 0.4"
@@ -4118,9 +4123,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.1.tgz",
- "integrity": "sha512-iU2Sya8hNn1LhsYyf0N+L4Gf9Qc+9eBTJJJsaOGUp+7x4n2M9dxTt8UvhJl3oeftSjblSlpCfvjA/IfP3g5VjQ==",
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz",
+ "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==",
"cpu": [
"arm"
],
@@ -4131,9 +4136,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.1.tgz",
- "integrity": "sha512-wlzcWiH2Ir7rdMELxFE5vuM7D6TsOcJ2Yw0c3vaBR3VOsJFVTx9xvwnAvhgU5Ii8Gd6+I11qNHwndDscIm0HXg==",
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz",
+ "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==",
"cpu": [
"arm64"
],
@@ -4144,9 +4149,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.1.tgz",
- "integrity": "sha512-YRXa1+aZIFN5BaImK+84B3uNK8C6+ynKLPgvn29X9s0LTVCByp54TB7tdSMHDR7GTV39bz1lOmlLDuedgTwwHg==",
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz",
+ "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==",
"cpu": [
"arm64"
],
@@ -4157,9 +4162,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.1.tgz",
- "integrity": "sha512-opjWJ4MevxeA8FhlngQWPBOvVWYNPFkq6/25rGgG+KOy0r8clYwL1CFd+PGwRqqMFVQ4/Qd3sQu5t7ucP7C/Uw==",
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz",
+ "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==",
"cpu": [
"x64"
],
@@ -4170,9 +4175,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.1.tgz",
- "integrity": "sha512-uBkwaI+gBUlIe+EfbNnY5xNyXuhZbDSx2nzzW8tRMjUmpScd6lCQYKY2V9BATHtv5Ef2OBq6SChEP8h+/cxifQ==",
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz",
+ "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==",
"cpu": [
"arm"
],
@@ -4183,9 +4188,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.1.tgz",
- "integrity": "sha512-0bK9aG1kIg0Su7OcFTlexkVeNZ5IzEsnz1ept87a0TUgZ6HplSgkJAnFpEVRW7GRcikT4GlPV0pbtVedOaXHQQ==",
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz",
+ "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==",
"cpu": [
"arm64"
],
@@ -4196,9 +4201,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.1.tgz",
- "integrity": "sha512-qB6AFRXuP8bdkBI4D7UPUbE7OQf7u5OL+R94JE42Z2Qjmyj74FtDdLGeriRyBDhm4rQSvqAGCGC01b8Fu2LthQ==",
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz",
+ "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==",
"cpu": [
"arm64"
],
@@ -4209,9 +4214,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.1.tgz",
- "integrity": "sha512-sHig3LaGlpNgDj5o8uPEoGs98RII8HpNIqFtAI8/pYABO8i0nb1QzT0JDoXF/pxzqO+FkxvwkHZo9k0NJYDedg==",
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz",
+ "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==",
"cpu": [
"riscv64"
],
@@ -4222,9 +4227,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.1.tgz",
- "integrity": "sha512-nD3YcUv6jBJbBNFvSbp0IV66+ba/1teuBcu+fBBPZ33sidxitc6ErhON3JNavaH8HlswhWMC3s5rgZpM4MtPqQ==",
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz",
+ "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==",
"cpu": [
"x64"
],
@@ -4235,9 +4240,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.1.tgz",
- "integrity": "sha512-7/XVZqgBby2qp/cO0TQ8uJK+9xnSdJ9ct6gSDdEr4MfABrjTyrW6Bau7HQ73a2a5tPB7hno49A0y1jhWGDN9OQ==",
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz",
+ "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==",
"cpu": [
"x64"
],
@@ -4248,9 +4253,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.1.tgz",
- "integrity": "sha512-CYc64bnICG42UPL7TrhIwsJW4QcKkIt9gGlj21gq3VV0LL6XNb1yAdHVp1pIi9gkts9gGcT3OfUYHjGP7ETAiw==",
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz",
+ "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==",
"cpu": [
"arm64"
],
@@ -4261,9 +4266,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.1.tgz",
- "integrity": "sha512-LN+vnlZ9g0qlHGlS920GR4zFCqAwbv2lULrR29yGaWP9u7wF5L7GqWu9Ah6/kFZPXPUkpdZwd//TNR+9XC9hvA==",
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz",
+ "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==",
"cpu": [
"ia32"
],
@@ -4274,9 +4279,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.1.tgz",
- "integrity": "sha512-n+vkrSyphvmU0qkQ6QBNXCGr2mKjhP08mPRM/Xp5Ck2FV4NrHU+y6axzDeixUrCBHVUS51TZhjqrKBBsHLKb2Q==",
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz",
+ "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==",
"cpu": [
"x64"
],
@@ -4572,9 +4577,9 @@
"integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw=="
},
"node_modules/@types/lodash": {
- "version": "4.14.202",
- "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz",
- "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
+ "version": "4.17.0",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.0.tgz",
+ "integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==",
"peer": true
},
"node_modules/@types/lodash-es": {
@@ -4631,9 +4636,9 @@
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
},
"node_modules/@types/node": {
- "version": "20.11.25",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.25.tgz",
- "integrity": "sha512-TBHyJxk2b7HceLVGFcpAUjsa5zIdsPWlR6XHfyGzd0SFu+/NFgQgMAl96MSDZgQDvJAvV6BKsFOrt6zIL09JDw==",
+ "version": "20.11.26",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.26.tgz",
+ "integrity": "sha512-YwOMmyhNnAWijOBQweOJnQPl068Oqd4K3OFbTc6AHJwzweUwwWG3GIFY74OKks2PJUDkQPeddOQES9mLn1CTEQ==",
"dependencies": {
"undici-types": "~5.26.4"
}
@@ -4753,16 +4758,16 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.1.1.tgz",
- "integrity": "sha512-zioDz623d0RHNhvx0eesUmGfIjzrk18nSBC8xewepKXbBvN/7c1qImV7Hg8TI1URTxKax7/zxfxj3Uph8Chcuw==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.2.0.tgz",
+ "integrity": "sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==",
"dev": true,
"dependencies": {
"@eslint-community/regexpp": "^4.5.1",
- "@typescript-eslint/scope-manager": "7.1.1",
- "@typescript-eslint/type-utils": "7.1.1",
- "@typescript-eslint/utils": "7.1.1",
- "@typescript-eslint/visitor-keys": "7.1.1",
+ "@typescript-eslint/scope-manager": "7.2.0",
+ "@typescript-eslint/type-utils": "7.2.0",
+ "@typescript-eslint/utils": "7.2.0",
+ "@typescript-eslint/visitor-keys": "7.2.0",
"debug": "^4.3.4",
"graphemer": "^1.4.0",
"ignore": "^5.2.4",
@@ -4821,15 +4826,15 @@
"dev": true
},
"node_modules/@typescript-eslint/parser": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.1.1.tgz",
- "integrity": "sha512-ZWUFyL0z04R1nAEgr9e79YtV5LbafdOtN7yapNbn1ansMyaegl2D4bL7vHoJ4HPSc4CaLwuCVas8CVuneKzplQ==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz",
+ "integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==",
"dev": true,
"dependencies": {
- "@typescript-eslint/scope-manager": "7.1.1",
- "@typescript-eslint/types": "7.1.1",
- "@typescript-eslint/typescript-estree": "7.1.1",
- "@typescript-eslint/visitor-keys": "7.1.1",
+ "@typescript-eslint/scope-manager": "7.2.0",
+ "@typescript-eslint/types": "7.2.0",
+ "@typescript-eslint/typescript-estree": "7.2.0",
+ "@typescript-eslint/visitor-keys": "7.2.0",
"debug": "^4.3.4"
},
"engines": {
@@ -4849,13 +4854,13 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.1.1.tgz",
- "integrity": "sha512-cirZpA8bJMRb4WZ+rO6+mnOJrGFDd38WoXCEI57+CYBqta8Yc8aJym2i7vyqLL1vVYljgw0X27axkUXz32T8TA==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz",
+ "integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==",
"dev": true,
"dependencies": {
- "@typescript-eslint/types": "7.1.1",
- "@typescript-eslint/visitor-keys": "7.1.1"
+ "@typescript-eslint/types": "7.2.0",
+ "@typescript-eslint/visitor-keys": "7.2.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
@@ -4866,13 +4871,13 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.1.1.tgz",
- "integrity": "sha512-5r4RKze6XHEEhlZnJtR3GYeCh1IueUHdbrukV2KSlLXaTjuSfeVF8mZUVPLovidCuZfbVjfhi4c0DNSa/Rdg5g==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.2.0.tgz",
+ "integrity": "sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==",
"dev": true,
"dependencies": {
- "@typescript-eslint/typescript-estree": "7.1.1",
- "@typescript-eslint/utils": "7.1.1",
+ "@typescript-eslint/typescript-estree": "7.2.0",
+ "@typescript-eslint/utils": "7.2.0",
"debug": "^4.3.4",
"ts-api-utils": "^1.0.1"
},
@@ -4893,9 +4898,9 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.1.tgz",
- "integrity": "sha512-KhewzrlRMrgeKm1U9bh2z5aoL4s7K3tK5DwHDn8MHv0yQfWFz/0ZR6trrIHHa5CsF83j/GgHqzdbzCXJ3crx0Q==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz",
+ "integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==",
"dev": true,
"engines": {
"node": "^16.0.0 || >=18.0.0"
@@ -4906,13 +4911,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.1.tgz",
- "integrity": "sha512-9ZOncVSfr+sMXVxxca2OJOPagRwT0u/UHikM2Rd6L/aB+kL/QAuTnsv6MeXtjzCJYb8PzrXarypSGIPx3Jemxw==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz",
+ "integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==",
"dev": true,
"dependencies": {
- "@typescript-eslint/types": "7.1.1",
- "@typescript-eslint/visitor-keys": "7.1.1",
+ "@typescript-eslint/types": "7.2.0",
+ "@typescript-eslint/visitor-keys": "7.2.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -5005,17 +5010,17 @@
"dev": true
},
"node_modules/@typescript-eslint/utils": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.1.1.tgz",
- "integrity": "sha512-thOXM89xA03xAE0lW7alstvnyoBUbBX38YtY+zAUcpRPcq9EIhXPuJ0YTv948MbzmKh6e1AUszn5cBFK49Umqg==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.2.0.tgz",
+ "integrity": "sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0",
- "@typescript-eslint/scope-manager": "7.1.1",
- "@typescript-eslint/types": "7.1.1",
- "@typescript-eslint/typescript-estree": "7.1.1",
+ "@typescript-eslint/scope-manager": "7.2.0",
+ "@typescript-eslint/types": "7.2.0",
+ "@typescript-eslint/typescript-estree": "7.2.0",
"semver": "^7.5.4"
},
"engines": {
@@ -5063,12 +5068,12 @@
"dev": true
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.1.tgz",
- "integrity": "sha512-yTdHDQxY7cSoCcAtiBzVzxleJhkGB9NncSIyMYe2+OGON1ZsP9zOPws/Pqgopa65jvknOjlk/w7ulPlZ78PiLQ==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz",
+ "integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==",
"dev": true,
"dependencies": {
- "@typescript-eslint/types": "7.1.1",
+ "@typescript-eslint/types": "7.2.0",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
@@ -5253,9 +5258,9 @@
}
},
"node_modules/@vuepress/helper": {
- "version": "2.0.0-rc.18",
- "resolved": "https://registry.npmjs.org/@vuepress/helper/-/helper-2.0.0-rc.18.tgz",
- "integrity": "sha512-Nh4q32qrm9Dpji0WaWU9yjhpxQ4nZXG8kq8XVIiZt7PHM75Q/CoofJWGKOt8qIafBKXtDUClVXLO2Xxp4ae9zg==",
+ "version": "2.0.0-rc.19",
+ "resolved": "https://registry.npmjs.org/@vuepress/helper/-/helper-2.0.0-rc.19.tgz",
+ "integrity": "sha512-g8udvFCIBcEcpLTo1BFZw452oBmnflW3lCmN0rR+SfIkZymi9CnFV8LgxTF/KV7vB71QMjN8IAwCVvJ3pGCUag==",
"dev": true,
"dependencies": {
"@vue/shared": "^3.4.21",
@@ -5305,12 +5310,12 @@
}
},
"node_modules/@vuepress/plugin-back-to-top": {
- "version": "2.0.0-rc.18",
- "resolved": "https://registry.npmjs.org/@vuepress/plugin-back-to-top/-/plugin-back-to-top-2.0.0-rc.18.tgz",
- "integrity": "sha512-NMaBWfj3fh5mpC6IKpBb+jO3oludU3UNXLd+ix8QSAnkBLnrQwDXSVlfWSZwqdotrFYrxW5KFBGR/1nw/SZrbQ==",
+ "version": "2.0.0-rc.19",
+ "resolved": "https://registry.npmjs.org/@vuepress/plugin-back-to-top/-/plugin-back-to-top-2.0.0-rc.19.tgz",
+ "integrity": "sha512-biR4S/7r8zwdukASG6o4JWv5Lp3SqPnOJnCHSUtZPnRqJsdxrSfYR62zgXfD5xukD+9PwqmwOdI5M5K0aHyytw==",
"dev": true,
"dependencies": {
- "@vuepress/helper": "~2.0.0-rc.18",
+ "@vuepress/helper": "~2.0.0-rc.19",
"@vueuse/core": "^10.9.0",
"vue": "^3.4.21"
},
@@ -5333,12 +5338,12 @@
}
},
"node_modules/@vuepress/plugin-copy-code": {
- "version": "2.0.0-rc.18",
- "resolved": "https://registry.npmjs.org/@vuepress/plugin-copy-code/-/plugin-copy-code-2.0.0-rc.18.tgz",
- "integrity": "sha512-9gAhPVn2dyFnpIWZzHVQdE8iNXZQP2C0x2oBbU23IW4AG66TXETS0iB1WYnffqpq7dBlzO/6MbeiORtZqdHshA==",
+ "version": "2.0.0-rc.19",
+ "resolved": "https://registry.npmjs.org/@vuepress/plugin-copy-code/-/plugin-copy-code-2.0.0-rc.19.tgz",
+ "integrity": "sha512-V85jVkTk5kjZ6LaXbudqBxdRy8Mqc8k7EN+Os3RIhUMBdHwgDkmTmS1QM6eOlKK19Yaw7MHtPFYS7NR262XnPQ==",
"dev": true,
"dependencies": {
- "@vuepress/helper": "~2.0.0-rc.18",
+ "@vuepress/helper": "~2.0.0-rc.19",
"@vueuse/core": "^10.9.0",
"vue": "^3.4.21"
},
@@ -5371,24 +5376,24 @@
}
},
"node_modules/@vuepress/plugin-links-check": {
- "version": "2.0.0-rc.18",
- "resolved": "https://registry.npmjs.org/@vuepress/plugin-links-check/-/plugin-links-check-2.0.0-rc.18.tgz",
- "integrity": "sha512-TqAZNqyNUj2SnZ2Mo1P3ufCnJWBB9sv2YqZSFbgtYoQhhNo3zkwhflOxeC/jNVaH+rw4azdD0iMFOTU41imoHw==",
+ "version": "2.0.0-rc.19",
+ "resolved": "https://registry.npmjs.org/@vuepress/plugin-links-check/-/plugin-links-check-2.0.0-rc.19.tgz",
+ "integrity": "sha512-DGcQ+xAnPAHT0JWifTVxEaH+U14IsRcuW9vuqn9n03+3xot2OkabxDa2zk5XgAcSn3QNg/pkLkImqTNRJ780eA==",
"dev": true,
"dependencies": {
- "@vuepress/helper": "~2.0.0-rc.18"
+ "@vuepress/helper": "~2.0.0-rc.19"
},
"peerDependencies": {
"vuepress": "2.0.0-rc.8"
}
},
"node_modules/@vuepress/plugin-medium-zoom": {
- "version": "2.0.0-rc.18",
- "resolved": "https://registry.npmjs.org/@vuepress/plugin-medium-zoom/-/plugin-medium-zoom-2.0.0-rc.18.tgz",
- "integrity": "sha512-szO65QaKUk5S0UYtEIWngkI/vXV0B1INiwgiGKSYabL6bLkJe1Fyv+8VT3Hos+aqdh+J+35ud+cIMI0nxUAqKw==",
+ "version": "2.0.0-rc.19",
+ "resolved": "https://registry.npmjs.org/@vuepress/plugin-medium-zoom/-/plugin-medium-zoom-2.0.0-rc.19.tgz",
+ "integrity": "sha512-zoIdSscgPwR252NKld6v1VnOKqEXIxW9KIj9S64AkzFtZdg/8ckTmOzw8VJ1ufMO3NUlqWlz4dIjsl9ckVudVQ==",
"dev": true,
"dependencies": {
- "@vuepress/helper": "~2.0.0-rc.18",
+ "@vuepress/helper": "~2.0.0-rc.19",
"medium-zoom": "^1.1.0",
"vue": "^3.4.21"
},
@@ -5446,24 +5451,24 @@
}
},
"node_modules/@vuepress/plugin-seo": {
- "version": "2.0.0-rc.18",
- "resolved": "https://registry.npmjs.org/@vuepress/plugin-seo/-/plugin-seo-2.0.0-rc.18.tgz",
- "integrity": "sha512-wTJqXIn+edDnKlL0ZOf7MLDQo59fhLePfsrpsCbaD8BKzHmEkx5aT1FF27JOvsRmMH4muv1uFQRXP837BsYzzw==",
+ "version": "2.0.0-rc.19",
+ "resolved": "https://registry.npmjs.org/@vuepress/plugin-seo/-/plugin-seo-2.0.0-rc.19.tgz",
+ "integrity": "sha512-/lTL6dFkuCK16M5yDZdD7zohdy+OqqeUjY1ZsXM2bYGjaha5CiukuUhJlIfRmM9oFQEOBirWCKPC0Ns4ObhPLA==",
"dev": true,
"dependencies": {
- "@vuepress/helper": "~2.0.0-rc.18"
+ "@vuepress/helper": "~2.0.0-rc.19"
},
"peerDependencies": {
"vuepress": "2.0.0-rc.8"
}
},
"node_modules/@vuepress/plugin-sitemap": {
- "version": "2.0.0-rc.18",
- "resolved": "https://registry.npmjs.org/@vuepress/plugin-sitemap/-/plugin-sitemap-2.0.0-rc.18.tgz",
- "integrity": "sha512-xDPZJWRD2bZhBPR0VA8F8jum5c8DV5P0+mvaQX6Vnugxo+0kETZSM5ctETA79CTmDdKuG6IW0tJkYK7ysb49zw==",
+ "version": "2.0.0-rc.19",
+ "resolved": "https://registry.npmjs.org/@vuepress/plugin-sitemap/-/plugin-sitemap-2.0.0-rc.19.tgz",
+ "integrity": "sha512-sIND+03O8h222BljQaSZQ8g7y+bHPZyjhEW8c8cLXv0/LZ7apO1qoEuU11ILYHZw5BF/zKNvRriw9QvrEDlIxA==",
"dev": true,
"dependencies": {
- "@vuepress/helper": "~2.0.0-rc.18",
+ "@vuepress/helper": "~2.0.0-rc.19",
"sitemap": "^7.1.1"
},
"peerDependencies": {
@@ -5492,25 +5497,25 @@
}
},
"node_modules/@vuepress/theme-default": {
- "version": "2.0.0-rc.18",
- "resolved": "https://registry.npmjs.org/@vuepress/theme-default/-/theme-default-2.0.0-rc.18.tgz",
- "integrity": "sha512-YcN4govU647we80OS/11W5cw+aliY5pXGbYJBRrIoIj/j10RKj6tDDAfv+orKt3lyswBRQQLAhP3NGDfsmx8+w==",
+ "version": "2.0.0-rc.20",
+ "resolved": "https://registry.npmjs.org/@vuepress/theme-default/-/theme-default-2.0.0-rc.20.tgz",
+ "integrity": "sha512-fXiUNMdmG2d1V6RRigm9/lMHKrJp1MoXPO4CRxdJnfpfxlzgTA9JqEPfgBqeovS1OE+pm+GrFSxRToAHSfS68g==",
"dev": true,
"dependencies": {
- "@vuepress/helper": "~2.0.0-rc.18",
+ "@vuepress/helper": "~2.0.0-rc.19",
"@vuepress/plugin-active-header-links": "~2.0.0-rc.18",
- "@vuepress/plugin-back-to-top": "~2.0.0-rc.18",
+ "@vuepress/plugin-back-to-top": "~2.0.0-rc.19",
"@vuepress/plugin-container": "~2.0.0-rc.15",
- "@vuepress/plugin-copy-code": "~2.0.0-rc.18",
+ "@vuepress/plugin-copy-code": "~2.0.0-rc.19",
"@vuepress/plugin-external-link-icon": "~2.0.0-rc.18",
"@vuepress/plugin-git": "~2.0.0-rc.15",
- "@vuepress/plugin-links-check": "~2.0.0-rc.18",
- "@vuepress/plugin-medium-zoom": "~2.0.0-rc.18",
+ "@vuepress/plugin-links-check": "~2.0.0-rc.19",
+ "@vuepress/plugin-medium-zoom": "~2.0.0-rc.19",
"@vuepress/plugin-nprogress": "~2.0.0-rc.18",
"@vuepress/plugin-palette": "~2.0.0-rc.15",
"@vuepress/plugin-prismjs": "~2.0.0-rc.15",
- "@vuepress/plugin-seo": "~2.0.0-rc.18",
- "@vuepress/plugin-sitemap": "~2.0.0-rc.18",
+ "@vuepress/plugin-seo": "~2.0.0-rc.19",
+ "@vuepress/plugin-sitemap": "~2.0.0-rc.19",
"@vuepress/plugin-theme-data": "~2.0.0-rc.18",
"@vueuse/core": "^10.9.0",
"sass": "^1.71.1",
@@ -6231,13 +6236,13 @@
}
},
"node_modules/babel-plugin-polyfill-corejs2": {
- "version": "0.4.8",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.8.tgz",
- "integrity": "sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg==",
+ "version": "0.4.9",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.9.tgz",
+ "integrity": "sha512-BXIWIaO3MewbXWdJdIGDWZurv5OGJlFNo7oy20DpB3kWDVJLcY2NRypRsRUbRe5KMqSNLuOGnWTFQQtY5MAsRw==",
"dev": true,
"dependencies": {
"@babel/compat-data": "^7.22.6",
- "@babel/helper-define-polyfill-provider": "^0.5.0",
+ "@babel/helper-define-polyfill-provider": "^0.6.0",
"semver": "^6.3.1"
},
"peerDependencies": {
@@ -6257,6 +6262,22 @@
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
}
},
+ "node_modules/babel-plugin-polyfill-corejs3/node_modules/@babel/helper-define-polyfill-provider": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz",
+ "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.22.6",
+ "@babel/helper-plugin-utils": "^7.22.5",
+ "debug": "^4.1.1",
+ "lodash.debounce": "^4.0.8",
+ "resolve": "^1.14.2"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
"node_modules/babel-plugin-polyfill-regenerator": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz",
@@ -6269,6 +6290,22 @@
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
}
},
+ "node_modules/babel-plugin-polyfill-regenerator/node_modules/@babel/helper-define-polyfill-provider": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz",
+ "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.22.6",
+ "@babel/helper-plugin-utils": "^7.22.5",
+ "debug": "^4.1.1",
+ "lodash.debounce": "^4.0.8",
+ "resolve": "^1.14.2"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -6708,9 +6745,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001596",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001596.tgz",
- "integrity": "sha512-zpkZ+kEr6We7w63ORkoJ2pOfBwBkY/bJrG/UZ90qNb45Isblu8wzDgevEOrRL1r9dWayHjYiiyCMEXPn4DweGQ==",
+ "version": "1.0.30001597",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001597.tgz",
+ "integrity": "sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==",
"funding": [
{
"type": "opencollective",
@@ -8393,9 +8430,9 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"node_modules/electron-to-chromium": {
- "version": "1.4.698",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.698.tgz",
- "integrity": "sha512-f9iZD1t3CLy1AS6vzM5EKGa6p9pRcOeEFXRFbaG2Ta+Oe7MkfRQ3fsvPYidzHe1h4i0JvIvpcY55C+B6BZNGtQ=="
+ "version": "1.4.701",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.701.tgz",
+ "integrity": "sha512-K3WPQ36bUOtXg/1+69bFlFOvdSm0/0bGqmsfPDLRXLanoKXdA+pIWuf/VbA9b+2CwBFuONgl4NEz4OEm+OJOKA=="
},
"node_modules/emoji-regex": {
"version": "8.0.0",
@@ -8511,9 +8548,9 @@
}
},
"node_modules/enhanced-resolve": {
- "version": "5.15.1",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.1.tgz",
- "integrity": "sha512-3d3JRbwsCLJsYgvb6NuWEG44jjPSOMuS73L/6+7BZuoKm3W+qXnSoIYVHi8dG7Qcg4inAY4jbzkZ7MnskePeDg==",
+ "version": "5.16.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz",
+ "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==",
"dependencies": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
@@ -10021,9 +10058,9 @@
"integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg=="
},
"node_modules/hasown": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
- "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dependencies": {
"function-bind": "^1.1.2"
},
@@ -17502,9 +17539,9 @@
}
},
"node_modules/read-pkg-up/node_modules/type-fest": {
- "version": "4.11.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.11.1.tgz",
- "integrity": "sha512-MFMf6VkEVZAETidGGSYW2B1MjXbGX+sWIywn2QPEaJ3j08V+MwVRHMXtf2noB8ENJaD0LIun9wh5Z6OPNf1QzQ==",
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.12.0.tgz",
+ "integrity": "sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==",
"dev": true,
"engines": {
"node": ">=16"
@@ -17557,9 +17594,9 @@
}
},
"node_modules/read-pkg/node_modules/type-fest": {
- "version": "4.11.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.11.1.tgz",
- "integrity": "sha512-MFMf6VkEVZAETidGGSYW2B1MjXbGX+sWIywn2QPEaJ3j08V+MwVRHMXtf2noB8ENJaD0LIun9wh5Z6OPNf1QzQ==",
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.12.0.tgz",
+ "integrity": "sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==",
"dev": true,
"engines": {
"node": ">=16"
@@ -17927,9 +17964,9 @@
}
},
"node_modules/rollup": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.1.tgz",
- "integrity": "sha512-ggqQKvx/PsB0FaWXhIvVkSWh7a/PCLQAsMjBc+nA2M8Rv2/HG0X6zvixAB7KyZBRtifBUhy5k8voQX/mRnABPg==",
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz",
+ "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==",
"dev": true,
"dependencies": {
"@types/estree": "1.0.5"
@@ -17942,19 +17979,19 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.12.1",
- "@rollup/rollup-android-arm64": "4.12.1",
- "@rollup/rollup-darwin-arm64": "4.12.1",
- "@rollup/rollup-darwin-x64": "4.12.1",
- "@rollup/rollup-linux-arm-gnueabihf": "4.12.1",
- "@rollup/rollup-linux-arm64-gnu": "4.12.1",
- "@rollup/rollup-linux-arm64-musl": "4.12.1",
- "@rollup/rollup-linux-riscv64-gnu": "4.12.1",
- "@rollup/rollup-linux-x64-gnu": "4.12.1",
- "@rollup/rollup-linux-x64-musl": "4.12.1",
- "@rollup/rollup-win32-arm64-msvc": "4.12.1",
- "@rollup/rollup-win32-ia32-msvc": "4.12.1",
- "@rollup/rollup-win32-x64-msvc": "4.12.1",
+ "@rollup/rollup-android-arm-eabi": "4.13.0",
+ "@rollup/rollup-android-arm64": "4.13.0",
+ "@rollup/rollup-darwin-arm64": "4.13.0",
+ "@rollup/rollup-darwin-x64": "4.13.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.13.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.13.0",
+ "@rollup/rollup-linux-arm64-musl": "4.13.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.13.0",
+ "@rollup/rollup-linux-x64-gnu": "4.13.0",
+ "@rollup/rollup-linux-x64-musl": "4.13.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.13.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.13.0",
+ "@rollup/rollup-win32-x64-msvc": "4.13.0",
"fsevents": "~2.3.2"
}
},
@@ -18588,16 +18625,16 @@
"dev": true
},
"node_modules/set-function-length": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz",
- "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==",
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"dependencies": {
- "define-data-property": "^1.1.2",
+ "define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
- "get-intrinsic": "^1.2.3",
+ "get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
- "has-property-descriptors": "^1.0.1"
+ "has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
@@ -19508,9 +19545,9 @@
}
},
"node_modules/ts-api-utils": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz",
- "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+ "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
"dev": true,
"engines": {
"node": ">=16"
@@ -20350,9 +20387,9 @@
}
},
"node_modules/webpack-dev-server/node_modules/open": {
- "version": "10.0.4",
- "resolved": "https://registry.npmjs.org/open/-/open-10.0.4.tgz",
- "integrity": "sha512-oujJ/FFr7ra6/7gJuQ4ZJJ8Gf2VHM0J3J/W7IvH++zaqEzacWVxzK++NiVY5NLHTTj7u/jNH5H3Ei9biL31Lng==",
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz",
+ "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==",
"dependencies": {
"default-browser": "^5.2.1",
"define-lazy-prop": "^3.0.0",
@@ -20702,9 +20739,9 @@
}
},
"node_modules/write-pkg/node_modules/type-fest": {
- "version": "4.11.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.11.1.tgz",
- "integrity": "sha512-MFMf6VkEVZAETidGGSYW2B1MjXbGX+sWIywn2QPEaJ3j08V+MwVRHMXtf2noB8ENJaD0LIun9wh5Z6OPNf1QzQ==",
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.12.0.tgz",
+ "integrity": "sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==",
"dev": true,
"engines": {
"node": ">=16"
@@ -20797,6 +20834,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "packages/container": {
+ "version": "0.10.0",
+ "license": "BSD-3-Clause",
+ "peerDependencies": {
+ "@aedart/contracts": "^0.10.0",
+ "@aedart/support": "^0.10.0"
+ }
+ },
"packages/contracts": {
"version": "0.10.0",
"license": "BSD-3-Clause"
@@ -20807,7 +20852,8 @@
"peerDependencies": {
"@aedart/contracts": "^0.10.0",
"@types/lodash-es": "^4.17.12",
- "lodash-es": "^4.17.21"
+ "lodash-es": "^4.17.21",
+ "tslib": "^2.6.2"
}
},
"packages/vuepress-utils": {
diff --git a/package.json b/package.json
index 2e5d9a7a..6fedd104 100644
--- a/package.json
+++ b/package.json
@@ -23,13 +23,14 @@
"node": "^20.11.0"
},
"devDependencies": {
- "@babel/cli": "^7.23.4",
- "@babel/core": "^7.23.7",
+ "@babel/cli": "^7.23.9",
+ "@babel/core": "^7.24.0",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-proposal-decorators": "^7.23.7",
"@babel/plugin-proposal-private-methods": "^7.18.6",
"@babel/plugin-transform-class-static-block": "^7.23.4",
"@babel/preset-env": "^7.23.8",
+ "@babel/runtime": "^7.24.0",
"@lerna-lite/changed": "^3.2.1",
"@lerna-lite/cli": "^3.2.1",
"@lerna-lite/list": "^3.2.1",
diff --git a/packages/container/LICENSE b/packages/container/LICENSE
new file mode 100644
index 00000000..912936bc
--- /dev/null
+++ b/packages/container/LICENSE
@@ -0,0 +1,11 @@
+Copyright (c) 2023-2024 Alin Eugen Deac . All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/packages/container/NOTICE b/packages/container/NOTICE
new file mode 100644
index 00000000..9d6c6e20
--- /dev/null
+++ b/packages/container/NOTICE
@@ -0,0 +1,44 @@
+NOTICES AND INFORMATION
+Please do not translate or Localize.
+
+Parts of the herein provided software are considered an "adaptation", or "derivative work", of 3rd party software.
+Below you will find general information about which parts are affected, or where you may find additional information
+, along with original license(s), terms and conditions as provided by the 3rd party software.
+
+3rd party software that are included as dependencies by this software is NOT covered by this NOTICE file, unless
+explicitly required by 3rd party software license(s). You can find original license(s), terms and
+conditions of the included 3rd party software, in the directory where it has been installed, e.g. inside your
+"node_modules" directory.
+
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+@aedart/container (Service Container)
+ The service container is an adaptation of Laravel's Container
+
+ See https://github.com/laravel/framework/blob/11.x/src/Illuminate/Container/Container.php
+
+ License MIT, Copyright (c) Taylor Otwell.
+
+ This part of the NOTICE file corresponds to terms and conditions set by the MIT License
+ =======================================================================================
+
+ The MIT License (MIT)
+
+ Copyright (c) Taylor Otwell
+
+ 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.
\ No newline at end of file
diff --git a/packages/container/README.md b/packages/container/README.md
new file mode 100644
index 00000000..00c771f4
--- /dev/null
+++ b/packages/container/README.md
@@ -0,0 +1,21 @@
+# Service Container
+
+The `@aedart/container` package offers an adaptation of [Laravel's Service Container](https://laravel.com/docs/11.x/container)
+(_originally licensed under [MIT](https://github.com/laravel/framework/blob/11.x/src/Illuminate/Container/LICENSE.md)_).
+
+The tools provided by this package give you a way to:
+
+* Manage class dependencies
+* Perform [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection)
+
+# Official Documentation
+
+Please read the [official documentation](https://aedart.github.io/ion/) for additional information.
+
+## Versioning
+
+This package follows [Semantic Versioning 2.0.0](http://semver.org/)
+
+## License
+
+[BSD-3-Clause](http://spdx.org/licenses/BSD-3-Clause), please read the [`LICENSE`](./LICENSE) file included in this project.
diff --git a/packages/container/package.json b/packages/container/package.json
new file mode 100644
index 00000000..70d23f7a
--- /dev/null
+++ b/packages/container/package.json
@@ -0,0 +1,47 @@
+{
+ "name": "@aedart/container",
+ "version": "0.10.0",
+ "description": "Service Container",
+ "keywords": [
+ "Service Container",
+ "Dependency Injection",
+ "Inverse of Control",
+ "IoC"
+ ],
+ "author": "Alin Eugen Deac ",
+ "license": "BSD-3-Clause",
+ "homepage": "https://aedart.github.io/ion/",
+ "bugs": {
+ "url": "https://github.com/aedart/ion/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/aedart/ion.git",
+ "directory": "packages/container"
+ },
+ "private": false,
+ "publishConfig": {
+ "access": "public"
+ },
+ "type": "module",
+ "exports": {
+ ".": {
+ "types": "./dist/types/container.d.ts",
+ "import": "./dist/esm/container.js",
+ "require": "./dist/cjs/container.cjs"
+ }
+ },
+ "files": [
+ "dist",
+ "!dist/**/*.map"
+ ],
+ "peerDependencies": {
+ "@aedart/contracts": "^0.10.0",
+ "@aedart/support": "^0.10.0"
+ },
+ "scripts": {
+ "compile": "rollup -c",
+ "watch": "rollup -c -w"
+ },
+ "sideEffects": false
+}
diff --git a/packages/container/rollup.config.mjs b/packages/container/rollup.config.mjs
new file mode 100644
index 00000000..d7737616
--- /dev/null
+++ b/packages/container/rollup.config.mjs
@@ -0,0 +1,28 @@
+import { createConfig } from '../../shared/rollup.config.mjs';
+
+export default createConfig({
+ baseDir: new URL('.', import.meta.url),
+ external: [
+ '@aedart/contracts/container',
+ '@aedart/contracts/support',
+ '@aedart/contracts/support/arrays',
+ '@aedart/contracts/support/container',
+ '@aedart/contracts/support/concerns',
+ '@aedart/contracts/support/exceptions',
+ '@aedart/contracts/support/meta',
+ '@aedart/contracts/support/mixins',
+ '@aedart/contracts/support/reflections',
+ '@aedart/support',
+ '@aedart/support/arrays',
+ '@aedart/support/container',
+ '@aedart/support/concerns',
+ '@aedart/support/exceptions',
+ '@aedart/support/facades',
+ '@aedart/support/meta',
+ '@aedart/support/misc',
+ '@aedart/support/mixins',
+ '@aedart/support/reflections',
+
+ 'lodash-es'
+ ]
+});
diff --git a/packages/container/src/BindingEntry.ts b/packages/container/src/BindingEntry.ts
new file mode 100644
index 00000000..aecd2876
--- /dev/null
+++ b/packages/container/src/BindingEntry.ts
@@ -0,0 +1,170 @@
+import type {
+ Binding,
+ FactoryCallback,
+ Identifier
+} from "@aedart/contracts/container";
+import type { Constructor } from "@aedart/contracts";
+import { isConstructor } from "@aedart/support/reflections";
+import { isBindingIdentifier } from "@aedart/support/container";
+
+/**
+ * Binding Entry
+ */
+export default class BindingEntry<
+ T = any /* eslint-disable-line @typescript-eslint/no-explicit-any */
+> implements Binding
+{
+ /**
+ * This binding's identifier
+ *
+ * @type {Identifier}
+ *
+ * @readonly
+ *
+ * @protected
+ */
+ protected readonly _identifier: Identifier;
+
+ /**
+ * The bound value to be resolved by a service container
+ *
+ * @template T = any
+ *
+ * @type {FactoryCallback | Constructor}
+ *
+ * @readonly
+ *
+ * @protected
+ */
+ protected readonly _value: FactoryCallback | Constructor;
+
+ /**
+ * Shared state of resolved value
+ *
+ * @type {boolean} If `true`, then service container must register resolved
+ * value as a singleton.
+ *
+ * @readonly
+ *
+ * @protected
+ */
+ protected readonly _shared: boolean;
+
+ /**
+ * State, whether value is a factory callback or not
+ *
+ * @type {boolean|null}
+ *
+ * @protected
+ */
+ protected _isFactoryCallback: boolean|null = null;
+
+ /**
+ * State, whether value is a constructor or not
+ *
+ * @type {boolean|null}
+ *
+ * @protected
+ */
+ _isConstructor: boolean|null = null;
+
+ /**
+ * Create new Binding Entry instance
+ *
+ * @template T = any
+ *
+ * @param {Identifier} identifier
+ * @param {FactoryCallback | Constructor} value
+ * @param {boolean} [shared=false]
+ *
+ * @throws {TypeError}
+ */
+ constructor(identifier: Identifier, value: FactoryCallback | Constructor, shared: boolean = false)
+ {
+ if (!isBindingIdentifier(identifier)) {
+ throw new TypeError(`Invalid binding identifier: ${typeof identifier} is not supported`, { cause: { identifier: identifier, value: value } });
+ }
+
+ this._identifier = identifier;
+ this._value = value;
+ this._shared = shared;
+
+ this.resolveIsConstructorOrFactoryCallback();
+ }
+
+ /**
+ * This binding's identifier
+ *
+ * @type {Identifier}
+ *
+ * @readonly
+ */
+ get identifier(): Identifier
+ {
+ return this._identifier;
+ }
+
+ /**
+ * The bound value to be resolved by a service container
+ *
+ * @template T = any
+ *
+ * @type {FactoryCallback | Constructor}
+ *
+ * @readonly
+ */
+ get value(): FactoryCallback | Constructor
+ {
+ return this._value;
+ }
+
+ /**
+ * Shared state of resolved value
+ *
+ * @type {boolean} If `true`, then service container must register resolved
+ * value as a singleton.
+ *
+ * @readonly
+ */
+ get shared(): boolean
+ {
+ return this._shared;
+ }
+
+ /**
+ * Determine if bound value is a {@link FactoryCallback}
+ *
+ * @returns {boolean}
+ */
+ isFactoryCallback(): boolean
+ {
+ return this._isFactoryCallback as boolean;
+ }
+
+ /**
+ * Determine if bound value is a {@link Constructor}
+ *
+ * @returns {boolean}
+ */
+ isConstructor(): boolean
+ {
+ return this._isConstructor as boolean;
+ }
+
+ /**
+ * Resolves the "is constructor" or "is factory callback" values
+ *
+ * @return {void}
+ *
+ * @protected
+ */
+ protected resolveIsConstructorOrFactoryCallback(): void
+ {
+ this._isConstructor = isConstructor(this._value);
+ this._isFactoryCallback = !this._isConstructor;
+
+ if (!this._isConstructor && !this._isFactoryCallback) {
+ throw new TypeError('Binding value must either be a valid constructor or factory callback', { cause: { identifier: this._identifier, value: this._value } });
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/container/src/BindingEntryBlueprint.ts b/packages/container/src/BindingEntryBlueprint.ts
new file mode 100644
index 00000000..0f284602
--- /dev/null
+++ b/packages/container/src/BindingEntryBlueprint.ts
@@ -0,0 +1,19 @@
+import type { ClassBlueprint } from "@aedart/contracts/support/reflections";
+
+/**
+ * Binding Entry Blueprint
+ *
+ * Defines the minimum members that a target class should contain, before it is
+ * considered to "look like" a [Binding]{@link import('@aedart/contracts/container').Binding}
+ *
+ * @type {ClassBlueprint}
+ */
+export const BindingEntryBlueprint: ClassBlueprint = {
+ members: [
+ 'identifier',
+ 'value',
+ 'shared',
+ 'isFactoryCallback',
+ 'isConstructor'
+ ]
+};
\ No newline at end of file
diff --git a/packages/container/src/Builder.ts b/packages/container/src/Builder.ts
new file mode 100644
index 00000000..e61e4c6a
--- /dev/null
+++ b/packages/container/src/Builder.ts
@@ -0,0 +1,83 @@
+import type {
+ Container,
+ ContextualBindingBuilder,
+ FactoryCallback,
+ Identifier
+} from "@aedart/contracts/container";
+import type { Constructor } from "@aedart/contracts";
+
+/**
+ * Contextual Binding Builder
+ *
+ * Adaptation of Laravel Contextual Binding Builder
+ *
+ * @see https://github.com/laravel/framework/blob/master/src/Illuminate/Container/ContextualBindingBuilder.php
+ */
+export default class Builder implements ContextualBindingBuilder
+{
+ /**
+ * The service container to be used in this context.
+ *
+ * @type {Container}
+ *
+ * @protected
+ */
+ protected container: Container;
+
+ /**
+ * The concrete constructor(s)
+ *
+ * @type {Constructor[]}
+ *
+ * @protected
+ */
+ protected concrete: Constructor[];
+
+ /**
+ * The target identifier in this context.
+ *
+ * @type {Identifier}
+ *
+ * @protected
+ */
+ protected identifier: Identifier | undefined = undefined;
+
+ /**
+ * Create a new Contextual Binding Builder instance
+ *
+ * @param {Container} container
+ * @param {...Constructor[]} concrete
+ */
+ constructor(container: Container, ...concrete: Constructor[]) {
+ this.container = container;
+ this.concrete = concrete;
+ }
+
+ /**
+ * Define the target identifier in this context.
+ *
+ * @param {Identifier} identifier
+ *
+ * @return {this}
+ */
+ public needs(identifier: Identifier): this
+ {
+ this.identifier = identifier;
+
+ return this;
+ }
+
+ /**
+ * Define the implementation to be resolved in this context.
+ *
+ * @param {FactoryCallback | Constructor} implementation
+ *
+ * @return {void}
+ */
+ public give(implementation: FactoryCallback | Constructor): void
+ {
+ for(const ctor of this.concrete) {
+ this.container.addContextualBinding(ctor, this.identifier as Identifier, implementation);
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/container/src/Container.ts b/packages/container/src/Container.ts
new file mode 100644
index 00000000..f8ce4b07
--- /dev/null
+++ b/packages/container/src/Container.ts
@@ -0,0 +1,1331 @@
+import {
+ AfterResolvedCallback,
+ Alias,
+ BeforeResolvedCallback,
+ Container as ServiceContainerContract,
+ ContextualBindingBuilder,
+ ExtendCallback,
+ FactoryCallback,
+ Identifier,
+ Binding,
+ ReboundCallback,
+} from "@aedart/contracts/container";
+import type {
+ Callback,
+ ClassMethodName,
+ ClassMethodReference,
+ Constructor,
+ ConstructorLike
+} from "@aedart/contracts";
+import type { CallbackWrapper } from "@aedart/contracts/support";
+import { DEPENDENCIES } from "@aedart/contracts/container";
+import { hasDependencies, getDependencies } from "@aedart/support/container";
+import { getErrorMessage } from "@aedart/support/exceptions";
+import {
+ isConstructor,
+ isClassMethodReference,
+ getNameOrDesc,
+ hasAllMethods,
+} from "@aedart/support/reflections";
+import {
+ isCallbackWrapper,
+ CallbackWrapper as Wrapper
+} from "@aedart/support";
+import CircularDependencyError from "./exceptions/CircularDependencyError";
+import ContainerError from "./exceptions/ContainerError";
+import NotFoundError from "./exceptions/NotFoundError";
+import Builder from "./Builder";
+import BindingEntry from "./BindingEntry";
+import { isBinding } from "./isBinding";
+
+/**
+ * Service Container
+ *
+ * Adaptation of Laravel's Service Container.
+ *
+ * @see https://github.com/laravel/framework/blob/11.x/src/Illuminate/Container/Container.php
+ */
+export default class Container implements ServiceContainerContract
+{
+ /**
+ * Singleton instance of the service container
+ *
+ * @type {ServiceContainerContract|null}
+ *
+ * @protected
+ *
+ * @static
+ */
+ protected static instance: ServiceContainerContract | null = null;
+
+ /**
+ * Registered bindings
+ *
+ * @type {Map}
+ *
+ * @protected
+ */
+ protected bindings: Map = new Map();
+
+ /**
+ * Registered aliases
+ *
+ * @type {Map}
+ *
+ * @protected
+ */
+ protected aliases: Map = new Map();
+
+ /**
+ * Registered "shared" instances (singletons)
+ *
+ * @type {Map}
+ *
+ * @protected
+ */
+ protected instances: Map = new Map();
+
+ /**
+ * Extend callbacks
+ *
+ * @type {Map}
+ *
+ * @protected
+ */
+ protected extenders: Map = new Map();
+
+ /**
+ * Resolved (built) identifiers
+ *
+ * @type {Set}
+ *
+ * @protected
+ */
+ protected resolved: Set = new Set();
+
+ /**
+ * Contextual Bindings
+ *
+ * @type {Map>}
+ *
+ * @protected
+ */
+ protected contextualBindings: Map<
+ Constructor,
+ Map
+ > = new Map();
+
+ /**
+ * "Before" resolved callbacks
+ *
+ * @type {Map}
+ *
+ * @protected
+ */
+ protected beforeResolvedCallbacks: Map = new Map();
+
+ /**
+ * "After" resolved callbacks
+ *
+ * @type {Map}
+ *
+ * @protected
+ */
+ protected afterResolvedCallbacks: Map = new Map();
+
+ /**
+ * Rebound callbacks
+ *
+ * @type {Map}
+ *
+ * @protected
+ */
+ protected reboundCallbacks: Map = new Map();
+
+ /**
+ * Resolve stack
+ *
+ * @type {Set}
+ *
+ * @protected
+ */
+ protected resolveStack: Set = new Set();
+
+ /**
+ * Returns the singleton instance of the service container
+ *
+ * @return {ServiceContainerContract|this}
+ */
+ public static getInstance(): ServiceContainerContract
+ {
+ if (this.instance === null) {
+ this.setInstance(new this());
+ }
+
+ return this.instance as ServiceContainerContract;
+ }
+
+ /**
+ * Set the singleton instance of the service container
+ *
+ * @param {ServiceContainerContract | null} [container]
+ *
+ * @return {ServiceContainerContract | null}
+ */
+ public static setInstance(container: ServiceContainerContract | null = null): ServiceContainerContract | null
+ {
+ return this.instance = container;
+ }
+
+ /**
+ * Register a binding
+ *
+ * @param {Identifier} identifier
+ * @param {FactoryCallback | Constructor} [concrete]
+ * @param {boolean} [shared=false]
+ *
+ * @returns {this}
+ *
+ * @throws {TypeError}
+ */
+ public bind(identifier: Identifier, concrete?: FactoryCallback | Constructor, shared: boolean = false): this
+ {
+ concrete = concrete ?? identifier as Constructor;
+
+ this.bindings.set(identifier, this.makeBindingEntry(identifier, concrete, shared));
+
+ // Invoke rebound callbacks, if the identifier has already been resolved, such that
+ // dependent objects can be updated...
+ if (this.isResolved(identifier)) {
+ this.rebound(identifier);
+ }
+
+ return this;
+ }
+
+ /**
+ * Register a binding, if none already exists for given identifier
+ *
+ * @param {Identifier} identifier
+ * @param {FactoryCallback | Constructor} [concrete]
+ * @param {boolean} [shared=false]
+ *
+ * @returns {this}
+ *
+ * @throws {TypeError}
+ */
+ public bindIf(identifier: Identifier, concrete?: FactoryCallback | Constructor, shared: boolean = false): this
+ {
+ if (!this.bound(identifier)) {
+ this.bind(identifier, concrete, shared);
+ }
+
+ return this;
+ }
+
+ /**
+ * Register a shared binding
+ *
+ * @param {Identifier} identifier
+ * @param {FactoryCallback | Constructor} [concrete]
+ *
+ * @returns {this}
+ *
+ * @throws {TypeError}
+ */
+ public singleton(identifier: Identifier, concrete?: FactoryCallback | Constructor): this
+ {
+ return this.bind(identifier, concrete, true);
+ }
+
+ /**
+ * Register a shared binding, if none already exists for given identifier
+ *
+ * @param {Identifier} identifier
+ * @param {FactoryCallback | Constructor} [concrete]
+ *
+ * @returns {this}
+ *
+ * @throws {TypeError}
+ */
+ public singletonIf(identifier: Identifier, concrete?: FactoryCallback | Constructor): this
+ {
+ if (!this.bound(identifier)) {
+ this.singleton(identifier, concrete);
+ }
+
+ return this;
+ }
+
+ /**
+ * Register existing object instance as a shared binding
+ *
+ * @template T = object
+ *
+ * @param {Identifier} identifier
+ * @param {T} instance
+ *
+ * @returns {T}
+ *
+ * @throws {TypeError}
+ */
+ public instance(identifier: Identifier, instance: T): T
+ {
+ const isBound: boolean = this.has(identifier);
+
+ this.instances.set(identifier, instance as object);
+
+ // If the identifier was already bound before, invoke the rebound callbacks.
+ if (isBound) {
+ this.rebound(identifier);
+ }
+
+ return instance;
+ }
+
+ /**
+ * Add a contextual binding in this container
+ *
+ * @param {Constructor} concrete
+ * @param {Identifier} identifier
+ * @param {FactoryCallback | Constructor} implementation
+ *
+ * @return {this}
+ *
+ * @throws {TypeError}
+ */
+
+ public addContextualBinding(
+ concrete: Constructor,
+ identifier: Identifier,
+ implementation: FactoryCallback | Constructor
+ ): this
+ {
+ if (!this.contextualBindings.has(concrete)) {
+ this.contextualBindings.set(concrete, new Map());
+ }
+
+ const entry = this.contextualBindings.get(concrete) as Map;
+ entry.set(this.getAlias(identifier), this.makeBindingEntry(identifier, implementation));
+
+ return this;
+ }
+
+ /**
+ * Define a contextual binding
+ *
+ * @param {...Constructor[]} concrete
+ *
+ * @return {ContextualBindingBuilder}
+ *
+ * @throws {TypeError}
+ */
+ public when(...concrete: Constructor[]): ContextualBindingBuilder
+ {
+ return new Builder(this, ...concrete);
+ }
+
+ /**
+ * Determine if target has one or more contextual bindings registered
+ *
+ * @param {Constructor} target
+ *
+ * @return {boolean}
+ */
+ public hasContextualBindings(target: Constructor): boolean
+ {
+ return this.contextualBindings.has(target) &&
+ (this.contextualBindings.get(target) as Map).size > 0;
+ }
+
+ /**
+ * Determine if a contextual binding is registered for the identifier in given target
+ *
+ * @param {Constructor} target
+ * @param {Identifier} identifier
+ *
+ * @return {boolean}
+ */
+ public hasContextualBinding(target: Constructor, identifier: Identifier): boolean
+ {
+ return this.hasContextualBindings(target)
+ && (this.contextualBindings.get(target) as Map).has(identifier);
+ }
+
+ /**
+ * Returns contextual binding implementation for given target and identifier
+ *
+ * @param {Constructor} target
+ * @param {Identifier} identifier
+ *
+ * @return {Binding | undefined}
+ */
+ public getContextualBinding(target: Constructor, identifier: Identifier): Binding | undefined
+ {
+ return this.contextualBindings.get(target)?.get(identifier);
+ }
+
+ /**
+ * Resolves binding value that matches identifier and returns it
+ *
+ * @template T = any
+ *
+ * @param {Identifier} identifier
+ *
+ * @returns {T}
+ *
+ * @throws {NotFoundException}
+ * @throws {ContainerException}
+ */
+ public get<
+ T = any /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ >(identifier: Identifier): T
+ {
+ return this.make(identifier);
+ }
+
+ /**
+ * Determine if an entry is registered for given identifier.
+ *
+ * @param {Identifier} identifier
+ *
+ * @returns {boolean}
+ */
+ public has(identifier: Identifier): boolean
+ {
+ return this.bindings.has(identifier)
+ || this.instances.has(identifier)
+ || this.isAlias(identifier);
+ }
+
+ /**
+ * Alias for {@link has}
+ *
+ * @param {Identifier} identifier
+ *
+ * @returns {boolean}
+ */
+ public bound(identifier: Identifier): boolean
+ {
+ return this.has(identifier);
+ }
+
+ /**
+ * Alias identifier as a different identifier
+ *
+ * @param {Identifier} identifier
+ * @param {Alias} alias
+ *
+ * @returns {this}
+ *
+ * @throws {TypeError}
+ */
+ public alias(identifier: Identifier, alias: Alias): this
+ {
+ if (alias === identifier) {
+ throw new TypeError(`${identifier.toString()} is aliased to itself`, { cause: { identifier: identifier, alias: alias } });
+ }
+
+ this.aliases.set(alias, identifier);
+
+ return this;
+ }
+
+ /**
+ * Determine if identifier is an alias
+ *
+ * @param {Identifier} identifier
+ *
+ * @return {boolean}
+ */
+ public isAlias(identifier: Identifier): boolean
+ {
+ return this.aliases.has(identifier);
+ }
+
+ /**
+ * Determine if identifier is registered as a "shared" binding
+ *
+ * @param {Identifier} identifier
+ *
+ * @returns {boolean}
+ */
+ public isShared(identifier: Identifier): boolean
+ {
+ return this.instances.has(identifier)
+ || (this.bindings.has(identifier) && (this.bindings.get(identifier) as Binding).shared)
+ }
+
+ /**
+ * Returns the identifier for given alias, if available
+ *
+ * @param {Identifier} alias
+ *
+ * @returns {Identifier}
+ */
+ public getAlias(alias: Identifier): Identifier
+ {
+ return this.aliases.has(alias)
+ ? this.getAlias(this.aliases.get(alias) as Identifier)
+ : alias;
+ }
+
+ /**
+ * Resolves binding value that matches identifier and returns it
+ *
+ * @template T = any
+ *
+ * @param {Identifier} identifier
+ * @param {any[]} [args] Eventual arguments to pass on to {@link FactoryCallback} or {@link Constructor}
+ *
+ * @returns {T}
+ *
+ * @throws {NotFoundException}
+ * @throws {ContainerException}
+ */
+ public make<
+ T = any /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ >(identifier: Identifier, args: any[] = [] /* eslint-disable-line @typescript-eslint/no-explicit-any */): T
+ {
+ return this.resolve(identifier, args);
+ }
+
+ /**
+ * Resolves values if a binding exists for identifier, or returns a default value
+ *
+ * @template T = any
+ * @template D = undefined
+ *
+ * @param {Identifier} identifier
+ * @param {any[]} [args] Eventual arguments to pass on to {@link FactoryCallback} or {@link Constructor}
+ * @param {D} [defaultValue]
+ *
+ * @returns {T}
+ *
+ * @throws {ContainerException}
+ */
+ public makeOrDefault<
+ T = any, /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ D = undefined
+ >(identifier: Identifier, args?: any[] /* eslint-disable-line @typescript-eslint/no-explicit-any */, defaultValue?: D): T | D
+ {
+ if (!this.has(identifier) && !this.isBuildable(identifier)) {
+ if (typeof defaultValue === 'function') {
+ return defaultValue(this, args);
+ }
+
+ return defaultValue as D;
+ }
+
+ return this.make(identifier, args);
+ }
+
+ /**
+ * Instantiate a new instance of given concrete
+ *
+ * @template T = object
+ *
+ * @param {Constructor | Binding} concrete
+ * @param {any[]} [args] Eventual arguments to pass on the concrete instance's constructor.
+ *
+ * @returns {T}
+ *
+ * @throws {ContainerException}
+ */
+ public build(
+ concrete: Constructor | Binding,
+ args: any[] = [] /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ ): T
+ {
+ const isBinding: boolean = this.isBinding(concrete);
+ let identifier: Identifier = concrete;
+
+ // Resolve factory callback, when binding is given of such type.
+ if (isBinding && (concrete as Binding).isFactoryCallback()) {
+ return this.resolveFactoryCallback((concrete as Binding).value as FactoryCallback, args, (concrete as Binding).identifier);
+ }
+
+ // Extract constructor, if concrete is a binding so that it can be resolved.
+ if (isBinding) {
+ identifier = (concrete as Binding).identifier;
+ concrete = (concrete as Binding).value as Constructor;
+ }
+
+ // Abort if concrete is not buildable
+ if (!this.isBuildable(concrete)) {
+ const nameOrDesc: string = getNameOrDesc(concrete as ConstructorLike);
+ throw new ContainerError(`Unable to build concrete: ${nameOrDesc} is not a constructor or a Binding Entry`, { cause: { concrete: concrete, args: args } });
+ }
+
+ // Resolve the constructor and eventual dependencies, when no arguments are given.
+ return this.resolveConstructor(
+ concrete as Constructor,
+ args,
+ identifier
+ );
+ }
+
+ /**
+ * Call given method and inject dependencies if needed
+ *
+ * @param {Callback | CallbackWrapper | ClassMethodReference} method
+ * @param {any[]} [args]
+ *
+ * @return {any}
+ *
+ * @throws {ContainerException}
+ */
+ public call(method: Callback | CallbackWrapper | ClassMethodReference, args: any[] = []): any /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ {
+ if (isClassMethodReference(method)) {
+ return this.invokeClassMethod(method as ClassMethodReference, args);
+ }
+
+ if (isCallbackWrapper(method)) {
+ return this.invokeCallbackWrapper(method as CallbackWrapper, args);
+ }
+
+ const type: string = typeof method;
+ if (type == 'function') {
+ return this.invokeCallback(method as Callback, args);
+ }
+
+ throw new ContainerError(`Unable to call method: ${type} is not supported`, { cause: { method: method, args: args } });
+ }
+
+ /**
+ * Extend the registered binding
+ *
+ * @param {Identifier} identifier
+ * @param {ExtendCallback} callback
+ *
+ * @return {this}
+ *
+ * @throws {TypeError}
+ * @throws {ContainerException}
+ */
+ public extend(identifier: Identifier, callback: ExtendCallback): this
+ {
+ identifier = this.getAlias(identifier);
+
+ // If identifier matches a "shared" instance, then extend that instance right
+ // away and rebound it.
+ if (this.instances.has(identifier)) {
+ const instance = this.instances.get(identifier) as object;
+
+ this.instances.set(identifier, callback(instance, this));
+
+ this.rebound(identifier);
+ } else {
+ // Otherwise, add extend callback to the existing for the identifier.
+ if (!this.extenders.has(identifier)) {
+ this.extenders.set(identifier, []);
+ }
+
+ const existing: ExtendCallback[] = this.extenders.get(identifier) as ExtendCallback[];
+ existing.push(callback);
+ this.extenders.set(identifier, existing);
+
+ if (this.isResolved(identifier)) {
+ this.rebound(identifier);
+ }
+ }
+
+ return this;
+ }
+
+ /**
+ * Register a callback to be invoked whenever identifier is "rebound"
+ *
+ * @param {Identifier} identifier
+ * @param {ReboundCallback} callback
+ *
+ * @return {any | void}
+ *
+ * @throws {ContainerException}
+ */
+ public rebinding(identifier: Identifier, callback: ReboundCallback): any | void /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ {
+ identifier = this.getAlias(identifier);
+
+ if (!this.reboundCallbacks.has(identifier)) {
+ this.reboundCallbacks.set(identifier, []);
+ }
+
+ const existing: ReboundCallback[] = this.reboundCallbacks.get(identifier) as ReboundCallback[];
+ existing.push(callback);
+ this.reboundCallbacks.set(identifier, existing);
+
+ if (this.has(identifier)) {
+ return this.make(identifier);
+ }
+ }
+
+ /**
+ * Forget binding and resolved instance for given identifier
+ *
+ * @param {Identifier} identifier
+ *
+ * @returns {boolean}
+ */
+ public forget(identifier: Identifier): boolean
+ {
+ const removedAlias: boolean = this.aliases.delete(identifier);
+ const removedInstance: boolean = this.forgetInstance(identifier);
+ const removedBinding: boolean = this.bindings.delete(this.getAlias(identifier));
+
+ return removedBinding || removedInstance || removedAlias;
+ }
+
+ /**
+ * Forget resolved instance for given identifier
+ *
+ * @param {Identifier} identifier
+ *
+ * @return {boolean}
+ */
+ public forgetInstance(identifier: Identifier): boolean
+ {
+ return this.instances.delete(this.getAlias(identifier));
+ }
+
+ /**
+ * Flush container of all bindings and resolved instances
+ *
+ * @returns {void}
+ */
+ public flush(): void
+ {
+ this.bindings.clear();
+ this.instances.clear();
+ this.aliases.clear();
+ this.resolved.clear();
+ this.contextualBindings.clear();
+ this.resolveStack.clear();
+ }
+
+ /**
+ * Determine if identifier has been resolved
+ *
+ * @param {Identifier} identifier
+ *
+ * @return {boolean}
+ */
+ public isResolved(identifier: Identifier): boolean
+ {
+ identifier = this.getAlias(identifier);
+
+ return this.resolved.has(identifier)
+ || this.instances.has(identifier);
+ }
+
+ /**
+ * Register a callback to be invoked before a binding is resolved
+ *
+ * @param {Identifier} identifier
+ * @param {BeforeResolvedCallback} callback
+ *
+ * @return {this}
+ */
+ public before(identifier: Identifier, callback: BeforeResolvedCallback): this
+ {
+ identifier = this.getAlias(identifier);
+
+ if (!this.beforeResolvedCallbacks.has(identifier)) {
+ this.beforeResolvedCallbacks.set(identifier, []);
+ }
+
+ const existing: BeforeResolvedCallback[] = this.beforeResolvedCallbacks.get(identifier) as BeforeResolvedCallback[];
+ existing.push(callback);
+ this.beforeResolvedCallbacks.set(identifier, existing);
+
+ return this;
+ }
+
+ /**
+ * Register a callback to be invoked after a binding has been resolved
+ *
+ * @param {Identifier} identifier
+ * @param {AfterResolvedCallback} callback
+ *
+ * @return {this}
+ */
+ public after(identifier: Identifier, callback: AfterResolvedCallback): this
+ {
+ identifier = this.getAlias(identifier);
+
+ if (!this.afterResolvedCallbacks.has(identifier)) {
+ this.afterResolvedCallbacks.set(identifier, []);
+ }
+
+ const existing: AfterResolvedCallback[] = this.afterResolvedCallbacks.get(identifier) as AfterResolvedCallback[];
+ existing.push(callback);
+ this.afterResolvedCallbacks.set(identifier, existing);
+
+ return this;
+ }
+
+ /**
+ * Resolves binding value that matches identifier
+ *
+ * @template T = any
+ *
+ * @param {Identifier} identifier
+ * @param {any[]} args Eventual arguments to pass on to {@link FactoryCallback} or {@link Constructor}
+ * @param {boolean} [fireEvents=true] If `true`, then "before" and "after" resolved callbacks will be invoked.
+ *
+ * @returns {T}
+ *
+ * @throws {NotFoundException}
+ * @throws {ContainerException}
+ *
+ * @protected
+ */
+ protected resolve<
+ T = any /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ >(
+ identifier: Identifier,
+ args: any[] = [], /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ fireEvents: boolean = true
+ ): T
+ {
+ identifier = this.getAlias(identifier);
+
+ // Fire "before" resolve callbacks
+ if (fireEvents) {
+ this.fireBeforeResolvedCallbacks(identifier, args);
+ }
+
+ // Returns "shared" instance, if requested identifier matches one.
+ if (this.instances.has(identifier)) {
+ return this.instances.get(identifier) as T;
+ }
+
+ // Find registered binding for identifier. Or, fail if no binding exists and given
+ // identifier is not buildable.
+ const binding: Binding | undefined = this.bindings.get(identifier);
+
+ if (binding === undefined && this.isBuildable(identifier)) {
+ return this.build(identifier as Constructor, args);
+ } else if (binding === undefined) {
+ throw new NotFoundError(`No binding found for ${identifier.toString()}`, { cause: { identifier: identifier, args: args } });
+ }
+
+ // Build instance or value that was registered for the given identifier.
+ let resolved: T = this.build(binding, args);
+
+ // Invoke all extend callbacks for identifier, if any have been registered.
+ const extendCallbacks: ExtendCallback[] = this.getExtenders(identifier);
+ for (const extendCallback of extendCallbacks) {
+ resolved = extendCallback(resolved, this) as T;
+ }
+
+ // If requested binding is registered as "shared", save the resolved as an instance.
+ if (this.isShared(identifier)) {
+ this.instances.set(identifier, resolved as object);
+ }
+
+ // Fire "after" resolved callbacks
+ if (fireEvents) {
+ this.fireAfterResolvedCallbacks(identifier, resolved);
+ }
+
+ // Mark identifier as resolved
+ this.resolved.add(identifier);
+
+ // Finally, return the resolved instance or value
+ return resolved;
+ }
+
+ /**
+ * Resolves given factory callback
+ *
+ * @template T = object
+ *
+ * @param {FactoryCallback} callback
+ * @param {any[]} [args]
+ * @param {Identifier} [identifier]
+ *
+ * @returns {T}
+ *
+ * @throws {ContainerException}
+ *
+ * @protected
+ */
+ protected resolveFactoryCallback(
+ callback: FactoryCallback,
+ args: any[] = [], /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ identifier?: Identifier
+ ): T
+ {
+ try {
+ return callback(this, ...args) as T;
+ } catch (e) {
+ identifier = identifier ?? callback;
+
+ const reason: string = getErrorMessage(e);
+ const options = {
+ cause: {
+ callback: callback,
+ args: args,
+ identifier: identifier,
+ previous: e
+ }
+ }
+
+ throw new ContainerError(`Unable to resolve factory callback for binding "${identifier.toString()}": ${reason}`, options);
+ }
+ }
+
+ /**
+ * Resolves given constructor and eventual dependencies
+ *
+ * @template T = object
+ *
+ * @param {Constructor} target
+ * @param {any[]} [args] Defaults to target class' dependencies (if available), when no arguments are
+ * given.
+ * @param {Identifier} [identifier]
+ *
+ * @returns {T}
+ *
+ * @throws {ContainerException}
+ *
+ * @protected
+ */
+ protected resolveConstructor(
+ target: Constructor,
+ args: any[] = [], /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ identifier?: Identifier
+ ): T
+ {
+ try {
+ this.preventCircularDependency(target as Constructor);
+
+ this.resolveStack.add(target as Constructor);
+
+ // When no arguments are given (overwrites), attempt to obtain defined
+ // dependencies for the target class and resolve them from the container.
+ if (args.length == 0 && this.hasDependencies(target)) {
+ args = this.resolveDependencies(target);
+ }
+
+ // Create the instance with arguments.
+ const resolved: T = new target(...args) as T;
+
+ this.resolveStack.delete(target as Constructor);
+
+ return resolved;
+ } catch (e) {
+ identifier = identifier ?? target;
+
+ if (e instanceof CircularDependencyError) {
+ if (e.cause === undefined) {
+ e.cause = Object.create(null);
+ }
+
+ (e.cause as any).target = target; /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ (e.cause as any).args = args; /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ (e.cause as any).identifier = identifier; /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ (e.cause as any).resolveStack = Array.from(this.resolveStack); /* eslint-disable-line @typescript-eslint/no-explicit-any */
+
+ this.resolveStack.delete(target as Constructor);
+
+ throw e;
+ }
+
+ const reason: string = getErrorMessage(e);
+ const options = {
+ cause: {
+ target: target,
+ args: args,
+ identifier: identifier,
+ resolveStack: Array.from(this.resolveStack),
+ previous: e
+ }
+ }
+
+ this.resolveStack.delete(target as Constructor);
+
+ throw new ContainerError(`Unable to resolve "${getNameOrDesc(target as ConstructorLike)}" for binding "${identifier.toString()}": ${reason}`, options);
+ }
+ }
+
+ /**
+ * Invokes method in class and returns the methods output
+ *
+ * @param {ClassMethodReference} reference
+ * @param {any[]} [args]
+ *
+ * @returns {any}
+ *
+ * @throws {ContainerError}
+ *
+ * @protected
+ */
+ protected invokeClassMethod(reference: ClassMethodReference, args: any[] = []): any /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ {
+ // Build object, when target is a constructor
+ let target = reference[0];
+ if (typeof target != 'object') {
+ target = this.make(target);
+ }
+
+ const name: ClassMethodName = reference[1];
+ const method: Callback = target[name];
+
+ // Resolve evt. dependencies if no arguments are given...
+ if (args.length == 0 && this.hasDependencies(method as object)) {
+ args = this.resolveDependencies(method as object);
+ }
+
+ // Wrap the class method into a callback-wrapper, such that the "ThisArg" can be
+ // applied correctly.
+ return this.invokeCallbackWrapper(
+ Wrapper.makeFor(target, method, args)
+ );
+ }
+
+ /**
+ * Invokes the wrapped callback and returns its output
+ *
+ * @param {CallbackWrapper} wrapper
+ * @param {any[]} [args]
+ *
+ * @returns {any}
+ *
+ * @throws {ContainerError}
+ *
+ * @protected
+ */
+ protected invokeCallbackWrapper(wrapper: CallbackWrapper, args: any[] = []): any /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ {
+ // A callback wrapper might already have arguments defined. However,
+ // if there are any arguments provided here, then the wrapper's arguments must
+ // be overwritten.
+ if (args.length != 0) {
+ wrapper.arguments = args;
+ }
+
+ // But, if no arguments are given, and if the wrapper does not have any arguments,
+ // then we check if "dependencies" has been defined. If so, they which must be resolved
+ // and set as arguments for the wrapper's callback.
+ if (args.length == 0
+ && !wrapper.hasArguments()
+ && hasAllMethods(wrapper, 'has', 'get')
+ /* @ts-expect-error TS7053 - has method is in wrapper at this point */
+ && wrapper['has'](DEPENDENCIES)
+ ) {
+ /* @ts-expect-error TS7053 - get method is in wrapper at this point */
+ const dependencies: Identifier[] = wrapper['get'](DEPENDENCIES) ?? [];
+ const resolved: any[] = []; /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ for(const identifier of dependencies) {
+ resolved.push(this.resolveDependency(identifier, wrapper));
+ }
+
+ wrapper.arguments = resolved;
+ }
+
+ // Finally, call the wrapper's callback.
+ return wrapper.call();
+ }
+
+ /**
+ * Invokes the given callback and returns its output
+ *
+ * @param {Callback} callback
+ * @param {any[]} [args]
+ *
+ * @returns {any}
+ *
+ * @throws {ContainerError}
+ *
+ * @protected
+ */
+ protected invokeCallback(callback: Callback, args: any[] = []): any /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ {
+ // When no arguments are provided, attempt to obtain and resolve dependencies
+ // for the callback (if any dependencies are defined for the callback).
+ if (args.length == 0 && this.hasDependencies(callback as object)) {
+ args = this.resolveDependencies(callback as object);
+ }
+
+ return callback(...args);
+ }
+
+ /**
+ * Obtains and resolves dependencies for given target
+ *
+ * @param {object} target
+ * @returns {any[]} Resolved dependencies or empty, if none available
+ *
+ * @throws {ContainerError}
+ *
+ * @protected
+ */
+ protected resolveDependencies(target: object): any[] /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ {
+ const resolved: any[] = []; /* eslint-disable-line @typescript-eslint/no-explicit-any */
+
+ const dependencies: Identifier[] = this.getDependencies(target);
+ for (const identifier of dependencies) {
+ resolved.push(this.resolveDependency(identifier, target));
+ }
+
+ return resolved;
+ }
+
+ /**
+ * Resolves dependency that matches given identifier, for given target
+ *
+ * @param {Identifier} identifier
+ * @param {object} target
+ *
+ * @returns {any}
+ *
+ * @throws {ContainerError}
+ *
+ * @protected
+ */
+ protected resolveDependency(identifier: Identifier, target: object): any /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ {
+ try {
+ // In case that the target is a constructor that has a contextual binding defined
+ // for given identifier, then it must be resolved.
+ if (this.hasContextualBinding(target as Constructor, identifier)) {
+ const binding = this.getContextualBinding(target as Constructor, identifier);
+
+ return this.build(binding as Binding);
+ }
+
+ // Otherwise, just resolve the given binding identifier...
+ return this.make(identifier);
+ } catch (e) {
+ if (e instanceof CircularDependencyError) {
+ if (e.cause === undefined) {
+ e.cause = Object.create(null);
+ }
+
+ (e.cause as any).target = target; /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ (e.cause as any).identifier = identifier; /* eslint-disable-line @typescript-eslint/no-explicit-any */
+
+ throw e;
+ }
+
+ const reason: string = getErrorMessage(e);
+ const options = {
+ cause: {
+ identifier: identifier,
+ target: target
+ }
+ }
+
+ throw new ContainerError(`Unable to resolve "${identifier.toString()}" for target "${getNameOrDesc(target as ConstructorLike)}": ${reason}`, options);
+ }
+ }
+
+ /**
+ * Invokes "rebound" callbacks for given identifier
+ *
+ * @param {Identifier} identifier
+ *
+ * @return {void}
+ *
+ * @throws {ContainerError}
+ *
+ * @protected
+ */
+ protected rebound(identifier: Identifier): void
+ {
+ const resolved = this.make(identifier);
+
+ const callbacks: ReboundCallback[] = this.getReboundCallbacks(identifier);
+ for (const callback of callbacks) {
+ callback(resolved, this);
+ }
+ }
+
+ /**
+ * Get "rebound" callbacks for given identifier
+ *
+ * @param {Identifier} identifier
+ *
+ * @return {ReboundCallback[]}
+ *
+ * @protected
+ */
+ protected getReboundCallbacks(identifier: Identifier): ReboundCallback[]
+ {
+ return this.reboundCallbacks.get(identifier) ?? [];
+ }
+
+ /**
+ * Invokes the "before" resolved callbacks for given identifier
+ *
+ * @param {Identifier} identifier
+ * @param {any[]} [args]
+ *
+ * @return {void}
+ *
+ * @protected
+ */
+ protected fireBeforeResolvedCallbacks(
+ identifier: Identifier,
+ args: any[] = [], /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ ): void
+ {
+ const callbacks: BeforeResolvedCallback[] = this.beforeResolvedCallbacks.get(identifier) ?? [];
+
+ for (const callback of callbacks) {
+ callback(identifier, args, this);
+ }
+ }
+
+ /**
+ * Invokes the "after" resolved callbacks for given identifier
+ *
+ * @param {Identifier} identifier
+ *
+ * @param {any} resolved
+ *
+ * @return {void}
+ *
+ * @protected
+ */
+ protected fireAfterResolvedCallbacks(
+ identifier: Identifier,
+ resolved: any /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ ): void
+ {
+ const callbacks: AfterResolvedCallback[] = this.afterResolvedCallbacks.get(identifier) ?? [];
+
+ for (const callback of callbacks) {
+ callback(identifier, resolved, this);
+ }
+ }
+
+ /**
+ * Returns list of extend callbacks for given identifier
+ *
+ * @param {Identifier} identifier
+ *
+ * @returns {ExtendCallback[]}
+ *
+ * @protected
+ */
+ protected getExtenders(identifier: Identifier): ExtendCallback[]
+ {
+ return this.extenders.get(this.getAlias(identifier)) ?? [];
+ }
+
+ /**
+ * Determine if given target is buildable
+ *
+ * @param {unknown} target
+ *
+ * @returns {boolean}
+ *
+ * @protected
+ */
+ protected isBuildable(target: unknown): boolean
+ {
+ return isConstructor(target);
+ }
+
+ /**
+ * Returns a new Binding Entry for given identifier and binding value
+ *
+ * @param {Identifier} identifier
+ * @param {FactoryCallback | Constructor} value
+ * @param {boolean} [shared]
+ *
+ * @return {Binding}
+ *
+ * @throws {TypeError}
+ *
+ * @protected
+ */
+ protected makeBindingEntry(
+ identifier: Identifier,
+ value: FactoryCallback | Constructor,
+ shared: boolean = false
+ ): Binding
+ {
+ return new BindingEntry(identifier, value, shared);
+ }
+
+ /**
+ * Determine if object is a binding entry
+ *
+ * @param {object} target
+ *
+ * @returns {boolean}
+ *
+ * @protected
+ */
+ protected isBinding(target: object): boolean
+ {
+ return isBinding(target);
+ }
+
+ /**
+ * Determine if target has dependencies defined
+ *
+ * @param {object} target
+ *
+ * @returns {boolean}
+ *
+ * @protected
+ */
+ protected hasDependencies(target: object): boolean
+ {
+ return hasDependencies(target);
+ }
+
+ /**
+ * Returns the defined dependencies for given target
+ *
+ * @param {object} target
+ *
+ * @returns {Identifier[]}
+ *
+ * @protected
+ */
+ protected getDependencies(target: object): Identifier[]
+ {
+ return getDependencies(target);
+ }
+
+ /**
+ * Aborts current make, build or call process, if given target is already in
+ * the process of being resolved.
+ *
+ * @param {Constructor} target
+ *
+ * @throws {CircularDependencyError}
+ *
+ * @protected
+ */
+ protected preventCircularDependency(target: Constructor): void
+ {
+ // Skip if target is not in the current "resolve" stack.
+ if (!this.resolveStack.has(target)) {
+ return;
+ }
+
+ // However, if the target is in the current "resolve" stack, it means that the
+ // target has a circular dependency to itself. To avoid an infinite loop, the
+ // make, build or call process must be aborted.
+
+ // Prepare a string "resolve" stack to make it somewhat easier for developers
+ // to understand how we got here...
+ const stack: string[] = Array.from(this.resolveStack).map((ctor: Constructor) => {
+ return getNameOrDesc(ctor);
+ });
+ stack.push(getNameOrDesc(target));
+
+ const trace: string = stack.join(' -> ');
+
+ throw new CircularDependencyError(`Circular Dependency for target "${getNameOrDesc(target as ConstructorLike)}". Resolve stack: ${trace}`);
+ }
+}
\ No newline at end of file
diff --git a/packages/container/src/exceptions/CircularDependencyError.ts b/packages/container/src/exceptions/CircularDependencyError.ts
new file mode 100644
index 00000000..aed50643
--- /dev/null
+++ b/packages/container/src/exceptions/CircularDependencyError.ts
@@ -0,0 +1,22 @@
+import type { CircularDependencyException } from "@aedart/contracts/container";
+import ContainerError from "./ContainerError";
+import { configureCustomError } from "@aedart/support/exceptions";
+
+/**
+ * Circular Dependency Error
+ */
+export default class CircularDependencyError extends ContainerError implements CircularDependencyException
+{
+ /**
+ * Create new Not Found Error instance
+ *
+ * @param {string} [message]
+ * @param {ErrorOptions} [options]
+ */
+ constructor(message?: string, options?: ErrorOptions)
+ {
+ super(message, options);
+
+ configureCustomError(this);
+ }
+}
\ No newline at end of file
diff --git a/packages/container/src/exceptions/ContainerError.ts b/packages/container/src/exceptions/ContainerError.ts
new file mode 100644
index 00000000..d62d1751
--- /dev/null
+++ b/packages/container/src/exceptions/ContainerError.ts
@@ -0,0 +1,23 @@
+import type { ContainerException } from "@aedart/contracts/container";
+import { configureCustomError } from "@aedart/support/exceptions";
+
+/**
+ * Container Error
+ *
+ * @see ContainerException
+ */
+export default class ContainerError extends Error implements ContainerException
+{
+ /**
+ * Create new Container Error instance
+ *
+ * @param {string} [message]
+ * @param {ErrorOptions} [options]
+ */
+ constructor(message?: string, options?: ErrorOptions)
+ {
+ super(message, options);
+
+ configureCustomError(this);
+ }
+}
\ No newline at end of file
diff --git a/packages/container/src/exceptions/NotFoundError.ts b/packages/container/src/exceptions/NotFoundError.ts
new file mode 100644
index 00000000..edf4c86d
--- /dev/null
+++ b/packages/container/src/exceptions/NotFoundError.ts
@@ -0,0 +1,24 @@
+import type { NotFoundException } from "@aedart/contracts/container";
+import ContainerError from "./ContainerError";
+import { configureCustomError } from "@aedart/support/exceptions";
+
+/**
+ * Not Found Error
+ *
+ * @see NotFoundException
+ */
+export default class NotFoundError extends ContainerError implements NotFoundException
+{
+ /**
+ * Create new Not Found Error instance
+ *
+ * @param {string} [message]
+ * @param {ErrorOptions} [options]
+ */
+ constructor(message?: string, options?: ErrorOptions)
+ {
+ super(message, options);
+
+ configureCustomError(this);
+ }
+}
\ No newline at end of file
diff --git a/packages/container/src/exceptions/index.ts b/packages/container/src/exceptions/index.ts
new file mode 100644
index 00000000..db9803db
--- /dev/null
+++ b/packages/container/src/exceptions/index.ts
@@ -0,0 +1,8 @@
+import CircularDependencyError from "./CircularDependencyError";
+import ContainerError from "./ContainerError";
+import NotFoundError from "./NotFoundError";
+export {
+ CircularDependencyError,
+ ContainerError,
+ NotFoundError
+}
\ No newline at end of file
diff --git a/packages/container/src/index.ts b/packages/container/src/index.ts
new file mode 100644
index 00000000..cc16885b
--- /dev/null
+++ b/packages/container/src/index.ts
@@ -0,0 +1,12 @@
+import Builder from "./Builder";
+import BindingEntry from "./BindingEntry";
+import Container from "./Container";
+export {
+ Builder,
+ BindingEntry,
+ Container
+}
+
+export * from './exceptions';
+export * from './BindingEntryBlueprint';
+export * from './isBinding';
\ No newline at end of file
diff --git a/packages/container/src/isBinding.ts b/packages/container/src/isBinding.ts
new file mode 100644
index 00000000..aa0dae8d
--- /dev/null
+++ b/packages/container/src/isBinding.ts
@@ -0,0 +1,28 @@
+import { classLooksLike } from "@aedart/support/reflections";
+import { isset } from "@aedart/support/misc";
+import { BindingEntryBlueprint } from "./BindingEntryBlueprint";
+import BindingEntry from "./BindingEntry";
+
+/**
+ * Determine if given object is a [Binding]{@link import('@aedart/contracts/container').Binding}
+ *
+ * @param {object} instance
+ *
+ * @returns {boolean}
+ */
+export function isBinding(instance: object): boolean
+{
+ if (!isset(instance) || typeof instance !== 'object') {
+ return false;
+ }
+
+ if (instance instanceof BindingEntry) {
+ return true;
+ }
+
+ if (!Reflect.has(instance, 'constructor')) {
+ return false;
+ }
+
+ return classLooksLike(instance.constructor as object, BindingEntryBlueprint);
+}
\ No newline at end of file
diff --git a/packages/container/tsconfig.json b/packages/container/tsconfig.json
new file mode 100644
index 00000000..463dd768
--- /dev/null
+++ b/packages/container/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "extends": "../../shared/tsconfig.json",
+ "compilerOptions": {
+ "rootDir": "./src",
+ "declarationDir": "./dist/tmp",
+ "composite": true
+ },
+ "include": [
+ "./src/**/*"
+ ],
+ "references": [
+ { "path": "../contracts", "prepend": false },
+ { "path": "../support", "prepend": false },
+ ]
+}
diff --git a/packages/contracts/NOTICE b/packages/contracts/NOTICE
index f97cc79b..baf4cfe1 100644
--- a/packages/contracts/NOTICE
+++ b/packages/contracts/NOTICE
@@ -2,8 +2,8 @@ NOTICES AND INFORMATION
Please do not translate or Localize.
Parts of the herein provided software are considered an "adaptation", or "derivative work", of 3rd party software.
-Below you will find general information about which parts are affected, or where you may find additional information
-such, along with original license(s), terms and conditions as provided by the 3rd party software.
+Below you will find general information about which parts are affected, or where you may find additional information,
+along with original license(s), terms and conditions as provided by the 3rd party software.
3rd party software that are included as dependencies by this software is NOT covered by this NOTICE file, unless
explicitly required by 3rd party software license(s). You can find original license(s), terms and
@@ -15,6 +15,10 @@ conditions of the included 3rd party software, in the directory where it has bee
The Arrayable interface is an interface is an adaptation of Laravel's Arrayable interface.
See https://github.com/laravel/framework/blob/master/src/Illuminate/Contracts/Support/Arrayable.php
+@aedart/contracts/container (Container)
+ The service container interface is heavily inspired by Laravel's Container
+
+ See https://github.com/laravel/framework/blob/master/src/Illuminate/Contracts/Container/Container.php
License MIT, Copyright (c) Taylor Otwell.
@@ -253,4 +257,39 @@ conditions of the included 3rd party software, in the directory where it has bee
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
- limitations under the License.
\ No newline at end of file
+ limitations under the License.
+
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+@aedart/contracts/container (Container, ContainerException, NotFoundException)
+ Parts of the service container interface, along with the exception interfaces are adapted from
+ Psr's version thereof.
+
+ See https://www.php-fig.org/psr/psr-11/
+
+ License MIT, Copyright (c) 2013-2016 container-interop
+ Copyright (c) 2016 PHP Framework Interoperability Group
+
+ This part of the NOTICE file corresponds to terms and conditions set by the MIT License
+ =======================================================================================
+
+ The MIT License (MIT)
+
+ Copyright (c) 2013-2016 container-interop
+ Copyright (c) 2016 PHP Framework Interoperability Group
+
+ 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.
\ No newline at end of file
diff --git a/packages/contracts/package.json b/packages/contracts/package.json
index d6cc2c6a..be0397e2 100644
--- a/packages/contracts/package.json
+++ b/packages/contracts/package.json
@@ -31,6 +31,11 @@
"import": "./dist/esm/contracts.js",
"require": "./dist/cjs/contracts.cjs"
},
+ "./container": {
+ "types": "./dist/types/container.d.ts",
+ "import": "./dist/esm/container.js",
+ "require": "./dist/cjs/container.cjs"
+ },
"./support": {
"types": "./dist/types/support.d.ts",
"import": "./dist/esm/support.js",
@@ -51,6 +56,11 @@
"import": "./dist/esm/support/exceptions.js",
"require": "./dist/cjs/support/exceptions.cjs"
},
+ "./support/facades": {
+ "types": "./dist/types/support/facades.d.ts",
+ "import": "./dist/esm/support/facades.js",
+ "require": "./dist/cjs/support/facades.cjs"
+ },
"./support/meta": {
"types": "./dist/types/support/meta.d.ts",
"import": "./dist/esm/support/meta.js",
diff --git a/packages/contracts/rollup.config.mjs b/packages/contracts/rollup.config.mjs
index 066e3fd3..b35ca141 100644
--- a/packages/contracts/rollup.config.mjs
+++ b/packages/contracts/rollup.config.mjs
@@ -3,10 +3,12 @@ import { createConfig } from '../../shared/rollup.config.mjs';
export default createConfig({
baseDir: new URL('.', import.meta.url),
external: [
+ '@aedart/contracts/container',
'@aedart/contracts/support',
'@aedart/contracts/support/arrays',
'@aedart/contracts/support/concerns',
'@aedart/contracts/support/exceptions',
+ '@aedart/contracts/support/facades',
'@aedart/contracts/support/meta',
'@aedart/contracts/support/mixins',
'@aedart/contracts/support/reflections',
diff --git a/packages/contracts/src/container/Binding.ts b/packages/contracts/src/container/Binding.ts
new file mode 100644
index 00000000..d1ea9107
--- /dev/null
+++ b/packages/contracts/src/container/Binding.ts
@@ -0,0 +1,55 @@
+import { Constructor } from "@aedart/contracts";
+import { FactoryCallback, Identifier } from "./types";
+
+/**
+ * Binding Entry
+ *
+ * @template T = any
+ */
+export default interface Binding<
+ T = any /* eslint-disable-line @typescript-eslint/no-explicit-any */
+> {
+ /**
+ * This binding's identifier
+ *
+ * @type {Identifier}
+ *
+ * @readonly
+ */
+ readonly identifier: Identifier;
+
+ /**
+ * The bound value to be resolved by a service container
+ *
+ * @template T = any
+ *
+ * @type {FactoryCallback | Constructor}
+ *
+ * @readonly
+ */
+ readonly value: FactoryCallback | Constructor;
+
+ /**
+ * Shared state of resolved value
+ *
+ * @type {boolean} If `true`, then service container must register resolved
+ * value as a singleton.
+ *
+ * @readonly
+ */
+ readonly shared: boolean;
+
+ /**
+ * Determine if bound value is a {@link FactoryCallback}
+ *
+ * @returns {boolean}
+ */
+ isFactoryCallback(): boolean;
+
+ /**
+ * Determine if bound value is a {@link Constructor}
+ *
+ * @returns {boolean}
+ */
+ isConstructor(): boolean;
+}
\ No newline at end of file
diff --git a/packages/contracts/src/container/Container.ts b/packages/contracts/src/container/Container.ts
new file mode 100644
index 00000000..5c9625ea
--- /dev/null
+++ b/packages/contracts/src/container/Container.ts
@@ -0,0 +1,328 @@
+import {
+ Callback,
+ Constructor,
+ ClassMethodReference
+} from "@aedart/contracts";
+import { CallbackWrapper } from "@aedart/contracts/support";
+import {
+ Alias,
+ Identifier,
+ FactoryCallback,
+ ExtendCallback,
+ ReboundCallback,
+ BeforeResolvedCallback,
+ AfterResolvedCallback,
+} from "./types";
+import Binding from "./Binding";
+import ContextualBindingBuilder from "./ContextualBindingBuilder";
+
+/**
+ * Service Container
+ *
+ * Adaptation of Psr's `ContainerInterface`, and Laravel's service `Container`.
+ *
+ * @see https://www.php-fig.org/psr/psr-11/#31-psrcontainercontainerinterface
+ * @see https://github.com/laravel/framework/blob/master/src/Illuminate/Contracts/Container/Container.php
+ */
+export default interface Container
+{
+ /**
+ * Register a binding
+ *
+ * @param {Identifier} identifier
+ * @param {FactoryCallback | Constructor} [concrete]
+ * @param {boolean} [shared=false]
+ *
+ * @returns {this}
+ *
+ * @throws {TypeError}
+ */
+ bind(identifier: Identifier, concrete?: FactoryCallback | Constructor, shared?: boolean): this;
+
+ /**
+ * Register a binding, if none already exists for given identifier
+ *
+ * @param {Identifier} identifier
+ * @param {FactoryCallback | Constructor} [concrete]
+ * @param {boolean} [shared=false]
+ *
+ * @returns {this}
+ *
+ * @throws {TypeError}
+ */
+ bindIf(identifier: Identifier, concrete?: FactoryCallback | Constructor, shared?: boolean): this;
+
+ /**
+ * Register a shared binding
+ *
+ * @param {Identifier} identifier
+ * @param {FactoryCallback | Constructor} [concrete]
+ *
+ * @returns {this}
+ *
+ * @throws {TypeError}
+ */
+ singleton(identifier: Identifier, concrete?: FactoryCallback | Constructor): this;
+
+ /**
+ * Register a shared binding, if none already exists for given identifier
+ *
+ * @param {Identifier} identifier
+ * @param {FactoryCallback | Constructor} [concrete]
+ *
+ * @returns {this}
+ *
+ * @throws {TypeError}
+ */
+ singletonIf(identifier: Identifier, concrete?: FactoryCallback | Constructor): this;
+
+ /**
+ * Register existing object instance as a shared binding
+ *
+ * @template T = object
+ *
+ * @param {Identifier} identifier
+ * @param {T} instance
+ *
+ * @returns {T}
+ *
+ * @throws {TypeError}
+ */
+ instance(identifier: Identifier, instance: T): T;
+
+ /**
+ * Add a contextual binding in this container
+ *
+ * @param {Constructor} concrete
+ * @param {Identifier} identifier
+ * @param {FactoryCallback | Constructor} implementation
+ *
+ * @return {this}
+ *
+ * @throws {TypeError}
+ */
+ addContextualBinding(
+ concrete: Constructor,
+ identifier: Identifier,
+ implementation: FactoryCallback | Constructor
+ ): this;
+
+ /**
+ * Define a contextual binding
+ *
+ * @param {...Constructor[]} concrete
+ *
+ * @return {ContextualBindingBuilder}
+ *
+ * @throws {TypeError}
+ */
+ when(...concrete: Constructor[]): ContextualBindingBuilder;
+
+ /**
+ * Resolves binding value that matches identifier and returns it
+ *
+ * @template T = any
+ *
+ * @param {Identifier} identifier
+ *
+ * @returns {T}
+ *
+ * @throws {NotFoundException}
+ * @throws {ContainerException}
+ */
+ get<
+ T = any /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ >(identifier: Identifier): T;
+
+ /**
+ * Determine if an entry is registered for given identifier.
+ *
+ * @param {Identifier} identifier
+ *
+ * @returns {boolean}
+ */
+ has(identifier: Identifier): boolean;
+
+ /**
+ * Alias for {@link has}
+ *
+ * @param {Identifier} identifier
+ *
+ * @returns {boolean}
+ */
+ bound(identifier: Identifier): boolean;
+
+ /**
+ * Alias identifier as a different identifier
+ *
+ * @param {Identifier} identifier
+ * @param {Alias} alias
+ *
+ * @returns {this}
+ *
+ * @throws {TypeError}
+ */
+ alias(identifier: Identifier, alias: Alias): this;
+
+ /**
+ * Determine if identifier is an alias
+ *
+ * @param {Identifier} identifier
+ *
+ * @return {boolean}
+ */
+ isAlias(identifier: Identifier): boolean;
+
+ /**
+ * Determine if identifier is registered as a "shared" binding
+ *
+ * @param {Identifier} identifier
+ *
+ * @returns {boolean}
+ */
+ isShared(identifier: Identifier): boolean;
+
+ /**
+ * Resolves binding value that matches identifier and returns it
+ *
+ * @template T = any
+ *
+ * @param {Identifier} identifier
+ * @param {any[]} [args] Eventual arguments to pass on to {@link FactoryCallback} or {@link Constructor}
+ *
+ * @returns {T}
+ *
+ * @throws {NotFoundException}
+ * @throws {ContainerException}
+ */
+ make<
+ T = any /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ >(identifier: Identifier, args?: any[] /* eslint-disable-line @typescript-eslint/no-explicit-any */): T;
+
+ /**
+ * Resolves values if a binding exists for identifier, or returns a default value
+ *
+ * @template T = any
+ * @template D = undefined
+ *
+ * @param {Identifier} identifier
+ * @param {any[]} [args] Eventual arguments to pass on to {@link FactoryCallback} or {@link Constructor}
+ * @param {D} [defaultValue]
+ *
+ * @returns {T}
+ *
+ * @throws {ContainerException}
+ */
+ makeOrDefault<
+ T = any, /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ D = undefined
+ >(identifier: Identifier, args?: any[] /* eslint-disable-line @typescript-eslint/no-explicit-any */, defaultValue?: D): T | D;
+
+ /**
+ * Instantiate a new instance of given concrete
+ *
+ * @template T = object
+ *
+ * @param {Constructor | Binding} concrete
+ * @param {any[]} [args] Eventual arguments to pass on the concrete instance's constructor.
+ *
+ * @returns {T}
+ *
+ * @throws {ContainerException}
+ */
+ build(
+ concrete: Constructor | Binding,
+ args?: any[] /* eslint-disable-line @typescript-eslint/no-explicit-any */
+ ): T;
+
+ /**
+ * Call given method and inject dependencies if needed
+ *
+ * @param {Callback | CallbackWrapper | ClassMethodReference} method
+ * @param {any[]} [args]
+ *
+ * @return {any}
+ *
+ * @throws {ContainerException}
+ */
+ call(method: Callback | CallbackWrapper | ClassMethodReference, args?: any[]): any; /* eslint-disable-line @typescript-eslint/no-explicit-any */
+
+ /**
+ * Extend the registered binding
+ *
+ * @param {Identifier} identifier
+ * @param {ExtendCallback} callback
+ *
+ * @return {this}
+ *
+ * @throws {TypeError}
+ * @throws {ContainerException}
+ */
+ extend(identifier: Identifier, callback: ExtendCallback): this;
+
+ /**
+ * Register a callback to be invoked whenever identifier is "rebound"
+ *
+ * @param {Identifier} identifier
+ * @param {ReboundCallback} callback
+ *
+ * @return {any | void}
+ *
+ * @throws {ContainerException}
+ */
+ rebinding(identifier: Identifier, callback: ReboundCallback): any | void; /* eslint-disable-line @typescript-eslint/no-explicit-any */
+
+ /**
+ * Forget binding and resolved instance for given identifier
+ *
+ * @param {Identifier} identifier
+ *
+ * @returns {boolean}
+ */
+ forget(identifier: Identifier): boolean;
+
+ /**
+ * Forget resolved instance for given identifier
+ *
+ * @param {Identifier} identifier
+ *
+ * @return {boolean}
+ */
+ forgetInstance(identifier: Identifier): boolean;
+
+ /**
+ * Flush container of all bindings and resolved instances
+ *
+ * @returns {void}
+ */
+ flush(): void;
+
+ /**
+ * Determine if identifier has been resolved
+ *
+ * @param {Identifier} identifier
+ *
+ * @return {boolean}
+ */
+ isResolved(identifier: Identifier): boolean;
+
+ /**
+ * Register a callback to be invoked before a binding is resolved
+ *
+ * @param {Identifier} identifier
+ * @param {BeforeResolvedCallback} callback
+ *
+ * @return {this}
+ */
+ before(identifier: Identifier, callback: BeforeResolvedCallback): this;
+
+ /**
+ * Register a callback to be invoked after a binding has been resolved
+ *
+ * @param {Identifier} identifier
+ * @param {AfterResolvedCallback} callback
+ *
+ * @return {this}
+ */
+ after(identifier: Identifier, callback: AfterResolvedCallback): this;
+}
\ No newline at end of file
diff --git a/packages/contracts/src/container/ContextualBindingBuilder.ts b/packages/contracts/src/container/ContextualBindingBuilder.ts
new file mode 100644
index 00000000..14781963
--- /dev/null
+++ b/packages/contracts/src/container/ContextualBindingBuilder.ts
@@ -0,0 +1,32 @@
+import { Constructor } from "@aedart/contracts";
+import { Identifier, FactoryCallback } from "./types";
+
+/**
+ * Contextual Binding Builder
+ *
+ * Adaptation of Laravel's Contextual Binding Builder.
+ *
+ * @see https://github.com/laravel/framework/blob/master/src/Illuminate/Contracts/Container/ContextualBindingBuilder.php
+ */
+export default interface ContextualBindingBuilder
+{
+ /**
+ * Define the target identifier in this context.
+ *
+ * @param {Identifier} identifier
+ *
+ * @return {this}
+ */
+ needs(identifier: Identifier): this;
+
+ /**
+ * Define the implementation to be resolved in this context.
+ *
+ * @param {FactoryCallback | Constructor} implementation
+ *
+ * @return {void}
+ *
+ * @throws {TypeError}
+ */
+ give(implementation: FactoryCallback | Constructor): void;
+}
\ No newline at end of file
diff --git a/packages/contracts/src/container/exceptions/CircularDependencyException.ts b/packages/contracts/src/container/exceptions/CircularDependencyException.ts
new file mode 100644
index 00000000..58a5b93f
--- /dev/null
+++ b/packages/contracts/src/container/exceptions/CircularDependencyException.ts
@@ -0,0 +1,8 @@
+import type ContainerException from "./ContainerException";
+
+/**
+ * Circular Dependency Exception
+ *
+ * To be thrown in situations when a binding has a circular dependency.
+ */
+export default interface CircularDependencyException extends ContainerException {}
\ No newline at end of file
diff --git a/packages/contracts/src/container/exceptions/ContainerException.ts b/packages/contracts/src/container/exceptions/ContainerException.ts
new file mode 100644
index 00000000..5b48e63a
--- /dev/null
+++ b/packages/contracts/src/container/exceptions/ContainerException.ts
@@ -0,0 +1,11 @@
+import {Throwable} from "@aedart/contracts/support/exceptions";
+
+/**
+ * Container Exception
+ *
+ * General exception to be thrown when something went wrong inside
+ * a service container. Inspired by Psr's `ContainerExceptionInterface`.
+ *
+ * @see https://www.php-fig.org/psr/psr-11/#32-psrcontainercontainerexceptioninterface
+ */
+export default interface ContainerException extends Throwable {}
\ No newline at end of file
diff --git a/packages/contracts/src/container/exceptions/NotFoundException.ts b/packages/contracts/src/container/exceptions/NotFoundException.ts
new file mode 100644
index 00000000..d6b2c278
--- /dev/null
+++ b/packages/contracts/src/container/exceptions/NotFoundException.ts
@@ -0,0 +1,11 @@
+import ContainerException from "./ContainerException";
+
+/**
+ * Not Found Exception
+ *
+ * To be thrown when no entry was found for a given binding identifier.
+ * Inspired by Psr's `NotFoundExceptionInterface`.
+ *
+ * @see https://www.php-fig.org/psr/psr-11/#33-psrcontainernotfoundexceptioninterface
+ */
+export default interface NotFoundException extends ContainerException {}
\ No newline at end of file
diff --git a/packages/contracts/src/container/exceptions/index.ts b/packages/contracts/src/container/exceptions/index.ts
new file mode 100644
index 00000000..dcc19dd3
--- /dev/null
+++ b/packages/contracts/src/container/exceptions/index.ts
@@ -0,0 +1,8 @@
+import CircularDependencyException from "./CircularDependencyException";
+import ContainerException from "./ContainerException";
+import NotFoundException from "./NotFoundException";
+export {
+ type CircularDependencyException,
+ type ContainerException,
+ type NotFoundException
+}
\ No newline at end of file
diff --git a/packages/contracts/src/container/index.ts b/packages/contracts/src/container/index.ts
new file mode 100644
index 00000000..9e27f986
--- /dev/null
+++ b/packages/contracts/src/container/index.ts
@@ -0,0 +1,28 @@
+/**
+ * Container identifier
+ *
+ * @type {Symbol}
+ */
+export const CONTAINER: unique symbol = Symbol('@aedart/contracts/container');
+
+/**
+ * Dependencies identifier
+ *
+ * Symbol is intended to be used as an identifier for when associating binding identifiers
+ * or "concrete" dependencies with an element, e.g. a class, class method, function,...etc.
+ *
+ * @type {symbol}
+ */
+export const DEPENDENCIES: unique symbol = Symbol('dependencies');
+
+import Binding from "./Binding";
+import Container from "./Container";
+import ContextualBindingBuilder from "./ContextualBindingBuilder";
+export {
+ type Binding,
+ type Container,
+ type ContextualBindingBuilder,
+}
+
+export * from './exceptions/index';
+export type * from './types';
\ No newline at end of file
diff --git a/packages/contracts/src/container/types.ts b/packages/contracts/src/container/types.ts
new file mode 100644
index 00000000..de546f69
--- /dev/null
+++ b/packages/contracts/src/container/types.ts
@@ -0,0 +1,71 @@
+import { Callback, ConstructorLike } from "@aedart/contracts";
+import Container from "./Container";
+
+/**
+ * Binding Identifier
+ *
+ * A unique identifier used for associating "concrete" items or values in
+ * a service container.
+ */
+export type Identifier = string | symbol | number | NonNullable