![Oracle Drags Its Feet in the JavaScript Trademark Dispute](https://cdn.sanity.io/images/cgdhsj6q/production/919c3b22c24f93884c548d60cbb338e819ff2435-1024x1024.webp?w=400&fit=max&auto=format)
Security News
Oracle Drags Its Feet in the JavaScript Trademark Dispute
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
eye-oh-see
Advanced tools
An inversion of control container for JavaScript and TypeScript applications that uses decorators for registration.
EyeOhSee is an inversion of control (IoC) container for TypeScript.
It uses TypeScript attributes and metadata to perform constructor injection without relying on error-prone strings.
To install the npm package in your project run the following command:
npm install --save eye-oh-see
Typings are included in the npm package.
EyeOhSee relies on metadata that the TypeScript compiler generates. The compiler will only generate this metadata if you include the following options in your tsconfig.json
configuration file:
{
"emitDecoratorMetadata": true,
"experimentalDecorators": true
}
Decorate your services and implementations as shown in the features section. For example:
import {InstancePerDependency} from 'eye-oh-see';
export abstract class MyService { ... }
@InstancePerDependency(MyService)
export class ServiceImpl extends MyService { ... }
Then register service interfaces and implementations with the container. For example:
import {Container} from 'eye-oh-see';
import {MyService, ServiceImpl} from './SomeFile';
const container = new Container();
container.register(MyService);
container.register(ServiceImpl);
Then resolve services from the container. For example:
const service = container.resolve(MyService);
Decorate your services and implementations as shown in the features section.
Ensure all your services and implementations are module exports.
Install webpack-env
typings using the following command:
typings install --save --global dt~webpack-env
Register all exported services and implementations from a module/directory using require.context(...)
:
import {Container, hasRegistrationAnnotation} from 'eye-oh-see';
// Module directories
const modules = [
require.context('./my-core-module', true, /^\.\/.*\.tsx?$/),
require.context('./my-ui-module', true, /^\.\/.*\.tsx?$/),
require.context('./my-connectivity-module', true, /^\.\/.*\.tsx?$/)
];
// Container
const container = new Container();
// Registration of module exports in container
modules.forEach(context => {
const moduleObjects = context.keys().map(context);
const moduleExports = flatten(moduleObjects.map(moduleObject => Object.keys(moduleObject).map(k => moduleObject[k])));
const serviceExports = moduleExports.filter(moduleExport => typeof moduleExport === 'function' && hasRegistrationAnnotation(moduleExport));
serviceExports.forEach(serviceExport => container.register(serviceExport));
});
// Resolution from container can happen from here!
TypeScript interfaces are erased and their metadata is not accessible at runtime. This means that all interfaces look the same to EyeOhSee and it is unable to resolve them correctly. We could have added support in EyeOhSee for interfaces using string identifiers to distinguish them; however, we felt this was messy, error-prone and there is an alternative.
This does not work with EyeOhSee:
interface ServiceA {
a(): void;
}
interface ServiceB {
abstract b(): void;
}
@SingleInstance(ServiceA, ServiceB)
class ServiceImpl implements ServiceA, ServiceB {
...
}
This does work:
abstract class ServiceA {
abstract a(): void;
}
abstract class ServiceB {
abstract b(): void;
}
@SingleInstance(ServiceA, ServiceB)
class ServiceImpl implements ServiceA, ServiceB {
...
}
TypeScript supports multiple inheritance of fully abstract classes via the implements
keyword. This allows us to keep the same semantics but avoid using error-prone string identifiers.
@InstancePerDependency()
@SingleInstance()
@InstancePerDependency(BaseClass)
and @SingleInstance(BaseClass)
@SingleInstance(BaseClassA, BaseClassB)
and @InstancePerDependency(BaseClassA, BaseClassB)
@ArrayOf(BaseClass)
@Factory(ReturnType)
@Factory(ParamTypeA, ParamTypeB, ReturnType)
@Disposable()
and @Disposable(instance => instance.disposeMethod()
@UnitOfWork(OwnedType)
@UnitOfWork(ParamTypeA, ParamTypeB, OwnedType)
// This will only be constructed once
@SingleInstance()
class MySingleton { ... }
// This is what consumers resolve
class MyBaseClass { ... }
// This is the implementation
@SingleInstance(MyBaseClass)
class MySingleton extends MyBaseClass { ... }
// This is one of the interfaces that consumers resolve
class MyBaseClassA { ... }
// This is another of the interfaces that consumers resolve
class MyBaseClassB { ... }
// This is the implementation
@SingleInstance(MyBaseClassA, MyBaseClassB)
class MySingleton implements MyBaseClassA, MyBaseClassB { ... }
// This will be constructed for each dependency
@InstancePerDependency()
class MyTransient { ... }
// This is what consumers resolve
class MyBaseClass { ... }
// This is the implementation
@InstancePerDependency(MyBaseClass)
class MyTransient extends MyBaseClass { ... }
// This is one of the interfaces that consumers resolve
class MyBaseClassA { ... }
// This is another of the interfaces that consumers resolve
class MyBaseClassB { ... }
// This is the implementation
@InstancePerDependency(MyBaseClassA, MyBaseClassB)
class MyTransient implements MyBaseClassA, MyBaseClassB { ... }
// This is the service interface
class MyBaseClass { ... }
// This is one implementation
@InstancePerDependency(MyBaseClass)
class MyFirstImpl { ... }
// This is another implementation
// Notice that you can mix resolution strategies of implementations
@SingleInstance(MyBaseClass)
class MySecondImpl { ... }
// This is the consumer
@InstancePerDependency()
class MyConsumer {
constructor(@ArrayOf(MyBaseClass) myThings: MyBaseClass[]) { ... }
...
}
// The service
@InstancePerDependency()
class MyService { ... }
// The consumer
@InstancePerDependency()
class MyConsumer {
constructor(@Factory(MyService) private factory: () => MyService) { ... }
...
public later() {
const myServiceInstance = this.factory();
...
}
}
class ToeDirection {
public static BigToeOnRight = new ToeDirection();
public static BigToeOnLeft = new ToeDirection();
}
// A foot has a toe direction
@InstancePerDependency()
class Foot {
constructor(toeDirection: ToeDirection) { ... }
...
}
// A leg has a foot
@InstancePerDependency()
class Leg {
constructor(foot: Foot) { ... }
...
}
// A robot has two legs
@InstancePerDependency()
class Robot {
private leftLeg: Leg;
private rightLeg: Leg;
// Factory attributes can take multiple parameters but here we only use one
constructor(@Factory(ToeDirection, Leg) legFactory: (toeDirection: ToeDirection) => Leg) {
// We construct each leg specifying how ToeDirection should be resolved by descendants
this.leftLeg = legFactory(ToeDirection.BigToeOnRight);
this.rightLeg = legFactory(ToeDirection.BigToeOnLeft);
}
...
}
// It calls the disposal method when the container it was resolved from is disposed
// It defaults to calling a method called "dispose"
@InstancePerDependency()
@Disposable()
class MyFirstResource {
dispose() {
// Clean up stuff here
}
}
// We can override the method it calls when the container is disposed
@InstancePerDependency()
@Disposable(instance => instance.cleanUp())
class MyFirstResource {
cleanUp() {
// Clean up stuff here
}
}
// Service used inside unit of work
@InstancePerDependency()
@Disposable()
class MyService {
dispose() { ... }
}
// Business aspect with some lifetime that is shorter than that of the application
@InstancePerDependency()
class MyRequest {
constructor(service: MyService)
}
// The application that handles the lifetimes of those business aspects
@InstancePerDependency()
class MyApplication {
...
constructor(@UnitOfWork(MyRequest) private requestFactory: () => IUnitOfWork<MyRequest>) { ... }
...
onStartRequest(id) {
...
this.requests[id] = this.requestFactory();
...
}
onFinishRequest(id) {
...
// Disposes the resolved instance of MyService for the given request
this.requests[id].dispose();
...
}
}
// Service used inside unit of work
@InstancePerDependency()
@Disposable()
class MyService {
// Accepts some configuration
constructor(config: Config) { ... }
dispose() { ... }
}
// Business aspect with some lifetime that is shorter than that of the application
@InstancePerDependency()
class MyRequest {
constructor(service: MyService)
}
// The application that handles the lifetimes of those business aspects
@InstancePerDependency()
class MyApplication {
...
constructor(@UnitOfWork(Config, MyRequest) private requestFactory: (config: Config) => IUnitOfWork<MyRequest>) { ... }
...
onStartRequest(id) {
...
// unit of work created with parameter that will be resolved by MyService instance
this.requests[id] = this.requestFactory(this.config);
...
}
onFinishRequest(id) {
...
// Disposes the resolved instance of MyService for the given request
this.requests[id].dispose();
...
}
}
const RequestScope = 'A Request';
// Service to be available as a singleton within a scope
@InstancePerScope(RequestScope)
class MyService { ... }
// First consumer of the service
@InstancePerDependency()
class MyFirstConsumer {
constructor(private service: MyService) {}
...
}
// Second consumer of the service
@InstancePerDependency()
class MySecondConsumer {
constructor(private service: MyService) {}
...
}
// Representation of a request or unit of work
@InstancePerDependency()
class Request {
constructor(private consumer1: MyFirstConsumer, private consumer2: MySecondConsumer) {}
}
// Application where each request
@InstancePerDependency()
class Application {
// Note: we could also unit a @ScopedUnitOfWork(...) decorator here!
constructor(@ScopedFactory(RequestScope, Request) private requestFactory: () => Request) {}
...
onStartRequest(id) {
...
// request created where request[1].consumer1.service === request[1].consumer2.service
// but request[1].consumer1.service === request[2].consumer1.service
this.requests[id] = this.requestFactory();
...
}
...
}
FAQs
An inversion of control container for JavaScript and TypeScript applications that uses decorators for registration.
We found that eye-oh-see demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
Security News
The Linux Foundation is warning open source developers that compliance with global sanctions is mandatory, highlighting legal risks and restrictions on contributions.
Security News
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.