lint: fixes from lint and updates to typescript linting

This commit is contained in:
JohnMark Sill 2022-02-18 17:14:16 -06:00
parent 3f11100e7a
commit c3b813e821
13 changed files with 872 additions and 156 deletions

View File

@ -13,6 +13,7 @@ module.exports = {
'prettier', 'prettier',
'preact', 'preact',
'plugin:import/react', 'plugin:import/react',
'plugin:import/typescript',
'plugin:testing-library/recommended', 'plugin:testing-library/recommended',
'plugin:jest/recommended', 'plugin:jest/recommended',
], ],
@ -137,4 +138,20 @@ module.exports = {
}, },
}, },
}, },
overrides: [
{
files: ['*.{ts,tsx}'],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
extends: ['plugin:@typescript-eslint/recommended'],
settings: {
'import/resolver': {
node: {
extensions: ['.ts', '.tsx'],
},
},
},
},
],
}; };

View File

@ -1,4 +1,4 @@
module.exports = { module.exports = {
presets: ['@babel/preset-env'], presets: ['@babel/preset-env', ['@babel/typescript', { jsxPragma: 'h' }]],
plugins: [['@babel/plugin-transform-react-jsx', { pragma: 'h' }]], plugins: [['@babel/plugin-transform-react-jsx', { pragma: 'h' }]],
}; };

8
web/jsconfig.json Normal file
View File

@ -0,0 +1,8 @@
{
"compilerOptions": {
"target": "ES2019",
"jsx": "preserve",
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment",
}
}

749
web/package-lock.json generated
View File

@ -350,6 +350,33 @@
"regexpu-core": "^4.7.1" "regexpu-core": "^4.7.1"
} }
}, },
"@babel/helper-environment-visitor": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz",
"integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==",
"dev": true,
"requires": {
"@babel/types": "^7.16.7"
},
"dependencies": {
"@babel/helper-validator-identifier": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz",
"integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==",
"dev": true
},
"@babel/types": {
"version": "7.17.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz",
"integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.16.7",
"to-fast-properties": "^2.0.0"
}
}
}
},
"@babel/helper-explode-assignable-expression": { "@babel/helper-explode-assignable-expression": {
"version": "7.12.13", "version": "7.12.13",
"resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.13.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.13.tgz",
@ -1072,6 +1099,23 @@
} }
} }
}, },
"@babel/plugin-syntax-typescript": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz",
"integrity": "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.16.7"
},
"dependencies": {
"@babel/helper-plugin-utils": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz",
"integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==",
"dev": true
}
}
},
"@babel/plugin-transform-arrow-functions": { "@babel/plugin-transform-arrow-functions": {
"version": "7.12.13", "version": "7.12.13",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.13.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.13.tgz",
@ -2704,6 +2748,206 @@
} }
} }
}, },
"@babel/plugin-transform-typescript": {
"version": "7.16.8",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz",
"integrity": "sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ==",
"dev": true,
"requires": {
"@babel/helper-create-class-features-plugin": "^7.16.7",
"@babel/helper-plugin-utils": "^7.16.7",
"@babel/plugin-syntax-typescript": "^7.16.7"
},
"dependencies": {
"@babel/code-frame": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz",
"integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==",
"dev": true,
"requires": {
"@babel/highlight": "^7.16.7"
}
},
"@babel/generator": {
"version": "7.17.3",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.3.tgz",
"integrity": "sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg==",
"dev": true,
"requires": {
"@babel/types": "^7.17.0",
"jsesc": "^2.5.1",
"source-map": "^0.5.0"
}
},
"@babel/helper-annotate-as-pure": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz",
"integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==",
"dev": true,
"requires": {
"@babel/types": "^7.16.7"
}
},
"@babel/helper-create-class-features-plugin": {
"version": "7.17.1",
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.1.tgz",
"integrity": "sha512-JBdSr/LtyYIno/pNnJ75lBcqc3Z1XXujzPanHqjvvrhOA+DTceTFuJi8XjmWTZh4r3fsdfqaCMN0iZemdkxZHQ==",
"dev": true,
"requires": {
"@babel/helper-annotate-as-pure": "^7.16.7",
"@babel/helper-environment-visitor": "^7.16.7",
"@babel/helper-function-name": "^7.16.7",
"@babel/helper-member-expression-to-functions": "^7.16.7",
"@babel/helper-optimise-call-expression": "^7.16.7",
"@babel/helper-replace-supers": "^7.16.7",
"@babel/helper-split-export-declaration": "^7.16.7"
}
},
"@babel/helper-function-name": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz",
"integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==",
"dev": true,
"requires": {
"@babel/helper-get-function-arity": "^7.16.7",
"@babel/template": "^7.16.7",
"@babel/types": "^7.16.7"
}
},
"@babel/helper-get-function-arity": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz",
"integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==",
"dev": true,
"requires": {
"@babel/types": "^7.16.7"
}
},
"@babel/helper-hoist-variables": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz",
"integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==",
"dev": true,
"requires": {
"@babel/types": "^7.16.7"
}
},
"@babel/helper-member-expression-to-functions": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz",
"integrity": "sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q==",
"dev": true,
"requires": {
"@babel/types": "^7.16.7"
}
},
"@babel/helper-optimise-call-expression": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz",
"integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==",
"dev": true,
"requires": {
"@babel/types": "^7.16.7"
}
},
"@babel/helper-plugin-utils": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz",
"integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==",
"dev": true
},
"@babel/helper-replace-supers": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz",
"integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==",
"dev": true,
"requires": {
"@babel/helper-environment-visitor": "^7.16.7",
"@babel/helper-member-expression-to-functions": "^7.16.7",
"@babel/helper-optimise-call-expression": "^7.16.7",
"@babel/traverse": "^7.16.7",
"@babel/types": "^7.16.7"
}
},
"@babel/helper-split-export-declaration": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz",
"integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==",
"dev": true,
"requires": {
"@babel/types": "^7.16.7"
}
},
"@babel/helper-validator-identifier": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz",
"integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==",
"dev": true
},
"@babel/highlight": {
"version": "7.16.10",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz",
"integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.16.7",
"chalk": "^2.0.0",
"js-tokens": "^4.0.0"
}
},
"@babel/parser": {
"version": "7.17.3",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.3.tgz",
"integrity": "sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA==",
"dev": true
},
"@babel/template": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz",
"integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.16.7",
"@babel/parser": "^7.16.7",
"@babel/types": "^7.16.7"
}
},
"@babel/traverse": {
"version": "7.17.3",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz",
"integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.16.7",
"@babel/generator": "^7.17.3",
"@babel/helper-environment-visitor": "^7.16.7",
"@babel/helper-function-name": "^7.16.7",
"@babel/helper-hoist-variables": "^7.16.7",
"@babel/helper-split-export-declaration": "^7.16.7",
"@babel/parser": "^7.17.3",
"@babel/types": "^7.17.0",
"debug": "^4.1.0",
"globals": "^11.1.0"
}
},
"@babel/types": {
"version": "7.17.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz",
"integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.16.7",
"to-fast-properties": "^2.0.0"
}
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
"dev": true
}
}
},
"@babel/plugin-transform-unicode-escapes": { "@babel/plugin-transform-unicode-escapes": {
"version": "7.12.13", "version": "7.12.13",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz",
@ -2869,6 +3113,31 @@
"esutils": "^2.0.2" "esutils": "^2.0.2"
} }
}, },
"@babel/preset-typescript": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz",
"integrity": "sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.16.7",
"@babel/helper-validator-option": "^7.16.7",
"@babel/plugin-transform-typescript": "^7.16.7"
},
"dependencies": {
"@babel/helper-plugin-utils": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz",
"integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==",
"dev": true
},
"@babel/helper-validator-option": {
"version": "7.16.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz",
"integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==",
"dev": true
}
}
},
"@babel/runtime": { "@babel/runtime": {
"version": "7.12.13", "version": "7.12.13",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.13.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.13.tgz",
@ -4063,6 +4332,102 @@
"integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==",
"dev": true "dev": true
}, },
"@typescript-eslint/eslint-plugin": {
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.12.0.tgz",
"integrity": "sha512-fwCMkDimwHVeIOKeBHiZhRUfJXU8n6xW1FL9diDxAyGAFvKcH4csy0v7twivOQdQdA0KC8TDr7GGRd3L4Lv0rQ==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "5.12.0",
"@typescript-eslint/type-utils": "5.12.0",
"@typescript-eslint/utils": "5.12.0",
"debug": "^4.3.2",
"functional-red-black-tree": "^1.0.1",
"ignore": "^5.1.8",
"regexpp": "^3.2.0",
"semver": "^7.3.5",
"tsutils": "^3.21.0"
},
"dependencies": {
"@typescript-eslint/scope-manager": {
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.12.0.tgz",
"integrity": "sha512-GAMobtIJI8FGf1sLlUWNUm2IOkIjvn7laFWyRx7CLrv6nLBI7su+B7lbStqVlK5NdLvHRFiJo2HhiDF7Ki01WQ==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.12.0",
"@typescript-eslint/visitor-keys": "5.12.0"
}
},
"@typescript-eslint/types": {
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.12.0.tgz",
"integrity": "sha512-JowqbwPf93nvf8fZn5XrPGFBdIK8+yx5UEGs2QFAYFI8IWYfrzz+6zqlurGr2ctShMaJxqwsqmra3WXWjH1nRQ==",
"dev": true
},
"@typescript-eslint/visitor-keys": {
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.12.0.tgz",
"integrity": "sha512-cFwTlgnMV6TgezQynx2c/4/tx9Tufbuo9LPzmWqyRC3QC4qTGkAG1C6pBr0/4I10PAI/FlYunI3vJjIcu+ZHMg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.12.0",
"eslint-visitor-keys": "^3.0.0"
}
},
"debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"dev": true,
"requires": {
"ms": "2.1.2"
}
},
"eslint-visitor-keys": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
"integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
"dev": true
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"regexpp": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
"integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
"dev": true
},
"semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
},
"tsutils": {
"version": "3.21.0",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
"integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
"dev": true,
"requires": {
"tslib": "^1.8.1"
}
}
}
},
"@typescript-eslint/experimental-utils": { "@typescript-eslint/experimental-utils": {
"version": "4.15.0", "version": "4.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.15.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.15.0.tgz",
@ -4077,6 +4442,164 @@
"eslint-utils": "^2.0.0" "eslint-utils": "^2.0.0"
} }
}, },
"@typescript-eslint/parser": {
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.12.0.tgz",
"integrity": "sha512-MfSwg9JMBojMUoGjUmX+D2stoQj1CBYTCP0qnnVtu9A+YQXVKNtLjasYh+jozOcrb/wau8TCfWOkQTiOAruBog==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "5.12.0",
"@typescript-eslint/types": "5.12.0",
"@typescript-eslint/typescript-estree": "5.12.0",
"debug": "^4.3.2"
},
"dependencies": {
"@typescript-eslint/scope-manager": {
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.12.0.tgz",
"integrity": "sha512-GAMobtIJI8FGf1sLlUWNUm2IOkIjvn7laFWyRx7CLrv6nLBI7su+B7lbStqVlK5NdLvHRFiJo2HhiDF7Ki01WQ==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.12.0",
"@typescript-eslint/visitor-keys": "5.12.0"
}
},
"@typescript-eslint/types": {
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.12.0.tgz",
"integrity": "sha512-JowqbwPf93nvf8fZn5XrPGFBdIK8+yx5UEGs2QFAYFI8IWYfrzz+6zqlurGr2ctShMaJxqwsqmra3WXWjH1nRQ==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.12.0.tgz",
"integrity": "sha512-Dd9gVeOqt38QHR0BEA8oRaT65WYqPYbIc5tRFQPkfLquVEFPD1HAtbZT98TLBkEcCkvwDYOAvuSvAD9DnQhMfQ==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.12.0",
"@typescript-eslint/visitor-keys": "5.12.0",
"debug": "^4.3.2",
"globby": "^11.0.4",
"is-glob": "^4.0.3",
"semver": "^7.3.5",
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.12.0.tgz",
"integrity": "sha512-cFwTlgnMV6TgezQynx2c/4/tx9Tufbuo9LPzmWqyRC3QC4qTGkAG1C6pBr0/4I10PAI/FlYunI3vJjIcu+ZHMg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.12.0",
"eslint-visitor-keys": "^3.0.0"
}
},
"debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"dev": true,
"requires": {
"ms": "2.1.2"
}
},
"eslint-visitor-keys": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
"integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
"dev": true
},
"fast-glob": {
"version": "3.2.11",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
"integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
"dev": true,
"requires": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
"glob-parent": "^5.1.2",
"merge2": "^1.3.0",
"micromatch": "^4.0.4"
}
},
"globby": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
"dev": true,
"requires": {
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
"fast-glob": "^3.2.9",
"ignore": "^5.2.0",
"merge2": "^1.4.1",
"slash": "^3.0.0"
}
},
"ignore": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
"dev": true
},
"is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"requires": {
"is-extglob": "^2.1.1"
}
},
"micromatch": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
"integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==",
"dev": true,
"requires": {
"braces": "^3.0.1",
"picomatch": "^2.2.3"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true
},
"semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
},
"tsutils": {
"version": "3.21.0",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
"integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
"dev": true,
"requires": {
"tslib": "^1.8.1"
}
}
}
},
"@typescript-eslint/scope-manager": { "@typescript-eslint/scope-manager": {
"version": "4.15.0", "version": "4.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.15.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.15.0.tgz",
@ -4087,6 +4610,49 @@
"@typescript-eslint/visitor-keys": "4.15.0" "@typescript-eslint/visitor-keys": "4.15.0"
} }
}, },
"@typescript-eslint/type-utils": {
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.12.0.tgz",
"integrity": "sha512-9j9rli3zEBV+ae7rlbBOotJcI6zfc6SHFMdKI9M3Nc0sy458LJ79Os+TPWeBBL96J9/e36rdJOfCuyRSgFAA0Q==",
"dev": true,
"requires": {
"@typescript-eslint/utils": "5.12.0",
"debug": "^4.3.2",
"tsutils": "^3.21.0"
},
"dependencies": {
"debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"dev": true,
"requires": {
"ms": "2.1.2"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
},
"tsutils": {
"version": "3.21.0",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
"integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
"dev": true,
"requires": {
"tslib": "^1.8.1"
}
}
}
},
"@typescript-eslint/types": { "@typescript-eslint/types": {
"version": "4.15.0", "version": "4.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.15.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.15.0.tgz",
@ -4108,6 +4674,189 @@
"tsutils": "^3.17.1" "tsutils": "^3.17.1"
} }
}, },
"@typescript-eslint/utils": {
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.12.0.tgz",
"integrity": "sha512-k4J2WovnMPGI4PzKgDtQdNrCnmBHpMUFy21qjX2CoPdoBcSBIMvVBr9P2YDP8jOqZOeK3ThOL6VO/sy6jtnvzw==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.9",
"@typescript-eslint/scope-manager": "5.12.0",
"@typescript-eslint/types": "5.12.0",
"@typescript-eslint/typescript-estree": "5.12.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0"
},
"dependencies": {
"@types/json-schema": {
"version": "7.0.9",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
"integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==",
"dev": true
},
"@typescript-eslint/scope-manager": {
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.12.0.tgz",
"integrity": "sha512-GAMobtIJI8FGf1sLlUWNUm2IOkIjvn7laFWyRx7CLrv6nLBI7su+B7lbStqVlK5NdLvHRFiJo2HhiDF7Ki01WQ==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.12.0",
"@typescript-eslint/visitor-keys": "5.12.0"
}
},
"@typescript-eslint/types": {
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.12.0.tgz",
"integrity": "sha512-JowqbwPf93nvf8fZn5XrPGFBdIK8+yx5UEGs2QFAYFI8IWYfrzz+6zqlurGr2ctShMaJxqwsqmra3WXWjH1nRQ==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.12.0.tgz",
"integrity": "sha512-Dd9gVeOqt38QHR0BEA8oRaT65WYqPYbIc5tRFQPkfLquVEFPD1HAtbZT98TLBkEcCkvwDYOAvuSvAD9DnQhMfQ==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.12.0",
"@typescript-eslint/visitor-keys": "5.12.0",
"debug": "^4.3.2",
"globby": "^11.0.4",
"is-glob": "^4.0.3",
"semver": "^7.3.5",
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.12.0.tgz",
"integrity": "sha512-cFwTlgnMV6TgezQynx2c/4/tx9Tufbuo9LPzmWqyRC3QC4qTGkAG1C6pBr0/4I10PAI/FlYunI3vJjIcu+ZHMg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.12.0",
"eslint-visitor-keys": "^3.0.0"
}
},
"debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"dev": true,
"requires": {
"ms": "2.1.2"
}
},
"eslint-utils": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
"integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
"dev": true,
"requires": {
"eslint-visitor-keys": "^2.0.0"
},
"dependencies": {
"eslint-visitor-keys": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
"dev": true
}
}
},
"eslint-visitor-keys": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
"integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
"dev": true
},
"fast-glob": {
"version": "3.2.11",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
"integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
"dev": true,
"requires": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
"glob-parent": "^5.1.2",
"merge2": "^1.3.0",
"micromatch": "^4.0.4"
}
},
"globby": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
"dev": true,
"requires": {
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
"fast-glob": "^3.2.9",
"ignore": "^5.2.0",
"merge2": "^1.4.1",
"slash": "^3.0.0"
}
},
"ignore": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
"dev": true
},
"is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"requires": {
"is-extglob": "^2.1.1"
}
},
"micromatch": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
"integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==",
"dev": true,
"requires": {
"braces": "^3.0.1",
"picomatch": "^2.2.3"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true
},
"semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
},
"tsutils": {
"version": "3.21.0",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
"integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
"dev": true,
"requires": {
"tslib": "^1.8.1"
}
}
}
},
"@typescript-eslint/visitor-keys": { "@typescript-eslint/visitor-keys": {
"version": "4.15.0", "version": "4.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.0.tgz",

View File

@ -2,12 +2,12 @@
"name": "frigate", "name": "frigate",
"private": true, "private": true,
"scripts": { "scripts": {
"start": "cross-env SNOWPACK_PUBLIC_API_HOST=http://localhost:5000 snowpack dev", "start": "cross-env SNOWPACK_PUBLIC_API_HOST=http://hawkeye:5000 snowpack dev",
"start:custom": "snowpack dev", "start:custom": "snowpack dev",
"prebuild": "rimraf build", "prebuild": "rimraf build",
"build": "cross-env NODE_ENV=production SNOWPACK_MODE=production SNOWPACK_PUBLIC_API_HOST='' snowpack build", "build": "cross-env NODE_ENV=production SNOWPACK_MODE=production SNOWPACK_PUBLIC_API_HOST='http://hawkeye:5000' snowpack build",
"lint": "npm run lint:cmd -- --fix", "lint": "npm run lint:cmd -- --fix",
"lint:cmd": "eslint ./ --ext .jsx,.js", "lint:cmd": "eslint ./ --ext .jsx,.js,.tsx,.ts",
"test": "jest" "test": "jest"
}, },
"dependencies": { "dependencies": {
@ -26,11 +26,14 @@
"@babel/eslint-parser": "^7.12.13", "@babel/eslint-parser": "^7.12.13",
"@babel/plugin-transform-react-jsx": "^7.12.13", "@babel/plugin-transform-react-jsx": "^7.12.13",
"@babel/preset-env": "^7.12.13", "@babel/preset-env": "^7.12.13",
"@babel/preset-typescript": "^7.16.7",
"@prefresh/snowpack": "^3.0.1", "@prefresh/snowpack": "^3.0.1",
"@snowpack/plugin-postcss": "^1.1.0", "@snowpack/plugin-postcss": "^1.1.0",
"@testing-library/jest-dom": "^5.11.9", "@testing-library/jest-dom": "^5.11.9",
"@testing-library/preact": "^2.0.1", "@testing-library/preact": "^2.0.1",
"@testing-library/user-event": "^12.7.1", "@testing-library/user-event": "^12.7.1",
"@typescript-eslint/eslint-plugin": "^5.12.0",
"@typescript-eslint/parser": "^5.12.0",
"autoprefixer": "^10.2.1", "autoprefixer": "^10.2.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "^7.19.0", "eslint": "^7.19.0",

View File

@ -2,7 +2,7 @@ import { h } from 'preact';
interface BubbleButtonProps { interface BubbleButtonProps {
variant?: 'primary' | 'secondary'; variant?: 'primary' | 'secondary';
children?: JSX.Element; children?: preact.JSX.Element;
disabled?: boolean; disabled?: boolean;
className?: string; className?: string;
onClick?: () => void; onClick?: () => void;
@ -22,13 +22,11 @@ export const BubbleButton = ({
if (disabled) { if (disabled) {
computedClass += ' text-gray-200 dark:text-gray-200'; computedClass += ' text-gray-200 dark:text-gray-200';
} else { } else if (variant === 'primary') {
if (variant === 'primary') {
computedClass += ` ${PRIMARY_CLASS}`; computedClass += ` ${PRIMARY_CLASS}`;
} else if (variant === 'secondary') { } else if (variant === 'secondary') {
computedClass += ` ${SECONDARY_CLASS}`; computedClass += ` ${SECONDARY_CLASS}`;
} }
}
const onClickHandler = () => { const onClickHandler = () => {
if (disabled) { if (disabled) {

View File

@ -1,6 +1,7 @@
import { h } from 'preact'; import { h } from 'preact';
import { useCallback, useEffect, useRef, useState } from 'preact/hooks'; import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
import { useApiHost } from '../../api'; import { useApiHost } from '../../api';
import { isNullOrUndefined } from '../../utils/objectUtils';
interface OnTimeUpdateEvent { interface OnTimeUpdateEvent {
timestamp: number; timestamp: number;
@ -17,13 +18,11 @@ interface HistoryVideoProps {
id: string; id: string;
isPlaying: boolean; isPlaying: boolean;
currentTime: number; currentTime: number;
onTimeUpdate: (event: OnTimeUpdateEvent) => void; onTimeUpdate?: (event: OnTimeUpdateEvent) => void;
onPause: () => void; onPause: () => void;
onPlay: () => void; onPlay: () => void;
} }
const isNullOrUndefined = (object: any): boolean => object === null || object === undefined;
export const HistoryVideo = ({ export const HistoryVideo = ({
id, id,
isPlaying: videoIsPlaying, isPlaying: videoIsPlaying,
@ -47,7 +46,7 @@ export const HistoryVideo = ({
setVideoHeight(videoHeight); setVideoHeight(videoHeight);
} }
} }
}, [videoRef.current]); }, [videoRef]);
useEffect(() => { useEffect(() => {
initializeVideoContainerHeight(); initializeVideoContainerHeight();
@ -56,6 +55,10 @@ export const HistoryVideo = ({
useEffect(() => { useEffect(() => {
const idExists = !isNullOrUndefined(id); const idExists = !isNullOrUndefined(id);
if (idExists) { if (idExists) {
if (videoRef.current && !videoRef.current.paused) {
videoRef.current = undefined;
}
setVideoProperties({ setVideoProperties({
posterUrl: `${apiHost}/api/events/${id}/snapshot.jpg`, posterUrl: `${apiHost}/api/events/${id}/snapshot.jpg`,
videoUrl: `${apiHost}/vod/event/${id}/index.m3u8`, videoUrl: `${apiHost}/vod/event/${id}/index.m3u8`,
@ -64,28 +67,15 @@ export const HistoryVideo = ({
} else { } else {
setVideoProperties(undefined); setVideoProperties(undefined);
} }
}, [id, videoHeight]); }, [id, videoHeight, videoRef, apiHost]);
useEffect(() => { useEffect(() => {
const playVideo = (video: HTMLMediaElement) => { const playVideo = (video: HTMLMediaElement) => video.play();
console.debug('playVideo: attempt playback');
video
.play()
.then(() => {
console.debug('playVideo: video started');
})
.catch((e) => {
console.error('Fail', { e });
});
};
const attemptPlayVideo = (video: HTMLMediaElement) => { const attemptPlayVideo = (video: HTMLMediaElement) => {
const videoHasNotLoaded = video.readyState <= 1; const videoHasNotLoaded = video.readyState <= 1;
console.debug('playVideo', { videoHasNotLoaded });
if (videoHasNotLoaded) { if (videoHasNotLoaded) {
console.debug('playVideo: attempt to load video');
video.oncanplay = () => { video.oncanplay = () => {
console.debug('onLoad: video loaded');
playVideo(video); playVideo(video);
}; };
video.load(); video.load();
@ -97,7 +87,6 @@ export const HistoryVideo = ({
const video = videoRef.current; const video = videoRef.current;
const videoExists = !isNullOrUndefined(video); const videoExists = !isNullOrUndefined(video);
if (videoExists) { if (videoExists) {
console.log('check should start', { videoIsPlaying });
if (videoIsPlaying) { if (videoIsPlaying) {
attemptPlayVideo(video); attemptPlayVideo(video);
} else { } else {
@ -122,19 +111,22 @@ export const HistoryVideo = ({
isPlaying: videoIsPlaying, isPlaying: videoIsPlaying,
timestamp: target.currentTime, timestamp: target.currentTime,
}; };
onTimeUpdate(timeUpdateEvent);
onTimeUpdate && onTimeUpdate(timeUpdateEvent);
}, },
[videoIsPlaying] [videoIsPlaying, onTimeUpdate]
); );
const videoPropertiesIsUndefined = isNullOrUndefined(videoProperties); const videoPropertiesIsUndefined = isNullOrUndefined(videoProperties);
if (videoPropertiesIsUndefined) { if (videoPropertiesIsUndefined) {
return <div style={{ height: `${videoHeight}px`, width: '100%' }}></div>; return <div style={{ height: `${videoHeight}px`, width: '100%' }} />;
} }
const { posterUrl, videoUrl, height } = videoProperties; const { posterUrl, videoUrl, height } = videoProperties;
return ( return (
<video <video
ref={videoRef} ref={videoRef}
key={posterUrl}
onTimeUpdate={onTimeUpdateHandler} onTimeUpdate={onTimeUpdateHandler}
onPause={onPause} onPause={onPause}
onPlay={onPlay} onPlay={onPlay}

View File

@ -1,5 +1,5 @@
import { Fragment, h } from 'preact'; import { Fragment, h } from 'preact';
import { useEffect, useState } from 'preact/hooks'; import { useCallback, useEffect, useState } from 'preact/hooks';
import { useEvents } from '../../api'; import { useEvents } from '../../api';
import { useSearchString } from '../../hooks/useSearchString'; import { useSearchString } from '../../hooks/useSearchString';
import { getNowYesterdayInLong } from '../../utils/dateUtil'; import { getNowYesterdayInLong } from '../../utils/dateUtil';
@ -25,9 +25,8 @@ export default function HistoryViewer({ camera }) {
} }
}, [events]); }, [events]);
const onTimeUpdateHandler = ({ timestamp, isPlaying }) => {}; const handleTimelineChange = useCallback(
(event: TimelineChangeEvent) => {
const handleTimelineChange = (event: TimelineChangeEvent) => {
if (event.seekComplete) { if (event.seekComplete) {
setCurrentEvent(event.timelineEvent); setCurrentEvent(event.timelineEvent);
@ -36,7 +35,9 @@ export default function HistoryViewer({ camera }) {
setCurrentTime(eventTime); setCurrentTime(eventTime);
} }
} }
}; },
[isPlaying]
);
const onPlayHandler = () => { const onPlayHandler = () => {
setIsPlaying(true); setIsPlaying(true);
@ -47,7 +48,6 @@ export default function HistoryViewer({ camera }) {
}; };
const onPlayPauseHandler = (isPlaying: boolean) => { const onPlayPauseHandler = (isPlaying: boolean) => {
console.debug('onPlayPauseHandler: setting isPlaying', { isPlaying });
setIsPlaying(isPlaying); setIsPlaying(isPlaying);
}; };
@ -61,7 +61,6 @@ export default function HistoryViewer({ camera }) {
id={currentEvent ? currentEvent.id : undefined} id={currentEvent ? currentEvent.id : undefined}
isPlaying={isPlaying} isPlaying={isPlaying}
currentTime={currentTime} currentTime={currentTime}
onTimeUpdate={onTimeUpdateHandler}
onPlay={onPlayHandler} onPlay={onPlayHandler}
onPause={onPausedHandler} onPause={onPausedHandler}
/> />

View File

@ -9,7 +9,7 @@ export function LiveChip({ className }) {
class='animate-ping absolute inline-flex h-full w-full rounded-full opacity-75' class='animate-ping absolute inline-flex h-full w-full rounded-full opacity-75'
style={{ backgroundColor: 'rgb(74 222 128)' }} style={{ backgroundColor: 'rgb(74 222 128)' }}
/> />
<span class='relative inline-flex rounded-full h-3 w-3' style={{ backgroundColor: 'rgb(74 222 128)' }}/> <span class='relative inline-flex rounded-full h-3 w-3' style={{ backgroundColor: 'rgb(74 222 128)' }} />
</span> </span>
</div> </div>
<span>Live</span> <span>Live</span>

View File

@ -1,6 +1,7 @@
import { Fragment, h } from 'preact'; import { Fragment, h } from 'preact';
import { useEffect, useMemo, useRef, useState } from 'preact/hooks'; import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks';
import { longToDate } from '../../utils/dateUtil'; import { getTimelineEventBlocksFromTimelineEvents } from '../../utils/Timeline/timelineEventUtils';
import { ScrollPermission } from './ScrollPermission';
import { TimelineBlocks } from './TimelineBlocks'; import { TimelineBlocks } from './TimelineBlocks';
import { TimelineChangeEvent } from './TimelineChangeEvent'; import { TimelineChangeEvent } from './TimelineChangeEvent';
import { DisabledControls, TimelineControls } from './TimelineControls'; import { DisabledControls, TimelineControls } from './TimelineControls';
@ -11,7 +12,7 @@ interface TimelineProps {
events: TimelineEvent[]; events: TimelineEvent[];
isPlaying: boolean; isPlaying: boolean;
onChange: (event: TimelineChangeEvent) => void; onChange: (event: TimelineChangeEvent) => void;
onPlayPause: (isPlaying: boolean) => void; onPlayPause?: (isPlaying: boolean) => void;
} }
export default function Timeline({ events, isPlaying, onChange, onPlayPause }: TimelineProps) { export default function Timeline({ events, isPlaying, onChange, onPlayPause }: TimelineProps) {
@ -26,12 +27,36 @@ export default function Timeline({ events, isPlaying, onChange, onPlayPause }: T
const [timelineOffset, setTimelineOffset] = useState<number | undefined>(undefined); const [timelineOffset, setTimelineOffset] = useState<number | undefined>(undefined);
const [markerTime, setMarkerTime] = useState<Date | undefined>(undefined); const [markerTime, setMarkerTime] = useState<Date | undefined>(undefined);
const [currentEvent, setCurrentEvent] = useState<TimelineEventBlock | undefined>(undefined); const [currentEvent, setCurrentEvent] = useState<TimelineEventBlock | undefined>(undefined);
const [scrollTimeout, setScrollTimeout] = useState<any | undefined>(undefined); const [scrollTimeout, setScrollTimeout] = useState<NodeJS.Timeout | undefined>(undefined);
const [scrollPermission, setScrollPermission] = useState<ScrollPermission>({ const [scrollPermission, setScrollPermission] = useState<ScrollPermission>({
allowed: true, allowed: true,
resetAfterSeeked: false, resetAfterSeeked: false,
}); });
const scrollToPosition = useCallback(
(positionX: number) => {
if (timelineContainerRef.current) {
const permission: ScrollPermission = {
allowed: true,
resetAfterSeeked: true,
};
setScrollPermission(permission);
timelineContainerRef.current.scroll({
left: positionX,
behavior: 'smooth',
});
}
},
[timelineContainerRef]
);
const scrollToEvent = useCallback(
(event, offset = 0) => {
scrollToPosition(event.positionX + offset - timelineOffset);
},
[timelineOffset, scrollToPosition]
);
useEffect(() => { useEffect(() => {
if (timeline.length > 0 && currentEvent) { if (timeline.length > 0 && currentEvent) {
const currentIndex = currentEvent.index; const currentIndex = currentEvent.index;
@ -57,86 +82,18 @@ export default function Timeline({ events, isPlaying, onChange, onPlayPause }: T
} }
}, [timeline, currentEvent]); }, [timeline, currentEvent]);
const checkEventForOverlap = (firstEvent: TimelineEvent, secondEvent: TimelineEvent) => {
if (secondEvent.startTime < firstEvent.endTime && secondEvent.startTime > firstEvent.startTime) {
return true;
}
return false;
};
const determineOffset = (currentEvent: TimelineEventBlock, previousEvents: TimelineEventBlock[]): number => {
const OFFSET_DISTANCE_IN_PIXELS = 10;
const previousIndex = previousEvents.length - 1;
const previousEvent = previousEvents[previousIndex];
if (previousEvent) {
const isOverlap = checkEventForOverlap(previousEvent, currentEvent);
if (isOverlap) {
return OFFSET_DISTANCE_IN_PIXELS + determineOffset(currentEvent, previousEvents.slice(0, previousIndex));
}
}
return 0;
};
const buildTimelineView = (events: TimelineEvent[]): TimelineEventBlock[] => {
const firstEvent = events[0];
const firstEventTime = longToDate(firstEvent.start_time);
return events
.map((e, index) => {
const startTime = longToDate(e.start_time);
const endTime = e.end_time ? longToDate(e.end_time) : new Date();
const seconds = Math.round(Math.abs(endTime.getTime() - startTime.getTime()) / 1000);
const positionX = Math.round(Math.abs(startTime.getTime() - firstEventTime.getTime()) / 1000 + timelineOffset);
return {
...e,
startTime,
endTime,
width: seconds,
positionX,
index,
} as TimelineEventBlock;
})
.reduce((rowMap, current) => {
for (let i = 0; i < rowMap.length; i++) {
const row = rowMap[i] ?? [];
const lastItem = row[row.length - 1];
if (lastItem) {
const isOverlap = checkEventForOverlap(lastItem, current);
if (isOverlap) {
continue;
}
}
rowMap[i] = [...row, current];
return rowMap;
}
rowMap.push([current]);
return rowMap;
}, [] as TimelineEventBlock[][])
.flatMap((rows, rowPosition) => {
rows.forEach((eventBlock) => {
const OFFSET_DISTANCE_IN_PIXELS = 10;
eventBlock.yOffset = OFFSET_DISTANCE_IN_PIXELS * rowPosition;
});
return rows;
})
.sort((a, b) => a.startTime.getTime() - b.startTime.getTime());
};
useEffect(() => { useEffect(() => {
if (events && events.length > 0 && timelineOffset) { if (events && events.length > 0 && timelineOffset) {
const timelineEvents = buildTimelineView(events); const timelineEvents = getTimelineEventBlocksFromTimelineEvents(events, timelineOffset);
const lastEventIndex = timelineEvents.length - 1; const lastEventIndex = timelineEvents.length - 1;
const recentEvent = timelineEvents[lastEventIndex]; const recentEvent = timelineEvents[lastEventIndex];
setTimeline(timelineEvents); setTimeline(timelineEvents);
setMarkerTime(recentEvent.startTime); setMarkerTime(recentEvent.startTime);
setCurrentEvent(recentEvent); setCurrentEvent(recentEvent);
onChange({ scrollToEvent(recentEvent);
timelineEvent: recentEvent,
markerTime: recentEvent.startTime,
seekComplete: true,
});
} }
}, [events, timelineOffset]); }, [events, timelineOffset, scrollToEvent]);
useEffect(() => { useEffect(() => {
const timelineIsLoaded = timeline.length > 0; const timelineIsLoaded = timeline.length > 0;
@ -144,7 +101,7 @@ export default function Timeline({ events, isPlaying, onChange, onPlayPause }: T
const lastEvent = timeline[timeline.length - 1]; const lastEvent = timeline[timeline.length - 1];
scrollToEvent(lastEvent); scrollToEvent(lastEvent);
} }
}, [timeline]); }, [timeline, scrollToEvent]);
const checkMarkerForEvent = (markerTime: Date) => { const checkMarkerForEvent = (markerTime: Date) => {
const adjustedMarkerTime = new Date(markerTime); const adjustedMarkerTime = new Date(markerTime);
@ -197,32 +154,14 @@ export default function Timeline({ events, isPlaying, onChange, onPlayPause }: T
} }
}; };
const scrollToPosition = (positionX: number) => { const getCurrentMarkerTime = useCallback(() => {
if (timelineContainerRef.current) {
const permission: ScrollPermission = {
allowed: true,
resetAfterSeeked: true,
};
setScrollPermission(permission);
timelineContainerRef.current.scroll({
left: positionX,
behavior: 'smooth',
});
}
};
const scrollToEvent = (event, offset = 0) => {
scrollToPosition(event.positionX + offset - timelineOffset);
};
const getCurrentMarkerTime = () => {
if (timelineContainerRef.current && timeline.length > 0) { if (timelineContainerRef.current && timeline.length > 0) {
const scrollPosition = timelineContainerRef.current.scrollLeft; const scrollPosition = timelineContainerRef.current.scrollLeft;
const firstTimelineEvent = timeline[0] as TimelineEventBlock; const firstTimelineEvent = timeline[0] as TimelineEventBlock;
const firstTimelineEventStartTime = firstTimelineEvent.startTime.getTime(); const firstTimelineEventStartTime = firstTimelineEvent.startTime.getTime();
return new Date(firstTimelineEventStartTime + scrollPosition * 1000); return new Date(firstTimelineEventStartTime + scrollPosition * 1000);
} }
}; }, [timeline, timelineContainerRef]);
useEffect(() => { useEffect(() => {
if (timelineContainerRef) { if (timelineContainerRef) {
@ -232,10 +171,13 @@ export default function Timeline({ events, isPlaying, onChange, onPlayPause }: T
} }
}, [timelineContainerRef]); }, [timelineContainerRef]);
const handleViewEvent = (event: TimelineEventBlock) => { const handleViewEvent = useCallback(
(event: TimelineEventBlock) => {
scrollToEvent(event); scrollToEvent(event);
setMarkerTime(getCurrentMarkerTime()); setMarkerTime(getCurrentMarkerTime());
}; },
[scrollToEvent, getCurrentMarkerTime]
);
const onPlayPauseHandler = (isPlaying: boolean) => { const onPlayPauseHandler = (isPlaying: boolean) => {
onPlayPause(isPlaying); onPlayPause(isPlaying);
@ -260,7 +202,7 @@ export default function Timeline({ events, isPlaying, onChange, onPlayPause }: T
if (timelineOffset && timeline.length > 0) { if (timelineOffset && timeline.length > 0) {
return <TimelineBlocks timeline={timeline} firstBlockOffset={timelineOffset} onEventClick={handleViewEvent} />; return <TimelineBlocks timeline={timeline} firstBlockOffset={timelineOffset} onEventClick={handleViewEvent} />;
} }
}, [timeline, timelineOffset]); }, [timeline, timelineOffset, handleViewEvent]);
return ( return (
<Fragment> <Fragment>
@ -279,7 +221,7 @@ export default function Timeline({ events, isPlaying, onChange, onPlayPause }: T
left: 'calc(100% / 2)', left: 'calc(100% / 2)',
borderRight: '2px solid rgba(252, 211, 77)', borderRight: '2px solid rgba(252, 211, 77)',
}} }}
></div> />
</div> </div>
</div> </div>
<div ref={timelineContainerRef} onScroll={onTimelineScrollHandler} className='overflow-x-auto hide-scroll'> <div ref={timelineContainerRef} onScroll={onTimelineScrollHandler} className='overflow-x-auto hide-scroll'>

View File

@ -9,7 +9,7 @@ interface TimelineBlockViewProps {
} }
export const TimelineBlockView = ({ block, onClick }: TimelineBlockViewProps) => { export const TimelineBlockView = ({ block, onClick }: TimelineBlockViewProps) => {
const onClickHandler = useCallback(() => onClick(block), [block]); const onClickHandler = useCallback(() => onClick(block), [block, onClick]);
return ( return (
<div <div
key={block.id} key={block.id}
@ -20,6 +20,6 @@ export const TimelineBlockView = ({ block, onClick }: TimelineBlockViewProps) =>
left: `${block.positionX}px`, left: `${block.positionX}px`,
width: `${block.width}px`, width: `${block.width}px`,
}} }}
></div> />
); );
}; };

View File

@ -6,7 +6,7 @@ import { useConfig } from '../api';
import { Tabs, TextTab } from '../components/Tabs'; import { Tabs, TextTab } from '../components/Tabs';
import { LiveChip } from '../components/LiveChip'; import { LiveChip } from '../components/LiveChip';
import { DebugCamera } from '../components/DebugCamera'; import { DebugCamera } from '../components/DebugCamera';
import { HistoryViewer } from '../components/HistoryViewer' import HistoryViewer from '../components/HistoryViewer/HistoryViewer.tsx';
export default function Camera({ camera }) { export default function Camera({ camera }) {
const { data: config } = useConfig(); const { data: config } = useConfig();

View File

@ -1,7 +1,15 @@
{ {
"compilerOptions": { "compilerOptions": {
"jsx": "preserve", "module": "CommonJS",
"target": "ES2019",
"jsx": "react",
"jsxFactory": "h", "jsxFactory": "h",
"jsxFragmentFactory": "Fragment", "lib": [
} "ES2019"
]
},
"include": [
"./src/**/*.tsx",
"./src/**/*.ts"
]
} }