@ospin/fct-graph
Advanced tools
Comparing version 2.5.0 to 2.6.0
{ | ||
"name": "@ospin/fct-graph", | ||
"author": "danielseehausen", | ||
"version": "2.5.0", | ||
"version": "2.6.0", | ||
"description": "Graph data structure with conditional edges via 'slots' on nodes. Intended to represent physical and virtual functionalities on a device.", | ||
@@ -6,0 +6,0 @@ "main": "index.js", |
@@ -158,3 +158,7 @@ [![codecov](https://codecov.io/gh/ospin-web-dev/FCTGraph/branch/main/graph/badge.svg?token=RXXLX0HDAR)](https://codecov.io/gh/ospin-web-dev/FCTGraph) | ||
│ └── OneOfInSlot | ||
└── OutSlot | ||
└── OutSlot (virtual) | ||
└── IntegerOutSlot | ||
└── FloatOutSlot | ||
└── BooleanOutSlot | ||
└── OneOfOutSlot | ||
@@ -165,3 +169,3 @@ // a Slot has many DataStreams | ||
All non-virtual classes (e.g. HeaterActuator, InSlot, etc.) compose the **JOIous** module, which provides the following: | ||
All non-virtual classes (e.g. HeaterActuator, IntegerInSlot, etc.) compose the **JOIous** module, which provides the following: | ||
- **post .constructor** - asserts the instance's data against the JOI SCHEMA (which provides nested data validation) as a final step | ||
@@ -182,3 +186,3 @@ - **.serialize** (virtual) - blows up - informing the user that the class that composed JOIous needs a `.serialize` method | ||
- **Functionality** delegates to the **SlotFactory** when slots are added. The factory will attempt to find the appropriate slot sub-class via the `type` key value and blow up if it can not find one. | ||
- **Functionality** delegates to the **SlotFactory** when slots are added. The factory will attempt to find the appropriate slot sub-class via the `type` and `dataType` key value and blow up if it can not find one. | ||
@@ -213,2 +217,3 @@ **Functionalities** and **Slots** can also be created directly calling the constructors on their non-virtual classes. See [Class Structure and Hierarchies](#ClassStructureAndHierarchies) | ||
SlotSeeder (virtual) | ||
├── RandomSlotSeeder (picks from a SlotSeeder below) | ||
├── InSlotSeeder (virtual) | ||
@@ -219,3 +224,7 @@ │ └── IntegerInSlotSeeder | ||
│ └── OneOfInSlotSeeder | ||
└── OutSlotSeeder | ||
└── OutSlotSeeder (virtual) | ||
└── IntegerOutSlotSeeder | ||
└── FloatOutSlotSeeder | ||
└── BooleanOutSlotSeeder | ||
└── OneOfOutSlotSeeder | ||
``` | ||
@@ -222,0 +231,0 @@ |
@@ -12,3 +12,3 @@ const util = require('util') | ||
const richFctDisplay = util.inspect(fctData, { compact: false, depth: 4 }) | ||
this.message = `Failed to add fct: ${richFctDisplay}\n\nUnderlying error msg: ${msg}` | ||
this.message = `Failed to add fct: ${richFctDisplay}\n\n${msg}` | ||
} | ||
@@ -15,0 +15,0 @@ |
@@ -8,3 +8,3 @@ const Joi = require('joi') | ||
const InputNode = require('../functionalities/InputNode') | ||
const OutputNode = require('../functionalities/OutputNode') | ||
const OutputNode = require('../functionalities/OutputNode') | ||
const RegexUtils = require('../utils/RegexUtils') | ||
@@ -79,3 +79,2 @@ const { publicSuccessRes, publicErrorRes } = require('../utils/publicResponses') | ||
// safe - returns a public response | ||
addFunctionality(fctData) { | ||
@@ -82,0 +81,0 @@ try { |
@@ -18,3 +18,6 @@ const Joi = require('joi') | ||
constructor(functionalityData) { | ||
super(functionalityData) | ||
super({ | ||
isVirtual: false, | ||
...functionalityData, | ||
}) | ||
this.type = Actuator.TYPE | ||
@@ -21,0 +24,0 @@ } |
@@ -18,3 +18,6 @@ const Joi = require('joi') | ||
constructor(functionalityData) { | ||
super(functionalityData) | ||
super({ | ||
isVirtual: true, | ||
...functionalityData, | ||
}) | ||
this.type = Controller.TYPE | ||
@@ -21,0 +24,0 @@ } |
@@ -7,2 +7,3 @@ const Joi = require('joi') | ||
const SlotFactory = require('../slots/factories/SlotFactory') | ||
const AddSlotError = require('./AddSlotError') | ||
@@ -19,5 +20,20 @@ class Functionality { | ||
controllerName: Joi.string().allow(''), // this is used to support the old devices: https://github.com/ospin-web-dev/hambda/issues/913 | ||
isVirtual: Joi.boolean().required(), | ||
}) | ||
} | ||
get isPhysical() { return !this.isVirtual } | ||
get slotNames() { return this.slots.map(({ name }) => name) } | ||
_assertSlotNameUnique(slot) { | ||
if (this.slotNames.includes(slot.name)) { | ||
throw new AddSlotError(slot, `functionality already has a slot with the same name. current slot names: ${this.slotNames}`) | ||
} | ||
} | ||
_assertSlotCanBeAdded(slot) { | ||
this._assertSlotNameUnique(slot) | ||
} | ||
_addSlot(slotData) { | ||
@@ -27,2 +43,3 @@ const { id: functionalityId } = this | ||
this._assertSlotCanBeAdded(newSlot) | ||
this.slots.push(newSlot) | ||
@@ -34,4 +51,3 @@ | ||
_addSlots(slotsData) { | ||
/* NOTE: slots should never be added or removed outside of initialization | ||
* a.k.a. if I could make this method more private I would */ | ||
// NOTE: slots should never be added or removed outside of initialization | ||
slotsData.map(slotData => this._addSlot(slotData)) | ||
@@ -43,2 +59,3 @@ } | ||
name, | ||
isVirtual = false, | ||
slots: slotsData, | ||
@@ -48,2 +65,3 @@ }) { | ||
this.name = name | ||
this.isVirtual = isVirtual | ||
this.slots = [] | ||
@@ -57,2 +75,3 @@ if (slotsData) this._addSlots(slotsData) | ||
name: this.name, | ||
isVirtual: this.isVirtual, | ||
slots: this.slots.map(slot => slot.serialize()), | ||
@@ -59,0 +78,0 @@ } |
@@ -18,3 +18,6 @@ const Joi = require('joi') | ||
constructor(functionalityData) { | ||
super(functionalityData) | ||
super({ | ||
isVirtual: true, | ||
...functionalityData, | ||
}) | ||
this.type = InputNode.TYPE | ||
@@ -21,0 +24,0 @@ } |
@@ -11,11 +11,33 @@ const Joi = require('joi') | ||
static get VALID_DESTINATIONS() { | ||
return { | ||
OSPIN_WEBAPP: { name: 'ospin-webapp' }, | ||
UNSPECIFIED: { name: 'unspecified' }, | ||
} | ||
} | ||
static get VALID_DESTINATION_NAMES() { | ||
return Object.values(OutputNode.VALID_DESTINATIONS).map(({ name }) => name) | ||
} | ||
static get DEFAULT_DESTINATION() { | ||
return OutputNode.VALID_DESTINATIONS.UNSPECIFIED | ||
} | ||
static get SCHEMA() { | ||
return Joi.object({ | ||
type: Joi.string().allow(OutputNode.TYPE).required(), | ||
destination: Joi.object({ | ||
name: Joi.string().allow(...OutputNode.VALID_DESTINATION_NAMES).required(), | ||
}), | ||
}).concat(super.SCHEMA) | ||
} | ||
constructor(functionalityData) { | ||
super(functionalityData) | ||
constructor({ | ||
destination = OutputNode.DEFAULT_DESTINATION, | ||
...functionalityData | ||
}) { | ||
super({ isVirtual: true, ...functionalityData }) | ||
this.type = OutputNode.TYPE | ||
this.destination = destination | ||
} | ||
@@ -27,2 +49,3 @@ | ||
type: this.type, | ||
destination: this.destination, | ||
} | ||
@@ -29,0 +52,0 @@ } |
@@ -18,3 +18,6 @@ const Joi = require('joi') | ||
constructor(functionalityData) { | ||
super(functionalityData) | ||
super({ | ||
isVirtual: false, | ||
...functionalityData, | ||
}) | ||
this.type = Sensor.TYPE | ||
@@ -21,0 +24,0 @@ } |
@@ -37,2 +37,7 @@ const util = require('util') | ||
static enrichJoiValidationError(error) { | ||
// eslint-disable-next-line | ||
error.message = `JOI error in ${this.name}:\n\n${error.annotate()}` | ||
} | ||
constructor(...args) { | ||
@@ -54,6 +59,11 @@ super(...args) | ||
assertStructure() { | ||
Joi.attempt( | ||
this.serialize(), | ||
ReceivingClass.SCHEMA, | ||
) | ||
try { | ||
Joi.attempt( | ||
this.serialize(), | ||
ReceivingClass.SCHEMA, | ||
) | ||
} catch (e) { | ||
if (e.isJoi) { this.constructor.enrichJoiValidationError(e) } | ||
throw e | ||
} | ||
} | ||
@@ -60,0 +70,0 @@ |
@@ -23,2 +23,3 @@ const faker = require('faker') | ||
name: faker.hacker.noun(), | ||
isVirtual: faker.datatype.boolean(), | ||
...data, | ||
@@ -25,0 +26,0 @@ } |
@@ -0,1 +1,2 @@ | ||
const ArrayUtils = require('@choux/array-utils') | ||
const FunctionalitySeeder = require('./FunctionalitySeeder') | ||
@@ -10,2 +11,3 @@ const OutputNode = require('../../functionalities/OutputNode') | ||
type: OutputNode.TYPE, | ||
destination: ArrayUtils.sample(Object.values(OutputNode.VALID_DESTINATIONS)), | ||
...data, | ||
@@ -12,0 +14,0 @@ } |
const ControllerSeeder = require('./ControllerSeeder') | ||
const { FloatInSlotSeeder, OutSlotSeeder } = require('../slots') | ||
const { FloatInSlotSeeder, FloatOutSlotSeeder } = require('../slots') | ||
const PIDController = require('../../functionalities/PIDController') | ||
@@ -14,3 +14,3 @@ | ||
FloatInSlotSeeder.generate({ name: 'value in' }), | ||
OutSlotSeeder.generate({ name: 'value out', dataType: 'float' }), | ||
FloatOutSlotSeeder.generate({ name: 'value out' }), | ||
] | ||
@@ -36,4 +36,4 @@ } | ||
FloatInSlotSeeder.generateUnitlessIn({ name: 'D' }), | ||
FloatInSlotSeeder.generateCelciusIn(), | ||
OutSlotSeeder.generate({ name: 'value out', dataType: 'float' }), | ||
FloatInSlotSeeder.generateCelciusIn({ name: 'value in' }), | ||
FloatOutSlotSeeder.generateCelciusOut({ name: 'value out' }), | ||
] | ||
@@ -40,0 +40,0 @@ |
const InputNodeSeeder = require('./InputNodeSeeder') | ||
const { OutSlotSeeder } = require('../slots') | ||
const { | ||
RandomSlotSeeder, | ||
IntegerOutSlotSeeder, | ||
} = require('../slots') | ||
const PushIn = require('../../functionalities/PushIn') | ||
@@ -9,3 +12,3 @@ | ||
return [ | ||
OutSlotSeeder.generate({ name: 'value out' }), | ||
RandomSlotSeeder.generateRandomOutSlot({ name: 'value out' }), | ||
] | ||
@@ -23,4 +26,10 @@ } | ||
static generateIntegerPushIn() { | ||
return this.generate({ | ||
slots: [ IntegerOutSlotSeeder.generate() ], | ||
}) | ||
} | ||
} | ||
module.exports = PushInSeeder |
const OutputNodeSeeder = require('./OutputNodeSeeder') | ||
const { FloatInSlotSeeder } = require('../slots') | ||
const { | ||
FloatInSlotSeeder, | ||
RandomSlotSeeder, | ||
} = require('../slots') | ||
const PushOut = require('../../functionalities/PushOut') | ||
@@ -9,3 +12,3 @@ | ||
return [ | ||
FloatInSlotSeeder.generateUnitlessIn({ name: 'value in' }), | ||
RandomSlotSeeder.generateRandomInSlot({ name: 'value in' }), | ||
] | ||
@@ -23,4 +26,10 @@ } | ||
static generateFloatPushOutCelcius() { | ||
return this.generate({ | ||
slots: [ FloatInSlotSeeder.generate({ unit: '°C' }) ], | ||
}) | ||
} | ||
} | ||
module.exports = PushOutSeeder |
@@ -0,3 +1,8 @@ | ||
const ArrayUtils = require('@choux/array-utils') | ||
const SensorSeeder = require('./SensorSeeder') | ||
const { OutSlotSeeder } = require('../slots') | ||
const { | ||
FloatOutSlotSeeder, | ||
IntegerOutSlotSeeder, | ||
} = require('../slots') | ||
const TemperatureSensor = require('../../functionalities/TemperatureSensor') | ||
@@ -7,8 +12,18 @@ | ||
static generateSlots() { | ||
static get VALID_OUT_SLOT_SEEDERS() { | ||
return [ | ||
OutSlotSeeder.generate({ name: 'value out', dataType: 'float' }), | ||
FloatOutSlotSeeder, | ||
IntegerOutSlotSeeder, | ||
] | ||
} | ||
static generateSlots(overrideSlotData) { | ||
return [ | ||
ArrayUtils.sample(this.VALID_OUT_SLOT_SEEDERS).generate({ | ||
name: 'value out', | ||
...overrideSlotData, | ||
}), | ||
] | ||
} | ||
static generate(overrideData = {}) { | ||
@@ -26,10 +41,29 @@ return { | ||
* **************************************************************** */ | ||
static generateCelciusProducer(data) { | ||
const slots = [ OutSlotSeeder.generateCelciusOut(data) ] | ||
static generateCelciusIntegerProducer(data) { | ||
return this.generate({ | ||
slots: [ IntegerOutSlotSeeder.generateCelciusOut() ], | ||
...data, | ||
}) | ||
} | ||
return this.generate({ slots, name: 'Temperature Producer' }) | ||
static generateCelciusFloatProducer(data) { | ||
return this.generate({ | ||
slots: [ FloatOutSlotSeeder.generateCelciusOut() ], | ||
...data, | ||
}) | ||
} | ||
static generateCelciusProducer() { | ||
const generator = ArrayUtils.sample([ | ||
this.generateCelciusIntegerProducer.bind(this), | ||
this.generateCelciusFloatProducer.bind(this), | ||
]) | ||
return generator({ | ||
name: 'Temperature Producer', | ||
}) | ||
} | ||
} | ||
module.exports = TemperatureSensorSeeder |
const SensorSeeder = require('./SensorSeeder') | ||
const { OutSlotSeeder } = require('../slots') | ||
const { RandomSlotSeeder } = require('../slots') | ||
const UnknownSensor = require('../../functionalities/UnknownSensor') | ||
@@ -9,3 +9,3 @@ | ||
return [ | ||
OutSlotSeeder.generate({ name: 'value out', dataType: 'float' }), | ||
RandomSlotSeeder.generateRandomOutSlot({ name: 'value out' }), | ||
] | ||
@@ -12,0 +12,0 @@ } |
@@ -5,5 +5,11 @@ const FloatInSlotSeeder = require('./FloatInSlotSeeder') | ||
const OneOfInSlotSeeder = require('./OneOfInSlotSeeder') | ||
const OutSlotSeeder = require('./OutSlotSeeder') | ||
module.exports = { | ||
const FloatOutSlotSeeder = require('./FloatOutSlotSeeder') | ||
const BooleanOutSlotSeeder = require('./BooleanOutSlotSeeder') | ||
const IntegerOutSlotSeeder = require('./IntegerOutSlotSeeder') | ||
const OneOfOutSlotSeeder = require('./OneOfOutSlotSeeder') | ||
const RandomSlotSeeder = require('./RandomSlotSeeder') | ||
const IN_SLOT_CLASS_EXPORTS = { | ||
FloatInSlotSeeder, | ||
@@ -13,3 +19,15 @@ IntegerInSlotSeeder, | ||
BooleanInSlotSeeder, | ||
OutSlotSeeder, | ||
} | ||
const OUT_SLOT_CLASS_EXPORTS = { | ||
FloatOutSlotSeeder, | ||
IntegerOutSlotSeeder, | ||
OneOfOutSlotSeeder, | ||
BooleanOutSlotSeeder, | ||
} | ||
module.exports = { | ||
...IN_SLOT_CLASS_EXPORTS, | ||
...OUT_SLOT_CLASS_EXPORTS, | ||
RandomSlotSeeder, | ||
} |
@@ -0,1 +1,2 @@ | ||
const InSlot = require('../../slots/InSlot') | ||
const SlotSeeder = require('./SlotSeeder') | ||
@@ -6,7 +7,6 @@ | ||
static generate(data = {}) { | ||
const slotData = super.generate(data) | ||
return { | ||
...slotData, | ||
type: 'InSlot', | ||
...super.generate(data), | ||
type: InSlot.TYPE, | ||
...data, | ||
} | ||
@@ -13,0 +13,0 @@ } |
@@ -1,6 +0,3 @@ | ||
const faker = require('faker') | ||
const OutSlot = require('../../slots/OutSlot') | ||
const SlotSeeder = require('./SlotSeeder') | ||
const DataStream = require('../../dataStreams/DataStream') | ||
@@ -12,4 +9,3 @@ class OutSlotSeeder extends SlotSeeder { | ||
...super.generate(data), | ||
type: 'OutSlot', | ||
dataType: faker.random.arrayElement(Object.values(OutSlot.DATA_TYPES)), | ||
type: OutSlot.TYPE, | ||
...data, | ||
@@ -19,5 +15,2 @@ } | ||
/* ******************************************************************* | ||
* PRESETS | ||
* **************************************************************** */ | ||
static generateCelciusOut(data) { | ||
@@ -27,3 +20,2 @@ return this.generate({ | ||
name: 'Celcius Out', | ||
dataType: OutSlot.DATA_TYPES.FLOAT, | ||
...data, | ||
@@ -39,23 +31,4 @@ }) | ||
static seedWithDataStream(data) { | ||
const slot = this.seedOne( | ||
this.generate(data), | ||
) | ||
const dataStream = new DataStream({ | ||
id: faker.datatype.uuid(), | ||
sourceFctId: faker.datatype.uuid(), | ||
sourceSlotName: slot.name, | ||
sinkFctId: faker.datatype.uuid(), | ||
sinkSlotName: faker.animal.lion(), | ||
averagingWindowSize: faker.datatype.number(), | ||
}) | ||
slot._addDataStreamAndAssertStructure(dataStream) | ||
return slot | ||
} | ||
} | ||
module.exports = OutSlotSeeder |
@@ -9,6 +9,2 @@ const faker = require('faker') | ||
/* NOTE: | ||
* This class should not be documented as public interface. Consumers | ||
* should instead be guided to make use of the slot sub class seeders | ||
* (in/out slot seeders) instead of this virtual seeder */ | ||
class SlotSeeder { | ||
@@ -15,0 +11,0 @@ |
@@ -6,9 +6,11 @@ const FloatInSlot = require('../FloatInSlot') | ||
const OutSlot = require('../OutSlot') | ||
const FloatOutSlot = require('../FloatOutSlot') | ||
const IntegerOutSlot = require('../IntegerOutSlot') | ||
const BooleanOutSlot = require('../BooleanOutSlot') | ||
const OneOfOutSlot = require('../OneOfOutSlot') | ||
class SlotFactory { | ||
static get SUPPORTED_CLASSES() { | ||
static get SUPPORTED_IN_SLOT_CLASSES() { | ||
return [ | ||
OutSlot, | ||
FloatInSlot, | ||
@@ -21,11 +23,18 @@ IntegerInSlot, | ||
static get SUPPORTED_IN_SLOT_CLASSES() { | ||
static get SUPPORTED_OUT_SLOT_CLASSES() { | ||
return [ | ||
FloatInSlot, | ||
IntegerInSlot, | ||
BooleanInSlot, | ||
OneOfInSlot, | ||
FloatOutSlot, | ||
IntegerOutSlot, | ||
BooleanOutSlot, | ||
OneOfOutSlot, | ||
] | ||
} | ||
static get SUPPORTED_CLASSES() { | ||
return [ | ||
...SlotFactory.SUPPORTED_IN_SLOT_CLASSES, | ||
...SlotFactory.SUPPORTED_OUT_SLOT_CLASSES, | ||
] | ||
} | ||
static get SUPPORTED_CLASSES_SCHEMAS() { | ||
@@ -52,6 +61,6 @@ return SlotFactory.SUPPORTED_CLASSES.map( | ||
return { | ||
[OutSlot.DATA_TYPES.INTEGER]: OutSlot, | ||
[OutSlot.DATA_TYPES.FLOAT]: OutSlot, | ||
[OutSlot.DATA_TYPES.BOOLEAN]: OutSlot, | ||
[OutSlot.DATA_TYPES.ONE_OF]: OutSlot, | ||
[IntegerOutSlot.DATA_TYPE]: IntegerOutSlot, | ||
[FloatOutSlot.DATA_TYPE]: FloatOutSlot, | ||
[BooleanOutSlot.DATA_TYPE]: BooleanOutSlot, | ||
[OneOfOutSlot.DATA_TYPE]: OneOfOutSlot, | ||
} | ||
@@ -76,9 +85,4 @@ } | ||
return classes[dataType] | ||
} | ||
const FoundClass = classes[dataType] | ||
static newFromType(slotData) { | ||
const { type, dataType } = slotData | ||
const FoundClass = SlotFactory.getSlotClass(slotData) | ||
if (!SlotFactory.classIsSupported(FoundClass)) { | ||
@@ -89,2 +93,8 @@ const json = JSON.stringify(slotData) | ||
return FoundClass | ||
} | ||
static newFromType(slotData) { | ||
const FoundClass = SlotFactory.getSlotClass(slotData) | ||
return new FoundClass(slotData) | ||
@@ -91,0 +101,0 @@ } |
@@ -1,9 +0,23 @@ | ||
const Slot = require('./Slot') | ||
const InSlot = require('./InSlot') | ||
const OutSlot = require('./OutSlot') | ||
const BooleanInSlot = require('./BooleanInSlot') | ||
const FloatInSlot = require('./FloatInSlot') | ||
const IntegerInSlot = require('./IntegerInSlot') | ||
const OneOfInSlot = require('./OneOfInSlot') | ||
const BooleanOutSlot = require('./BooleanOutSlot') | ||
const FloatOutSlot = require('./FloatOutSlot') | ||
const IntegerOutSlot = require('./IntegerOutSlot') | ||
const OneOfOutSlot = require('./OneOfOutSlot') | ||
module.exports = { | ||
Slot, | ||
InSlot, | ||
OutSlot, | ||
BooleanInSlot, | ||
FloatInSlot, | ||
IntegerInSlot, | ||
OneOfInSlot, | ||
BooleanOutSlot, | ||
FloatOutSlot, | ||
IntegerOutSlot, | ||
OneOfOutSlot, | ||
} |
const Joi = require('joi') | ||
const JOIous = require('../mixins/instanceMixins/JOIous') | ||
const Slot = require('./Slot') | ||
@@ -12,11 +11,2 @@ | ||
static get DATA_TYPES() { | ||
return { | ||
INTEGER: 'integer', | ||
FLOAT: 'float', | ||
BOOLEAN: 'boolean', | ||
ONE_OF: 'oneOf', | ||
} | ||
} | ||
/* ******************************************************************* | ||
@@ -47,3 +37,2 @@ * CALIBRATION | ||
type: Joi.string().allow(OutSlot.TYPE).required(), | ||
dataType: Joi.string().allow(...Object.values(OutSlot.DATA_TYPES)).required(), | ||
calibrations: OutSlot.CALIBRATIONS_SCHEMA, | ||
@@ -69,4 +58,2 @@ }).concat(super.SCHEMA) | ||
module.exports = ( | ||
JOIous(OutSlot) | ||
) | ||
module.exports = OutSlot |
@@ -77,3 +77,3 @@ const Joi = require('joi') | ||
// Virtual | ||
throw new Error(`${this} requires an .assertStructure method to mutate. See mixin 'JOIous'`) | ||
throw new Error(`${this.constructor.name} requires an .assertStructure method to mutate. See mixin 'JOIous'`) | ||
} | ||
@@ -95,4 +95,2 @@ | ||
this.unit !== otherSlot.unit | ||
&& !this.isUnitless() | ||
&& !otherSlot.isUnitless() | ||
) { | ||
@@ -117,3 +115,2 @@ throw new SlotConnectionError(this, otherSlot, 'units must match between slots') | ||
this._assertSlotTypeCompatible(otherSlot) | ||
} | ||
@@ -120,0 +117,0 @@ |
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
114104
74
2208
262