@plumier/core
Advanced tools
Comparing version 1.0.2-canary.kp2a4t.0 to 1.0.2
@@ -6,4 +6,21 @@ # Change Log | ||
## [1.0.2](https://github.com/plumier/plumier/compare/v1.0.0...v1.0.2) (2021-06-06) | ||
### Bug Fixes | ||
* Fix array relation should not accessible (read/write) on first class entity ([#956](https://github.com/plumier/plumier/issues/956)) ([58058a5](https://github.com/plumier/plumier/commit/58058a54861447d04cedfd585d60687eb3d4e1d4)) | ||
* Fix authorization evaluation issue on undefined value should be skipped ([#945](https://github.com/plumier/plumier/issues/945)) ([7e9acd3](https://github.com/plumier/plumier/commit/7e9acd330032f7f829ef738668768daf4c379566)) | ||
* Fix response authorization error when no ID provided on response ([#948](https://github.com/plumier/plumier/issues/948)) ([2b5429e](https://github.com/plumier/plumier/commit/2b5429ef30f9cfb3843fb07c5af271dd3223b14c)) | ||
* Fix Swagger urls not visible on route analysis ([#935](https://github.com/plumier/plumier/issues/935)) ([e858197](https://github.com/plumier/plumier/commit/e8581971087ddde0d3642aaa930ac36eafc9bc26)) | ||
* Give proper error message when action return type array, but got non array ([#934](https://github.com/plumier/plumier/issues/934)) ([05cd377](https://github.com/plumier/plumier/commit/05cd377823e789e1c18c3902cad69798f196549e)) | ||
* Give proper error message when found cross reference entity issue ([#933](https://github.com/plumier/plumier/issues/933)) ([0d09d22](https://github.com/plumier/plumier/commit/0d09d22589b751f54c0c4bd03de9f0581334b2ff)) | ||
* Unable to apply write authorization on relation property ([#941](https://github.com/plumier/plumier/issues/941)) ([39ff2b6](https://github.com/plumier/plumier/commit/39ff2b638d9cc5895b1368ef5e419e28c142b359)) | ||
## 1.0.1 (2021-05-18) | ||
**Note:** Version bump only for package @plumier/core |
@@ -86,2 +86,3 @@ "use strict"; | ||
async authorize(ctx) { | ||
var _a, _b; | ||
for (const Auth of this.policies.reverse()) { | ||
@@ -92,3 +93,3 @@ const authPolicy = new Auth(); | ||
const authorize = await authPolicy.authorize(ctx); | ||
log.debug("%s by %s", authorize ? "AUTHORIZED" : "FORBIDDEN", authPolicy.friendlyName()); | ||
log.debug("%s -> %s.%s by %s", authorize ? "AUTHORIZED" : "FORBIDDEN", (_b = (_a = ctx.metadata.current.parent) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : "", ctx.metadata.current.name, authPolicy.friendlyName()); | ||
if (authorize) | ||
@@ -185,4 +186,3 @@ return true; | ||
const entity = ctx.metadata.current.parent; | ||
const meta = reflect_1.reflect(entity); | ||
const prop = meta.properties.find(p => p.decorators.some((x) => x.kind === "plumier-meta:entity-id")); | ||
const prop = common_1.entityHelper.getIdProp(entity); | ||
if (!prop) | ||
@@ -328,3 +328,18 @@ throw new Error(`Entity ${entity.name} doesn't have primary ID information required for entity policy`); | ||
return []; | ||
else if (Array.isArray(meta.type)) { | ||
// skip check on GET method | ||
if (ctx.info.ctx.method === "GET") | ||
return []; | ||
const decorators = meta.decorators.filter(createDecoratorFilter(x => x.access === "write")); | ||
if (decorators.length > 0) { | ||
const info = createContext(ctx, value, meta); | ||
const allowed = await executeAuthorizer(decorators, info); | ||
if (!allowed) | ||
return [ctx.path.join(".")]; | ||
} | ||
// if the property is a relation property just skip checking, since we allow set relation using ID | ||
const isRelation = meta.decorators.some((x) => x.kind === "plumier-meta:relation"); | ||
if (isRelation) | ||
return []; | ||
// loop through property of type array | ||
if (Array.isArray(meta.type)) { | ||
const newMeta = Object.assign(Object.assign({}, meta), { type: meta.type[0] }); | ||
@@ -338,3 +353,4 @@ const result = []; | ||
} | ||
else if (common_1.isCustomClass(meta.type)) { | ||
// loop through custom class properties | ||
if (common_1.isCustomClass(meta.type)) { | ||
const classMeta = reflect_1.reflect(meta.type); | ||
@@ -344,14 +360,4 @@ const values = classMeta.properties.map(x => value[x.name]); | ||
} | ||
else { | ||
// skip check on GET method | ||
if (ctx.info.ctx.method === "GET") | ||
return []; | ||
const decorators = meta.decorators.filter(createDecoratorFilter(x => x.access === "write")); | ||
// if no decorator then just allow, follow route authorization | ||
if (decorators.length === 0) | ||
return []; | ||
const info = createContext(ctx, value, meta); | ||
const allowed = await executeAuthorizer(decorators, info); | ||
return allowed ? [] : [ctx.path.join(".")]; | ||
} | ||
// everything when fine then just return [] | ||
return []; | ||
} | ||
@@ -362,6 +368,2 @@ async function checkParameters(meta, value, ctx) { | ||
const prop = meta[i]; | ||
// if the property is a relation property just skip checking, since we allow set relation using ID | ||
const isRelation = prop.decorators.some((x) => x.kind === "plumier-meta:relation"); | ||
if (isRelation) | ||
continue; | ||
const issues = await checkParameter(prop, value[i], Object.assign(Object.assign({}, ctx), { path: ctx.path.concat(prop.name) })); | ||
@@ -415,4 +417,2 @@ result.push(...issues); | ||
var _a; | ||
if (raw === undefined || raw === null) | ||
return undefined; | ||
if (node.kind === "Array") { | ||
@@ -433,2 +433,4 @@ const result = []; | ||
const value = raw[prop.name]; | ||
if (value === null || value === undefined) | ||
continue; | ||
const authorized = await getAuthorize(prop.authorizer, Object.assign(Object.assign({}, ctx), { value, parentValue: raw, metadata: Object.assign(Object.assign({}, ctx.metadata), { current: prop.meta }) })); | ||
@@ -435,0 +437,0 @@ if (authorized) { |
import glob from "glob"; | ||
declare type Class<T = any> = new (...args: any[]) => T; | ||
interface ClassWithRoot { | ||
root: string; | ||
type: Class; | ||
} | ||
declare global { | ||
@@ -19,2 +23,7 @@ interface String { | ||
declare function findFilesRecursive(path: string): Promise<string[]>; | ||
declare function appendRoute(...args: string[]): string; | ||
declare function findClassRecursive(path: string | string[] | Class | Class[], option?: { | ||
directoryAsPath?: boolean; | ||
rootDir?: string; | ||
}): Promise<ClassWithRoot[]>; | ||
interface ColumnMeta { | ||
@@ -57,2 +66,2 @@ align?: "left" | "right"; | ||
} | ||
export { ellipsis, toBoolean, getChildValue, Class, hasKeyOf, isCustomClass, entityHelper, findFilesRecursive, memoize, printTable, analyzeModel, AnalysisMessage, globAsync, EntityRelationInfo, OneToManyRelationInfo, ManyToOneRelationInfo }; | ||
export { ellipsis, toBoolean, getChildValue, Class, hasKeyOf, isCustomClass, entityHelper, findFilesRecursive, memoize, printTable, analyzeModel, AnalysisMessage, globAsync, EntityRelationInfo, OneToManyRelationInfo, ManyToOneRelationInfo, appendRoute, findClassRecursive, ClassWithRoot }; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.globAsync = exports.analyzeModel = exports.printTable = exports.memoize = exports.findFilesRecursive = exports.entityHelper = exports.isCustomClass = exports.hasKeyOf = exports.getChildValue = exports.toBoolean = exports.ellipsis = void 0; | ||
exports.findClassRecursive = exports.appendRoute = exports.globAsync = exports.analyzeModel = exports.printTable = exports.memoize = exports.findFilesRecursive = exports.entityHelper = exports.isCustomClass = exports.hasKeyOf = exports.getChildValue = exports.toBoolean = exports.ellipsis = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -10,2 +10,3 @@ const fs_1 = require("fs"); | ||
const types_1 = require("./types"); | ||
const path_1 = require("path"); | ||
const lstatAsync = util_1.promisify(fs_1.lstat); | ||
@@ -99,2 +100,48 @@ const existsAsync = util_1.promisify(fs_1.exists); | ||
exports.findFilesRecursive = findFilesRecursive; | ||
function appendRoute(...args) { | ||
return "/" + args | ||
.filter(x => !!x) | ||
.map(x => x.toLowerCase()) | ||
.map(x => x.startsWith("/") ? x.slice(1) : x) | ||
.map(x => x.endsWith("/") ? x.slice(0, -1) : x) | ||
.filter(x => !!x) | ||
.join("/"); | ||
} | ||
exports.appendRoute = appendRoute; | ||
function getRoot(rootPath, path) { | ||
// directoryAsPath should not working with glob | ||
if (rootPath.indexOf("*") >= 0) | ||
return; | ||
const part = path.slice(rootPath.length).split("/").filter(x => !!x) | ||
.slice(0, -1); | ||
return (part.length === 0) ? undefined : appendRoute(...part); | ||
} | ||
async function findClassRecursive(path, option) { | ||
var _a; | ||
const opt = Object.assign({ rootDir: "", directoryAsPath: false }, option); | ||
if (Array.isArray(path)) { | ||
const result = []; | ||
for (const p of path) { | ||
result.push(...await findClassRecursive(p, opt)); | ||
} | ||
return result; | ||
} | ||
if (typeof path === "string") { | ||
const absPath = path_1.isAbsolute(path) ? path : path_1.join(opt.rootDir, path); | ||
//read all files and get module reflection | ||
const files = await findFilesRecursive(absPath); | ||
const result = []; | ||
for (const file of files) { | ||
const root = !!opt.directoryAsPath ? ((_a = getRoot(absPath, file)) !== null && _a !== void 0 ? _a : "") : ""; | ||
for (const member of reflect_1.default(file).members) { | ||
if (member.kind === "Class") | ||
result.push({ root, type: member.type }); | ||
} | ||
} | ||
return result; | ||
} | ||
else | ||
return [{ root: "", type: path }]; | ||
} | ||
exports.findClassRecursive = findClassRecursive; | ||
function printTable(meta, data, option) { | ||
@@ -101,0 +148,0 @@ const getText = (col, row) => { |
@@ -9,6 +9,6 @@ import { val } from "@plumier/validator"; | ||
export { response } from "./response"; | ||
export { generateRoutes, findClassRecursive, appendRoute, IgnoreDecorator, RouteDecorator, transformController, ControllerTransformOption } from "./route-generator"; | ||
export { generateRoutes, IgnoreDecorator, RouteDecorator, transformController, ControllerTransformOption } from "./route-generator"; | ||
export { analyzeRoutes, printAnalysis } from "./route-analyzer"; | ||
export { router } from "./router"; | ||
export { Class, findFilesRecursive, getChildValue, hasKeyOf, isCustomClass, printTable, toBoolean, ellipsis, analyzeModel, AnalysisMessage, entityHelper, globAsync, EntityRelationInfo, OneToManyRelationInfo, ManyToOneRelationInfo } from "./common"; | ||
export { Class, findFilesRecursive, getChildValue, hasKeyOf, isCustomClass, printTable, toBoolean, ellipsis, analyzeModel, AnalysisMessage, entityHelper, globAsync, EntityRelationInfo, OneToManyRelationInfo, ManyToOneRelationInfo, findClassRecursive, appendRoute, ClassWithRoot } from "./common"; | ||
export { AuthDecoratorImpl, authorize } from "./decorator/authorize"; | ||
@@ -15,0 +15,0 @@ export { ApiDescriptionDecorator, ApiEnumDecorator, ApiFieldNameDecorator, ApiRequiredDecorator, ApiResponseDecorator, ApiTagDecorator, api, ApiReadOnlyDecorator, ApiWriteOnlyDecorator, ApiHideRelationDecorator } from "./decorator/api"; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.entityProvider = exports.middleware = exports.domain = exports.bind = exports.api = exports.authorize = exports.AuthDecoratorImpl = exports.globAsync = exports.entityHelper = exports.analyzeModel = exports.ellipsis = exports.toBoolean = exports.printTable = exports.isCustomClass = exports.hasKeyOf = exports.getChildValue = exports.findFilesRecursive = exports.router = exports.printAnalysis = exports.analyzeRoutes = exports.transformController = exports.appendRoute = exports.findClassRecursive = exports.generateRoutes = exports.response = exports.invoke = exports.ParameterBinderMiddleware = exports.binder = exports.getPolicyInfo = exports.createAuthorizationAnalyzer = exports.analyzeAuthPolicyNameConflict = exports.updateRouteAuthorizationAccess = exports.getRouteAuthorizeDecorators = exports.throwAuthError = exports.createAuthContext = exports.executeAuthorizer = exports.WriteonlyAuthPolicy = exports.ReadonlyAuthPolicy = exports.AuthenticatedAuthPolicy = exports.PublicAuthPolicy = exports.globalPolicies = exports.EntityAuthPolicy = exports.CustomAuthPolicy = exports.Authenticated = exports.Public = exports.PolicyAuthorizer = exports.entityPolicy = exports.authPolicy = exports.checkAuthorize = exports.val = void 0; | ||
exports.entityProvider = exports.middleware = exports.domain = exports.bind = exports.api = exports.authorize = exports.AuthDecoratorImpl = exports.appendRoute = exports.findClassRecursive = exports.globAsync = exports.entityHelper = exports.analyzeModel = exports.ellipsis = exports.toBoolean = exports.printTable = exports.isCustomClass = exports.hasKeyOf = exports.getChildValue = exports.findFilesRecursive = exports.router = exports.printAnalysis = exports.analyzeRoutes = exports.transformController = exports.generateRoutes = exports.response = exports.invoke = exports.ParameterBinderMiddleware = exports.binder = exports.getPolicyInfo = exports.createAuthorizationAnalyzer = exports.analyzeAuthPolicyNameConflict = exports.updateRouteAuthorizationAccess = exports.getRouteAuthorizeDecorators = exports.throwAuthError = exports.createAuthContext = exports.executeAuthorizer = exports.WriteonlyAuthPolicy = exports.ReadonlyAuthPolicy = exports.AuthenticatedAuthPolicy = exports.PublicAuthPolicy = exports.globalPolicies = exports.EntityAuthPolicy = exports.CustomAuthPolicy = exports.Authenticated = exports.Public = exports.PolicyAuthorizer = exports.entityPolicy = exports.authPolicy = exports.checkAuthorize = exports.val = void 0; | ||
exports.MetadataImpl = exports.NestedControllerGeneric = exports.ControllerGeneric = exports.FormFile = exports.DefaultDependencyResolver = exports.errorMessage = exports.ValidationError = exports.RedirectActionResult = exports.MiddlewareUtil = exports.HttpStatusError = exports.DefaultFacility = exports.ActionResult = exports.ValidatorMiddleware = exports.validate = exports.HttpStatus = exports.meta = exports.postSave = exports.preSave = exports.entity = exports.RouteDecoratorImpl = exports.route = exports.responseType = void 0; | ||
@@ -41,4 +41,2 @@ // TypeScript bug https://github.com/microsoft/TypeScript/issues/18877 | ||
Object.defineProperty(exports, "generateRoutes", { enumerable: true, get: function () { return route_generator_1.generateRoutes; } }); | ||
Object.defineProperty(exports, "findClassRecursive", { enumerable: true, get: function () { return route_generator_1.findClassRecursive; } }); | ||
Object.defineProperty(exports, "appendRoute", { enumerable: true, get: function () { return route_generator_1.appendRoute; } }); | ||
Object.defineProperty(exports, "transformController", { enumerable: true, get: function () { return route_generator_1.transformController; } }); | ||
@@ -61,2 +59,4 @@ var route_analyzer_1 = require("./route-analyzer"); | ||
Object.defineProperty(exports, "globAsync", { enumerable: true, get: function () { return common_1.globAsync; } }); | ||
Object.defineProperty(exports, "findClassRecursive", { enumerable: true, get: function () { return common_1.findClassRecursive; } }); | ||
Object.defineProperty(exports, "appendRoute", { enumerable: true, get: function () { return common_1.appendRoute; } }); | ||
var authorize_1 = require("./decorator/authorize"); | ||
@@ -63,0 +63,0 @@ Object.defineProperty(exports, "AuthDecoratorImpl", { enumerable: true, get: function () { return authorize_1.AuthDecoratorImpl; } }); |
@@ -25,10 +25,4 @@ import { Class } from "./common"; | ||
} | ||
interface ClassWithRoot { | ||
root: string; | ||
type: Class; | ||
} | ||
declare function appendRoute(...args: string[]): string; | ||
declare function findClassRecursive(path: string): Promise<ClassWithRoot[]>; | ||
declare function transformController(object: Class, opt: ControllerTransformOption): RouteInfo[]; | ||
declare function generateRoutes(controller: string | string[] | Class[] | Class, option?: Partial<ControllerTransformOption>): Promise<RouteMetadata[]>; | ||
export { generateRoutes, transformController, RouteDecorator, IgnoreDecorator, RootDecorator, appendRoute, findClassRecursive, ControllerTransformOption }; | ||
export { generateRoutes, transformController, RouteDecorator, IgnoreDecorator, RootDecorator, ControllerTransformOption }; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.findClassRecursive = exports.appendRoute = exports.transformController = exports.generateRoutes = void 0; | ||
exports.transformController = exports.generateRoutes = void 0; | ||
const reflect_1 = require("@plumier/reflect"); | ||
const path_1 = require("path"); | ||
const common_1 = require("./common"); | ||
@@ -10,12 +9,2 @@ /* ------------------------------------------------------------------------------- */ | ||
/* ------------------------------------------------------------------------------- */ | ||
function appendRoute(...args) { | ||
return "/" + args | ||
.filter(x => !!x) | ||
.map(x => x.toLowerCase()) | ||
.map(x => x.startsWith("/") ? x.slice(1) : x) | ||
.map(x => x.endsWith("/") ? x.slice(0, -1) : x) | ||
.filter(x => !!x) | ||
.join("/"); | ||
} | ||
exports.appendRoute = appendRoute; | ||
function striveController(name) { | ||
@@ -36,28 +25,5 @@ return name.replace(/controller$/i, ""); | ||
else { | ||
return [{ root: appendRoute(root, striveController(controller.name)), map: {} }]; | ||
return [{ root: common_1.appendRoute(root, striveController(controller.name)), map: {} }]; | ||
} | ||
} | ||
function getRoot(rootPath, path) { | ||
// directoryAsPath should not working with glob | ||
if (rootPath.indexOf("*") >= 0) | ||
return; | ||
const part = path.slice(rootPath.length).split("/").filter(x => !!x) | ||
.slice(0, -1); | ||
return (part.length === 0) ? undefined : appendRoute(...part); | ||
} | ||
async function findClassRecursive(path) { | ||
var _a; | ||
//read all files and get module reflection | ||
const files = await common_1.findFilesRecursive(path); | ||
const result = []; | ||
for (const file of files) { | ||
const root = (_a = getRoot(path, file)) !== null && _a !== void 0 ? _a : ""; | ||
for (const member of reflect_1.reflect(file).members) { | ||
if (member.kind === "Class") | ||
result.push({ root, type: member.type }); | ||
} | ||
} | ||
return result; | ||
} | ||
exports.findClassRecursive = findClassRecursive; | ||
class ParamMapper { | ||
@@ -89,3 +55,3 @@ constructor(map) { | ||
else { | ||
return appendRoute(root, actionDecorator.url || actionName.toLowerCase()); | ||
return common_1.appendRoute(root, actionDecorator.url || actionName.toLowerCase()); | ||
} | ||
@@ -110,3 +76,3 @@ } | ||
group, method: "get", | ||
url: appendRoute(root.root, method.name), | ||
url: common_1.appendRoute(root.root, method.name), | ||
controller, | ||
@@ -142,25 +108,4 @@ action: method, | ||
async function extractController(controller, option) { | ||
if (typeof controller === "string") { | ||
const ctl = path_1.isAbsolute(controller) ? controller : path_1.join(option.rootDir, controller); | ||
const types = await findClassRecursive(ctl); | ||
const result = []; | ||
for (const type of types) { | ||
const ctl = await extractController(type.type, option); | ||
result.push(...ctl.map(x => ({ | ||
root: option.directoryAsPath ? type.root : "", | ||
type: x.type | ||
}))); | ||
} | ||
return result; | ||
} | ||
else if (Array.isArray(controller)) { | ||
const raw = controller; | ||
const controllers = await Promise.all(raw.map(x => extractController(x, option))); | ||
return controllers.flatten(); | ||
} | ||
// common controller | ||
if (isController(controller)) { | ||
return [{ root: "", type: controller }]; | ||
} | ||
return []; | ||
const classes = await common_1.findClassRecursive(controller, option); | ||
return classes.filter(x => isController(x.type)); | ||
} | ||
@@ -172,3 +117,3 @@ async function generateRoutes(controller, option) { | ||
for (const controller of controllers) { | ||
routes.push(...transformController(controller.type, Object.assign(Object.assign({}, opt), { rootPath: appendRoute(controller.root, opt.rootPath) }))); | ||
routes.push(...transformController(controller.type, Object.assign(Object.assign({}, opt), { rootPath: common_1.appendRoute(controller.root, opt.rootPath) }))); | ||
} | ||
@@ -175,0 +120,0 @@ return routes; |
@@ -80,3 +80,3 @@ /// <reference types="node" /> | ||
export interface Facility { | ||
generateRoutes(app: Readonly<PlumierApplication>): Promise<RouteMetadata[]>; | ||
generateRoutes(app: Readonly<PlumierApplication>, routes: RouteMetadata[]): Promise<RouteMetadata[]>; | ||
setup(app: Readonly<PlumierApplication>): void; | ||
@@ -87,3 +87,3 @@ preInitialize(app: Readonly<PlumierApplication>): Promise<void>; | ||
export declare class DefaultFacility implements Facility { | ||
generateRoutes(app: Readonly<PlumierApplication>): Promise<RouteMetadata[]>; | ||
generateRoutes(app: Readonly<PlumierApplication>, routes: RouteMetadata[]): Promise<RouteMetadata[]>; | ||
setup(app: Readonly<PlumierApplication>): void; | ||
@@ -273,2 +273,3 @@ preInitialize(app: Readonly<PlumierApplication>): Promise<void>; | ||
export interface SelectQuery { | ||
includeId?: true; | ||
columns?: any; | ||
@@ -275,0 +276,0 @@ relations?: any; |
@@ -67,3 +67,3 @@ "use strict"; | ||
class DefaultFacility { | ||
async generateRoutes(app) { return []; } | ||
async generateRoutes(app, routes) { return []; } | ||
setup(app) { } | ||
@@ -70,0 +70,0 @@ async preInitialize(app) { } |
{ | ||
"name": "@plumier/core", | ||
"version": "1.0.2-canary.kp2a4t.0+05cd377", | ||
"version": "1.0.2", | ||
"description": "Delightful Node.js Rest Framework", | ||
@@ -23,8 +23,8 @@ "main": "lib/index.js", | ||
"dependencies": { | ||
"@plumier/reflect": "1.0.2-canary.kp2a4t.0+05cd377", | ||
"@plumier/validator": "1.0.2-canary.kp2a4t.0+05cd377", | ||
"@plumier/reflect": "^1.0.2", | ||
"@plumier/validator": "^1.0.2", | ||
"@types/debug": "^4.1.5", | ||
"@types/glob": "^7.1.3", | ||
"chalk": "^4.1.1", | ||
"debug": "^4.3.1", | ||
"debug": "^4.3.2", | ||
"glob": "^7.1.7", | ||
@@ -50,3 +50,3 @@ "path-to-regexp": "^6.2.0", | ||
}, | ||
"gitHead": "05cd377823e789e1c18c3902cad69798f196549e" | ||
"gitHead": "21a5ddc95244c15fc218146daee6a4e521ce3c0f" | ||
} |
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
210627
4727
1
0
+ Added@plumier/reflect@1.1.3(transitive)
+ Added@plumier/validator@1.1.3(transitive)
+ Added@types/acorn@4.0.6(transitive)
+ Added@types/estree@1.0.6(transitive)
+ Added@types/validator@13.12.2(transitive)
+ Addedacorn@8.8.1(transitive)
+ Addedreflect-metadata@0.1.14(transitive)
+ Addedvalidator@13.12.0(transitive)
Updated@plumier/reflect@^1.0.2
Updated@plumier/validator@^1.0.2
Updateddebug@^4.3.2