diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e007e97 --- /dev/null +++ b/.env.example @@ -0,0 +1,14 @@ +# Hold some settings that are used by the build + +# This is the profile name to use when exporting +ACARS_PROFILE_NAME= + +# This is the path to the configuration root +ACARS_CONFIG_PATH=$HOME/Documents/vmsacars + +# This is the path to where the scripts, etc are stored +# It's used in the live-watch +ACARS_SCRIPTS_PATH=$HOME/Documents/vmsacars/data/$ACARS_PROFILE_NAME/config + +# Name of the distribution zip file that's created +ACARS_DIST_ZIP=dist.zip diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fb5e939 --- /dev/null +++ b/.gitignore @@ -0,0 +1,68 @@ +.env +node_modules/ +.node_modules/ +built/* +dist/ +tests/cases/rwc/* +tests/cases/perf/* +!tests/cases/webharness/compilerToString.js +test-args.txt +~*.docx +\#*\# +.\#* +tests/baselines/local/* +tests/baselines/local.old/* +tests/services/baselines/local/* +tests/baselines/prototyping/local/* +tests/baselines/rwc/* +tests/baselines/reference/projectOutput/* +tests/baselines/local/projectOutput/* +tests/baselines/reference/testresults.tap +tests/baselines/symlinks/* +tests/services/baselines/prototyping/local/* +tests/services/browser/typescriptServices.js +src/harness/*.js +src/compiler/diagnosticInformationMap.generated.ts +src/compiler/diagnosticMessages.generated.json +src/parser/diagnosticInformationMap.generated.ts +src/parser/diagnosticMessages.generated.json +rwc-report.html +*.swp +build.json +*.actual +tests/webTestServer.js +tests/webTestServer.js.map +tests/webhost/*.d.ts +tests/webhost/webtsc.js +tests/cases/**/*.js +tests/cases/**/*.js.map +*.config +scripts/eslint/built/ +scripts/debug.bat +scripts/run.bat +scripts/**/*.js +scripts/**/*.js.map +coverage/ +internal/ +**/.DS_Store +.settings +**/.vs +**/.vscode/* +!**/.vscode/tasks.json +!**/.vscode/settings.template.json +!**/.vscode/launch.template.json +!**/.vscode/extensions.json +!tests/cases/projects/projectOption/**/node_modules +!tests/cases/projects/NodeModulesSearch/**/* +!tests/baselines/reference/project/nodeModules*/**/* +.idea +yarn.lock +yarn-error.log +.parallelperf.* +tests/baselines/reference/dt +.failed-tests +TEST-results.xml +package-lock.json +.eslintcache +*v8.log +/lib/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c18ad98 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,16 @@ +This file explains how Visual Studio created the project. + +The following tools were used to generate this project: +- TypeScript Compiler (tsc) + +The following steps were used to generate this project: +- Create project file (`Content.Source.esproj`). +- Create `launch.json` to enable debugging. +- Create `nuget.config` to specify location of the JavaScript Project System SDK (which is used in the first line in `Content.Source.esproj`). +- Install npm packages and create `tsconfig.json`: `npm init && npm i --save-dev eslint typescript @types/node @typescript-eslint/eslint-plugin @typescript-eslint/parser && npx tsc --init --sourceMap true`. +- Create `app.ts`. +- Update `package.json` entry point. +- Update TypeScript build scripts in `package.json`. +- Create `.eslintrc.json` to enable linting. +- Add project to solution. +- Write this file. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6286474 --- /dev/null +++ b/README.md @@ -0,0 +1,288 @@ +# vmsACARS Plugin Development Kit (PDK) + +## Overview + +The plugins and scripts for vmsACARS are written in Typescript, and then transpiled to JS. +Typescript ensures that the interfaces required are following, and that the proper things +are returned so ACARS can run them. While Typescript isn't required, it's best to use it to +ensure proper values are passed - especially around enums. + +--- + +# Setup + +## Required: + +- nodejs/npm +- Typescript +- Gulp + +Run: + +```shell +npm install +``` + +### Customizing using the `.env` file: + +Next, copy the `.env.default` to `.env`. Then edit this file to change the profile name. + +The available options: + +- `ACARS_PROFILE_NAME` - The default profile to use for testing +- `ACARS_CONFIG_PATH` - The default usually works, but you can change this to the path where you put ACARS, if you did a + local install +- `ACARS_SCRIPTS_PATH` - Uses the `ACARS_PROFILE_NAME` to build the path to where the scripts should be sent after a + build +- `ACARS_DIST_ZIP` - The distribution filename + +--- + +### Commands + +Then there are multiple commands you can use: + +#### To run a build: + +This creates a `dist` directory, with all of the JS files in it + +```shell +npm run build +``` + +This doesn't copy it anywhere, just runs a compile and build + +### Create a distribution file + +This creates a `dist.zip` (you can rename it in the `.env` file) after running a compile. +You can modify the `gulpfile.mjs` to include other files in the `dist/` directory - this +directory is simply zipped and placed into the `dist/` directory. You can then configure +Github Actions to then upload this zip somewhere for ACARS to download. + +#### Automatically build and copy to ACARS + +This will setup a watch, and then automatically transpile and then copy the contents of the `dist` folder +into the `ACARS_PROFILE_PATH` directory that's defined in the `.env` file. + +```shell +npm run dev +``` + +It's recommended to run this *after* you've started ACARS, or, in the ACARS configuration, disable the +remote-download of configs: + +> TODO: Guide on how to disable remote config downloading + + +--- + +# Development Documentation + +There are several core files/interfaces that are included: + +### `src/global.d.ts` + +This describes the globally available functions, including the logging methods available through `console` and +`Acars`. + +### `src/types.d.ts` + +This contains all of the base types: + +- `Pirep` - data that's available about a PIREP, and it's associated interfaces (`Airport`, `Runway`, etc) +- `Telemetry` - telemetry information that's come out of the simulator +- `User` - information about the current user + +It also includes other detailed type information, for example `Length`, so you can retrieve that type of information. + +## Aircraft Rules: + +Aircraft rules are required to inherit the `AircraftConfig` abstract class. An example class would look like: + +```typescript +import { AircraftConfigSimType, AircraftFeature, FeatureType } from '../defs' +// Additional mports are left out for now + +export default class FenixA320 extends AircraftConfig { + meta: Meta = { + id: 'fenix_a320', + name: 'Fenix A320', + sim: AircraftConfigSimType.MsFs, + enabled: true, + priority: 2, + } + + features: FeatureAddresses = { + // Aircraft feature + [AircraftFeature.BeaconLights]: { + 'lvar name': FeatureType.Int, + }, + } + + flapNames: FlapNames = { + 0: 'UP', + 1: 'CONF 1', + 2: 'CONF 1+F', + 3: 'CONF 2', + 4: 'CONF 3', + 5: 'FULL', + } + + match(title: string, icao: string, config_path: string): boolean { + // Check the aircraft title and return true/false if this matches + } + + beaconLights(lvar_value: number): FeatureState { + // Check the lvar_value if the + } +} +``` + +The configuration is a class which has a few different components. + +1. `meta`, which gives some general information about the configuration: + - `name` - a name for this script + - `sim` - The simulator it's for + - `AircraftConfigSimType.XPlane` + - `AircraftConfigSimType.Fsuipc` + - `AircraftConfigSimType.MsFs` + - `enabled` + - `priority` - from 1 (lowest) to 10 (highest). If there are multiple rules which match this, then which one takes + priority. All the built-in rules are at a priority 1, and aircraft specifics rules are priority 2. I recommend + using a priority of 3 or higher. More on this below + +2. `features` - this is the type `FeatureAddresses` - see `defs.ts` for the definitions + - MSFS - the lookups you enter are LVars + - X-Plane - the looks ups are via datarefs + - FSUIPC - the lookups are offsets +3. `match()` + - This needs to return a boolean + - A method (`match()`) which passes some information about the starting aircraft + - For MSFS, it's the aircraft ICAO + - For FSX/P3d, the value looked at is the aircraft title field, offset `0x3D00` + - For X-Plane, the value looked at is `sim/aircraft/view/acf_descrip` + - This method can be used to determine if this rule should match +4. Methods for the different features (see below) + - The maps - a group of datarefs or offsets which constitute that feature being "on" or "enabled" + +In the above example, for the Fenix A320, the landing lights are controlled by two datarefs, both of which the +values need to be 1 or 2 for the landing lights to be considered "on". + +### Features + +Features are essentially stored in a dictionary of dictionaries, of type `FeatureAddresses`: + +```typescript +features: FeatureAddresses = { + // Aircraft feature + [AircraftFeature.BeaconLights]: { + 'lvar name': FeatureType.Int, + }, +} +``` + +In the above example: + +- `AircraftFeature.BeaconLights` is an enum value of the feature type. It's put in `[]` because it's a variable name +- It's set to an object, where the keys are the lookup address or lvar. +- `FeatureType.Int` - is the type of value that's returned. + +The different features available are: + +- beaconLights +- landingLights +- logoLights +- navigationLights +- strobeLights +- taxiLights +- wingLights +- flaps + +The different features contain how to look up the value, and the type. You can have multiple variables to be +read and looked at for a feature. Each feature then corresponds to a method which is called to return if +that feature is on or off. That method will have the equivalent number of arguments for each data reference + +Example: + +```typescript + +export default class Example extends AircraftConfig { + features: FeatureAddresses = { + // Aircraft feature + [AircraftFeature.BeaconLights]: { + 'sample/dataref/1': FeatureType.Bool, + 'sample/dataref/2': FeatureType.Bool, + }, + } + + beaconLights(dataref_1: boolean, dataref_2: boolean): FeatureState { + if (dataref_1 && dataref_2) { + return true; + } + + return false; + } +} +``` + +### Ignoring Features + +To ignore a feature in the rules (for example, if a feature doesn't work properly), set the feature to false: + +```typescript +import { AircraftFeature } from './defs' + +features: FeatureAddresses = { + // Aircraft feature + [AircraftFeature.BeaconLights]: { + 'lvar name': FeatureType.Int, + }, + [AircraftFeature.LandingLights]: false, +} +``` + +### Mixed priorities + +If there are two scripts which match a particular aircraft, and a feature is omitted, it will use the lower priority +one in place. For example: + +```typescript +import { FeatureAddresses } from './aircraft' + +export default class Example extends AircraftConfig { + meta: Meta = { + // ... + priority: 1 + } + + features: FeatureAddresses = { + [AircraftFeature.BeaconLights]: { + 'sample/dataref/1': FeatureType.Bool, + 'sample/dataref/2': FeatureType.Bool, + }, + [AircraftFeature.LandingLights]: { + 'sample/landing/light/1': FeatureType.Bool, + 'sample/landing/light/2': FeatureType.Bool, + }, + } +} + +export default class ExampleOverride { + meta: Meta = { + // ... + priority: 10 + } + + features: FeatureAddresses = { + [AircraftFeature.LandingLights]: { + 'override/landing/light/1': FeatureType.Bool, + 'override/landing/light/2': FeatureType.Bool, + }, + } +} +``` + +In this case, the lookups used for the rules will be: + +- beaconLights - `sample/dataref/1|2` +- landingLights - `override/landing/light/1|2` diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..84a48f2 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,26 @@ +import typescriptEslint from '@typescript-eslint/eslint-plugin' +import stylisticEslintPluginJs from '@stylistic/eslint-plugin-js' +import tsParser from '@typescript-eslint/parser' + +export default [ + { + files: ["src/*.ts"], + plugins: { + '@typescript-eslint': typescriptEslint, + '@stylistic/js': stylisticEslintPluginJs, + }, + + languageOptions: { + parser: tsParser, + ecmaVersion: 'latest', + sourceType: 'module', + parserOptions: { + project: 'tsconfig.json', + }, + }, + + rules: { + "indent": ['error', 2], + "no-undef": "warn" + }, + }] diff --git a/gulpfile.mjs b/gulpfile.mjs new file mode 100644 index 0000000..ed79d5e --- /dev/null +++ b/gulpfile.mjs @@ -0,0 +1,175 @@ +import dotconfig from '@dotenvx/dotenvx' +import { deleteAsync } from 'del' +import { dest, series, src, watch } from 'gulp' +import eslint from 'gulp-eslint-new' +import rsync from 'gulp-rsync' +import ts from 'gulp-typescript' +import merge2 from 'merge2' + +dotconfig.config() + + +/** + * Different paths we use... + */ +const paths = { + src: 'src', + dest: 'dist', + + /** + * ACARS scripts/config directory. This, by default, points to the home directory + * But you can change this to point to a local directory + */ + acars: process.env.ACARS_SCRIPTS_PATH, +} + +/** + * Configure the ts transpilation + */ +const tsProject = ts.createProject('tsconfig.json') + +/** + * Build the project, copy the appropriate files over + */ +export async function build() { + return merge2( + // Build the TS files + tsProject.src() + .pipe(eslint()) + .pipe(eslint.failAfterError()) + .pipe(tsProject()) + .js.pipe(dest(paths.dest)), + + + // Copy the package json file over + src([paths.src + '/package.json']).pipe(dest(paths.dest)), + ) +} + +/** + * Copy the files from dist into ACARS_SCRIPTS_PATH + * + * @returns {Promise} + */ +export async function copy() { + console.log(`Copying files to ${paths.acars}`) + return src([paths.dest + '/**/*']).pipe(dest(paths.acars)) +} + +/** + * Build a distribution zip file, which can be easily uploaded + */ +export function dist() { + +} + +/** + * Watch the src folder for updates, compile them and then copy them + * to the config directory. ACARS should auto-reload + */ +export async function testing() { + watch('src/', { + ignoreInitial: false, + delay: 500, + }, series(build, copy)) +} + +/** + * Watch the files and distribute them out + */ +export function watchFiles() { + watch('src/', build) +} + +export { watchFiles as watch } + +/** + * Clean up the /dest directory + */ +export async function clean() { + await deleteAsync([paths.dest]) +} + +/** + * Internal task to copy files over to the PDK distribution + * @returns {Promise} + */ +export async function pdk() { + const source = '.' + const pdk_path = process.env.PDK_DISTRIBUTION_DIRECTORY + + const files = [ + `${source}/**/*`, + `!${source}/bin`, + `!${source}/dist`, + `!${source}/node_modules`, + `!${source}/obj`, + `!${source}/.env`, + `!${source}/nuget.config`, + `!${source}/Content.Source.*`, + ] + + src([source]).pipe(rsync({ + root: '.', + destination: pdk_path, + recursive: true, + exclude: [ + `${source}/bin`, + `${source}/dist`, + `${source}/node_modules`, + `${source}/obj`, + `${source}/.env`, + `${source}/nuget.config`, + `${source}/Content.Source.*`, + ], + })) + + /*const deleteFiles = [ + `${pdk_path}/dist`, + `${pdk_path}/node_modules`, + `!${pdk_path}/src/aircraft/Example.ts`, + `!${pdk_path}/src/rules/example.ts`, + ] + + await deleteAsync(deleteFiles, { + force: true, + })*/ +} + +/** + * The default action + */ +export default build + +/** + * Get the default profile name + * + * @returns {*} + */ +/*async function getDefaultProfilePath() { + if (profileName === null || profileName === '') { + const f = await fs.promises.readFile(`${paths.acars}/settings.json`) + const settings = JSON.parse(f) + profileName = settings.Profile + console.log('No profile name set, looked in settings and used ' + profileName) + } + + // Read all of the profiles + let dirent + const dir = await fs.promises.opendir(`${paths.acars}/profiles`) + for await (const dirent of dir) { + const pf = await fs.promises.readFile(`${dirent.parentPath}/${dirent.name}`) + if (pf === null) { + continue + } + + const profile = JSON.parse(pf) + console.log(profile) + + if (profile.Name === profileName) { + return `${paths.acars}/data/${profile.Domain}/config/` + } + } + + return null +}*/ diff --git a/package.json b/package.json new file mode 100644 index 0000000..e8afe20 --- /dev/null +++ b/package.json @@ -0,0 +1,44 @@ +{ + "name": "acars-pdk", + "version": "1.0.0", + "private": true, + "description": "", + "main": "app.js", + "scripts": { + "build": "gulp build", + "copy": "gulp copy", + "buildcopy": "cross-env ACARS_SCRIPTS_PATH=../Content/config/default gulp build copy", + "tsbuild": "tsc --build", + "lint": "eslint --fix", + "format": "prettier --ignore-path .gitignore --write \"src/**/*.+(js|ts|json)\"", + "testing": "gulp testing", + "watch": "gulp watch" + }, + "keywords": [], + "author": "Nabeel Shahzad ", + "license": "ISC", + "devDependencies": { + "@dotenvx/dotenvx": "^1.13.2", + "@stylistic/eslint-plugin-js": "^2.7.2", + "@types/node": "^22.5.1", + "@typescript-eslint/eslint-plugin": "^8.3.0", + "@typescript-eslint/parser": "^8.3.0", + "cross-env": "^7.0.3", + "del": "^7.1.0", + "eslint": "^9.9.1", + "gulp": "^5.0.0", + "gulp-eslint-new": "^2.3.0", + "gulp-rsync": "^0.1.0", + "gulp-typescript": "^6.0.0-alpha.1", + "gulp-util": "^3.0.8", + "merge2": "^1.4.1", + "prettier": "^3.3.3", + "typescript": "^5.5.4" + }, + "prettier": { + "trailingComma": "all", + "tabWidth": 2, + "semi": false, + "singleQuote": true + } +} diff --git a/src/aircraft/example.ts b/src/aircraft/example.ts new file mode 100644 index 0000000..8bdc101 --- /dev/null +++ b/src/aircraft/example.ts @@ -0,0 +1,44 @@ +import { AircraftConfigSimType, AircraftFeature, FeatureType } from '../defs' +import { + AircraftConfig, + FeatureAddresses, + FeatureState, + FlapNames, + Meta, +} from '../interface/aircraft' + +export default class Example extends AircraftConfig { + meta: Meta = { + id: 'example', + name: 'example', + sim: AircraftConfigSimType.MsFs, + enabled: true, + priority: 2, + } + + features: FeatureAddresses = { + [AircraftFeature.BeaconLights]: { + example_lvar: FeatureType.Int, + }, + } + + flapNames: FlapNames = { + 0: 'UP', + 1: 'CONF 1', + } + + /** + * + * @param {string} title The title of the aircraft, lowercased + * @param {string=} icao The ICAO of the aircraft. Might not be available + * @param {string=} config_path Path to the aircraft config. Might not be there + * @return {boolean} + */ + match(title: string, icao: string, config_path: string): boolean { + return ['example', 'aircraft'].every(title.includes) + } + + beaconLights(): FeatureState { + return null + } +} diff --git a/src/defs.ts b/src/defs.ts new file mode 100644 index 0000000..07b1ea7 --- /dev/null +++ b/src/defs.ts @@ -0,0 +1,141 @@ +export enum GateType { + None = 0, + Ramp_GA = 1, + Ramp_GA_Small = 2, + Ramp_GA_Medium = 3, + Ramp_GA_Large = 4, + Ramp_Cargo = 5, + Ramp_Military_Cargo = 6, + Ramp_Military_Combat = 7, + Gate_Small = 8, + Gate_Medium = 9, + Gate_Heavy = 10, + Dock_GA = 11, + Fuel = 12, + Vehicles = 13, + Ramp_GA_Extra = 14, + Gate_Extra = 15, + Jetway = 16, +} +export enum AircraftType { + Airliner = 0, + Cargo = 1, + GeneralAviation = 2, + Helicopter = 3, +} +export enum EngineType { + Piston = 0, + Jet = 1, + None = 2, + Helo = 3, + Rocket = 4, + Turboprop = 5, +} +export enum SimType { + None = 0, + Prepar3D = 1, + Xplane = 2, + FlightSimulator = 3, + Fsx = 4, + Fs9 = 5, +} +export enum Surface { + Concrete = 0, + Grass = 1, + Water = 2, + Unknown3 = 3, + Asphalt = 4, + Unknown5 = 5, + Unknown6 = 6, + Clay = 7, + Snow = 8, + Ice = 9, + Unknown10 = 10, + Unknown11 = 11, + Dirt = 12, + Coral = 13, + Gravel = 14, + Oil_treated = 15, + Mats = 16, + Bituminous = 17, + Brick = 18, + Macadam = 19, + Planks = 20, + Sand = 21, + Shale = 22, + Tarmac = 23, + Unknown = 99999, +} +export enum FareType { + /** Primarily a passenger flight */ + Passenger = 0, + /** Primarily a cargo flight */ + Cargo = 1, +} +/** The different types of flight plans that can be parsed */ +export enum FlightPlanType { + /** Was entered using ACARS */ + VmsAcars = 0, + /** Flight plan was created in Prepar3d/FSX/FS9 */ + Prepar3d = 1, + /** Flight plan was created using X-Plane */ + Xplane = 2, + /** Flightplan was created using SimBrief */ + SimBrief = 3, + /** Flightplan was create using Flight Simulator */ + MsFs = 4, +} +/** The PIREP states - these match the phase */ +export enum PirepState { + /** Internal only. Rules are not parsed in this state */ + NotRunning = 0, + /** Internal only. Rules are not parsed in this state */ + Initialized = 1, + /** Internal only. Rules are not parsed in this state */ + Boarding = 2, + /** When parking brake is released. Pushback may get skipped over and go directly into taxi out */ + Pushback = 3, + TaxiOut = 4, + Takeoff = 5, + Enroute = 6, + Approach = 7, + Final = 8, + Landed = 9, + TaxiIn = 10, + Arrived = 11, + /** Internal only. Rules are not parsed in this state */ + Cancelled = 12, + /** Internal only. Rules are not parsed in this state */ + Filed = 13, + /** Internal only. Rules are not parsed in this state */ + Paused = 14, + OnBlock = 15, +} +export enum AircraftFeature { + BeaconLights = 0, + LandingLights = 1, + LogoLight = 2, + NavigationLights = 3, + StrobeLights = 4, + TaxiLight = 5, + WingLights = 6, + Flaps = 7, + APU = 8, +} +/** The simtype for the rule file */ +export enum AircraftConfigSimType { + MsFs = 0, + XPlane = 1, + Fsuipc = 2, +} +/** The type of the dataref */ +export enum FeatureType { + Bool = 0, + Int = 1, + /** A single number (double, float) value */ + Number = 2, + /** An array of integers */ + IntArray = 3, + /** An array of numbers */ + NumberArray = 4, +} diff --git a/src/interface/aircraft.ts b/src/interface/aircraft.ts new file mode 100644 index 0000000..5c616a0 --- /dev/null +++ b/src/interface/aircraft.ts @@ -0,0 +1,138 @@ +import { AircraftConfigSimType, AircraftFeature, FeatureType } from '../defs' + +/** + * The flap names dictionary + */ +export type FlapNames = { [key: number]: string } + +/** + * A feature can be on/off (true/false), or null in the case of it being ignored + */ +export type FeatureState = boolean | null | undefined + +/** + * How the features are definedw + */ +export type FeatureAddresses = { + [key in AircraftFeature]?: boolean | { [key: string]: FeatureType } +} + +/** + * The allowed list of priorities. Default rules are at a 1, and then + * the build-in aircraft are a 2. Extras should be higher. I recommend 5 or 6. + */ +export type Priority = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 + +/** + * Meta information about the aircraft config scripts + */ +export interface Meta { + /** + * A unique ID for the rule. Should be short, no spaces. + * If you want it truly unique: https://shortunique.id + */ + id: string + + /** + * Name of the rule + */ + name: string + + /** + * Enabled or not + */ + enabled: boolean + + /** + * The priority of this mapping, with 1 being the lowest + * and 10 being the highest + */ + priority: Priority + + /** + * Sim name - e.g, AircraftSimType.MSFS + */ + sim: AircraftConfigSimType + + /** + * Author of this config file + */ + author?: string +} + +export abstract class AircraftConfig { + /** + * The information about the the config rules + */ + abstract meta: Meta + + /** + * Aircraft features and the lookup keys. E.g: + * + * AircraftFeature.BeaconLights: { + * "some/dref/path": "type" + * } + */ + abstract features: FeatureAddresses + + /** + * Store all the flap names here + */ + flapNames: FlapNames = {} + + /** + * See if the title, icao or config_path match with what the simulator + * is saying. All of the values are passed in already lower-cased. + * Return true or false + * + * The ICAO and config_path may not be available in some sims. + * + * @param {string} title The title of the aircraft + * @param {string=} icao The ICAO of the aircraft. Might not be available + * @param {string=} config_path Path to the aircraft config. Might not be there + * @return {boolean} + */ + abstract match(title: string, icao: string, config_path: string): boolean + + /** + * Get the right text for the flaps. Default implementation + * + * @param {int} value + * @returns {string} + */ + flaps(value: number): string | number { + return this.flapNames[value] || value + } + + /** + * Provide an implementation for but, by default, it's null + * @param args + */ + beaconLights(...args: any): FeatureState { + return null + } + + landingLights(...args: any): FeatureState { + return null + } + + logoLights(...args: any): FeatureState { + return null + } + + navigationLights(...args: any): FeatureState { + return null + } + + strobeLights(...args: any): FeatureState { + return null + } + + taxiLights(...args: any): FeatureState { + return null + } + + wingLights(...args: any): FeatureState { + return null + } +} diff --git a/src/package.json b/src/package.json new file mode 100644 index 0000000..3db052b --- /dev/null +++ b/src/package.json @@ -0,0 +1,19 @@ +{ + "name": "plugin", + "version": "1.0.0", + "type": "module", + "config": { + "aircraft": { + "path": "aircraft" + }, + "rules": { + "path": "rules" + }, + "scripts": { + "path": "scripts" + }, + "sounds": { + "path": "sounds" + } + } +} diff --git a/src/rules/example.ts b/src/rules/example.ts new file mode 100644 index 0000000..d81b4cc --- /dev/null +++ b/src/rules/example.ts @@ -0,0 +1,34 @@ +/** + * Determine if the beacon lights are on while the aircraft is in motio + */ +import { Meta, Rule, RuleValue } from '../types/rule' +import { Pirep, Telemetry } from '../types/types' + +export default class ExampleRule implements Rule { + meta: Meta = { + id: 'ExampleRule', + name: 'An Example Rule', + enabled: true, + message: 'A example rule!', + states: [], + repeatable: false, // set from remote later + cooldown: 60, // set from remote later + max_count: 3, // set from remote later + points: -1, // set from remote later + delay_time: 5000, + } + + violated(pirep: Pirep, data: Telemetry, previousData?: Telemetry): RuleValue { + return Acars.ViolatedAfterDelay( + this.meta.name, + this.meta.delay_time, + () => { + if (data.onGround) { + return [false, 0] + } + + return [true] + }, + ) + } +} diff --git a/src/types/global.d.ts b/src/types/global.d.ts new file mode 100644 index 0000000..de5c500 --- /dev/null +++ b/src/types/global.d.ts @@ -0,0 +1,105 @@ +import { RuleValue } from './rule' +import { AircraftFeature } from '../defs' + +declare global { + namespace console { + /** + * Log this item out only once + * + * @param args + */ + function once(...args: any[]): void {} + /** + * @param args + */ + function log(...args: any[]): void {} + /** A debug message */ + function debug(...args: any[]): void {} + /** A debug message */ + function error(...args: any[]): void {} + /** A debug message */ + function trace(...args: any[]): void {} + } +} + +declare global { + namespace Acars { + /** + * Whether or not the given feature is enabled for the aircraft, depending on the + * aircraft config that was read and parsed + */ + function IsFeatureEnabled(feature: AircraftFeature): boolean {} + /** Get something from storage */ + function Get(key: string): string {} + /** Save something to storage */ + function Set(key: string, value?: string): void {} + /** + * @param key + * @param value + */ + function SetPirepField(key: string, value: string): void {} + /** Add something to the PIREP log */ + function AddPirepLog(message: string): void {} + /** + * @param name + */ + function StartTimer(name: string): void {} + /** + * The elapsed time, in milliseconds + * + * @param name + */ + function TimeElapsed(name: string): number {} + /** + * If the given amount of time has elapsed or not + * + * @param name The name of the timer + * @param delay In milliseconds + */ + function IsTimeElapsed(name: string, delay: number): boolean {} + /** + * Stops and resets the given timer + * + * @param name + */ + function StopTimer(name: string): void {} + /** + * Determine if the rule has been violated, but only after a delay. Pass in a callback method, + * where the rule evaluation takes place. This method is evaluated often (many times a second), + * and if at any point before the timeout is hit, it will stop and reset. + * + * @param name The name to use to track this internally + * @param timeout Amount of time, in milliseconds + * @param callback Needs to return an array matching the RuleValue type + */ + function ViolatedAfterDelay( + name: string, + timeout: number, + callback: () => RuleValue, + ): RuleValue {} + /** + * Determine if a number is within a percentage of a level + * + * @param value + * @param level + * @param percent + */ + function NumberWithinPercent( + value: number, + level: number, + percent: number, + ): boolean {} + /** + * Whether a number is a certain percent over the level + * + * @param value + * @param level + * @param percent + */ + function NumberOverPercent( + value: number, + level: number, + percent: number, + ): boolean {} + } +} diff --git a/src/types/rule.d.ts b/src/types/rule.d.ts new file mode 100644 index 0000000..51dc329 --- /dev/null +++ b/src/types/rule.d.ts @@ -0,0 +1,98 @@ +/** + * THIS FILE IS GENERATED BY HAND! + */ +import { PirepState } from '../defs' +import type { Pirep, Telemetry } from '../types' + +/** + * The return value from evaluating if a rule has been broken or not + * Return it as a tuple. + * + * If a rule is passing/hasn't been violated: + * return [false] + * + * If a rule has been violated: + * + * return [true, points, message] + * + * points and message are optional - if omitted, they're pulled from + * the 'meta' block + */ +export type RuleValue = [boolean, number?, string?] + +/** + * + */ +export interface Rule { + meta: Meta + + /** + * Run the evaluation data + * + * @param pirep + * @param data + * @param previousData + */ + violated(pirep: Pirep, data: Telemetry, previousData?: Telemetry): RuleValue + + /** + * Optional callback that runs after the evaluation is complete and the + * rule was violated + * + * @param points The number of points that were deducted + * @param count How many times this was violated + * @param pirep + * @param data + */ + completed?(points: int, count: int, pirep: Pirep, data: Telemetry) +} + +/** + * + */ +export interface Meta { + /** + * A unique ID for this rule + */ + id: string + + /** + * A short name of the rule + */ + name: string + + /** + * If this rule is enabled or not + */ + enabled: boolean + + /** + * A detailed error message about the + */ + message: string + + /** + * General states this is active in + */ + states?: PirepState[] + + /** + * After being violated once, can it be violated again? + */ + repeatable?: boolean + + /** + * The amount of time, in seconds, between violations + */ + cooldown?: number + + /** + * The maximum number of violations + */ + max_count?: number + + /** + * This just allows any other properties + */ + [others: string]: any +} diff --git a/src/types/types.d.ts b/src/types/types.d.ts new file mode 100644 index 0000000..fc787dd --- /dev/null +++ b/src/types/types.d.ts @@ -0,0 +1,761 @@ +import { EngineType } from '../defs' +import { AircraftType } from '../defs' +import { FlightPlanType } from '../defs' +import { GateType } from '../defs' +import { SimType } from '../defs' +import { PirepState } from '../defs' +import { Surface } from '../defs' + +/** Point-in-time telemetry from the aircraft */ +export interface Telemetry { + /** If all of the engines are running */ + allEnginesRunning: boolean + /** If any engines are running */ + anyEnginesRunning: boolean + /** The runway the user is approaching. This means they're within a 20ft distance */ + approachingRunway?: Runway + /** Aircraft bank, +/- */ + bank: number + /** Boolean for lights */ + beaconLights: boolean + /** The date/time in the sim */ + dateTime: DateTimeOffset + /** The date/time on the client machine */ + dateTimeSystem: DateTimeOffset + /** The number of engines */ + engineCount: number + /** Array with the fuel flow values for an engine */ + engineFuelFlow: MassFlow[] + /** The total N1 RPM */ + engineMaxRpm: RotationalSpeed + engineN2Average: number + /** Array with the N2 values for an engine */ + engineN2Percent: number[] + /** Engine RPM */ + engineRpm: RotationalSpeed + /** The type of engine */ + engineType: EngineType + /** Flap position, from 0 */ + flaps: number + /** The current fuel quantity (weight) */ + fuelQuantity: Mass + /** True if the gear is up */ + gearUp: boolean + /** The current g-force */ + gForce: number + /** The g-force at touchdown */ + gForceTouchDown: number + /** Altitude of plane above the ground. Has units, like .Feet/.Meters */ + groundAltitude: Length + /** Has units, like .Knots */ + groundSpeed: Speed + /** The true heading */ + heading: number + /** The magnetic heading */ + headingMagnetic: number + /** Indicated airspeed. Has units, like .Knots */ + indicatedAirspeed: Speed + /** Boolean for lights */ + landingLights: boolean + location: Coords + /** Boolean for lights */ + logoLight: boolean + /** Magnetic variation */ + magVar: number + /** The total N1 percent */ + n1Percent: number + /** Boolean for lights */ + navigationLights: boolean + /** If the aircraft is on the ground */ + onGround: boolean + /** If the overspeed warning is on in the sim */ + overspeedWarning: boolean + /** If the parking brake is on */ + parkBrake: boolean + /** Is the sim paused? */ + paused: boolean + /** Weight of the payload */ + payloadWeight: Mass + /** Aircraft bank, +/- */ + pitch: number + /** Altitude of plane above MSL. Has units, like .Feet/.Meters */ + planeAltitude: Length + /** Are they in a replay? */ + replay: boolean + runway?: Runway + /** The current simulation rate */ + simRate: number + /** If the slew is enabled */ + slewActive: boolean + /** If the stall warning is on in the sim */ + stallWarning: boolean + /** Boolean for lights */ + strobeLights: boolean + /** Boolean for lights */ + taxiLights: boolean + /** Average throttle percent */ + throttlePct: number + /** Array with the throttle position for an engine */ + throttles: number[] + /** Total weight of the aircraft */ + totalWeight: Mass + /** Transponder code */ + transponderCode: number + /** If unlimited fuel is enabled */ + unlimitedFuel: boolean + /** Their vertical speed (+/-). Has units - FeetPerMinute */ + verticalSpeed: Speed + /** The vertical speed at touchdown */ + verticalSpeedTouchdown: Speed + /** Boolean for lights */ + wingLights: boolean + /** The zero fuel weight */ + zeroFuelWeight: Mass +} +export interface Aircraft { + /** Reported ICAO of the aircraft */ + icao: string + id: string + /** Aircraft ident */ + ident: string + /** Name of the aircraft */ + name: string + registration: string + /** Status */ + status: string + /** Aircraft type */ + type: string +} +export interface Airline { + iata: string + icao: string + /** Show "friendlier" name for airline - the code + name */ + ident: string + name: string +} +export interface Airport { + altitude: Length + /** The name of the city this airports serves */ + city?: string + /** The country this airport is in */ + country?: string + /** Current distance to this airport */ + distance: Length + /** IATA of the airport */ + iata: string + /** The ICAO code of this airport */ + icao: string + /** The Latitude and Longitude of this airport (usually somehere near the centre). */ + location: Coords + /** The name of this airport */ + name?: string + /** The path to the scenery (.BGL) file that defines this airport */ + sceneryFilePath?: string + /** The name of the scenery that defines this airport. */ + sceneryName?: string + /** The state of the USA this airport is in. This is blank for airports outside the USA. */ + state?: string + transitionAltitude: Length + transitionLevel: Length +} +export interface Coords { + /** Latitude */ + lat: number + /** Longitude */ + lon: number +} +/** This is returned on first start when we initally connect to the sim */ +export interface Features { + aircraftIcao: string + aircraftType: AircraftType + configFile: string + /** The date/time in the sim */ + dateTime: DateTimeOffset + /** The date/time on the client machine */ + dateTimeSystem: DateTimeOffset + engineCount: number + engineType: EngineType + flapsCount: number + gearRetractable: boolean + heading: number + headingMagnetic: number + location: Coords + onGround: boolean + parkBrake: boolean + paused: boolean + /** Just a reference to the sim type */ + simType: string + title: string + unlimitedFuel: boolean +} +export interface Field { + name: string + value: string +} +/** This is the loaded flight from phpVMS */ +export interface Flight { + altIcao: string + arrivalIcao: string + arrivalTime: string + departureIcao: string + departureTime: string + distance: Length + fields: { [key: string]: string } + flightNumber: number + flightTime: number + flightType: string + ident: string + level: number + loadFactor: number + loadFactorVariance: number + route: string + routeCode?: string + routeLeg?: string +} +export interface FlightPlan { + altIcao: string + arrivalAirport?: Airport + cargoWeight: Mass + cruiseAlt: Length + currentPoint?: RoutePoint + departureAirport?: Airport + distance: Length + /** The remote ID */ + flightId: string + flightPlanFields: Field[] + flightPlanType: FlightPlanType + loadedFromFile: boolean + nextPoint?: RoutePoint + passengersCount: number + pointCount: number + points: RoutePoint[] + route: string + simbriefId: string + toc?: Coords + tod?: Coords +} +/** Gate information */ +export interface Gate { + id: string + /** Coordinates of this location (x, y) */ + location: Coords + radius: Length + /** The type of gate */ + type: GateType +} +export interface Pirep { + /** + * Return false if the PIREP state is in one of the following states. Rules don't run when + * the PIREP/processing isn't active. + * NotRunning + * Initialized + * Arrived + * Filed + * Cancelled + */ + active: boolean + /** + * The distance when the flightplan is measured + * Might not always be set or available + */ + actualDistance: Length + /** The aircraft the pilot is flying */ + aircraft?: Aircraft + /** + * The airline the flight belongs to, if a flight was loaded + * Otherwise, it's the airline the user selected + */ + airline?: Airline + /** A DateTime object, the time in the sim */ + approachTime: DateTimeOffset + /** A DateTime object, system time */ + approachTimeSystem: DateTimeOffset + /** The arrival airport, null if they haven't landed yet */ + arrivalAirport?: Airport + /** + * The arrival gate, null if they aren't at one. + * Might be null if it wasn't detected + */ + arrivalGate?: Gate + /** + * The runway that they landed on. + * Might be null if it wasn't detected + */ + arrivalRunway?: Runway + /** DateTime object, the time in the sim */ + blocksOffTime: DateTimeOffset + /** A DateTime object. Real time */ + blocksOffTimeSystem: DateTimeOffset + /** A DateTime object, the time in the sim */ + blocksOnTime: DateTimeOffset + /** A DateTime object, system time */ + blocksOnTimeSystem: DateTimeOffset + /** Unix timestamp, the time in the sim */ + boardingTime: DateTimeOffset + /** A DateTime object, sim time */ + boardingTimeSystem: DateTimeOffset + /** + * This will have a value, with the runway information, + * if they're currently on a runway + * Might be null if it wasn't detected + */ + crossingRunway?: Runway + /** + * The airport they departed from + * Might be null if it wasn't detected + */ + departureAirport?: Airport + /** + * The departure gate, null if they aren't at one. + * Might be null if it wasn't detected + */ + departureGate?: Gate + /** + * The runway they took off from + * Might be null if it wasn't detected + */ + departureRunway?: Runway + /** Is this a direct takeoff? That means when in "boarding", the OnGround flag is immediately detected as true */ + directTakeoff: boolean + /** + * How far they ahve already travelled + * Might not always be set or available + */ + distanceToGo: Length + /** Get the time elapsed, subtract the current sim time from the blocks off time */ + elapsedTime: TimeSpan + elapsedTimeHumanReadable?: string + engineStartStates: boolean[] + fares: any[] + /** The startup variables/state when this PIREP started */ + features?: Features + /** + * The flight this PIREP originated from, if it was from + * a bid, or loaded on the screen + */ + flight?: Flight + /** The ID of the flight from phpVMS */ + flightId: string + flightNumber: string + flightPlan?: FlightPlan + flightType: string + /** The fuel */ + fuelAtApproach?: Mass + /** The fuel */ + fuelAtLanding?: Mass + /** The fuel */ + fuelAtTakeOff?: Mass + /** Weight */ + fuelBlocksOff?: Mass + /** The fuel */ + fuelBlocksOn?: Mass + /** The fuel */ + fuelUsed?: Mass + /** The phpVMS PIREP ID */ + id: string + ident: string + ignoreLightsRules: boolean + /** + * Is this flight in an active state? Meaning, it's not in one of these states: + * - Initialized + * - NotRunning + * - Boarding + * - Arrived + */ + isInActiveState: boolean + /** If this PIREP is being resumed */ + isResuming: boolean + /** A DateTime object, the time in the sim */ + landedTime: DateTimeOffset + /** A DateTime object, system time */ + landedTimeSystem: DateTimeOffset + landingRate: Speed + notes?: string + pauseLocation: Coords + /** + * - INI - Initialized + * - BST - Boarding started + * - PBT - Pushback started + * - TXI - Taxi + * - TOF - Takeoff + * - ENR - Enroute + * - APR - Approach + * - FIN - On Final + * - LAN - Landing + * - TXI - Taxi + * - ONB - On-block + * - ARR - Arrived + * - CNL - Cancelled + * - FIL - Filed + * - PSD - Paused + */ + phase?: string + /** + * Distance planned + * Might not always be set or available + */ + plannedDistance: Length + /** Gets or sets the planned flight time. */ + plannedFlightTime: number + route: string + routeCode: string + routeLeg: string + score: number + /** The simulator type they're using */ + simType: SimType + sourceName: string + /** + * Did they start the flight with the brakes on? Use this to determine the criterea on + * moving out of the Boarding phase + */ + startedWithBrakes: boolean + /** DateTime object, the time in the sim */ + startTime: DateTimeOffset + state: PirepState + /** A DateTime object, sim time */ + takeoffTime: DateTimeOffset + /** A DateTime object, system time */ + takeoffTimeSystem: DateTimeOffset + /** Timespan object */ + taxiInDuration: TimeSpan + /** Timespan object */ + taxiOutDuration: TimeSpan + /** A DateTime object, sim time */ + taxiOutTime: DateTimeOffset + /** A DateTime object, system time */ + taxiOutTimeSystem: DateTimeOffset + /** Distance from the threshold */ + thresholdDistance: Length + /** Save whether we've already paused or not for the TOD */ + todPauseCompleted: boolean + /** Or pause the given distance before */ + todPauseDistance: number + /** Whether we should pause at TOD or not */ + todPauseOn: boolean + /** Timespan object */ + totalPauseTime: TimeSpan +} +export interface RoutePoint { + /** If this is the currently active point */ + active: boolean + /** Coordinates of this location (x, y) */ + location: Coords + /** Name of the current active nav point */ + name: string +} +/** A class holding all the information about a runway */ +export interface Runway { + approachingRunway: boolean + approachingRunwayDistance: Length + bearingToMagnetic: number + bearingToTrue: number + closedForLanding: boolean + closedForTakeoff: boolean + distance: Length + endLocation: Coords + headingMagnetic: number + headingTrue: number + /** The airport ICAO */ + icao: string + length: Length + magVar: number + runwayCenter: Coords + /** The number of the runway (e.g. 27L) */ + runwayId: string + startLocation: Coords + surface: Surface + thresholdLocation: Coords + thresholdOffset: Length + width: Length +} +export interface User { + airline: Airline + /** + * The name with their ID + the name, e.g: + * VMS001 Nabeel S + */ + displayName: string + globalId: string + /** phpVMS User ID */ + id: string + /** Their ident, e.g, VMS001 */ + ident: string + /** The user's private name */ + name: string +} +export interface DateTime { + Date: DateTime + Day: number + DayOfWeek: number + DayOfYear: number + Hour: number + Kind: number + Microsecond: number + Millisecond: number + Minute: number + Month: number + Nanosecond: number + Now: DateTime + Second: number + Ticks: number + TimeOfDay: TimeSpan + Today: DateTime + UtcNow: DateTime + Year: number +} +export interface DateTimeOffset { + Date: DateTime + DateTime: DateTime + Day: number + DayOfWeek: number + DayOfYear: number + Hour: number + LocalDateTime: DateTime + Microsecond: number + Millisecond: number + Minute: number + Month: number + Nanosecond: number + Now: DateTimeOffset + Offset: TimeSpan + Second: number + Ticks: number + TimeOfDay: TimeSpan + TotalOffsetMinutes: number + UtcDateTime: DateTime + UtcNow: DateTimeOffset + UtcTicks: number + Year: number +} +export interface TimeSpan { + Days: number + Hours: number + Microseconds: number + Milliseconds: number + Minutes: number + Nanoseconds: number + Seconds: number + Ticks: number + TotalDays: number + TotalHours: number + TotalMicroseconds: number + TotalMilliseconds: number + TotalMinutes: number + TotalNanoseconds: number + TotalSeconds: number +} +export interface Length { + AdditiveIdentity: Length + Angstroms: number + AstronomicalUnits: number + BaseDimensions: any + BaseUnit: number + Centimeters: number + Chains: number + DataMiles: number + Decameters: number + Decimeters: number + DefaultConversionFunctions: any + Dimensions: any + DtpPicas: number + DtpPoints: number + Fathoms: number + Feet: number + FeetInches: any + Femtometers: number + Gigameters: number + Hands: number + Hectometers: number + Inches: number + Info: any + Kilofeet: number + KilolightYears: number + Kilometers: number + Kiloparsecs: number + Kiloyards: number + LightYears: number + MegalightYears: number + Megameters: number + Megaparsecs: number + Meters: number + Microinches: number + Micrometers: number + Miles: number + Millimeters: number + Mils: number + Nanometers: number + NauticalMiles: number + Parsecs: number + Picometers: number + PrinterPicas: number + PrinterPoints: number + QuantityInfo: any + Shackles: number + SolarRadiuses: number + Twips: number + Unit: number + Units: number[] + UsSurveyFeet: number + Value: number + Yards: number + Zero: Length +} +export interface Speed { + AdditiveIdentity: Speed + BaseDimensions: any + BaseUnit: number + CentimetersPerHour: number + CentimetersPerMinutes: number + CentimetersPerSecond: number + DecimetersPerMinutes: number + DecimetersPerSecond: number + DefaultConversionFunctions: any + Dimensions: any + FeetPerHour: number + FeetPerMinute: number + FeetPerSecond: number + InchesPerHour: number + InchesPerMinute: number + InchesPerSecond: number + Info: any + KilometersPerHour: number + KilometersPerMinutes: number + KilometersPerSecond: number + Knots: number + Mach: number + MetersPerHour: number + MetersPerMinutes: number + MetersPerSecond: number + MicrometersPerMinutes: number + MicrometersPerSecond: number + MilesPerHour: number + MillimetersPerHour: number + MillimetersPerMinutes: number + MillimetersPerSecond: number + NanometersPerMinutes: number + NanometersPerSecond: number + QuantityInfo: any + Unit: number + Units: number[] + UsSurveyFeetPerHour: number + UsSurveyFeetPerMinute: number + UsSurveyFeetPerSecond: number + Value: number + YardsPerHour: number + YardsPerMinute: number + YardsPerSecond: number + Zero: Speed +} +export interface Mass { + AdditiveIdentity: Mass + BaseDimensions: any + BaseUnit: number + Centigrams: number + Decagrams: number + Decigrams: number + DefaultConversionFunctions: any + Dimensions: any + EarthMasses: number + Femtograms: number + Grains: number + Grams: number + Hectograms: number + Info: any + Kilograms: number + Kilopounds: number + Kilotonnes: number + LongHundredweight: number + LongTons: number + Megapounds: number + Megatonnes: number + Micrograms: number + Milligrams: number + Nanograms: number + Ounces: number + Picograms: number + Pounds: number + QuantityInfo: any + ShortHundredweight: number + ShortTons: number + Slugs: number + SolarMasses: number + Stone: number + StonePounds: any + Tonnes: number + Unit: number + Units: number[] + Value: number + Zero: Mass +} +export interface MassFlow { + AdditiveIdentity: MassFlow + BaseDimensions: any + BaseUnit: number + CentigramsPerDay: number + CentigramsPerSecond: number + DecagramsPerDay: number + DecagramsPerSecond: number + DecigramsPerDay: number + DecigramsPerSecond: number + DefaultConversionFunctions: any + Dimensions: any + GramsPerDay: number + GramsPerHour: number + GramsPerSecond: number + HectogramsPerDay: number + HectogramsPerSecond: number + Info: any + KilogramsPerDay: number + KilogramsPerHour: number + KilogramsPerMinute: number + KilogramsPerSecond: number + MegagramsPerDay: number + MegapoundsPerDay: number + MegapoundsPerHour: number + MegapoundsPerMinute: number + MegapoundsPerSecond: number + MicrogramsPerDay: number + MicrogramsPerSecond: number + MilligramsPerDay: number + MilligramsPerSecond: number + NanogramsPerDay: number + NanogramsPerSecond: number + PoundsPerDay: number + PoundsPerHour: number + PoundsPerMinute: number + PoundsPerSecond: number + QuantityInfo: any + ShortTonsPerHour: number + TonnesPerDay: number + TonnesPerHour: number + Unit: number + Units: number[] + Value: number + Zero: MassFlow +} +export interface RotationalSpeed { + AdditiveIdentity: RotationalSpeed + BaseDimensions: any + BaseUnit: number + CentiradiansPerSecond: number + DeciradiansPerSecond: number + DefaultConversionFunctions: any + DegreesPerMinute: number + DegreesPerSecond: number + Dimensions: any + Info: any + MicrodegreesPerSecond: number + MicroradiansPerSecond: number + MillidegreesPerSecond: number + MilliradiansPerSecond: number + NanodegreesPerSecond: number + NanoradiansPerSecond: number + QuantityInfo: any + RadiansPerSecond: number + RevolutionsPerMinute: number + RevolutionsPerSecond: number + Unit: number + Units: number[] + Value: number + Zero: RotationalSpeed +} diff --git a/tests/mocks.js b/tests/mocks.js new file mode 100644 index 0000000..ea65357 --- /dev/null +++ b/tests/mocks.js @@ -0,0 +1,191 @@ +'use strict' + +/** + * Create a fake PIREP object + * @returns {Object.} + */ +export function createMockPirep() { + return { + depicao: 'KJFK', + arricao: 'KAUS', + phase: 'takeoff', + } +} + +/** + * Create mocks and global replacements for methods that are provided by ACARS + */ +export function createMocks() { + /** + * Used to keep track of the generated timers + * + * @type {Object.} + */ + const timers = {} + + /** + * Just some local storage + * + * @type {Object.} + */ + const storage = {} + + /** + * Store what's already been logged out + * + * @type {*[]} + */ + const once = [] + + /** + * Add a specialized method which keeps track of the keys + * + * @param key + * @param args + */ + console.once = (key, ...args) => { + if (once.includes(key)) { + return + } + + once.push(key) + console.log(...args) + } + + /** + * + * @type {{}} + */ + global.Acars = {} + + /** + * This returns back immediately and doesn't wait + * + * @param {string} name + * @param {number} timeout + * @param {Function} callback + * + * @returns {number} + */ + global.Acars.ViolatedAfterDelay = (name, timeout, callback) => { + return callback() + } + + /** + * Mock the timers + * + * @param {string} name + */ + global.Acars.StartTimer = (name) => { + if (name in timers) { + timers[name] = Date.now() + } + } + + /** + * Get the time elapsed + * + * @param {string} name + * + * @returns {number} + */ + const timeElapsed = (global.Acars.TimeElapsed = (name) => { + if (name in timers) { + return Date.now() - timers[name] + } + }) + + /** + * Whether the time has elapsed or not + * + * @param {string} name + * @param {number} delta + * + * @returns {boolean} + */ + global.Acars.IsTimeElapsed = (name, delta) => { + return timeElapsed(name) >= delta + } + + /** + * End the timer and reset it + * + * @param {string} name + */ + global.Acars.EndTimer = (name) => { + if (name in timers) { + delete timers[name] + } + } + + /** + * Determine if a value is within a percentage of a level + * + * @param {number} value + * @param {number} level + * @param {number} percent + * @returns {boolean} + */ + global.Acars.NumberWithinPercent = (value, level, percent) => { + const diff = level * (percent / 100) + const upper = level + diff + const lower = level - diff + + return value >= lower && value <= upper + } + + /** + * Whether the value is a certain percent over the level + * + * @param {number} value + * @param {number} level + * @param {number} percent + * @returns {boolean} + */ + global.Acars.NumberOverPercent = (value, level, percent) => { + const diff = level * (percent / 100) + const upper = level + diff + + return value > upper + } + + /** + * Put something into storage + * + * @param {string} name + * @param {string} value + */ + global.Acars.Set = (name, value) => { + storage[name] = value + } + + /** + * Get data from storage + * + * @param {string} name + * @returns {string} + */ + global.Acars.Get = (name) => { + return storage[name] + } + + /** + * Set a field to the PIREP to be sent + * + * @param {string} name + * @param {string} value + */ + global.Acars.SetPirepField = (name, value) => { + console.log('SetPirepField', name, value) + } + + /** + * Add something to the PIREP log + * + * @param {string} message + * @param {boolean} add_to_pirep If this is only shown to the local user, or sent with the PIREP + */ + global.Acars.AddPirepLog = (message, add_to_pirep) => { + console.log('AddPirepLog', message, add_to_pirep) + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..18196b1 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,166 @@ +{ + "compileOnSave": true, + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + /* Enable constraints that allow a TypeScript project to be used with project references. */ + "composite": false, + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + /* Specify an output folder for all emitted files. */ + "outDir": "./dist", + /* Language and Environment */ + "target": "es2023", + /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "lib": [ + "es2023" + ], + /* Specify what module code is generated. */ + "module": "es2022", + /* Specify how TypeScript looks up a file from a given module specifier. */ + "moduleResolution": "node10", + /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + /* Disable including any library files, including the default lib.d.ts. */ + // "noLib": true, + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + + /* Specify the root folder within your source files. */ + "rootDir": "./src", + /* Specify the base directory to resolve non-relative module names. */ + "baseUrl": "./src", + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + /*"typeRoots": [],*/ + /* Specify multiple folders that act like './node_modules/@types'. */ + /*"types": [ + "./src/types" + ],*/ + /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + "allowJs": true, + /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + "declaration": false, + /* Create sourcemaps for d.ts files. */ + // "declarationMap": false, + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, + /* Create source map files for emitted JavaScript files. */ + "inlineSourceMap": false, + /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + + /* Disable emitting comments. */ + // "removeComments": true, + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + /* Disable erasing 'const enum' declarations in generated code. */ + // "preserveConstEnums": true, + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + + /* Interop Constraints */ + "isolatedModules": false, + /* Ensure that each file can be safely transpiled without relying on other imports. */ + /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "verbatimModuleSyntax": true, + // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "esModuleInterop": true, + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + // "forceConsistentCasingInFileNames": true, + /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, + /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + /* Ensure 'use strict' is always emitted. */ + "alwaysStrict": true, + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipDefaultLibCheck": true, + /* Skip type checking all .d.ts files. */ + "skipLibCheck": true + }, + "include": [ + "src/**/*", + "src/package.json" + ], + "exclude": [ + "typings/browser.d.ts", + "typings/browser", + "node_modules" + ], + "watchOptions": { + // Use native file system events for files and directories + "watchFile": "useFsEvents", + "watchDirectory": "useFsEvents", + // Poll files for updates more frequently + // when they're updated a lot. + "fallbackPolling": "dynamicPriority", + // Don't coalesce watch notification + "synchronousWatchDirectory": true, + // Finally, two additional settings for reducing the amount of possible + // files to track work from these directories + "excludeDirectories": [ + "**/node_modules", + "_build" + ], + "excludeFiles": [] + } +} diff --git a/tsconfig.tsbuildinfo b/tsconfig.tsbuildinfo new file mode 100644 index 0000000..7ac217e --- /dev/null +++ b/tsconfig.tsbuildinfo @@ -0,0 +1 @@ +{"program":{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./config/defs.ts","./config/types.d.ts","./config/default/rules/rule.ts","./config/default/rules/beacon_lights.ts","./node_modules/@types/estree/index.d.ts","./node_modules/@types/json-schema/index.d.ts","./node_modules/@types/eslint/use-at-your-own-risk.d.ts","./node_modules/@types/eslint/index.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/file.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/filereader.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/dom-events.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/sqlite.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/globals.global.d.ts","./node_modules/@types/node/index.d.ts"],"fileInfos":[{"version":"44e584d4f6444f58791784f1d530875970993129442a847597db702a073ca68c","affectsGlobalScope":true},"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","9a68c0c07ae2fa71b44384a839b7b8d81662a236d4b9ac30916718f7510b1b2d","5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","5514e54f17d6d74ecefedc73c504eadffdeda79c7ea205cf9febead32d45c4bc","27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10",{"version":"6920e1448680767498a0b77c6a00a8e77d14d62c3da8967b171f1ddffa3c18e4","affectsGlobalScope":true},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true},{"version":"4443e68b35f3332f753eacc66a04ac1d2053b8b035a0e0ac1d455392b5e243b3","affectsGlobalScope":true},{"version":"bc47685641087c015972a3f072480889f0d6c65515f12bd85222f49a98952ed7","affectsGlobalScope":true},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true},{"version":"93495ff27b8746f55d19fcbcdbaccc99fd95f19d057aed1bd2c0cafe1335fbf0","affectsGlobalScope":true},{"version":"6fc23bb8c3965964be8c597310a2878b53a0306edb71d4b5a4dfe760186bcc01","affectsGlobalScope":true},{"version":"ea011c76963fb15ef1cdd7ce6a6808b46322c527de2077b6cfdf23ae6f5f9ec7","affectsGlobalScope":true},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true},{"version":"bb42a7797d996412ecdc5b2787720de477103a0b2e53058569069a0e2bae6c7e","affectsGlobalScope":true},{"version":"4738f2420687fd85629c9efb470793bb753709c2379e5f85bc1815d875ceadcd","affectsGlobalScope":true},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true},{"version":"9fc46429fbe091ac5ad2608c657201eb68b6f1b8341bd6d670047d32ed0a88fa","affectsGlobalScope":true},{"version":"61c37c1de663cf4171e1192466e52c7a382afa58da01b1dc75058f032ddf0839","affectsGlobalScope":true},{"version":"b541a838a13f9234aba650a825393ffc2292dc0fc87681a5d81ef0c96d281e7a","affectsGlobalScope":true},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true},{"version":"ae37d6ccd1560b0203ab88d46987393adaaa78c919e51acf32fb82c86502e98c","affectsGlobalScope":true},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true},{"version":"bf14a426dbbf1022d11bd08d6b8e709a2e9d246f0c6c1032f3b2edb9a902adbe","affectsGlobalScope":true},{"version":"5e07ed3809d48205d5b985642a59f2eba47c402374a7cf8006b686f79efadcbd","affectsGlobalScope":true},{"version":"2b72d528b2e2fe3c57889ca7baef5e13a56c957b946906d03767c642f386bbc3","affectsGlobalScope":true},{"version":"479553e3779be7d4f68e9f40cdb82d038e5ef7592010100410723ceced22a0f7","affectsGlobalScope":true},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true},{"version":"d3d7b04b45033f57351c8434f60b6be1ea71a2dfec2d0a0c3c83badbb0e3e693","affectsGlobalScope":true},{"version":"956d27abdea9652e8368ce029bb1e0b9174e9678a273529f426df4b3d90abd60","affectsGlobalScope":true},{"version":"4fa6ed14e98aa80b91f61b9805c653ee82af3502dc21c9da5268d3857772ca05","affectsGlobalScope":true},{"version":"e6633e05da3ff36e6da2ec170d0d03ccf33de50ca4dc6f5aeecb572cedd162fb","affectsGlobalScope":true},{"version":"d8670852241d4c6e03f2b89d67497a4bbefe29ecaa5a444e2c11a9b05e6fccc6","affectsGlobalScope":true},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true},{"version":"caccc56c72713969e1cfe5c3d44e5bab151544d9d2b373d7dbe5a1e4166652be","affectsGlobalScope":true},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true},{"version":"9d540251809289a05349b70ab5f4b7b99f922af66ab3c39ba56a475dcf95d5ff","affectsGlobalScope":true},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true},{"version":"811c71eee4aa0ac5f7adf713323a5c41b0cf6c4e17367a34fbce379e12bbf0a4","affectsGlobalScope":true},{"version":"33358442698bb565130f52ba79bfd3d4d484ac85fe33f3cb1759c54d18201393","affectsGlobalScope":true},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true},{"version":"b3725d53fe7a8d46586e173aaa6a32a7860490b6a7c6140c7adf2eff4a5ac778","signature":"088b6f14d5a4649fc61422eb3139fa762dbf5a15e3aacb296dea6267c2949d21"},{"version":"478532e919ea7375d3f18fe283602c110b77b055ab53e67afdf7e6b57dafa045","affectsGlobalScope":true},{"version":"69eb5d20f20e00a4424650299cae508170d44dfd41aea313d59c7fc950d9e264","signature":"65593ad4c8bc3f6d31b488b95d4486272703b083f6f213b4327d05132cba25a1"},{"version":"823f8b4b386ae95b217858a6a676e2ed4a46ee3db652f06c93b66f7f25a6fbb8","signature":"6d059a4a85b17d115fcc811550f25ddc579e12a4470d52bc2ede15cb4f5775b3"},"ee7d8894904b465b072be0d2e4b45cf6b887cdba16a467645c4e200982ece7ea","f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","a4a39b5714adfcadd3bbea6698ca2e942606d833bde62ad5fb6ec55f5e438ff8","bbc1d029093135d7d9bfa4b38cbf8761db505026cc458b5e9c8b74f4000e5e75","e142fda89ed689ea53d6f2c93693898464c7d29a0ae71c6dc8cdfe5a1d76c775","7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419","5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","24bd580b5743dc56402c440dc7f9a4f5d592ad7a419f25414d37a7bfe11e342b","25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","6bdc71028db658243775263e93a7db2fd2abfce3ca569c3cca5aee6ed5eb186d","cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","4d2b0eb911816f66abe4970898f97a2cfc902bcd743cbfa5017fad79f7ef90d8","bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","e53a3c2a9f624d90f24bf4588aacd223e7bec1b9d0d479b68d2f4a9e6011147f","24b8685c62562f5d98615c5a0c1d05f297cf5065f15246edfe99e81ec4c0e011","93507c745e8f29090efb99399c3f77bec07db17acd75634249dc92f961573387","339dc5265ee5ed92e536a93a04c4ebbc2128f45eeec6ed29f379e0085283542c","4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107",{"version":"b028f7df55fa70e9d8939bba6c57967ee4eea0fc5d1911b52289978818ab10a3","affectsGlobalScope":true},"db3ec8993b7596a4ef47f309c7b25ee2505b519c13050424d9c34701e5973315",{"version":"5a38909344f43b30b74c90623f83f4412344e992f2ff158e3b6d3725af18dc02","affectsGlobalScope":true},"af49b066a76ce26673fe49d1885cc6b44153f1071ed2d952f2a90fccba1095c9","f22fd1dc2df53eaf5ce0ff9e0a3326fc66f880d6a652210d50563ae72625455f",{"version":"3ddbdb519e87a7827c4f0c4007013f3628ca0ebb9e2b018cf31e5b2f61c593f1","affectsGlobalScope":true},"a40826e8476694e90da94aa008283a7de50d1dafd37beada623863f1901cb7fb",{"version":"6d498d4fd8036ea02a4edcae10375854a0eb1df0496cf0b9d692577d3c0fd603","affectsGlobalScope":true},"24642567d3729bcc545bacb65ee7c0db423400c7f1ef757cab25d05650064f98","fd09b892597ab93e7f79745ce725a3aaf6dd005e8db20f0c63a5d10984cba328","a3be878ff1e1964ab2dc8e0a3b67087cf838731c7f3d8f603337e7b712fdd558","5433f7f77cd1fd53f45bd82445a4e437b2f6a72a32070e907530a4fea56c30c8","9be74296ee565af0c12d7071541fdd23260f53c3da7731fb6361f61150a791f6",{"version":"ee1ee365d88c4c6c0c0a5a5701d66ebc27ccd0bcfcfaa482c6e2e7fe7b98edf7","affectsGlobalScope":true},{"version":"f501a53b94ba382d9ba396a5c486969a3abc68309828fa67f916035f5d37fe2b","affectsGlobalScope":true},"aa658b5d765f630c312ac9202d110bbaf2b82d180376457f0a9d57b42629714a","312ac7cbd070107766a9886fd27f9faad997ef57d93fdfb4095df2c618ac8162","2e9b4e7f9942af902eb85bae6066d04ef1afee51d61554a62d144df3da7dec94","672ad3045f329e94002256f8ed460cfd06173a50c92cde41edaadfacffd16808","64da4965d1e0559e134d9c1621ae400279a216f87ed00c4cce4f2c7c78021712","2205527b976f4f1844adc46a3f0528729fb68cac70027a5fb13c49ca23593797",{"version":"0166fce1204d520fdfd6b5febb3cda3deee438bcbf8ce9ffeb2b1bcde7155346","affectsGlobalScope":true},"d8b13eab85b532285031b06a971fa051bf0175d8fff68065a24a6da9c1c986cf","50c382ba1827988c59aa9cc9d046e386d55d70f762e9e352e95ee8cb7337cdb8","bb9627ab9d078c79bb5623de4ac8e5d08f806ec9b970962dfc83b3211373690d",{"version":"21d7e87f271e72d02f8d167edc902f90b04525edc7918f00f01dd0bd00599f7e","affectsGlobalScope":true},{"version":"6f6abdaf8764ef01a552a958f45e795b5e79153b87ddad3af5264b86d2681b72","affectsGlobalScope":true},"a215554477f7629e3dcbc8cde104bec036b78673650272f5ffdc5a2cee399a0a","c3497fc242aabfedcd430b5932412f94f157b5906568e737f6a18cc77b36a954","cdc1de3b672f9ef03ff15c443aa1b631edca35b6ae6970a7da6400647ff74d95","139ad1dc93a503da85b7a0d5f615bddbae61ad796bc68fedd049150db67a1e26","bf01fdd3b93cf633b3f7420718457af19c57ab8cbfea49268df60bae2e84d627","15c5e91b5f08be34a78e3d976179bf5b7a9cc28dc0ef1ffebffeb3c7812a2dca","5f461d6f5d9ff474f1121cc3fd86aa3cd67476c701f55c306d323c5112201207","65b39cc6b610a4a4aecc321f6efb436f10c0509d686124795b4c36a5e915b89e","269929a24b2816343a178008ac9ae9248304d92a8ba8e233055e0ed6dbe6ef71","93452d394fdd1dc551ec62f5042366f011a00d342d36d50793b3529bfc9bd633","3c1f19c7abcda6b3a4cf9438a15c7307a080bd3b51dfd56b198d9f86baf19447","d3edb86744e2c19f2c1503849ac7594a5e06024f2451bacae032390f2e20314a",{"version":"ea70400f0fe63efb412817f818a4f67afeb9f7edf4c6a320064b8dabe05588d4","affectsGlobalScope":true},{"version":"8a3e61347b8f80aa5af532094498bceb0c0b257b25a6aa8ab4880fd6ed57c95a","affectsGlobalScope":true},"98e00f3613402504bc2a2c9a621800ab48e0a463d1eed062208a4ae98ad8f84c","950f6810f7c80e0cffefcf1bcc6ade3485c94394720e334c3c2be3c16b6922fb","5475df7cfc493a08483c9d7aa61cc04791aecba9d0a2efc213f23c4006d4d3cd","000720870b275764c65e9f28ac97cc9e4d9e4a36942d4750ca8603e416e9c57c",{"version":"54412c70bacb9ed547ed6caae8836f712a83ccf58d94466f3387447ec4e82dc3","affectsGlobalScope":true},{"version":"e74e7b0baa7a24f073080091427d36a75836d584b9393e6ac2b1daf1647fe65a","affectsGlobalScope":true},"4c48e931a72f6971b5add7fdb1136be1d617f124594e94595f7114af749395e0","478eb5c32250678a906d91e0529c70243fc4d75477a08f3da408e2615396f558","e686a88c9ee004c8ba12ffc9d674ca3192a4c50ed0ca6bd5b2825c289e2b2bfe",{"version":"0d27932df2fbc3728e78b98892540e24084424ce12d3bd32f62a23cf307f411f","affectsGlobalScope":true},"4423fb3d6abe6eefb8d7f79eb2df9510824a216ec1c6feee46718c9b18e6d89f",{"version":"ab9b9a36e5284fd8d3bf2f7d5fcbc60052f25f27e4d20954782099282c60d23e","affectsGlobalScope":true},"01c47d1c006b3a15b51d89d7764fff7e4fabc4e412b3a61ee5357bd74b822879"],"root":[[63,66]],"options":{"allowJs":true,"composite":true,"inlineSourceMap":false,"module":7,"skipLibCheck":true,"strict":true,"target":10},"fileIdsList":[[63,64,65],[64],[63],[67,68,69],[70],[71],[111],[112,117,147],[113,118,124,125,132,144,155],[113,114,124,132],[115,156],[116,117,125,133],[117,144,152],[118,120,124,132],[111,119],[120,121],[124],[122,124],[111,124],[124,125,126,144,155],[124,125,126,139,144,147],[109,112,160],[109,120,124,127,132,144,155],[124,125,127,128,132,144,152,155],[127,129,144,152,155],[71,72,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162],[124,130],[131,155,160],[120,124,132,144],[133],[134],[111,135],[71,72,111,112,113,114,115,116,117,118,119,120,121,122,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161],[137],[138],[124,139,140],[139,141,156,158],[112,124,144,145,146,147],[112,144,146],[144,145],[147],[148],[71,144],[124,150,151],[150,151],[117,132,144,152],[153],[132,154],[112,127,138,155],[117,156],[144,157],[131,158],[159],[112,117,124,126,135,144,155,158,160],[144,161],[81,85,155],[81,144,155],[76],[78,81,152,155],[132,152],[163],[76,163],[78,81,132,155],[73,74,77,80,112,124,144,155],[81,88],[73,79],[81,102,103],[77,81,112,147,155,163],[112,163],[102,112,163],[75,76,163],[81],[75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,103,104,105,106,107,108],[81,96],[81,88,89],[79,81,89,90],[80],[73,76,81],[81,85,89,90],[85],[79,81,84,155],[73,78,81,88],[112,144],[76,81,102,112,160,163]],"referencedMap":[[66,1],[65,2],[64,3],[70,4],[69,5],[71,6],[72,6],[111,7],[112,8],[113,9],[114,10],[115,11],[116,12],[117,13],[118,14],[119,15],[120,16],[121,16],[123,17],[122,18],[124,19],[125,20],[126,21],[110,22],[127,23],[128,24],[129,25],[163,26],[130,27],[131,28],[132,29],[133,30],[134,31],[135,32],[136,33],[137,34],[138,35],[139,36],[140,36],[141,37],[144,38],[146,39],[145,40],[147,41],[148,42],[149,43],[150,44],[151,45],[152,46],[153,47],[154,48],[155,49],[156,50],[157,51],[158,52],[159,53],[160,54],[161,55],[88,56],[98,57],[87,56],[108,58],[79,59],[78,60],[107,61],[101,62],[106,63],[81,64],[95,65],[80,66],[104,67],[76,68],[75,69],[105,70],[77,71],[82,72],[86,72],[109,73],[99,74],[90,75],[91,76],[93,77],[89,78],[92,79],[102,61],[84,80],[85,81],[94,82],[74,83],[97,74],[96,72],[103,84]],"latestChangedDtsFile":"./config/default/rules/beacon_lights.d.ts"},"version":"5.5.4"} \ No newline at end of file