--- title: "How to: Bring Your Own Average and Round Operation" slug: "how-to-bring-your-own-operation" description: "Learn to create a custom Average and Rounding Operation in Unqork, integrating JavaScript for precise calculations and event handling." updated: 2025-11-26T19:55:49Z published: 2025-11-26T19:55:49Z canonical: "docs.unqork.io/how-to-bring-your-own-operation" --- > ## Documentation Index > Fetch the complete documentation index at: https://docs.unqork.io/llms.txt > Use this file to discover all available pages before exploring further. # How to: Bring Your Own Average and Round Operation ## Overview This how-to guide details how to create a **custom** ([Bring Your Own](/v1/docs/introduction-to-the-bring-your-own-byo-framework)) **Average and Rounding Operation**. It demonstrates how to build an asset that fully integrates with the Unqork platform. The Unqork platform has built-in support for average and rounding, so you don't need to create a custom operation for them. Use this guide as an example for the operation implementation process. > [!WARNING] > The [Bring Your Own](/v1/docs/introduction-to-the-bring-your-own-byo-framework) feature is intended for developers who have a strong understanding of the Unqork Designer Platform and JavaScript. ## BYO Average and Round Operation Ideation For this example, the fictional client company "ABC" wants to implement an operation for averaging a value and rounding it to a specified decimal value. The operation itself should let Creators configure array values for rounding and specify the number of values. The client requests the operation to support an event that emits when a user clicks on an item in the checklist. They also ask that it include an operation that sets the completed (checked/unchecked) state of a single item in the checklist. As the developer assigned to this task, break down and document the client's ask into actionable sections: - **Asset Types**: What’s needed to meet the client's requirements of an averaging and rounding asset? - **Component(s)**: Does the client's request require the creation of something not currently supported in the Unqork platform? - **Event(s)**: Does the asset need to emit an event when it's used? What action should emit the event? - **Operation(s)**: Does the asset need to be manipulated by other elements using an operation? - **Inputs & Outputs**: Catalog a list of potential inputs and outputs for the asset. Events are typically an output in this list. - **Naming Scheme**: Develop a naming scheme for the asset that aligns with Unqork values and avoids conflicts with potential future custom assets. > To understand naming scheme limitations, view the Important callout in the overview of our [Bring Your Own (BYO): Understanding the manifest.json File](/v1/docs/bring-your-own-byo-understanding-the-manifestjson-file#file-specification) article. Now that the client has described the ask, create a document detailing all the potential elements the operation asset requires. ## Example Ideation Documentation The sections below detail the product requirements that meet the client's request. Developers use this information to implement the operation’s JavaScript, create the manifest file and package, then package it to the Unqork Designer Platform. ### Operation An operation that averages an array of numbers and rounds the result to a specified number of decimals. | Operation Name | Operation Value | Description | | --- | --- | --- | | Average and Round | `exAvgAndRound` | Input options: these are the fields Creators use to average numbers and specify the decimal rounding value. | | ↳ | `numbers` (`num[]`, `optional`) | An array of numbers to average. By default, the value is `[]`. | | | `roundNumDec` ( `integer`, `optional`) | An integer representing the number of decimal places for rounding. **Constrained**: A value between `0` and `10`. **Placeholder**: Specify an integer between `0` and `10`. By default, the value is `2`. | | Context Payload. This is the operation’s output after processing the input values. | | ↳ | `result` (number, required) | The averaged value after rounding to the requested decimals. This value is returned in the operation's context. | ### Inputs Creators use the input fields to average a set of numbers and specify the decimal rounding value. | Parameter | Object Type | Description | | --- | --- | --- | | `numbers` | number array | An array of numbers to average. By default, the value is `[]`. | | `roundNumDec` | integer | An integer representing the number of decimal places for rounding. **Constrained**: A value between `0` and `10`. **Placeholder**: Specify an integer between `0` and `10`. By default, the value is `2`. | ### Outputs This is the output the operation produces after processing the input values. | Parameter | Description | | --- | --- | | `contextModel` | A schema that defines the output of the operation. It tells the Unqork platform what data to expect back so it can be used in subsequent steps of a workflow. | | ↳ | `result` | Return an object with a single property named result, which is a number. | ### Naming Scheme Verify that all naming values do not conflict with existing Unqork assets or other custom assets in the environment. > [!WARNING] > IMPORTANT > > Unqork does not enforce programmatic name spacing for custom asset types. All type values for components, operations, and events must be globally unique. If multiple custom assets share the same type, or if a custom asset uses a type that conflicts with a native Unqork type, the runtime will render or execute only the first matching definition. Doing so leads to unpredictable behavior, asset shadowing, or broken functionality—especially when bundling or importing multiple packages. > > It is the responsibility of the developer to ensure type uniqueness to prevent collisions. Component, operation, and event types require a unique name to avoid collision issues. > > To avoid namespace issues, prefix custom components with a name unique to your company or application. For example, if a company named **ABC** creates a **Card Panel** component. The component's name space could be `abcCardPanel`. | Asset Type | Asset Name | Naming Scheme | | --- | --- | --- | | Operation | Example and Round Average | `exAvgAndRound` | | Input | Numbers | `numbers` | | Input | Round Number Decimals | `roundNumDec` | ## Create the `manifest.json` File The manfest.json file serves as the contract between Unqork and the operation implementation. ```json {  "name": "exAvgAndRound",  "version": "1.0.0",  "main": "exAvgAndRoundOperation.js",  "type": "custom",  "productType": "BYO",  "description": "A simple operation that averages an array of numbers and rounds result to specified decimals.",  "operations": [    {      "name": "Example Average and Round",      "type": "exAvgAndRound",      "description": "Averages an array of numbers and rounds result to specified decimals.",      "stability": "STABLE",      "model": {        "type": "object",        "properties": {          "options": {            "type": "object",            "properties": {              "numbers": {                "name": "Numbers",                "type": "array",                "description": "Flat array of numbers to average.",                "items": { "type": "number" },                "default": []              },              "roundNumDec": {                "name": "Round Number Decimals",                "type": "integer",                "description": "Number of decimals to round result.",                "minimum": 0,                "maximum": 10,                "default": 2,                "placeholder": "specify an integer between 0 and 10"              }            }          },          "type": {            "const": "exAvgAndRound",            "type": "string"          }        }      },      "contextModel": {        "type": "object",        "properties": {          "result": {            "type": "object",            "properties": {              "result": {                "name": "Result",                "type": "number",                "description": "Result of the average operation."              }            }          }        }      }    }  ] } ``` The following fields are used at the root level of the manifest | Field | Type | Required | Description | | --- | --- | --- | --- | | name | string | ![Checkmark](https://cdn.us.document360.io/15548f06-2fba-434c-b0c1-5df88317dbd7/Images/Documentation/UDLC_Enterprise_View_Green_Checkmark.png?sv=2026-02-06&spr=https&st=2026-11-08T18%3A05%3A09Z&se=2026-11-08T19%3A05%3A09Z&sr=c&sp=r&sig=a24FthqT2tLQYbxbDe3IvNWT2QINSzbXh3jbniwiEoQ%3D) | A unique identifier for the package. For example, `MyCompanyNameAssets`. | | version | string | ![Checkmark](https://cdn.us.document360.io/15548f06-2fba-434c-b0c1-5df88317dbd7/Images/Documentation/UDLC_Enterprise_View_Green_Checkmark.png?sv=2026-02-06&spr=https&st=2026-11-08T18%3A05%3A09Z&se=2026-11-08T19%3A05%3A09Z&sr=c&sp=r&sig=a24FthqT2tLQYbxbDe3IvNWT2QINSzbXh3jbniwiEoQ%3D) | The semantic version number. For example, `1.0.0`. | | type | enum | ![Checkmark](https://cdn.us.document360.io/15548f06-2fba-434c-b0c1-5df88317dbd7/Images/Documentation/UDLC_Enterprise_View_Green_Checkmark.png?sv=2026-02-06&spr=https&st=2026-11-08T18%3A05%3A09Z&se=2026-11-08T19%3A05%3A09Z&sr=c&sp=r&sig=a24FthqT2tLQYbxbDe3IvNWT2QINSzbXh3jbniwiEoQ%3D) | The package type. Currently, the only supported value is `custom`. | | productType | enum | ![Checkmark](https://cdn.us.document360.io/15548f06-2fba-434c-b0c1-5df88317dbd7/Images/Documentation/UDLC_Enterprise_View_Green_Checkmark.png?sv=2026-02-06&spr=https&st=2026-11-08T18%3A05%3A09Z&se=2026-11-08T19%3A05%3A09Z&sr=c&sp=r&sig=a24FthqT2tLQYbxbDe3IvNWT2QINSzbXh3jbniwiEoQ%3D) | The target platform feature. Currently, the only supported value is `BYO`. | | description | string | ![Checkmark](https://cdn.us.document360.io/15548f06-2fba-434c-b0c1-5df88317dbd7/Images/Documentation/UDLC_Enterprise_View_Green_Checkmark.png?sv=2026-02-06&spr=https&st=2026-11-08T18%3A05%3A09Z&se=2026-11-08T19%3A05%3A09Z&sr=c&sp=r&sig=a24FthqT2tLQYbxbDe3IvNWT2QINSzbXh3jbniwiEoQ%3D) | A description that displays in the Admin Panel after installation. | | main | string | ![Checkmark](https://cdn.us.document360.io/15548f06-2fba-434c-b0c1-5df88317dbd7/Images/Documentation/UDLC_Enterprise_View_Green_Checkmark.png?sv=2026-02-06&spr=https&st=2026-11-08T18%3A05%3A09Z&se=2026-11-08T19%3A05%3A09Z&sr=c&sp=r&sig=a24FthqT2tLQYbxbDe3IvNWT2QINSzbXh3jbniwiEoQ%3D) | The relative path to the JavaScript entry file. For example, `index.js`. | | components | array | Optional | A list of component definitions included in the package. | | events | array | Optional | List of event definitions. | | operations | array | Optional | List of operation definitions | ### Understanding the `Operations[0]` (The `exAvgAndRound` Operation) This is the main definition for the operation itself. | Property | Description | | --- | --- | | `name` and `type` | Define the component's name and its internal identifier. The type (`exAvgAndRound`) must match the export name in the JavaScript file (`exAvgAndRound.js`) so Unqork can link the JSON definition to the correct code. | | `model` | This object defines the number array and decimal rounding inputs that a user can configure when using this operation. | | ↳ | `numbers` | This is an input field for a user to provide an array of numbers. The schema specifies that it must be an array of number types. By default, this is an empty array. | | | `roundNumDec` | This input field is for the number of decimal places for rounding. The schema specifies that it must be an integer between `0` and `10`. By default, this value is `2`. | | `contextModel` | This is a schema that defines the output of the operation. It tells the Unqork platform what data to expect back so it can be used in subsequent steps of a workflow. | | ↳ | `result` | This field specifies that the operation will return an object with a single property named result, which is a number. | ## Implement the Average and Round Operation For this example, create a file called `exAvgAndRoundOperation.js`. In this file, create an operation that does the following: 1. Defines the data model for the operation. 2. Averages the operation’s input values and rounds them to the specified decimal value. 3. Defines the operation’s data structure for the returned value. 4. Defines the return values in an export object. Review the code example below for more information on each method: > [!NOTE] > The comments in the JavaScript below are written in the JSDoc format. For more information on JSDoc block tags (@param, @return), visit the JSDoc website: [https://jsdoc.app/](https://jsdoc.app/). ```javascript // Operation options class ExAvgAndRoundOptions {  // Flat array of numbers to average. Defaults to an empty array  numbers = []  // Integer decimals (0..10) to round the result. Defaults to 2  roundNumDec = 2 } // Operation model class ExAvgAndRoundOperation {  // MUST match the manifest "type" and the named export below  type = 'exAvgAndRound'  options = new ExAvgAndRoundOptions() } // Operation handler class ExAvgAndRoundHandler {  /**   * Execute the operation.   * @param {ExAvgAndRoundOperation} operation   * @param {*} api Operation API: { state: { resolveByKey, set, get }, events: { emit } }   * @returns {{ result: number }}   */  async execute(operation, api) {    // Get the operation options or default to an empty object    const opts = operation.options.config ?? {}    // Extract the numbers array from the options    const nums = opts.numbers    // Calculate the mean (average).    // The result is either 0 (if the array is empty) or the calculated mean of the numbers.    const mean = nums.length === 0 ? 0 : nums.reduce((acc, n) => acc + n, 0) / nums.length    // Round the mean to the specified number of decimals    // Javascript method toFixed returns a string so it needs to be coerced back to number    const decimals = opts.roundNumDec    const result = Number(mean.toFixed(decimals))    return { result }  } } // Context result (what the op returns into context) // If the operation handler does not return a result. This can be omitted. class ExAvgAndRoundContextResult {  result = 0 } // If the operation handler does not return a result. This can be omitted. class ExAvgAndRoundContext {  result = new ExAvgAndRoundContextResult() } // Export for the operation // Named export MUST match manifest "type" export const exAvgAndRound = {  model: async () => ExAvgAndRoundOperation,  handler: async () => ExAvgAndRoundHandler,  contextModel: async () => ExAvgAndRoundContext, // If the operation handler does not return a result. This can be omitted. } ``` ### Understanding `ExAvgAndRoundOptions` and `ExAvgAndRoundOperation` These classes define the data model for the operation's configuration. | Class | Description | | --- | --- | | `ExAvgAndRoundOptions` | Specifies the `numbers` and `roundNumDec` input parameters | | ↳ | `numbers` | An array of numbers to be averaged. It defaults to an empty array. | | | `roundNumDec` | An integer that specifies how many decimal places to round the final result to. Default is `2`. | | `ExAvgAndRoundOperation` | Defines the overall operation model, linking the options to the operation's type, which must match the export name. | ### Understanding `ExAvgAndRoundHandler` This is the core logic of the operation. The `execute` method runs when the operation is called. | Class | Description | | --- | --- | | `const opts = operation.options.config ?? {}` | Specifies the two input parameters: - `numbers`: An array of numbers to be averaged. It defaults to an empty array. For example, `"numbers":[2,4,8,5,23,7]` - `roundNumDec`: An integer that specifies how many decimal places to round the final result to. For example, `"roundNumDec":3` Default is `2`. | | `const mean =` | Calculates the average (mean) of the numbers provided in the `nums` array. If the array is empty, it returns `0` to prevent a division-by-zero error. | | `const result =` | Rounds the calculated `mean` to the number of decimal places specified by `decimals`. Since JavaScript's `toFixed()` method returns a string, it's converted back to a number. | | `return { result }` | This returns the final calculated and rounded value. | ### Understanding `ExAvgAndRoundContextResult` and `ExAvgAndRoundContext` These classes define the structure of the data the operation returns when it's completed. Doing so allows the result to be used in subsequent steps in an Unqork workflow. The `contextModel` is what enables other parts of the application to access the `result` value. ### Understanding the `exAvgAndRound` Export The final export object, `exAvgAndRound`, is the manifest that binds everything together. It tells the Unqork platform where to find the operation's data model (`model`), its logic (`handler`), and the structure of its return value (`contextModel`). ## Create the BYO Operation Package After the asset JavaScript and manifest.json files are complete, compress them into a BYO package. To compile a file using Terminal (Mac) or Command Prompt (Windows): 1. Using the command line prompt, navigate to the directory containing the `exAvgAndRoundOperation.js` and `manifest.json` files. For example, `cd C:\Users\UserName\Desktop\exAvgAndRoundOperation`. 2. Execute the following command: `tar -cvzf exAvgAndRoundOperation.tar.gz manifest.json exAvgAndRoundOperation.js`. ![Terminal command showing directory navigation and file compression in a coding environment.](https://cdn.us.document360.io/15548f06-2fba-434c-b0c1-5df88317dbd7/Images/Documentation/exRoundAvg-compress.webp?sv=2026-02-06&spr=https&st=2026-11-08T18%3A05%3A09Z&se=2026-11-08T19%3A05%3A09Z&sr=c&sp=r&sig=a24FthqT2tLQYbxbDe3IvNWT2QINSzbXh3jbniwiEoQ%3D) A static image displaying the Terminal and the tar.gz prompt. The `exAvgAndRoundOperation.js` file and `manifest.json` add to the `tar.gz` file in the same folder they're located in. > [!NOTE] > NOTE > > The steps above use the tar command and the following options: > > `-c`: Creates a new archive. > > `-z`: Compresses the archive using gzip. > > `-v`: Lists the files in the command line as they are added to archive. > > `-f`: Specifies the file name for the archived files. ## Upload the BYO Operation Package After creating the `exAvgAndRound.tar.gz` package, upload it to the Unqork Designer Platform using the Custom Assets Administration page: 1. From the UDesigner homepage, click **Administration**. 2. Under Assets, click **Custom Assets Administration**. 3. Click **+ Create New**. The Create Asset modal displays. 4. Under **File Upload**, click or drag the BYO Operation package from your local device to the upload window. 5. Click **Scan File**. The package details display. ![Form for creating an asset with name, version, and description fields displayed.](https://cdn.us.document360.io/15548f06-2fba-434c-b0c1-5df88317dbd7/Images/Documentation/exRoundAvg-createAsset.webp?sv=2026-02-06&spr=https&st=2026-11-08T18%3A05%3A09Z&se=2026-11-08T19%3A05%3A09Z&sr=c&sp=r&sig=a24FthqT2tLQYbxbDe3IvNWT2QINSzbXh3jbniwiEoQ%3D) 6. Click **Review Assets**. 7. Review the asset's details, including Components, Events, and Operations. 8. Click **Confirm & Create**. 9. Review the Library Usage Acknowledgment and Liability Agreement. Click the **☐ (checkbox)** to acknowledge the agreement. 10. Click **Create Asset**. The asset package displays in the Custom Assets’ **Packages** list. The package's individual assets display in the Assets tab's **Assets** list. The assets are now available in the Module Builder for all applications in the environment. ## Test the Average and Round Operation Now that the BYO Average and Round Operation is in the Unqork Designer Platform, test its functionality in the Module Builder and Express View. This example assumes you are configuring a Vega (2.0) module. For this example, enter the following functions: - **Average**: Verify that the operation averages the numbers array contained in the configuration. - **Round**: Verify the operation rounds to the decimal value specified in the configuration. ### Configure the Text Field Component Configure a [Text Field](/v1/docs/text-field-component) component to receive the operation’s results. 1. In the Module Builder, drag and drop a **Text Field** component onto your canvas. 2. In the **Property ID** field, enter `results`. 3. In the **Label Text** field, enter `Result`. 4. Click **Save** **Component**. ### Configure the Button Component Configure a [Button](/v1/docs/button-component) component to trigger the average and round operation. When end-users click on the button, an event is emitted and the Average and Round operation executes. 1. Drag and drop a **Button** component onto your canvas. 2. In the **Property ID** field, enter `btnCalculate`. 3. In the **Label Text** field, enter `Calculate`. 4. Navigate to the **Actions** settings. 5. Set **Action Type** to **Event**. #### Configure the btnCalculate Events and Operations While remaining in the Button component’s configuration drawer, open the [Operations Builder](/v1/docs/operations-builder). Set up an **On Click** event to trigger the BYO `exAvgAndRound` operation. Set up the operation to average a set of values, then round the decimal to the third. For this example, average the following values `2`, `4`, `8`, `5`, `23`, `7`, and a rounding value of `3`. 1. Next to Events & Operations, click **Edit**. 2. From the **Select an Event** drop-down, enter or select **On Click**. 3. Click **Add**. 4. Click **+ Add Operation**. 5. From the **Operation Type** drop-down, select **Example Average and Round**. 6. In the **Operation Name** field, enter `exAvgAndRound`. 7. In the **Config** field, enter `{"numbers":[2,4,8,5,23,7],"roundNumDec":3}`. 8. Click **Save**. 9. Click **+ Add Operation**. 10. From the **Operation** Type drop-down, select **Set Property**. 11. In the **Target Key** field, enter `results`. This sends the operation’s output to the `results` Text Field component. 12. In the **Property** field, enter `value`. 13. In the **Value** field, enter `$operationContext.exAvgAndRound.result.result`. This dynamic value becomes the final value after the operation executes its calculations. 14. Click **Save**. 15. Click **Close**. 16. Click **Save Component**. 17. **Save** your module. Your completed module looks like the following: ![User interface displaying components, calculation button, and result field in an application.](https://cdn.us.document360.io/15548f06-2fba-434c-b0c1-5df88317dbd7/Images/Documentation/moduleBuilder.webp?sv=2026-02-06&spr=https&st=2026-11-08T18%3A05%3A09Z&se=2026-11-08T19%3A05%3A09Z&sr=c&sp=r&sig=a24FthqT2tLQYbxbDe3IvNWT2QINSzbXh3jbniwiEoQ%3D) ### Preview and Test the Module Lastly, test your configuration and verify that the operation outputs the correct value. The decimal should round to the thousandths place. 1. **Preview** your module in Express View. 2. Click the **Calculate** button. 3. In the **Result** field, verify that the return value is `8.167`. [expressView.webm](https://cdn.us.document360.io/15548f06-2fba-434c-b0c1-5df88317dbd7/Images/Documentation/expressView.webm?sv=2026-02-06&spr=https&st=2026-11-08T18%3A05%3A09Z&se=2026-11-08T19%3A05%3A09Z&sr=c&sp=r&sig=a24FthqT2tLQYbxbDe3IvNWT2QINSzbXh3jbniwiEoQ%3D) If the custom asset functions as intended, then it’s ready to be used by Creators in the development of Unqork applications. If the Custom Operation generates errors or is missing features: 1. Create a copy of the current package and its contents. 2. Update the package's required files. 3. Change the package's version number. 4. Update the package by referring to the Package Management section in our [BYO Best Practices](/v1/docs/best-practices-bring-your-own-byo-framework#best-practices-bring-your-own-byo-framework) article. ## Related - [Bring Your Own (BYO) General FAQ (Frequently Asked Questions)](/bring-your-own-byo-general-faq-frequently-asked-questions.md) - [Bring Your Own (BYO): Custom Operations](/bring-your-own-byo-custom-operations.md) - [Introduction to the Bring Your Own (BYO) Framework](/introduction-to-the-bring-your-own-byo-framework.md)