Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

koatty_container

Package Overview
Dependencies
Maintainers
1
Versions
63
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

koatty_container - npm Package Compare versions

Comparing version 1.6.11 to 1.6.12

.commitlintrc.js

14

CHANGELOG.md

@@ -5,14 +5,6 @@ # Changelog

### [1.6.10](https://github.com/koatty/koatty_container/compare/v1.6.8...v1.6.10) (2021-12-08)
### [1.6.12](https://github.com/koatty/koatty_container/compare/v1.6.12-1...v1.6.12) (2021-12-18)
### [1.6.8](https://github.com/koatty/koatty_container/compare/v1.6.7...v1.6.8) (2021-11-23)
### [1.6.12-1](https://github.com/koatty/koatty_container/compare/v1.6.12-0...v1.6.12-1) (2021-12-18)
### [1.5.2](https://github.com/koatty/koatty_container/compare/v1.4.4...v1.5.2) (2021-07-09)
### [1.4.4](https://github.com/koatty/koatty_container/compare/v1.4.3...v1.4.4) (2021-07-07)
### [1.4.3](https://github.com/koatty/koatty_container/compare/v1.4.2...v1.4.3) (2021-06-29)
### [1.4.2](https://github.com/koatty/koatty_container/compare/v1.4.1...v1.4.2) (2021-06-29)
### [1.4.1](https://github.com/koatty/koatty_container/compare/v1.3.12...v1.4.1) (2021-06-28)
### [1.6.12-0](https://github.com/koatty/koatty_container/compare/v1.6.10...v1.6.12-0) (2021-12-17)

@@ -0,12 +1,473 @@

/*!
* @Author: richen
* @Date: 2021-12-18 12:13:33
* @License: BSD (3-Clause)
* @Copyright (c) - <richenlin(at)gmail.com>
* @HomePage: https://koatty.org/
*/
/**
* Executed after specifying the PointCut method.
*
* @export
* @param {(string | Function)} aopName
* @returns {MethodDecorator}
*/
export declare function After(aopName: string | Function): MethodDecorator;
/**
* Executed after execution of each method of the specified PointCut class.
*
* @export
* @param {string} aopName
* @returns {Function}
*/
export declare function AfterEach(aopName?: string | Function): ClassDecorator;
/**
* defined AOP type
*
* @export
* @enum {number}
*/
export declare enum AOPType {
"Before" = "Before",
"BeforeEach" = "BeforeEach",
"After" = "After",
"AfterEach" = "AfterEach"
}
/**
* Base Application
*
* @export
* @interface Application
*/
export declare interface Application {
env: string;
options: any;
use: (fn: Function) => any;
config: (name: string, type?: string) => any;
on(event: string, callback: () => void): any;
once(event: string, callback: () => void): any;
/**
* app metadata
*
* @memberof Application
*/
getMetaData: (key: string) => unknown;
setMetaData: (key: string, value: unknown) => Map<string, unknown>;
}
/**
* Indicates that an decorated class is a "aspect".
*
* @export
* @param {string} [identifier]
* @returns {ClassDecorator}
*/
export declare function Aspect(identifier?: string): ClassDecorator;
/**
* Marks a constructor method as to be autowired by Koatty"s dependency injection facilities.
*
* @export
* @param {string} [identifier]
* @param {ComponentType} [type]
* @param {any[]} [constructArgs]
* @param {boolean} [isDelay=false]
* @returns {PropertyDecorator}
*/
export declare function Autowired(identifier?: string, type?: ComponentType, constructArgs?: any[], isDelay?: boolean): PropertyDecorator;
/**
* Executed before specifying the PointCut method.
*
* @export
* @param {(string | Function)} aopName
* @returns {MethodDecorator}
*/
export declare function Before(aopName: string | Function): MethodDecorator;
/**
* Executed after execution of each method of the specified PointCut class.
*
* @export
* @param {string} [aopName]
* @returns {Function}
*/
export declare function BeforeEach(aopName?: string | Function): ClassDecorator;
export declare type ComponentType = 'COMPONENT' | 'CONTROLLER' | 'MIDDLEWARE' | 'SERVICE';
/**
* Indicates that an decorated configuration as a property.
*
* @export
* @param {string} identifier configuration key
* @param {string} [type] configuration type
* @returns {PropertyDecorator}
*/
export declare const Config: typeof Value;
/**
* IOC Container
*
* @export
* @class Container
* @implements {IContainer}
*/
export declare class Container implements IContainer {
/**
*
*
* @static
* @returns
* @memberof ValidateUtil
*/
static getInstance(): Container;
/**
* creates an instance of Container.
* @param {*} app
* @memberof Container
*/
/**
* set app
*
* @param {Koatty} app
* @returns
* @memberof Container
*/
setApp(app: Application): void;
/**
* get app
*
* @returns
* @memberof Container
*/
getApp(): Application;
/**
* registering an instance of a class to an IOC container.
*
* @template T
* @param {T} target
* @param {ObjectDefinitionOptions} [options]
* @returns {T}
* @memberof Container
*/
reg<T>(target: T, options?: ObjectDefinitionOptions): T;
reg<T>(identifier: string, target: T, options?: ObjectDefinitionOptions): T;
/**
* get instance from IOC container.
*
* @param {string} identifier
* @param {ComponentType} [type="SERVICE"]
* @param {any[]} [args=[]]
* @returns {*}
* @memberof Container
*/
get(identifier: string, type?: ComponentType, args?: any[]): any;
/**
* get class from IOC container by identifier.
*
* @param {string} identifier
* @param {ComponentType} [type="SERVICE"]
* @returns {Function}
* @memberof Container
*/
getClass(identifier: string, type?: ComponentType): Function;
/**
* get instance from IOC container by class.
*
* @template T
* @param {T} target
* @param {any[]} [args=[]]
* @returns {T}
* @memberof Container
*/
getInsByClass<T>(target: T, args?: any[]): T;
/**
* get metadata from class
*
* @static
* @param {(string | symbol)} metadataKey
* @param {(Function | object)} target
* @param {(string | symbol)} [propertyKey]
* @returns
* @memberof Injectable
*/
getMetadataMap(metadataKey: string | symbol, target: Function | Object, propertyKey?: string | symbol): any;
/**
* get identifier from class
*
* @param {Function | Object} target
* @returns
* @memberof Container
*/
getIdentifier(target: Function | Object): string;
/**
* get component type from class
*
* @param {Function} target
* @returns
* @memberof Container
*/
getType(target: Function | Object): any;
/**
* save class to Container
*
* @param {ComponentType} type
* @param {Function} module
* @param {string} identifier
* @memberof Container
*/
saveClass(type: ComponentType, module: Function, identifier: string): void;
/**
* get all class from Container
*
* @param {ComponentType} type
* @returns
* @memberof Container
*/
listClass(type: ComponentType): any[];
/**
* save meta data to class or property
*
* @param {string} type
* @param {(string | symbol)} decoratorNameKey
* @param {*} data
* @param {(Function | object)} target
* @param {string} [propertyName]
* @memberof Container
*/
saveClassMetadata(type: string, decoratorNameKey: string | symbol, data: any, target: Function | Object, propertyName?: string): void;
/**
* attach data to class or property
*
* @param {string} type
* @param {(string | symbol)} decoratorNameKey
* @param {*} data
* @param {(Function | object)} target
* @param {string} [propertyName]
* @memberof Container
*/
attachClassMetadata(type: string, decoratorNameKey: string | symbol, data: any, target: Function | Object, propertyName?: string): void;
/**
* get single data from class or property
*
* @param {string} type
* @param {(string | symbol)} decoratorNameKey
* @param {(Function | object)} target
* @param {string} [propertyName]
* @returns
* @memberof Container
*/
getClassMetadata(type: string, decoratorNameKey: string | symbol, target: Function | Object, propertyName?: string): any;
/**
* save property data to class
*
* @param {(string | symbol)} decoratorNameKey
* @param {*} data
* @param {(Function | object)} target
* @param {(string | symbol)} propertyName
* @memberof Container
*/
savePropertyData(decoratorNameKey: string | symbol, data: any, target: Function | Object, propertyName: string | symbol): void;
/**
* attach property data to class
*
* @param {(string | symbol)} decoratorNameKey
* @param {*} data
* @param {(Function | object)} target
* @param {(string | symbol)} propertyName
* @memberof Container
*/
attachPropertyData(decoratorNameKey: string | symbol, data: any, target: Function | Object, propertyName: string | symbol): void;
/**
* get property data from class
*
* @param {(string | symbol)} decoratorNameKey
* @param {(Function | object)} target
* @param {(string | symbol)} propertyName
* @returns
* @memberof Container
*/
getPropertyData(decoratorNameKey: string | symbol, target: Function | Object, propertyName: string | symbol): any;
/**
* list property data from class
*
* @param {(string | symbol)} decoratorNameKey
* @param {(Function | object)} target
* @returns
* @memberof Container
*/
listPropertyData(decoratorNameKey: string | symbol, target: Function | Object): any;
}
/**
* Base Context.
*
* @export
* @interface Context
* @extends {Koa.Context}
*/
export declare interface Context {
/**
* Replace ctx.throw
*
* @type {(status: number, message?: string)}
* @type {(message: string, code?: number, status?: HttpStatusCode)}
* @memberof Context
*/
throw(status: number, message?: string): never;
throw(message: string, code?: number, status?: any): never;
/**
* context metadata
*
* @memberof Context
*/
getMetaData: (key: string) => unknown;
setMetaData: (key: string, value: unknown) => Map<string, unknown>;
}
/**
* Find all methods on a given ES6 class
*
* @param {*} target
* @param {boolean} isSelfProperties
* @returns {string[]}
*/
export declare function getMethodNames(target: any, isSelfProperties?: boolean): string[];
/**
* Find all property on a given ES6 class
*
* @export
* @param {*} target
* @param {boolean} isSelfProperties
* @returns {string[]}
*/
export declare function getPropertyNames(target: any, isSelfProperties?: boolean): string[];
/**
* Aspect interface
*
* @export
* @interface IAspect
*/
export declare interface IAspect {
app: any;
run: (...args: any[]) => Promise<any>;
}
/**
* Container interface
*
* @export
* @interface IContainer
*/
export declare interface IContainer {
setApp(app: Application): void;
reg<T>(target: T, options?: ObjectDefinitionOptions): T;
reg<T>(identifier: string, target: T, options?: ObjectDefinitionOptions): T;
get(identifier: string, type?: ComponentType, args?: any[]): any;
getClass(identifier: string, type?: ComponentType): Function;
getInsByClass<T>(target: T, args?: any[]): T;
saveClass(type: ComponentType, module: Function, identifier: string): void;
listClass(type: ComponentType): any[];
getIdentifier(target: Function): string;
getType(target: Function): string;
getMetadataMap(metadataKey: string | symbol, target: any, propertyKey?: string | symbol): any;
saveClassMetadata(type: string, decoratorNameKey: string | symbol, data: any, target: Function | Object, propertyName?: string): void;
attachClassMetadata(type: string, decoratorNameKey: string | symbol, data: any, target: Function | Object, propertyName?: string): void;
getClassMetadata(type: string, decoratorNameKey: string | symbol, target: Function | Object, propertyName?: string): any;
savePropertyData(decoratorNameKey: string | symbol, data: any, target: Function | Object, propertyName: string | symbol): void;
attachPropertyData(decoratorNameKey: string | symbol, data: any, target: Function | Object, propertyName: string | symbol): void;
getPropertyData(decoratorNameKey: string | symbol, target: Function | Object, propertyName: string | symbol): any;
listPropertyData(decoratorNameKey: string | symbol, target: Function | Object): any[];
}
export declare const IOCContainer: Container;
/**
* BeanFactory Object interface
*
* @export
* @interface ObjectDefinitionOptions
*/
export declare interface ObjectDefinitionOptions {
isAsync?: boolean;
initMethod?: string;
destroyMethod?: string;
scope?: Scope;
type: ComponentType;
args: any[];
}
/**
* get metadata value of a metadata key on the prototype chain of an object and property
* @param metadataKey metadata key
* @param target the target of metadataKey
*/
export declare function RecursiveGetMetadata(metadataKey: any, target: any, propertyKey?: string | symbol): any[];
/**
*
*
* @export
* @interface ReflectResult
*/
export declare interface ReflectResult {
[key: string]: TagPropsMetadata[];
}
/**
* @ author: richen
* @ copyright: Copyright (c) - <richenlin(at)gmail.com>
* @ license: MIT
* @ version: 2020-05-10 11:31:10
* @ version: 2020-05-10 11:41:01
*/
export * from "./Container";
export * from "./IContainer";
export * from "./Util";
export { Autowired } from "./Autowired";
export { Config, Value } from "./Value";
export { AOPType, IAspect, Aspect, Before, BeforeEach, After, AfterEach } from "./AOP";
export declare type Scope = 'Singleton' | 'Prototype';
/**
*
*
* @export
* @interface TagClsMetadata
*/
export declare interface TagClsMetadata {
id: string;
originName: string;
}
export declare const TAGGED_AOP = "TAGGED_AOP";
export declare const TAGGED_ARGS = "TAGGED_ARGS";
export declare const TAGGED_CLS = "INJECT_TAGGED_CLS";
export declare const TAGGED_METHOD = "INJECT_TAGGED_METHOD";
export declare const TAGGED_PROP = "INJECT_TAGGED_PROP";
/**
*
*
* @export
* @interface TagPropsMetadata
*/
export declare interface TagPropsMetadata {
key: string | number | symbol;
value: any;
}
/**
* Indicates that an decorated configuration as a property.
*
* @export
* @param {string} identifier configuration key
* @param {string} [type] configuration type
* @returns {PropertyDecorator}
*/
export declare function Value(key?: string, type?: string): PropertyDecorator;
export { }

@@ -1,26 +0,1084 @@

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AfterEach = exports.After = exports.BeforeEach = exports.Before = exports.Aspect = exports.AOPType = exports.Value = exports.Config = exports.Autowired = void 0;
const tslib_1 = require("tslib");
/*!
* @Author: richen
* @Date: 2021-12-18 12:13:22
* @License: BSD (3-Clause)
* @Copyright (c) - <richenlin(at)gmail.com>
* @HomePage: https://koatty.org/
*/
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
require('reflect-metadata');
var helper = require('koatty_lib');
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return Object.freeze(n);
}
var helper__namespace = /*#__PURE__*/_interopNamespace(helper);
// used to store class properties aop
const TAGGED_AOP = 'TAGGED_AOP';
// used to store class properties args
const TAGGED_ARGS = 'TAGGED_ARGS';
// used to store class to be injected
const TAGGED_CLS = 'INJECT_TAGGED_CLS';
// used to store class properties tags
const TAGGED_PROP = 'INJECT_TAGGED_PROP';
// used to store class method to be injected
const TAGGED_METHOD = 'INJECT_TAGGED_METHOD';
/*
* @Author: richen
* @Date: 2020-12-17 20:04:07
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-12-18 02:58:21
* @License: BSD (3-Clause)
* @Copyright (c) - <richenlin(at)gmail.com>
*/
// get property of an object
const functionPrototype = Object.getPrototypeOf(Function);
// https://tc39.github.io/ecma262/#sec-ordinarygetprototypeof
function ordinaryGetPrototypeOf(obj) {
const proto = Object.getPrototypeOf(obj);
if (typeof obj !== "function" || obj === functionPrototype) {
return proto;
}
// TypeScript doesn't set __proto__ in ES5, as it's non-standard.
// Try to determine the superclass constructor. Compatible implementations
// must either set __proto__ on a subclass constructor to the superclass constructor,
// or ensure each class has a valid `constructor` property on its prototype that
// points back to the constructor.
// If this is not the same as Function.[[Prototype]], then this is definitely inherited.
// This is the case when in ES6 or when using __proto__ in a compatible browser.
if (proto !== functionPrototype) {
return proto;
}
// If the super prototype is Object.prototype, null, or undefined, then we cannot determine the heritage.
const prototype = obj.prototype;
const prototypeProto = prototype && Object.getPrototypeOf(prototype);
// tslint:disable-next-line: triple-equals
if (prototypeProto == undefined || prototypeProto === Object.prototype) {
return proto;
}
// If the constructor was not a function, then we cannot determine the heritage.
const constructor = prototypeProto.constructor;
if (typeof constructor !== "function") {
return proto;
}
// If we have some kind of self-reference, then we cannot determine the heritage.
if (constructor === obj) {
return proto;
}
// we have a pretty good guess at the heritage.
return constructor;
}
/**
* get metadata value of a metadata key on the prototype chain of an object and property
* @param metadataKey metadata key
* @param target the target of metadataKey
*/
function RecursiveGetMetadata(metadataKey, target, propertyKey) {
var _a;
// get metadata value of a metadata key on the prototype
// let metadata = Reflect.getOwnMetadata(metadataKey, target, propertyKey);
const IOCContainer = Container.getInstance();
const metadata = (_a = IOCContainer.listPropertyData(metadataKey, target)) !== null && _a !== void 0 ? _a : {};
// get metadata value of a metadata key on the prototype chain
let parent = ordinaryGetPrototypeOf(target);
while (parent !== null) {
// metadata = Reflect.getOwnMetadata(metadataKey, parent, propertyKey);
const metadata = IOCContainer.listPropertyData(metadataKey, parent);
if (metadata) {
for (const n in metadata) {
if (!metadata.hasOwnProperty(n)) {
metadata[n] = metadata[n];
}
}
}
parent = ordinaryGetPrototypeOf(parent);
}
return metadata;
}
/**
* Find all methods on a given ES6 class
*
* @param {*} target
* @param {boolean} isSelfProperties
* @returns {string[]}
*/
function getMethodNames(target, isSelfProperties = false) {
const result = [];
const enumerableOwnKeys = Object.getOwnPropertyNames(target.prototype);
if (!isSelfProperties) {
// searching prototype chain for methods
let parent = ordinaryGetPrototypeOf(target);
while (helper__namespace.isClass(parent) && parent.constructor) {
const allOwnKeysOnPrototype = Object.getOwnPropertyNames(parent.prototype);
// get methods from es6 class
allOwnKeysOnPrototype.forEach((k) => {
if (!result.includes(k) && helper__namespace.isFunction(parent.prototype[k])) {
result.push(k);
}
});
parent = ordinaryGetPrototypeOf(parent);
}
}
// leave out those methods on Object's prototype
enumerableOwnKeys.forEach((k) => {
if (!result.includes(k) && helper__namespace.isFunction(target.prototype[k])) {
result.push(k);
}
});
return result;
}
/**
* Find all property on a given ES6 class
*
* @export
* @param {*} target
* @param {boolean} isSelfProperties
* @returns {string[]}
*/
function getPropertyNames(target, isSelfProperties = false) {
const result = [];
const enumerableOwnKeys = Object.getOwnPropertyNames(target);
if (!isSelfProperties) {
// searching prototype chain for methods
let parent = ordinaryGetPrototypeOf(target);
while (helper__namespace.isClass(parent) && parent.constructor) {
const allOwnKeysOnPrototype = Object.getOwnPropertyNames(parent);
// get methods from es6 class
allOwnKeysOnPrototype.forEach((k) => {
if (!result.includes(k) && !helper__namespace.isFunction(parent.prototype[k])) {
result.push(k);
}
});
parent = ordinaryGetPrototypeOf(parent);
}
}
// leave out those methods on Object's prototype
enumerableOwnKeys.forEach((k) => {
if (!result.includes(k) && !helper__namespace.isFunction(target.prototype[k])) {
result.push(k);
}
});
return result;
}
/**
* @ author: richen
* @ copyright: Copyright (c) - <richenlin(at)gmail.com>
* @ license: MIT
* @ version: 2020-05-10 11:31:10
* @ version: 2020-05-10 11:03:58
*/
(0, tslib_1.__exportStar)(require("./Container"), exports);
(0, tslib_1.__exportStar)(require("./IContainer"), exports);
(0, tslib_1.__exportStar)(require("./Util"), exports);
var Autowired_1 = require("./Autowired");
Object.defineProperty(exports, "Autowired", { enumerable: true, get: function () { return Autowired_1.Autowired; } });
var Value_1 = require("./Value");
Object.defineProperty(exports, "Config", { enumerable: true, get: function () { return Value_1.Config; } });
Object.defineProperty(exports, "Value", { enumerable: true, get: function () { return Value_1.Value; } });
var AOP_1 = require("./AOP");
Object.defineProperty(exports, "AOPType", { enumerable: true, get: function () { return AOP_1.AOPType; } });
Object.defineProperty(exports, "Aspect", { enumerable: true, get: function () { return AOP_1.Aspect; } });
Object.defineProperty(exports, "Before", { enumerable: true, get: function () { return AOP_1.Before; } });
Object.defineProperty(exports, "BeforeEach", { enumerable: true, get: function () { return AOP_1.BeforeEach; } });
Object.defineProperty(exports, "After", { enumerable: true, get: function () { return AOP_1.After; } });
Object.defineProperty(exports, "AfterEach", { enumerable: true, get: function () { return AOP_1.AfterEach; } });
//# sourceMappingURL=index.js.map
/**
* Marks a constructor method as to be autowired by Koatty"s dependency injection facilities.
*
* @export
* @param {string} [identifier]
* @param {ComponentType} [type]
* @param {any[]} [constructArgs]
* @param {boolean} [isDelay=false]
* @returns {PropertyDecorator}
*/
function Autowired(identifier, type, constructArgs, isDelay = false) {
return (target, propertyKey) => {
const designType = Reflect.getMetadata("design:type", target, propertyKey);
if (!identifier) {
if (!designType || designType.name === "Object") {
// throw Error("identifier cannot be empty when circular dependency exists");
identifier = helper__namespace.camelCase(propertyKey, true);
}
else {
identifier = designType.name;
}
}
if (!identifier) {
throw Error("identifier cannot be empty when circular dependency exists");
}
if (type === undefined) {
if (identifier.indexOf("Controller") > -1) {
type = "CONTROLLER";
}
else if (identifier.indexOf("Middleware") > -1) {
type = "MIDDLEWARE";
}
else if (identifier.indexOf("Service") > -1) {
type = "SERVICE";
}
else {
type = "COMPONENT";
}
}
//Cannot rely on injection controller
if (type === "CONTROLLER") {
throw new Error(`Controller cannot be injection!`);
}
//Cannot rely on injection middleware
// if (type === "MIDDLEWARE") {
// throw new Error(`Middleware ${identifier ?? ""} cannot be injected!`);
// }
if (!designType || designType.name === "Object") {
isDelay = true;
}
IOCContainer.savePropertyData(TAGGED_PROP, {
type,
identifier,
delay: isDelay,
args: constructArgs !== null && constructArgs !== void 0 ? constructArgs : []
}, target, propertyKey);
};
}
/**
* inject autowired class
*
* @export
* @param {*} target
* @param {*} instance
* @param {Container} container
* @param {boolean} [isLazy=false]
*/
function injectAutowired(target, instance, container, isLazy = false) {
var _a;
const metaData = RecursiveGetMetadata(TAGGED_PROP, target);
// tslint:disable-next-line: forin
for (const metaKey in metaData) {
let dep;
const { type, identifier, delay, args } = metaData[metaKey] || { type: "", identifier: "", delay: false, args: [] };
if (type && identifier) {
if (!delay || isLazy) {
dep = container.get(identifier, type, args);
if (dep) {
// logger.Debug(`Register inject ${target.name} properties key: ${metaKey} => value: ${JSON.stringify(metaData[metaKey])}`);
Reflect.defineProperty(instance, metaKey, {
enumerable: true,
configurable: false,
writable: true,
value: dep
});
}
else {
throw new Error(`Component ${(_a = metaData[metaKey].identifier) !== null && _a !== void 0 ? _a : ""} not found. It's autowired in class ${target.name}`);
}
}
else {
// Delay loading solves the problem of cyclic dependency
const app = container.getApp();
// tslint:disable-next-line: no-unused-expression
if (app && app.once) {
app.once("appReady", () => {
// lazy inject autowired
injectAutowired(target, instance, container, true);
});
}
}
}
}
}
/*
* @Author: richen
* @Date: 2020-12-18 10:37:03
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-11-23 10:53:10
* @License: BSD (3-Clause)
* @Copyright (c) - <richenlin(at)gmail.com>
*/
/**
* Indicates that an decorated configuration as a property.
*
* @export
* @param {string} identifier configuration key
* @param {string} [type] configuration type
* @returns {PropertyDecorator}
*/
function Value(key, type) {
return (target, propertyKey) => {
// ###############
// PropertyDecorator is executed before ClassDecorator, resulting in that componentType cannot be obtained here...
// ###############
// const componentType = IOCContainer.getType(target);
// if (componentType === "MIDDLEWARE") {
// throw Error("Value decorator cannot be used in the middleware class. Please use app.config() to get the configuration.");
// }
// identifier = identifier || helper.camelCase(propertyKey, { pascalCase: true });
key = key || propertyKey;
IOCContainer.savePropertyData(TAGGED_ARGS, `${key !== null && key !== void 0 ? key : ""}|${type || "config"}`, target, propertyKey);
};
}
/**
* Indicates that an decorated configuration as a property.
*
* @export
* @param {string} identifier configuration key
* @param {string} [type] configuration type
* @returns {PropertyDecorator}
*/
const Config = Value;
/**
*
*
* @export
* @param {*} target
* @param {*} instance
* @param {Container} container
*/
function injectValue(target, instance, container) {
// const componentType = IOCContainer.getType(target);
// if (componentType === "MIDDLEWARE") {
// throw Error("Value decorator cannot be used in the middleware class. Please use app.config() to get the configuration.");
// }
const app = container.getApp();
if (!app || !app.config) {
return;
}
const metaData = RecursiveGetMetadata(TAGGED_ARGS, target);
// tslint:disable-next-line: forin
for (const metaKey in metaData) {
// logger.Debug(`Register inject ${IOCContainer.getIdentifier(target)} config key: ${metaKey} => value: ${metaData[metaKey]}`);
const propKeys = metaData[metaKey].split("|");
const [propKey, type] = propKeys;
Reflect.defineProperty(instance, metaKey, {
enumerable: true,
configurable: false,
writable: true,
value: app.config(propKey, type)
});
}
}
/**
* @ author: richen
* @ copyright: Copyright (c) - <richenlin(at)gmail.com>
* @ license: BSD (3-Clause)
* @ version: 2020-07-06 11:19:30
*/
/**
* defined AOP type
*
* @export
* @enum {number}
*/
exports.AOPType = void 0;
(function (AOPType) {
AOPType["Before"] = "Before";
AOPType["BeforeEach"] = "BeforeEach";
AOPType["After"] = "After";
AOPType["AfterEach"] = "AfterEach";
})(exports.AOPType || (exports.AOPType = {}));
/**
* Indicates that an decorated class is a "aspect".
*
* @export
* @param {string} [identifier]
* @returns {ClassDecorator}
*/
function Aspect(identifier) {
return (target) => {
identifier = identifier || IOCContainer.getIdentifier(target);
if (!identifier.endsWith("Aspect")) {
throw Error("Aspect class names must use a suffix `Aspect`.");
}
const oldMethod = Reflect.get(target.prototype, "run");
if (!oldMethod) {
throw Error("The aspect class must implement the `run` method.");
}
IOCContainer.saveClass("COMPONENT", target, identifier);
};
}
/**
* Executed before specifying the PointCut method.
*
* @export
* @param {(string | Function)} aopName
* @returns {MethodDecorator}
*/
function Before(aopName) {
return (target, methodName, descriptor) => {
if (!aopName) {
throw Error("AopName is required.");
}
// const { value, configurable, enumerable } = descriptor;
// descriptor = {
// configurable,
// enumerable,
// writable: true,
// async value(...props: any[]) {
// await executeAspect(aopName, props);
// // tslint:disable-next-line: no-invalid-this
// return value.apply(this, props);
// },
// };
// return descriptor;
IOCContainer.saveClassMetadata(TAGGED_CLS, TAGGED_AOP, {
type: exports.AOPType.Before,
name: aopName,
method: methodName,
}, target);
};
}
/**
* Executed after execution of each method of the specified PointCut class.
*
* @export
* @param {string} [aopName]
* @returns {Function}
*/
function BeforeEach(aopName) {
return (target) => {
IOCContainer.saveClassMetadata(TAGGED_CLS, TAGGED_AOP, {
type: exports.AOPType.BeforeEach,
name: aopName
}, target);
};
}
/**
* Executed after specifying the PointCut method.
*
* @export
* @param {(string | Function)} aopName
* @returns {MethodDecorator}
*/
function After(aopName) {
return (target, methodName, descriptor) => {
if (!aopName) {
throw Error("AopName is required.");
}
// const { value, configurable, enumerable } = descriptor;
// descriptor = {
// configurable,
// enumerable,
// writable: true,
// async value(...props: any[]) {
// // tslint:disable-next-line: no-invalid-this
// const res = await value.apply(this, props);
// await executeAspect(aopName, props);
// return res;
// }
// };
// return descriptor;
IOCContainer.saveClassMetadata(TAGGED_CLS, TAGGED_AOP, {
type: exports.AOPType.After,
name: aopName,
method: methodName,
}, target);
};
}
/**
* Executed after execution of each method of the specified PointCut class.
*
* @export
* @param {string} aopName
* @returns {Function}
*/
function AfterEach(aopName) {
return (target) => {
IOCContainer.saveClassMetadata(TAGGED_CLS, TAGGED_AOP, {
type: exports.AOPType.AfterEach,
name: aopName
}, target);
};
}
/**
* Execute aspect
*
* @param {(string | Function)} aopName
* @param {any[]} props
* @returns {*}
*/
async function executeAspect(aopName, props) {
// tslint:disable-next-line: one-variable-per-declaration
let aspect; //, name = "";
if (helper__namespace.isClass(aopName)) {
// tslint:disable-next-line: no-invalid-this
aspect = IOCContainer.getInsByClass(aopName);
// name = IOCContainer.getIdentifier(<Function>aopName) || (<Function>aopName).name || "";
}
else {
// tslint:disable-next-line: no-invalid-this
aspect = IOCContainer.get(aopName, "COMPONENT");
// name = <string>aopName;
}
if (aspect && helper__namespace.isFunction(aspect.run)) {
// logger.Info(`Execute the aspect ${name}`);
// tslint:disable-next-line: no-invalid-this
await aspect.run(props);
}
return Promise.resolve();
}
/**
* inject AOP
*
* @export
* @param {*} target
* @param {*} instance
* @param {Container} container
*/
function injectAOP(target, instance, container) {
// If the class has defined the default AOP method, @BeforeEach and @AfterEach will not take effect
const allMethods = getMethodNames(target);
const methods = allMethods.filter((m) => !["constructor", "init", "__before", "__after"].includes(m) && target.prototype.hasOwnProperty(m));
if (allMethods.includes("__before") || allMethods.includes("__after")) {
// inject default AOP method
injectDefaultAOP(target, instance, methods);
}
else {
const classMetaData = IOCContainer.getClassMetadata(TAGGED_CLS, TAGGED_AOP, target);
if (classMetaData) {
const { type, name, method } = classMetaData;
if (name && [exports.AOPType.Before, exports.AOPType.BeforeEach, exports.AOPType.After, exports.AOPType.AfterEach].includes(type)) {
methods.forEach((element) => {
if ([exports.AOPType.Before, exports.AOPType.After].includes(type) && method === element) {
// Logger.Debug(`Register inject AOP ${target.name} method: ${element} => ${type}`);
defineAOPProperty(target, element, name, type);
}
else {
// Logger.Debug(`Register inject AOP ${target.name} method: ${element} => ${type}`);
defineAOPProperty(target, element, name, type);
}
});
}
}
}
}
// /**
// * Determine whether the class contains the default AOP method
// *
// * @param {*} target
// * @returns {*} {boolean}
// */
// function hasDefaultAOP(target: any): boolean {
// const allMethods = getMethodNames(target).filter((m: string) =>
// !["constructor", "init"].includes(m)
// );
// // class contains the default AOP method
// if (allMethods.includes("__before") || allMethods.includes("__after")) {
// return true;
// }
// return false;
// }
/**
* inject default AOP
*
* @export
* @param {*} target
* @param {*} instance
* @param {string[]} methods
* @returns {*}
*/
function injectDefaultAOP(target, instance, methods) {
// class methods
// const methods = getMethodNames(target, true).filter((m: string) =>
// !["constructor", "init", "__before", "__after"].includes(m)
// );
// logger.Warn(`The ${target.name} class has a default AOP method, @BeforeEach and @AfterEach maybe not take effect`);
methods.forEach((element) => {
if (helper__namespace.isFunction(instance.__before)) {
// logger.Debug(`Register inject default AOP ${target.name} method: ${element} => __before`);
defineAOPProperty(target, element, "__before", exports.AOPType.BeforeEach);
}
if (helper__namespace.isFunction(instance.__after)) {
// logger.Debug(`Register inject default AOP ${target.name} method: ${element} => __after`);
defineAOPProperty(target, element, "__after", exports.AOPType.AfterEach);
}
});
}
/**
* Dynamically add methods for target class types
*
* @param {Function} classes
* @param {string} protoName
* @param {(string | Function)} aopName
*/
function defineAOPProperty(classes, protoName, aopName, type) {
const oldMethod = Reflect.get(classes.prototype, protoName);
if (oldMethod) {
Reflect.defineProperty(classes.prototype, protoName, {
writable: true,
async value(...props) {
if ([exports.AOPType.Before, exports.AOPType.BeforeEach].includes(type)) {
if (aopName === "__before") {
// logger.Info(`Execute the aspect ${classes.name}.__before`);
// tslint:disable-next-line: no-invalid-this
await Reflect.apply(this.__before, this, props);
}
else {
await executeAspect(aopName, props);
}
// tslint:disable-next-line: no-invalid-this
return Reflect.apply(oldMethod, this, props);
}
else {
// tslint:disable-next-line: no-invalid-this
const res = await Reflect.apply(oldMethod, this, props);
if (aopName === "__after") {
// logger.Info(`Execute the aspect ${classes.name}.__after`);
// tslint:disable-next-line: no-invalid-this
await Reflect.apply(this.__after, this, props);
}
else {
await executeAspect(aopName, props);
}
return res;
}
}
});
}
else {
throw Error(`${protoName} method does not exist.`);
}
}
/**
* @ author: richen
* @ copyright: Copyright (c) - <richenlin(at)gmail.com>
* @ license: MIT
* @ version: 2020-05-10 11:00:05
*/
/**
* IOC Container
*
* @export
* @class Container
* @implements {IContainer}
*/
class Container {
/**
* creates an instance of Container.
* @param {*} app
* @memberof Container
*/
constructor() {
this.classMap = new Map();
this.instanceMap = new WeakMap();
this.metadataMap = new WeakMap();
}
/**
*
*
* @static
* @returns
* @memberof ValidateUtil
*/
static getInstance() {
return this.instance || (this.instance = new Container());
}
/**
* set app
*
* @param {Koatty} app
* @returns
* @memberof Container
*/
setApp(app) {
this.app = app;
}
/**
* get app
*
* @returns
* @memberof Container
*/
getApp() {
return this.app;
}
reg(identifier, target, options) {
if (helper__namespace.isClass(identifier) || helper__namespace.isFunction(identifier)) {
options = target;
target = identifier;
identifier = this.getIdentifier(target);
}
if (!helper__namespace.isClass(target)) {
return target;
}
let instance = this.instanceMap.get(target);
if (!instance) {
options = {
isAsync: false,
initMethod: "constructor",
destroyMethod: "distructor",
scope: "Singleton",
type: "COMPONENT",
args: [],
...options
};
options.args = options.args.length ? options.args : [];
// inject options once
Reflect.defineProperty(target.prototype, "_options", {
enumerable: false,
configurable: false,
writable: true,
value: options
});
// define app as getter
const app = this.app;
Reflect.defineProperty(target.prototype, "app", {
get() {
return app;
},
configurable: false,
enumerable: false
});
// inject autowired
injectAutowired(target, target.prototype, this);
// inject value
injectValue(target, target.prototype, this);
// inject AOP
injectAOP(target, target.prototype);
const ref = this.getClass(options.type, identifier);
if (!ref) {
this.saveClass(options.type, target, identifier);
}
// instantiation
instance = Reflect.construct(target, options.args);
if (options.scope === "Singleton") {
instance = Object.seal(instance);
}
// registration
this.instanceMap.set(target, instance);
}
return instance;
}
/**
* get instance from IOC container.
*
* @param {string} identifier
* @param {ComponentType} [type="SERVICE"]
* @param {any[]} [args=[]]
* @returns {*}
* @memberof Container
*/
get(identifier, type = "SERVICE", args = []) {
const target = this.getClass(identifier, type);
if (!target) {
return null;
}
// get instance from the Container
const instance = this.instanceMap.get(target);
// require Prototype instance
if (args.length > 0) {
// instantiation
return Reflect.construct(target, args);
}
else {
return instance;
}
}
/**
* get class from IOC container by identifier.
*
* @param {string} identifier
* @param {ComponentType} [type="SERVICE"]
* @returns {Function}
* @memberof Container
*/
getClass(identifier, type = "SERVICE") {
return this.classMap.get(`${type}:${identifier}`);
}
/**
* get instance from IOC container by class.
*
* @template T
* @param {T} target
* @param {any[]} [args=[]]
* @returns {T}
* @memberof Container
*/
getInsByClass(target, args = []) {
if (!helper__namespace.isClass(target)) {
return null;
}
// get instance from the Container
const instance = this.instanceMap.get(target);
// require Prototype instance
if (args.length > 0) {
// instantiation
return Reflect.construct(target, args);
}
else {
return instance;
}
}
/**
* get metadata from class
*
* @static
* @param {(string | symbol)} metadataKey
* @param {(Function | object)} target
* @param {(string | symbol)} [propertyKey]
* @returns
* @memberof Injectable
*/
getMetadataMap(metadataKey, target, propertyKey) {
// filter Object.create(null)
if (typeof target === "object" && target.constructor) {
target = target.constructor;
}
if (!this.metadataMap.has(target)) {
this.metadataMap.set(target, new Map());
}
if (propertyKey) {
// for property or method
const key = `${helper__namespace.toString(metadataKey)}:${helper__namespace.toString(propertyKey)}`;
const map = this.metadataMap.get(target);
if (!map.has(key)) {
map.set(key, new Map());
}
return map.get(key);
}
else {
// for class
const map = this.metadataMap.get(target);
if (!map.has(metadataKey)) {
map.set(metadataKey, new Map());
}
return map.get(metadataKey);
}
}
/**
* get identifier from class
*
* @param {Function | Object} target
* @returns
* @memberof Container
*/
getIdentifier(target) {
var _a, _b, _c;
let name = "";
if (helper__namespace.isFunction(target)) {
const metaData = Reflect.getOwnMetadata(TAGGED_CLS, target);
if (metaData) {
name = (_a = metaData.id) !== null && _a !== void 0 ? _a : "";
}
else {
name = (_b = target.name) !== null && _b !== void 0 ? _b : "";
}
}
else {
name = target.constructor ? ((_c = target.constructor.name) !== null && _c !== void 0 ? _c : "") : "";
}
return name;
}
/**
* get component type from class
*
* @param {Function} target
* @returns
* @memberof Container
*/
getType(target) {
var _a, _b;
const metaData = Reflect.getOwnMetadata(TAGGED_CLS, target);
if (metaData) {
return metaData.type;
}
else {
let name = (_a = target.name) !== null && _a !== void 0 ? _a : "";
name = name || (target.constructor ? ((_b = target.constructor.name) !== null && _b !== void 0 ? _b : "") : "");
if (~name.indexOf("Controller")) {
return "CONTROLLER";
}
else if (~name.indexOf("Middleware")) {
return "MIDDLEWARE";
}
else if (~name.indexOf("Service")) {
return "SERVICE";
}
else {
return "COMPONENT";
}
}
}
/**
* save class to Container
*
* @param {ComponentType} type
* @param {Function} module
* @param {string} identifier
* @memberof Container
*/
saveClass(type, module, identifier) {
Reflect.defineMetadata(TAGGED_CLS, { id: identifier, type }, module);
const key = `${type}:${identifier}`;
if (!this.classMap.has(key)) {
this.classMap.set(key, module);
}
}
/**
* get all class from Container
*
* @param {ComponentType} type
* @returns
* @memberof Container
*/
listClass(type) {
const modules = [];
this.classMap.forEach((v, k) => {
if (k.startsWith(type)) {
modules.push({
id: k,
target: v
});
}
});
return modules;
}
/**
* save meta data to class or property
*
* @param {string} type
* @param {(string | symbol)} decoratorNameKey
* @param {*} data
* @param {(Function | object)} target
* @param {string} [propertyName]
* @memberof Container
*/
saveClassMetadata(type, decoratorNameKey, data, target, propertyName) {
if (propertyName) {
const originMap = this.getMetadataMap(type, target, propertyName);
originMap.set(decoratorNameKey, data);
}
else {
const originMap = this.getMetadataMap(type, target);
originMap.set(decoratorNameKey, data);
}
}
/**
* attach data to class or property
*
* @param {string} type
* @param {(string | symbol)} decoratorNameKey
* @param {*} data
* @param {(Function | object)} target
* @param {string} [propertyName]
* @memberof Container
*/
attachClassMetadata(type, decoratorNameKey, data, target, propertyName) {
let originMap;
if (propertyName) {
originMap = this.getMetadataMap(type, target, propertyName);
}
else {
originMap = this.getMetadataMap(type, target);
}
if (!originMap.has(decoratorNameKey)) {
originMap.set(decoratorNameKey, []);
}
originMap.get(decoratorNameKey).push(data);
}
/**
* get single data from class or property
*
* @param {string} type
* @param {(string | symbol)} decoratorNameKey
* @param {(Function | object)} target
* @param {string} [propertyName]
* @returns
* @memberof Container
*/
getClassMetadata(type, decoratorNameKey, target, propertyName) {
if (propertyName) {
const originMap = this.getMetadataMap(type, target, propertyName);
return originMap.get(decoratorNameKey);
}
else {
const originMap = this.getMetadataMap(type, target);
return originMap.get(decoratorNameKey);
}
}
/**
* save property data to class
*
* @param {(string | symbol)} decoratorNameKey
* @param {*} data
* @param {(Function | object)} target
* @param {(string | symbol)} propertyName
* @memberof Container
*/
savePropertyData(decoratorNameKey, data, target, propertyName) {
const originMap = this.getMetadataMap(decoratorNameKey, target);
originMap.set(propertyName, data);
}
/**
* attach property data to class
*
* @param {(string | symbol)} decoratorNameKey
* @param {*} data
* @param {(Function | object)} target
* @param {(string | symbol)} propertyName
* @memberof Container
*/
attachPropertyData(decoratorNameKey, data, target, propertyName) {
const originMap = this.getMetadataMap(decoratorNameKey, target);
if (!originMap.has(propertyName)) {
originMap.set(propertyName, []);
}
originMap.get(propertyName).push(data);
}
/**
* get property data from class
*
* @param {(string | symbol)} decoratorNameKey
* @param {(Function | object)} target
* @param {(string | symbol)} propertyName
* @returns
* @memberof Container
*/
getPropertyData(decoratorNameKey, target, propertyName) {
const originMap = this.getMetadataMap(decoratorNameKey, target);
return originMap.get(propertyName);
}
/**
* list property data from class
*
* @param {(string | symbol)} decoratorNameKey
* @param {(Function | object)} target
* @returns
* @memberof Container
*/
listPropertyData(decoratorNameKey, target) {
const originMap = this.getMetadataMap(decoratorNameKey, target);
const datas = {};
for (const [key, value] of originMap) {
datas[key] = value;
}
return datas;
}
}
// export Singleton
const IOCContainer = Container.getInstance();
exports.After = After;
exports.AfterEach = AfterEach;
exports.Aspect = Aspect;
exports.Autowired = Autowired;
exports.Before = Before;
exports.BeforeEach = BeforeEach;
exports.Config = Config;
exports.Container = Container;
exports.IOCContainer = IOCContainer;
exports.RecursiveGetMetadata = RecursiveGetMetadata;
exports.TAGGED_AOP = TAGGED_AOP;
exports.TAGGED_ARGS = TAGGED_ARGS;
exports.TAGGED_CLS = TAGGED_CLS;
exports.TAGGED_METHOD = TAGGED_METHOD;
exports.TAGGED_PROP = TAGGED_PROP;
exports.Value = Value;
exports.getMethodNames = getMethodNames;
exports.getPropertyNames = getPropertyNames;

@@ -28,4 +28,2 @@ /**

'ts-jest': {
// 是否使用 babel 配置来转译
babelConfig: true,
// 编译 Typescript 所依赖的配置

@@ -32,0 +30,0 @@ tsconfig: '<rootDir>/tsconfig.json',

{
"name": "koatty_container",
"version": "1.6.11",
"version": "1.6.12",
"description": "IOC Container for Koatty.",
"scripts": {
"build": "del-cli --force dist && tsc",
"build": "npm run build:js && npm run build:dts && npm run build:doc && npm run build:cp",
"build:cp": "node scripts/postBuild && copyfiles package.json LICENSE README.md dist/",
"build:js": "del-cli --force dist && npx rollup -c",
"build:doc": "del-cli --force docs/api && npx api-documenter markdown --input temp --output docs/api",
"build:dts": "del-cli --force lib && npx tsc && npx api-extractor run --local --verbose && del-cli --force lib",
"eslint": "eslint --ext .ts,.js ./",
"prepublishOnly": "npm test && npm run build",
"release": "npm run prepublishOnly && standard-version",
"prerelease": "npm test && npm run build",
"release": "standard-version",
"release:pre": "npm run release -- --prerelease",
"release:major": "npm run release -- --release-as major",
"release:minor": "npm run release -- --release-as minor",
"pub": "git push --follow-tags origin && npm publish",
"pub:pre": "git push --follow-tags origin && npm publish --tag prerelease",
"test": "npm run eslint && jest --passWithNoTests",

@@ -44,8 +53,5 @@ "test:cov": "jest --collectCoverage --detectOpenHandles",

"devDependencies": {
"@babel/core": "^7.x.x",
"@babel/plugin-proposal-decorators": "^7.x.x",
"@babel/preset-env": "^7.x.x",
"@babel/preset-typescript": "^7.x.x",
"@commitlint/cli": "^12.x.x",
"@commitlint/config-conventional": "^15.x.x",
"@microsoft/api-documenter": "^7.x.x",
"@microsoft/api-extractor": "^7.x.x",
"@rollup/plugin-json": "^4.x.x",
"@types/jest": "^27.x.x",

@@ -56,3 +62,6 @@ "@types/koa": "^2.x.x",

"@typescript-eslint/parser": "^5.x.x",
"commitlint": "^15.x.x",
"commitlint-config-gitmoji": "^2.x.x",
"conventional-changelog-cli": "^2.x.x",
"copyfiles": "^2.x.x",
"del-cli": "^4.x.x",

@@ -64,2 +73,4 @@ "eslint": "^8.x.x",

"jest-html-reporters": "^2.x.x",
"rollup": "^2.x.x",
"rollup-plugin-typescript2": "^0.x.x",
"standard-version": "^9.x.x",

@@ -80,2 +91,2 @@ "ts-jest": "^27.x.x",

}
}
}

@@ -14,5 +14,6 @@ {

"declaration": true /* Generates corresponding '.d.ts' file. */,
"declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
"sourceMap": true /* Generates corresponding '.map' file. */,
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "./dist/" /* Redirect output structure to the directory. */,
"outDir": "./lib" /* Redirect output structure to the directory. */,
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */

@@ -25,3 +26,3 @@ "removeComments": false /* Do not emit comments to output. */,

/* Strict Type-Checking Options */
"stripInternal": true /* Enable all strict type-checking options. */,
"stripInternal": true, /* Enable all strict type-checking options. */
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,

@@ -53,2 +54,3 @@ // "strictNullChecks": true, /* Enable strict null checks. */

"esModuleInterop": true,
"useUnknownInCatchVariables": false, /* Default catch clause variables as unknown instead of any. */
/* Source Map Options */

@@ -55,0 +57,0 @@ // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc