Skip to content

Commit

Permalink
Merge branch 'redesign-config-loading' into add-core
Browse files Browse the repository at this point in the history
  • Loading branch information
aedart committed Oct 6, 2024
2 parents 241f920 + 6ad896d commit 3eaec16
Show file tree
Hide file tree
Showing 34 changed files with 2,092 additions and 994 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* `isBindingTuple` util in `@aedart/container`.
* `test:fast` script in `packages.json`, to allow testing without (re)transpiling all packages.
* `@types/jasmine` as development dependency (_in root package only_).
* `@babel/plugin-syntax-dynamic-import` as development dependency (_in root package, for testing_).

### Changed

Expand Down
3 changes: 2 additions & 1 deletion babel.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
],
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-private-methods",
"@babel/plugin-transform-class-static-block"
"@babel/plugin-transform-class-static-block",
"@babel/plugin-syntax-dynamic-import"
]
}
2,487 changes: 1,536 additions & 951 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@babel/plugin-proposal-decorators": "^7.24.7",
"@babel/plugin-proposal-private-methods": "^7.18.6",
"@babel/plugin-transform-class-static-block": "^7.24.7",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/preset-env": "^7.25.4",
"@babel/runtime": "^7.25.6",
"@lerna-lite/changed": "^3.9.1",
Expand Down
1 change: 1 addition & 0 deletions packages/config/rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export default createConfig({
external: [
'@aedart/contracts/config',
'@aedart/contracts/container',
'@aedart/contracts/core',
'@aedart/contracts/support',
'@aedart/contracts/support/arrays',
'@aedart/contracts/support/container',
Expand Down
23 changes: 23 additions & 0 deletions packages/config/src/exceptions/ResolveError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { ResolveException } from "@aedart/contracts/config";
import { configureCustomError } from "@aedart/support/exceptions";

/**
* Configuration Resolve Error
*
* @see {ResolveException}
*/
export default class ResolveError extends Error implements ResolveException
{
/**
* Create new Resolve Error instance
*
* @param {string} [message]
* @param {ErrorOptions} [options]
*/
constructor(message?: string, options?: ErrorOptions)
{
super(message, options);

configureCustomError(this);
}
}
24 changes: 24 additions & 0 deletions packages/config/src/exceptions/UnsupportedSourceError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { UnsupportedSourceException } from "@aedart/contracts/config";
import { configureCustomError } from "@aedart/support/exceptions";
import ResolveError from "./ResolveError";

/**
* Unsupported Configuration Source Error
*
* @see {UnsupportedSourceException}
*/
export default class UnsupportedSourceError extends ResolveError implements UnsupportedSourceException
{
/**
* Create new Unsupported Source Error instance
*
* @param {string} [message]
* @param {ErrorOptions} [options]
*/
constructor(message?: string, options?: ErrorOptions)
{
super(message, options);

configureCustomError(this);
}
}
6 changes: 6 additions & 0 deletions packages/config/src/exceptions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import ResolveError from './ResolveError';
import UnsupportedSourceError from "./UnsupportedSourceError";
export {
ResolveError,
UnsupportedSourceError
}
4 changes: 3 additions & 1 deletion packages/config/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ export {
Repository
}

export * from './providers/index';
export * from './resolvers/index';
export * from './providers/index';
export * from './exceptions/index';
7 changes: 5 additions & 2 deletions packages/config/src/providers/ConfigServiceProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { CONFIG } from "@aedart/contracts/config";
import { CONFIG, CONFIG_RESOLVER } from "@aedart/contracts/config";
import { ServiceProvider } from "@aedart/support/services";
import Repository from '../Repository';
import Resolver from '../resolvers/DefaultResolver';

/**
* Configuration Service Provider
Expand All @@ -14,6 +15,8 @@ export default class ConfigServiceProvider extends ServiceProvider
{
this.app
.singleton(CONFIG, Repository)
.alias(CONFIG, 'config');
.alias(CONFIG, 'config')

.singleton(CONFIG_RESOLVER, Resolver);
}
}
60 changes: 60 additions & 0 deletions packages/config/src/resolvers/DefaultResolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { Items, Resolver, Source, ResolveCallback } from "@aedart/contracts/config";
import { isPromise, isCallable } from "@aedart/support/reflections";
import { getErrorMessage } from "@aedart/support/exceptions";
import ResolveError from "../exceptions/ResolveError";
import UnsupportedSourceError from "../exceptions/UnsupportedSourceError";

/**
* Default Configuration Resolver
*
* @see {Resolver}
*/
export default class DefaultResolver implements Resolver
{
/**
* Resolves configuration items from the given source
*
* @param {Source} source
*
* @returns {Promise<Items>}
*
* @throws {ResolveException}
*/
public async resolve(source: Source): Promise<Items>
{
if (isPromise(source)) {
return this.resolveItems(source as Promise<Items>);
}

if (typeof source === 'object') {
return this.resolveItems(new Promise<Items>((resolve) => {
resolve(source as Items);
}));
}

if (isCallable(source)) {
return this.resolveItems((source as ResolveCallback)());
}

throw new UnsupportedSourceError('Unable to resolve configuration items from source', { cause: { source: source } });
}

/**
* Resolves configuration items from the given promise
*
* @param {Promise<Items>} promise
*
* @returns {Promise<Items>}
*
* @protected
*/
protected async resolveItems(promise: Promise<Items>): Promise<Items>
{
try {
return await promise;
} catch (e) {
const reason: string = getErrorMessage(e);
throw new ResolveError(`Unable to resolve configuration items: ${reason}`, { cause: { source: promise } });
}
}
}
4 changes: 4 additions & 0 deletions packages/config/src/resolvers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import DefaultResolver from "./DefaultResolver";
export {
DefaultResolver
}
18 changes: 18 additions & 0 deletions packages/contracts/src/config/Resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Items, Source } from './types';

/**
* Configuration Resolver
*/
export default interface Resolver
{
/**
* Resolves configuration items from the given source
*
* @param {Source} source
*
* @returns {Promise<Items>}
*
* @throws {ResolveException}
*/
resolve(source: Source): Promise<Items>;
}
10 changes: 10 additions & 0 deletions packages/contracts/src/config/exceptions/ResolveException.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Throwable } from "@aedart/contracts/support/exceptions";

/**
* Configuration Resolve Exception
*
* To be thrown whenever configuration [items]{@link import('@aedart/contracts/config').Items}
* cannot be resolved.
*/
export default interface ResolveException extends Throwable
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import ResolveException from './ResolveException';

/**
* Unsupported Configuration Source Exception
*
* To be thrown whenever a [Resolver]{@link import('@aedart/contracts/config').Resolver} is unable to
* resolve configuration items, due to an unsupported [source]{@link import('@aedart/contracts/config').Source}.
*/
export default interface UnsupportedSourceException extends ResolveException
{}
6 changes: 6 additions & 0 deletions packages/contracts/src/config/exceptions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import ResolveException from "./ResolveException";
import UnsupportedSourceException from "./UnsupportedSourceException";
export {
type ResolveException,
type UnsupportedSourceException
}
12 changes: 11 additions & 1 deletion packages/contracts/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,19 @@
*/
export const CONFIG: unique symbol = Symbol('@aedart/contracts/config');

/**
* Configuration Resolver identifier
*
* @type {Symbol}
*/
export const CONFIG_RESOLVER: unique symbol = Symbol('@aedart/contracts/config/resolver');

import Repository from './Repository';
import Resolver from './Resolver';
export {
type Repository
type Repository,
type Resolver
}

export * from './exceptions/index';
export type * from './types';
17 changes: 12 additions & 5 deletions packages/contracts/src/config/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
/**
* Configuration Items
*
* A general type for describing a key-value store containing configuration items
* for an application or component.
* A key-value store containing configuration items for an application, its service, and or component.
*/
export type Items = Record<PropertyKey, any>; /* eslint-disable-line @typescript-eslint/no-explicit-any */
export type Items = Record<PropertyKey, any>; /* eslint-disable-line @typescript-eslint/no-explicit-any */

/**
* Callback that is responsible for resolving configuration {@link Items}.
*/
export type ResolveCallback = () => Promise<Items>;

/**
* A source that ultimately must resolve into configuration {@link Items}.
*/
export type Source = Items | Promise<Items> | ResolveCallback;
25 changes: 24 additions & 1 deletion packages/contracts/src/core/Application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import {
Callback,
ClassMethodReference
} from "@aedart/contracts";
import { Source } from "@aedart/contracts/config";
import { Container } from "@aedart/contracts/container";
import { CallbackWrapper } from "@aedart/contracts/support";
import {
Registrar,
ServiceProvider,
ServiceProviderConstructor,
BootException,
} from "@aedart/contracts/support/services";
import {
ConfiguratorCallback,
Expand All @@ -30,6 +30,29 @@ import BootstrapperConstructor from "./BootstrapperConstructor";
*/
export default interface Application extends Container
{
/**
* Prepare this application using the given configuration source.
*
* **Note**: _Method is shorthand for [configuring]{@link configure} this application
* using a default {@link Configurator} with a configuration {@link Source}:_
*
* @example
* const source = {}; // not shown here...
*
* // Prepare using configuration source
* app.prepare(source);
*
* // Above is equivalent to:
* app.configure( (configurator) => configurator.with(...) )
*
* @param {Source} using
*
* @returns {this}
*
* @see {Configurator.with}
*/
prepare(using: Source): this;

/**
* Configure this application using given configurator
*
Expand Down
10 changes: 6 additions & 4 deletions packages/contracts/src/core/configuration/Configurator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
IdentifierAliasTuple,
IdentifierInstanceTuple,
} from "@aedart/contracts/container";
import { Items } from "@aedart/contracts/config";
import { Source } from "@aedart/contracts/config";
import BootstrapperConstructor from "../BootstrapperConstructor";
import Application from '../Application';

Expand All @@ -29,15 +29,17 @@ export default interface Configurator
for(app: Application): this;

/**
* Add configuration items for the application
* Set the source of the application's configuration items
*
* **Caution**: _Method overwrites eventual previous set configuration source!_
*
* @see {import('@aedart/contracts/config').Repository}
*
* @param {Items} items
* @param {Source} source A source that resolves into configuration [items]{@link import('@aedart/contracts/config').Items}
*
* @return {this}
*/
with(items: Items): this;
with(source: Source): this;

/**
* Add "core" bindings to be registered
Expand Down
9 changes: 9 additions & 0 deletions packages/contracts/src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ export const APPLICATION: symbol = CORE;
*/
export const APP_ENV: unique symbol = Symbol('APP_ENV');

/**
* Configuration Source identifier
*
* @type {Symbol}
*
* @see {import('@aedart/contracts/config').Source}
*/
export const CONFIG_SOURCE: unique symbol = Symbol('@aedart/contracts/core/config/source');

import Application from "./Application";
import Bootstrapper from "./Bootstrapper";
import BootstrapperConstructor from "./BootstrapperConstructor";
Expand Down
Loading

0 comments on commit 3eaec16

Please sign in to comment.