@microsoft/fast-element
Advanced tools
Comparing version 0.7.0 to 0.8.0
@@ -6,2 +6,18 @@ # Change Log | ||
# [0.8.0](https://github.com/Microsoft/fast-dna/compare/@microsoft/fast-element@0.7.0...@microsoft/fast-element@0.8.0) (2020-04-27) | ||
### Bug Fixes | ||
* **fast-element:** attr bindings preserved during upgrade ([#3010](https://github.com/Microsoft/fast-dna/issues/3010)) ([e9b14cc](https://github.com/Microsoft/fast-dna/commit/e9b14ccf70efa7eac4b055d7a34fc4e2d775fa0c)) | ||
### Features | ||
* **fast-element:** add a render method to templates for pure templating ([#3018](https://github.com/Microsoft/fast-dna/issues/3018)) ([c4ac6b2](https://github.com/Microsoft/fast-dna/commit/c4ac6b2a20d67992d5d820e9ae56eb75db7e2e3a)) | ||
# [0.7.0](https://github.com/Microsoft/fast-dna/compare/@microsoft/fast-element@0.6.0...@microsoft/fast-element@0.7.0) (2020-04-22) | ||
@@ -8,0 +24,0 @@ |
@@ -0,1 +1,2 @@ | ||
import { Accessor } from "./observation/observable"; | ||
export interface ValueConverter { | ||
@@ -15,5 +16,5 @@ toView(value: any): string | null; | ||
export declare const nullableNumberConverter: ValueConverter; | ||
export declare class AttributeDefinition { | ||
export declare class AttributeDefinition implements Accessor { | ||
readonly Owner: Function; | ||
readonly property: string; | ||
readonly name: string; | ||
readonly attribute: string; | ||
@@ -26,5 +27,5 @@ readonly mode: AttributeMode; | ||
private readonly guards; | ||
constructor(Owner: Function, property: string, attribute?: string, mode?: AttributeMode, converter?: ValueConverter | undefined); | ||
setValue(element: HTMLElement, newValue: any): void; | ||
getValue(element: HTMLElement): any; | ||
constructor(Owner: Function, name: string, attribute?: string, mode?: AttributeMode, converter?: ValueConverter | undefined); | ||
setValue(source: HTMLElement, newValue: any): void; | ||
getValue(source: HTMLElement): any; | ||
onAttributeChangedCallback(element: HTMLElement, value: any): void; | ||
@@ -31,0 +32,0 @@ private tryReflectToAttribute; |
@@ -35,5 +35,5 @@ import { Observable } from "./observation/observable"; | ||
export class AttributeDefinition { | ||
constructor(Owner, property, attribute = property.toLowerCase(), mode = "reflect", converter) { | ||
constructor(Owner, name, attribute = name.toLowerCase(), mode = "reflect", converter) { | ||
this.Owner = Owner; | ||
this.property = property; | ||
this.name = name; | ||
this.attribute = attribute; | ||
@@ -43,4 +43,4 @@ this.mode = mode; | ||
this.guards = new Set(); | ||
this.fieldName = `_${property}`; | ||
this.callbackName = `${property}Changed`; | ||
this.fieldName = `_${name}`; | ||
this.callbackName = `${name}Changed`; | ||
this.hasCallback = this.callbackName in Owner.prototype; | ||
@@ -51,4 +51,4 @@ if (mode === "boolean" && converter === void 0) { | ||
} | ||
setValue(element, newValue) { | ||
const oldValue = element[this.fieldName]; | ||
setValue(source, newValue) { | ||
const oldValue = source[this.fieldName]; | ||
const converter = this.converter; | ||
@@ -59,13 +59,13 @@ if (converter !== void 0) { | ||
if (oldValue !== newValue) { | ||
element[this.fieldName] = newValue; | ||
this.tryReflectToAttribute(element); | ||
source[this.fieldName] = newValue; | ||
this.tryReflectToAttribute(source); | ||
if (this.hasCallback) { | ||
element[this.callbackName](oldValue, newValue); | ||
source[this.callbackName](oldValue, newValue); | ||
} | ||
element.$fastController.notify(element, this.property); | ||
source.$fastController.notify(source, this.name); | ||
} | ||
} | ||
getValue(element) { | ||
Observable.track(element, this.property); | ||
return element[this.fieldName]; | ||
getValue(source) { | ||
Observable.track(source, this.name); | ||
return source[this.fieldName]; | ||
} | ||
@@ -72,0 +72,0 @@ onAttributeChangedCallback(element, value) { |
@@ -36,9 +36,9 @@ import { FASTElement } from "./fast-element"; | ||
// the browser upgraded the element. Then delete the property since it will | ||
// shadow the getter/setter that is required to make the observable function. | ||
// shadow the getter/setter that is required to make the observable operate. | ||
// Later, in the connect callback, we'll re-apply the values. | ||
const observedProps = Observable.getObservedProperties(element); | ||
if (observedProps.length > 0) { | ||
const accessors = Observable.getAccessors(element); | ||
if (accessors.length > 0) { | ||
const boundObservables = (this.boundObservables = Object.create(null)); | ||
for (let i = 0, ii = observedProps.length; i < ii; ++i) { | ||
const propertyName = observedProps[i]; | ||
for (let i = 0, ii = accessors.length; i < ii; ++i) { | ||
const propertyName = accessors[i].name; | ||
const value = element[propertyName]; | ||
@@ -45,0 +45,0 @@ if (value !== void 0) { |
@@ -10,5 +10,4 @@ import { Observable } from "../observation/observable"; | ||
bind(source) { | ||
this.shouldUpdate = | ||
Observable.getObservedProperties(source).indexOf(this.options.property) !== | ||
-1; | ||
const name = this.options.property; | ||
this.shouldUpdate = Observable.getAccessors(source).some((x) => x.name === name); | ||
this.source = source; | ||
@@ -15,0 +14,0 @@ this.updateTarget(this.getNodes()); |
import { CaptureType, SyntheticViewTemplate, ViewTemplate } from "../template"; | ||
import { ExecutionContext, Expression } from "../observation/observable"; | ||
import { Subscriber } from "../observation/subscriber-collection"; | ||
import { Subscriber } from "../observation/notifier"; | ||
import { Splice } from "../observation/array-change-records"; | ||
@@ -5,0 +5,0 @@ import { Behavior } from "./behavior"; |
@@ -48,3 +48,3 @@ import { DOM } from "../dom"; | ||
if (this.itemsObserver !== void 0) { | ||
this.itemsObserver.removeSubscriber(this); | ||
this.itemsObserver.unsubscribe(this); | ||
} | ||
@@ -70,5 +70,5 @@ this.unbindAllViews(); | ||
if (oldObserver !== void 0) { | ||
oldObserver.removeSubscriber(this); | ||
oldObserver.unsubscribe(this); | ||
} | ||
newObserver.addSubscriber(this); | ||
newObserver.subscribe(this); | ||
} | ||
@@ -75,0 +75,0 @@ } |
import { Controller } from "./controller"; | ||
import { AttributeDefinition } from "./attributes"; | ||
import { Observable } from "./observation/observable"; | ||
const defaultShadowOptions = { mode: "open" }; | ||
@@ -64,13 +65,5 @@ const defaultElementOptions = {}; | ||
observedAttributes[i] = current.attribute; | ||
propertyLookup[current.property] = current; | ||
propertyLookup[current.name] = current; | ||
attributeLookup[current.attribute] = current; | ||
Reflect.defineProperty(proto, current.property, { | ||
enumerable: true, | ||
get: function () { | ||
return current.getValue(this); | ||
}, | ||
set: function (value) { | ||
return current.setValue(this, value); | ||
}, | ||
}); | ||
Observable.defineProperty(proto, current); | ||
} | ||
@@ -77,0 +70,0 @@ Reflect.defineProperty(Type, "observedAttributes", { |
@@ -10,2 +10,3 @@ export * from "./template"; | ||
export * from "./observation/observable"; | ||
export * from "./observation/notifier"; | ||
export * from "./dom"; | ||
@@ -12,0 +13,0 @@ export * from "./directives/behavior"; |
@@ -10,2 +10,3 @@ export * from "./template"; | ||
export * from "./observation/observable"; | ||
export * from "./observation/notifier"; | ||
export * from "./dom"; | ||
@@ -12,0 +13,0 @@ export * from "./directives/binding"; |
@@ -1,5 +0,4 @@ | ||
import { Subscriber, SubscriberCollection } from "./subscriber-collection"; | ||
import { Notifier } from "./notifier"; | ||
import { SubscriberCollection } from "./notifier"; | ||
import { Splice } from "./array-change-records"; | ||
export declare class ArrayObserver extends SubscriberCollection implements Notifier { | ||
export declare class ArrayObserver extends SubscriberCollection { | ||
private collection; | ||
@@ -9,10 +8,8 @@ private oldCollection; | ||
private needsQueue; | ||
subscribe: (subscriber: Subscriber) => void; | ||
unsubscribe: (subscriber: Subscriber) => void; | ||
call: () => void; | ||
constructor(collection: any[]); | ||
addSplice(splice: Splice): void; | ||
reset(oldCollection: any[] | undefined): void; | ||
notify(): void; | ||
call(): void; | ||
flush(): void; | ||
} | ||
export declare function enableArrayObservation(): void; |
import { DOM } from "../dom"; | ||
import { Observable } from "./observable"; | ||
import { SubscriberCollection } from "./subscriber-collection"; | ||
import { SubscriberCollection } from "./notifier"; | ||
import { calcSplices, newSplice, projectArraySplices, } from "./array-change-records"; | ||
@@ -28,4 +28,3 @@ let arrayObservationEnabled = false; | ||
this.needsQueue = true; | ||
this.subscribe = this.addSubscriber; | ||
this.unsubscribe = this.removeSubscriber; | ||
this.call = this.flush; | ||
collection.$fastController = this; | ||
@@ -35,18 +34,16 @@ this.collection = collection; | ||
addSplice(splice) { | ||
if (this.hasSubscribers()) { | ||
if (this.splices === void 0) { | ||
this.splices = [splice]; | ||
} | ||
else { | ||
this.splices.push(splice); | ||
} | ||
if (this.needsQueue) { | ||
this.needsQueue = false; | ||
DOM.queueUpdate(this); | ||
} | ||
if (this.splices === void 0) { | ||
this.splices = [splice]; | ||
} | ||
else { | ||
this.splices.push(splice); | ||
} | ||
if (this.needsQueue) { | ||
this.needsQueue = false; | ||
DOM.queueUpdate(this); | ||
} | ||
} | ||
reset(oldCollection) { | ||
this.oldCollection = oldCollection; | ||
if (this.hasSubscribers() && this.needsQueue) { | ||
if (this.needsQueue) { | ||
this.needsQueue = false; | ||
@@ -56,19 +53,15 @@ DOM.queueUpdate(this); | ||
} | ||
notify() { | ||
if (this.splices !== void 0 || this.oldCollection !== void 0) { | ||
this.call(); | ||
} | ||
} | ||
call() { | ||
flush() { | ||
const splices = this.splices; | ||
const oldCollection = this.oldCollection; | ||
if (splices === void 0 && oldCollection === void 0) { | ||
return; | ||
} | ||
this.needsQueue = true; | ||
this.splices = void 0; | ||
this.oldCollection = void 0; | ||
if (this.hasSubscribers()) { | ||
const finalSplices = oldCollection === void 0 | ||
? projectArraySplices(this.collection, splices) | ||
: calcSplices(this.collection, 0, this.collection.length, oldCollection, 0, oldCollection.length); | ||
this.notifySubscribers(this, finalSplices); | ||
} | ||
const finalSplices = oldCollection === void 0 | ||
? projectArraySplices(this.collection, splices) | ||
: calcSplices(this.collection, 0, this.collection.length, oldCollection, 0, oldCollection.length); | ||
this.notify(this, finalSplices); | ||
} | ||
@@ -115,3 +108,3 @@ } | ||
if (o !== void 0) { | ||
o.notify(); | ||
o.flush(); | ||
oldArray = this.slice(); | ||
@@ -138,3 +131,3 @@ } | ||
if (o !== void 0) { | ||
o.notify(); | ||
o.flush(); | ||
oldArray = this.slice(); | ||
@@ -141,0 +134,0 @@ } |
@@ -1,2 +0,7 @@ | ||
import { Subscriber } from "./subscriber-collection"; | ||
/** | ||
* Implemented by objects that are interested in change notifications. | ||
*/ | ||
export interface Subscriber { | ||
handleChange(source: any, args: any): void; | ||
} | ||
export interface Notifier { | ||
@@ -7,2 +12,18 @@ notify(source: any, args: any): void; | ||
} | ||
/** | ||
* Efficiently keeps track of subscribers interested in change notifications. | ||
* | ||
* @remarks | ||
* This collection is optimized for the most common scenario of 1 or 2 subscribers. | ||
* With this in mind, it can store a subscriber in an internal field, allowing it to avoid Array#push operations. | ||
* If the collection ever exceeds two subscribers, it upgrade to an array. | ||
*/ | ||
export declare class SubscriberCollection implements Notifier { | ||
private sub1; | ||
private sub2; | ||
private spillover; | ||
subscribe(subscriber: Subscriber): void; | ||
unsubscribe(subscriber: Subscriber): void; | ||
notify(source: any, args: any): void; | ||
} | ||
export declare class PropertyChangeNotifier implements Notifier { | ||
@@ -9,0 +30,0 @@ private subscribers; |
@@ -1,2 +0,75 @@ | ||
import { SubscriberCollection } from "./subscriber-collection"; | ||
function spilloverSubscribe(subscriber) { | ||
const spillover = this.spillover; | ||
const index = spillover.indexOf(subscriber); | ||
if (index === -1) { | ||
spillover.push(subscriber); | ||
} | ||
} | ||
function spilloverUnsubscribe(subscriber) { | ||
const spillover = this.spillover; | ||
const index = spillover.indexOf(subscriber); | ||
if (index !== -1) { | ||
spillover.splice(index, 1); | ||
} | ||
} | ||
function spilloverNotifySubscribers(source, args) { | ||
const spillover = this.spillover; | ||
for (let i = 0, ii = spillover.length; i < ii; ++i) { | ||
spillover[i].handleChange(source, args); | ||
} | ||
} | ||
/** | ||
* Efficiently keeps track of subscribers interested in change notifications. | ||
* | ||
* @remarks | ||
* This collection is optimized for the most common scenario of 1 or 2 subscribers. | ||
* With this in mind, it can store a subscriber in an internal field, allowing it to avoid Array#push operations. | ||
* If the collection ever exceeds two subscribers, it upgrade to an array. | ||
*/ | ||
export class SubscriberCollection { | ||
constructor() { | ||
this.sub1 = void 0; | ||
this.sub2 = void 0; | ||
this.spillover = void 0; | ||
} | ||
subscribe(subscriber) { | ||
if (this.sub1 === subscriber || this.sub2 === subscriber) { | ||
return; | ||
} | ||
if (this.sub1 === void 0) { | ||
this.sub1 = subscriber; | ||
return; | ||
} | ||
if (this.sub2 === void 0) { | ||
this.sub2 = subscriber; | ||
return; | ||
} | ||
this.spillover = [this.sub1, this.sub2]; | ||
this.subscribe = spilloverSubscribe; | ||
this.unsubscribe = spilloverUnsubscribe; | ||
this.notify = spilloverNotifySubscribers; | ||
this.sub1 = void 0; | ||
this.sub2 = void 0; | ||
} | ||
unsubscribe(subscriber) { | ||
if (this.sub1 === subscriber) { | ||
this.sub1 = void 0; | ||
return; | ||
} | ||
if (this.sub2 === subscriber) { | ||
this.sub2 = void 0; | ||
return; | ||
} | ||
} | ||
notify(source, args) { | ||
const sub1 = this.sub1; | ||
const sub2 = this.sub2; | ||
if (sub1 !== void 0) { | ||
sub1.handleChange(source, args); | ||
} | ||
if (sub2 !== void 0) { | ||
sub2.handleChange(source, args); | ||
} | ||
} | ||
} | ||
export class PropertyChangeNotifier { | ||
@@ -9,9 +82,11 @@ constructor() { | ||
if (subscribers !== void 0) { | ||
subscribers.notifySubscribers(source, propertyName); | ||
subscribers.notify(source, propertyName); | ||
} | ||
} | ||
subscribe(subscriber, propertyName) { | ||
const subscribers = this.subscribers[propertyName] || | ||
(this.subscribers[propertyName] = new SubscriberCollection()); | ||
subscribers.addSubscriber(subscriber); | ||
let subscribers = this.subscribers[propertyName]; | ||
if (subscribers === void 0) { | ||
this.subscribers[propertyName] = subscribers = new SubscriberCollection(); | ||
} | ||
subscribers.subscribe(subscriber); | ||
} | ||
@@ -23,4 +98,4 @@ unsubscribe(subscriber, propertyName) { | ||
} | ||
subscribers.removeSubscriber(subscriber); | ||
subscribers.unsubscribe(subscriber); | ||
} | ||
} |
import { Notifier } from "./notifier"; | ||
export interface Accessor { | ||
name: string; | ||
getValue(source: any): any; | ||
setValue(source: any, value: any): void; | ||
} | ||
export declare const Observable: { | ||
@@ -7,4 +12,4 @@ createArrayObserver(array: any[]): Notifier; | ||
notify(source: unknown, args: any): void; | ||
define(target: {}, propertyName: string): void; | ||
getObservedProperties(target: {}): string[]; | ||
defineProperty(target: {}, nameOrAccessor: string | Accessor): void; | ||
getAccessors(target: {}): Accessor[]; | ||
}; | ||
@@ -11,0 +16,0 @@ export declare function observable($target: {}, $prop: string): void; |
@@ -1,6 +0,32 @@ | ||
import { emptyArray } from "../interfaces"; | ||
import { DOM } from "../dom"; | ||
import { PropertyChangeNotifier } from "./notifier"; | ||
const notifierLookup = new WeakMap(); | ||
const accessorLookup = new WeakMap(); | ||
let watcher = void 0; | ||
class DefaultObservableAccessor { | ||
constructor(name, target) { | ||
this.name = name; | ||
this.field = `_${name}`; | ||
this.callback = `${name}Changed`; | ||
this.hasCallback = this.callback in target; | ||
} | ||
getValue(source) { | ||
if (watcher !== void 0) { | ||
watcher.observe(source, this.name); | ||
} | ||
return source[this.field]; | ||
} | ||
setValue(source, newValue) { | ||
const field = this.field; | ||
const oldValue = source[field]; | ||
if (oldValue !== newValue) { | ||
source[field] = newValue; | ||
if (this.hasCallback) { | ||
source[this.callback](oldValue, newValue); | ||
} | ||
/* eslint-disable-next-line @typescript-eslint/no-use-before-define */ | ||
getNotifier(source).notify(source, this.name); | ||
} | ||
} | ||
} | ||
export const Observable = { | ||
@@ -32,32 +58,34 @@ /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ | ||
}, | ||
define(target, propertyName) { | ||
const fieldName = `_${propertyName}`; | ||
const callbackName = `${propertyName}Changed`; | ||
const hasCallback = callbackName in target; | ||
const observedProperties = target.observedProperties || | ||
(target.observedProperties = []); | ||
observedProperties.push(propertyName); | ||
Reflect.defineProperty(target, propertyName, { | ||
defineProperty(target, nameOrAccessor) { | ||
if (typeof nameOrAccessor === "string") { | ||
nameOrAccessor = new DefaultObservableAccessor(nameOrAccessor, target); | ||
} | ||
this.getAccessors(target).push(nameOrAccessor); | ||
Reflect.defineProperty(target, nameOrAccessor.name, { | ||
enumerable: true, | ||
get: function () { | ||
if (watcher !== void 0) { | ||
watcher.observe(this, propertyName); | ||
} | ||
return this[fieldName]; | ||
return nameOrAccessor.getValue(this); | ||
}, | ||
set: function (newValue) { | ||
const oldValue = this[fieldName]; | ||
if (oldValue !== newValue) { | ||
this[fieldName] = newValue; | ||
if (hasCallback) { | ||
this[callbackName](oldValue, newValue); | ||
} | ||
/* eslint-disable-next-line @typescript-eslint/no-use-before-define */ | ||
getNotifier(this).notify(this, propertyName); | ||
} | ||
nameOrAccessor.setValue(this, newValue); | ||
}, | ||
}); | ||
}, | ||
getObservedProperties(target) { | ||
return target.observedProperties || emptyArray; | ||
getAccessors(target) { | ||
let accessors = accessorLookup.get(target); | ||
if (accessors === void 0) { | ||
let currentTarget = Reflect.getPrototypeOf(target); | ||
while (accessors === void 0 && currentTarget !== null) { | ||
accessors = accessorLookup.get(currentTarget); | ||
currentTarget = Reflect.getPrototypeOf(currentTarget); | ||
} | ||
if (accessors === void 0) { | ||
accessors = []; | ||
} | ||
else { | ||
accessors = accessors.slice(0); | ||
} | ||
accessorLookup.set(target, accessors); | ||
} | ||
return accessors; | ||
}, | ||
@@ -68,3 +96,3 @@ }; | ||
export function observable($target, $prop) { | ||
Observable.define($target, $prop); | ||
Observable.defineProperty($target, $prop); | ||
} | ||
@@ -103,4 +131,4 @@ let currentEvent = null; | ||
} | ||
Observable.define(ExecutionContext.prototype, "index"); | ||
Observable.define(ExecutionContext.prototype, "length"); | ||
Observable.defineProperty(ExecutionContext.prototype, "index"); | ||
Observable.defineProperty(ExecutionContext.prototype, "length"); | ||
export const defaultExecutionContext = new ExecutionContext(); | ||
@@ -107,0 +135,0 @@ export class ObservableExpression { |
@@ -8,3 +8,3 @@ import { ElementView, HTMLView, SyntheticView } from "./view"; | ||
} | ||
export interface SyntheticViewTemplate<TScope = any, TParent = any> { | ||
export interface SyntheticViewTemplate<TSource = any, TParent = any> { | ||
create(): SyntheticView; | ||
@@ -18,3 +18,3 @@ } | ||
} | ||
export declare class ViewTemplate<TScope = any, TParent = any> extends Directive implements ElementViewTemplate, SyntheticViewTemplate { | ||
export declare class ViewTemplate<TSource = any, TParent = any> extends Directive implements ElementViewTemplate, SyntheticViewTemplate { | ||
private html; | ||
@@ -31,9 +31,10 @@ private directives; | ||
create(host?: Element): HTMLView; | ||
render(source: TSource, host: HTMLElement | string): HTMLView; | ||
createBehavior(target: any): HTMLTemplateBehavior; | ||
} | ||
export declare const lastAttributeNameRegex: RegExp; | ||
export interface CaptureType<TScope> { | ||
export interface CaptureType<TSource> { | ||
} | ||
declare type TemplateValue<TScope, TParent = any> = Expression<TScope, any, TParent> | string | number | Directive | CaptureType<TScope>; | ||
export declare function html<TScope = any, TParent = any>(strings: TemplateStringsArray, ...values: TemplateValue<TScope, TParent>[]): ViewTemplate<TScope, TParent>; | ||
export declare function html<TSource = any, TParent = any>(strings: TemplateStringsArray, ...values: TemplateValue<TSource, TParent>[]): ViewTemplate<TSource, TParent>; | ||
export {}; |
@@ -6,2 +6,3 @@ import { compileTemplate } from "./template-compiler"; | ||
import { BindingDirective } from "./directives/binding"; | ||
import { defaultExecutionContext, } from "./observation/observable"; | ||
export class HTMLTemplateBehavior { | ||
@@ -86,2 +87,11 @@ constructor(template, location) { | ||
} | ||
render(source, host) { | ||
if (typeof host === "string") { | ||
host = document.getElementById(host); | ||
} | ||
const view = this.create(host); | ||
view.bind(source, defaultExecutionContext); | ||
view.appendTo(host); | ||
return view; | ||
} | ||
createBehavior(target) { | ||
@@ -88,0 +98,0 @@ return new HTMLTemplateBehavior(this, target); |
@@ -5,3 +5,3 @@ { | ||
"sideEffects": false, | ||
"version": "0.7.0", | ||
"version": "0.8.0", | ||
"author": { | ||
@@ -34,3 +34,3 @@ "name": "Microsoft", | ||
}, | ||
"gitHead": "d23405bdc1b1bd3b2c278443d22a469b1283c598" | ||
"gitHead": "526e002ac76d999f6ab3acea249e63bbc4d79173" | ||
} |
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
175229
3602
0
52