@thednp/event-listener
Advanced tools
Comparing version 2.0.2 to 2.0.3
/** | ||
* Advanced event listener based on subscribe / publish pattern. | ||
* Type definitions addapted from React 18.2 | ||
* Project: https://react.dev/ | ||
*/ | ||
export type NativeAnimationEvent = AnimationEvent; | ||
export type NativeClipboardEvent = ClipboardEvent; | ||
export type NativeCompositionEvent = CompositionEvent; | ||
export type NativeDragEvent = DragEvent; | ||
export type NativeFocusEvent = FocusEvent; | ||
export type NativeKeyboardEvent = KeyboardEvent; | ||
export type NativeMouseEvent = MouseEvent; | ||
export type NativeTouchEvent = TouchEvent; | ||
export type NativePointerEvent = PointerEvent; | ||
export type NativeTransitionEvent = TransitionEvent; | ||
export type NativeUIEvent = UIEvent; | ||
export type NativeWheelEvent = WheelEvent; | ||
export interface AbstractView { | ||
styleMedia: StyleMedia; | ||
document: Document; | ||
} | ||
export interface BaseEvent<E = unknown, C = unknown, T = unknown> { | ||
nativeEvent: Event & E; | ||
currentTarget: C & EventTarget; | ||
target: T & EventTarget; | ||
bubbles: boolean; | ||
cancelable: boolean; | ||
defaultPrevented: boolean; | ||
eventPhase: number; | ||
isTrusted: boolean; | ||
preventDefault(): void; | ||
isDefaultPrevented(): boolean; | ||
stopPropagation(): void; | ||
isPropagationStopped(): boolean; | ||
persist(): void; | ||
timeStamp: number; | ||
type: string & NativeEventTypes; | ||
} | ||
/** | ||
* currentTarget - a reference to the element on which the event listener is registered. | ||
* | ||
* @see https://www.patterns.dev/posts/classic-design-patterns/#observerpatternjavascript | ||
* @see https://gist.github.com/shystruk/d16c0ee7ac7d194da9644e5d740c8338#file-subpub-js | ||
* @see https://hackernoon.com/do-you-still-register-window-event-listeners-in-each-component-react-in-example-31a4b1f6f1c8 | ||
* target - a reference to the element from which the event was originally dispatched. | ||
* This might be a child element to the element on which the event listener is registered. | ||
* If you thought this should be `EventTarget & T`, see https://github.com/DefinitelyTyped/DefinitelyTyped/issues/11508#issuecomment-256045682 | ||
*/ | ||
export type ListenerObject = Map<EventListener, AddEventListenerOptions | undefined | boolean>; | ||
export type EventsRegistry = Record<string, Map<EventTarget, ListenerObject>>; | ||
export type NativeEvent<T = PossibleEventTarget, E = Event> = BaseEvent<E, EventTarget & T, EventTarget>; | ||
export interface ClipboardEvent<T = Element> extends NativeEvent<T, NativeClipboardEvent> { | ||
clipboardData?: DataTransfer; | ||
} | ||
export interface CompositionEvent<T = Element> extends NativeEvent<T, NativeCompositionEvent> { | ||
data: string; | ||
} | ||
export interface DragEvent<T = Element> extends MouseEvent<T, NativeDragEvent> { | ||
dataTransfer: DataTransfer; | ||
} | ||
export interface PointerEvent<T = Element> extends MouseEvent<T, NativePointerEvent> { | ||
pointerId: number; | ||
pressure: number; | ||
tangentialPressure: number; | ||
tiltX: number; | ||
tiltY: number; | ||
twist: number; | ||
width: number; | ||
height: number; | ||
pointerType: "mouse" | "pen" | "touch"; | ||
isPrimary: boolean; | ||
} | ||
export interface FocusEvent<T = Element, R = Element> extends NativeEvent<T, NativeFocusEvent> { | ||
relatedTarget: (EventTarget & R) | null; | ||
target: EventTarget & T; | ||
} | ||
export type FormControl = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement; | ||
export type FormEvent<T = FormControl> = NativeEvent<T>; | ||
export interface ChangeEvent<T = FormControl> extends FormEvent<T> { | ||
target: EventTarget & T; | ||
} | ||
export type ModifierKey = "Alt" | "AltGraph" | "CapsLock" | "Control" | "Fn" | "FnLock" | "Hyper" | "Meta" | "NumLock" | "ScrollLock" | "Shift" | "Super" | "Symbol" | "SymbolLock"; | ||
export interface KeyboardEvent<T = Element> extends UIEvent<T, NativeKeyboardEvent> { | ||
altKey: boolean; | ||
/** @deprecated */ | ||
charCode: number; | ||
ctrlKey: boolean; | ||
code: string; | ||
/** | ||
* See [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#keys-modifier). for a list of valid (case-sensitive) arguments to this method. | ||
*/ | ||
getModifierState(key: ModifierKey): boolean; | ||
/** | ||
* See the [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#named-key-attribute-values). for possible values | ||
*/ | ||
key: string; | ||
/** @deprecated */ | ||
keyCode: number; | ||
locale: string; | ||
location: number; | ||
metaKey: boolean; | ||
repeat: boolean; | ||
shiftKey: boolean; | ||
/** @deprecated */ | ||
which: number; | ||
} | ||
export interface MouseEvent<T = Element, E = NativeMouseEvent> extends UIEvent<T, E> { | ||
altKey: boolean; | ||
button: number; | ||
buttons: number; | ||
clientX: number; | ||
clientY: number; | ||
ctrlKey: boolean; | ||
/** | ||
* See [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#keys-modifier). for a list of valid (case-sensitive) arguments to this method. | ||
*/ | ||
getModifierState(key: ModifierKey): boolean; | ||
metaKey: boolean; | ||
movementX: number; | ||
movementY: number; | ||
pageX: number; | ||
pageY: number; | ||
relatedTarget: EventTarget | null; | ||
screenX: number; | ||
screenY: number; | ||
shiftKey: boolean; | ||
} | ||
export interface TouchEvent<T = Element> extends UIEvent<T, NativeTouchEvent> { | ||
altKey: boolean; | ||
changedTouches: TouchList; | ||
ctrlKey: boolean; | ||
/** | ||
* See [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#keys-modifier). for a list of valid (case-sensitive) arguments to this method. | ||
*/ | ||
getModifierState(key: ModifierKey): boolean; | ||
metaKey: boolean; | ||
shiftKey: boolean; | ||
targetTouches: TouchList; | ||
touches: TouchList; | ||
} | ||
export interface UIEvent<T = Element, E = NativeUIEvent> extends NativeEvent<T, E> { | ||
detail: number; | ||
view: AbstractView; | ||
} | ||
export interface WheelEvent<T = Element> extends MouseEvent<T, NativeWheelEvent> { | ||
deltaMode: number; | ||
deltaX: number; | ||
deltaY: number; | ||
deltaZ: number; | ||
} | ||
export interface AnimationEvent<T = Element> extends NativeEvent<T, NativeAnimationEvent> { | ||
animationName: string; | ||
elapsedTime: number; | ||
pseudoElement: string; | ||
} | ||
export interface TransitionEvent<T = Element> extends NativeEvent<T, NativeTransitionEvent> { | ||
elapsedTime: number; | ||
propertyName: string; | ||
pseudoElement: string; | ||
} | ||
export type EventHandler<E extends NativeEvent<unknown, unknown>> = (event: E) => void; | ||
export type NativeEventHandler<T = Element> = EventHandler<NativeEvent<T>>; | ||
export type ClipboardEventHandler<T = Element> = EventHandler<ClipboardEvent<T>>; | ||
export type CompositionEventHandler<T = Element> = EventHandler<CompositionEvent<T>>; | ||
export type DragEventHandler<T = Element> = EventHandler<DragEvent<T>>; | ||
export type FocusEventHandler<T = Element> = EventHandler<FocusEvent<T>>; | ||
export type FormEventHandler<T = Element> = EventHandler<FormEvent<T>>; | ||
export type ChangeEventHandler<T = Element> = EventHandler<ChangeEvent<T>>; | ||
export type KeyboardEventHandler<T = Element> = EventHandler<KeyboardEvent<T>>; | ||
export type MouseEventHandler<T = Element> = EventHandler<MouseEvent<T>>; | ||
export type TouchEventHandler<T = Element> = EventHandler<TouchEvent<T>>; | ||
export type PointerEventHandler<T = Element> = EventHandler<PointerEvent<T>>; | ||
export type UIEventHandler<T = Element> = EventHandler<UIEvent<T>>; | ||
export type WheelEventHandler<T = Element> = EventHandler<WheelEvent<T>>; | ||
export type AnimationEventHandler<T = Element> = EventHandler<AnimationEvent<T>>; | ||
export type TransitionEventHandler<T = Element> = EventHandler<TransitionEvent<T>>; | ||
export type SupportedEventObject<T> = NativeEvent<T> | ClipboardEvent<T> | CompositionEvent<T> | DragEvent<T> | FocusEvent<T> | FormEvent<T> | ChangeEvent<T> | KeyboardEvent<T> | MouseEvent<T> | TouchEvent<T> | PointerEvent<T> | UIEvent<T> | WheelEvent<T> | AnimationEvent<T> | TransitionEvent<T>; | ||
export type SupportedEventHandler<T> = NativeEventHandler<T> | ClipboardEventHandler<T> | CompositionEventHandler<T> | DragEventHandler<T> | FocusEventHandler<T> | FormEventHandler<T> | ChangeEventHandler<T> | KeyboardEventHandler<T> | MouseEventHandler<T> | TouchEventHandler<T> | PointerEventHandler<T> | UIEventHandler<T> | WheelEventHandler<T> | AnimationEventHandler<T> | TransitionEventHandler<T>; | ||
export type NativeEventTypes = "DOMContentLoaded" | "DOMMouseScroll" | "abort" | "beforeunload" | "blur" | "change" | "click" | "contextmenu" | "dblclick" | "error" | "focus" | "focusin" | "focusout" | "gesturechange" | "gestureend" | "gesturestart" | "hover" | "keydown" | "keypress" | "keyup" | "load" | "mousedown" | "mouseenter" | "mousein" | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" | "mousewheel" | "move" | "orientationchange" | "pointercancel" | "pointerdown" | "pointerleave" | "pointermove" | "pointerup" | "readystatechange" | "reset" | "resize" | "scroll" | "select" | "selectend" | "selectstart" | "submit" | "touchcancel" | "touchend" | "touchmove" | "touchstart" | "unload"; | ||
export type PossibleEventTarget = EventTarget & (Element | Document | Window); | ||
export type ListenerObject<T = PossibleEventTarget, H = SupportedEventHandler<T>> = Map<H, AddEventListenerOptions | undefined | boolean>; | ||
export type EventsRegistry<T = PossibleEventTarget> = Record<string, Map<T, ListenerObject<T>>>; | ||
export declare const registry: EventsRegistry; | ||
@@ -17,3 +184,3 @@ /** | ||
*/ | ||
export declare const globalListener: (e: Event) => void; | ||
export declare const globalListener: (e: NativeEvent) => void; | ||
/** | ||
@@ -23,3 +190,3 @@ * Register a new listener with its options and attach the `globalListener` | ||
*/ | ||
export declare const addListener: (element: EventTarget, eventType: string, listener: EventListener, options?: AddEventListenerOptions) => void; | ||
export declare const addListener: <T extends PossibleEventTarget>(element: T, eventType: NativeEventTypes, listener: SupportedEventHandler<T>, options?: AddEventListenerOptions) => void; | ||
/** | ||
@@ -30,3 +197,3 @@ * Remove a listener from registry and detach the `globalListener` | ||
*/ | ||
export declare const removeListener: (element: EventTarget, eventType: string, listener: EventListener, options?: AddEventListenerOptions) => void; | ||
export declare const removeListener: <T extends PossibleEventTarget>(element: T, eventType: NativeEventTypes, listener: SupportedEventHandler<PossibleEventTarget>, options?: AddEventListenerOptions) => void; | ||
export declare const on: typeof addListener; | ||
@@ -33,0 +200,0 @@ export declare const off: typeof removeListener; |
{ | ||
"name": "@thednp/event-listener", | ||
"author": "thednp", | ||
"version": "2.0.2", | ||
"version": "2.0.3", | ||
"description": "Modern event listener for efficient web applications based on subscribe-publish pattern.", | ||
@@ -39,25 +39,12 @@ "license": "MIT", | ||
}, | ||
"scripts": { | ||
"pre-test": "npm run clean-coverage", | ||
"test": "npm run pre-test && cypress run", | ||
"clean-coverage": "rimraf coverage .nyc_output", | ||
"cypress": "npm run pre-test && npx cypress open", | ||
"coverage:report": "nyc report --reporter=lcov --reporter=json --reporter=text --reporter=json-summary", | ||
"format": "prettier --write \"src/**/*.ts\"", | ||
"lint:ts": "eslint -c .eslintrc.cjs --ext .ts src", | ||
"fix:ts": "eslint -c .eslintrc.cjs --ext .ts src --fix", | ||
"dts": "dts-bundle-generator --config ./dts.config.ts", | ||
"build": "npm run lint:ts && vite build && npm run docs && npm run dts", | ||
"docs": "ncp dist/event-listener.js docs/event-listener.js && ncp dist/event-listener.js.map docs/event-listener.js.map" | ||
}, | ||
"devDependencies": { | ||
"@bahmutov/cypress-esbuild-preprocessor": "^2.2.0", | ||
"@cypress/code-coverage": "^3.10.8", | ||
"@cypress/code-coverage": "^3.12.0", | ||
"@types/istanbul-lib-instrument": "^1.7.4", | ||
"@typescript-eslint/eslint-plugin": "^5.61.0", | ||
"@typescript-eslint/parser": "^5.61.0", | ||
"cypress": "^12.16.0", | ||
"@typescript-eslint/eslint-plugin": "^5.62.0", | ||
"@typescript-eslint/parser": "^5.62.0", | ||
"cypress": "^12.17.4", | ||
"dts-bundle-generator": "^8.0.1", | ||
"eslint": "^8.44.0", | ||
"eslint-plugin-jsdoc": "^46.4.3", | ||
"eslint": "^8.48.0", | ||
"eslint-plugin-jsdoc": "^46.5.1", | ||
"eslint-plugin-prefer-arrow": "^1.2.3", | ||
@@ -71,5 +58,23 @@ "eslint-plugin-prettier": "^4.2.1", | ||
"rimraf": "^5.0.1", | ||
"typescript": "^5.1.6", | ||
"vite": "^4.3.9" | ||
"typescript": "^5.2.2", | ||
"vite": "^4.4.9" | ||
}, | ||
"packageManager": "pnpm@8.6.12", | ||
"engines": { | ||
"node": ">=16", | ||
"pnpm": ">=8.6.0" | ||
}, | ||
"scripts": { | ||
"pre-test": "pnpm clean-coverage", | ||
"test": "pnpm pre-test && cypress run", | ||
"clean-coverage": "rimraf coverage .nyc_output", | ||
"cypress": "pnpm pre-test && cypress open", | ||
"coverage:report": "nyc report --reporter=lcov --reporter=json --reporter=text --reporter=json-summary", | ||
"format": "prettier --write \"src/**/*.ts\"", | ||
"lint:ts": "eslint -c .eslintrc.cjs --ext .ts src", | ||
"fix:ts": "eslint -c .eslintrc.cjs --ext .ts src --fix", | ||
"dts": "dts-bundle-generator --config ./dts.config.ts", | ||
"build": "pnpm lint:ts && vite build && pnpm dts", | ||
"docs": "ncp dist/event-listener.js docs/event-listener.js && ncp dist/event-listener.js.map docs/event-listener.js.map" | ||
} | ||
} | ||
} |
@@ -8,6 +8,6 @@ ## EventListener | ||
[![jsDeliver](https://img.shields.io/jsdelivr/npm/hw/@thednp/event-listener)](https://www.jsdelivr.com/package/npm/@thednp/event-listener) | ||
[![cypress version](https://img.shields.io/badge/cypress-12.16.0-brightgreen)](https://cypress.io/) | ||
[![typescript version](https://img.shields.io/badge/typescript-5.1.6-brightgreen)](https://www.typescriptlang.org/) | ||
[![eslint version](https://img.shields.io/badge/eslint-8.44.0-brightgreen)](https://github.com/eslint) | ||
[![vite version](https://img.shields.io/badge/vite-4.3.9-brightgreen)](https://github.com/vitejs) | ||
[![cypress version](https://img.shields.io/badge/cypress-12.17.4-brightgreen)](https://cypress.io/) | ||
[![typescript version](https://img.shields.io/badge/typescript-5.2.2-brightgreen)](https://www.typescriptlang.org/) | ||
[![eslint version](https://img.shields.io/badge/eslint-8.48.0-brightgreen)](https://github.com/eslint) | ||
[![vite version](https://img.shields.io/badge/vite-4.4.9-brightgreen)](https://github.com/vitejs) | ||
[![prettier version](https://img.shields.io/badge/prettier-2.8.8-brightgreen)](https://prettier.io/) | ||
@@ -19,3 +19,3 @@ | ||
- **EventListener** is TypeScript sourced; | ||
- **EventListener** is TypeScript sourced, with some types addapted from React; | ||
- **EventListener** makes use of the native [Map](https://caniuse.com/mdn-javascript_builtins_map) to subscribe/register or unsubscribe/remove listeners, which is perfect since we need to make sure the exact listeners are added/removed; this completely invalidates the need to [deconstruct function objects](https://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-deep-clone-an-object-in-javascript) for comparison's sake to make sure event listeners are properly handled; | ||
@@ -51,3 +51,3 @@ - **EventListener** allows you to register multiple listeners for the same target, even of the same type, but always uses a single `globalListener` to call them all at once when event is triggered; | ||
// add a listener with `useCapture: false` | ||
function handleMyClick(e) { | ||
function handleMyClick(e: Listener.NativeEvent) { | ||
if (e.target.tagName === 'button') { | ||
@@ -107,8 +107,13 @@ e.preventDefault(); | ||
You can also make use of "tree shaking" to import only the module you want, for instance: | ||
You can also make use of the types for more consistent code: | ||
```js | ||
import { on } from '@thednp/event-listener'; | ||
import { on, FocusEventHandler } from '@thednp/event-listener'; | ||
on(document, handleMyClick, true); | ||
const handleMyFocus: FocusEventHandler<HTMLInputElement> = (e) => { | ||
console.log(e) | ||
} | ||
const myInput = document.querySelector('input') || document.createElement('input'); | ||
on(myInput, 'focus', handleMyFocus); | ||
``` | ||
@@ -118,3 +123,3 @@ | ||
## Run the tests suite (new) | ||
## Run the tests suite | ||
@@ -121,0 +126,0 @@ - [Download](https://github.com/thednp/event-listener/archive/refs/heads/master.zip) the package from Github; |
109
src/index.ts
@@ -9,4 +9,42 @@ /** | ||
type ListenerObject = Map<EventListener, AddEventListenerOptions | undefined | boolean>; | ||
type EventsRegistry = Record<string, Map<EventTarget, ListenerObject>>; | ||
import { | ||
SupportedEventObject, | ||
SupportedEventHandler, | ||
EventHandler, | ||
NativeEventTypes, | ||
NativeEvent, | ||
ClipboardEvent, | ||
CompositionEvent, | ||
DragEvent, | ||
FocusEvent, | ||
FormEvent, | ||
ChangeEvent, | ||
KeyboardEvent, | ||
MouseEvent, | ||
TouchEvent, | ||
PointerEvent, | ||
UIEvent, | ||
WheelEvent, | ||
AnimationEvent, | ||
TransitionEvent, | ||
NativeEventHandler, | ||
ClipboardEventHandler, | ||
CompositionEventHandler, | ||
DragEventHandler, | ||
FocusEventHandler, | ||
FormEventHandler, | ||
ChangeEventHandler, | ||
KeyboardEventHandler, | ||
MouseEventHandler, | ||
TouchEventHandler, | ||
PointerEventHandler, | ||
UIEventHandler, | ||
WheelEventHandler, | ||
AnimationEventHandler, | ||
TransitionEventHandler, | ||
PossibleEventTarget, | ||
ListenerObject, | ||
EventsRegistry, | ||
} from './types'; | ||
const registry: EventsRegistry = {}; | ||
@@ -20,3 +58,3 @@ | ||
*/ | ||
const globalListener = (e: Event): void => { | ||
const globalListener = (e: NativeEvent) => { | ||
const { type, currentTarget } = e; | ||
@@ -28,3 +66,3 @@ | ||
[...listenersMap].forEach(([listener, options]) => { | ||
listener.apply(element, [e]); | ||
(listener as EventHandler<typeof e>).apply(element, [e]); | ||
@@ -43,6 +81,6 @@ if (typeof options === 'object' && options.once) { | ||
*/ | ||
const addListener = ( | ||
element: EventTarget, | ||
eventType: string, | ||
listener: EventListener, | ||
const addListener = <T extends PossibleEventTarget>( | ||
element: T, | ||
eventType: NativeEventTypes, | ||
listener: SupportedEventHandler<T>, | ||
options?: AddEventListenerOptions, | ||
@@ -54,3 +92,3 @@ ): void => { | ||
} | ||
const oneEventMap = registry[eventType]; | ||
const oneEventMap = registry[eventType] as unknown as Map<T, ListenerObject<T, SupportedEventHandler<T>>>; | ||
@@ -66,7 +104,7 @@ if (!oneEventMap.has(element)) { | ||
// register listener with its options | ||
oneElementMap.set(listener, options); | ||
oneElementMap.set(listener as SupportedEventHandler<PossibleEventTarget>, options); | ||
// add listener last | ||
if (!size) { | ||
element.addEventListener(eventType, globalListener, options); | ||
element.addEventListener(eventType, globalListener as unknown as EventListenerObject, options); | ||
} | ||
@@ -80,6 +118,6 @@ }; | ||
*/ | ||
const removeListener = ( | ||
element: EventTarget, | ||
eventType: string, | ||
listener: EventListener, | ||
const removeListener = <T extends PossibleEventTarget>( | ||
element: T, | ||
eventType: NativeEventTypes, | ||
listener: SupportedEventHandler<PossibleEventTarget>, | ||
options?: AddEventListenerOptions, | ||
@@ -103,3 +141,3 @@ ): void => { | ||
if (!oneElementMap || !oneElementMap.size) { | ||
element.removeEventListener(eventType, globalListener, eventOptions); | ||
element.removeEventListener(eventType, globalListener as unknown as EventListenerObject, eventOptions); | ||
} | ||
@@ -113,1 +151,40 @@ }; | ||
export { addListener, removeListener, on, off, globalListener, registry }; | ||
export type { | ||
SupportedEventObject, | ||
SupportedEventHandler, | ||
EventHandler, | ||
NativeEventTypes, | ||
NativeEvent, | ||
ClipboardEvent, | ||
CompositionEvent, | ||
DragEvent, | ||
FocusEvent, | ||
FormEvent, | ||
ChangeEvent, | ||
KeyboardEvent, | ||
MouseEvent, | ||
TouchEvent, | ||
PointerEvent, | ||
UIEvent, | ||
WheelEvent, | ||
AnimationEvent, | ||
TransitionEvent, | ||
NativeEventHandler, | ||
ClipboardEventHandler, | ||
CompositionEventHandler, | ||
DragEventHandler, | ||
FocusEventHandler, | ||
FormEventHandler, | ||
ChangeEventHandler, | ||
KeyboardEventHandler, | ||
MouseEventHandler, | ||
TouchEventHandler, | ||
PointerEventHandler, | ||
UIEventHandler, | ||
WheelEventHandler, | ||
AnimationEventHandler, | ||
TransitionEventHandler, | ||
PossibleEventTarget, | ||
ListenerObject, | ||
EventsRegistry, | ||
}; |
@@ -5,3 +5,3 @@ { | ||
"lib": ["DOM", "ESNext", "DOM.Iterable"], | ||
"types": ["vite", "vite/client", "cypress"], | ||
"types": ["vite", "vite/client"], | ||
"rootDir": "./src", | ||
@@ -8,0 +8,0 @@ "baseUrl": "./", |
@@ -27,5 +27,4 @@ import path from 'path'; | ||
}, | ||
target: 'ESNext', | ||
sourcemap: true, | ||
}, | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
75766
31
1340
130