Compare commits
184 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cfe5704328 | ||
|
|
d16846fa3c | ||
|
|
112e79a64c | ||
|
|
eeb1d5b0c4 | ||
|
|
d6bc33ec86 | ||
|
|
956052c8ff | ||
|
|
3785e410db | ||
|
|
481a02afd5 | ||
|
|
5bbbb9cecc | ||
|
|
db47440d47 | ||
|
|
1bcc6c8b2b | ||
|
|
3c3ec80525 | ||
|
|
803ef2bb3e | ||
|
|
43ce834750 | ||
|
|
44d79e2c9a | ||
|
|
0e52a99efa | ||
|
|
834ce8cf9e | ||
|
|
2354f0ba9f | ||
|
|
8e96c19126 | ||
|
|
99fb4c73f5 | ||
|
|
d11a674a3c | ||
|
|
31f70913a2 | ||
|
|
bd23faa28f | ||
|
|
5b1fbe1e2f | ||
|
|
96265b75de | ||
|
|
17298f0b2c | ||
|
|
61b475fa8d | ||
|
|
455084c17b | ||
|
|
c3c5b897f3 | ||
|
|
a1871a2982 | ||
|
|
87de017afd | ||
|
|
5a2c263af3 | ||
|
|
ddd2e704db | ||
|
|
9b5e0b0591 | ||
|
|
9b6636e21a | ||
|
|
a8358b8e7a | ||
|
|
5f091bb6ab | ||
|
|
17b334aaad | ||
|
|
c65209e6a9 | ||
|
|
d2518b11a7 | ||
|
|
70cdf3865a | ||
|
|
7c02ffb6c9 | ||
|
|
f2d9881382 | ||
|
|
d7761ab30e | ||
|
|
bf83c58982 | ||
|
|
2e082932c9 | ||
|
|
2f90cac52a | ||
|
|
20a0071c0d | ||
|
|
a40f83d6b6 | ||
|
|
0db8cc4206 | ||
|
|
97ddc027cb | ||
|
|
82c43ba2e3 | ||
|
|
799fb091b8 | ||
|
|
5ead1a087d | ||
|
|
64631a4552 | ||
|
|
f47cb04860 | ||
|
|
504fa056d7 | ||
|
|
739287ac71 | ||
|
|
ab8bce7686 | ||
|
|
e6152fa76f | ||
|
|
a8031d18d5 | ||
|
|
9aa8166891 | ||
|
|
236a0f6c82 | ||
|
|
2492f2d814 | ||
|
|
410bcd8244 | ||
|
|
b08a6b5cec | ||
|
|
37ad26a082 | ||
|
|
0696ed8396 | ||
|
|
9942df16c8 | ||
|
|
20b7d283b0 | ||
|
|
f39ee76c0c | ||
|
|
4b2390736a | ||
|
|
c8cb7a5c28 | ||
|
|
5217b0b758 | ||
|
|
ddf417a16a | ||
|
|
2f0321f315 | ||
|
|
4d7ff7edc5 | ||
|
|
862914b06e | ||
|
|
6c3c2e6709 | ||
|
|
c92dc1e253 | ||
|
|
e73c0ad1bf | ||
|
|
6a89c6224b | ||
|
|
dcccb61781 | ||
|
|
c0c475ff56 | ||
|
|
6dc768817f | ||
|
|
439cd303ff | ||
|
|
ec0c972d34 | ||
|
|
2a08855e5d | ||
|
|
1c6b3057ea | ||
|
|
ea5f9ec27d | ||
|
|
f2935e4008 | ||
|
|
487001af48 | ||
|
|
71e70e50c5 | ||
|
|
0a857aa09e | ||
|
|
b976b92031 | ||
|
|
db62ed7f3a | ||
|
|
36f0805590 | ||
|
|
49600c5f37 | ||
|
|
eb9ac35a92 | ||
|
|
77148980e0 | ||
|
|
b3d2e82025 | ||
|
|
b25b8cc805 | ||
|
|
8141a01ef7 | ||
|
|
a2f10857e2 | ||
|
|
aea04e5f7c | ||
|
|
60c80611ea | ||
|
|
b1ed3ce55f | ||
|
|
040ed2701c | ||
|
|
00d8e551db | ||
|
|
3e9c99f5f8 | ||
|
|
02bdc4cf04 | ||
|
|
5c43965f0b | ||
|
|
b2376ecc30 | ||
|
|
aeaa6deeb4 | ||
|
|
448e378dc4 | ||
|
|
ac2249f256 | ||
|
|
05932c5a36 | ||
|
|
6f46cdb4ed | ||
|
|
5f527a00cf | ||
|
|
1935db1019 | ||
|
|
1f515e7be5 | ||
|
|
1a5f92021f | ||
|
|
f3c7413f52 | ||
|
|
646db90585 | ||
|
|
1f8a0cf9ab | ||
|
|
bd41af466f | ||
|
|
970221b996 | ||
|
|
15004ff1f1 | ||
|
|
65226f3984 | ||
|
|
b0a7d0b53b | ||
|
|
ee43fd92a0 | ||
|
|
cf39e6d254 | ||
|
|
1260eea690 | ||
|
|
45a3669443 | ||
|
|
c9b91f6d8f | ||
|
|
9a6b903b92 | ||
|
|
7661575573 | ||
|
|
f1abd7682f | ||
|
|
575636e6b7 | ||
|
|
daa997b21b | ||
|
|
5934b17283 | ||
|
|
d7de420d5c | ||
|
|
df273f7f63 | ||
|
|
67b2d1c11c | ||
|
|
15353d0e25 | ||
|
|
f1e21babbf | ||
|
|
34b8822ac8 | ||
|
|
73e0520de7 | ||
|
|
fbc3b109b9 | ||
|
|
229c13a195 | ||
|
|
d7f9ef1cbe | ||
|
|
7930bef48c | ||
|
|
8b0e47da38 | ||
|
|
2316e3fb68 | ||
|
|
4015e2ccd8 | ||
|
|
cf907d029a | ||
|
|
79a6c8b2ef | ||
|
|
c318bd301a | ||
|
|
86a2b2fda0 | ||
|
|
8a8b7319d5 | ||
|
|
2428de23ee | ||
|
|
7ec889e759 | ||
|
|
9d009c40dd | ||
|
|
663d63bde0 | ||
|
|
6b83dcbf8f | ||
|
|
72e925fb6f | ||
|
|
c299e95bc6 | ||
|
|
2e40605d59 | ||
|
|
3455a2ca6c | ||
|
|
6fe858d86a | ||
|
|
7cc161c828 | ||
|
|
e14bf2bfa0 | ||
|
|
34672414c3 | ||
|
|
f7557bcc0f | ||
|
|
e4b6cdfb18 | ||
|
|
8cd3352017 | ||
|
|
c4ec6a1445 | ||
|
|
b3117c27f2 | ||
|
|
54ba4dbb0b | ||
|
|
a744415eb2 | ||
|
|
55f936fee9 | ||
|
|
d9e44e2574 | ||
|
|
52d4313156 | ||
|
|
c2b531e968 |
7
.editorconfig
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[*.{js,jsx,ts,tsx,vue}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
max_line_length = 100
|
||||||
291
.eslintrc.js
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
const { rules: baseBestPracticesRules } = require('eslint-config-airbnb-base/rules/best-practices');
|
||||||
|
const { rules: baseErrorsRules } = require('eslint-config-airbnb-base/rules/errors');
|
||||||
|
const { rules: baseES6Rules } = require('eslint-config-airbnb-base/rules/es6');
|
||||||
|
const { rules: baseImportsRules } = require('eslint-config-airbnb-base/rules/imports');
|
||||||
|
const { rules: baseStyleRules } = require('eslint-config-airbnb-base/rules/style');
|
||||||
|
const { rules: baseVariablesRules } = require('eslint-config-airbnb-base/rules/variables');
|
||||||
|
const tsconfigJson = require('./tsconfig.json');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: {
|
||||||
|
node: true,
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
// Vue specific rules, eslint-plugin-vue
|
||||||
|
// Added by Vue CLI
|
||||||
|
'plugin:vue/essential',
|
||||||
|
|
||||||
|
// Extends eslint-config-airbnb
|
||||||
|
// Added by Vue CLI
|
||||||
|
// Here until https://github.com/vuejs/eslint-config-airbnb/issues/23 is done
|
||||||
|
'@vue/airbnb',
|
||||||
|
|
||||||
|
// Extends @typescript-eslint/recommended
|
||||||
|
// Uses the recommended rules from the @typescript-eslint/eslint-plugin
|
||||||
|
// Added by Vue CLI
|
||||||
|
'@vue/typescript/recommended',
|
||||||
|
],
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...getOwnRules(),
|
||||||
|
...getTurnedOffBrokenRules(),
|
||||||
|
...getOpinionatedRuleOverrides(),
|
||||||
|
...getTodoRules(),
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'**/__tests__/*.{j,t}s?(x)',
|
||||||
|
'**/tests/unit/**/*.spec.{j,t}s?(x)',
|
||||||
|
],
|
||||||
|
env: {
|
||||||
|
mocha: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.ts?(x)', '**/*.d.ts'],
|
||||||
|
parserOptions: {
|
||||||
|
// Setting project is required for some rules such as @typescript-eslint/dot-notation,
|
||||||
|
// @typescript-eslint/return-await and @typescript-eslint/no-throw-literal.
|
||||||
|
// If this property is missing they fail due to missing parser.
|
||||||
|
project: ['./tsconfig.json'],
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...getTypeScriptOverrides(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/tests/**/*.{j,t}s?(x)'],
|
||||||
|
rules: {
|
||||||
|
'no-console': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
function getOwnRules() {
|
||||||
|
return {
|
||||||
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
|
'linebreak-style': ['error', 'unix'], // This is also enforced in .editorconfig and .gitattributes files
|
||||||
|
'import/order': [ // Enforce strict import order taking account into aliases
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
groups: [ // Enforce more strict order than AirBnb
|
||||||
|
'builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
|
||||||
|
pathGroups: [ // Fix manually configured paths being incorrectly grouped as "external"
|
||||||
|
...getAliasesFromTsConfig(),
|
||||||
|
'js-yaml-loader!@/**',
|
||||||
|
].map((pattern) => ({ pattern, group: 'internal' })),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTodoRules() { // Should be worked on separate future commits
|
||||||
|
return {
|
||||||
|
'import/no-extraneous-dependencies': 'off',
|
||||||
|
// Accessibility improvements:
|
||||||
|
'vuejs-accessibility/form-control-has-label': 'off',
|
||||||
|
'vuejs-accessibility/click-events-have-key-events': 'off',
|
||||||
|
'vuejs-accessibility/anchor-has-content': 'off',
|
||||||
|
'vuejs-accessibility/accessible-emoji': 'off',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTurnedOffBrokenRules() {
|
||||||
|
return {
|
||||||
|
// Broken in TypeScript
|
||||||
|
'no-useless-constructor': 'off', // Cannot interpret TypeScript constructors
|
||||||
|
'no-shadow': 'off', // Fails with TypeScript enums
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOpinionatedRuleOverrides() {
|
||||||
|
return {
|
||||||
|
// https://erkinekici.com/articles/linting-trap#no-use-before-define
|
||||||
|
'no-use-before-define': 'off',
|
||||||
|
// https://erkinekici.com/articles/linting-trap#arrow-body-style
|
||||||
|
'arrow-body-style': 'off',
|
||||||
|
// https://erkinekici.com/articles/linting-trap#no-plusplus
|
||||||
|
'no-plusplus': 'off',
|
||||||
|
// https://erkinekici.com/articles/linting-trap#no-param-reassign
|
||||||
|
'no-param-reassign': 'off',
|
||||||
|
// https://erkinekici.com/articles/linting-trap#class-methods-use-this
|
||||||
|
'class-methods-use-this': 'off',
|
||||||
|
// https://erkinekici.com/articles/linting-trap#importprefer-default-export
|
||||||
|
'import/prefer-default-export': 'off',
|
||||||
|
// https://erkinekici.com/articles/linting-trap#disallowing-for-of
|
||||||
|
// Original: https://github.com/airbnb/javascript/blob/d8cb404da74c302506f91e5928f30cc75109e74d/packages/eslint-config-airbnb-base/rules/style.js#L333-L351
|
||||||
|
'no-restricted-syntax': [
|
||||||
|
baseStyleRules['no-restricted-syntax'][0],
|
||||||
|
...baseStyleRules['no-restricted-syntax'].slice(1).filter((rule) => rule.selector !== 'ForOfStatement'),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTypeScriptOverrides() {
|
||||||
|
/*
|
||||||
|
Here until Vue supports AirBnb Typescript overrides (vuejs/eslint-config-airbnb#23).
|
||||||
|
Based on `eslint-config-airbnb-typescript`.
|
||||||
|
Source: https://github.com/iamturns/eslint-config-airbnb-typescript/blob/v16.1.0/lib/shared.js
|
||||||
|
It cannot be used directly due to compilation errors.
|
||||||
|
*/
|
||||||
|
return {
|
||||||
|
'brace-style': 'off',
|
||||||
|
'@typescript-eslint/brace-style': baseStyleRules['brace-style'],
|
||||||
|
|
||||||
|
camelcase: 'off',
|
||||||
|
'@typescript-eslint/naming-convention': [
|
||||||
|
'error',
|
||||||
|
{ selector: 'variable', format: ['camelCase', 'PascalCase', 'UPPER_CASE'] },
|
||||||
|
{ selector: 'function', format: ['camelCase', 'PascalCase'] },
|
||||||
|
{ selector: 'typeLike', format: ['PascalCase'] },
|
||||||
|
],
|
||||||
|
|
||||||
|
'comma-dangle': 'off',
|
||||||
|
'@typescript-eslint/comma-dangle': [
|
||||||
|
baseStyleRules['comma-dangle'][0],
|
||||||
|
{
|
||||||
|
...baseStyleRules['comma-dangle'][1],
|
||||||
|
enums: baseStyleRules['comma-dangle'][1].arrays,
|
||||||
|
generics: baseStyleRules['comma-dangle'][1].arrays,
|
||||||
|
tuples: baseStyleRules['comma-dangle'][1].arrays,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
'comma-spacing': 'off',
|
||||||
|
'@typescript-eslint/comma-spacing': baseStyleRules['comma-spacing'],
|
||||||
|
|
||||||
|
'default-param-last': 'off',
|
||||||
|
'@typescript-eslint/default-param-last': baseBestPracticesRules['default-param-last'],
|
||||||
|
|
||||||
|
'dot-notation': 'off',
|
||||||
|
'@typescript-eslint/dot-notation': baseBestPracticesRules['dot-notation'],
|
||||||
|
|
||||||
|
'func-call-spacing': 'off',
|
||||||
|
'@typescript-eslint/func-call-spacing': baseStyleRules['func-call-spacing'],
|
||||||
|
|
||||||
|
// ❌ Broken for some cases, but still useful.
|
||||||
|
// Here until Prettifier is used.
|
||||||
|
indent: 'off',
|
||||||
|
'@typescript-eslint/indent': baseStyleRules.indent,
|
||||||
|
|
||||||
|
'keyword-spacing': 'off',
|
||||||
|
'@typescript-eslint/keyword-spacing': baseStyleRules['keyword-spacing'],
|
||||||
|
|
||||||
|
'lines-between-class-members': 'off',
|
||||||
|
'@typescript-eslint/lines-between-class-members': baseStyleRules['lines-between-class-members'],
|
||||||
|
|
||||||
|
'no-array-constructor': 'off',
|
||||||
|
'@typescript-eslint/no-array-constructor': baseStyleRules['no-array-constructor'],
|
||||||
|
|
||||||
|
'no-dupe-class-members': 'off',
|
||||||
|
'@typescript-eslint/no-dupe-class-members': baseES6Rules['no-dupe-class-members'],
|
||||||
|
|
||||||
|
'no-empty-function': 'off',
|
||||||
|
'@typescript-eslint/no-empty-function': baseBestPracticesRules['no-empty-function'],
|
||||||
|
|
||||||
|
'no-extra-parens': 'off',
|
||||||
|
'@typescript-eslint/no-extra-parens': baseErrorsRules['no-extra-parens'],
|
||||||
|
|
||||||
|
'no-extra-semi': 'off',
|
||||||
|
'@typescript-eslint/no-extra-semi': baseErrorsRules['no-extra-semi'],
|
||||||
|
|
||||||
|
// ❌ Fails due to missing parser
|
||||||
|
// 'no-implied-eval': 'off',
|
||||||
|
// 'no-new-func': 'off',
|
||||||
|
// '@typescript-eslint/no-implied-eval': baseBestPracticesRules['no-implied-eval'],
|
||||||
|
|
||||||
|
'no-loss-of-precision': 'off',
|
||||||
|
'@typescript-eslint/no-loss-of-precision': baseErrorsRules['no-loss-of-precision'],
|
||||||
|
|
||||||
|
'no-loop-func': 'off',
|
||||||
|
'@typescript-eslint/no-loop-func': baseBestPracticesRules['no-loop-func'],
|
||||||
|
|
||||||
|
'no-magic-numbers': 'off',
|
||||||
|
'@typescript-eslint/no-magic-numbers': baseBestPracticesRules['no-magic-numbers'],
|
||||||
|
|
||||||
|
'no-redeclare': 'off',
|
||||||
|
'@typescript-eslint/no-redeclare': baseBestPracticesRules['no-redeclare'],
|
||||||
|
|
||||||
|
// ESLint variant does not work with TypeScript enums.
|
||||||
|
'no-shadow': 'off',
|
||||||
|
'@typescript-eslint/no-shadow': baseVariablesRules['no-shadow'],
|
||||||
|
|
||||||
|
'no-throw-literal': 'off',
|
||||||
|
'@typescript-eslint/no-throw-literal': baseBestPracticesRules['no-throw-literal'],
|
||||||
|
|
||||||
|
'no-unused-expressions': 'off',
|
||||||
|
'@typescript-eslint/no-unused-expressions': baseBestPracticesRules['no-unused-expressions'],
|
||||||
|
|
||||||
|
'no-unused-vars': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': baseVariablesRules['no-unused-vars'],
|
||||||
|
|
||||||
|
// https://erkinekici.com/articles/linting-trap#no-use-before-define
|
||||||
|
// 'no-use-before-define': 'off',
|
||||||
|
// '@typescript-eslint/no-use-before-define': baseVariablesRules['no-use-before-define'],
|
||||||
|
|
||||||
|
// ESLint variant does not understand TypeScript constructors.
|
||||||
|
// eslint/eslint/#14118, typescript-eslint/typescript-eslint#873
|
||||||
|
'no-useless-constructor': 'off',
|
||||||
|
'@typescript-eslint/no-useless-constructor': baseES6Rules['no-useless-constructor'],
|
||||||
|
|
||||||
|
quotes: 'off',
|
||||||
|
'@typescript-eslint/quotes': baseStyleRules.quotes,
|
||||||
|
|
||||||
|
semi: 'off',
|
||||||
|
'@typescript-eslint/semi': baseStyleRules.semi,
|
||||||
|
|
||||||
|
'space-before-function-paren': 'off',
|
||||||
|
'@typescript-eslint/space-before-function-paren': baseStyleRules['space-before-function-paren'],
|
||||||
|
|
||||||
|
'require-await': 'off',
|
||||||
|
'@typescript-eslint/require-await': baseBestPracticesRules['require-await'],
|
||||||
|
|
||||||
|
'no-return-await': 'off',
|
||||||
|
'@typescript-eslint/return-await': baseBestPracticesRules['no-return-await'],
|
||||||
|
|
||||||
|
'space-infix-ops': 'off',
|
||||||
|
'@typescript-eslint/space-infix-ops': baseStyleRules['space-infix-ops'],
|
||||||
|
|
||||||
|
'object-curly-spacing': 'off',
|
||||||
|
'@typescript-eslint/object-curly-spacing': baseStyleRules['object-curly-spacing'],
|
||||||
|
|
||||||
|
'import/extensions': [
|
||||||
|
baseImportsRules['import/extensions'][0],
|
||||||
|
baseImportsRules['import/extensions'][1],
|
||||||
|
{
|
||||||
|
...baseImportsRules['import/extensions'][2],
|
||||||
|
ts: 'never',
|
||||||
|
tsx: 'never',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
// Changes required is not yet implemented:
|
||||||
|
// 'import/no-extraneous-dependencies': [
|
||||||
|
// baseImportsRules['import/no-extraneous-dependencies'][0],
|
||||||
|
// {
|
||||||
|
// ...baseImportsRules['import/no-extraneous-dependencies'][1],
|
||||||
|
// devDependencies: baseImportsRules[
|
||||||
|
// 'import/no-extraneous-dependencies'
|
||||||
|
// ][1].devDependencies.reduce((result, devDep) => {
|
||||||
|
// const toAppend = [devDep];
|
||||||
|
// const devDepWithTs = devDep.replace(/\bjs(x?)\b/g, 'ts$1');
|
||||||
|
// if (devDepWithTs !== devDep) {
|
||||||
|
// toAppend.push(devDepWithTs);
|
||||||
|
// }
|
||||||
|
// return [...result, ...toAppend];
|
||||||
|
// }, []),
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAliasesFromTsConfig() {
|
||||||
|
return Object.keys(tsconfigJson.compilerOptions.paths)
|
||||||
|
.map((path) => `${path}*`);
|
||||||
|
}
|
||||||
6
.gitattributes
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Prevent Git from auto-converting to CRLF on Windows, and convert to LF on checkin.
|
||||||
|
# * : All files
|
||||||
|
# text=auto : If Git decides content it text, it converts CRLF to LF on checkin.
|
||||||
|
# eol=lf : forces Git to normalize line endings to LF on checkin and prevents conversion
|
||||||
|
# to CRLF when the file is checked out.
|
||||||
|
* text=auto eol=lf
|
||||||
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
github: undergroundwires
|
||||||
56
.github/ISSUE_TEMPLATE/1-bug-report-scripts.md
vendored
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
---
|
||||||
|
name: Bug report (script bug or unexpected script behavior)
|
||||||
|
about: Create a bug report for generated scripts to help privacy.sexy improve
|
||||||
|
labels: bug
|
||||||
|
title: '[BUG]: '
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Thank you for reporting an issue with generated script(s).
|
||||||
|
Please fill in as much of the template below as you're able.
|
||||||
|
As a small open source project with small community, it can sometimes take a long time for issues to be addressed so please be patient.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
<!--
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### OS
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Which OS are you using? What version of OS you were using?
|
||||||
|
On Windows you can find it using "Start button" > "Settings" > "System" > "About".
|
||||||
|
On macOS you can find it using "Apple menu (top left corner)" > "About This Mac".
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Reproduction steps
|
||||||
|
|
||||||
|
<!--
|
||||||
|
How can the bug be recreated?
|
||||||
|
It's the most important information in the bug report. Bugs that cannot be reproduced cannot be fixed and verified.
|
||||||
|
E.g.
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Scripts
|
||||||
|
|
||||||
|
<!--
|
||||||
|
If applicable, please attach the generated privacy.sexy file instead of copy pasting which becomes too long.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Screenshots
|
||||||
|
|
||||||
|
<!--
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Additional information
|
||||||
|
|
||||||
|
<!--
|
||||||
|
If applicable, add any other context about the problem here.
|
||||||
|
-->
|
||||||
55
.github/ISSUE_TEMPLATE/2-bug-report-generic.md
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
name: Bug report (unrelated to generated scripts)
|
||||||
|
about: Create a bug report that's not related to generated scripts to help privacy.sexy improve
|
||||||
|
labels: bug
|
||||||
|
title: '[BUG]: '
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Thank you for reporting an issue.
|
||||||
|
Please fill in as much of the template below as you're able.
|
||||||
|
As a small open source project with small community, it can sometimes take a long time for issues to be addressed so please be patient.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
<!--
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Reproduction steps
|
||||||
|
|
||||||
|
<!--
|
||||||
|
It's the most important information in the bug report. Bugs that cannot be reproduced cannot be fixed and verified.
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Expected behavior
|
||||||
|
|
||||||
|
<!--
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Screenshots
|
||||||
|
|
||||||
|
<!--
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Distribution
|
||||||
|
|
||||||
|
<!--
|
||||||
|
If applicable, mention how you were using privacy.sexy when the bug was encountered:
|
||||||
|
- Web (on Desktop or mobile?)
|
||||||
|
- Or desktop (Windows, macOS or Linux?)
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Additional context
|
||||||
|
|
||||||
|
<!--
|
||||||
|
If applicable, add any other context about the problem here.
|
||||||
|
-->
|
||||||
36
.github/ISSUE_TEMPLATE/3-feature-request.md
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for privacy.sexy
|
||||||
|
labels: enhancement
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Thank you for suggesting an idea to improve privacy better 🤗.
|
||||||
|
Please fill in as much of the template below as you're able.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Problem description
|
||||||
|
|
||||||
|
<!--
|
||||||
|
What are we trying to solve?
|
||||||
|
Please add a clear and concise description of the problem you are seeking to solve with this feature request.
|
||||||
|
E.g. I'm always frustrated when [...]
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Proposed solution
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Describe the solution you'd like in a clear and concise manner.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Alternatives considered
|
||||||
|
|
||||||
|
<!--
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Additional information
|
||||||
|
|
||||||
|
<!--
|
||||||
|
If applicable, add any other context or screenshots about the feature request here.
|
||||||
|
-->
|
||||||
73
.github/ISSUE_TEMPLATE/4-new-script-suggestion.md
vendored
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
---
|
||||||
|
name: New script suggestion
|
||||||
|
about: Suggest a new script for privacy.sexy
|
||||||
|
labels: enhancement
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Thank you for suggesting an script to make privacy better. 🤗
|
||||||
|
Please fill in as much of the template below as you're able.
|
||||||
|
You could alternatively send a PR directly (see CONTRIBUTING.md).
|
||||||
|
-->
|
||||||
|
|
||||||
|
### OS
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Which OS will the new script configure?
|
||||||
|
Either "Windows" or "macOS".
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Name
|
||||||
|
|
||||||
|
<!--
|
||||||
|
The name of the script.
|
||||||
|
It should start with an imperative noun such as "disable", "turn off" , "clear"...
|
||||||
|
E.g. "Disable webcam telemetry"
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Script code
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Code that will be executed when script is selected.
|
||||||
|
Try to keep it as simple and backwards-compatible as possible.
|
||||||
|
Allowed languages:
|
||||||
|
- macOS: bash (sh)
|
||||||
|
- Windows: PowerShell (ps1) or batchfile
|
||||||
|
- 💡 Prioritize the one that's simpler, batchfile if similar.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Revert code
|
||||||
|
|
||||||
|
<!--
|
||||||
|
If applicable, add code that will revert the script code to its original (OS default) state.
|
||||||
|
It may require additional time, but it's much appreciated by the community.
|
||||||
|
Leave blank if the script is nonreversible (e.g. when clearing data without backup).
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Suggested category
|
||||||
|
|
||||||
|
<!--
|
||||||
|
If applicable, suggest one more multiple suitable parent category of script.
|
||||||
|
A category is the item where the script will be presented under.
|
||||||
|
Most likely there already is a category for the script, so check the existing categories.
|
||||||
|
If you're unsure, leave blank and maintainer(s) will choose one.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Suggested recommendation level
|
||||||
|
|
||||||
|
<!--
|
||||||
|
If applicable, suggest recommending the script or not recommending at all.
|
||||||
|
A script should be only recommended if it'll be safe for your grandmother to run.
|
||||||
|
So you have three options here:
|
||||||
|
STANDARD: Non-breaking scripts that does not limit any functionality.
|
||||||
|
STRICT: Scripts that can break certain functionality but not intrusive to common daily OS usage.
|
||||||
|
NONE: Script is not recommended for newbies at all, only those who knows what's going on should select it.
|
||||||
|
If you're unsure, leave blank and maintainer(s) will choose one.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Additional documentation/references
|
||||||
|
|
||||||
|
<!--
|
||||||
|
If applicable, refer to documentation that should show up on the script description.
|
||||||
|
Sources (URLs) should be as high quality as possible e.g. vendor documentation is favored over user forums.
|
||||||
|
-->
|
||||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
blank_issues_enabled: true
|
||||||
59
.github/workflows/checks.build.yaml
vendored
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
name: build-checks
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-web:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ macos, ubuntu, windows ]
|
||||||
|
mode: [ development, test, production ]
|
||||||
|
fail-fast: false # Allows to see results from other combinations
|
||||||
|
runs-on: ${{ matrix.os }}-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
-
|
||||||
|
name: Setup node
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 15.x
|
||||||
|
-
|
||||||
|
name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
-
|
||||||
|
name: Build
|
||||||
|
run: npm run build -- --mode ${{ matrix.mode }}
|
||||||
|
|
||||||
|
# A new job is used due to environments/modes different from Vue CLI, https://github.com/nklayman/vue-cli-plugin-electron-builder/issues/1626
|
||||||
|
build-desktop:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ macos, ubuntu, windows ]
|
||||||
|
mode: [ development, production ] # "test" is not supported https://github.com/nklayman/vue-cli-plugin-electron-builder/issues/1627
|
||||||
|
fail-fast: false # Allows to see results from other combinations
|
||||||
|
runs-on: ${{ matrix.os }}-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
-
|
||||||
|
name: Setup node
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 15.x
|
||||||
|
-
|
||||||
|
name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
-
|
||||||
|
name: Install cross-env
|
||||||
|
# Used to set NODE_ENV due to https://github.com/nklayman/vue-cli-plugin-electron-builder/issues/1626
|
||||||
|
run: npm install --global cross-env
|
||||||
|
-
|
||||||
|
name: Build
|
||||||
|
run: |-
|
||||||
|
cross-env-shell NODE_ENV=${{ matrix.mode }}
|
||||||
|
npm run electron:build -- --publish never --mode ${{ matrix.mode }}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Quality checks
|
name: quality-checks
|
||||||
|
|
||||||
on: [ push, pull_request ]
|
on: [ push, pull_request ]
|
||||||
|
|
||||||
@@ -8,18 +8,20 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
lint-command:
|
lint-command:
|
||||||
- npm run lint:vue
|
- npm run lint:eslint
|
||||||
- npm run lint:yaml
|
- npm run lint:yaml
|
||||||
- npm run lint:md
|
- npm run lint:md
|
||||||
- npm run lint:md:relative-urls
|
- npm run lint:md:relative-urls
|
||||||
- npm run lint:md:consistency
|
- npm run lint:md:consistency
|
||||||
|
os: [ macos, ubuntu, windows ]
|
||||||
|
fail-fast: false # Still interested to see results from other combinations
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setup node
|
- name: Setup node
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 14.x
|
node-version: 15.x
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
- name: Lint
|
- name: Lint
|
||||||
24
.github/workflows/checks.security.yaml
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
name: security-checks
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
paths: [ '/package.json', '/package-lock.json' ] # Allow PRs to be green if they do not introduce dependency change
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * 0' # at 00:00 on every Sunday
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
npm-audit:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
-
|
||||||
|
name: Setup node
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 15.x
|
||||||
|
-
|
||||||
|
name: NPM audit
|
||||||
|
run: exit "$(npm audit)" # Since node 15.x, it does not fail with error if we don't explicitly exit
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Deploy desktop
|
name: release-desktop
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
@@ -10,6 +10,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos, ubuntu, windows]
|
os: [macos, ubuntu, windows]
|
||||||
|
fail-fast: false # So publish runs for other OSes if one fails
|
||||||
runs-on: ${{ matrix.os }}-latest
|
runs-on: ${{ matrix.os }}-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@@ -21,12 +22,13 @@ jobs:
|
|||||||
- name: Setup node
|
- name: Setup node
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: '14.x'
|
node-version: 15.x
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
- name: Run tests
|
- name: Run unit tests
|
||||||
run: npm run test:unit
|
run: npm run test:unit
|
||||||
- name: Publish desktop app
|
- name: Publish desktop app
|
||||||
run: npm run electron:build -- -p always # https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/recipes.html#upload-release-to-github
|
run: npm run electron:build -- -p always # https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/recipes.html#upload-release-to-github
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
EP_GH_IGNORE_TIME: true # Otherwise publishing fails if GitHub release is more than 2 hours old https://github.com/electron-userland/electron-builder/issues/2074
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Bump & release
|
name: release-git
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push: # Ensure a new release is created for each new tag
|
push: # Ensure a new release is created for each new tag
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Deploy site
|
name: release-site
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
@@ -83,13 +83,13 @@ jobs:
|
|||||||
name: "App: Setup node"
|
name: "App: Setup node"
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: '14.x'
|
node-version: 15.x
|
||||||
-
|
-
|
||||||
name: "App: Install dependencies"
|
name: "App: Install dependencies"
|
||||||
run: npm ci
|
run: npm ci
|
||||||
working-directory: site
|
working-directory: site
|
||||||
-
|
-
|
||||||
name: "App: Run tests"
|
name: "App: Run unit tests"
|
||||||
run: npm run test:unit
|
run: npm run test:unit
|
||||||
working-directory: site
|
working-directory: site
|
||||||
-
|
-
|
||||||
23
.github/workflows/security-checks.yaml
vendored
@@ -1,23 +0,0 @@
|
|||||||
name: Security checks
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
pull_request:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 0 * * 0'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
npm-audit:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
-
|
|
||||||
name: Setup node
|
|
||||||
uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
node-version: 14.x
|
|
||||||
-
|
|
||||||
name: NPM audit
|
|
||||||
run: npm audit
|
|
||||||
28
.github/workflows/tests.e2e.yaml
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
name: e2e-tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
run-tests:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [macos, ubuntu, windows]
|
||||||
|
fail-fast: false # So it still runs on other OSes if one of them fails
|
||||||
|
runs-on: ${{ matrix.os }}-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
-
|
||||||
|
name: Setup node
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 15.x
|
||||||
|
-
|
||||||
|
name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
-
|
||||||
|
name: Run e2e tests
|
||||||
|
run: npm run test:e2e -- --headless
|
||||||
30
.github/workflows/tests.integration.yaml
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
name: integration-tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
schedule: # To get notified about problems from third party dependencies
|
||||||
|
- cron: '0 0 * * 0' # at 00:00 on every Sunday
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
run-tests:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [macos, ubuntu, windows]
|
||||||
|
fail-fast: false # So it still runs on other OSes if one of them fails
|
||||||
|
runs-on: ${{ matrix.os }}-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
-
|
||||||
|
name: Setup node
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 15.x
|
||||||
|
-
|
||||||
|
name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
-
|
||||||
|
name: Run integration tests
|
||||||
|
run: npm run test:integration
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
name: Test
|
name: unit-tests
|
||||||
|
|
||||||
on: [ push, pull_request ]
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
run-tests:
|
run-tests:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos, ubuntu, windows]
|
os: [macos, ubuntu, windows]
|
||||||
|
fail-fast: false # So it still runs on other OSes if one of them fails
|
||||||
runs-on: ${{ matrix.os }}-latest
|
runs-on: ${{ matrix.os }}-latest
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
@@ -16,10 +19,10 @@ jobs:
|
|||||||
name: Setup node
|
name: Setup node
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: '14.x'
|
node-version: 15.x
|
||||||
-
|
-
|
||||||
name: Install dependencies
|
name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
-
|
-
|
||||||
name: Run tests
|
name: Run unit tests
|
||||||
run: npm run test:unit
|
run: npm run test:unit
|
||||||
6
.gitignore
vendored
@@ -1,6 +1,10 @@
|
|||||||
node_modules
|
node_modules
|
||||||
dist/
|
dist/
|
||||||
.vs
|
.vs
|
||||||
.vscode
|
.vscode/**/*
|
||||||
|
!.vscode/extensions.json
|
||||||
#Electron-builder output
|
#Electron-builder output
|
||||||
/dist_electron
|
/dist_electron
|
||||||
|
# Cypress
|
||||||
|
/tests/e2e/screenshots
|
||||||
|
/tests/e2e/videos
|
||||||
|
|||||||
23
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
// Common
|
||||||
|
"editorconfig.editorconfig", // Applies .editorconfig to follow project style.
|
||||||
|
"wengerk.highlight-bad-chars", // Highlights bad chars.
|
||||||
|
"wayou.vscode-todo-highlight", // Highlights TODO.
|
||||||
|
"wix.vscode-import-cost", // Shows in KB how much a require include in code.
|
||||||
|
// Documentation
|
||||||
|
"davidanson.vscode-markdownlint", // Lints markdown.
|
||||||
|
// TypeScript / JavaScript
|
||||||
|
"dbaeumer.vscode-eslint", // Lints JavaScript/TypeScript.
|
||||||
|
"pmneo.tsimporter", // Provides better auto-complete for TypeScripts imports.
|
||||||
|
// Vue
|
||||||
|
"jcbuisson.vue", // Highlights syntax.
|
||||||
|
"octref.vetur", // Adds Vetur, Vue tooling support.
|
||||||
|
// Scripting
|
||||||
|
"timonwong.shellcheck", // Lints bash files.
|
||||||
|
"ms-vscode.powershell", // Lints PowerShell files.
|
||||||
|
"ms-python.python", // Lints Python files.
|
||||||
|
// Distribution
|
||||||
|
"ms-azuretools.vscode-docker" // Adds Docker support.
|
||||||
|
]
|
||||||
|
}
|
||||||
563
CHANGELOG.md
@@ -1,200 +1,427 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.11.3 (2022-01-05)
|
||||||
|
|
||||||
|
* Fix double backlashes in Windows vscode scripts | [5f091bb](https://github.com/undergroundwires/privacy.sexy/commit/5f091bb6abed878271e2321cd784f34436c677bd)
|
||||||
|
* Fix OS desktop detection tests and edge cases | [a8358b8](https://github.com/undergroundwires/privacy.sexy/commit/a8358b8e7a93214f3d22a4488007ded5f623d845)
|
||||||
|
* Fix clearing Windows product key showing dialog | [9b6636e](https://github.com/undergroundwires/privacy.sexy/commit/9b6636e21a922a4750dc19f4854f8ae679187926)
|
||||||
|
* Document and unrecommend Cloud Experience Host | [9b5e0b0](https://github.com/undergroundwires/privacy.sexy/commit/9b5e0b0591fee56af52d83334a1f19180a49516f)
|
||||||
|
* Add initial e2e testing with cypress | [ddd2e70](https://github.com/undergroundwires/privacy.sexy/commit/ddd2e704dbd361cbd219f3dfe644b983ad254095)
|
||||||
|
* Restructure pipelines and badges | [5a2c263](https://github.com/undergroundwires/privacy.sexy/commit/5a2c263af35b8785e75ead6c43c3f17186dc15c8)
|
||||||
|
* Fix failing of functions without revert code | [87de017](https://github.com/undergroundwires/privacy.sexy/commit/87de017afd6e08acbd2deea150c6af9c7ee778fc)
|
||||||
|
* Fix typos in privacy modal #109 | [a1871a2](https://github.com/undergroundwires/privacy.sexy/commit/a1871a2982c9e3192193f836b97b1a6ccda5a2ab)
|
||||||
|
* Refactor to add readonly interfaces | [c3c5b89](https://github.com/undergroundwires/privacy.sexy/commit/c3c5b897f308f613c252182a02cdd4cfa7150fa3)
|
||||||
|
* Document and unrecommend AAD app removal #24, #54 | [455084c](https://github.com/undergroundwires/privacy.sexy/commit/455084c17b32d11d046515e8dc1447adf4bea4c3)
|
||||||
|
* Migrate from TSLint to ESLint | [61b475f](https://github.com/undergroundwires/privacy.sexy/commit/61b475fa8de433cdada2efa7eac197683aacd956)
|
||||||
|
* Add build checks and improve existing CI/CD checks | [17298f0](https://github.com/undergroundwires/privacy.sexy/commit/17298f0b2c51cb9becc0eb2ffe0d93d6a4c503a6)
|
||||||
|
* Upgrade to Vue CLI 5 (and webpack 5) | [96265b7](https://github.com/undergroundwires/privacy.sexy/commit/96265b75deafb85978b16460138fb4a814c07cfe)
|
||||||
|
* Refactor code to comply with ESLint rules | [5b1fbe1](https://github.com/undergroundwires/privacy.sexy/commit/5b1fbe1e2fb1354a5f060f8c8e3794ce756e16a7)
|
||||||
|
* Fix mutated line endings on Windows | [bd23faa](https://github.com/undergroundwires/privacy.sexy/commit/bd23faa28f6d781581a33d5b780f4b33f7e2cd8b)
|
||||||
|
* Refactor to improve iterations | [31f7091](https://github.com/undergroundwires/privacy.sexy/commit/31f70913a2f30baf5a9d6690f192e6a63da50114)
|
||||||
|
* win: unrecommend and document Live ID service #100 | [d11a674](https://github.com/undergroundwires/privacy.sexy/commit/d11a674a3c4ad8f4972a870c2f0977ac53297273)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.11.2...0.11.3)
|
||||||
|
|
||||||
|
## 0.11.2 (2021-12-03)
|
||||||
|
|
||||||
|
* Fix Windows TrustedInstaller session errors | [20a0071](https://github.com/undergroundwires/privacy.sexy/commit/20a0071c0d3d769a8f31218abdbfc4cafa25c6ff)
|
||||||
|
* Improve tests for `UserSelection` | [2f90cac](https://github.com/undergroundwires/privacy.sexy/commit/2f90cac52ab9e57615aeec41f9daa842bce770a5)
|
||||||
|
* Fix disabling/enabling Defender on Windows #104 | [2e08293](https://github.com/undergroundwires/privacy.sexy/commit/2e082932c952b0849ab2b8709ff0c75293b88e95)
|
||||||
|
* Refactor Saas naming, structure and modules | [bf83c58](https://github.com/undergroundwires/privacy.sexy/commit/bf83c58982ffa178facc6d35e50c7f1eac7ff236)
|
||||||
|
* Fix Defender features errors in Windows #104 | [d7761ab](https://github.com/undergroundwires/privacy.sexy/commit/d7761ab30e7f1e10a2919c196804d67511d6163a)
|
||||||
|
* Fix unintendedly inlined Windows scripts | [f2d9881](https://github.com/undergroundwires/privacy.sexy/commit/f2d988138257ff184884e4adc83c39e3bc247e9b)
|
||||||
|
* Fix Defender error due to non-english Windows #104 | [7c02ffb](https://github.com/undergroundwires/privacy.sexy/commit/7c02ffb6c95382b94f0b05e6f259cc418ec91c93)
|
||||||
|
* Improve and unify disabling of Windows services | [70cdf38](https://github.com/undergroundwires/privacy.sexy/commit/70cdf3865a0de3214fc9e26fbdada4b0cb413c46)
|
||||||
|
* Improve Windows defender docs and errors #104 | [d2518b1](https://github.com/undergroundwires/privacy.sexy/commit/d2518b11a7774ec58b9b46a691e2f013855bf0f9)
|
||||||
|
* Unrecommend and complete Windows Push Notif. #101 | [c65209e](https://github.com/undergroundwires/privacy.sexy/commit/c65209e6a99230f15ace8955e8d5a6f3333d146b)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.11.1...0.11.2)
|
||||||
|
|
||||||
|
## 0.11.1 (2021-11-04)
|
||||||
|
|
||||||
|
* Update dependencies | [64631a4](https://github.com/undergroundwires/privacy.sexy/commit/64631a4552fad7f7b06286aba8d3ca2d731f9342)
|
||||||
|
* Fix, document, unrecommend Windows browser cleanup | [5ead1a0](https://github.com/undergroundwires/privacy.sexy/commit/5ead1a087d91948890bc4ae6fea176123f18c285)
|
||||||
|
* Fix failing URL status checking integration tests | [799fb09](https://github.com/undergroundwires/privacy.sexy/commit/799fb091b8eb06c70ac0c67f2ef5385dce73501f)
|
||||||
|
* Refactor to remove "Async" function name suffix | [82c43ba](https://github.com/undergroundwires/privacy.sexy/commit/82c43ba2e37fb6e7f62ccd9bec8c5f48575f0613)
|
||||||
|
* Fix dead URLs and use forks as GitHub references | [97ddc02](https://github.com/undergroundwires/privacy.sexy/commit/97ddc027cb5395a74991cabc1d8c875ee945636d)
|
||||||
|
* Fix website not loading on Safari | [0db8cc4](https://github.com/undergroundwires/privacy.sexy/commit/0db8cc420655e01cbbed57c4658489b761a15899)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.11.0...0.11.1)
|
||||||
|
|
||||||
|
## 0.11.0 (2021-10-21)
|
||||||
|
|
||||||
|
* Change "grouping" to "view" | [c0c475f](https://github.com/undergroundwires/privacy.sexy/commit/c0c475ff564b23a4dabcc03ac2909207a8eb61ce)
|
||||||
|
* Tighten parameter substitution tolerance | [dcccb61](https://github.com/undergroundwires/privacy.sexy/commit/dcccb617813625c224a28242c5b965bb4cd6f189)
|
||||||
|
* Add optionality for parameters | [6a89c62](https://github.com/undergroundwires/privacy.sexy/commit/6a89c6224bdef5eb96980471f3b3935b9351b197)
|
||||||
|
* Do not collapse cards on links and code area #88 | [e73c0ad](https://github.com/undergroundwires/privacy.sexy/commit/e73c0ad1bf922b1dd3360fc5aafc3434951fa63c)
|
||||||
|
* Add scripts to disable, hide and opt-out from Siri | [c92dc1e](https://github.com/undergroundwires/privacy.sexy/commit/c92dc1e25387c65a3a41ca64d2a23cf8131b4c86)
|
||||||
|
* Improve macOS scripts for cleaning OS logs | [6c3c2e6](https://github.com/undergroundwires/privacy.sexy/commit/6c3c2e6709ec84f8e0411f19c024bab2c7e5753b)
|
||||||
|
* Add "with" expression for templating #53 | [862914b](https://github.com/undergroundwires/privacy.sexy/commit/862914b06ea9ef74c4b58a9a4164a10a38273638)
|
||||||
|
* Add support for pipes in templates #53 | [4d7ff7e](https://github.com/undergroundwires/privacy.sexy/commit/4d7ff7edc5a96cc0d99d3c1ca4fdf9bbdace3fd2)
|
||||||
|
* Bump node environment to 15.x | [2f0321f](https://github.com/undergroundwires/privacy.sexy/commit/2f0321f315ac0da8c713dd50e37032f1de194942)
|
||||||
|
* Add new UX for optionally downloading updates | [ddf417a](https://github.com/undergroundwires/privacy.sexy/commit/ddf417a16a79551b43576befab0541ea08487969)
|
||||||
|
* Add pipes to write pretty PowerShell #53 | [5217b0b](https://github.com/undergroundwires/privacy.sexy/commit/5217b0b7587ccfe509ba8adc3a7748b9bae14d7a)
|
||||||
|
* Improve alignment, padding/margin issues on UI | [c8cb7a5](https://github.com/undergroundwires/privacy.sexy/commit/c8cb7a5c28420557319606da82f56b011e88f470)
|
||||||
|
* Support disabling per-user services in Windows #16 | [4b23907](https://github.com/undergroundwires/privacy.sexy/commit/4b2390736ac1f9de2d5176b7b07da0e827112f9a)
|
||||||
|
* Add script to remove Meet Now icon in Windows | [f39ee76](https://github.com/undergroundwires/privacy.sexy/commit/f39ee76c0cda95f54502b19d5c49390fd0f12b5e)
|
||||||
|
* Add support for more depth in function calls | [20b7d28](https://github.com/undergroundwires/privacy.sexy/commit/20b7d283b02dd751dfbde18ef1fe334c6bf76e2b)
|
||||||
|
* Increase default screen width on desktop app | [9942df1](https://github.com/undergroundwires/privacy.sexy/commit/9942df16c8334ff041fb92f432a3a29e351c88df)
|
||||||
|
* Improve disabling of SmartScreen #74 | [0696ed8](https://github.com/undergroundwires/privacy.sexy/commit/0696ed8396e298a358bec17adb91c9145dd90418)
|
||||||
|
* Remove integration tests from deployments #90 | [37ad26a](https://github.com/undergroundwires/privacy.sexy/commit/37ad26a082851c02497c36e7fce40555b9480e11)
|
||||||
|
* Use a consistent color system | [b08a6b5](https://github.com/undergroundwires/privacy.sexy/commit/b08a6b5cecf4a53023053695292146edbd24b960)
|
||||||
|
* Add semi-automatic update support for macOS | [410bcd8](https://github.com/undergroundwires/privacy.sexy/commit/410bcd82445097c29c9fcf0eabf7af9ebcb93c1e)
|
||||||
|
* Add more ways to disable and clean Defender #74 | [2492f2d](https://github.com/undergroundwires/privacy.sexy/commit/2492f2d8141b3abdf590ccad59680b1f50ecb59e)
|
||||||
|
* Add privacy over security scripts for macOS #83 | [236a0f6](https://github.com/undergroundwires/privacy.sexy/commit/236a0f6c8241294fc397194cd1b20bdeccbbb50b)
|
||||||
|
* Change PowerShell double quotes escape | [9aa8166](https://github.com/undergroundwires/privacy.sexy/commit/9aa816689146ee6cd86d8262112677c38651c6bd)
|
||||||
|
* Change theme colors | [a8031d1](https://github.com/undergroundwires/privacy.sexy/commit/a8031d18d520dd3b0567f7b8cfe2dcd694b65073)
|
||||||
|
* Improve security hardening for macOS | [e6152fa](https://github.com/undergroundwires/privacy.sexy/commit/e6152fa76f5e7d23b0f79d5dd98713daaecbff90)
|
||||||
|
* Support disabling of protected services #74 | [ab8bce7](https://github.com/undergroundwires/privacy.sexy/commit/ab8bce768650a10677f0a13b3a9fae93c83802ff)
|
||||||
|
* Fix minor issues with Defender scripts | [739287a](https://github.com/undergroundwires/privacy.sexy/commit/739287ac71b3f8b04348fc101f1fa06f2d7d86a2)
|
||||||
|
* Update screenshot | [504fa05](https://github.com/undergroundwires/privacy.sexy/commit/504fa056d7d8b17fc20afd398f9a557495fca7e8)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.10.3...0.11.0)
|
||||||
|
|
||||||
|
## 0.10.3 (2021-08-27)
|
||||||
|
|
||||||
|
* unrecommend VSS and document its breaking behavior | [7714898](https://github.com/undergroundwires/privacy.sexy/commit/77148980e08859f89c15c6604e55b56ce4f74358)
|
||||||
|
* fix incorrect modification of Desktop folder on ThisPC (#71) | [eb9ac35](https://github.com/undergroundwires/privacy.sexy/commit/eb9ac35a923325cc2c9983ef71c0d904337a58f5)
|
||||||
|
* add initial integration tests | [49600c5](https://github.com/undergroundwires/privacy.sexy/commit/49600c5f37ca33c1687885fdf02a71ef7d3e6e8c)
|
||||||
|
* unify usage of sleepAsync and add tests | [36f0805](https://github.com/undergroundwires/privacy.sexy/commit/36f08055909f371fd9cbe3480ea813b963aea22b)
|
||||||
|
* fix broken URLs and automate broken URL checks #70 | [db62ed7](https://github.com/undergroundwires/privacy.sexy/commit/db62ed7f3ac63e9f2d762eb946060595eb9f5626)
|
||||||
|
* fix hiding recent files in quick access | [b976b92](https://github.com/undergroundwires/privacy.sexy/commit/b976b920318dba55b32d39f148fdca4f6be3cce3)
|
||||||
|
* bump dependencies to latest #75, #69 | [0a857aa](https://github.com/undergroundwires/privacy.sexy/commit/0a857aa09ee703d34ad0422bd1731158017a9a58)
|
||||||
|
* Fix NTP configuration before running the service (#72) | [71e70e5](https://github.com/undergroundwires/privacy.sexy/commit/71e70e50c51249bb10f6203414948b325acc2b2a)
|
||||||
|
* Fix typo on main page (#82) | [487001a](https://github.com/undergroundwires/privacy.sexy/commit/487001af485fdbb958615d7b52c09c2e386ddaf2)
|
||||||
|
* Improve issue templates | [f2935e4](https://github.com/undergroundwires/privacy.sexy/commit/f2935e4008f1231ef174f8932290e11715564d20)
|
||||||
|
* Fix infinitely subscribing to state changes | [ea5f9ec](https://github.com/undergroundwires/privacy.sexy/commit/ea5f9ec27df7cec6ac575e23fef18948d2b8e68a)
|
||||||
|
* Fix select options being clickable when disabled | [1c6b305](https://github.com/undergroundwires/privacy.sexy/commit/1c6b3057ea6e45125cadf374f20a905712ccdf3c)
|
||||||
|
* Fix tests for `ParameterSubstitutionParser` | [2a08855](https://github.com/undergroundwires/privacy.sexy/commit/2a08855e5d1bdf74354fd692cbfebd1a48e495ac)
|
||||||
|
* Fix excessive highlighting on hover | [ec0c972](https://github.com/undergroundwires/privacy.sexy/commit/ec0c972d348ffd5897f115d201031b704875b56a)
|
||||||
|
* Fix dead URLs | [439cd30](https://github.com/undergroundwires/privacy.sexy/commit/439cd303ff3db96a53664e5f44fefe12b95c5e6c)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.10.2...0.10.3)
|
||||||
|
|
||||||
|
## 0.10.2 (2021-04-19)
|
||||||
|
|
||||||
|
* in CI/CD, run other tests/check even if one of them fails | [5c43965](https://github.com/undergroundwires/privacy.sexy/commit/5c43965f0bc44f991ada7d3bad68937a80665dc3)
|
||||||
|
* fix desktop initial window size being bigger than current display size on smaller Linux/Windows screens | [02bdc4c](https://github.com/undergroundwires/privacy.sexy/commit/02bdc4cf0426c452f3fc9af52b819ca9b0757290)
|
||||||
|
* refactor extra code, duplicates, complexity | [00d8e55](https://github.com/undergroundwires/privacy.sexy/commit/00d8e551db001247fadfb6f6af7a4c5ce19a9e64)
|
||||||
|
* improve disabling ads and marketing #65 | [040ed27](https://github.com/undergroundwires/privacy.sexy/commit/040ed2701c4a468749901f4c5369b221bc0973c4)
|
||||||
|
* document breaking behavior in script name #64 | [b1ed3ce](https://github.com/undergroundwires/privacy.sexy/commit/b1ed3ce55f2d003cad1ead23e674aa66d4eb5802)
|
||||||
|
* add module alias '@tests/' | [60c8061](https://github.com/undergroundwires/privacy.sexy/commit/60c80611eab227791fabb883caf93418cef5fd00)
|
||||||
|
* document chromium warning for policy changes | [aea04e5](https://github.com/undergroundwires/privacy.sexy/commit/aea04e5f7cd48fbb9b407b68ade75575a6064c82)
|
||||||
|
* fix script revert activating recommendation level | [a2f1085](https://github.com/undergroundwires/privacy.sexy/commit/a2f10857e2a8debb3ce01f79b0dfbe8649ea9a17)
|
||||||
|
* fix typo and dead URL in Windows scripts (#70) | [8141a01](https://github.com/undergroundwires/privacy.sexy/commit/8141a01ef798331b4d82f5ca95f7b18df4f6f912)
|
||||||
|
* fix vue warning for undefined property during render | [b25b8cc](https://github.com/undergroundwires/privacy.sexy/commit/b25b8cc8052655af70b0695c6c3085974d783bb6)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.10.1...0.10.2)
|
||||||
|
|
||||||
|
## 0.10.1 (2021-03-25)
|
||||||
|
|
||||||
|
* refactor script compilation to make it easy to add new expressions #41 #53 | [646db90](https://github.com/undergroundwires/privacy.sexy/commit/646db9058541cebd0af437554de04fdc6bb63a6e)
|
||||||
|
* restructure presentation layer | [f3c7413](https://github.com/undergroundwires/privacy.sexy/commit/f3c7413f529be4a00dba7b0ab23904b48ea13a35)
|
||||||
|
* fix a test where "it" is not used inside "describe" | [1a5f920](https://github.com/undergroundwires/privacy.sexy/commit/1a5f92021f7423cd039f8f5326cd6f99b355c962)
|
||||||
|
* bump dependencies to latest | [1f515e7](https://github.com/undergroundwires/privacy.sexy/commit/1f515e7be525291c960ccb71db05312db6da53f5)
|
||||||
|
* fix throttle function not being able to run with argument(s) | [1935db1](https://github.com/undergroundwires/privacy.sexy/commit/1935db10192051401ab00ca2cd767955d0d3b866)
|
||||||
|
* fix fs module hanging not allowing code to run | [5f527a0](https://github.com/undergroundwires/privacy.sexy/commit/5f527a00cf225d3e74b3f6577d6e2456e919de24)
|
||||||
|
* refactor all modals to use same dialog component | [6f46cdb](https://github.com/undergroundwires/privacy.sexy/commit/6f46cdb4ed49a8941c6c0dde5c5e2a816c06daef)
|
||||||
|
* fix safari cleanup scripts that are not working on modern versions | [05932c5](https://github.com/undergroundwires/privacy.sexy/commit/05932c5a36446d551c5bc811165e3295fbe15e3f)
|
||||||
|
* refactor features to use shared functions #41 | [ac2249f](https://github.com/undergroundwires/privacy.sexy/commit/ac2249f25664827d8a6d2c7ebd659ccf126b0cde)
|
||||||
|
* increase performance by polyfilling ResizeObserver only if required | [448e378](https://github.com/undergroundwires/privacy.sexy/commit/448e378dc4501f9de69af63634c87d0e5060bf52)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.10.0...0.10.1)
|
||||||
|
|
||||||
|
## 0.10.0 (2021-03-02)
|
||||||
|
|
||||||
|
* allow functions to call other functions #53 | [7661575](https://github.com/undergroundwires/privacy.sexy/commit/7661575573c6d3e8f4bc28bfa7a124a764c72ef9)
|
||||||
|
* add option to run script directly in desktop app | [9a6b903](https://github.com/undergroundwires/privacy.sexy/commit/9a6b903b9297802845043fd41115756acd4a145c)
|
||||||
|
* add script to automatically kill devicecensus process | [c9b91f6](https://github.com/undergroundwires/privacy.sexy/commit/c9b91f6d8f9bd16308b6beda119e7154a985b6cf)
|
||||||
|
* refactor disabling application experience and document better | [45a3669](https://github.com/undergroundwires/privacy.sexy/commit/45a3669443d82855a52f60524d341c15f380f9e7)
|
||||||
|
* escape printed characters to prevent command injection #45 | [1260eea](https://github.com/undergroundwires/privacy.sexy/commit/1260eea690e4fa5420e58c9de9f88cc29cb242db)
|
||||||
|
* move code area to right on bigger screens | [cf39e6d](https://github.com/undergroundwires/privacy.sexy/commit/cf39e6d2541ea547f41d9553c380c54c24c58038)
|
||||||
|
* more scripts to disable speech recognition and Cortana | [ee43fd9](https://github.com/undergroundwires/privacy.sexy/commit/ee43fd92a019ebd26c13890f9146c5b5bb56afaf)
|
||||||
|
* add more macos scripts for privacy cleanup | [b0a7d0b](https://github.com/undergroundwires/privacy.sexy/commit/b0a7d0b53b3d8ac144a0241d70c037f460b0c0cc)
|
||||||
|
* add better error messages to setting vscode settings | [65226f3](https://github.com/undergroundwires/privacy.sexy/commit/65226f3984480d0bc7932fd8d76a328f08308850)
|
||||||
|
* remove windows scripts for removing non-bloating system apps #55 | [15004ff](https://github.com/undergroundwires/privacy.sexy/commit/15004ff1f1fb85a1d92e11ef695bcb2f37110610)
|
||||||
|
* remove "preview" disclaimer from macOS | [970221b](https://github.com/undergroundwires/privacy.sexy/commit/970221b996e25fe5b029cbaa78607c9bbc8c3c0e)
|
||||||
|
* update screenshot | [bd41af4](https://github.com/undergroundwires/privacy.sexy/commit/bd41af466fd135f7dc2f171633e4f60d8547c373)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.9.2...0.10.0)
|
||||||
|
|
||||||
|
## 0.9.2 (2021-02-13)
|
||||||
|
|
||||||
|
* do not compile with unused locals vuejs/vetur#1063 | [73e0520](https://github.com/undergroundwires/privacy.sexy/commit/73e0520de70cdbaf0ecdc6e9be5e85f003fcfb79)
|
||||||
|
* fix wrong path for NvTelemtry file in NVIDIA script | [34b8822](https://github.com/undergroundwires/privacy.sexy/commit/34b8822ac821acb47e483e21b57e380551bcf455)
|
||||||
|
* refactor event handling to consume base class for lifecycling | [f1e21ba](https://github.com/undergroundwires/privacy.sexy/commit/f1e21babbfaac21903594a37e30163bfe3338279)
|
||||||
|
* make compiler throw if a function call includes an unexpected parameter | [15353d0](https://github.com/undergroundwires/privacy.sexy/commit/15353d0e2513c89ee4ffd9d9c5e9e83ef69b96b6)
|
||||||
|
* refactor vscode configuration scripts using functions #41 | [67b2d1c](https://github.com/undergroundwires/privacy.sexy/commit/67b2d1c11cd5b131dff93a4437db79d96ed8b3dc)
|
||||||
|
* refactor state handling to make application available independent of the state | [df273f7](https://github.com/undergroundwires/privacy.sexy/commit/df273f7f635ab156ac51a8dfb3fec66c4979f1c4)
|
||||||
|
* add test to ensure correct shared functions are being parsed | [d7de420](https://github.com/undergroundwires/privacy.sexy/commit/d7de420d5c91bd9ce64880cd4a4391ad3a0a5401)
|
||||||
|
* refactor and add tests for NonCollapsingDirective | [5934b17](https://github.com/undergroundwires/privacy.sexy/commit/5934b1728328c3b2ece1597b74dd87477d162175)
|
||||||
|
* add GitHub issue templates | [daa997b](https://github.com/undergroundwires/privacy.sexy/commit/daa997b21b624d133c6f5e4cd6b70214588f9144)
|
||||||
|
* correct the typo in application.md (#60) | [575636e](https://github.com/undergroundwires/privacy.sexy/commit/575636e6b728a2bdd1a9bd72c57bbf2752f10887)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.9.1...0.9.2)
|
||||||
|
|
||||||
|
## 0.9.1 (2021-01-23)
|
||||||
|
|
||||||
|
* in CI/CD, allow publishing to github if release is more than 2 hours old electron-userland/electron-builder#2074 | [cf907d0](https://github.com/undergroundwires/privacy.sexy/commit/cf907d029a6d80682ba78ec887a9c4fab639db51)
|
||||||
|
* in CI/CD, publish packages for other OSes if single one fails | [4015e2c](https://github.com/undergroundwires/privacy.sexy/commit/4015e2ccd8492e0693365b70fbfe3bd0ac7a6ea2)
|
||||||
|
* specify desktop publish targets as defaults (may) change | [2316e3f](https://github.com/undergroundwires/privacy.sexy/commit/2316e3fb6867e5d765eafcf675b77f88bd2a0f52)
|
||||||
|
* fix selection state indicator on cards not showing up | [8b0e47d](https://github.com/undergroundwires/privacy.sexy/commit/8b0e47da38c49cfe2645d7d25970c448ecd200f8)
|
||||||
|
* transpile using babel for legacy browser support | [7930bef](https://github.com/undergroundwires/privacy.sexy/commit/7930bef48c4e9a4fe0823673958ed8377f5ee533)
|
||||||
|
* fix node APIs no longer working on desktop nklayman/vue-cli-plugin-electron-builder#610, nklayman/vue-cli-plugin-electron-builder#742 | [d7f9ef1](https://github.com/undergroundwires/privacy.sexy/commit/d7f9ef1cbebe911aa19f29be8c5fa9360550793e)
|
||||||
|
* improve explanation for selections | [229c13a](https://github.com/undergroundwires/privacy.sexy/commit/229c13a195dee92e4a31731b7b41c319273a16f1)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.9.0...0.9.1)
|
||||||
|
|
||||||
|
## 0.9.0 (2021-01-15)
|
||||||
|
|
||||||
|
* refactor application.yaml to become an os definition #40 | [f7557bc](https://github.com/undergroundwires/privacy.sexy/commit/f7557bcc0faf44e8395b68c7eb14c5f715f07b92)
|
||||||
|
* refactor folders to move "/state" (IApplicationState) inside "/context" (IApplicationContext) | [3467241](https://github.com/undergroundwires/privacy.sexy/commit/34672414c3e0757173036e351df0a73c1708ded5)
|
||||||
|
* add scripts to prevent family safety monitoring | [e14bf2b](https://github.com/undergroundwires/privacy.sexy/commit/e14bf2bfa03efe28ff39942c9891fca605f13eed)
|
||||||
|
* rework Cortana scripts to remove duplicates, better document and support Windows version 2004/2009 #43 | [7cc161c](https://github.com/undergroundwires/privacy.sexy/commit/7cc161c828a3fa49f6f254e31834a95a502b7aa2)
|
||||||
|
* rename Application to CategoryCollection #40 | [6fe858d](https://github.com/undergroundwires/privacy.sexy/commit/6fe858d86aeb0f8b6d5ae5c2a5e3c25ff32e5f6f)
|
||||||
|
* add script to clean previous windows installation #35 | [3455a2c](https://github.com/undergroundwires/privacy.sexy/commit/3455a2ca6ce13f9b0e866d88532a5c3d6de30d4d)
|
||||||
|
* refactor to allow switching ICategoryCollection context #40 | [2e40605](https://github.com/undergroundwires/privacy.sexy/commit/2e40605d59eb764768457c6af561487e7ff09777)
|
||||||
|
* fix typo causing uninstalling capabilities to fail #51 | [c299e95](https://github.com/undergroundwires/privacy.sexy/commit/c299e95bc6d588317b69a9efcf5752ff5c9c3926)
|
||||||
|
* improve uninstalling apps to show errors and exit if taking ownership fails #51 | [72e925f](https://github.com/undergroundwires/privacy.sexy/commit/72e925fb6f908cd58fb50618f29726b3fb54a7f1)
|
||||||
|
* move application.yaml to collections/windows.yaml #40 | [6b83dcb](https://github.com/undergroundwires/privacy.sexy/commit/6b83dcbf8fa08b4efe9974c7d7a667458f7c595c)
|
||||||
|
* recommend onedrive removal on strict mode | [663d63b](https://github.com/undergroundwires/privacy.sexy/commit/663d63bde08dd1b0d43ec144c758399cec90ec70)
|
||||||
|
* document app connector removal and recommend on strict mode | [9d009c4](https://github.com/undergroundwires/privacy.sexy/commit/9d009c40dd411c73c7ae032a78ec51490ecce024)
|
||||||
|
* recommend removing cortana taskbar icon on standard mode | [7ec889e](https://github.com/undergroundwires/privacy.sexy/commit/7ec889e759df04bba99d3b6c4d0597809bd94058)
|
||||||
|
* fix unintended null file creation #52 | [2428de2](https://github.com/undergroundwires/privacy.sexy/commit/2428de23ee02de987e7e6ec80ebd67be369d9048)
|
||||||
|
* add initial macOS support #40 | [8a8b731](https://github.com/undergroundwires/privacy.sexy/commit/8a8b7319d539b31c1d8ad9eaf541762d64f02493)
|
||||||
|
* add scripts to manage chromium based edge | [86a2b2f](https://github.com/undergroundwires/privacy.sexy/commit/86a2b2fda0b6a2565c550758c7c175fa795926b7)
|
||||||
|
* update screenshot | [c318bd3](https://github.com/undergroundwires/privacy.sexy/commit/c318bd301a2cbebbf5cdba06c0f18ac291aa4788)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.8.2...0.9.0)
|
||||||
|
|
||||||
|
## 0.8.2 (2020-12-26)
|
||||||
|
|
||||||
|
* replace ampersand in "Movies & TV app" with "and" to prevent batch file from misinterpreting it (#45) | [52d4313](https://github.com/undergroundwires/privacy.sexy/commit/52d4313156d2dcbc508b7271e7d9dfd45723d7bc)
|
||||||
|
* update dependencies to latest #46 | [d9e44e2](https://github.com/undergroundwires/privacy.sexy/commit/d9e44e25744e5d0aa01b8fc0f0af74c48027aea3)
|
||||||
|
* fix type assignment error after typescript upgrade | [55f936f](https://github.com/undergroundwires/privacy.sexy/commit/55f936fee9f86757f63fa8952d89711feb247e5b)
|
||||||
|
* correct typos (#48) | [a744415](https://github.com/undergroundwires/privacy.sexy/commit/a744415eb2ab65ee4f519f863fdd6a43953377bb)
|
||||||
|
* in ci/cd, do not run security checks if PRs do not change dependencies #48 | [54ba4db](https://github.com/undergroundwires/privacy.sexy/commit/54ba4dbb0bf8f08f9479f8facb2e12c786c1bc51)
|
||||||
|
* rename app launch tracking tweak to make it more clear #44 | [b3117c2](https://github.com/undergroundwires/privacy.sexy/commit/b3117c27f283c2d5a25fd94021a9f628a272cda6)
|
||||||
|
* refactor capabilities to use a shared function #41 #47 | [c4ec6a1](https://github.com/undergroundwires/privacy.sexy/commit/c4ec6a1445d2fd5eb923c97b54aee01e272e13a8)
|
||||||
|
* rename "disable" to "uninstall" for removing capabilities #47 | [8cd3352](https://github.com/undergroundwires/privacy.sexy/commit/8cd3352017f9dc85f8efcd7b450d90f555d3e92e)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.8.1...0.8.2)
|
||||||
|
|
||||||
|
## 0.8.1 (2020-11-16)
|
||||||
|
|
||||||
|
* refactor removing bloatware to use functions #41 | [ffa279f](https://github.com/undergroundwires/privacy.sexy/commit/ffa279f3dfe51db564f0a3859543eb212170e173)
|
||||||
|
* fix reinstalling store apps by searching appx for all users | [2c5ab3e](https://github.com/undergroundwires/privacy.sexy/commit/2c5ab3ea7da159cfb9fbfbbb7cdd28afbee965ea)
|
||||||
|
* fix clearing jump lists causing os to break and user pin removal #37 | [92c3dd9](https://github.com/undergroundwires/privacy.sexy/commit/92c3dd923257ac940eab6cbab858698ed55a09b7)
|
||||||
|
* fix reinstalling store apps by searching appx for all users | [4e72673](https://github.com/undergroundwires/privacy.sexy/commit/4e7267337301fe4a0480ba0603218fca25c2d096)
|
||||||
|
* refactor unused imports | [45b8dd9](https://github.com/undergroundwires/privacy.sexy/commit/45b8dd972b1edf9e263858c23b27e7a1d2e07077)
|
||||||
|
* fix not being able to uninstall system apps | [31e08d2](https://github.com/undergroundwires/privacy.sexy/commit/31e08d231d52e2a691400468b7c599c142a29448)
|
||||||
|
* fix wrong app names caused by wrong Microsoft docs | [e41e40c](https://github.com/undergroundwires/privacy.sexy/commit/e41e40c5bf01e2971d3054fcd3a48f8465a96622)
|
||||||
|
* unrecommend some system apps and document more | [29c7704](https://github.com/undergroundwires/privacy.sexy/commit/29c7704e0bd38f6e9923cde84accb569b02d2dd6)
|
||||||
|
* fix not being able to rename paths including brackets | [ad1872e](https://github.com/undergroundwires/privacy.sexy/commit/ad1872e7cd4ad7ef9facf33fadfa8c6a55065dd3)
|
||||||
|
* fix errors when file already exists | [c26bc20](https://github.com/undergroundwires/privacy.sexy/commit/c26bc209eb167aa71cad10b7f3ea02d0dedd97b0)
|
||||||
|
* move Microsoft.Appconnector to right category | [b247b12](https://github.com/undergroundwires/privacy.sexy/commit/b247b12c3f009aab4350e33f4779fd193e570050)
|
||||||
|
* replace deprecated github ::set-env command | [ab7d617](https://github.com/undergroundwires/privacy.sexy/commit/ab7d617886a65fe4f3c2daa929168e5678ccae60)
|
||||||
|
|
||||||
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.8.0...0.8.1)
|
||||||
|
|
||||||
## 0.8.0 (2020-11-01)
|
## 0.8.0 (2020-11-01)
|
||||||
|
|
||||||
* add support for different recommendation levels: strict and standard | [commit](https://github.com/undergroundwires/privacy.sexy/commit/14be3017c55ed5e0d9bdecb63fcc4e1131e79ab0)
|
* add support for different recommendation levels: strict and standard | [14be301](https://github.com/undergroundwires/privacy.sexy/commit/14be3017c55ed5e0d9bdecb63fcc4e1131e79ab0)
|
||||||
* Add GroupMe and Spotify removal option (#34) | [commit](https://github.com/undergroundwires/privacy.sexy/commit/3785c623f837b182d82fa383dfe7709722a67248)
|
* Add GroupMe and Spotify removal option (#34) | [3785c62](https://github.com/undergroundwires/privacy.sexy/commit/3785c623f837b182d82fa383dfe7709722a67248)
|
||||||
* switch places of download and copy buttons | [commit](https://github.com/undergroundwires/privacy.sexy/commit/50fb29038ae19b17ec006093db02cf1e568d53c3)
|
* switch places of download and copy buttons | [50fb290](https://github.com/undergroundwires/privacy.sexy/commit/50fb29038ae19b17ec006093db02cf1e568d53c3)
|
||||||
* change "download" button to "save" on desktop | [commit](https://github.com/undergroundwires/privacy.sexy/commit/07fc555324d8bf4fa3594a9701daaa124a873153)
|
* change "download" button to "save" on desktop | [07fc555](https://github.com/undergroundwires/privacy.sexy/commit/07fc555324d8bf4fa3594a9701daaa124a873153)
|
||||||
* show icons on cards during indeterminate and fully selected states | [commit](https://github.com/undergroundwires/privacy.sexy/commit/1072505219edc47d82a91f148d1f310f32869fea)
|
* show icons on cards during indeterminate and fully selected states | [1072505](https://github.com/undergroundwires/privacy.sexy/commit/1072505219edc47d82a91f148d1f310f32869fea)
|
||||||
* add scripts to increase cryptography, enable camera notifications and remove todo app (#36) | [commit](https://github.com/undergroundwires/privacy.sexy/commit/4c68408f1ec339dc8d39c7ab044f825a7f7185cb)
|
* add scripts to increase cryptography, enable camera notifications and remove todo app (#36) | [4c68408](https://github.com/undergroundwires/privacy.sexy/commit/4c68408f1ec339dc8d39c7ab044f825a7f7185cb)
|
||||||
* update recommendations to be safer and consistent | [commit](https://github.com/undergroundwires/privacy.sexy/commit/d0019c2c9b1eea620e2e8e02b586903ce62b80e3)
|
* update recommendations to be safer and consistent | [d0019c2](https://github.com/undergroundwires/privacy.sexy/commit/d0019c2c9b1eea620e2e8e02b586903ce62b80e3)
|
||||||
* rework disabling metadata retrieval | [commit](https://github.com/undergroundwires/privacy.sexy/commit/ac70b063b8a15bc528256185792939685be6b36f)
|
* rework disabling metadata retrieval | [ac70b06](https://github.com/undergroundwires/privacy.sexy/commit/ac70b063b8a15bc528256185792939685be6b36f)
|
||||||
* add all dist folders in gitignore because of files auto-generated by vscode | [commit](https://github.com/undergroundwires/privacy.sexy/commit/1a9db31c7778c3269a71c0bd9665827efda70a02)
|
* add all dist folders in gitignore because of files auto-generated by vscode | [1a9db31](https://github.com/undergroundwires/privacy.sexy/commit/1a9db31c7778c3269a71c0bd9665827efda70a02)
|
||||||
* add support for shared functions #41 | [commit](https://github.com/undergroundwires/privacy.sexy/commit/8ce06facbd54184402a4b1af3c7303e64db85b8a)
|
* add support for shared functions #41 | [8ce06fa](https://github.com/undergroundwires/privacy.sexy/commit/8ce06facbd54184402a4b1af3c7303e64db85b8a)
|
||||||
* hide scrollbars on code area when not overflowing | [commit](https://github.com/undergroundwires/privacy.sexy/commit/fd28eaad061c75ea1aa7e0f0d60ea37a7e52f8c4)
|
* hide scrollbars on code area when not overflowing | [fd28eaa](https://github.com/undergroundwires/privacy.sexy/commit/fd28eaad061c75ea1aa7e0f0d60ea37a7e52f8c4)
|
||||||
* update screenshot | [commit](https://github.com/undergroundwires/privacy.sexy/commit/cfedcd724cad7708b30c7390a7bca3b6313b6726)
|
* update screenshot | [cfedcd7](https://github.com/undergroundwires/privacy.sexy/commit/cfedcd724cad7708b30c7390a7bca3b6313b6726)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.7.6...0.8.0)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.7.6...0.8.0)
|
||||||
|
|
||||||
## 0.7.6 (2020-10-18)
|
## 0.7.6 (2020-10-18)
|
||||||
|
|
||||||
* add docs for default0 pointing to github discussion (#30) | [commit](https://github.com/undergroundwires/privacy.sexy/commit/a3fc3782efd346b4c99d2a0b40df2eb0229f5b36)
|
* add docs for default0 pointing to github discussion (#30) | [a3fc378](https://github.com/undergroundwires/privacy.sexy/commit/a3fc3782efd346b4c99d2a0b40df2eb0229f5b36)
|
||||||
* add robots.txt to explicitly allow indexing | [commit](https://github.com/undergroundwires/privacy.sexy/commit/4c2f74949b0758d33049bdfa4f0124a28958f8ea)
|
* add robots.txt to explicitly allow indexing | [4c2f749](https://github.com/undergroundwires/privacy.sexy/commit/4c2f74949b0758d33049bdfa4f0124a28958f8ea)
|
||||||
* add more reversibility | [commit](https://github.com/undergroundwires/privacy.sexy/commit/19a092dd31fb3588277f1ab3120b409d98506752)
|
* add more reversibility | [19a092d](https://github.com/undergroundwires/privacy.sexy/commit/19a092dd31fb3588277f1ab3120b409d98506752)
|
||||||
* refactor to read more from package.json | [commit](https://github.com/undergroundwires/privacy.sexy/commit/784a67afff681bc19147d03c947de0e165d97e87)
|
* refactor to read more from package.json | [784a67a](https://github.com/undergroundwires/privacy.sexy/commit/784a67afff681bc19147d03c947de0e165d97e87)
|
||||||
* simplify "why" section | [commit](https://github.com/undergroundwires/privacy.sexy/commit/77c3d2bbb8d13db86bb82ed0b5cbeaacfdea3db9)
|
* simplify "why" section | [77c3d2b](https://github.com/undergroundwires/privacy.sexy/commit/77c3d2bbb8d13db86bb82ed0b5cbeaacfdea3db9)
|
||||||
* update dependencies to latest | [commit](https://github.com/undergroundwires/privacy.sexy/commit/11e06131655398db08faeeacff62062e46e0dddd)
|
* update dependencies to latest | [11e0613](https://github.com/undergroundwires/privacy.sexy/commit/11e06131655398db08faeeacff62062e46e0dddd)
|
||||||
* run tests on all operating systems: macos, ubuntu, windows | [commit](https://github.com/undergroundwires/privacy.sexy/commit/d9d7f62d81d4d8f95104d33211e82641884d711f)
|
* run tests on all operating systems: macos, ubuntu, windows | [d9d7f62](https://github.com/undergroundwires/privacy.sexy/commit/d9d7f62d81d4d8f95104d33211e82641884d711f)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.7.5...0.7.6)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.7.5...0.7.6)
|
||||||
|
|
||||||
## 0.7.5 (2020-09-14)
|
## 0.7.5 (2020-09-14)
|
||||||
|
|
||||||
* fix reverting (reinstalling) capabilities not working | [commit](https://github.com/undergroundwires/privacy.sexy/commit/939d838e3535bb1c9b00c8ea9dacb735ae41d700)
|
* fix reverting (reinstalling) capabilities not working | [939d838](https://github.com/undergroundwires/privacy.sexy/commit/939d838e3535bb1c9b00c8ea9dacb735ae41d700)
|
||||||
* fix tests and checks are not running on PRs | [commit](https://github.com/undergroundwires/privacy.sexy/commit/82d509129b4e4a5df4b84786a0d6842a7d26e888)
|
* fix tests and checks are not running on PRs | [82d5091](https://github.com/undergroundwires/privacy.sexy/commit/82d509129b4e4a5df4b84786a0d6842a7d26e888)
|
||||||
* fix the recycling bin option (#32) | [commit](https://github.com/undergroundwires/privacy.sexy/commit/15db3118012a172a2191a2afad57084a65b34642)
|
* fix the recycling bin option (#32) | [15db311](https://github.com/undergroundwires/privacy.sexy/commit/15db3118012a172a2191a2afad57084a65b34642)
|
||||||
* fix rendering issue in older edge/IE | [commit](https://github.com/undergroundwires/privacy.sexy/commit/6efed72bf25c2ddf0901caab7f22966ca13cd47a)
|
* fix rendering issue in older edge/IE | [6efed72](https://github.com/undergroundwires/privacy.sexy/commit/6efed72bf25c2ddf0901caab7f22966ca13cd47a)
|
||||||
* fix pasting in search bar after page load showing no results | [commit](https://github.com/undergroundwires/privacy.sexy/commit/d1694341578288eeaf8b80caf9296a38d76789f0)
|
* fix pasting in search bar after page load showing no results | [d169434](https://github.com/undergroundwires/privacy.sexy/commit/d1694341578288eeaf8b80caf9296a38d76789f0)
|
||||||
* fix typo | [commit](https://github.com/undergroundwires/privacy.sexy/commit/7dd15ed06433e0e6583ab0fa46a683ce6554bbea)
|
* fix typo | [7dd15ed](https://github.com/undergroundwires/privacy.sexy/commit/7dd15ed06433e0e6583ab0fa46a683ce6554bbea)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.7.4...0.7.5)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.7.4...0.7.5)
|
||||||
|
|
||||||
## 0.7.4 (2020-09-12)
|
## 0.7.4 (2020-09-12)
|
||||||
|
|
||||||
* fix checked checkbox has blue border | [commit](https://github.com/undergroundwires/privacy.sexy/commit/4ae385b7fcea9014a68442714b7d99e2ee7df7d0)
|
* fix checked checkbox has blue border | [4ae385b](https://github.com/undergroundwires/privacy.sexy/commit/4ae385b7fcea9014a68442714b7d99e2ee7df7d0)
|
||||||
* fix spectre protection getting single lined #31 | [commit](https://github.com/undergroundwires/privacy.sexy/commit/22b23a9ece446c7f9abd4ede293051eb616ad50a)
|
* fix spectre protection getting single lined #31 | [22b23a9](https://github.com/undergroundwires/privacy.sexy/commit/22b23a9ece446c7f9abd4ede293051eb616ad50a)
|
||||||
* fix missing reg value in denying app access to account | [commit](https://github.com/undergroundwires/privacy.sexy/commit/3c13a9e837e06e097450b31d7eb0c0e6bf20cefb)
|
* fix missing reg value in denying app access to account | [3c13a9e](https://github.com/undergroundwires/privacy.sexy/commit/3c13a9e837e06e097450b31d7eb0c0e6bf20cefb)
|
||||||
* fix wrong path in clear all firefox user profile settings | [commit](https://github.com/undergroundwires/privacy.sexy/commit/ee66196d9a60f27d17ae7f62d02b4f119a47e6e0)
|
* fix wrong path in clear all firefox user profile settings | [ee66196](https://github.com/undergroundwires/privacy.sexy/commit/ee66196d9a60f27d17ae7f62d02b4f119a47e6e0)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.7.3...0.7.4)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.7.3...0.7.4)
|
||||||
|
|
||||||
## 0.7.3 (2020-09-12)
|
## 0.7.3 (2020-09-12)
|
||||||
|
|
||||||
* fix vscode settings file override and add more configs | [commit](https://github.com/undergroundwires/privacy.sexy/commit/a0d61728ead04b4455437f85820121a848db9e00)
|
* fix vscode settings file override and add more configs | [a0d6172](https://github.com/undergroundwires/privacy.sexy/commit/a0d61728ead04b4455437f85820121a848db9e00)
|
||||||
* fix nvidia tweak error message, categorize and add reversibility | [commit](https://github.com/undergroundwires/privacy.sexy/commit/99a2035fdb0766a4dfc2753133eab0d7666516cd)
|
* fix nvidia tweak error message, categorize and add reversibility | [99a2035](https://github.com/undergroundwires/privacy.sexy/commit/99a2035fdb0766a4dfc2753133eab0d7666516cd)
|
||||||
* improve CPU specific tweaks by conditional platform checks and reversibility | [commit](https://github.com/undergroundwires/privacy.sexy/commit/8df5faf4ef05a49da63973bd0fbb5c5d07d5bd93)
|
* improve CPU specific tweaks by conditional platform checks and reversibility | [8df5faf](https://github.com/undergroundwires/privacy.sexy/commit/8df5faf4ef05a49da63973bd0fbb5c5d07d5bd93)
|
||||||
* fix wrong path to the main telemetry file | [commit](https://github.com/undergroundwires/privacy.sexy/commit/de4ac978bdda79573b36d355697b8a028d2c0beb)
|
* fix wrong path to the main telemetry file | [de4ac97](https://github.com/undergroundwires/privacy.sexy/commit/de4ac978bdda79573b36d355697b8a028d2c0beb)
|
||||||
* fix naming of firefox cleanup to mention profiles | [commit](https://github.com/undergroundwires/privacy.sexy/commit/3ab48b1cf5f7f934f07e468ef2318ccee07f530c)
|
* fix naming of firefox cleanup to mention profiles | [3ab48b1](https://github.com/undergroundwires/privacy.sexy/commit/3ab48b1cf5f7f934f07e468ef2318ccee07f530c)
|
||||||
* add reversibility and more scripts to denying app access with better structure | [commit](https://github.com/undergroundwires/privacy.sexy/commit/1d465ee3189d0e5a827453b3f0eb4361efe23770)
|
* add reversibility and more scripts to denying app access with better structure | [1d465ee](https://github.com/undergroundwires/privacy.sexy/commit/1d465ee3189d0e5a827453b3f0eb4361efe23770)
|
||||||
* fix comment lines are being detected as duplicate in validation | [commit](https://github.com/undergroundwires/privacy.sexy/commit/b6ccb5927a20412976a54fd2215eb645092f98a8)
|
* fix comment lines are being detected as duplicate in validation | [b6ccb59](https://github.com/undergroundwires/privacy.sexy/commit/b6ccb5927a20412976a54fd2215eb645092f98a8)
|
||||||
* add more detailed error message | [commit](https://github.com/undergroundwires/privacy.sexy/commit/1f11c39773c12eccfb3efb898b58c2f6f37ab9ca)
|
* add more detailed error message | [1f11c39](https://github.com/undergroundwires/privacy.sexy/commit/1f11c39773c12eccfb3efb898b58c2f6f37ab9ca)
|
||||||
* fix typo in a test | [commit](https://github.com/undergroundwires/privacy.sexy/commit/1f19b2528a69383e63e579d2885f01cd804abf6c)
|
* fix typo in a test | [1f19b25](https://github.com/undergroundwires/privacy.sexy/commit/1f19b2528a69383e63e579d2885f01cd804abf6c)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.7.2...0.7.3)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.7.2...0.7.3)
|
||||||
|
|
||||||
## 0.7.2 (2020-09-06)
|
## 0.7.2 (2020-09-06)
|
||||||
|
|
||||||
* update onesync documentation and do not recommend it as it breaks other apps | [commit](https://github.com/undergroundwires/privacy.sexy/commit/f36d8bfc7848bb65ac0c641e318a689bf3816ccf)
|
* update onesync documentation and do not recommend it as it breaks other apps | [f36d8bf](https://github.com/undergroundwires/privacy.sexy/commit/f36d8bfc7848bb65ac0c641e318a689bf3816ccf)
|
||||||
* add reversibility for biometric disabling and do not recommend it | [commit](https://github.com/undergroundwires/privacy.sexy/commit/db74531cd4139615c6d595959217d3651f099019)
|
* add reversibility for biometric disabling and do not recommend it | [db74531](https://github.com/undergroundwires/privacy.sexy/commit/db74531cd4139615c6d595959217d3651f099019)
|
||||||
* fix bad highlighting of selected nodes when using keyboard navigation | [commit](https://github.com/undergroundwires/privacy.sexy/commit/255133af4dfae40171406648a3e2920f16d71cb3)
|
* fix bad highlighting of selected nodes when using keyboard navigation | [255133a](https://github.com/undergroundwires/privacy.sexy/commit/255133af4dfae40171406648a3e2920f16d71cb3)
|
||||||
* add reversibility to removing bloatware | [commit](https://github.com/undergroundwires/privacy.sexy/commit/c7b2a703128470a05f12c9c6e8002444def37ef8)
|
* add reversibility to removing bloatware | [c7b2a70](https://github.com/undergroundwires/privacy.sexy/commit/c7b2a703128470a05f12c9c6e8002444def37ef8)
|
||||||
* fix indeterminate state being lost | [commit](https://github.com/undergroundwires/privacy.sexy/commit/1f266c33535f72b69c65985bf2eff27cd2c5a104)
|
* fix indeterminate state being lost | [1f266c3](https://github.com/undergroundwires/privacy.sexy/commit/1f266c33535f72b69c65985bf2eff27cd2c5a104)
|
||||||
* fix wording in default text in text area | [commit](https://github.com/undergroundwires/privacy.sexy/commit/ca63a0979ef55d07d09d9443e5cea9aa888870a5)
|
* fix wording in default text in text area | [ca63a09](https://github.com/undergroundwires/privacy.sexy/commit/ca63a0979ef55d07d09d9443e5cea9aa888870a5)
|
||||||
* add best practice suggestion to come back | [commit](https://github.com/undergroundwires/privacy.sexy/commit/f4885b6f1c82752f2143934e336d6d1b1af03015)
|
* add best practice suggestion to come back | [f4885b6](https://github.com/undergroundwires/privacy.sexy/commit/f4885b6f1c82752f2143934e336d6d1b1af03015)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.7.1...0.7.2)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.7.1...0.7.2)
|
||||||
|
|
||||||
## 0.7.1 (2020-09-04)
|
## 0.7.1 (2020-09-04)
|
||||||
|
|
||||||
* fix some browsers (including firefox) downloading the script as a text file | [commit](https://github.com/undergroundwires/privacy.sexy/commit/8c17929151f9c4fa5f48564492bbf400ced95eea)
|
* fix some browsers (including firefox) downloading the script as a text file | [8c17929](https://github.com/undergroundwires/privacy.sexy/commit/8c17929151f9c4fa5f48564492bbf400ced95eea)
|
||||||
* rename screenshot image file | [commit](https://github.com/undergroundwires/privacy.sexy/commit/b8682a852a14ed6cf49986695d9510b840ac9d3d)
|
* rename screenshot image file | [b8682a8](https://github.com/undergroundwires/privacy.sexy/commit/b8682a852a14ed6cf49986695d9510b840ac9d3d)
|
||||||
* fix new/changed script higlighting not working on production builds | [commit](https://github.com/undergroundwires/privacy.sexy/commit/8c38dd73d8c7b77d8d341c0389f4d7229f9b97fd)
|
* fix new/changed script higlighting not working on production builds | [8c38dd7](https://github.com/undergroundwires/privacy.sexy/commit/8c38dd73d8c7b77d8d341c0389f4d7229f9b97fd)
|
||||||
* refactor unused imports | [commit](https://github.com/undergroundwires/privacy.sexy/commit/6badfef9daace0c5de3fd33652a82bfe22261b11)
|
* refactor unused imports | [6badfef](https://github.com/undergroundwires/privacy.sexy/commit/6badfef9daace0c5de3fd33652a82bfe22261b11)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.7.0...0.7.1)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.7.0...0.7.1)
|
||||||
|
|
||||||
## 0.7.0 (2020-09-02)
|
## 0.7.0 (2020-09-02)
|
||||||
|
|
||||||
* [search] better (multilined) message when there are no results | [commit](https://github.com/undergroundwires/privacy.sexy/commit/ec15af01dd020b364c2174fe562fd66227c2320c)
|
* [search] better (multilined) message when there are no results | [ec15af0](https://github.com/undergroundwires/privacy.sexy/commit/ec15af01dd020b364c2174fe562fd66227c2320c)
|
||||||
* [search] added clear/close button | [commit](https://github.com/undergroundwires/privacy.sexy/commit/d6fa9a2a03c0ebe68b94f0b80cc52b4e200c9213)
|
* [search] added clear/close button | [d6fa9a2](https://github.com/undergroundwires/privacy.sexy/commit/d6fa9a2a03c0ebe68b94f0b80cc52b4e200c9213)
|
||||||
* move script generation to /generation | [commit](https://github.com/undergroundwires/privacy.sexy/commit/5df458739d076719e350ba194c4f3f772884fcdb)
|
* move script generation to /generation | [5df4587](https://github.com/undergroundwires/privacy.sexy/commit/5df458739d076719e350ba194c4f3f772884fcdb)
|
||||||
* add auto-highlighting of selected/updated code | [commit](https://github.com/undergroundwires/privacy.sexy/commit/b789250cb89e2130b08e1a927df8181cf945dfeb)
|
* add auto-highlighting of selected/updated code | [b789250](https://github.com/undergroundwires/privacy.sexy/commit/b789250cb89e2130b08e1a927df8181cf945dfeb)
|
||||||
* prompt admin priviliges automatically | [commit](https://github.com/undergroundwires/privacy.sexy/commit/f8ba5c46e4923d9c35f200f8a08aa6437f7c0ecc)
|
* prompt admin priviliges automatically | [f8ba5c4](https://github.com/undergroundwires/privacy.sexy/commit/f8ba5c46e4923d9c35f200f8a08aa6437f7c0ecc)
|
||||||
* add removal of ghost (default0) telemetry user | [commit](https://github.com/undergroundwires/privacy.sexy/commit/c262681011f39b4412669b6cf233476f676ca550)
|
* add removal of ghost (default0) telemetry user | [c262681](https://github.com/undergroundwires/privacy.sexy/commit/c262681011f39b4412669b6cf233476f676ca550)
|
||||||
* add more windows defender tweaks, categorization and reversibility | [commit](https://github.com/undergroundwires/privacy.sexy/commit/1a34c7374ba56bafa0209bbb55c81b233bb419ed)
|
* add more windows defender tweaks, categorization and reversibility | [1a34c73](https://github.com/undergroundwires/privacy.sexy/commit/1a34c7374ba56bafa0209bbb55c81b233bb419ed)
|
||||||
* fix NTP script documentation is on wrong place | [commit](https://github.com/undergroundwires/privacy.sexy/commit/3060ebf79cf242370433495cc3e1878b7581b202)
|
* fix NTP script documentation is on wrong place | [3060ebf](https://github.com/undergroundwires/privacy.sexy/commit/3060ebf79cf242370433495cc3e1878b7581b202)
|
||||||
* updated dependencies to latest and audit fixes (#25) | [commit](https://github.com/undergroundwires/privacy.sexy/commit/c628aa9aef8ab7c815661d3c1711e7fbc65c69a2)
|
* updated dependencies to latest and audit fixes (#25) | [c628aa9](https://github.com/undergroundwires/privacy.sexy/commit/c628aa9aef8ab7c815661d3c1711e7fbc65c69a2)
|
||||||
* categorize, fix and extend windows log files cleanup | [commit](https://github.com/undergroundwires/privacy.sexy/commit/594a14d6ca76cbd27a21877b8c373c1930589ca6)
|
* categorize, fix and extend windows log files cleanup | [594a14d](https://github.com/undergroundwires/privacy.sexy/commit/594a14d6ca76cbd27a21877b8c373c1930589ca6)
|
||||||
* add more OneDrive cleanup scripts and categorize them | [commit](https://github.com/undergroundwires/privacy.sexy/commit/978d7d08638dd161082f239ed088b12302f29458)
|
* add more OneDrive cleanup scripts and categorize them | [978d7d0](https://github.com/undergroundwires/privacy.sexy/commit/978d7d08638dd161082f239ed088b12302f29458)
|
||||||
* add disabling firefox telemetry | [commit](https://github.com/undergroundwires/privacy.sexy/commit/f8b8b4c97ab734d5ba7370894b694993924388da)
|
* add disabling firefox telemetry | [f8b8b4c](https://github.com/undergroundwires/privacy.sexy/commit/f8b8b4c97ab734d5ba7370894b694993924388da)
|
||||||
* add disabling ccleaner telemetry | [commit](https://github.com/undergroundwires/privacy.sexy/commit/018b7e270f207aac926cb12f8069ebfcdce193ce)
|
* add disabling ccleaner telemetry | [018b7e2](https://github.com/undergroundwires/privacy.sexy/commit/018b7e270f207aac926cb12f8069ebfcdce193ce)
|
||||||
* Add disabling of PowerShell 7+ telemetry (#29) | [commit](https://github.com/undergroundwires/privacy.sexy/commit/456e40bedf9afcc846f9b13f1ea144cef6115cf6)
|
* Add disabling of PowerShell 7+ telemetry (#29) | [456e40b](https://github.com/undergroundwires/privacy.sexy/commit/456e40bedf9afcc846f9b13f1ea144cef6115cf6)
|
||||||
* categorize, fix, make scripts reversible in "UI for privacy", "security improvements" and "configure browsers" | [commit](https://github.com/undergroundwires/privacy.sexy/commit/532915b95da9fecd6b981d91bf489359e4e53caa)
|
* categorize, fix, make scripts reversible in "UI for privacy", "security improvements" and "configure browsers" | [532915b](https://github.com/undergroundwires/privacy.sexy/commit/532915b95da9fecd6b981d91bf489359e4e53caa)
|
||||||
* fix "Configure Defender" being in wrong category #28 | [commit](https://github.com/undergroundwires/privacy.sexy/commit/f709d6a566ed7846b677b383863deda9680a2a9c)
|
* fix "Configure Defender" being in wrong category #28 | [f709d6a](https://github.com/undergroundwires/privacy.sexy/commit/f709d6a566ed7846b677b383863deda9680a2a9c)
|
||||||
* do not hardcode capability versions and make them reversible | [commit](https://github.com/undergroundwires/privacy.sexy/commit/2afef4ea3d0d3d09aa1fa1eedba8493680bd8f10)
|
* do not hardcode capability versions and make them reversible | [2afef4e](https://github.com/undergroundwires/privacy.sexy/commit/2afef4ea3d0d3d09aa1fa1eedba8493680bd8f10)
|
||||||
* exclude paint, wordpad and notepad from bloatware removal | [commit](https://github.com/undergroundwires/privacy.sexy/commit/d235dee95514a01745aef9479d07f88ffb4b40b8)
|
* exclude paint, wordpad and notepad from bloatware removal | [d235dee](https://github.com/undergroundwires/privacy.sexy/commit/d235dee95514a01745aef9479d07f88ffb4b40b8)
|
||||||
* add reversibility on category level | [commit](https://github.com/undergroundwires/privacy.sexy/commit/f51e8859eeb32c944126d692cfe03a0320c8b568)
|
* add reversibility on category level | [f51e885](https://github.com/undergroundwires/privacy.sexy/commit/f51e8859eeb32c944126d692cfe03a0320c8b568)
|
||||||
* refactor unused imports & variables | [commit](https://github.com/undergroundwires/privacy.sexy/commit/a23d28f2cfa2d64d45460697cf5ee9d6b5920752)
|
* refactor unused imports & variables | [a23d28f](https://github.com/undergroundwires/privacy.sexy/commit/a23d28f2cfa2d64d45460697cf5ee9d6b5920752)
|
||||||
* fix search (got broken in b789250) with tests and refactorings | [commit](https://github.com/undergroundwires/privacy.sexy/commit/8bbe6ebf750f1a1cbab493fb99b5ea91f4e21609)
|
* fix search (got broken in b789250) with tests and refactorings | [8bbe6eb](https://github.com/undergroundwires/privacy.sexy/commit/8bbe6ebf750f1a1cbab493fb99b5ea91f4e21609)
|
||||||
* update the screenshot to show off highlighting | [commit](https://github.com/undergroundwires/privacy.sexy/commit/b4aacea2a3e0bbcf2d8a79ff67f51c0f19e888a6)
|
* update the screenshot to show off highlighting | [b4aacea](https://github.com/undergroundwires/privacy.sexy/commit/b4aacea2a3e0bbcf2d8a79ff67f51c0f19e888a6)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.6.2...0.7.0)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.6.2...0.7.0)
|
||||||
|
|
||||||
## 0.6.2 (2020-08-16)
|
## 0.6.2 (2020-08-16)
|
||||||
|
|
||||||
* 🐛 fixed disabling error reporting for november 2019 update | [commit](https://github.com/undergroundwires/privacy.sexy/commit/5967347b80976a519f6f4eb1972a62f3e600df2b)
|
* 🐛 fixed disabling error reporting for november 2019 update | [5967347](https://github.com/undergroundwires/privacy.sexy/commit/5967347b80976a519f6f4eb1972a62f3e600df2b)
|
||||||
* 🐛 fixed blank screen and icons on mac | [commit](https://github.com/undergroundwires/privacy.sexy/commit/7fac0fe79f252e8f9dda4f6f83cd6fa4ba2b539f)
|
* 🐛 fixed blank screen and icons on mac | [7fac0fe](https://github.com/undergroundwires/privacy.sexy/commit/7fac0fe79f252e8f9dda4f6f83cd6fa4ba2b539f)
|
||||||
* 🐛 fixed removing onedrive does not delete scheduled tasks | [commit](https://github.com/undergroundwires/privacy.sexy/commit/b6bfc2572740c0cd46d3bc0058fa767dd5fa862e)
|
* 🐛 fixed removing onedrive does not delete scheduled tasks | [b6bfc25](https://github.com/undergroundwires/privacy.sexy/commit/b6bfc2572740c0cd46d3bc0058fa767dd5fa862e)
|
||||||
* ⚙️ enhanced tweak to disable for office telemetry | [commit](https://github.com/undergroundwires/privacy.sexy/commit/afc3bfb3b8896f332c9a196973ded3dce8fd21e4)
|
* ⚙️ enhanced tweak to disable for office telemetry | [afc3bfb](https://github.com/undergroundwires/privacy.sexy/commit/afc3bfb3b8896f332c9a196973ded3dce8fd21e4)
|
||||||
* ✨ added script to clear dotnet telemery | [commit](https://github.com/undergroundwires/privacy.sexy/commit/1663bfeac7b6580b1335ca5fcf3587b69c080c72)
|
* ✨ added script to clear dotnet telemery | [1663bfe](https://github.com/undergroundwires/privacy.sexy/commit/1663bfeac7b6580b1335ca5fcf3587b69c080c72)
|
||||||
* 🐛 fixed changing time server not working | [commit](https://github.com/undergroundwires/privacy.sexy/commit/c69998c7cb29ffcf40f0af03b73150736581da69)
|
* 🐛 fixed changing time server not working | [c69998c](https://github.com/undergroundwires/privacy.sexy/commit/c69998c7cb29ffcf40f0af03b73150736581da69)
|
||||||
* 🔥 removed disabling ClickToRun as it breaks office | [commit](https://github.com/undergroundwires/privacy.sexy/commit/3d3380f27ebeea53f17f49974aaa89300ffaf2dd)
|
* 🔥 removed disabling ClickToRun as it breaks office | [3d3380f](https://github.com/undergroundwires/privacy.sexy/commit/3d3380f27ebeea53f17f49974aaa89300ffaf2dd)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.6.1...0.6.2)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.6.1...0.6.2)
|
||||||
|
|
||||||
## 0.6.1 (2020-08-09)
|
## 0.6.1 (2020-08-09)
|
||||||
|
|
||||||
* updated documentation | [commit](https://github.com/undergroundwires/privacy.sexy/commit/5963d2bac551083f9d16cce6b851abf0e8b88ce7)
|
* updated documentation | [5963d2b](https://github.com/undergroundwires/privacy.sexy/commit/5963d2bac551083f9d16cce6b851abf0e8b88ce7)
|
||||||
* fixed typo in footer | [commit](https://github.com/undergroundwires/privacy.sexy/commit/5c15a7a64aaf24578a32713dec491bf494216303)
|
* fixed typo in footer | [5c15a7a](https://github.com/undergroundwires/privacy.sexy/commit/5c15a7a64aaf24578a32713dec491bf494216303)
|
||||||
* more scripts can be reverted | [commit](https://github.com/undergroundwires/privacy.sexy/commit/831c014f977515454ee6dc664d77a8c434495501)
|
* more scripts can be reverted | [831c014](https://github.com/undergroundwires/privacy.sexy/commit/831c014f977515454ee6dc664d77a8c434495501)
|
||||||
* moved windows connect now to security & recommended | [commit](https://github.com/undergroundwires/privacy.sexy/commit/6049a2b834d8d17af741f8d8f8b07cd15153b001)
|
* moved windows connect now to security & recommended | [6049a2b](https://github.com/undergroundwires/privacy.sexy/commit/6049a2b834d8d17af741f8d8f8b07cd15153b001)
|
||||||
* fixed mac / linux download links | [commit](https://github.com/undergroundwires/privacy.sexy/commit/4c8be45e287b5ea009d6f828f7f327f37850569e)
|
* fixed mac / linux download links | [4c8be45](https://github.com/undergroundwires/privacy.sexy/commit/4c8be45e287b5ea009d6f828f7f327f37850569e)
|
||||||
* tweaks to disable webcam, speech and compatibility telemetry | [commit](https://github.com/undergroundwires/privacy.sexy/commit/a5dbe66fc175e39397f296ab2ff703e9b0ab4d7c)
|
* tweaks to disable webcam, speech and compatibility telemetry | [a5dbe66](https://github.com/undergroundwires/privacy.sexy/commit/a5dbe66fc175e39397f296ab2ff703e9b0ab4d7c)
|
||||||
* refactorings | [commit](https://github.com/undergroundwires/privacy.sexy/commit/66d4d39d5bf3db305450514c6b6224654dafbfb2)
|
* refactorings | [66d4d39](https://github.com/undergroundwires/privacy.sexy/commit/66d4d39d5bf3db305450514c6b6224654dafbfb2)
|
||||||
* fixed removing onedrive does not clean start menu / quick access | [commit](https://github.com/undergroundwires/privacy.sexy/commit/1cc12195a3e9a11c590d3ed64d80299b50f74838)
|
* fixed removing onedrive does not clean start menu / quick access | [1cc1219](https://github.com/undergroundwires/privacy.sexy/commit/1cc12195a3e9a11c590d3ed64d80299b50f74838)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.6.0...0.6.1)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.6.0...0.6.1)
|
||||||
|
|
||||||
## 0.6.0 (2020-07-26)
|
## 0.6.0 (2020-07-26)
|
||||||
|
|
||||||
* fixed dead links in documentation | [commit](https://github.com/undergroundwires/privacy.sexy/commit/25ce236a7737decaf2eb9b8c29a4c4f34d43f770)
|
* fixed dead links in documentation | [25ce236](https://github.com/undergroundwires/privacy.sexy/commit/25ce236a7737decaf2eb9b8c29a4c4f34d43f770)
|
||||||
* runs tests on each push on the repository | [commit](https://github.com/undergroundwires/privacy.sexy/commit/73c426844a0330718a9ab7de12b61ca05e853323)
|
* runs tests on each push on the repository | [73c4268](https://github.com/undergroundwires/privacy.sexy/commit/73c426844a0330718a9ab7de12b61ca05e853323)
|
||||||
* code area now shows "how" before "why" | [commit](https://github.com/undergroundwires/privacy.sexy/commit/4ff4b52202b1c5dbfe2b80580bbe7d93132ab05c)
|
* code area now shows "how" before "why" | [4ff4b52](https://github.com/undergroundwires/privacy.sexy/commit/4ff4b52202b1c5dbfe2b80580bbe7d93132ab05c)
|
||||||
* support for desktop versions #20 | [commit](https://github.com/undergroundwires/privacy.sexy/commit/04b9b59e14766ccd251474ad3710baf1f682fd49)
|
* support for desktop versions #20 | [04b9b59](https://github.com/undergroundwires/privacy.sexy/commit/04b9b59e14766ccd251474ad3710baf1f682fd49)
|
||||||
* reworked on footer & removed github icon | [commit](https://github.com/undergroundwires/privacy.sexy/commit/60a5a2aa4026d384bef9e6a203f1b7514a269c33)
|
* reworked on footer & removed github icon | [60a5a2a](https://github.com/undergroundwires/privacy.sexy/commit/60a5a2aa4026d384bef9e6a203f1b7514a269c33)
|
||||||
* updated dependencies to latest | [commit](https://github.com/undergroundwires/privacy.sexy/commit/45816a2bccb3d11a50e3f2bc19c0a6cc2587deaa)
|
* updated dependencies to latest | [45816a2](https://github.com/undergroundwires/privacy.sexy/commit/45816a2bccb3d11a50e3f2bc19c0a6cc2587deaa)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.5.0...0.6.0)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.5.0...0.6.0)
|
||||||
|
|
||||||
## 0.5.0 (2020-07-19)
|
## 0.5.0 (2020-07-19)
|
||||||
|
|
||||||
* added ability to revert (#21) | [commit](https://github.com/undergroundwires/privacy.sexy/commit/9c063d59defa6297c64f50b49403e8bd10620de9)
|
* added ability to revert (#21) | [9c063d5](https://github.com/undergroundwires/privacy.sexy/commit/9c063d59defa6297c64f50b49403e8bd10620de9)
|
||||||
* search placeholder shows total scripts | [commit](https://github.com/undergroundwires/privacy.sexy/commit/1d5225de07186f853f4cf7aedd4998f5d00c107a)
|
* search placeholder shows total scripts | [1d5225d](https://github.com/undergroundwires/privacy.sexy/commit/1d5225de07186f853f4cf7aedd4998f5d00c107a)
|
||||||
* do not collapse card when on "Search" and "Select" | [commit](https://github.com/undergroundwires/privacy.sexy/commit/dd7e1416b4df54bf71b719d4654db88769dc0994)
|
* do not collapse card when on "Search" and "Select" | [dd7e141](https://github.com/undergroundwires/privacy.sexy/commit/dd7e1416b4df54bf71b719d4654db88769dc0994)
|
||||||
* opening a card scrolls to its content div | [commit](https://github.com/undergroundwires/privacy.sexy/commit/31d2067f076c3159483baec49975617dddbd158d)
|
* opening a card scrolls to its content div | [31d2067](https://github.com/undergroundwires/privacy.sexy/commit/31d2067f076c3159483baec49975617dddbd158d)
|
||||||
* all cards in same line now have same height | [commit](https://github.com/undergroundwires/privacy.sexy/commit/a9f9e9044385d9aed3b5551fc6c6823e813fd1e5)
|
* all cards in same line now have same height | [a9f9e90](https://github.com/undergroundwires/privacy.sexy/commit/a9f9e9044385d9aed3b5551fc6c6823e813fd1e5)
|
||||||
* patched loadash vulnerability (#18) | [commit](https://github.com/undergroundwires/privacy.sexy/commit/92a7118d1c5013312772e075b9ee5a79c93710b8)
|
* patched loadash vulnerability (#18) | [92a7118](https://github.com/undergroundwires/privacy.sexy/commit/92a7118d1c5013312772e075b9ee5a79c93710b8)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.10...0.5.0)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.10...0.5.0)
|
||||||
|
|
||||||
## 0.4.10 (2020-07-15)
|
## 0.4.10 (2020-07-15)
|
||||||
|
|
||||||
* fixed script errors & added tests | [commit](https://github.com/undergroundwires/privacy.sexy/commit/9e722ddfb3825fb29d6298025baaaa033120d017)
|
* fixed script errors & added tests | [9e722dd](https://github.com/undergroundwires/privacy.sexy/commit/9e722ddfb3825fb29d6298025baaaa033120d017)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.9...0.4.10)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.9...0.4.10)
|
||||||
|
|
||||||
## 0.4.9 (2020-07-14)
|
## 0.4.9 (2020-07-14)
|
||||||
|
|
||||||
* disable office telemetry Disassembler0/Win10-Initial-Setup-Script#288 | [commit](https://github.com/undergroundwires/privacy.sexy/commit/53cf595e1726ee3de79137fd566978fd512d218f)
|
* disable office telemetry Disassembler0/Win10-Initial-Setup-Script#288 | [53cf595](https://github.com/undergroundwires/privacy.sexy/commit/53cf595e1726ee3de79137fd566978fd512d218f)
|
||||||
* updated to may 2020 update | [commit](https://github.com/undergroundwires/privacy.sexy/commit/909c44d72a4a602ee8f27d06b6ec706c1e432ce1)
|
* updated to may 2020 update | [909c44d](https://github.com/undergroundwires/privacy.sexy/commit/909c44d72a4a602ee8f27d06b6ec706c1e432ce1)
|
||||||
* simplified docker builds | [commit](https://github.com/undergroundwires/privacy.sexy/commit/f27a2871d74e5117fc029be82caef12246e10879)
|
* simplified docker builds | [f27a287](https://github.com/undergroundwires/privacy.sexy/commit/f27a2871d74e5117fc029be82caef12246e10879)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.8...0.4.9)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.8...0.4.9)
|
||||||
|
|
||||||
## 0.4.8 (2020-07-11)
|
## 0.4.8 (2020-07-11)
|
||||||
|
|
||||||
* added more scripts #16 (#17) | [commit](https://github.com/undergroundwires/privacy.sexy/commit/d8552c62ffea13ce62abce836c7dd4980eef6bb9)
|
* added more scripts #16 (#17) | [d8552c6](https://github.com/undergroundwires/privacy.sexy/commit/d8552c62ffea13ce62abce836c7dd4980eef6bb9)
|
||||||
* stopping services before disabling #16 | [commit](https://github.com/undergroundwires/privacy.sexy/commit/628c16eb952495f5b3f6d794161b355f4b08b819)
|
* stopping services before disabling #16 | [628c16e](https://github.com/undergroundwires/privacy.sexy/commit/628c16eb952495f5b3f6d794161b355f4b08b819)
|
||||||
* can disable features, capabilities & remove onedrive #16 | [commit](https://github.com/undergroundwires/privacy.sexy/commit/30efbcc621eb83dd5a9c1e66b8f1f5350eb95006)
|
* can disable features, capabilities & remove onedrive #16 | [30efbcc](https://github.com/undergroundwires/privacy.sexy/commit/30efbcc621eb83dd5a9c1e66b8f1f5350eb95006)
|
||||||
* updated one more typo (#19) | [commit](https://github.com/undergroundwires/privacy.sexy/commit/d7a1325c0b7665ce712dc411965d00fc1d6fa384)
|
* updated one more typo (#19) | [d7a1325](https://github.com/undergroundwires/privacy.sexy/commit/d7a1325c0b7665ce712dc411965d00fc1d6fa384)
|
||||||
* more tweaks #16 | [commit](https://github.com/undergroundwires/privacy.sexy/commit/2c4eb78c3f156cb0d033977cffbe7464697680f5)
|
* more tweaks #16 | [2c4eb78](https://github.com/undergroundwires/privacy.sexy/commit/2c4eb78c3f156cb0d033977cffbe7464697680f5)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.7...0.4.8)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.7...0.4.8)
|
||||||
|
|
||||||
## 0.4.7 (2020-06-30)
|
## 0.4.7 (2020-06-30)
|
||||||
|
|
||||||
* removed HKU tweak as all HKU's are changed #10 | [commit](https://github.com/undergroundwires/privacy.sexy/commit/c937af8ee7da9aa95131e56abf7bf24800390fe6)
|
* removed HKU tweak as all HKU's are changed #10 | [c937af8](https://github.com/undergroundwires/privacy.sexy/commit/c937af8ee7da9aa95131e56abf7bf24800390fe6)
|
||||||
* Fixed types + script in "Clear Windows log files" (#15) | [commit](https://github.com/undergroundwires/privacy.sexy/commit/461a4f122b342369db5cc08c5e30961c64e68cdd)
|
* Fixed types + script in "Clear Windows log files" (#15) | [461a4f1](https://github.com/undergroundwires/privacy.sexy/commit/461a4f122b342369db5cc08c5e30961c64e68cdd)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.6...0.4.7)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.6...0.4.7)
|
||||||
|
|
||||||
## 0.4.6 (2020-06-16)
|
## 0.4.6 (2020-06-16)
|
||||||
|
|
||||||
* Fixed Some More Issues (#12) | [commit](https://github.com/undergroundwires/privacy.sexy/commit/52d5713a99422cdf900aba819e49e998abac33cc)
|
* Fixed Some More Issues (#12) | [52d5713](https://github.com/undergroundwires/privacy.sexy/commit/52d5713a99422cdf900aba819e49e998abac33cc)
|
||||||
* removed failing continuous deployment #14 | [commit](https://github.com/undergroundwires/privacy.sexy/commit/583c5660d6ac934b845a044e013357aa91f61c15)
|
* removed failing continuous deployment #14 | [583c566](https://github.com/undergroundwires/privacy.sexy/commit/583c5660d6ac934b845a044e013357aa91f61c15)
|
||||||
* Updated Some Tweaks (#11) | [commit](https://github.com/undergroundwires/privacy.sexy/commit/0fc18459cde57684f00764815062f838f932aed5)
|
* Updated Some Tweaks (#11) | [0fc1845](https://github.com/undergroundwires/privacy.sexy/commit/0fc18459cde57684f00764815062f838f932aed5)
|
||||||
* Updated Some More Tweaks (#13) | [commit](https://github.com/undergroundwires/privacy.sexy/commit/019b838925e963b7ec052ac76c6faf5650b9eb67)
|
* Updated Some More Tweaks (#13) | [019b838](https://github.com/undergroundwires/privacy.sexy/commit/019b838925e963b7ec052ac76c6faf5650b9eb67)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.5...0.4.6)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.5...0.4.6)
|
||||||
|
|
||||||
@@ -204,91 +431,91 @@
|
|||||||
|
|
||||||
## 0.4.4 (2020-05-24)
|
## 0.4.4 (2020-05-24)
|
||||||
|
|
||||||
* fixed close card button not being visible & cleanup | [commit](https://github.com/undergroundwires/privacy.sexy/commit/0d2efe5b05aa965458b78b8fa43754ce2f4fe11b)
|
* fixed close card button not being visible & cleanup | [0d2efe5](https://github.com/undergroundwires/privacy.sexy/commit/0d2efe5b05aa965458b78b8fa43754ce2f4fe11b)
|
||||||
* new footer with privacy policy | [commit](https://github.com/undergroundwires/privacy.sexy/commit/e2ab124fb799f56ada3570fdc911361cb803e889)
|
* new footer with privacy policy | [e2ab124](https://github.com/undergroundwires/privacy.sexy/commit/e2ab124fb799f56ada3570fdc911361cb803e889)
|
||||||
* one command to lint everything "npm run lint" | [commit](https://github.com/undergroundwires/privacy.sexy/commit/bb98d20637cbf1d524ebb2973e308773006e3153)
|
* one command to lint everything "npm run lint" | [bb98d20](https://github.com/undergroundwires/privacy.sexy/commit/bb98d20637cbf1d524ebb2973e308773006e3153)
|
||||||
* fix "group by" overflows on smaller screens | [commit](https://github.com/undergroundwires/privacy.sexy/commit/c668a97950a1cb7c8bf2a7fd8a72d1101e65e8ce)
|
* fix "group by" overflows on smaller screens | [c668a97](https://github.com/undergroundwires/privacy.sexy/commit/c668a97950a1cb7c8bf2a7fd8a72d1101e65e8ce)
|
||||||
* clicking outside of a card closes it | [commit](https://github.com/undergroundwires/privacy.sexy/commit/aab8f21a8d8dbed54798af581e6e1ad9e86a4be1)
|
* clicking outside of a card closes it | [aab8f21](https://github.com/undergroundwires/privacy.sexy/commit/aab8f21a8d8dbed54798af581e6e1ad9e86a4be1)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.3...0.4.4)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.3...0.4.4)
|
||||||
|
|
||||||
## 0.4.3 (2020-05-23)
|
## 0.4.3 (2020-05-23)
|
||||||
|
|
||||||
* removed redundant documentation | [commit](https://github.com/undergroundwires/privacy.sexy/commit/749a140eb8dba09cb67fec2f8dec937e66e3cff5)
|
* removed redundant documentation | [749a140](https://github.com/undergroundwires/privacy.sexy/commit/749a140eb8dba09cb67fec2f8dec937e66e3cff5)
|
||||||
* fixed broke link | [commit](https://github.com/undergroundwires/privacy.sexy/commit/97b7e03233d9718a8df30cb01ce06ca9489a0295)
|
* fixed broke link | [97b7e03](https://github.com/undergroundwires/privacy.sexy/commit/97b7e03233d9718a8df30cb01ce06ca9489a0295)
|
||||||
* simplified heading | [commit](https://github.com/undergroundwires/privacy.sexy/commit/226074c5342f7463c06fcff1457d352ca30295a3)
|
* simplified heading | [226074c](https://github.com/undergroundwires/privacy.sexy/commit/226074c5342f7463c06fcff1457d352ca30295a3)
|
||||||
* reading version from package.json instead of version file #5 | [commit](https://github.com/undergroundwires/privacy.sexy/commit/691f989682179016ddcbf55a05cded29155288c9)
|
* reading version from package.json instead of version file #5 | [691f989](https://github.com/undergroundwires/privacy.sexy/commit/691f989682179016ddcbf55a05cded29155288c9)
|
||||||
* automatically increases patch number #5 | [commit](https://github.com/undergroundwires/privacy.sexy/commit/3e3bc07576f7c7e74e3e11fc7d197cbb9a9fb8c0)
|
* automatically increases patch number #5 | [3e3bc07](https://github.com/undergroundwires/privacy.sexy/commit/3e3bc07576f7c7e74e3e11fc7d197cbb9a9fb8c0)
|
||||||
* using deployment operations from aws-static-site-with-cd | [commit](https://github.com/undergroundwires/privacy.sexy/commit/997be7113f676888892ffa35566d9ebb58a3e9ea)
|
* using deployment operations from aws-static-site-with-cd | [997be71](https://github.com/undergroundwires/privacy.sexy/commit/997be7113f676888892ffa35566d9ebb58a3e9ea)
|
||||||
* automated using bump-everywhere + more quality checks (#8) | [commit](https://github.com/undergroundwires/privacy.sexy/commit/4a91e8ccd8a707bc6bea34ee28cff7fa4f66ee2f)
|
* automated using bump-everywhere + more quality checks (#8) | [4a91e8c](https://github.com/undergroundwires/privacy.sexy/commit/4a91e8ccd8a707bc6bea34ee28cff7fa4f66ee2f)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.2...0.4.3)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.2...0.4.3)
|
||||||
|
|
||||||
## 0.4.2 (2020-02-29)
|
## 0.4.2 (2020-02-29)
|
||||||
|
|
||||||
* added missing semicolon for masking | [commit](https://github.com/undergroundwires/privacy.sexy/commit/e63ac4ae67da68243a525af149ff30e5d485b641)
|
* added missing semicolon for masking | [e63ac4a](https://github.com/undergroundwires/privacy.sexy/commit/e63ac4ae67da68243a525af149ff30e5d485b641)
|
||||||
* set font on input | [commit](https://github.com/undergroundwires/privacy.sexy/commit/0c39a06be5e4b0a2031ad5e9f5220dd669afee53)
|
* set font on input | [0c39a06](https://github.com/undergroundwires/privacy.sexy/commit/0c39a06be5e4b0a2031ad5e9f5220dd669afee53)
|
||||||
* shortened all HKEY paths | [commit](https://github.com/undergroundwires/privacy.sexy/commit/802b36bdd8dcc1f0a2853fe7da2ea2fccd69a88c)
|
* shortened all HKEY paths | [802b36b](https://github.com/undergroundwires/privacy.sexy/commit/802b36bdd8dcc1f0a2853fe7da2ea2fccd69a88c)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.1...0.4.2)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.1...0.4.2)
|
||||||
|
|
||||||
## 0.4.1 (2020-01-11)
|
## 0.4.1 (2020-01-11)
|
||||||
|
|
||||||
* fixed search bug | [commit](https://github.com/undergroundwires/privacy.sexy/commit/31364bdfec503af09ffbb58044a17dfb833fc8d9)
|
* fixed search bug | [31364bd](https://github.com/undergroundwires/privacy.sexy/commit/31364bdfec503af09ffbb58044a17dfb833fc8d9)
|
||||||
* hide grouping while searching | [commit](https://github.com/undergroundwires/privacy.sexy/commit/92f1a36bcb1e1fe7c90efe8ccd3ede55991e9d9c)
|
* hide grouping while searching | [92f1a36](https://github.com/undergroundwires/privacy.sexy/commit/92f1a36bcb1e1fe7c90efe8ccd3ede55991e9d9c)
|
||||||
* 👀🔍 showing search queries | [commit](https://github.com/undergroundwires/privacy.sexy/commit/97a7747933d2b515cc03ab8243e6a8ae702ef16a)
|
* 👀🔍 showing search queries | [97a7747](https://github.com/undergroundwires/privacy.sexy/commit/97a7747933d2b515cc03ab8243e6a8ae702ef16a)
|
||||||
* more efficient queries with single lowercase | [commit](https://github.com/undergroundwires/privacy.sexy/commit/19813b691746d98670823025c460480400e34b6e)
|
* more efficient queries with single lowercase | [19813b6](https://github.com/undergroundwires/privacy.sexy/commit/19813b691746d98670823025c460480400e34b6e)
|
||||||
* using right 🔍 input type | [commit](https://github.com/undergroundwires/privacy.sexy/commit/0ce354ea0956391ad3f37b252daac1127bfc601a)
|
* using right 🔍 input type | [0ce354e](https://github.com/undergroundwires/privacy.sexy/commit/0ce354ea0956391ad3f37b252daac1127bfc601a)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.0...0.4.1)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.4.0...0.4.1)
|
||||||
|
|
||||||
## 0.4.0 (2020-01-11)
|
## 0.4.0 (2020-01-11)
|
||||||
|
|
||||||
* 🔍 support for search | [commit](https://github.com/undergroundwires/privacy.sexy/commit/89862b2775703257b9dc2e19fbebde2c0d0fbda0)
|
* 🔍 support for search | [89862b2](https://github.com/undergroundwires/privacy.sexy/commit/89862b2775703257b9dc2e19fbebde2c0d0fbda0)
|
||||||
* more scripts & better organized | [commit](https://github.com/undergroundwires/privacy.sexy/commit/95baf3175b0d2c7df516f7893a96346b94ac8eca)
|
* more scripts & better organized | [95baf31](https://github.com/undergroundwires/privacy.sexy/commit/95baf3175b0d2c7df516f7893a96346b94ac8eca)
|
||||||
* refactorings | [commit](https://github.com/undergroundwires/privacy.sexy/commit/e3f82e069e305f6d94eab335470c8e7b44295dd6)
|
* refactorings | [e3f82e0](https://github.com/undergroundwires/privacy.sexy/commit/e3f82e069e305f6d94eab335470c8e7b44295dd6)
|
||||||
* more margin for the scripts | [commit](https://github.com/undergroundwires/privacy.sexy/commit/5ea46ecbf52236953d19f09a8eade08b83e6cd34)
|
* more margin for the scripts | [5ea46ec](https://github.com/undergroundwires/privacy.sexy/commit/5ea46ecbf52236953d19f09a8eade08b83e6cd34)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.3.0...0.4.0)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.3.0...0.4.0)
|
||||||
|
|
||||||
## 0.3.0 (2020-01-09)
|
## 0.3.0 (2020-01-09)
|
||||||
|
|
||||||
* added description & more descriptive title | [commit](https://github.com/undergroundwires/privacy.sexy/commit/99576340b648550149871e2c0fe0b0d8c2dd0d7c)
|
* added description & more descriptive title | [9957634](https://github.com/undergroundwires/privacy.sexy/commit/99576340b648550149871e2c0fe0b0d8c2dd0d7c)
|
||||||
* allow robots | [commit](https://github.com/undergroundwires/privacy.sexy/commit/eee0e785ec2c5e6bed53d21b4126a57773e35dba)
|
* allow robots | [eee0e78](https://github.com/undergroundwires/privacy.sexy/commit/eee0e785ec2c5e6bed53d21b4126a57773e35dba)
|
||||||
* removed unused references | [commit](https://github.com/undergroundwires/privacy.sexy/commit/cfd888f3afc5c260a0a4a73f2843b86b9f1df2cd)
|
* removed unused references | [cfd888f](https://github.com/undergroundwires/privacy.sexy/commit/cfd888f3afc5c260a0a4a73f2843b86b9f1df2cd)
|
||||||
* 🚫 disable NVIDIA telemetry | [commit](https://github.com/undergroundwires/privacy.sexy/commit/ab28f4ed8538d51e1777c86302a63a0cd9c3cb2a)
|
* 🚫 disable NVIDIA telemetry | [ab28f4e](https://github.com/undergroundwires/privacy.sexy/commit/ab28f4ed8538d51e1777c86302a63a0cd9c3cb2a)
|
||||||
* backwards compatibility for fonts | [commit](https://github.com/undergroundwires/privacy.sexy/commit/4bc13e11926a6df77079646499e799742153b4ab)
|
* backwards compatibility for fonts | [4bc13e1](https://github.com/undergroundwires/privacy.sexy/commit/4bc13e11926a6df77079646499e799742153b4ab)
|
||||||
* added back meta needed for responsiveness | [commit](https://github.com/undergroundwires/privacy.sexy/commit/ed872ef3d9f6c92afc0ce0d06998c60463a8b4e8)
|
* added back meta needed for responsiveness | [ed872ef](https://github.com/undergroundwires/privacy.sexy/commit/ed872ef3d9f6c92afc0ce0d06998c60463a8b4e8)
|
||||||
* fancy-font is renamed to main and now used | [commit](https://github.com/undergroundwires/privacy.sexy/commit/6825001c61426194dc363b96b57a321241f3ba57)
|
* fancy-font is renamed to main and now used | [6825001](https://github.com/undergroundwires/privacy.sexy/commit/6825001c61426194dc363b96b57a321241f3ba57)
|
||||||
* added support for grouping | [commit](https://github.com/undergroundwires/privacy.sexy/commit/ec6b3c54072a77bb4305da1c234db6c649218b88)
|
* added support for grouping | [ec6b3c5](https://github.com/undergroundwires/privacy.sexy/commit/ec6b3c54072a77bb4305da1c234db6c649218b88)
|
||||||
* less hyphens as it looks better on mobile | [commit](https://github.com/undergroundwires/privacy.sexy/commit/e0b080af69157f46ba12e2c25e794f5384671b51)
|
* less hyphens as it looks better on mobile | [e0b080a](https://github.com/undergroundwires/privacy.sexy/commit/e0b080af69157f46ba12e2c25e794f5384671b51)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.2.0...0.3.0)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.2.0...0.3.0)
|
||||||
|
|
||||||
## 0.2.0 (2020-01-06)
|
## 0.2.0 (2020-01-06)
|
||||||
|
|
||||||
* added GitHub Actions badge for build & deploy | [commit](https://github.com/undergroundwires/privacy.sexy/commit/a229aca68a92bbcd8e8176ac1dd25ce03509e074)
|
* added GitHub Actions badge for build & deploy | [a229aca](https://github.com/undergroundwires/privacy.sexy/commit/a229aca68a92bbcd8e8176ac1dd25ce03509e074)
|
||||||
* more badges 📛🏆📜 | [commit](https://github.com/undergroundwires/privacy.sexy/commit/090e8319091044e53484ba8338510f6fb7c3cb80)
|
* more badges 📛🏆📜 | [090e831](https://github.com/undergroundwires/privacy.sexy/commit/090e8319091044e53484ba8338510f6fb7c3cb80)
|
||||||
* typo fixes + whitespace refactorings | [commit](https://github.com/undergroundwires/privacy.sexy/commit/e99f210c9dcf61a21e445e2a331384b6066f2c98)
|
* typo fixes + whitespace refactorings | [e99f210](https://github.com/undergroundwires/privacy.sexy/commit/e99f210c9dcf61a21e445e2a331384b6066f2c98)
|
||||||
* switched content information to "why" section | [commit](https://github.com/undergroundwires/privacy.sexy/commit/beb3c8339f83a224ca66ad8a911a9265ffe7c9c0)
|
* switched content information to "why" section | [beb3c83](https://github.com/undergroundwires/privacy.sexy/commit/beb3c8339f83a224ca66ad8a911a9265ffe7c9c0)
|
||||||
* fixed contribution URL | [commit](https://github.com/undergroundwires/privacy.sexy/commit/7b4277d7706ccf6ba7e4b7b01aa46f8e3852cfc6)
|
* fixed contribution URL | [7b4277d](https://github.com/undergroundwires/privacy.sexy/commit/7b4277d7706ccf6ba7e4b7b01aa46f8e3852cfc6)
|
||||||
* fixed wrong relation + lighter style | [commit](https://github.com/undergroundwires/privacy.sexy/commit/8d05b03c9f3c9fc015be6615da8c283809712065)
|
* fixed wrong relation + lighter style | [8d05b03](https://github.com/undergroundwires/privacy.sexy/commit/8d05b03c9f3c9fc015be6615da8c283809712065)
|
||||||
* better URL validation | [commit](https://github.com/undergroundwires/privacy.sexy/commit/aff463dd64fecff92a786fcba88621dff6b1cf73)
|
* better URL validation | [aff463d](https://github.com/undergroundwires/privacy.sexy/commit/aff463dd64fecff92a786fcba88621dff6b1cf73)
|
||||||
* refactoring to new function | [commit](https://github.com/undergroundwires/privacy.sexy/commit/c646c102730481c3f4648eb714dc0a84ce35b13c)
|
* refactoring to new function | [c646c10](https://github.com/undergroundwires/privacy.sexy/commit/c646c102730481c3f4648eb714dc0a84ce35b13c)
|
||||||
* optimized find queries & refactorings | [commit](https://github.com/undergroundwires/privacy.sexy/commit/d38f6cd6a8b33e11df854c7abea05974dc04d4ce)
|
* optimized find queries & refactorings | [d38f6cd](https://github.com/undergroundwires/privacy.sexy/commit/d38f6cd6a8b33e11df854c7abea05974dc04d4ce)
|
||||||
* 🎨 styled no JS error | [commit](https://github.com/undergroundwires/privacy.sexy/commit/c359f1d89c6874b3cc94154b993e33f58bd32268)
|
* 🎨 styled no JS error | [c359f1d](https://github.com/undergroundwires/privacy.sexy/commit/c359f1d89c6874b3cc94154b993e33f58bd32268)
|
||||||
* simplified finding duplicates | [commit](https://github.com/undergroundwires/privacy.sexy/commit/57037aaefcc0e80f0f4719cea89568490a731028)
|
* simplified finding duplicates | [57037aa](https://github.com/undergroundwires/privacy.sexy/commit/57037aaefcc0e80f0f4719cea89568490a731028)
|
||||||
* fixed maintainability badge URL | [commit](https://github.com/undergroundwires/privacy.sexy/commit/aaea47e7d15fe41dea26968db0107a0c53d108f3)
|
* fixed maintainability badge URL | [aaea47e](https://github.com/undergroundwires/privacy.sexy/commit/aaea47e7d15fe41dea26968db0107a0c53d108f3)
|
||||||
* fixed wrong line dumps | [commit](https://github.com/undergroundwires/privacy.sexy/commit/5ccc7c59528885ae7729197df3dfa00f924a2b3f)
|
* fixed wrong line dumps | [5ccc7c5](https://github.com/undergroundwires/privacy.sexy/commit/5ccc7c59528885ae7729197df3dfa00f924a2b3f)
|
||||||
* refactorings in parsing | [commit](https://github.com/undergroundwires/privacy.sexy/commit/2aa3742e30646bf1d1f3779419d161c3fb6c4808)
|
* refactorings in parsing | [2aa3742](https://github.com/undergroundwires/privacy.sexy/commit/2aa3742e30646bf1d1f3779419d161c3fb6c4808)
|
||||||
* using free function | [commit](https://github.com/undergroundwires/privacy.sexy/commit/20020af7c1d8de13948d8761fd4e7f0affb2badb)
|
* using free function | [20020af](https://github.com/undergroundwires/privacy.sexy/commit/20020af7c1d8de13948d8761fd4e7f0affb2badb)
|
||||||
* default selection is now none | [commit](https://github.com/undergroundwires/privacy.sexy/commit/3140cc663b86394d543de90228aa53e6a304d8d9)
|
* default selection is now none | [3140cc6](https://github.com/undergroundwires/privacy.sexy/commit/3140cc663b86394d543de90228aa53e6a304d8d9)
|
||||||
* added hyphen lines for longer names | [commit](https://github.com/undergroundwires/privacy.sexy/commit/cced601d686d550f4225018e5311b7433efbb5ae)
|
* added hyphen lines for longer names | [cced601](https://github.com/undergroundwires/privacy.sexy/commit/cced601d686d550f4225018e5311b7433efbb5ae)
|
||||||
* more descriptive subtitle | [commit](https://github.com/undergroundwires/privacy.sexy/commit/2cf9214b14d9720f747a71b3864ba7a28acf0ff4)
|
* more descriptive subtitle | [2cf9214](https://github.com/undergroundwires/privacy.sexy/commit/2cf9214b14d9720f747a71b3864ba7a28acf0ff4)
|
||||||
* added footer with version | [commit](https://github.com/undergroundwires/privacy.sexy/commit/10a34fae2f1a219ec52db0c74edb39b46ebd8abc)
|
* added footer with version | [10a34fa](https://github.com/undergroundwires/privacy.sexy/commit/10a34fae2f1a219ec52db0c74edb39b46ebd8abc)
|
||||||
* using font variables | [commit](https://github.com/undergroundwires/privacy.sexy/commit/60e6348dc8d53f1e81ebdb2ec0e1962aac1e9842)
|
* using font variables | [60e6348](https://github.com/undergroundwires/privacy.sexy/commit/60e6348dc8d53f1e81ebdb2ec0e1962aac1e9842)
|
||||||
* code-gen refactorings | [commit](https://github.com/undergroundwires/privacy.sexy/commit/246e753ddc9dc8bf630e538663584bf3423cc749)
|
* code-gen refactorings | [246e753](https://github.com/undergroundwires/privacy.sexy/commit/246e753ddc9dc8bf630e538663584bf3423cc749)
|
||||||
* added text when nothing is chosen | [commit](https://github.com/undergroundwires/privacy.sexy/commit/a7da75d4428090423b692ce45423f5bd300d8442)
|
* added text when nothing is chosen | [a7da75d](https://github.com/undergroundwires/privacy.sexy/commit/a7da75d4428090423b692ce45423f5bd300d8442)
|
||||||
|
|
||||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.1.0...0.2.0)
|
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.1.0...0.2.0)
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +1,51 @@
|
|||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
- Love your input! Contributing to this project should be as easy and transparent as possible, whether it's:
|
Love your input! Contributing to this project should be as easy and transparent as possible, whether it's:
|
||||||
- Reporting a bug
|
|
||||||
- Discussing the current state of the code
|
|
||||||
- Submitting a fix
|
|
||||||
- Proposing new features
|
|
||||||
- Becoming a maintainer
|
|
||||||
|
|
||||||
## Pull Request Process
|
- reporting a bug,
|
||||||
|
- discussing the current state of the code,
|
||||||
|
- submitting a fix,
|
||||||
|
- proposing new features,
|
||||||
|
- or becoming a maintainer.
|
||||||
|
|
||||||
- [GitHub flow](https://guides.github.com/introduction/flow/index.html) is used
|
As a small open source project with small community, it can sometimes take a long time to address the issues so please be patient.
|
||||||
- Your pull requests are actively welcomed.
|
|
||||||
- The steps:
|
|
||||||
1. Fork the repo and create your branch from master.
|
|
||||||
2. If you've added code that should be tested, add tests.
|
|
||||||
3. If you've changed APIs, update the documentation.
|
|
||||||
4. Ensure the test suite passes.
|
|
||||||
5. Make sure your code lints.
|
|
||||||
6. Issue that pull request!
|
|
||||||
- 🙏 DO
|
|
||||||
- Document your changes in the pull request
|
|
||||||
- ❗ DON'T
|
|
||||||
- Do not update the versions, current version is only [set by the maintainer](./img/architecture/gitops.png) and updated automatically by [bump-everywhere](https://github.com/undergroundwires/bump-everywhere)
|
|
||||||
|
|
||||||
## Guidelines
|
## Pull request process
|
||||||
|
|
||||||
### Handle the state in presentation layer
|
Your pull requests are actively welcomed. We collaborate using [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow).
|
||||||
|
|
||||||
- There are two types of components:
|
The steps:
|
||||||
- **Stateless**, extends `Vue`
|
|
||||||
- **Stateful**, extends [`StatefulVue`](./src/presentation/StatefulVue.ts)
|
1. Fork the repo and create your branch from master.
|
||||||
- The source of truth for the state lies in application layer (`./src/application/`) and must be updated from the views if they're mutating the state
|
2. If you've added code that requires testing, add tests. See [tests.md](./docs/tests.md).
|
||||||
- They mutate or/and reacts to changes in [application state](src/application/State/ApplicationState.ts).
|
3. If you've done a major change, update the documentation. See [docs/](./docs/).
|
||||||
- You can react by getting the state and listening to it and update the view accordingly in [`mounted()`](https://vuejs.org/v2/api/#mounted) method.
|
4. Ensure the test suite passes. See [development.md | Testing](./docs/development.md#testing) for commands.
|
||||||
|
5. Make sure your code lints.See [development.md | Linting](./docs/development.md#linting) for commands.
|
||||||
|
6. Issue that pull request!
|
||||||
|
|
||||||
|
**🙏 DO:**
|
||||||
|
|
||||||
|
- Document why (what you're trying to solve) rather than what in the pull request.
|
||||||
|
|
||||||
|
**❗ DON'T:**
|
||||||
|
|
||||||
|
- Do not update the versions, current version is [set by the maintainer](./docs/ci-cd.md#gitops) and updated automatically by [bump-everywhere](https://github.com/undergroundwires/bump-everywhere).
|
||||||
|
|
||||||
|
Automated pipelines will run to control your PR and they will publish your code once the maintainer merges your PR.
|
||||||
|
|
||||||
|
📖 You can read more in [ci-cd.md](./docs/ci-cd.md).
|
||||||
|
|
||||||
|
## Extend scripts
|
||||||
|
|
||||||
|
Here's quick information for you who want to add more scripts.
|
||||||
|
|
||||||
|
You have two alternatives:
|
||||||
|
|
||||||
|
1. [Create an issue](https://github.com/undergroundwires/privacy.sexy/issues/new/choose) and ask for someone else to add the script for you.
|
||||||
|
2. Or send a PR yourself. This would make it faster to get your code into the project. You need to add scripts to related OS in [collections](src/application/collections/) folder. Then you'd sent a pull request, see [pull request process](#pull-request-process).
|
||||||
|
- 📖 If you're unsure about the syntax, check [collection-files.md](docs/collection-files.md).
|
||||||
|
- 📖 If you wish to use templates, use [templating.md](./docs/templating.md).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
By contributing, you agree that your contributions will be licensed under its GNU General Public License v3.0.
|
By contributing, you agree that your [GNU General Public License v3.0](./LICENSE) will be the license for your contributions.
|
||||||
|
|||||||
191
README.md
@@ -1,88 +1,141 @@
|
|||||||
# privacy.sexy
|
# privacy.sexy
|
||||||
|
|
||||||
> Enforce privacy & security best-practices on Windows, because privacy is sexy 🍑🍆
|
> Enforce privacy & security best-practices on Windows and macOS, because privacy is sexy 🍑🍆
|
||||||
|
|
||||||
[](./CONTRIBUTING.md)
|
<!-- markdownlint-disable MD033 -->
|
||||||
[](https://lgtm.com/projects/g/undergroundwires/privacy.sexy/context:javascript)
|
<p align="center">
|
||||||
[](https://codeclimate.com/github/undergroundwires/privacy.sexy/maintainability)
|
<a href="https://undergroundwires.dev/donate?project=privacy.sexy">
|
||||||
[](https://github.com/undergroundwires/privacy.sexy/actions)
|
<img
|
||||||
[](https://github.com/undergroundwires/privacy.sexy/actions)
|
alt="donation badge"
|
||||||
[](https://github.com/undergroundwires/privacy.sexy/actions)
|
src="https://undergroundwires.dev/img/badges/donate/flat.svg"
|
||||||
[](https://github.com/undergroundwires/privacy.sexy/actions)
|
/>
|
||||||
[](https://github.com/undergroundwires/privacy.sexy/actions)
|
</a>
|
||||||
[](https://github.com/undergroundwires/bump-everywhere)
|
<a href="https://github.com/undergroundwires/privacy.sexy/blob/master/CONTRIBUTING.md">
|
||||||
|
<img
|
||||||
|
alt="contributions are welcome"
|
||||||
|
src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<!-- Code quality -->
|
||||||
|
<br />
|
||||||
|
<a href="https://lgtm.com/projects/g/undergroundwires/privacy.sexy/context:javascript">
|
||||||
|
<img
|
||||||
|
alt="Language grade: JavaScript/TypeScript"
|
||||||
|
src="https://img.shields.io/lgtm/grade/javascript/g/undergroundwires/privacy.sexy.svg?logo=lgtm&logoWidth=18"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="https://codeclimate.com/github/undergroundwires/privacy.sexy/maintainability">
|
||||||
|
<img
|
||||||
|
alt="Maintainability"
|
||||||
|
src="https://api.codeclimate.com/v1/badges/3a70b7ef602e2264342c/maintainability"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<!-- Tests -->
|
||||||
|
<br />
|
||||||
|
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/tests.unit.yaml">
|
||||||
|
<img
|
||||||
|
alt="Unit tests status"
|
||||||
|
src="https://github.com/undergroundwires/privacy.sexy/workflows/unit-tests/badge.svg"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/tests.integration.yaml">
|
||||||
|
<img
|
||||||
|
alt="Integration tests status"
|
||||||
|
src="https://github.com/undergroundwires/privacy.sexy/workflows/integration-tests/badge.svg"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/tests.e2e.yaml">
|
||||||
|
<img
|
||||||
|
alt="E2E tests status"
|
||||||
|
src="https://github.com/undergroundwires/privacy.sexy/workflows/e2e-tests/badge.svg"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<!-- Checks -->
|
||||||
|
<br />
|
||||||
|
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.quality.yaml">
|
||||||
|
<img
|
||||||
|
alt="Quality checks status"
|
||||||
|
src="https://github.com/undergroundwires/privacy.sexy/workflows/quality-checks/badge.svg"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.security.yaml">
|
||||||
|
<img
|
||||||
|
alt="Security checks status"
|
||||||
|
src="https://github.com/undergroundwires/privacy.sexy/workflows/security-checks/badge.svg"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.build.yaml">
|
||||||
|
<img
|
||||||
|
alt="Build checks status"
|
||||||
|
src="https://github.com/undergroundwires/privacy.sexy/workflows/build-checks/badge.svg"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<!-- Release -->
|
||||||
|
<br />
|
||||||
|
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/release.git.yaml">
|
||||||
|
<img
|
||||||
|
alt="Git release status"
|
||||||
|
src="https://github.com/undergroundwires/privacy.sexy/workflows/release-git/badge.svg"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/release.site.yaml">
|
||||||
|
<img
|
||||||
|
alt="Site release status"
|
||||||
|
src="https://github.com/undergroundwires/privacy.sexy/workflows/release-site/badge.svg"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/release.desktop.yaml">
|
||||||
|
<img
|
||||||
|
alt="Desktop application release status"
|
||||||
|
src="https://github.com/undergroundwires/privacy.sexy/workflows/release-desktop/badge.svg"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<!-- Others -->
|
||||||
|
<br />
|
||||||
|
<a href="https://github.com/undergroundwires/bump-everywhere">
|
||||||
|
<img
|
||||||
|
alt="Auto-versioned by bump-everywhere"
|
||||||
|
src="https://github.com/undergroundwires/bump-everywhere/blob/master/badge.svg?raw=true"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<!-- markdownlint-restore -->
|
||||||
|
|
||||||
## Get started
|
## Get started
|
||||||
|
|
||||||
- Online version: [https://privacy.sexy](https://privacy.sexy)
|
- 🌍️ **Online**: [https://privacy.sexy](https://privacy.sexy).
|
||||||
- or download latest desktop version for [Windows](https://github.com/undergroundwires/privacy.sexy/releases/download/0.8.0/privacy.sexy-Setup-0.8.0.exe), [Linux](https://github.com/undergroundwires/privacy.sexy/releases/download/0.8.0/privacy.sexy-0.8.0.AppImage), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.8.0/privacy.sexy-0.8.0.dmg)
|
- 🖥️ **Offline**: Check [releases page](https://github.com/undergroundwires/privacy.sexy/releases), or download directly for: [Windows](https://github.com/undergroundwires/privacy.sexy/releases/download/0.11.2/privacy.sexy-Setup-0.11.2.exe), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.11.2/privacy.sexy-0.11.2.dmg), [Linux](https://github.com/undergroundwires/pr.vacy.sexy/releases/download/0.11.2/privacy.sexy-0.11.2.AppImage).
|
||||||
- 💡 Come back regularly to apply latest version for stronger privacy and security.
|
|
||||||
|
|
||||||
[](https://privacy.sexy)
|
Online version does not require to run any software on your computer. Offline version has more functions such as running the scripts directly.
|
||||||
|
|
||||||
## Why
|
💡 You should apply your configuration from time to time (more than once). It would strengthen your privacy and security control because privacy.sexy and its scripts get better and stronger in every new version.
|
||||||
|
|
||||||
- Rich tweak pool to harden security & privacy of the OS and other software on it
|
[](https://privacy.sexy)
|
||||||
- Free (both free as in beer and free as in speech)
|
|
||||||
- No need to run any compiled software that has access to your system, just run the generated scripts
|
|
||||||
- Have full visibility into what the tweaks do as you enable them
|
|
||||||
- Ability to revert (undo) applied scripts
|
|
||||||
- Everything is transparent: both application and its infrastructure are open-source and automated
|
|
||||||
- Easily extendable
|
|
||||||
|
|
||||||
## Extend scripts
|
## Features
|
||||||
|
|
||||||
- Fork it & add more scripts in [application.yaml](src/application/application.yaml) and send a pull request 👌
|
- **Rich**: Hundreds of scripts that aims to give you control of your data.
|
||||||
- 📖 If you're unsure about the syntax you can refer to the [application file | documentation](docs/application-file.md).
|
- **Free**: Both free as in "beer" and free as in "speech".
|
||||||
- 🙏 For any new script, please add `revertCode` and `docs` values if possible.
|
- **Transparent**. Have full visibility into what the tweaks do as you enable them.
|
||||||
|
- **Reversible**. Revert if something feels wrong.
|
||||||
|
- **Accessible**. No need to run any compiled software on your computer with web version.
|
||||||
|
- **Open**. What you see as code in this repository is what you get. The application itself, its infrastructure and deployments are open-source and automated thanks to [bump-everywhere](https://github.com/undergroundwires/bump-everywhere).
|
||||||
|
- **Tested.** A lot of tests. Automated and manual. Community-testing and verification. Stability improvements comes before new features.
|
||||||
|
- **Extensible**. Effortlessly [extend scripts](./CONTRIBUTING.md#extend-scripts) with a custom designed [templating language](./docs/templating.md).
|
||||||
|
- **Portable and simple**. Every script is independently executable without cross-dependencies.
|
||||||
|
|
||||||
## Commands
|
## Support
|
||||||
|
|
||||||
- Project setup: `npm install`
|
**Sponsor 💕**. This project is free, and it might not be tempting to donate since you don't have to pay. But your donations will ensure that this project stays alive. A monthly coffee from you would make a difference. Recurring donations allow me to spend more time and resources on this project. Consider sponsoring on [GitHub Sponsors](https://github.com/sponsors/undergroundwires), or you can donate using [other ways such as crypto or a coffee](https://undergroundwires.dev/donate).
|
||||||
- Testing
|
|
||||||
- Run unit tests: `npm run test:unit`
|
|
||||||
- Lint: `npm run lint`
|
|
||||||
- **Desktop app**
|
|
||||||
- Development: `npm run electron:serve`
|
|
||||||
- Production: `npm run electron:build` to build an executable
|
|
||||||
- **Webpage**
|
|
||||||
- Development: `npm run serve` to compile & hot-reload for development.
|
|
||||||
- Production: `npm run build` to prepare files for distribution.
|
|
||||||
- Or run using Docker:
|
|
||||||
1. Build: `docker build -t undergroundwires/privacy.sexy:0.8.0 .`
|
|
||||||
2. Run: `docker run -it -p 8080:80 --rm --name privacy.sexy-0.8.0 undergroundwires/privacy.sexy:0.8.0`
|
|
||||||
|
|
||||||
## Architecture
|
**Star 🤩**. I know that not everyone can afford donating a coffee to show support. In this case, feel free to give it a star ⭐ . It helps me to see that you appreciate the project.
|
||||||
|
|
||||||
### Application
|
**Contribute 👷**. Contributions of any type are welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) as the starting point. It includes useful information like [how to add new scripts](./CONTRIBUTING.md#extend-scripts).
|
||||||
|
|
||||||
- Powered by **TypeScript**, **Vue.js** and **Electron** 💪
|
## Development
|
||||||
- and driven by **Domain-driven design**, **Event-driven architecture**, **Data-driven programming** concepts.
|
|
||||||
- Application uses highly decoupled models & services in different DDD layers.
|
|
||||||
- **Domain layer** is where the application is modelled with validation logic.
|
|
||||||
- **Presentation Layer**
|
|
||||||
- Consists of Vue.js components and other UI-related code.
|
|
||||||
- Desktop application is created using [Electron](https://www.electronjs.org/).
|
|
||||||
- Event driven as in components simply listens to events from the state and act accordingly.
|
|
||||||
- **Application Layer**
|
|
||||||
- Keeps the application state
|
|
||||||
- The [state](src/application/State/ApplicationState.ts) is a mutable singleton & event producer.
|
|
||||||
- The application is defined & controlled in a [single YAML file](src/application/application.yaml) (see [Data-driven programming](https://en.wikipedia.org/wiki/Data-driven_programming))
|
|
||||||
|
|
||||||

|
Refer to [development.md](./docs/development.md) for Docker usage and reading more about setting up your development environment.
|
||||||
|
|
||||||
### AWS Infrastructure
|
Check [architecture.md](./docs/architecture.md) for an overview of design and how different parts and layers work together. You can refer to [application.md](./docs/application.md) for a closer look at application layer codebase and [presentation.md](./docs/presentation.md) for code related to GUI layer. [collection-files.md](./docs/collection-files.md) explains the YAML files that are the core of the application and [templating.md](./docs/templating.md) documents how to use templating language in those files. In [ci-cd.md](./docs/ci-cd.md), you can read more about the pipelines that automates maintenance tasks and ensures you get what see.
|
||||||
|
|
||||||
[](https://github.com/undergroundwires/aws-static-site-with-cd)
|
[docs/](./docs/) folder includes all other documentation.
|
||||||
|
|
||||||
- It uses infrastructure from the following repository: [aws-static-site-with-cd](https://github.com/undergroundwires/aws-static-site-with-cd)
|
|
||||||
- Runs on AWS 100% serverless and automatically provisioned using [GitHub Actions](.github/workflows/).
|
|
||||||
- Maximum security & automation and minimum AWS costs are the highest priorities of the design.
|
|
||||||
|
|
||||||
#### GitOps: CI/CD to AWS
|
|
||||||
|
|
||||||
- CI/CD is fully automated for this repo using different GIT events & GitHub actions.
|
|
||||||
- Versioning, tagging, creation of `CHANGELOG.md` and releasing is automated using [bump-everywhere](https://github.com/undergroundwires/bump-everywhere) action
|
|
||||||
- Everything that's merged in the master goes directly to production.
|
|
||||||
|
|
||||||
[](.github/workflows/)
|
|
||||||
|
|||||||
5
babel.config.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/cli-plugin-babel/preset',
|
||||||
|
],
|
||||||
|
};
|
||||||
3
cypress.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"pluginsFile": "tests/e2e/plugins/index.js"
|
||||||
|
}
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
# Application file
|
|
||||||
|
|
||||||
- privacy.sexy is a data-driven application where it reads the necessary OS-specific logic from [`application.yaml`](./../src/application/application.yaml)
|
|
||||||
- 💡 Best practices
|
|
||||||
- If you repeat yourself, try to utilize [YAML-defined functions](#function)
|
|
||||||
- Always try to add documentation and a way to revert a tweak in [scripts](#script)
|
|
||||||
- 📖 Types in code: [`application.d.ts`](./../src/application/application.yaml.d.ts)
|
|
||||||
|
|
||||||
## Objects
|
|
||||||
|
|
||||||
### `Application`
|
|
||||||
|
|
||||||
- Application file simply defines different categories and their scripts in a tree structure.
|
|
||||||
- Application file also allows defining common [function](#function)s to be used throughout the application if you'd like different scripts to share same code.
|
|
||||||
|
|
||||||
#### `Application` syntax
|
|
||||||
|
|
||||||
- `actions: [` ***[`Category`](#Category)*** `, ... ]` **(required)**
|
|
||||||
- Each [category](#category) is rendered as different cards in card presentation.
|
|
||||||
- ❗ Application must consist of at least one category.
|
|
||||||
- `functions: [` ***[`Function`](#Function)*** `, ... ]`
|
|
||||||
- Functions are optionally defined to re-use the same code throughout different scripts.
|
|
||||||
|
|
||||||
### `Category`
|
|
||||||
|
|
||||||
- Category has a parent that has tree-like structure where it can have subcategories or subscripts.
|
|
||||||
- It's a logical grouping of different scripts and other categories.
|
|
||||||
|
|
||||||
#### `Category` syntax
|
|
||||||
|
|
||||||
- `category:` *`string`* (**required**)
|
|
||||||
- Name of the category
|
|
||||||
- ❗ Must be unique throughout the application
|
|
||||||
- `children: [` ***[`Category`](#category)*** `|` [***`Script`***](#Script) `, ... ]` (**required**)
|
|
||||||
- ❗ Category must consist of at least one subcategory or script.
|
|
||||||
- Children can be combination of scripts and subcategories.
|
|
||||||
|
|
||||||
### `Script`
|
|
||||||
|
|
||||||
- Script represents a single tweak.
|
|
||||||
- A script must include either:
|
|
||||||
- A `code` and `revertCode`
|
|
||||||
- Or `call` to call YAML-defined functions
|
|
||||||
- 🙏 For any new script, please add `revertCode` and `docs` values if possible.
|
|
||||||
|
|
||||||
#### `Script` syntax
|
|
||||||
|
|
||||||
- `name`: *`string`* (**required**)
|
|
||||||
- Name of the script
|
|
||||||
- ❗ Must be unique throughout the application
|
|
||||||
- E.g. `Disable targeted ads`
|
|
||||||
- `code`: *`string`* (may be **required**)
|
|
||||||
- Batch file commands that will be executed
|
|
||||||
- 💡 If defined, best practice to also define `revertCode`
|
|
||||||
- ❗ If not defined `call` must be defined, do not define if `call` is defined.
|
|
||||||
- `revertCode`: `string`
|
|
||||||
- Code that'll undo the change done by `code` property.
|
|
||||||
- E.g. let's say `code` sets an environment variable as `setx POWERSHELL_TELEMETRY_OPTOUT 1`
|
|
||||||
- then `revertCode` should be doing `setx POWERSHELL_TELEMETRY_OPTOUT 0`
|
|
||||||
- ❗ Do not define if `call` is defined.
|
|
||||||
- `call`: ***[`FunctionCall`](#FunctionCall)*** | `[` ***[`FunctionCall`](#FunctionCall)*** `, ... ]` (may be **required**)
|
|
||||||
- A shared function or sequence of functions to call (called in order)
|
|
||||||
- ❗ If not defined `code` must be defined
|
|
||||||
- `docs`: *`string`* | `[`*`string`*`, ... ]`
|
|
||||||
- Single documentation URL or list of URLs for those who wants to learn more about the script
|
|
||||||
- E.g. `https://docs.microsoft.com/en-us/windows-server/`
|
|
||||||
- `recommend`: `"standard"` | `"strict"` | `undefined` (default)
|
|
||||||
- If not defined then the script will not be recommended
|
|
||||||
- If defined it can be either
|
|
||||||
- `standard`: Only non-breaking scripts without limiting OS functionality
|
|
||||||
- `strict`: Scripts that can break certain functionality in favor of privacy and security
|
|
||||||
|
|
||||||
### `FunctionCall`
|
|
||||||
|
|
||||||
- Describes a single call to a function by optionally providing values to its parameters.
|
|
||||||
- 👀 See [parameter substitution](#parameter-substitution) for an example usage
|
|
||||||
|
|
||||||
#### `FunctionCall` syntax
|
|
||||||
|
|
||||||
- `function`: *`string`* (**required**)
|
|
||||||
- Name of the function to call.
|
|
||||||
- ❗ Function with same name must defined in `functions` property of [Application](#application)
|
|
||||||
- `parameters`: `[ parameterName:` *`parameterValue`*`, ... ]`
|
|
||||||
- Defines key value dictionary for each parameter and its value
|
|
||||||
- E.g.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
parameters:
|
|
||||||
userDefinedParameterName: parameterValue
|
|
||||||
# ...
|
|
||||||
appName: Microsoft.WindowsFeedbackHub
|
|
||||||
```
|
|
||||||
|
|
||||||
### `Function`
|
|
||||||
|
|
||||||
- Functions allow re-usable code throughout the defined scripts.
|
|
||||||
- Functions are templates compiled by privacy.sexy and uses special expressions.
|
|
||||||
- Expressions are defined inside mustaches (double brackets, `{{` and `}}`)
|
|
||||||
- 👀 See [parameter substitution](#parameter-substitution) for an example usage
|
|
||||||
|
|
||||||
#### Parameter substitution
|
|
||||||
|
|
||||||
A simple function example
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
function: EchoArgument
|
|
||||||
parameters: [ 'argument' ]
|
|
||||||
code: Hello {{ $argument }} !
|
|
||||||
```
|
|
||||||
|
|
||||||
It would print "Hello world" if it's called in a [script](#script) as following:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
script: Echo script
|
|
||||||
call:
|
|
||||||
function: EchoArgument
|
|
||||||
parameters:
|
|
||||||
argument: World
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `Function` syntax
|
|
||||||
|
|
||||||
- `name`: *`string`* (**required**)
|
|
||||||
- Name of the function that scripts will use.
|
|
||||||
- Convention is to use camelCase, and be verbs.
|
|
||||||
- E.g. `uninstallStoreApp`
|
|
||||||
- ❗ Function names must be unique
|
|
||||||
- `parameters`: `[` *`string`* `, ... ]`
|
|
||||||
- Name of the parameters that the function has.
|
|
||||||
- Parameter values are provided by a [Script](#script) through a [FunctionCall](#functioncall)
|
|
||||||
- Parameter names must be defined to be used in expressions such as [parameter substitution](#parameter-substitution)
|
|
||||||
- ❗ Parameter names must be unique
|
|
||||||
`code`: *`string`* (**required**)
|
|
||||||
- Batch file commands that will be executed
|
|
||||||
- 💡 If defined, best practice to also define `revertCode`
|
|
||||||
- `revertCode`: *`string`*
|
|
||||||
- Code that'll undo the change done by `code` property.
|
|
||||||
- E.g. let's say `code` sets an environment variable as `setx POWERSHELL_TELEMETRY_OPTOUT 1`
|
|
||||||
- then `revertCode` should be doing `setx POWERSHELL_TELEMETRY_OPTOUT 0`
|
|
||||||
45
docs/application.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Application
|
||||||
|
|
||||||
|
Application layer is mainly responsible for:
|
||||||
|
|
||||||
|
- creating an event-based and mutable [application state](#application-state),
|
||||||
|
- [parsing and compiling](#parsing-and-compiling) the [application data](#application-data).
|
||||||
|
|
||||||
|
📖 Refer to [architecture.md | Layered Application](./architecture.md#layered-application) to read more about the layered architecture.
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
Application layer code exists in [`/src/application`](./../src/application/) and includes following structure:
|
||||||
|
|
||||||
|
- [**`collections/`**](./../src/application/collections/): Holds [collection files](./collection-files.md).
|
||||||
|
- [**`Common/`**](./../src/application/Common/): Contains common functionality in application layer.
|
||||||
|
- `...`: rest of the application layer source code organized using folders-by-feature structure.
|
||||||
|
|
||||||
|
## Application state
|
||||||
|
|
||||||
|
It uses [state pattern](https://en.wikipedia.org/wiki/State_pattern) with context and state objects. [`ApplicationContext.ts`](./../src/application/Context/ApplicationContext.ts) the "Context" of state pattern provides an instance of [`CategoryCollectionState.ts`](./../src/application/Context/State/CategoryCollectionState.ts) (the "State" of the state pattern) for every supported collection.
|
||||||
|
|
||||||
|
Presentation layer uses a singleton (same instance of) [`ApplicationContext.ts`](./../src/application/Context/ApplicationContext.ts) throughout the application to ensure consistent state.
|
||||||
|
|
||||||
|
📖 Refer to [architecture.md | Application State](./architecture.md#application-state) to get an overview of event handling and [presentation.md | Application State](./presentation.md#application-state) for deeper look into how the presentation layer manages state.
|
||||||
|
|
||||||
|
## Application data
|
||||||
|
|
||||||
|
Application data is collection files using YAML. You can refer to [collection-files.md](./collection-files.md) to read more about the scheme and structure of application data files. You can also check the source code [collection yaml files](./../src/application/collections/) to directly see the application data using that scheme.
|
||||||
|
|
||||||
|
Application layer [parses and compiles](#parsing-and-compiling) application data into [`Application`](./../src/domain/Application.ts)). Once parsed, application layer provides the necessary functionality to presentation layer based on the application data. You can read more about how presentation layer consumes the application data in [presentation.md | Application Data](./presentation.md#application-data).
|
||||||
|
|
||||||
|
Application layer enables [data-driven programming](https://en.wikipedia.org/wiki/Data-driven_programming) by leveraging the data to the rest of the source code. It makes it easy for community to contribute on the project by using a declarative language used in collection files.
|
||||||
|
|
||||||
|
### Parsing and compiling
|
||||||
|
|
||||||
|
Application layer parses the application data to compile the domain object [`Application.ts`](./../src/domain/Application.ts).
|
||||||
|
|
||||||
|
A webpack loader loads (or injects) application data ([collection yaml files](./../src/application/collections/)) into the application layer in compile time. Application layer ([`ApplicationFactory.ts`](./../src/application/ApplicationFactory.ts)) parses and compiles this data in runtime.
|
||||||
|
|
||||||
|
Application layer compiles templating syntax during parsing to create the end scripts. You can read more about templating syntax in [templating.md](./templating.md) and how application data uses them through functions in [collection-files.md | Function](./collection-files.md#function).
|
||||||
|
|
||||||
|
The steps to extend the templating syntax:
|
||||||
|
|
||||||
|
1. Add a new parser under [SyntaxParsers](./../src/application/Parser/Script/Compiler/Expressions/SyntaxParsers) where you can look at other parsers to understand more.
|
||||||
|
2. Register your in [CompositeExpressionParser](./../src/application/Parser/Script/Compiler/Expressions/Parser/CompositeExpressionParser.ts).
|
||||||
66
docs/architecture.md
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
# Architecture overview
|
||||||
|
|
||||||
|
This repository consists of:
|
||||||
|
|
||||||
|
- A [layered application](#layered-application).
|
||||||
|
- [AWS infrastructure](#aws-infrastructure) as code and instructions to host the website.
|
||||||
|
- [GitOps](#gitops) practices for development, maintenance and deployment.
|
||||||
|
|
||||||
|
## Layered application
|
||||||
|
|
||||||
|
Application is
|
||||||
|
|
||||||
|
- powered by **TypeScript**, **Vue.js** and **Electron** 💪,
|
||||||
|
- and driven by **Domain-driven design**, **Event-driven architecture**, **Data-driven programming** concepts.
|
||||||
|
|
||||||
|
Application uses highly decoupled models & services in different DDD layers:
|
||||||
|
|
||||||
|
- presentation layer (see [presentation.md](./presentation.md)),
|
||||||
|
- application layer (see [application.md](./application.md)),
|
||||||
|
- and domain layer.
|
||||||
|
|
||||||
|
Application layer depends on and consumes domain layer. [Presentation layer](./presentation.md) consumes and depends on application layer along with domain layer. Application and presentation layers can communicate through domain model.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Application state
|
||||||
|
|
||||||
|
State handling uses an event-driven subscription model to signal state changes and special functions to register changes. It does not depend on third party packages.
|
||||||
|
|
||||||
|
Each layer treat application layer differently.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
*[Presentation layer](./presentation.md)*:
|
||||||
|
|
||||||
|
- Each component holds their own state about presentation-related data.
|
||||||
|
- Components register shared state changes into application state using functions.
|
||||||
|
- Components listen to shared state changes using event subscriptions.
|
||||||
|
- 📖 Read more: [presentation.md | Application state](./presentation.md#application-state).
|
||||||
|
|
||||||
|
*[Application layer](./application.md)*:
|
||||||
|
|
||||||
|
- Stores the application-specific state.
|
||||||
|
- The state it exposed for read with getter functions and set using setter functions, setter functions also fire application events that allows other parts of application and the view in presentation layer to react.
|
||||||
|
- So state is mutable, and fires related events when mutated.
|
||||||
|
- 📖 Read more: [application.md | Application state](./application.md#application-state).
|
||||||
|
|
||||||
|
It's comparable with flux ([`redux`](https://redux.js.org/)) or flux-like ([`vuex`](https://vuex.vuejs.org/)) patterns. Flux component "view" is [presentation layer](./presentation.md) in Vue. Flux functions "dispatcher", "store" and "action creation" functions lie in the [application layer](./application.md). A difference is that application state in privacy.sexy is mutable and lies in single flux "store" that holds app state and logic. The "actions" mutate the state directly which in turns act as dispatcher to notify its own event subscriptions (callbacks).
|
||||||
|
|
||||||
|
## AWS infrastructure
|
||||||
|
|
||||||
|
The web-site runs on serverless AWS infrastructure. Infrastructure is open-source and deployed as code. [aws-static-site-with-cd](https://github.com/undergroundwires/aws-static-site-with-cd) project includes the source code.
|
||||||
|
|
||||||
|
[](https://github.com/undergroundwires/aws-static-site-with-cd)
|
||||||
|
|
||||||
|
The design priorities highest security then minimizing cloud infrastructure costs.
|
||||||
|
|
||||||
|
This project includes [GitHub Actions](../.github/workflows/) to automatically provision the infrastructure with zero-touch and without any "hidden" steps, ensuring everything is open-source and transparent. Git repositories includes all necessary instructions and automation with [GitOps](#gitops) practices.
|
||||||
|
|
||||||
|
## GitOps
|
||||||
|
|
||||||
|
CI/CD pipelines automate operational tasks based on different Git events. [bump-everywhere](https://github.com/undergroundwires/bump-everywhere) enables this automation.
|
||||||
|
|
||||||
|
📖 Read more in [`ci-cd.md`](./ci-cd.md#gitops).
|
||||||
|
|
||||||
|
[](../.github/workflows/)
|
||||||
45
docs/ci-cd.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# CI/CD overview
|
||||||
|
|
||||||
|
## GitOps
|
||||||
|
|
||||||
|
CI/CD is fully automated using different Git events and GitHub actions. This repository uses [bump-everywhere](https://github.com/undergroundwires/bump-everywhere) to automate versioning, tagging, creation of `CHANGELOG.md` and GitHub releases. A dedicated workflow [release.desktop.yaml](./../.github/workflows/release.desktop.yaml) creates desktop installers and executables and attaches them into GitHub releases.
|
||||||
|
|
||||||
|
Everything that's merged in the master goes directly to production.
|
||||||
|
|
||||||
|
[](../.github/workflows/)
|
||||||
|
|
||||||
|
## Pipeline files
|
||||||
|
|
||||||
|
privacy.sexy uses [GitHub actions](https://github.com/features/actions) to define and run pipelines as code.
|
||||||
|
|
||||||
|
GitHub workflows i.e. pipelines exist in [`/.github/.workflows/`](./../.github/workflows/) folder without any subfolders due to GitHub actions requirements [1] .
|
||||||
|
|
||||||
|
[1]: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#about-yaml-syntax-for-workflows
|
||||||
|
|
||||||
|
## Pipeline types
|
||||||
|
|
||||||
|
We categorize pipelines into different categories. We use these names in convention when naming files and actions, see [naming conventions](#naming-conventions).
|
||||||
|
|
||||||
|
The categories consist of:
|
||||||
|
|
||||||
|
- `tests`: Different types of tests to verify functionality.
|
||||||
|
- `checks`: Other controls such as vulnerability scans or styling checks.
|
||||||
|
- `release`: Pipelines used for release of deployment such as building and testing.
|
||||||
|
|
||||||
|
## Naming conventions
|
||||||
|
|
||||||
|
Convention for naming pipeline files: **`<type>.<name>.yaml`**.
|
||||||
|
|
||||||
|
**`type`**:
|
||||||
|
|
||||||
|
- Sub-folders do not work for GitHub workflows [1] so we use `<type>.` prefix to organize them.
|
||||||
|
- See also [pipeline types](#pipeline-types) for list of all usable types.
|
||||||
|
|
||||||
|
**`name`**:
|
||||||
|
|
||||||
|
- We name workflows using kebab-case.
|
||||||
|
- E.g. file name `tests.unit.yaml`, pipeline file should set the naem as: `name: unit-tests`.
|
||||||
|
- Kebab-case allows to have better URL references to them.
|
||||||
|
- [README.md](./../README.md) uses URL references to show status badges for actions.
|
||||||
|
|
||||||
|
[1]: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#about-yaml-syntax-for-workflows
|
||||||
173
docs/collection-files.md
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
# Collection files
|
||||||
|
|
||||||
|
- privacy.sexy is a data-driven application where it reads the necessary OS-specific logic from yaml files in [`application/collections`](./../src/application/collections/)
|
||||||
|
- 💡 Best practices
|
||||||
|
- If you repeat yourself, try to utilize [YAML-defined functions](#Function)
|
||||||
|
- Always try to add documentation and a way to revert a tweak in [scripts](#Script)
|
||||||
|
- 📖 Types in code: [`collection.yaml.d.ts`](./../src/application/collections/collection.yaml.d.ts)
|
||||||
|
|
||||||
|
## Objects
|
||||||
|
|
||||||
|
### `Collection`
|
||||||
|
|
||||||
|
- A collection simply defines:
|
||||||
|
- different categories and their scripts in a tree structure
|
||||||
|
- OS specific details
|
||||||
|
- Also allows defining common [function](#Function)s to be used throughout the collection if you'd like different scripts to share same code.
|
||||||
|
|
||||||
|
#### `Collection` syntax
|
||||||
|
|
||||||
|
- `os:` *`string`* (**required**)
|
||||||
|
- Operating system that the [Collection](#collection) is written for.
|
||||||
|
- 📖 See [OperatingSystem.ts](./../src/domain/OperatingSystem.ts) enumeration for allowed values.
|
||||||
|
- `actions: [` ***[`Category`](#Category)*** `, ... ]` **(required)**
|
||||||
|
- Each [category](#category) is rendered as different cards in card presentation.
|
||||||
|
- ❗ A [Collection](#collection) must consist of at least one category.
|
||||||
|
- `functions: [` ***[`Function`](#Function)*** `, ... ]`
|
||||||
|
- Functions are optionally defined to re-use the same code throughout different scripts.
|
||||||
|
- `scripting:` ***[`ScriptingDefinition`](#ScriptingDefinition)*** **(required)**
|
||||||
|
- Defines the scripting language that the code of other action uses.
|
||||||
|
|
||||||
|
### `Category`
|
||||||
|
|
||||||
|
- Category has a parent that has tree-like structure where it can have subcategories or subscripts.
|
||||||
|
- It's a logical grouping of different scripts and other categories.
|
||||||
|
|
||||||
|
#### `Category` syntax
|
||||||
|
|
||||||
|
- `category:` *`string`* (**required**)
|
||||||
|
- Name of the category
|
||||||
|
- ❗ Must be unique throughout the [Collection](#collection)
|
||||||
|
- `children: [` ***[`Category`](#Category)*** `|` [***`Script`***](#Script) `, ... ]` (**required**)
|
||||||
|
- ❗ Category must consist of at least one subcategory or script.
|
||||||
|
- Children can be combination of scripts and subcategories.
|
||||||
|
|
||||||
|
### `Script`
|
||||||
|
|
||||||
|
- Script represents a single tweak.
|
||||||
|
- A script can be of two different types (just like [functions](#function)):
|
||||||
|
1. Inline script; a script with an inline code
|
||||||
|
- Must define `code` property and optionally `revertCode` but not `call`
|
||||||
|
2. Caller script; a script that calls other functions
|
||||||
|
- Must define `call` property but not `code` or `revertCode`
|
||||||
|
- 🙏 For any new script, please add `revertCode` and `docs` values if possible.
|
||||||
|
|
||||||
|
#### `Script` syntax
|
||||||
|
|
||||||
|
- `name`: *`string`* (**required**)
|
||||||
|
- Name of the script
|
||||||
|
- ❗ Must be unique throughout the [Collection](#collection)
|
||||||
|
- E.g. `Disable targeted ads`
|
||||||
|
- `code`: *`string`* (may be **required**)
|
||||||
|
- Batch file commands that will be executed
|
||||||
|
- 💡 If defined, best practice to also define `revertCode`
|
||||||
|
- ❗ If not defined `call` must be defined, do not define if `call` is defined.
|
||||||
|
- `revertCode`: `string`
|
||||||
|
- Code that'll undo the change done by `code` property.
|
||||||
|
- E.g. let's say `code` sets an environment variable as `setx POWERSHELL_TELEMETRY_OPTOUT 1`
|
||||||
|
- then `revertCode` should be doing `setx POWERSHELL_TELEMETRY_OPTOUT 0`
|
||||||
|
- ❗ Do not define if `call` is defined.
|
||||||
|
- `call`: ***[`FunctionCall`](#FunctionCall)*** | `[` ***[`FunctionCall`](#FunctionCall)*** `, ... ]` (may be **required**)
|
||||||
|
- A shared function or sequence of functions to call (called in order)
|
||||||
|
- ❗ If not defined `code` must be defined
|
||||||
|
- `docs`: *`string`* | `[`*`string`*`, ... ]`
|
||||||
|
- Single documentation URL or list of URLs for those who wants to learn more about the script
|
||||||
|
- E.g. `https://docs.microsoft.com/en-us/windows-server/`
|
||||||
|
- `recommend`: `"standard"` | `"strict"` | `undefined` (default)
|
||||||
|
- If not defined then the script will not be recommended
|
||||||
|
- If defined it can be either
|
||||||
|
- `standard`: Only non-breaking scripts without limiting OS functionality
|
||||||
|
- `strict`: Scripts that can break certain functionality in favor of privacy and security
|
||||||
|
|
||||||
|
### `FunctionCall`
|
||||||
|
|
||||||
|
- Describes a single call to a function by optionally providing values to its parameters.
|
||||||
|
- 👀 See [parameter substitution](./templating.md#parameter-substitution) for an example usage
|
||||||
|
|
||||||
|
#### `FunctionCall` syntax
|
||||||
|
|
||||||
|
- `function`: *`string`* (**required**)
|
||||||
|
- Name of the function to call.
|
||||||
|
- ❗ Function with same name must defined in `functions` property of [Collection](#collection)
|
||||||
|
- `parameters`: `[ parameterName:` *`parameterValue`*`, ... ]`
|
||||||
|
- Defines key value dictionary for each parameter and its value
|
||||||
|
- E.g.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
parameters:
|
||||||
|
userDefinedParameterName: parameterValue
|
||||||
|
# ...
|
||||||
|
appName: Microsoft.WindowsFeedbackHub
|
||||||
|
```
|
||||||
|
|
||||||
|
- 💡 [Expressions (templating)](./templating.md#expressions) can be used as parameter value
|
||||||
|
|
||||||
|
### `Function`
|
||||||
|
|
||||||
|
- Functions allow re-usable code throughout the defined scripts.
|
||||||
|
- Functions are templates compiled by privacy.sexy and uses special expression expressions.
|
||||||
|
- A function can be of two different types (just like [scripts](#script)):
|
||||||
|
1. Inline function: a function with an inline code.
|
||||||
|
- Must define `code` property and optionally `revertCode` but not `call`.
|
||||||
|
2. Caller function: a function that calls other functions.
|
||||||
|
- Must define `call` property but not `code` or `revertCode`.
|
||||||
|
- 👀 Read more on [Templating](./templating.md) for function expressions and [example usages](./templating.md#parameter-substitution).
|
||||||
|
|
||||||
|
#### `Function` syntax
|
||||||
|
|
||||||
|
- `name`: *`string`* (**required**)
|
||||||
|
- Name of the function that scripts will use.
|
||||||
|
- Convention is to use camelCase, and be verbs.
|
||||||
|
- E.g. `uninstallStoreApp`
|
||||||
|
- ❗ Function names must be unique
|
||||||
|
- `parameters`: `[` ***[`FunctionParameter`](#FunctionParameter)*** `, ... ]`
|
||||||
|
- List of parameters that function code refers to.
|
||||||
|
- ❗ Must be defined to be able use in [`FunctionCall`](#functioncall) or [expressions (templating)](./templating.md#expressions)
|
||||||
|
`code`: *`string`* (**required** if `call` is undefined)
|
||||||
|
- Batch file commands that will be executed
|
||||||
|
- 💡 [Expressions (templating)](./templating.md#expressions) can be used in its value
|
||||||
|
- 💡 If defined, best practice to also define `revertCode`
|
||||||
|
- ❗ If not defined `call` must be defined
|
||||||
|
- `revertCode`: *`string`*
|
||||||
|
- Code that'll undo the change done by `code` property.
|
||||||
|
- E.g. let's say `code` sets an environment variable as `setx POWERSHELL_TELEMETRY_OPTOUT 1`
|
||||||
|
- then `revertCode` should be doing `setx POWERSHELL_TELEMETRY_OPTOUT 0`
|
||||||
|
- 💡 [Expressions (templating)](./templating.md#expressions) can be used in code
|
||||||
|
- `call`: ***[`FunctionCall`](#FunctionCall)*** | `[` ***[`FunctionCall`](#FunctionCall)*** `, ... ]` (may be **required**)
|
||||||
|
- A shared function or sequence of functions to call (called in order)
|
||||||
|
- The parameter values that are sent can use [expressions (templating)](./templating.md#expressions)
|
||||||
|
- ❗ If not defined `code` must be defined
|
||||||
|
|
||||||
|
### `FunctionParameter`
|
||||||
|
|
||||||
|
- Defines a parameter that function requires optionally or mandatory.
|
||||||
|
- Its arguments are provided by a [Script](#script) through a [FunctionCall](#FunctionCall).
|
||||||
|
|
||||||
|
#### `FunctionParameter` syntax
|
||||||
|
|
||||||
|
- `name`: *`string`* (**required**)
|
||||||
|
- Name of the parameters that the function has.
|
||||||
|
- Parameter names must be defined to be used in [expressions (templating)](./templating.md#expressions).
|
||||||
|
- ❗ Parameter names must be unique and include alphanumeric characters only.
|
||||||
|
- `optional`: *`boolean`* (default: `false`)
|
||||||
|
- Specifies whether the caller [Script](#script) must provide any value for the parameter.
|
||||||
|
- If set to `false` i.e. an argument value is not optional then it expects a non-empty value for the variable;
|
||||||
|
- Otherwise it throws.
|
||||||
|
- 💡 Set it to `true` if a parameter is used conditionally;
|
||||||
|
- Or else set it to `false` for verbosity or do not define it as default value is `false` anyway.
|
||||||
|
- 💡 Can be used in conjunction with [`with` expression](./templating.md#with).
|
||||||
|
|
||||||
|
### `ScriptingDefinition`
|
||||||
|
|
||||||
|
- Defines global properties for scripting that's used throughout its parent [Collection](#collection).
|
||||||
|
|
||||||
|
#### `ScriptingDefinition` syntax
|
||||||
|
|
||||||
|
- `language:` *`string`* (**required**)
|
||||||
|
- 📖 See [ScriptingLanguage.ts](./../src/domain/ScriptingLanguage.ts) enumeration for allowed values.
|
||||||
|
- `startCode:` *`string`* (**required**)
|
||||||
|
- Code that'll be inserted on top of user created script.
|
||||||
|
- Global variables such as `$homepage`, `$version`, `$date` can be used using [parameter substitution](./templating.md#parameter-substitution) code syntax such as `Welcome to {{ $homepage }}!`
|
||||||
|
- `endCode:` *`string`* (**required**)
|
||||||
|
- Code that'll be inserted at the end of user created script.
|
||||||
|
- Global variables such as `$homepage`, `$version`, `$date` can be used using [parameter substitution](./templating.md#parameter-substitution) code syntax such as `Welcome to {{ $homepage }}!`
|
||||||
53
docs/development.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# Development
|
||||||
|
|
||||||
|
Before your commit, a good practice is to:
|
||||||
|
|
||||||
|
1. [Run unit tests](#testing)
|
||||||
|
2. [Lint your code](#linting)
|
||||||
|
|
||||||
|
You could run other types of tests as well, but they may take longer time and overkill for your changes. Automated actions executes the tests for a pull request or change in the main branch. See [ci-cd.md](./ci-cd.md) for more information.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Install node >15.x.
|
||||||
|
- Install dependencies using `npm install`.
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
- Run unit tests: `npm run test:unit`
|
||||||
|
- Run integration tests: `npm run test:integration`
|
||||||
|
- Run e2e (end-to-end) tests
|
||||||
|
- Interactive mode with GUI: `npm run test:e2e`
|
||||||
|
- Headless mode without GUI: `npm run test:e2e -- --headless`
|
||||||
|
|
||||||
|
📖 Read more about testing in [tests](./tests.md).
|
||||||
|
|
||||||
|
### Linting
|
||||||
|
|
||||||
|
- Lint all (recommended 💡): `npm run lint`
|
||||||
|
- Markdown: `npm run lint:md`
|
||||||
|
- Markdown consistency `npm run lint:md:consistency`
|
||||||
|
- Markdown relative URLs: `npm run lint:md:relative-urls`
|
||||||
|
- JavaScript/TypeScript: `npm run lint:eslint`
|
||||||
|
- Yaml: `npm run lint:yaml`
|
||||||
|
|
||||||
|
### Running
|
||||||
|
|
||||||
|
- Run in local server: `npm run serve`
|
||||||
|
- 💡 Meant for local development with features such as hot-reloading.
|
||||||
|
- Run using Docker:
|
||||||
|
1. Build: `docker build -t undergroundwires/privacy.sexy:latest .`
|
||||||
|
2. Run: `docker run -it -p 8080:80 --rm --name privacy.sexy undergroundwires/privacy.sexy:latest`
|
||||||
|
|
||||||
|
### Building
|
||||||
|
|
||||||
|
- Build web application: `npm run build`
|
||||||
|
- Build desktop application: `npm run electron:build`
|
||||||
|
|
||||||
|
## Recommended extensions
|
||||||
|
|
||||||
|
You should use EditorConfig to follow project style.
|
||||||
|
|
||||||
|
For Visual Studio Code, [`.vscode/extensions.json`](./../.vscode/extensions.json) includes list of recommended extensions.
|
||||||
82
docs/presentation.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# Presentation layer
|
||||||
|
|
||||||
|
Presentation layer consists of UI-related code. It uses Vue.js as JavaScript framework and includes Vue.js components. It also includes [Electron](https://www.electronjs.org/) to provide functionality to desktop application.
|
||||||
|
|
||||||
|
It's designed event-driven from bottom to top. It listens user events (from top) and state events (from bottom) to update state or the GUI.
|
||||||
|
|
||||||
|
📖 Refer to [architecture.md (Layered Application)](./architecture.md#layered-application) to read more about the layered architecture.
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
- [`/src/` **`presentation/`**](./../src/presentation/): Contains all presentation related code including Vue and Electron configurations
|
||||||
|
- [**`bootstrapping/`**](./../src/presentation/bootstrapping/): Registers Vue global objects including components and plugins.
|
||||||
|
- [**`components/`**](./../src/presentation/components/): Contains all Vue components and their helper classes.
|
||||||
|
- [**`Shared/`**](./../src/presentation/components/Shared): Contains Vue components and component helpers that other components share.
|
||||||
|
- [**`assets/`**](./../src/presentation/assets/styles/): Contains assets that webpack will process.
|
||||||
|
- [**`fonts/`**](./../src/presentation/assets/fonts/): Contains fonts
|
||||||
|
- [**`styles/`**](./../src/presentation/assets/styles/): Contains shared styles used throughout different components.
|
||||||
|
- [**`components/`**](./../src/presentation/assets/styles/components): Contains reusable styles coupled to a Vue/HTML component.
|
||||||
|
- [**`vendors-extensions/`**](./../src/presentation/assets/styles/third-party-extensions): Contains styles that override third-party components used.
|
||||||
|
- [**`main.scss`**](./../src/presentation/assets/styles/main.scss): Primary Sass file, passes along all other styles, should be the single file used from other components.
|
||||||
|
- [**`main.ts`**](./../src/presentation/main.ts): Application entry point that mounts and starts Vue application.
|
||||||
|
- [**`electron/`**](./../src/presentation/electron/): Electron configuration for the desktop application.
|
||||||
|
- [**`main.ts`**](./../src/presentation/main.ts): Main process of Electron, started as first thing when app starts.
|
||||||
|
- [**`/public/`**](./../public/): Contains static assets that are directly copied and do not go through webpack.
|
||||||
|
- [**`/vue.config.js`**](./../vue.config.js): Global Vue CLI configurations loaded by `@vue/cli-service`.
|
||||||
|
- [**`/postcss.config.js`**](./../postcss.config.js): PostCSS configurations used by Vue CLI internally.
|
||||||
|
- [**`/babel.config.js`**](./../babel.config.js): Babel configurations for polyfills used by `@vue/cli-plugin-babel`.
|
||||||
|
|
||||||
|
## Application data
|
||||||
|
|
||||||
|
Components (should) use [ApplicationFactory](./../src/application/ApplicationFactory.ts) singleton to reach the application domain to avoid [parsing and compiling](./application.md#parsing-and-compiling) the application again.
|
||||||
|
|
||||||
|
[Application.ts](../src/domain/Application.ts) is an immutable domain model that represents application state. It includes:
|
||||||
|
|
||||||
|
- available scripts, collections as defined in [collection files](./collection-files.md),
|
||||||
|
- package information as defined in [`package.json`](./../package.json).
|
||||||
|
|
||||||
|
You can read more about how application layer provides application data to he presentation in [application.md | Application data](./application.md#application-data).
|
||||||
|
|
||||||
|
## Application state
|
||||||
|
|
||||||
|
Inheritance of a Vue components marks whether it uses application state . Components that does not handle application state extends `Vue`. Stateful components mutate or/and react to state changes (such as user selection or search queries) in [ApplicationContext](./../src/application/Context/ApplicationContext.ts) extend [`StatefulVue`](./../src/presentation/components/Shared/StatefulVue.ts) class to access the context / state.
|
||||||
|
|
||||||
|
[`StatefulVue`](./../src/presentation/components/Shared/StatefulVue.ts) functions include:
|
||||||
|
|
||||||
|
- Creating a singleton of the state and makes it available to presentation layer as single source of truth.
|
||||||
|
- Providing virtual abstract `handleCollectionState` callback that it calls when
|
||||||
|
- the Vue loads the component,
|
||||||
|
- and also every time when state changes.
|
||||||
|
- Providing `events` member to make lifecycling of state subscriptions events easier because it ensures that components unsubscribe from listening to state events when
|
||||||
|
- the component is no longer used (destroyed),
|
||||||
|
- an if [ApplicationContext](./../src/application/Context/ApplicationContext.ts) changes the active [collection](./collection-files.md) to a different one.
|
||||||
|
|
||||||
|
📖 Refer to [architecture.md | Application State](./architecture.md#application-state) to get an overview of event handling and [application.md | Application State](./presentation.md#application-state) for deeper look into how the application layer manages state.
|
||||||
|
|
||||||
|
## Modals
|
||||||
|
|
||||||
|
[Dialog.vue](./../src/presentation/components/Shared/Dialog.vue) is a shared component that other components used to show modal windows.
|
||||||
|
|
||||||
|
You can use it by wrapping the content inside of its `slot` and call `.show()` function on its reference. For example:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<Dialog ref="testDialog">
|
||||||
|
<div>Hello world</div>
|
||||||
|
</Dialog>
|
||||||
|
<div @click="$refs.testDialog.show()">Show dialog</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sass naming convention
|
||||||
|
|
||||||
|
- Use lowercase for variables/functions/mixins, e.g.:
|
||||||
|
- Variable: `$variable: value;`
|
||||||
|
- Function: `@function function() {}`
|
||||||
|
- Mixin: `@mixin mixin() {}`
|
||||||
|
- Use - for a phrase/compound word, e.g.:
|
||||||
|
- Variable: `$some-variable: value;`
|
||||||
|
- Function: `@function some-function() {}`
|
||||||
|
- Mixin: `@mixin some-mixin() {}`
|
||||||
|
- Grouping and name variables from generic to specific, e.g.:
|
||||||
|
- ✅ `$border-blue`, `$border-blue-light`, `$border-blue-lightest`, `$border-red`
|
||||||
|
- ❌ `$blue-border`, `$light-blue-border`, `$lightest-blue-border`, `$red-border`
|
||||||
|
|
||||||
91
docs/templating.md
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# Templating
|
||||||
|
|
||||||
|
## Benefits of templating
|
||||||
|
|
||||||
|
- Generating scripts by sharing code to increase best-practice usage and maintainability.
|
||||||
|
- Creating self-contained scripts without cross-dependencies.
|
||||||
|
- Use of pipes for writing cleaner code and letting pipes do dirty work.
|
||||||
|
|
||||||
|
## Expressions
|
||||||
|
|
||||||
|
- Expressions start and end with mustaches (double brackets, `{{` and `}}`).
|
||||||
|
- E.g. `Hello {{ $name }} !`
|
||||||
|
- Syntax is close to [Go Templates ❤️](https://pkg.go.dev/text/template) that has inspired this templating language.
|
||||||
|
- Functions enables usage of expressions.
|
||||||
|
- In script definition parts of a function, see [`Function`](./collection-files.md#Function).
|
||||||
|
- When doing a call as argument values, see [`FunctionCall`](./collection-files.md#Function).
|
||||||
|
|
||||||
|
### Parameter substitution
|
||||||
|
|
||||||
|
A simple function example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
function: EchoArgument
|
||||||
|
parameters:
|
||||||
|
- name: 'argument'
|
||||||
|
code: Hello {{ $argument }} !
|
||||||
|
```
|
||||||
|
|
||||||
|
It would print "Hello world" if it's called in a [script](./collection-files.md#script) as following:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
script: Echo script
|
||||||
|
call:
|
||||||
|
function: EchoArgument
|
||||||
|
parameters:
|
||||||
|
argument: World
|
||||||
|
```
|
||||||
|
|
||||||
|
A function can call other functions such as:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
-
|
||||||
|
function: CallerFunction
|
||||||
|
parameters:
|
||||||
|
- name: 'value'
|
||||||
|
call:
|
||||||
|
function: EchoArgument
|
||||||
|
parameters:
|
||||||
|
argument: {{ $value }}
|
||||||
|
-
|
||||||
|
function: EchoArgument
|
||||||
|
parameters:
|
||||||
|
- name: 'argument'
|
||||||
|
code: Hello {{ $argument }} !
|
||||||
|
```
|
||||||
|
|
||||||
|
### with
|
||||||
|
|
||||||
|
Skips its "block" if the variable is absent or empty. Its "block" is between `with` start (`{{ with .. }}`) and end (`{{ end }`}) expressions. E.g. `{{ with $parameterName }} Hi, I'm a block! {{ end }}`.
|
||||||
|
|
||||||
|
Binds its context (`.`) value of provided argument for the parameter if provided one. E.g. `{{ with $parameterName }} Parameter value is {{ . }} here {{ end }}`.
|
||||||
|
|
||||||
|
💡 Declare parameters used for `with` condition as optional. Set `optional: true` for the argument if you use it like `{{ with $argument }} .. {{ end }}`.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
function: FunctionThatOutputsConditionally
|
||||||
|
parameters:
|
||||||
|
- name: 'argument'
|
||||||
|
optional: true
|
||||||
|
code: |-
|
||||||
|
{{ with $argument }}
|
||||||
|
Value is: {{ . }}
|
||||||
|
{{ end }}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pipes
|
||||||
|
|
||||||
|
- Pipes are functions available for handling text.
|
||||||
|
- Allows stacking actions one after another also known as "chaining".
|
||||||
|
- Like [Unix pipelines](https://en.wikipedia.org/wiki/Pipeline_(Unix)), the concept is simple: each pipeline's output becomes the input of the following pipe.
|
||||||
|
- You cannot create pipes. [A dedicated compiler](./application.md#parsing-and-compiling) provides pre-defined pipes to consume in collection files.
|
||||||
|
- You can combine pipes with other expressions such as [parameter substitution](#parameter-substitution) and [with](#with) syntax.
|
||||||
|
- ❗ Pipe names must be camelCase without any space or special characters.
|
||||||
|
- **Existing pipes**
|
||||||
|
- `inlinePowerShell`: Converts a multi-lined PowerShell script to a single line.
|
||||||
|
- `escapeDoubleQuotes`: Escapes `"` characters, allows you to use them inside double quotes (`"`).
|
||||||
|
- **Example usages**
|
||||||
|
- `{{ with $code }} echo "{{ . | inlinePowerShell }}" {{ end }}`
|
||||||
|
- `{{ with $code }} echo "{{ . | inlinePowerShell | escapeDoubleQuotes }}" {{ end }}`
|
||||||
81
docs/tests.md
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
# Tests
|
||||||
|
|
||||||
|
There are different types of tests executed:
|
||||||
|
|
||||||
|
1. [Unit tests](#unit-tests)
|
||||||
|
2. [Integration tests](#integration-tests)
|
||||||
|
3. [End-to-end (E2E) tests](#e2e-tests)
|
||||||
|
|
||||||
|
Common aspects for all tests:
|
||||||
|
|
||||||
|
- They use [Mocha](https://mochajs.org/) and [Chai](https://www.chaijs.com/).
|
||||||
|
- Their files end with `.spec.{ts|js}` suffix.
|
||||||
|
|
||||||
|
💡 You can use path/module alias `@/tests` in import statements.
|
||||||
|
|
||||||
|
## Unit tests
|
||||||
|
|
||||||
|
- Unit tests test each component in isolation.
|
||||||
|
- All unit tests goes under [`./tests/unit`](./../tests/unit).
|
||||||
|
- They rely on [stubs](./../tests/unit/shared/Stubs) for isolation.
|
||||||
|
|
||||||
|
### Unit tests structure
|
||||||
|
|
||||||
|
- [`./src/`](./../src/)
|
||||||
|
- Includes source code that unit tests will test.
|
||||||
|
- [`./tests/unit/`](./../tests/unit/)
|
||||||
|
- Includes test code.
|
||||||
|
- Tests follow same folder structure as [`./src/`](./../src).
|
||||||
|
- E.g. if system under test lies in [`./src/application/ApplicationFactory.ts`](./../src/application/ApplicationFactory.ts) then its tests would be in test would be at [`./tests/unit/application/ApplicationFactory.spec.ts`](./../tests/unit/application/ApplicationFactory.spec.ts).
|
||||||
|
- [`shared/`](./../tests/unit/shared/)
|
||||||
|
- Includes common functionality that's shared across unit tests.
|
||||||
|
- [`Assertions/`](./../tests/unit/shared/Assertions):
|
||||||
|
- Common assertions that extend [Chai Assertion Library](https://www.chaijs.com/).
|
||||||
|
- Asserting functions should start with `expect` prefix.
|
||||||
|
- [`TestCases/`](./../tests/unit/shared/TestCases/)
|
||||||
|
- Shared test cases.
|
||||||
|
- Functions that calls `it()` from [Mocha test framework](https://mochajs.org/) should have `it` prefix.
|
||||||
|
- E.g. `itEachAbsentCollectionValue()`.
|
||||||
|
- [`Stubs/`](./../tests/unit/shared/Stubs)
|
||||||
|
- Includes stubs to be able to test components in isolation.
|
||||||
|
- Stubs have minimal and dummy behavior to be functional, they may also have spying or mocking functions.
|
||||||
|
|
||||||
|
### Unit tests naming
|
||||||
|
|
||||||
|
- Each test suite first describe the system under test.
|
||||||
|
- E.g. tests for class `Application.ts` are all inside `Application.spec.ts`.
|
||||||
|
- `describe` blocks tests for same function (if applicable).
|
||||||
|
- E.g. test for `run()` are inside `describe('run', () => ..)`.
|
||||||
|
|
||||||
|
### Act, arrange, assert
|
||||||
|
|
||||||
|
- Tests use act, arrange and assert (AAA) pattern when applicable.
|
||||||
|
- **Arrange**
|
||||||
|
- Sets up the test case.
|
||||||
|
- Starts with comment line `// arrange`.
|
||||||
|
- **Act**
|
||||||
|
- Executes the actual test.
|
||||||
|
- Starts with comment line `// act`.
|
||||||
|
- **Assert**
|
||||||
|
- Elicit some sort of expectation.
|
||||||
|
- Starts with comment line `// assert`.
|
||||||
|
|
||||||
|
## Integration tests
|
||||||
|
|
||||||
|
- Tests functionality of a component in combination with others (not isolated).
|
||||||
|
- Ensure dependencies to third parties work as expected.
|
||||||
|
- Defined in [./tests/integration](./../tests/integration).
|
||||||
|
|
||||||
|
## E2E tests
|
||||||
|
|
||||||
|
- Test the functionality and performance of a running application.
|
||||||
|
- Vue CLI plugin [`e2e-cypress`](https://github.com/vuejs/vue-cli/tree/dev/packages/@vue/cli-plugin-e2e-cypress#readme) configures E2E tests.
|
||||||
|
- Test names and folders have logical structure based on tests executed.
|
||||||
|
- The structure is following:
|
||||||
|
- [`cypress.json`](./../cypress.json): Cypress configuration file.
|
||||||
|
- [`./tests/e2e/`](./../tests/e2e/): Base Cypress folder.
|
||||||
|
- [`/specs/`](./../tests/e2e/specs/): Test files named with `.spec.js` extension.
|
||||||
|
- [`/plugins/index.js`](./../tests/e2e/plugins/index.js): Plugin file executed before loading project.
|
||||||
|
- [`/support/index.js`](./../tests/e2e/support/index.js): Support file, runs before every single spec file.
|
||||||
|
- *(Ignored)* `/videos`: Asset folder for videos taken during tests.
|
||||||
|
- *(Ignored)* `/screenshots`: Asset folder for Screenshots taken during tests.
|
||||||
@@ -1 +1 @@
|
|||||||
<mxfile host="www.draw.io" modified="2019-12-27T03:04:27.829Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36" etag="O-1eaon4mqUmgvki0auB" version="12.4.3" pages="1"><diagram id="rhL8jzEM8kVVyiS98U7u" name="Page-1">3Zpdk6I4FIZ/jZdakPDlpa3tzlTNbnVVV83OzE1XhACZAUKF2Or8+k0wCBhau1f8GrpayUkC5HlzTg7IAE7T9V8M5fHfNMDJABjBegBnAwA8YIpPadhsDS50t4aIkWBrMmvDM/mNldFQ1iUJcNFqyClNOMnbRp9mGfZ5y4YYo6t2s5Am7bPmKMKa4dlHiW79lwQ8VsOyjdr+CZMors5sGqomRVVjZShiFNBVwwQfB3DKKOXbvXQ9xYlkV3HZ9pu/Ubu7MIYz/p4OD0/efJxMXmZfs6kd/vj2Txp+HlrVxfFNNWIcCACqSBmPaUQzlDzW1gdGl1mA5WENUarbfKE0F0ZTGH9izjdKTbTkVJhiniaqFq8J/ya7j2xV+t6oma3VkcvCpipknG0anWTxe7Ou7laWqn4FZ/QXntKEsnJ8cFxuomY7cjncN4kqU0GXzMcHMFYzE7EI8wPtwE534S+YplhcqejHcII4eW1fB1IzN9q1q8UVO0rfj2gNrqL1TreWarWI96AbPFG3suuEMbRpNMgpyXjROPKTNIgGKnZC5Zsqctp77n24NTDtvQmzPX09fXbjOGFGwatGj0bsMEbAfmf4uONpaF0zfKjjvqJkqc70xHAhxirOTTNtIrRlXsWE4+cclRhWIlNoSxqSJGkwDmzsBVYXfQ8soOPIHjTj1TyrrgwzjteHxdDhqQ6O23Yf5U2res2H1coeN9Z7xzgTbehc07XMlmu907PMtmeBm3AtcCHX+n8RftwO2iZsBe2j7YHrnj/IQ+9eUsSbmlCnpgwnRQ+gxepJnifE7yNUtwKvFrpDW/51ieGUmzpCw77ddiKdFMXtdhTf3Yw1wzjoCOPu2cK4fS/Oc9P3V/AeEiSoOd2Mpoj0mxotzCAIO7mbhgvH+Ayp0XjPqca6UwGrw6nsczmVpYH+nIUMCSJLny8Z7hV46PnY97uALzzbso0zAHf3clGnA7h3ySjmaMDxK1aZzRugzY+DDrHTDTpwxwvD6GeF8PbYujpbtwMtOBdaV0PLcE4Lwqk6+F3jhR0L8EXxenqoeMw44ffIdj8uXB3uWIPLY4ZRQLLoD8B79dBg6ll8Kn9e6TfsLpDvBbALLoCWZQf9wBXJSRuuYelw7ZHZgRecC6+er4ldEhy7R7pVxMA2R+Obg2xrkAuO+KEU7R18e6C15+xm9ZTjiqjOk2X1MbOc47DMrqej52Olp02o+YDDSGhE/NsDZ+pLymXBVWPoBjfaiNFfG9rtMdMXCg1SEaNc7pK0fJ+giUTCEHyTSUKiTNi4fIizs35BC5w8yYRfTlw4W1DOaSoaJLLiAfm/olKAVjokN9GkPNmkyLfvPcg0CFWFkKylZA/qemYx5/KFiYkkAeZ+kFkj4tMsJEJaNvLFGcFcrHxIfEm7iDlz4UK0GKIsGC6Y+JQmW6Ykc+i4L1+X+GfxIpuINMUb5VXG1+t9r60Jb1/yMQPQ16771x1+WPchCugC79QHnvfyjIqiR91NcGPC6yvx/QtvHhV+ka3Ep5B1Lf7FnlgYpFk+3Sp6Uhq4+zkqBCNdbQh1tStb/2rrucT9q20cVTukLMLZsMBU/rI/d8Stwjwn5fPTYkhzTlLyu0wKenR0WN0O7MTXpbc6pLc+LL0o1m/WbX8JrV9PhI//AQ==</diagram></mxfile>
|
<mxfile host="Electron" modified="2021-01-31T12:32:01.751Z" agent="5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.1.8 Chrome/87.0.4280.88 Electron/11.1.1 Safari/537.36" etag="OTbSPW1ZOLwiPL6mt-j9" version="14.1.8" type="device"><diagram id="rhL8jzEM8kVVyiS98U7u" name="Page-1">3VtZd6JKF/01/dhZjA6PREjCXRbGFpM2L70QCYIoLsUI/PpvnwKcMD3cazrJl6wEqfHU3vvsKoz5Infm6e3KWU5ZPPGiL5IwSb/I+hdJUsUmflNBVhQoDaEo8FfBpCgS9wWDIPfKwqrZJph466OGSRxHSbA8LnTjxcJzk6MyZ7WKt8fNnuPoeNal43u1goHrRPXSx2CSTIvSlirsy++8wJ9WM4tCWTN3qsZlwXrqTOLtQZFsfJE7qzhOilfztONFhF2FS9Hv5pXaXWArb5H8Tofr+9ZNO9J+6A+Ljvr89N2aP5tflSq4JKtW7E0AQHkbr5Jp7McLJzL2pdereLOYeDSsgLt9m24cL1EoojD0kiQr2XQ2SYyiaTKPylovDZLv1P1KLe9GBzV6Wo7Mb7LqZpGssoNOdDs6rNt343dVv3WyimdeJ47iFV+f3OZfqKkDWGK6jjcr1/sJapUQnZXvJT9pJxXtCNGDCUp6br147iFSNFh5kZMEL8eSc0rl+rt2e3LxouT3T7iW3oXrHW9HrO1J/IC8yZfmjXfVVisnO2iwjINFsj4Y+Z4K0KD0TrnMzdI51ZP0/nlrSVRPBFNMv5fPbh3/QVHyu7rHgXcIV5L6m/bxeWSofCj7KMd9caJNOdP9yltj8Zg7XtSEcEzzdhok3mDpcFy2OCkcU/ocRNEBxhPVa02Uc+i3pLHcaFCPeJFUOjuzFVbBeqvES38KXlnbaB6nT5lN2/2eL1c7+/Rgv69OMxdHW268Z2qJR6n1m5klHmeW9B6pJb1Xav07h28fm7YoH5n2L9tLzebbm7zc+ixHxPcU1MWPDP/JPaSaV2vLZRS4l7DqI+OtWfezSt/nyGjwr3KEg/Li62ck/b6Lq8cuvnsYO7Rx6YyNN9/MxtXPkjwf6flK/pQHJLmWdHo8d4LLHo3G4mTyfBZ3UWjKbe8Njkbtk6Rq15NKUs4klfpWSaXUgDYXzysHiGzcZLPyLgr4c8v1XPcc4OOWqqjCGwDePDmLNs4A3vqbLtaoAe69eOXJ5hWgxT8H+tlrnAd60myPBeEyO0TrBNtmHdvmGWilt4K2WYN25S3jdZDE5eCfGl75zAb8V+Ft1a3CWCRB8hmxPfWFdwe3XQM3ma48ZxIs/P8DeN/dGsT6KX5Of165rO2OBU/2GufAFbyW0GpdBlwcTo7BFZQ6uOqVeAZe6a3grZ/X8DKY/OoZ6aNCLKniVfvDgazWQF4nTvKzI9qf42vo9H0ZFE9MQKze/XhHCP/K6euSGJ6+mXoORPHcu6lvh2H9mOUcviEiRLEfuJ8HULG+Nf1dQHdr2AOazamXG0eR5xKqH1efHw/N+lZUA289dZb0MpjzTywcQkUrh5QjLQr8BcoSeptoV9p1xl50T48UJHVZH8dJEs/RIKKKa8ed+ZyYowMXfaEJn0xbL4tPVtBBy6lunoOUqLwu49GnSUIfydAICenGnSyUq8CNF88BKF9duZhRusHe6uBC5VDHDZIuXn91FpOv4xV+U5FKh54budH88bDxwvUPaoKDUOtqWZ0pL/pk3aa/UpxyryhXap38fenl6a9vkp+ffvmP6f/qTOKxtxOB1Gr9GDjr9QXpl9TWVVP9PQlI5zUgvZkI6tv85xeB+EsRjBdb/AbFKX7wCtsyFdNbausLsS7LyhHlcuOqVT+QSO0zht9+K7Lr55HPT7bwS7Kf45XvLb6uvZg+TXDTAC83y4C/Z7v+Gi+TYB7k/ER2wZzfvUtSsS/XqK/0cUh9VfZfqH/Y3Hrj5vegr7fv2awhLwLh8cyf5n7J/CWIPS+Z8+Qe8V/RzamUteJWulm/gJzrlM58nfs7S3rKrpXxY7pxcyFw7r4Jrh6/dOWJPMlUmWXqizt3X1iobVmnnU/mbmDeTZZPd9/i+4GZWfYoMG+nkfM4iSe6ELBwKJnBteQ8Psj9eVtBm62paz4v103RCkzMfX/r+k/zaD1Gj/G8vXkamMV9R8wmj2l0P/gnmswfNmPp28wMldZIirKRlEbm7dNyfLttmwHL+uE/N0wwMLtFs6Qs9Om1unt9Z7bN0Mh6HfPlPkwXB33Vb7OH62/hLCjLEnfxsH6yi1i8+UM2zniUd9fTya3vPyFK2zawfkVkuulbubGxwijs2qbUBS7WQBC6oZtbgSBYgSJaobthoZl27SGVSyzQUqujCMweJqhPcfWZrW0se5Z17f6G5SMZ2KDtKLcGGq6mYOpMxb1gzY0MV8nKtOoKPA2fdajdKMOcVC4yiSVWpsiWbSbMZhjbxNgzxDFCHKZIYzEepylYmZAjnhx1PuJRmO6jD9aUz1LiqBsaQq8jpKjLMJZv6RjH9uWubaTd0M+tx3MxaRTTpmcPJawb8/Zlby4kWBvKZqqpu2jLtpbEUo4jxwBz2kOxWLshW4GWsUDJe3bfZ/kM8ZBmGOZkSq+j5bQ+FjJoiWL1wYO76el+jv4y4tiygaaijYR+GBu42po0sk1wxfEtr4hzoG27wIjpmkj4WzraZVgvjU9zow/TXQXrABY+8Qeeh4SJivi2TGcJHz830MYgvMCvoIBrzkcP2Fq8DeFPPDDU9WX0zxnVvcqpkHfDmQAN4DpUvYArdzv6/i02b/ttcyakPYo3NGl9W8xHvFPcWyg+QY5izQblGcadIXcFgc+n+xwP8CsWmhymLBBIu+grEB8CcEW8Ptqwck0zWgv0CDz1IepGGLt/Hs8O8BwoWyuc+Wifs9wKGdeARtio4C4j3Vvh0IfuVHALfg1gPEOsBtbaV0x9RPFluE8pZ4ABrn3oxkQe+FuKE30zWgt+gP1MfY1bU+8Tf9Ay8kDvS6RDi17nlAsjhWvtkdY7pHFIY4h1poInmluxOhrmdIUe8gXYiozj7UNrTCy4nAms0KqAssQKSQt96Jj0MiIfkICpwmwXGoWOwdPrsRJOrgz8ZCt/CLkubZYjZ0n3AmLJMJZq6W5i0Rj5CJgMEYNJmMiESY+0w/EeIUcpTkMs8sYAf8CNuAk06Arj5CbnGXy/ih/4Jx3JRR4b4hkdKtxrMi3t6S7laT7KMa+tCUUu+6RB4lXqwc8QP7h3eU739D7mIe0apPuUUUz6kDREeKo9e5T0qL3OuEYseFGXvIzy5/bVvCFNy/C3jPN2W3ohjYs1kkd0aT2hKUKLaW+gKD3ySfJpvQ//MEhjyG3kAvyD8XgE0qxI8XA/DYeUx7S2DJiCY7STXo0HOsB6uIZd1ZKXCTiBRkzu9z19BLwoj42kyEsT+qdYfLHUYIo4Ea+m0N6CeAXue1inRdjkM65dC+OAb8xtSow8PSM9ukmP+1c/ezVXqd4eCZQnaC+McoPvNSzTuIfhWuwPpF/eZkQ5grWT12jEPfj0aayUv87dpPBt4piwYrTPUR5R/iiv40T+oEHPtA/McuSpCK1k2DtoPInZ/4RYM+FAuSgSLvBZeBe45X7F84G4zfg+m1VtuYbLMu20n1/1IzzIO7G/kW/tvL+o2/Xf9ava0rqrGE777eYF5zx/4Iamfj0lbjAW4uvnZZ7Bi4wyV3gcx/WYAzGU9dcN2+7THqLynON7Vz8d5aSZ4UE8/dN4hAMcgAnXu0px4QwjHuC2679fB28LXWrJHhPe73DMPaaFXgn7Au+BdgbvPTZFjgAdySjXDB/RWbVu0jr5vQLfOq2vtFPUL+Kgmyst9/ZGcDrXM5wcLcxH5wHSqkjnLkv/Fv4Rd3WshDpW7BQr9V9j1flbWGEs7rczn2so9zOce6CxmVLstxo8XeBeWJwrNRWemvL9Jx8mHMP8aQo/ynCu86trNU9RT35J50BXIo/r2eRptNe78B/yf9pvyZ/orGls6ZxSxK3RtYxBK2OqYtCKmIr85vtOEdNTwwxadFLvtBf34fblCUroyniiyRV6hrrMnz8l+Uo9fuNJlurPoY0zz6GNP34Oxe3+v8qKTwHv/zVPNv4H</diagram></mxfile>
|
||||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 29 KiB |
1
img/architecture/app-state.drawio
Normal file
BIN
img/architecture/app-state.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 483 KiB After Width: | Height: | Size: 255 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 98 KiB |
49036
package-lock.json
generated
115
package.json
@@ -1,71 +1,96 @@
|
|||||||
{
|
{
|
||||||
"name": "privacy.sexy",
|
"name": "privacy.sexy",
|
||||||
"version": "0.8.0",
|
"version": "0.11.3",
|
||||||
"author": "undergroundwires",
|
|
||||||
"description": "Enforce privacy & security best-practices on Windows, because privacy is sexy 🍑🍆",
|
|
||||||
"homepage": "https://privacy.sexy",
|
|
||||||
"private": true,
|
"private": true,
|
||||||
"repository": {
|
"description": "Enforce privacy & security best-practices on Windows and macOS, because privacy is sexy 🍑🍆",
|
||||||
"type": "git",
|
"author": "undergroundwires",
|
||||||
"url": "https://github.com/undergroundwires/privacy.sexy.git"
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve",
|
"serve": "vue-cli-service serve",
|
||||||
"build": "vue-cli-service build",
|
"build": "vue-cli-service build",
|
||||||
"test:unit": "vue-cli-service test:unit",
|
"test:unit": "vue-cli-service test:unit",
|
||||||
"lint": "npm run lint:vue && npm run lint:yaml && npm run lint:md && npm run lint:md:relative-urls && npm run lint:md:consistency",
|
"test:e2e": "vue-cli-service test:e2e",
|
||||||
|
"lint": "npm run lint:md && npm run lint:md:consistency && npm run lint:md:relative-urls && npm run lint:eslint && npm run lint:yaml",
|
||||||
"electron:build": "vue-cli-service electron:build",
|
"electron:build": "vue-cli-service electron:build",
|
||||||
"electron:serve": "vue-cli-service electron:serve",
|
"electron:serve": "vue-cli-service electron:serve",
|
||||||
"lint:md": "markdownlint **/*.md --ignore node_modules",
|
"lint:md": "markdownlint **/*.md --ignore node_modules",
|
||||||
"lint:md:consistency": "remark . --frail --use remark-preset-lint-consistent",
|
"lint:md:consistency": "remark . --frail --use remark-preset-lint-consistent",
|
||||||
"lint:md:relative-urls": "remark . --frail --use remark-validate-links",
|
"lint:md:relative-urls": "remark . --frail --use remark-validate-links",
|
||||||
"lint:vue": "vue-cli-service lint --no-fix",
|
"lint:eslint": "vue-cli-service lint --no-fix --mode production",
|
||||||
"lint:yaml": "yamllint **/*.yaml --ignore=node_modules/**/*.yaml",
|
"lint:yaml": "yamllint **/*.yaml --ignore=node_modules/**/*.yaml",
|
||||||
"postinstall": "electron-builder install-app-deps",
|
"postinstall": "electron-builder install-app-deps",
|
||||||
"postuninstall": "electron-builder install-app-deps"
|
"postuninstall": "electron-builder install-app-deps",
|
||||||
|
"test:integration": "vue-cli-service test:unit \"tests/integration/**/*.spec.ts\""
|
||||||
},
|
},
|
||||||
"main": "background.js",
|
"main": "background.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.32",
|
"@fortawesome/fontawesome-svg-core": "^1.2.36",
|
||||||
"@fortawesome/free-brands-svg-icons": "^5.15.1",
|
"@fortawesome/free-brands-svg-icons": "^5.15.4",
|
||||||
"@fortawesome/free-regular-svg-icons": "^5.15.1",
|
"@fortawesome/free-regular-svg-icons": "^5.15.4",
|
||||||
"@fortawesome/free-solid-svg-icons": "^5.15.1",
|
"@fortawesome/free-solid-svg-icons": "^5.15.4",
|
||||||
"@fortawesome/vue-fontawesome": "^2.0.0",
|
"@fortawesome/vue-fontawesome": "^2.0.6",
|
||||||
"ace-builds": "^1.4.12",
|
"@juggle/resize-observer": "^3.3.1",
|
||||||
"file-saver": "^2.0.2",
|
"ace-builds": "^1.4.13",
|
||||||
"inversify": "^5.0.1",
|
"core-js": "^3.18.3",
|
||||||
|
"cross-fetch": "^3.1.4",
|
||||||
|
"electron-progressbar": "^2.0.1",
|
||||||
|
"file-saver": "^2.0.5",
|
||||||
|
"install": "^0.13.0",
|
||||||
"liquor-tree": "^0.2.70",
|
"liquor-tree": "^0.2.70",
|
||||||
"v-tooltip": "2.0.2",
|
"npm": "^8.1.1",
|
||||||
"vue": "^2.6.12",
|
"v-tooltip": "2.1.3",
|
||||||
|
"vue": "^2.6.14",
|
||||||
"vue-class-component": "^7.2.6",
|
"vue-class-component": "^7.2.6",
|
||||||
"vue-js-modal": "^2.0.0-rc.6",
|
"vue-js-modal": "^2.0.1",
|
||||||
"vue-property-decorator": "^9.0.2"
|
"vue-property-decorator": "^9.1.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/ace": "0.0.44",
|
"@types/ace": "0.0.47",
|
||||||
"@types/chai": "^4.2.14",
|
"@types/chai": "^4.2.22",
|
||||||
"@types/file-saver": "^2.0.1",
|
"@types/file-saver": "^2.0.3",
|
||||||
"@types/mocha": "^8.0.3",
|
"@types/mocha": "^9.0.0",
|
||||||
"@vue/cli-plugin-typescript": "^4.5.7",
|
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||||
"@vue/cli-plugin-unit-mocha": "^4.5.7",
|
"@typescript-eslint/parser": "^5.4.0",
|
||||||
"@vue/cli-service": "^4.5.7",
|
"@vue/cli-plugin-babel": "~5.0.0-rc.1",
|
||||||
"@vue/test-utils": "1.1.0",
|
"@vue/cli-plugin-e2e-cypress": "~5.0.0-rc.1",
|
||||||
"chai": "^4.2.0",
|
"@vue/cli-plugin-eslint": "~5.0.0-rc.1",
|
||||||
"electron": "^10.1.3",
|
"@vue/cli-plugin-typescript": "~5.0.0-rc.1",
|
||||||
"electron-devtools-installer": "^3.1.1",
|
"@vue/cli-plugin-unit-mocha": "~5.0.0-rc.1",
|
||||||
"electron-log": "^4.2.4",
|
"@vue/cli-service": "~5.0.0-rc.1",
|
||||||
"electron-updater": "^4.3.5",
|
"@vue/eslint-config-airbnb": "^6.0.0",
|
||||||
|
"@vue/eslint-config-typescript": "^9.1.0",
|
||||||
|
"@vue/test-utils": "1.2.2",
|
||||||
|
"chai": "^4.3.4",
|
||||||
|
"cypress": "^8.3.0",
|
||||||
|
"electron": "^15.3.0",
|
||||||
|
"electron-builder": "^22.14.13",
|
||||||
|
"electron-devtools-installer": "^3.2.0",
|
||||||
|
"electron-log": "^4.4.1",
|
||||||
|
"electron-updater": "^4.3.9",
|
||||||
|
"eslint": "^7.32.0",
|
||||||
|
"eslint-plugin-import": "^2.25.3",
|
||||||
|
"eslint-plugin-vue": "^8.0.3",
|
||||||
|
"eslint-plugin-vuejs-accessibility": "^1.1.0",
|
||||||
"js-yaml-loader": "^1.2.2",
|
"js-yaml-loader": "^1.2.2",
|
||||||
"markdownlint-cli": "^0.24.0",
|
"markdownlint-cli": "^0.29.0",
|
||||||
"remark-cli": "^9.0.0",
|
"remark-cli": "^10.0.0",
|
||||||
"remark-lint-no-dead-urls": "^1.1.0",
|
"remark-lint-no-dead-urls": "^1.1.0",
|
||||||
"remark-preset-lint-consistent": "^4.0.0",
|
"remark-preset-lint-consistent": "^5.1.0",
|
||||||
"remark-validate-links": "^10.0.2",
|
"remark-validate-links": "^11.0.1",
|
||||||
"sass": "^1.27.0",
|
"sass": "^1.43.3",
|
||||||
"sass-loader": "^10.0.3",
|
"sass-loader": "10.2.0",
|
||||||
"typescript": "^4.0.3",
|
"ts-loader": "9.0.1",
|
||||||
"vue-cli-plugin-electron-builder": "^2.0.0-rc.4",
|
"tslib": "^2.3.1",
|
||||||
"vue-template-compiler": "^2.6.12",
|
"typescript": "^4.4.4",
|
||||||
|
"vue-cli-plugin-electron-builder": "^2.1.1",
|
||||||
|
"vue-template-compiler": "^2.6.14",
|
||||||
"yaml-lint": "^1.2.4"
|
"yaml-lint": "^1.2.4"
|
||||||
|
},
|
||||||
|
"//devDependencies": {
|
||||||
|
"ts-loader": "Here as workaround for vue-cli-plugin-electron-builder using older webpack 4"
|
||||||
|
},
|
||||||
|
"homepage": "https://privacy.sexy",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/undergroundwires/privacy.sexy.git"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: {
|
plugins: {
|
||||||
autoprefixer: {}
|
autoprefixer: {},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
<title>Privacy is sexy 🍑🍆 - Enforce privacy & security on Windows</title>
|
<title>Privacy is sexy 🍑🍆 - Enforce privacy & security on Windows and macOS</title>
|
||||||
<meta name="robots" content="index,follow" />
|
<meta name="robots" content="index,follow" />
|
||||||
<meta name="description" content="Web tool to generate scripts for enforcing privacy & security best-practices such as stopping data collection of Windows and different softwares on it."/>
|
<meta name="description" content="Web tool to generate scripts for enforcing privacy & security best-practices such as stopping data collection of Windows and different softwares on it."/>
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
|||||||
79
src/App.vue
@@ -1,79 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div id="app">
|
|
||||||
<div class="wrapper">
|
|
||||||
<TheHeader class="row" />
|
|
||||||
<TheSearchBar class="row" />
|
|
||||||
<TheScripts class="row"/>
|
|
||||||
<TheCodeArea class="row" theme="xcode" />
|
|
||||||
<TheCodeButtons class="row code-buttons" />
|
|
||||||
<TheFooter />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { Component, Vue } from 'vue-property-decorator';
|
|
||||||
import TheHeader from '@/presentation/TheHeader.vue';
|
|
||||||
import TheFooter from '@/presentation/TheFooter/TheFooter.vue';
|
|
||||||
import TheCodeArea from '@/presentation/TheCodeArea.vue';
|
|
||||||
import TheCodeButtons from '@/presentation/TheCodeButtons.vue';
|
|
||||||
import TheSearchBar from '@/presentation/TheSearchBar.vue';
|
|
||||||
import TheScripts from '@/presentation/Scripts/TheScripts.vue';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
components: {
|
|
||||||
TheHeader,
|
|
||||||
TheCodeArea,
|
|
||||||
TheCodeButtons,
|
|
||||||
TheScripts,
|
|
||||||
TheSearchBar,
|
|
||||||
TheFooter,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
export default class App extends Vue {
|
|
||||||
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import "@/presentation/styles/colors.scss";
|
|
||||||
@import "@/presentation/styles/fonts.scss";
|
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background: $light-gray;
|
|
||||||
font-family: $main-font;
|
|
||||||
color: $slate;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#app {
|
|
||||||
margin-right: auto;
|
|
||||||
margin-left: auto;
|
|
||||||
max-width: 1500px;
|
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
margin: 0% 2% 0% 2%;
|
|
||||||
background-color: white;
|
|
||||||
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.06);
|
|
||||||
padding: 2%;
|
|
||||||
display:flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.row {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-buttons {
|
|
||||||
padding-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@import "@/presentation/styles/tooltip.scss";
|
|
||||||
@import "@/presentation/styles/tree.scss";
|
|
||||||
</style>
|
|
||||||
24
src/application/ApplicationFactory.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { IApplication } from '@/domain/IApplication';
|
||||||
|
import { AsyncLazy } from '@/infrastructure/Threading/AsyncLazy';
|
||||||
|
import { IApplicationFactory } from './IApplicationFactory';
|
||||||
|
import { parseApplication } from './Parser/ApplicationParser';
|
||||||
|
|
||||||
|
export type ApplicationGetterType = () => IApplication;
|
||||||
|
const ApplicationGetter: ApplicationGetterType = parseApplication;
|
||||||
|
|
||||||
|
export class ApplicationFactory implements IApplicationFactory {
|
||||||
|
public static readonly Current: IApplicationFactory = new ApplicationFactory(ApplicationGetter);
|
||||||
|
|
||||||
|
private readonly getter: AsyncLazy<IApplication>;
|
||||||
|
|
||||||
|
protected constructor(costlyGetter: ApplicationGetterType) {
|
||||||
|
if (!costlyGetter) {
|
||||||
|
throw new Error('missing getter');
|
||||||
|
}
|
||||||
|
this.getter = new AsyncLazy<IApplication>(() => Promise.resolve(costlyGetter()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public getApp(): Promise<IApplication> {
|
||||||
|
return this.getter.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/application/Common/Array.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// Compares to Array<T> objects for equality, ignoring order
|
||||||
|
export function scrambledEqual<T>(array1: readonly T[], array2: readonly T[]) {
|
||||||
|
if (!array1) { throw new Error('missing first array'); }
|
||||||
|
if (!array2) { throw new Error('missing second array'); }
|
||||||
|
const sortedArray1 = sort(array1);
|
||||||
|
const sortedArray2 = sort(array2);
|
||||||
|
return sequenceEqual(sortedArray1, sortedArray2);
|
||||||
|
function sort(array: readonly T[]) {
|
||||||
|
return array.slice().sort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compares to Array<T> objects for equality in same order
|
||||||
|
export function sequenceEqual<T>(array1: readonly T[], array2: readonly T[]) {
|
||||||
|
if (!array1) { throw new Error('missing first array'); }
|
||||||
|
if (!array2) { throw new Error('missing second array'); }
|
||||||
|
if (array1.length !== array2.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return array1.every((val, index) => val === array2[index]);
|
||||||
|
}
|
||||||
63
src/application/Common/Enum.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// Because we cannot do "T extends enum" 😞 https://github.com/microsoft/TypeScript/issues/30611
|
||||||
|
export type EnumType = number | string;
|
||||||
|
export type EnumVariable<T extends EnumType, TEnumValue extends EnumType>
|
||||||
|
= { [key in T]: TEnumValue };
|
||||||
|
|
||||||
|
export interface IEnumParser<TEnum> {
|
||||||
|
parseEnum(value: string, propertyName: string): TEnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createEnumParser<T extends EnumType, TEnumValue extends EnumType>(
|
||||||
|
enumVariable: EnumVariable<T, TEnumValue>,
|
||||||
|
): IEnumParser<TEnumValue> {
|
||||||
|
return {
|
||||||
|
parseEnum: (value, propertyName) => parseEnumValue(value, propertyName, enumVariable),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseEnumValue<T extends EnumType, TEnumValue extends EnumType>(
|
||||||
|
value: string,
|
||||||
|
enumName: string,
|
||||||
|
enumVariable: EnumVariable<T, TEnumValue>,
|
||||||
|
): TEnumValue {
|
||||||
|
if (!value) {
|
||||||
|
throw new Error(`missing ${enumName}`);
|
||||||
|
}
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
throw new Error(`unexpected type of ${enumName}: "${typeof value}"`);
|
||||||
|
}
|
||||||
|
const casedValue = getEnumNames(enumVariable)
|
||||||
|
.find((enumValue) => enumValue.toLowerCase() === value.toLowerCase());
|
||||||
|
if (!casedValue) {
|
||||||
|
throw new Error(`unknown ${enumName}: "${value}"`);
|
||||||
|
}
|
||||||
|
return enumVariable[casedValue as keyof typeof enumVariable];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getEnumNames
|
||||||
|
<T extends EnumType, TEnumValue extends EnumType>(
|
||||||
|
enumVariable: EnumVariable<T, TEnumValue>,
|
||||||
|
): string[] {
|
||||||
|
return Object
|
||||||
|
.values(enumVariable)
|
||||||
|
.filter((enumMember) => typeof enumMember === 'string') as string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getEnumValues<T extends EnumType, TEnumValue extends EnumType>(
|
||||||
|
enumVariable: EnumVariable<T, TEnumValue>,
|
||||||
|
): TEnumValue[] {
|
||||||
|
return getEnumNames(enumVariable)
|
||||||
|
.map((level) => enumVariable[level]) as TEnumValue[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertInRange<T extends EnumType, TEnumValue extends EnumType>(
|
||||||
|
value: TEnumValue,
|
||||||
|
enumVariable: EnumVariable<T, TEnumValue>,
|
||||||
|
) {
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
throw new Error('absent enum value');
|
||||||
|
}
|
||||||
|
if (!(value in enumVariable)) {
|
||||||
|
throw new RangeError(`enum value "${value}" is out of range`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||||
|
|
||||||
|
export interface IScriptingLanguageFactory<T> {
|
||||||
|
create(language: ScriptingLanguage): T;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||||
|
import { assertInRange } from '@/application/Common/Enum';
|
||||||
|
import { IScriptingLanguageFactory } from './IScriptingLanguageFactory';
|
||||||
|
|
||||||
|
type Getter<T> = () => T;
|
||||||
|
|
||||||
|
export abstract class ScriptingLanguageFactory<T> implements IScriptingLanguageFactory<T> {
|
||||||
|
private readonly getters = new Map<ScriptingLanguage, Getter<T>>();
|
||||||
|
|
||||||
|
public create(language: ScriptingLanguage): T {
|
||||||
|
assertInRange(language, ScriptingLanguage);
|
||||||
|
if (!this.getters.has(language)) {
|
||||||
|
throw new RangeError(`unknown language: "${ScriptingLanguage[language]}"`);
|
||||||
|
}
|
||||||
|
const getter = this.getters.get(language);
|
||||||
|
const instance = getter();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected registerGetter(language: ScriptingLanguage, getter: Getter<T>) {
|
||||||
|
assertInRange(language, ScriptingLanguage);
|
||||||
|
if (!getter) {
|
||||||
|
throw new Error('missing getter');
|
||||||
|
}
|
||||||
|
if (this.getters.has(language)) {
|
||||||
|
throw new Error(`${ScriptingLanguage[language]} is already registered`);
|
||||||
|
}
|
||||||
|
this.getters.set(language, getter);
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/application/Context/ApplicationContext.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { IApplication } from '@/domain/IApplication';
|
||||||
|
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||||
|
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||||
|
import { EventSource } from '@/infrastructure/Events/EventSource';
|
||||||
|
import { assertInRange } from '@/application/Common/Enum';
|
||||||
|
import { CategoryCollectionState } from './State/CategoryCollectionState';
|
||||||
|
import { ICategoryCollectionState } from './State/ICategoryCollectionState';
|
||||||
|
import { IApplicationContext, IApplicationContextChangedEvent } from './IApplicationContext';
|
||||||
|
|
||||||
|
type StateMachine = Map<OperatingSystem, ICategoryCollectionState>;
|
||||||
|
|
||||||
|
export class ApplicationContext implements IApplicationContext {
|
||||||
|
public readonly contextChanged = new EventSource<IApplicationContextChangedEvent>();
|
||||||
|
|
||||||
|
public collection: ICategoryCollection;
|
||||||
|
|
||||||
|
public currentOs: OperatingSystem;
|
||||||
|
|
||||||
|
public get state(): ICategoryCollectionState {
|
||||||
|
return this.states[this.collection.os];
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly states: StateMachine;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public readonly app: IApplication,
|
||||||
|
initialContext: OperatingSystem,
|
||||||
|
) {
|
||||||
|
validateApp(app);
|
||||||
|
this.states = initializeStates(app);
|
||||||
|
this.changeContext(initialContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public changeContext(os: OperatingSystem): void {
|
||||||
|
assertInRange(os, OperatingSystem);
|
||||||
|
if (this.currentOs === os) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.collection = this.app.getCollection(os);
|
||||||
|
if (!this.collection) {
|
||||||
|
throw new Error(`os "${OperatingSystem[os]}" is not defined in application`);
|
||||||
|
}
|
||||||
|
const event: IApplicationContextChangedEvent = {
|
||||||
|
newState: this.states[os],
|
||||||
|
oldState: this.states[this.currentOs],
|
||||||
|
};
|
||||||
|
this.contextChanged.notify(event);
|
||||||
|
this.currentOs = os;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateApp(app: IApplication) {
|
||||||
|
if (!app) {
|
||||||
|
throw new Error('missing app');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeStates(app: IApplication): StateMachine {
|
||||||
|
const machine = new Map<OperatingSystem, ICategoryCollectionState>();
|
||||||
|
for (const collection of app.collections) {
|
||||||
|
machine[collection.os] = new CategoryCollectionState(collection);
|
||||||
|
}
|
||||||
|
return machine;
|
||||||
|
}
|
||||||
32
src/application/Context/ApplicationContextFactory.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { IApplicationContext } from '@/application/Context/IApplicationContext';
|
||||||
|
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||||
|
import { IApplication } from '@/domain/IApplication';
|
||||||
|
import { Environment } from '../Environment/Environment';
|
||||||
|
import { IEnvironment } from '../Environment/IEnvironment';
|
||||||
|
import { IApplicationFactory } from '../IApplicationFactory';
|
||||||
|
import { ApplicationFactory } from '../ApplicationFactory';
|
||||||
|
import { ApplicationContext } from './ApplicationContext';
|
||||||
|
|
||||||
|
export async function buildContext(
|
||||||
|
factory: IApplicationFactory = ApplicationFactory.Current,
|
||||||
|
environment = Environment.CurrentEnvironment,
|
||||||
|
): Promise<IApplicationContext> {
|
||||||
|
if (!factory) { throw new Error('missing factory'); }
|
||||||
|
if (!environment) { throw new Error('missing environment'); }
|
||||||
|
const app = await factory.getApp();
|
||||||
|
const os = getInitialOs(app, environment);
|
||||||
|
return new ApplicationContext(app, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInitialOs(app: IApplication, environment: IEnvironment): OperatingSystem {
|
||||||
|
const currentOs = environment.os;
|
||||||
|
const supportedOsList = app.getSupportedOsList();
|
||||||
|
if (supportedOsList.includes(currentOs)) {
|
||||||
|
return currentOs;
|
||||||
|
}
|
||||||
|
supportedOsList.sort((os1, os2) => {
|
||||||
|
const getPriority = (os: OperatingSystem) => app.getCollection(os).totalScripts;
|
||||||
|
return getPriority(os2) - getPriority(os1);
|
||||||
|
});
|
||||||
|
return supportedOsList[0];
|
||||||
|
}
|
||||||
20
src/application/Context/IApplicationContext.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||||
|
import { IEventSource } from '@/infrastructure/Events/IEventSource';
|
||||||
|
import { IApplication } from '@/domain/IApplication';
|
||||||
|
import { ICategoryCollectionState, IReadOnlyCategoryCollectionState } from './State/ICategoryCollectionState';
|
||||||
|
|
||||||
|
export interface IReadOnlyApplicationContext {
|
||||||
|
readonly app: IApplication;
|
||||||
|
readonly state: IReadOnlyCategoryCollectionState;
|
||||||
|
readonly contextChanged: IEventSource<IApplicationContextChangedEvent>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IApplicationContext extends IReadOnlyApplicationContext {
|
||||||
|
readonly state: ICategoryCollectionState;
|
||||||
|
changeContext(os: OperatingSystem): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IApplicationContextChangedEvent {
|
||||||
|
readonly newState: ICategoryCollectionState;
|
||||||
|
readonly oldState: ICategoryCollectionState;
|
||||||
|
}
|
||||||
26
src/application/Context/State/CategoryCollectionState.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||||
|
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||||
|
import { UserFilter } from './Filter/UserFilter';
|
||||||
|
import { IUserFilter } from './Filter/IUserFilter';
|
||||||
|
import { ApplicationCode } from './Code/ApplicationCode';
|
||||||
|
import { UserSelection } from './Selection/UserSelection';
|
||||||
|
import { IUserSelection } from './Selection/IUserSelection';
|
||||||
|
import { ICategoryCollectionState } from './ICategoryCollectionState';
|
||||||
|
import { IApplicationCode } from './Code/IApplicationCode';
|
||||||
|
|
||||||
|
export class CategoryCollectionState implements ICategoryCollectionState {
|
||||||
|
public readonly os: OperatingSystem;
|
||||||
|
|
||||||
|
public readonly code: IApplicationCode;
|
||||||
|
|
||||||
|
public readonly selection: IUserSelection;
|
||||||
|
|
||||||
|
public readonly filter: IUserFilter;
|
||||||
|
|
||||||
|
public constructor(readonly collection: ICategoryCollection) {
|
||||||
|
this.selection = new UserSelection(collection, []);
|
||||||
|
this.code = new ApplicationCode(this.selection, collection.scripting);
|
||||||
|
this.filter = new UserFilter(collection);
|
||||||
|
this.os = collection.os;
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/application/Context/State/Code/ApplicationCode.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||||
|
import { IReadOnlyUserSelection } from '@/application/Context/State/Selection/IUserSelection';
|
||||||
|
import { EventSource } from '@/infrastructure/Events/EventSource';
|
||||||
|
import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||||
|
import { CodeChangedEvent } from './Event/CodeChangedEvent';
|
||||||
|
import { CodePosition } from './Position/CodePosition';
|
||||||
|
import { ICodeChangedEvent } from './Event/ICodeChangedEvent';
|
||||||
|
import { UserScriptGenerator } from './Generation/UserScriptGenerator';
|
||||||
|
import { IApplicationCode } from './IApplicationCode';
|
||||||
|
import { IUserScriptGenerator } from './Generation/IUserScriptGenerator';
|
||||||
|
|
||||||
|
export class ApplicationCode implements IApplicationCode {
|
||||||
|
public readonly changed = new EventSource<ICodeChangedEvent>();
|
||||||
|
|
||||||
|
public current: string;
|
||||||
|
|
||||||
|
private scriptPositions = new Map<SelectedScript, CodePosition>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
userSelection: IReadOnlyUserSelection,
|
||||||
|
private readonly scriptingDefinition: IScriptingDefinition,
|
||||||
|
private readonly generator: IUserScriptGenerator = new UserScriptGenerator(),
|
||||||
|
) {
|
||||||
|
if (!userSelection) { throw new Error('missing userSelection'); }
|
||||||
|
if (!scriptingDefinition) { throw new Error('missing scriptingDefinition'); }
|
||||||
|
if (!generator) { throw new Error('missing generator'); }
|
||||||
|
this.setCode(userSelection.selectedScripts);
|
||||||
|
userSelection.changed.on((scripts) => {
|
||||||
|
this.setCode(scripts);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private setCode(scripts: ReadonlyArray<SelectedScript>): void {
|
||||||
|
const oldScripts = Array.from(this.scriptPositions.keys());
|
||||||
|
const code = this.generator.buildCode(scripts, this.scriptingDefinition);
|
||||||
|
this.current = code.code;
|
||||||
|
this.scriptPositions = code.scriptPositions;
|
||||||
|
const event = new CodeChangedEvent(code.code, oldScripts, code.scriptPositions);
|
||||||
|
this.changed.notify(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
71
src/application/Context/State/Code/Event/CodeChangedEvent.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { IScript } from '@/domain/IScript';
|
||||||
|
import { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition';
|
||||||
|
import { SelectedScript } from '../../Selection/SelectedScript';
|
||||||
|
import { ICodeChangedEvent } from './ICodeChangedEvent';
|
||||||
|
|
||||||
|
export class CodeChangedEvent implements ICodeChangedEvent {
|
||||||
|
public readonly code: string;
|
||||||
|
|
||||||
|
public readonly addedScripts: ReadonlyArray<IScript>;
|
||||||
|
|
||||||
|
public readonly removedScripts: ReadonlyArray<IScript>;
|
||||||
|
|
||||||
|
public readonly changedScripts: ReadonlyArray<IScript>;
|
||||||
|
|
||||||
|
private readonly scripts: Map<IScript, ICodePosition>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
code: string,
|
||||||
|
oldScripts: ReadonlyArray<SelectedScript>,
|
||||||
|
scripts: Map<SelectedScript, ICodePosition>,
|
||||||
|
) {
|
||||||
|
ensureAllPositionsExist(code, Array.from(scripts.values()));
|
||||||
|
this.code = code;
|
||||||
|
const newScripts = Array.from(scripts.keys());
|
||||||
|
this.addedScripts = selectIfNotExists(newScripts, oldScripts);
|
||||||
|
this.removedScripts = selectIfNotExists(oldScripts, newScripts);
|
||||||
|
this.changedScripts = getChangedScripts(oldScripts, newScripts);
|
||||||
|
this.scripts = new Map<IScript, ICodePosition>();
|
||||||
|
scripts.forEach((position, selection) => {
|
||||||
|
this.scripts.set(selection.script, position);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public isEmpty(): boolean {
|
||||||
|
return this.scripts.size === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getScriptPositionInCode(script: IScript): ICodePosition {
|
||||||
|
return this.scripts.get(script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureAllPositionsExist(script: string, positions: ReadonlyArray<ICodePosition>) {
|
||||||
|
const totalLines = script.split(/\r\n|\r|\n/).length;
|
||||||
|
const missingPositions = positions.filter((position) => position.endLine > totalLines);
|
||||||
|
if (missingPositions.length > 0) {
|
||||||
|
throw new Error(
|
||||||
|
`Out of range script end line: "${missingPositions.map((pos) => pos.endLine).join('", "')}"`
|
||||||
|
+ `(total code lines: ${totalLines}).`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getChangedScripts(
|
||||||
|
oldScripts: ReadonlyArray<SelectedScript>,
|
||||||
|
newScripts: ReadonlyArray<SelectedScript>,
|
||||||
|
): ReadonlyArray<IScript> {
|
||||||
|
return newScripts
|
||||||
|
.filter((newScript) => oldScripts.find((oldScript) => oldScript.id === newScript.id
|
||||||
|
&& oldScript.revert !== newScript.revert))
|
||||||
|
.map((selection) => selection.script);
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectIfNotExists(
|
||||||
|
selectableContainer: ReadonlyArray<SelectedScript>,
|
||||||
|
test: ReadonlyArray<SelectedScript>,
|
||||||
|
) {
|
||||||
|
return selectableContainer
|
||||||
|
.filter((script) => !test.find((oldScript) => oldScript.id === script.id))
|
||||||
|
.map((selection) => selection.script);
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { IScript } from '@/domain/IScript';
|
||||||
|
import { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition';
|
||||||
|
|
||||||
|
export interface ICodeChangedEvent {
|
||||||
|
readonly code: string;
|
||||||
|
addedScripts: ReadonlyArray<IScript>;
|
||||||
|
removedScripts: ReadonlyArray<IScript>;
|
||||||
|
changedScripts: ReadonlyArray<IScript>;
|
||||||
|
isEmpty(): boolean;
|
||||||
|
getScriptPositionInCode(script: IScript): ICodePosition;
|
||||||
|
}
|
||||||
68
src/application/Context/State/Code/Generation/CodeBuilder.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { ICodeBuilder } from './ICodeBuilder';
|
||||||
|
|
||||||
|
const NewLine = '\n';
|
||||||
|
const TotalFunctionSeparatorChars = 58;
|
||||||
|
|
||||||
|
export abstract class CodeBuilder implements ICodeBuilder {
|
||||||
|
private readonly lines = new Array<string>();
|
||||||
|
|
||||||
|
// Returns current line starting from 0 (no lines), or 1 (have single line)
|
||||||
|
public get currentLine(): number {
|
||||||
|
return this.lines.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public appendLine(code?: string): CodeBuilder {
|
||||||
|
if (!code) {
|
||||||
|
this.lines.push('');
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
const lines = code.match(/[^\r\n]+/g);
|
||||||
|
this.lines.push(...lines);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public appendTrailingHyphensCommentLine(
|
||||||
|
totalRepeatHyphens: number = TotalFunctionSeparatorChars,
|
||||||
|
): CodeBuilder {
|
||||||
|
return this.appendCommentLine('-'.repeat(totalRepeatHyphens));
|
||||||
|
}
|
||||||
|
|
||||||
|
public appendCommentLine(commentLine?: string): CodeBuilder {
|
||||||
|
this.lines.push(`${this.getCommentDelimiter()} ${commentLine}`);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public appendFunction(name: string, code: string): CodeBuilder {
|
||||||
|
if (!name) { throw new Error('name cannot be empty or null'); }
|
||||||
|
if (!code) { throw new Error('code cannot be empty or null'); }
|
||||||
|
return this
|
||||||
|
.appendCommentLineWithHyphensAround(name)
|
||||||
|
.appendLine(this.writeStandardOut(`--- ${name}`))
|
||||||
|
.appendLine(code)
|
||||||
|
.appendTrailingHyphensCommentLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public appendCommentLineWithHyphensAround(
|
||||||
|
sectionName: string,
|
||||||
|
totalRepeatHyphens: number = TotalFunctionSeparatorChars,
|
||||||
|
): CodeBuilder {
|
||||||
|
if (!sectionName) { throw new Error('sectionName cannot be empty or null'); }
|
||||||
|
if (sectionName.length >= totalRepeatHyphens) {
|
||||||
|
return this.appendCommentLine(sectionName);
|
||||||
|
}
|
||||||
|
const firstHyphens = '-'.repeat(Math.floor((totalRepeatHyphens - sectionName.length) / 2));
|
||||||
|
const secondHyphens = '-'.repeat(Math.ceil((totalRepeatHyphens - sectionName.length) / 2));
|
||||||
|
return this
|
||||||
|
.appendTrailingHyphensCommentLine()
|
||||||
|
.appendCommentLine(firstHyphens + sectionName + secondHyphens)
|
||||||
|
.appendTrailingHyphensCommentLine(TotalFunctionSeparatorChars);
|
||||||
|
}
|
||||||
|
|
||||||
|
public toString(): string {
|
||||||
|
return this.lines.join(NewLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract getCommentDelimiter(): string;
|
||||||
|
|
||||||
|
protected abstract writeStandardOut(text: string): string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { ScriptingLanguageFactory } from '@/application/Common/ScriptingLanguage/ScriptingLanguageFactory';
|
||||||
|
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||||
|
import { ICodeBuilder } from './ICodeBuilder';
|
||||||
|
import { BatchBuilder } from './Languages/BatchBuilder';
|
||||||
|
import { ShellBuilder } from './Languages/ShellBuilder';
|
||||||
|
import { ICodeBuilderFactory } from './ICodeBuilderFactory';
|
||||||
|
|
||||||
|
export class CodeBuilderFactory
|
||||||
|
extends ScriptingLanguageFactory<ICodeBuilder>
|
||||||
|
implements ICodeBuilderFactory {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.registerGetter(ScriptingLanguage.shellscript, () => new ShellBuilder());
|
||||||
|
this.registerGetter(ScriptingLanguage.batchfile, () => new BatchBuilder());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export interface ICodeBuilder {
|
||||||
|
currentLine: number;
|
||||||
|
appendLine(code?: string): ICodeBuilder;
|
||||||
|
appendTrailingHyphensCommentLine(totalRepeatHyphens: number): ICodeBuilder;
|
||||||
|
appendCommentLine(commentLine?: string): ICodeBuilder;
|
||||||
|
appendCommentLineWithHyphensAround(sectionName: string, totalRepeatHyphens: number): ICodeBuilder;
|
||||||
|
appendFunction(name: string, code: string): ICodeBuilder;
|
||||||
|
toString(): string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { IScriptingLanguageFactory } from '@/application/Common/ScriptingLanguage/IScriptingLanguageFactory';
|
||||||
|
import { ICodeBuilder } from './ICodeBuilder';
|
||||||
|
|
||||||
|
export type ICodeBuilderFactory = IScriptingLanguageFactory<ICodeBuilder>;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||||
|
import { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition';
|
||||||
|
|
||||||
|
export interface IUserScript {
|
||||||
|
code: string;
|
||||||
|
scriptPositions: Map<SelectedScript, ICodePosition>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||||
|
import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||||
|
import { IUserScript } from './IUserScript';
|
||||||
|
|
||||||
|
export interface IUserScriptGenerator {
|
||||||
|
buildCode(
|
||||||
|
selectedScripts: ReadonlyArray<SelectedScript>,
|
||||||
|
scriptingDefinition: IScriptingDefinition): IUserScript;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { CodeBuilder } from '@/application/Context/State/Code/Generation/CodeBuilder';
|
||||||
|
|
||||||
|
export class BatchBuilder extends CodeBuilder {
|
||||||
|
protected getCommentDelimiter(): string {
|
||||||
|
return '::';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected writeStandardOut(text: string): string {
|
||||||
|
return `echo ${escapeForEcho(text)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeForEcho(text: string) {
|
||||||
|
return text
|
||||||
|
.replace(/&/g, '^&')
|
||||||
|
.replace(/%/g, '%%');
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { CodeBuilder } from '@/application/Context/State/Code/Generation/CodeBuilder';
|
||||||
|
|
||||||
|
export class ShellBuilder extends CodeBuilder {
|
||||||
|
protected getCommentDelimiter(): string {
|
||||||
|
return '#';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected writeStandardOut(text: string): string {
|
||||||
|
return `echo '${escapeForEcho(text)}'`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeForEcho(text: string) {
|
||||||
|
return text
|
||||||
|
.replace(/'/g, '\'\\\'\'');
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||||
|
import { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition';
|
||||||
|
import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||||
|
import { CodePosition } from '../Position/CodePosition';
|
||||||
|
import { IUserScriptGenerator } from './IUserScriptGenerator';
|
||||||
|
import { IUserScript } from './IUserScript';
|
||||||
|
import { ICodeBuilder } from './ICodeBuilder';
|
||||||
|
import { ICodeBuilderFactory } from './ICodeBuilderFactory';
|
||||||
|
import { CodeBuilderFactory } from './CodeBuilderFactory';
|
||||||
|
|
||||||
|
export class UserScriptGenerator implements IUserScriptGenerator {
|
||||||
|
constructor(private readonly codeBuilderFactory: ICodeBuilderFactory = new CodeBuilderFactory()) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public buildCode(
|
||||||
|
selectedScripts: ReadonlyArray<SelectedScript>,
|
||||||
|
scriptingDefinition: IScriptingDefinition,
|
||||||
|
): IUserScript {
|
||||||
|
if (!selectedScripts) { throw new Error('missing scripts'); }
|
||||||
|
if (!scriptingDefinition) { throw new Error('missing definition'); }
|
||||||
|
if (!selectedScripts.length) {
|
||||||
|
return { code: '', scriptPositions: new Map<SelectedScript, ICodePosition>() };
|
||||||
|
}
|
||||||
|
let builder = this.codeBuilderFactory.create(scriptingDefinition.language);
|
||||||
|
builder = initializeCode(scriptingDefinition.startCode, builder);
|
||||||
|
const scriptPositions = selectedScripts.reduce((result, selection) => {
|
||||||
|
return appendSelection(selection, result, builder);
|
||||||
|
}, new Map<SelectedScript, ICodePosition>());
|
||||||
|
const code = finalizeCode(builder, scriptingDefinition.endCode);
|
||||||
|
return { code, scriptPositions };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeCode(startCode: string, builder: ICodeBuilder): ICodeBuilder {
|
||||||
|
if (!startCode) {
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
return builder
|
||||||
|
.appendLine(startCode)
|
||||||
|
.appendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
function finalizeCode(builder: ICodeBuilder, endCode: string): string {
|
||||||
|
if (!endCode) {
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
return builder.appendLine()
|
||||||
|
.appendLine(endCode)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendSelection(
|
||||||
|
selection: SelectedScript,
|
||||||
|
scriptPositions: Map<SelectedScript, ICodePosition>,
|
||||||
|
builder: ICodeBuilder,
|
||||||
|
): Map<SelectedScript, ICodePosition> {
|
||||||
|
// Start from next line because first line will be empty to separate scripts
|
||||||
|
const startPosition = builder.currentLine + 1;
|
||||||
|
appendCode(selection, builder);
|
||||||
|
const endPosition = builder.currentLine - 1;
|
||||||
|
builder.appendLine();
|
||||||
|
const position = new CodePosition(startPosition, endPosition);
|
||||||
|
scriptPositions.set(selection, position);
|
||||||
|
return scriptPositions;
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendCode(selection: SelectedScript, builder: ICodeBuilder): ICodeBuilder {
|
||||||
|
const { script } = selection;
|
||||||
|
const name = selection.revert ? `${script.name} (revert)` : script.name;
|
||||||
|
const scriptCode = selection.revert ? script.code.revert : script.code.execute;
|
||||||
|
return builder
|
||||||
|
.appendLine()
|
||||||
|
.appendFunction(name, scriptCode);
|
||||||
|
}
|
||||||
7
src/application/Context/State/Code/IApplicationCode.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { IEventSource } from '@/infrastructure/Events/IEventSource';
|
||||||
|
import { ICodeChangedEvent } from './Event/ICodeChangedEvent';
|
||||||
|
|
||||||
|
export interface IApplicationCode {
|
||||||
|
readonly changed: IEventSource<ICodeChangedEvent>;
|
||||||
|
readonly current: string;
|
||||||
|
}
|
||||||
25
src/application/Context/State/Code/Position/CodePosition.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { ICodePosition } from './ICodePosition';
|
||||||
|
|
||||||
|
export class CodePosition implements ICodePosition {
|
||||||
|
public get totalLines(): number {
|
||||||
|
return this.endLine - this.startLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly startLine: number,
|
||||||
|
public readonly endLine: number,
|
||||||
|
) {
|
||||||
|
if (startLine < 0) {
|
||||||
|
throw new Error('Code cannot start in a negative line');
|
||||||
|
}
|
||||||
|
if (endLine < 0) {
|
||||||
|
throw new Error('Code cannot end in a negative line');
|
||||||
|
}
|
||||||
|
if (endLine === startLine) {
|
||||||
|
throw new Error('Empty code');
|
||||||
|
}
|
||||||
|
if (endLine < startLine) {
|
||||||
|
throw new Error('End line cannot be less than start line');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export interface ICodePosition {
|
||||||
|
readonly startLine: number;
|
||||||
|
readonly endLine: number;
|
||||||
|
readonly totalLines: number;
|
||||||
|
}
|
||||||
20
src/application/Context/State/Filter/FilterResult.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { IScript } from '@/domain/IScript';
|
||||||
|
import { ICategory } from '@/domain/ICategory';
|
||||||
|
import { IFilterResult } from './IFilterResult';
|
||||||
|
|
||||||
|
export class FilterResult implements IFilterResult {
|
||||||
|
constructor(
|
||||||
|
public readonly scriptMatches: ReadonlyArray<IScript>,
|
||||||
|
public readonly categoryMatches: ReadonlyArray<ICategory>,
|
||||||
|
public readonly query: string,
|
||||||
|
) {
|
||||||
|
if (!query) { throw new Error('Query is empty or undefined'); }
|
||||||
|
if (!scriptMatches) { throw new Error('Script matches is undefined'); }
|
||||||
|
if (!categoryMatches) { throw new Error('Category matches is undefined'); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public hasAnyMatches(): boolean {
|
||||||
|
return this.scriptMatches.length > 0
|
||||||
|
|| this.categoryMatches.length > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/application/Context/State/Filter/IFilterResult.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { IScript, ICategory } from '@/domain/ICategory';
|
||||||
|
|
||||||
|
export interface IFilterResult {
|
||||||
|
readonly categoryMatches: ReadonlyArray<ICategory>;
|
||||||
|
readonly scriptMatches: ReadonlyArray<IScript>;
|
||||||
|
readonly query: string;
|
||||||
|
hasAnyMatches(): boolean;
|
||||||
|
}
|
||||||
13
src/application/Context/State/Filter/IUserFilter.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { IEventSource } from '@/infrastructure/Events/IEventSource';
|
||||||
|
import { IFilterResult } from './IFilterResult';
|
||||||
|
|
||||||
|
export interface IReadOnlyUserFilter {
|
||||||
|
readonly currentFilter: IFilterResult | undefined;
|
||||||
|
readonly filtered: IEventSource<IFilterResult>;
|
||||||
|
readonly filterRemoved: IEventSource<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IUserFilter extends IReadOnlyUserFilter {
|
||||||
|
setFilter(filter: string): void;
|
||||||
|
removeFilter(): void;
|
||||||
|
}
|
||||||
56
src/application/Context/State/Filter/UserFilter.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { IScript } from '@/domain/IScript';
|
||||||
|
import { EventSource } from '@/infrastructure/Events/EventSource';
|
||||||
|
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||||
|
import { FilterResult } from './FilterResult';
|
||||||
|
import { IFilterResult } from './IFilterResult';
|
||||||
|
import { IUserFilter } from './IUserFilter';
|
||||||
|
|
||||||
|
export class UserFilter implements IUserFilter {
|
||||||
|
public readonly filtered = new EventSource<IFilterResult>();
|
||||||
|
|
||||||
|
public readonly filterRemoved = new EventSource<void>();
|
||||||
|
|
||||||
|
public currentFilter: IFilterResult | undefined;
|
||||||
|
|
||||||
|
constructor(private collection: ICategoryCollection) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public setFilter(filter: string): void {
|
||||||
|
if (!filter) {
|
||||||
|
throw new Error('Filter must be defined and not empty. Use removeFilter() to remove the filter');
|
||||||
|
}
|
||||||
|
const filterLowercase = filter.toLocaleLowerCase();
|
||||||
|
const filteredScripts = this.collection.getAllScripts().filter(
|
||||||
|
(script) => isScriptAMatch(script, filterLowercase),
|
||||||
|
);
|
||||||
|
const filteredCategories = this.collection.getAllCategories().filter(
|
||||||
|
(category) => category.name.toLowerCase().includes(filterLowercase),
|
||||||
|
);
|
||||||
|
const matches = new FilterResult(
|
||||||
|
filteredScripts,
|
||||||
|
filteredCategories,
|
||||||
|
filter,
|
||||||
|
);
|
||||||
|
this.currentFilter = matches;
|
||||||
|
this.filtered.notify(matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeFilter(): void {
|
||||||
|
this.currentFilter = undefined;
|
||||||
|
this.filterRemoved.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isScriptAMatch(script: IScript, filterLowercase: string) {
|
||||||
|
if (script.name.toLowerCase().includes(filterLowercase)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (script.code.execute.toLowerCase().includes(filterLowercase)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (script.code.revert) {
|
||||||
|
return script.code.revert.toLowerCase().includes(filterLowercase);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
18
src/application/Context/State/ICategoryCollectionState.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||||
|
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||||
|
import { IReadOnlyUserFilter, IUserFilter } from './Filter/IUserFilter';
|
||||||
|
import { IReadOnlyUserSelection, IUserSelection } from './Selection/IUserSelection';
|
||||||
|
import { IApplicationCode } from './Code/IApplicationCode';
|
||||||
|
|
||||||
|
export interface IReadOnlyCategoryCollectionState {
|
||||||
|
readonly code: IApplicationCode;
|
||||||
|
readonly os: OperatingSystem;
|
||||||
|
readonly filter: IReadOnlyUserFilter;
|
||||||
|
readonly selection: IReadOnlyUserSelection;
|
||||||
|
readonly collection: ICategoryCollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICategoryCollectionState extends IReadOnlyCategoryCollectionState {
|
||||||
|
readonly filter: IUserFilter;
|
||||||
|
readonly selection: IUserSelection;
|
||||||
|
}
|
||||||
23
src/application/Context/State/Selection/IUserSelection.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { IScript } from '@/domain/IScript';
|
||||||
|
import { ICategory } from '@/domain/ICategory';
|
||||||
|
import { IEventSource } from '@/infrastructure/Events/IEventSource';
|
||||||
|
import { SelectedScript } from './SelectedScript';
|
||||||
|
|
||||||
|
export interface IReadOnlyUserSelection {
|
||||||
|
readonly changed: IEventSource<ReadonlyArray<SelectedScript>>;
|
||||||
|
readonly selectedScripts: ReadonlyArray<SelectedScript>;
|
||||||
|
isSelected(scriptId: string): boolean;
|
||||||
|
areAllSelected(category: ICategory): boolean;
|
||||||
|
isAnySelected(category: ICategory): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IUserSelection extends IReadOnlyUserSelection {
|
||||||
|
removeAllInCategory(categoryId: number): void;
|
||||||
|
addOrUpdateAllInCategory(categoryId: number, revert: boolean): void;
|
||||||
|
addSelectedScript(scriptId: string, revert: boolean): void;
|
||||||
|
addOrUpdateSelectedScript(scriptId: string, revert: boolean): void;
|
||||||
|
removeSelectedScript(scriptId: string): void;
|
||||||
|
selectOnly(scripts: ReadonlyArray<IScript>): void;
|
||||||
|
selectAll(): void;
|
||||||
|
deselectAll(): void;
|
||||||
|
}
|
||||||
14
src/application/Context/State/Selection/SelectedScript.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { BaseEntity } from '@/infrastructure/Entity/BaseEntity';
|
||||||
|
import { IScript } from '@/domain/IScript';
|
||||||
|
|
||||||
|
export class SelectedScript extends BaseEntity<string> {
|
||||||
|
constructor(
|
||||||
|
public readonly script: IScript,
|
||||||
|
public readonly revert: boolean,
|
||||||
|
) {
|
||||||
|
super(script.id);
|
||||||
|
if (revert && !script.canRevert()) {
|
||||||
|
throw new Error('cannot revert an irreversible script');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
167
src/application/Context/State/Selection/UserSelection.ts
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
import { InMemoryRepository } from '@/infrastructure/Repository/InMemoryRepository';
|
||||||
|
import { IScript } from '@/domain/IScript';
|
||||||
|
import { EventSource } from '@/infrastructure/Events/EventSource';
|
||||||
|
import { IRepository } from '@/infrastructure/Repository/IRepository';
|
||||||
|
import { ICategory } from '@/domain/ICategory';
|
||||||
|
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||||
|
import { IUserSelection } from './IUserSelection';
|
||||||
|
import { SelectedScript } from './SelectedScript';
|
||||||
|
|
||||||
|
export class UserSelection implements IUserSelection {
|
||||||
|
public readonly changed = new EventSource<ReadonlyArray<SelectedScript>>();
|
||||||
|
|
||||||
|
private readonly scripts: IRepository<string, SelectedScript>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly collection: ICategoryCollection,
|
||||||
|
selectedScripts: ReadonlyArray<SelectedScript>,
|
||||||
|
) {
|
||||||
|
this.scripts = new InMemoryRepository<string, SelectedScript>();
|
||||||
|
for (const script of selectedScripts) {
|
||||||
|
this.scripts.addItem(script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public areAllSelected(category: ICategory): boolean {
|
||||||
|
if (this.selectedScripts.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const scripts = category.getAllScriptsRecursively();
|
||||||
|
if (this.selectedScripts.length < scripts.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return scripts.every(
|
||||||
|
(script) => this.selectedScripts.some((selected) => selected.id === script.id),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isAnySelected(category: ICategory): boolean {
|
||||||
|
if (this.selectedScripts.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.selectedScripts.some((s) => category.includes(s.script));
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeAllInCategory(categoryId: number): void {
|
||||||
|
const category = this.collection.findCategory(categoryId);
|
||||||
|
const scriptsToRemove = category.getAllScriptsRecursively()
|
||||||
|
.filter((script) => this.scripts.exists(script.id));
|
||||||
|
if (!scriptsToRemove.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const script of scriptsToRemove) {
|
||||||
|
this.scripts.removeItem(script.id);
|
||||||
|
}
|
||||||
|
this.changed.notify(this.scripts.getItems());
|
||||||
|
}
|
||||||
|
|
||||||
|
public addOrUpdateAllInCategory(categoryId: number, revert = false): void {
|
||||||
|
const scriptsToAddOrUpdate = this.collection
|
||||||
|
.findCategory(categoryId)
|
||||||
|
.getAllScriptsRecursively()
|
||||||
|
.filter(
|
||||||
|
(script) => !this.scripts.exists(script.id)
|
||||||
|
|| this.scripts.getById(script.id).revert !== revert,
|
||||||
|
)
|
||||||
|
.map((script) => new SelectedScript(script, revert));
|
||||||
|
if (!scriptsToAddOrUpdate.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const script of scriptsToAddOrUpdate) {
|
||||||
|
this.scripts.addOrUpdateItem(script);
|
||||||
|
}
|
||||||
|
this.changed.notify(this.scripts.getItems());
|
||||||
|
}
|
||||||
|
|
||||||
|
public addSelectedScript(scriptId: string, revert: boolean): void {
|
||||||
|
const script = this.collection.findScript(scriptId);
|
||||||
|
if (!script) {
|
||||||
|
throw new Error(`Cannot add (id: ${scriptId}) as it is unknown`);
|
||||||
|
}
|
||||||
|
const selectedScript = new SelectedScript(script, revert);
|
||||||
|
this.scripts.addItem(selectedScript);
|
||||||
|
this.changed.notify(this.scripts.getItems());
|
||||||
|
}
|
||||||
|
|
||||||
|
public addOrUpdateSelectedScript(scriptId: string, revert: boolean): void {
|
||||||
|
const script = this.collection.findScript(scriptId);
|
||||||
|
const selectedScript = new SelectedScript(script, revert);
|
||||||
|
this.scripts.addOrUpdateItem(selectedScript);
|
||||||
|
this.changed.notify(this.scripts.getItems());
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeSelectedScript(scriptId: string): void {
|
||||||
|
this.scripts.removeItem(scriptId);
|
||||||
|
this.changed.notify(this.scripts.getItems());
|
||||||
|
}
|
||||||
|
|
||||||
|
public isSelected(scriptId: string): boolean {
|
||||||
|
return this.scripts.exists(scriptId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get users scripts based on his/her selections */
|
||||||
|
public get selectedScripts(): ReadonlyArray<SelectedScript> {
|
||||||
|
return this.scripts.getItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
public selectAll(): void {
|
||||||
|
const scriptsToSelect = this.collection
|
||||||
|
.getAllScripts()
|
||||||
|
.filter((script) => !this.scripts.exists(script.id))
|
||||||
|
.map((script) => new SelectedScript(script, false));
|
||||||
|
if (scriptsToSelect.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const script of scriptsToSelect) {
|
||||||
|
this.scripts.addItem(script);
|
||||||
|
}
|
||||||
|
this.changed.notify(this.scripts.getItems());
|
||||||
|
}
|
||||||
|
|
||||||
|
public deselectAll(): void {
|
||||||
|
if (this.scripts.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const selectedScriptIds = this.scripts.getItems().map((script) => script.id);
|
||||||
|
for (const scriptId of selectedScriptIds) {
|
||||||
|
this.scripts.removeItem(scriptId);
|
||||||
|
}
|
||||||
|
this.changed.notify([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public selectOnly(scripts: readonly IScript[]): void {
|
||||||
|
if (!scripts || scripts.length === 0) {
|
||||||
|
throw new Error('Scripts are empty. Use deselectAll() if you want to deselect everything');
|
||||||
|
}
|
||||||
|
let totalChanged = 0;
|
||||||
|
totalChanged += this.unselectMissingWithoutNotifying(scripts);
|
||||||
|
totalChanged += this.selectNewWithoutNotifying(scripts);
|
||||||
|
if (totalChanged > 0) {
|
||||||
|
this.changed.notify(this.scripts.getItems());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private unselectMissingWithoutNotifying(scripts: readonly IScript[]): number {
|
||||||
|
if (this.scripts.length === 0 || scripts.length === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const existingItems = this.scripts.getItems();
|
||||||
|
const missingIds = existingItems
|
||||||
|
.filter((existing) => !scripts.some((script) => existing.id === script.id))
|
||||||
|
.map((script) => script.id);
|
||||||
|
for (const id of missingIds) {
|
||||||
|
this.scripts.removeItem(id);
|
||||||
|
}
|
||||||
|
return missingIds.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private selectNewWithoutNotifying(scripts: readonly IScript[]): number {
|
||||||
|
const unselectedScripts = scripts
|
||||||
|
.filter((script) => !this.scripts.exists(script.id))
|
||||||
|
.map((script) => new SelectedScript(script, false));
|
||||||
|
for (const newScript of unselectedScripts) {
|
||||||
|
this.scripts.addItem(newScript);
|
||||||
|
}
|
||||||
|
return unselectedScripts.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,52 +3,55 @@ import { DetectorBuilder } from './DetectorBuilder';
|
|||||||
import { IBrowserOsDetector } from './IBrowserOsDetector';
|
import { IBrowserOsDetector } from './IBrowserOsDetector';
|
||||||
|
|
||||||
export class BrowserOsDetector implements IBrowserOsDetector {
|
export class BrowserOsDetector implements IBrowserOsDetector {
|
||||||
private readonly detectors = BrowserDetectors;
|
private readonly detectors = BrowserDetectors;
|
||||||
public detect(userAgent: string): OperatingSystem {
|
|
||||||
if (!userAgent) {
|
public detect(userAgent: string): OperatingSystem | undefined {
|
||||||
return OperatingSystem.Unknown;
|
if (!userAgent) {
|
||||||
}
|
return undefined;
|
||||||
for (const detector of this.detectors) {
|
|
||||||
const os = detector.detect(userAgent);
|
|
||||||
if (os !== OperatingSystem.Unknown) {
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return OperatingSystem.Unknown;
|
|
||||||
}
|
}
|
||||||
|
for (const detector of this.detectors) {
|
||||||
|
const os = detector.detect(userAgent);
|
||||||
|
if (os !== undefined) {
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference: https://github.com/keithws/browser-report/blob/master/index.js#L304
|
// Reference: https://github.com/keithws/browser-report/blob/master/index.js#L304
|
||||||
const BrowserDetectors =
|
const BrowserDetectors = [
|
||||||
[
|
define(OperatingSystem.KaiOS, (b) => b
|
||||||
define(OperatingSystem.KaiOS, (b) =>
|
.mustInclude('KAIOS')),
|
||||||
b.mustInclude('KAIOS')),
|
define(OperatingSystem.ChromeOS, (b) => b
|
||||||
define(OperatingSystem.ChromeOS, (b) =>
|
.mustInclude('CrOS')),
|
||||||
b.mustInclude('CrOS')),
|
define(OperatingSystem.BlackBerryOS, (b) => b
|
||||||
define(OperatingSystem.BlackBerryOS, (b) =>
|
.mustInclude('BlackBerry')),
|
||||||
b.mustInclude('BlackBerry')),
|
define(OperatingSystem.BlackBerryTabletOS, (b) => b
|
||||||
define(OperatingSystem.BlackBerryTabletOS, (b) =>
|
.mustInclude('RIM Tablet OS')),
|
||||||
b.mustInclude('RIM Tablet OS')),
|
define(OperatingSystem.BlackBerry, (b) => b
|
||||||
define(OperatingSystem.BlackBerry, (b) =>
|
.mustInclude('BB10')),
|
||||||
b.mustInclude('BB10')),
|
define(OperatingSystem.Android, (b) => b
|
||||||
define(OperatingSystem.Android, (b) =>
|
.mustInclude('Android').mustNotInclude('Windows Phone')),
|
||||||
b.mustInclude('Android').mustNotInclude('Windows Phone')),
|
define(OperatingSystem.Android, (b) => b
|
||||||
define(OperatingSystem.Android, (b) =>
|
.mustInclude('Adr').mustNotInclude('Windows Phone')),
|
||||||
b.mustInclude('Adr').mustNotInclude('Windows Phone')),
|
define(OperatingSystem.iOS, (b) => b
|
||||||
define(OperatingSystem.iOS, (b) =>
|
.mustInclude('like Mac OS X')),
|
||||||
b.mustInclude('like Mac OS X')),
|
define(OperatingSystem.Linux, (b) => b
|
||||||
define(OperatingSystem.Linux, (b) =>
|
.mustInclude('Linux').mustNotInclude('Android').mustNotInclude('Adr')),
|
||||||
b.mustInclude('Linux').mustNotInclude('Android').mustNotInclude('Adr')),
|
define(OperatingSystem.Windows, (b) => b
|
||||||
define(OperatingSystem.Windows, (b) =>
|
.mustInclude('Windows').mustNotInclude('Windows Phone')),
|
||||||
b.mustInclude('Windows').mustNotInclude('Windows Phone')),
|
define(OperatingSystem.WindowsPhone, (b) => b
|
||||||
define(OperatingSystem.WindowsPhone, (b) =>
|
.mustInclude('Windows Phone')),
|
||||||
b.mustInclude('Windows Phone')),
|
define(OperatingSystem.macOS, (b) => b
|
||||||
define(OperatingSystem.macOS, (b) =>
|
.mustInclude('OS X').mustNotInclude('Android').mustNotInclude('like Mac OS X')),
|
||||||
b.mustInclude('OS X').mustNotInclude('Android').mustNotInclude('like Mac OS X')),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function define(os: OperatingSystem, applyRules: (builder: DetectorBuilder) => DetectorBuilder): IBrowserOsDetector {
|
function define(
|
||||||
const builder = new DetectorBuilder(os);
|
os: OperatingSystem,
|
||||||
applyRules(builder);
|
applyRules: (builder: DetectorBuilder) => DetectorBuilder,
|
||||||
return builder.build();
|
): IBrowserOsDetector {
|
||||||
|
const builder = new DetectorBuilder(os);
|
||||||
|
applyRules(builder);
|
||||||
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,53 +1,54 @@
|
|||||||
import { IBrowserOsDetector } from './IBrowserOsDetector';
|
|
||||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||||
|
import { IBrowserOsDetector } from './IBrowserOsDetector';
|
||||||
|
|
||||||
export class DetectorBuilder {
|
export class DetectorBuilder {
|
||||||
private readonly existingPartsInUserAgent = new Array<string>();
|
private readonly existingPartsInUserAgent = new Array<string>();
|
||||||
private readonly notExistingPartsInUserAgent = new Array<string>();
|
|
||||||
|
|
||||||
constructor(private readonly os: OperatingSystem) { }
|
private readonly notExistingPartsInUserAgent = new Array<string>();
|
||||||
|
|
||||||
public mustInclude(str: string): DetectorBuilder {
|
constructor(private readonly os: OperatingSystem) { }
|
||||||
return this.add(str, this.existingPartsInUserAgent);
|
|
||||||
|
public mustInclude(str: string): DetectorBuilder {
|
||||||
|
return this.add(str, this.existingPartsInUserAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public mustNotInclude(str: string): DetectorBuilder {
|
||||||
|
return this.add(str, this.notExistingPartsInUserAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public build(): IBrowserOsDetector {
|
||||||
|
if (!this.existingPartsInUserAgent.length) {
|
||||||
|
throw new Error('Must include at least a part');
|
||||||
}
|
}
|
||||||
|
return {
|
||||||
|
detect: (agent) => this.detect(agent),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public mustNotInclude(str: string): DetectorBuilder {
|
private detect(userAgent: string): OperatingSystem {
|
||||||
return this.add(str, this.notExistingPartsInUserAgent);
|
if (!userAgent) {
|
||||||
|
throw new Error('missing userAgent');
|
||||||
}
|
}
|
||||||
|
if (this.existingPartsInUserAgent.some((part) => !userAgent.includes(part))) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (this.notExistingPartsInUserAgent.some((part) => userAgent.includes(part))) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return this.os;
|
||||||
|
}
|
||||||
|
|
||||||
public build(): IBrowserOsDetector {
|
private add(part: string, array: string[]): DetectorBuilder {
|
||||||
if (!this.existingPartsInUserAgent.length) {
|
if (!part) {
|
||||||
throw new Error('Must include at least a part');
|
throw new Error('part is empty or undefined');
|
||||||
}
|
|
||||||
return {
|
|
||||||
detect: (agent) => this.detect(agent),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
if (this.existingPartsInUserAgent.includes(part)) {
|
||||||
private detect(userAgent: string): OperatingSystem {
|
throw new Error(`part ${part} is already included as existing part`);
|
||||||
if (!userAgent) {
|
|
||||||
throw new Error('User agent is null or undefined');
|
|
||||||
}
|
|
||||||
if (this.existingPartsInUserAgent.some((part) => !userAgent.includes(part))) {
|
|
||||||
return OperatingSystem.Unknown;
|
|
||||||
}
|
|
||||||
if (this.notExistingPartsInUserAgent.some((part) => userAgent.includes(part))) {
|
|
||||||
return OperatingSystem.Unknown;
|
|
||||||
}
|
|
||||||
return this.os;
|
|
||||||
}
|
}
|
||||||
|
if (this.notExistingPartsInUserAgent.includes(part)) {
|
||||||
private add(part: string, array: string[]): DetectorBuilder {
|
throw new Error(`part ${part} is already included as not existing part`);
|
||||||
if (!part) {
|
|
||||||
throw new Error('part is empty or undefined');
|
|
||||||
}
|
|
||||||
if (this.existingPartsInUserAgent.includes(part)) {
|
|
||||||
throw new Error(`part ${part} is already included as existing part`);
|
|
||||||
}
|
|
||||||
if (this.notExistingPartsInUserAgent.includes(part)) {
|
|
||||||
throw new Error(`part ${part} is already included as not existing part`);
|
|
||||||
}
|
|
||||||
array.push(part);
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
array.push(part);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||||
|
|
||||||
export interface IBrowserOsDetector {
|
export interface IBrowserOsDetector {
|
||||||
detect(userAgent: string): OperatingSystem;
|
detect(userAgent: string): OperatingSystem | undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,80 +1,89 @@
|
|||||||
|
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||||
import { BrowserOsDetector } from './BrowserOs/BrowserOsDetector';
|
import { BrowserOsDetector } from './BrowserOs/BrowserOsDetector';
|
||||||
import { IBrowserOsDetector } from './BrowserOs/IBrowserOsDetector';
|
import { IBrowserOsDetector } from './BrowserOs/IBrowserOsDetector';
|
||||||
import { IEnvironment } from './IEnvironment';
|
import { IEnvironment } from './IEnvironment';
|
||||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
|
||||||
|
|
||||||
interface IEnvironmentVariables {
|
export interface IEnvironmentVariables {
|
||||||
readonly window: Window & typeof globalThis;
|
readonly window: Window & typeof globalThis;
|
||||||
readonly process: NodeJS.Process;
|
readonly process: NodeJS.Process;
|
||||||
readonly navigator: Navigator;
|
readonly navigator: Navigator;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Environment implements IEnvironment {
|
export class Environment implements IEnvironment {
|
||||||
public static readonly CurrentEnvironment: IEnvironment = new Environment({
|
public static readonly CurrentEnvironment: IEnvironment = new Environment({
|
||||||
window,
|
window,
|
||||||
process,
|
process: typeof process !== 'undefined' ? process /* electron only */ : undefined,
|
||||||
navigator,
|
navigator,
|
||||||
});
|
});
|
||||||
public readonly isDesktop: boolean;
|
|
||||||
public readonly os: OperatingSystem;
|
public readonly isDesktop: boolean;
|
||||||
protected constructor(
|
|
||||||
variables: IEnvironmentVariables,
|
public readonly os: OperatingSystem;
|
||||||
browserOsDetector: IBrowserOsDetector = new BrowserOsDetector()) {
|
|
||||||
if (!variables) {
|
protected constructor(
|
||||||
throw new Error('variables is null or empty');
|
variables: IEnvironmentVariables,
|
||||||
}
|
browserOsDetector: IBrowserOsDetector = new BrowserOsDetector(),
|
||||||
this.isDesktop = isDesktop(variables);
|
) {
|
||||||
this.os = this.isDesktop ?
|
if (!variables) {
|
||||||
getDesktopOsType(getProcessPlatform(variables))
|
throw new Error('variables is null or empty');
|
||||||
: browserOsDetector.detect(getUserAgent(variables));
|
|
||||||
}
|
}
|
||||||
|
this.isDesktop = isDesktop(variables);
|
||||||
|
if (this.isDesktop) {
|
||||||
|
this.os = getDesktopOsType(getProcessPlatform(variables));
|
||||||
|
} else {
|
||||||
|
const userAgent = getUserAgent(variables);
|
||||||
|
this.os = !userAgent ? undefined : browserOsDetector.detect(userAgent);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUserAgent(variables: IEnvironmentVariables): string {
|
function getUserAgent(variables: IEnvironmentVariables): string {
|
||||||
if (!variables.window || !variables.window.navigator) {
|
if (!variables.window || !variables.window.navigator) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return variables.window.navigator.userAgent;
|
return variables.window.navigator.userAgent;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProcessPlatform(variables: IEnvironmentVariables): string {
|
function getProcessPlatform(variables: IEnvironmentVariables): string {
|
||||||
if (!variables.process || !variables.process.platform) {
|
if (!variables.process || !variables.process.platform) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return variables.process.platform;
|
return variables.process.platform;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDesktopOsType(processPlatform: string): OperatingSystem {
|
function getDesktopOsType(processPlatform: string): OperatingSystem | undefined {
|
||||||
// https://nodejs.org/api/process.html#process_process_platform
|
// https://nodejs.org/api/process.html#process_process_platform
|
||||||
if (processPlatform === 'darwin') {
|
switch (processPlatform) {
|
||||||
return OperatingSystem.macOS;
|
case 'darwin':
|
||||||
} else if (processPlatform === 'win32') {
|
return OperatingSystem.macOS;
|
||||||
return OperatingSystem.Windows;
|
case 'win32':
|
||||||
} else if (processPlatform === 'linux') {
|
return OperatingSystem.Windows;
|
||||||
return OperatingSystem.Linux;
|
case 'linux':
|
||||||
}
|
return OperatingSystem.Linux;
|
||||||
return OperatingSystem.Unknown;
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDesktop(variables: IEnvironmentVariables): boolean {
|
function isDesktop(variables: IEnvironmentVariables): boolean {
|
||||||
// More: https://github.com/electron/electron/issues/2288
|
// More: https://github.com/electron/electron/issues/2288
|
||||||
// Renderer process
|
// Renderer process
|
||||||
if (variables.window
|
if (variables.window
|
||||||
&& variables.window.process
|
&& variables.window.process
|
||||||
&& variables.window.process.type === 'renderer') {
|
&& variables.window.process.type === 'renderer') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Main process
|
// Main process
|
||||||
if (variables.process
|
if (variables.process
|
||||||
&& variables.process.versions
|
&& variables.process.versions
|
||||||
&& Boolean(variables.process.versions.electron)) {
|
&& Boolean(variables.process.versions.electron)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Detect the user agent when the `nodeIntegration` option is set to true
|
// Detect the user agent when the `nodeIntegration` option is set to true
|
||||||
if (variables.navigator
|
if (variables.navigator
|
||||||
&& variables.navigator.userAgent
|
&& variables.navigator.userAgent
|
||||||
&& variables.navigator.userAgent.includes('Electron')) {
|
&& variables.navigator.userAgent.includes('Electron')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||||
|
|
||||||
export interface IEnvironment {
|
export interface IEnvironment {
|
||||||
isDesktop: boolean;
|
readonly isDesktop: boolean;
|
||||||
os: OperatingSystem;
|
readonly os: OperatingSystem;
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/application/IApplicationFactory.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { IApplication } from '@/domain/IApplication';
|
||||||
|
|
||||||
|
export interface IApplicationFactory {
|
||||||
|
getApp(): Promise<IApplication>;
|
||||||
|
}
|
||||||
@@ -1,42 +1,41 @@
|
|||||||
import { Category } from '@/domain/Category';
|
import type { CollectionData } from '@/application/collections/';
|
||||||
import { Application } from '@/domain/Application';
|
|
||||||
import { IApplication } from '@/domain/IApplication';
|
import { IApplication } from '@/domain/IApplication';
|
||||||
import { IProjectInformation } from '@/domain/IProjectInformation';
|
import { IProjectInformation } from '@/domain/IProjectInformation';
|
||||||
import { ApplicationYaml } from 'js-yaml-loader!./../application.yaml';
|
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||||
import { parseCategory } from './CategoryParser';
|
import WindowsData from '@/application/collections/windows.yaml';
|
||||||
import { ProjectInformation } from '@/domain/ProjectInformation';
|
import MacOsData from '@/application/collections/macos.yaml';
|
||||||
import { ScriptCompiler } from './Compiler/ScriptCompiler';
|
import { parseProjectInformation } from '@/application/Parser/ProjectInformationParser';
|
||||||
|
import { Application } from '@/domain/Application';
|
||||||
|
import { parseCategoryCollection } from './CategoryCollectionParser';
|
||||||
|
|
||||||
|
export function parseApplication(
|
||||||
export function parseApplication(content: ApplicationYaml, env: NodeJS.ProcessEnv = process.env): IApplication {
|
parser = CategoryCollectionParser,
|
||||||
validate(content);
|
processEnv: NodeJS.ProcessEnv = process.env,
|
||||||
const compiler = new ScriptCompiler(content.functions);
|
collectionsData = PreParsedCollections,
|
||||||
const categories = new Array<Category>();
|
): IApplication {
|
||||||
for (const action of content.actions) {
|
validateCollectionsData(collectionsData);
|
||||||
const category = parseCategory(action, compiler);
|
const information = parseProjectInformation(processEnv);
|
||||||
categories.push(category);
|
const collections = collectionsData.map((collection) => parser(collection, information));
|
||||||
}
|
const app = new Application(information, collections);
|
||||||
const info = readAppInformation(env);
|
return app;
|
||||||
const app = new Application(
|
|
||||||
info,
|
|
||||||
categories);
|
|
||||||
return app;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function readAppInformation(environment: NodeJS.ProcessEnv): IProjectInformation {
|
export type CategoryCollectionParserType
|
||||||
return new ProjectInformation(
|
= (file: CollectionData, info: IProjectInformation) => ICategoryCollection;
|
||||||
environment.VUE_APP_NAME,
|
|
||||||
environment.VUE_APP_VERSION,
|
|
||||||
environment.VUE_APP_REPOSITORY_URL,
|
|
||||||
environment.VUE_APP_HOMEPAGE_URL,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function validate(content: ApplicationYaml): void {
|
const CategoryCollectionParser: CategoryCollectionParserType = (file, info) => {
|
||||||
if (!content) {
|
return parseCategoryCollection(file, info);
|
||||||
throw new Error('application is null or undefined');
|
};
|
||||||
}
|
|
||||||
if (!content.actions || content.actions.length <= 0) {
|
const PreParsedCollections: readonly CollectionData [] = [
|
||||||
throw new Error('application does not define any action');
|
WindowsData, MacOsData,
|
||||||
}
|
];
|
||||||
|
|
||||||
|
function validateCollectionsData(collections: readonly CollectionData[]) {
|
||||||
|
if (!collections || !collections.length) {
|
||||||
|
throw new Error('missing collections');
|
||||||
|
}
|
||||||
|
if (collections.some((collection) => !collection)) {
|
||||||
|
throw new Error('missing collection provided');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
37
src/application/Parser/CategoryCollectionParser.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import type { CollectionData } from '@/application/collections/';
|
||||||
|
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||||
|
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||||
|
import { CategoryCollection } from '@/domain/CategoryCollection';
|
||||||
|
import { IProjectInformation } from '@/domain/IProjectInformation';
|
||||||
|
import { createEnumParser } from '../Common/Enum';
|
||||||
|
import { parseCategory } from './CategoryParser';
|
||||||
|
import { CategoryCollectionParseContext } from './Script/CategoryCollectionParseContext';
|
||||||
|
import { ScriptingDefinitionParser } from './ScriptingDefinition/ScriptingDefinitionParser';
|
||||||
|
|
||||||
|
export function parseCategoryCollection(
|
||||||
|
content: CollectionData,
|
||||||
|
info: IProjectInformation,
|
||||||
|
osParser = createEnumParser(OperatingSystem),
|
||||||
|
): ICategoryCollection {
|
||||||
|
validate(content);
|
||||||
|
const scripting = new ScriptingDefinitionParser()
|
||||||
|
.parse(content.scripting, info);
|
||||||
|
const context = new CategoryCollectionParseContext(content.functions, scripting);
|
||||||
|
const categories = content.actions.map((action) => parseCategory(action, context));
|
||||||
|
const os = osParser.parseEnum(content.os, 'os');
|
||||||
|
const collection = new CategoryCollection(
|
||||||
|
os,
|
||||||
|
categories,
|
||||||
|
scripting,
|
||||||
|
);
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validate(content: CollectionData): void {
|
||||||
|
if (!content) {
|
||||||
|
throw new Error('missing content');
|
||||||
|
}
|
||||||
|
if (!content.actions || content.actions.length <= 0) {
|
||||||
|
throw new Error('content does not define any action');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,73 +1,86 @@
|
|||||||
import { YamlCategory, YamlScript } from 'js-yaml-loader!./application.yaml';
|
import type {
|
||||||
|
CategoryData, ScriptData, CategoryOrScriptData, InstructionHolder,
|
||||||
|
} from '@/application/collections/';
|
||||||
import { Script } from '@/domain/Script';
|
import { Script } from '@/domain/Script';
|
||||||
import { Category } from '@/domain/Category';
|
import { Category } from '@/domain/Category';
|
||||||
import { parseDocUrls } from './DocumentationParser';
|
import { parseDocUrls } from './DocumentationParser';
|
||||||
import { parseScript } from './ScriptParser';
|
import { ICategoryCollectionParseContext } from './Script/ICategoryCollectionParseContext';
|
||||||
import { IScriptCompiler } from './Compiler/IScriptCompiler';
|
import { parseScript } from './Script/ScriptParser';
|
||||||
|
|
||||||
let categoryIdCounter: number = 0;
|
let categoryIdCounter = 0;
|
||||||
|
|
||||||
|
export function parseCategory(
|
||||||
|
category: CategoryData,
|
||||||
|
context: ICategoryCollectionParseContext,
|
||||||
|
): Category {
|
||||||
|
if (!context) { throw new Error('missing context'); }
|
||||||
|
ensureValid(category);
|
||||||
|
const children: ICategoryChildren = {
|
||||||
|
subCategories: new Array<Category>(),
|
||||||
|
subScripts: new Array<Script>(),
|
||||||
|
};
|
||||||
|
for (const data of category.children) {
|
||||||
|
parseCategoryChild(data, children, category, context);
|
||||||
|
}
|
||||||
|
return new Category(
|
||||||
|
/* id: */ categoryIdCounter++,
|
||||||
|
/* name: */ category.category,
|
||||||
|
/* docs: */ parseDocUrls(category),
|
||||||
|
/* categories: */ children.subCategories,
|
||||||
|
/* scripts: */ children.subScripts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureValid(category: CategoryData) {
|
||||||
|
if (!category) {
|
||||||
|
throw Error('missing category');
|
||||||
|
}
|
||||||
|
if (!category.children || category.children.length === 0) {
|
||||||
|
throw Error(`category has no children: "${category.category}"`);
|
||||||
|
}
|
||||||
|
if (!category.category || category.category.length === 0) {
|
||||||
|
throw Error('category has no name');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interface ICategoryChildren {
|
interface ICategoryChildren {
|
||||||
subCategories: Category[];
|
subCategories: Category[];
|
||||||
subScripts: Script[];
|
subScripts: Script[];
|
||||||
}
|
|
||||||
|
|
||||||
export function parseCategory(category: YamlCategory, compiler: IScriptCompiler): Category {
|
|
||||||
if (!compiler) {
|
|
||||||
throw new Error('undefined compiler');
|
|
||||||
}
|
|
||||||
ensureValid(category);
|
|
||||||
const children: ICategoryChildren = {
|
|
||||||
subCategories: new Array<Category>(),
|
|
||||||
subScripts: new Array<Script>(),
|
|
||||||
};
|
|
||||||
for (const categoryOrScript of category.children) {
|
|
||||||
parseCategoryChild(categoryOrScript, children, category, compiler);
|
|
||||||
}
|
|
||||||
return new Category(
|
|
||||||
/*id*/ categoryIdCounter++,
|
|
||||||
/*name*/ category.category,
|
|
||||||
/*docs*/ parseDocUrls(category),
|
|
||||||
/*categories*/ children.subCategories,
|
|
||||||
/*scripts*/ children.subScripts,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ensureValid(category: YamlCategory) {
|
|
||||||
if (!category) {
|
|
||||||
throw Error('category is null or undefined');
|
|
||||||
}
|
|
||||||
if (!category.children || category.children.length === 0) {
|
|
||||||
throw Error('category has no children');
|
|
||||||
}
|
|
||||||
if (!category.category || category.category.length === 0) {
|
|
||||||
throw Error('category has no name');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCategoryChild(
|
function parseCategoryChild(
|
||||||
categoryOrScript: any,
|
data: CategoryOrScriptData,
|
||||||
children: ICategoryChildren,
|
children: ICategoryChildren,
|
||||||
parent: YamlCategory,
|
parent: CategoryData,
|
||||||
compiler: IScriptCompiler) {
|
context: ICategoryCollectionParseContext,
|
||||||
if (isCategory(categoryOrScript)) {
|
) {
|
||||||
const subCategory = parseCategory(categoryOrScript as YamlCategory, compiler);
|
if (isCategory(data)) {
|
||||||
children.subCategories.push(subCategory);
|
const subCategory = parseCategory(data as CategoryData, context);
|
||||||
} else if (isScript(categoryOrScript)) {
|
children.subCategories.push(subCategory);
|
||||||
const yamlScript = categoryOrScript as YamlScript;
|
} else if (isScript(data)) {
|
||||||
const script = parseScript(yamlScript, compiler);
|
const scriptData = data as ScriptData;
|
||||||
children.subScripts.push(script);
|
const script = parseScript(scriptData, context);
|
||||||
} else {
|
children.subScripts.push(script);
|
||||||
throw new Error(`Child element is neither a category or a script.
|
} else {
|
||||||
Parent: ${parent.category}, element: ${JSON.stringify(categoryOrScript)}`);
|
throw new Error(`Child element is neither a category or a script.
|
||||||
}
|
Parent: ${parent.category}, element: ${JSON.stringify(data)}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isScript(categoryOrScript: any): boolean {
|
function isScript(data: CategoryOrScriptData): data is ScriptData {
|
||||||
return (categoryOrScript.code && categoryOrScript.code.length > 0)
|
const holder = (data as InstructionHolder);
|
||||||
|| categoryOrScript.call;
|
return hasCode(holder) || hasCall(holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCategory(categoryOrScript: any): boolean {
|
function isCategory(data: CategoryOrScriptData): data is CategoryData {
|
||||||
return categoryOrScript.category && categoryOrScript.category.length > 0;
|
const { category } = data as CategoryData;
|
||||||
|
return category && category.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasCode(holder: InstructionHolder): boolean {
|
||||||
|
return holder.code && holder.code.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasCall(holder: InstructionHolder) {
|
||||||
|
return holder.call !== undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import { IScriptCode } from '@/domain/IScriptCode';
|
|
||||||
import { YamlScript } from 'js-yaml-loader!./application.yaml';
|
|
||||||
|
|
||||||
export interface IScriptCompiler {
|
|
||||||
canCompile(script: YamlScript): boolean;
|
|
||||||
compile(script: YamlScript): IScriptCode;
|
|
||||||
}
|
|
||||||
@@ -1,200 +0,0 @@
|
|||||||
import { IScriptCode } from '@/domain/IScriptCode';
|
|
||||||
import { ScriptCode } from '@/domain/ScriptCode';
|
|
||||||
import { YamlScript, YamlFunction, FunctionCall, ScriptFunctionCall, FunctionCallParameters } from 'js-yaml-loader!./application.yaml';
|
|
||||||
import { IScriptCompiler } from './IScriptCompiler';
|
|
||||||
|
|
||||||
interface ICompiledCode {
|
|
||||||
readonly code: string;
|
|
||||||
readonly revertCode: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ScriptCompiler implements IScriptCompiler {
|
|
||||||
constructor(private readonly functions: readonly YamlFunction[]) {
|
|
||||||
ensureValidFunctions(functions);
|
|
||||||
}
|
|
||||||
public canCompile(script: YamlScript): boolean {
|
|
||||||
if (!script.call) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
public compile(script: YamlScript): IScriptCode {
|
|
||||||
this.ensureCompilable(script.call);
|
|
||||||
const compiledCodes = new Array<ICompiledCode>();
|
|
||||||
const calls = getCallSequence(script.call);
|
|
||||||
calls.forEach((currentCall, currentCallIndex) => {
|
|
||||||
ensureValidCall(currentCall, script.name);
|
|
||||||
const commonFunction = this.getFunctionByName(currentCall.function);
|
|
||||||
let functionCode = compileCode(commonFunction, currentCall.parameters);
|
|
||||||
if (currentCallIndex !== calls.length - 1) {
|
|
||||||
functionCode = appendLine(functionCode);
|
|
||||||
}
|
|
||||||
compiledCodes.push(functionCode);
|
|
||||||
});
|
|
||||||
const scriptCode = merge(compiledCodes);
|
|
||||||
return new ScriptCode(script.name, scriptCode.code, scriptCode.revertCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getFunctionByName(name: string): YamlFunction {
|
|
||||||
const func = this.functions.find((f) => f.name === name);
|
|
||||||
if (!func) {
|
|
||||||
throw new Error(`called function is not defined "${name}"`);
|
|
||||||
}
|
|
||||||
return func;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ensureCompilable(call: ScriptFunctionCall) {
|
|
||||||
if (!this.functions || this.functions.length === 0) {
|
|
||||||
throw new Error('cannot compile without shared functions');
|
|
||||||
}
|
|
||||||
if (typeof call !== 'object') {
|
|
||||||
throw new Error('called function(s) must be an object');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDuplicates(texts: readonly string[]): string[] {
|
|
||||||
return texts.filter((item, index) => texts.indexOf(item) !== index);
|
|
||||||
}
|
|
||||||
|
|
||||||
function printList(list: readonly string[]): string {
|
|
||||||
return `"${list.join('","')}"`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ensureNoDuplicatesInFunctionNames(functions: readonly YamlFunction[]) {
|
|
||||||
const duplicateFunctionNames = getDuplicates(functions
|
|
||||||
.map((func) => func.name.toLowerCase()));
|
|
||||||
if (duplicateFunctionNames.length) {
|
|
||||||
throw new Error(`duplicate function name: ${printList(duplicateFunctionNames)}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function ensureNoDuplicatesInParameterNames(functions: readonly YamlFunction[]) {
|
|
||||||
const functionsWithParameters = functions
|
|
||||||
.filter((func) => func.parameters && func.parameters.length > 0);
|
|
||||||
for (const func of functionsWithParameters) {
|
|
||||||
const duplicateParameterNames = getDuplicates(func.parameters);
|
|
||||||
if (duplicateParameterNames.length) {
|
|
||||||
throw new Error(`"${func.name}": duplicate parameter name: ${printList(duplicateParameterNames)}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function ensureNoDuplicateCode(functions: readonly YamlFunction[]) {
|
|
||||||
const duplicateCodes = getDuplicates(functions.map((func) => func.code));
|
|
||||||
if (duplicateCodes.length > 0) {
|
|
||||||
throw new Error(`duplicate "code" in functions: ${printList(duplicateCodes)}`);
|
|
||||||
}
|
|
||||||
const duplicateRevertCodes = getDuplicates(functions
|
|
||||||
.filter((func) => func.revertCode)
|
|
||||||
.map((func) => func.revertCode));
|
|
||||||
if (duplicateRevertCodes.length > 0) {
|
|
||||||
throw new Error(`duplicate "revertCode" in functions: ${printList(duplicateRevertCodes)}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function ensureValidFunctions(functions: readonly YamlFunction[]) {
|
|
||||||
if (!functions) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ensureNoDuplicatesInFunctionNames(functions);
|
|
||||||
ensureNoDuplicatesInParameterNames(functions);
|
|
||||||
ensureNoDuplicateCode(functions);
|
|
||||||
}
|
|
||||||
|
|
||||||
function appendLine(code: ICompiledCode): ICompiledCode {
|
|
||||||
const appendLineIfNotEmpty = (str: string) => str ? `${str}\n` : str;
|
|
||||||
return {
|
|
||||||
code: appendLineIfNotEmpty(code.code),
|
|
||||||
revertCode: appendLineIfNotEmpty(code.revertCode),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function merge(codes: readonly ICompiledCode[]): ICompiledCode {
|
|
||||||
return {
|
|
||||||
code: codes.map((code) => code.code).join(''),
|
|
||||||
revertCode: codes.map((code) => code.revertCode).join(''),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function compileCode(func: YamlFunction, parameters: FunctionCallParameters): ICompiledCode {
|
|
||||||
return {
|
|
||||||
code: compileExpressions(func.code, parameters),
|
|
||||||
revertCode: compileExpressions(func.revertCode, parameters),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function compileExpressions(code: string, parameters: FunctionCallParameters): string {
|
|
||||||
let intermediateCode = compileToIL(code);
|
|
||||||
intermediateCode = substituteParameters(intermediateCode, parameters);
|
|
||||||
ensureNoExpressionLeft(intermediateCode);
|
|
||||||
return intermediateCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
function substituteParameters(intermediateCode: string, parameters: FunctionCallParameters): string {
|
|
||||||
const parameterNames = getUniqueParameterNamesFromIL(intermediateCode);
|
|
||||||
if (parameterNames.length && !parameters) {
|
|
||||||
throw new Error(`no parameters defined, expected: ${printList(parameterNames)}`);
|
|
||||||
}
|
|
||||||
for (const parameterName of parameterNames) {
|
|
||||||
const parameterValue = parameters[parameterName];
|
|
||||||
intermediateCode = substituteParameter(intermediateCode, parameterName, parameterValue);
|
|
||||||
}
|
|
||||||
return intermediateCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ensureValidCall(call: FunctionCall, scriptName: string) {
|
|
||||||
if (!call) {
|
|
||||||
throw new Error(`undefined function call in script "${scriptName}"`);
|
|
||||||
}
|
|
||||||
if (!call.function) {
|
|
||||||
throw new Error(`empty function name called in script "${scriptName}"`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCallSequence(call: ScriptFunctionCall): FunctionCall[] {
|
|
||||||
if (call instanceof Array) {
|
|
||||||
return call as FunctionCall[];
|
|
||||||
}
|
|
||||||
return [ call as FunctionCall ];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDistinctValues(values: readonly string[]): string[] {
|
|
||||||
return values.filter((value, index, self) => {
|
|
||||||
return self.indexOf(value) === index;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trim each expression and put them inside "{{exp|}}" e.g. "{{ $hello }}" becomes "{{exp|$hello}}"
|
|
||||||
function compileToIL(code: string) {
|
|
||||||
return code.replace(/\{\{([\s]*[^;\s\{]+[\s]*)\}\}/g, (_, match) => {
|
|
||||||
return `\{\{exp|${match.trim()}\}\}`;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parses all distinct usages of {{exp|$parameterName}}
|
|
||||||
function getUniqueParameterNamesFromIL(ilCode: string) {
|
|
||||||
const allSubstitutions = ilCode.matchAll(/\{\{exp\|\$([^;\s\{]+[\s]*)\}\}/g);
|
|
||||||
const allParameters = Array.from(allSubstitutions, (match) => match[1]);
|
|
||||||
const uniqueParameterNames = getDistinctValues(allParameters);
|
|
||||||
return uniqueParameterNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
// substitutes {{exp|$parameterName}} to value of the parameter
|
|
||||||
function substituteParameter(ilCode: string, parameterName: string, parameterValue: string) {
|
|
||||||
if (!parameterValue) {
|
|
||||||
throw Error(`parameter value is not provided for "${parameterName}" in function call`);
|
|
||||||
}
|
|
||||||
const pattern = `{{exp|$${parameterName}}}`;
|
|
||||||
return ilCode.split(pattern).join(parameterValue); // as .replaceAll() is not yet supported by TS
|
|
||||||
}
|
|
||||||
|
|
||||||
// finds all "{{exp|..}} left"
|
|
||||||
function ensureNoExpressionLeft(ilCode: string) {
|
|
||||||
const allSubstitutions = ilCode.matchAll(/\{\{exp\|(.*?)\}\}/g);
|
|
||||||
const allMatches = Array.from(allSubstitutions, (match) => match[1]);
|
|
||||||
const uniqueExpressions = getDistinctValues(allMatches);
|
|
||||||
if (uniqueExpressions.length > 0) {
|
|
||||||
throw new Error(`unknown expression: ${printList(uniqueExpressions)}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,61 +1,64 @@
|
|||||||
import { YamlDocumentable, DocumentationUrls } from 'js-yaml-loader!./application.yaml';
|
import type { DocumentableData, DocumentationUrlsData } from '@/application/collections/';
|
||||||
|
|
||||||
export function parseDocUrls(documentable: YamlDocumentable): ReadonlyArray<string> {
|
export function parseDocUrls(documentable: DocumentableData): ReadonlyArray<string> {
|
||||||
if (!documentable) {
|
if (!documentable) {
|
||||||
throw new Error('documentable is null or undefined');
|
throw new Error('missing documentable');
|
||||||
}
|
}
|
||||||
const docs = documentable.docs;
|
const { docs } = documentable;
|
||||||
if (!docs || !docs.length) {
|
if (!docs || !docs.length) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
let result = new DocumentationUrlContainer();
|
let result = new DocumentationUrlContainer();
|
||||||
result = addDocs(docs, result);
|
result = addDocs(docs, result);
|
||||||
return result.getAll();
|
return result.getAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
function addDocs(docs: DocumentationUrls, urls: DocumentationUrlContainer): DocumentationUrlContainer {
|
function addDocs(
|
||||||
if (docs instanceof Array) {
|
docs: DocumentationUrlsData,
|
||||||
urls.addUrls(docs);
|
urls: DocumentationUrlContainer,
|
||||||
} else if (typeof docs === 'string') {
|
): DocumentationUrlContainer {
|
||||||
urls.addUrl(docs);
|
if (docs instanceof Array) {
|
||||||
} else {
|
urls.addUrls(docs);
|
||||||
throw new Error('Docs field (documentation url) must a string or array of strings');
|
} else if (typeof docs === 'string') {
|
||||||
}
|
urls.addUrl(docs);
|
||||||
return urls;
|
} else {
|
||||||
|
throw new Error('Docs field (documentation url) must a string or array of strings');
|
||||||
|
}
|
||||||
|
return urls;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DocumentationUrlContainer {
|
class DocumentationUrlContainer {
|
||||||
private readonly urls = new Array<string>();
|
private readonly urls = new Array<string>();
|
||||||
|
|
||||||
public addUrl(url: string) {
|
public addUrl(url: string) {
|
||||||
validateUrl(url);
|
validateUrl(url);
|
||||||
this.urls.push(url);
|
this.urls.push(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public addUrls(urls: any[]) {
|
public addUrls(urls: readonly string[]) {
|
||||||
for (const url of urls) {
|
for (const url of urls) {
|
||||||
if (typeof url !== 'string') {
|
if (typeof url !== 'string') {
|
||||||
throw new Error('Docs field (documentation url) must be an array of strings');
|
throw new Error('Docs field (documentation url) must be an array of strings');
|
||||||
}
|
}
|
||||||
this.addUrl(url);
|
this.addUrl(url);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public getAll(): ReadonlyArray<string> {
|
public getAll(): ReadonlyArray<string> {
|
||||||
return this.urls;
|
return this.urls;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateUrl(docUrl: string): void {
|
function validateUrl(docUrl: string): void {
|
||||||
if (!docUrl) {
|
if (!docUrl) {
|
||||||
throw new Error('Documentation url is null or empty');
|
throw new Error('Documentation url is null or empty');
|
||||||
}
|
}
|
||||||
if (docUrl.includes('\n')) {
|
if (docUrl.includes('\n')) {
|
||||||
throw new Error('Documentation url cannot be multi-lined.');
|
throw new Error('Documentation url cannot be multi-lined.');
|
||||||
}
|
}
|
||||||
const res = docUrl.match(
|
const validUrlRegex = /(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/g;
|
||||||
/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g);
|
const res = docUrl.match(validUrlRegex);
|
||||||
if (res == null) {
|
if (res == null) {
|
||||||
throw new Error(`Invalid documentation url: ${docUrl}`);
|
throw new Error(`Invalid documentation url: ${docUrl}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/application/Parser/ProjectInformationParser.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { IProjectInformation } from '@/domain/IProjectInformation';
|
||||||
|
import { ProjectInformation } from '@/domain/ProjectInformation';
|
||||||
|
import { Version } from '@/domain/Version';
|
||||||
|
|
||||||
|
export function parseProjectInformation(
|
||||||
|
environment: NodeJS.ProcessEnv,
|
||||||
|
): IProjectInformation {
|
||||||
|
const version = new Version(environment.VUE_APP_VERSION);
|
||||||
|
return new ProjectInformation(
|
||||||
|
environment.VUE_APP_NAME,
|
||||||
|
version,
|
||||||
|
environment.VUE_APP_REPOSITORY_URL,
|
||||||
|
environment.VUE_APP_HOMEPAGE_URL,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import type { FunctionData } from '@/application/collections/';
|
||||||
|
import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||||
|
import { ILanguageSyntax } from '@/domain/ScriptCode';
|
||||||
|
import { IScriptCompiler } from './Compiler/IScriptCompiler';
|
||||||
|
import { ScriptCompiler } from './Compiler/ScriptCompiler';
|
||||||
|
import { ICategoryCollectionParseContext } from './ICategoryCollectionParseContext';
|
||||||
|
import { SyntaxFactory } from './Syntax/SyntaxFactory';
|
||||||
|
import { ISyntaxFactory } from './Syntax/ISyntaxFactory';
|
||||||
|
|
||||||
|
export class CategoryCollectionParseContext implements ICategoryCollectionParseContext {
|
||||||
|
public readonly compiler: IScriptCompiler;
|
||||||
|
|
||||||
|
public readonly syntax: ILanguageSyntax;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
functionsData: ReadonlyArray<FunctionData> | undefined,
|
||||||
|
scripting: IScriptingDefinition,
|
||||||
|
syntaxFactory: ISyntaxFactory = new SyntaxFactory(),
|
||||||
|
) {
|
||||||
|
if (!scripting) { throw new Error('missing scripting'); }
|
||||||
|
this.syntax = syntaxFactory.create(scripting.language);
|
||||||
|
this.compiler = new ScriptCompiler(functionsData, this.syntax);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import { FunctionParameterCollection } from '@/application/Parser/Script/Compiler/Function/Parameter/FunctionParameterCollection';
|
||||||
|
import { IReadOnlyFunctionCallArgumentCollection } from '../../Function/Call/Argument/IFunctionCallArgumentCollection';
|
||||||
|
import { IReadOnlyFunctionParameterCollection } from '../../Function/Parameter/IFunctionParameterCollection';
|
||||||
|
import { FunctionCallArgumentCollection } from '../../Function/Call/Argument/FunctionCallArgumentCollection';
|
||||||
|
import { IExpression } from './IExpression';
|
||||||
|
import { ExpressionPosition } from './ExpressionPosition';
|
||||||
|
import { ExpressionEvaluationContext, IExpressionEvaluationContext } from './ExpressionEvaluationContext';
|
||||||
|
|
||||||
|
export type ExpressionEvaluator = (context: IExpressionEvaluationContext) => string;
|
||||||
|
export class Expression implements IExpression {
|
||||||
|
public readonly parameters: IReadOnlyFunctionParameterCollection;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly position: ExpressionPosition,
|
||||||
|
public readonly evaluator: ExpressionEvaluator,
|
||||||
|
parameters?: IReadOnlyFunctionParameterCollection,
|
||||||
|
) {
|
||||||
|
if (!position) {
|
||||||
|
throw new Error('missing position');
|
||||||
|
}
|
||||||
|
if (!evaluator) {
|
||||||
|
throw new Error('missing evaluator');
|
||||||
|
}
|
||||||
|
this.parameters = parameters ?? new FunctionParameterCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public evaluate(context: IExpressionEvaluationContext): string {
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('missing context');
|
||||||
|
}
|
||||||
|
validateThatAllRequiredParametersAreSatisfied(this.parameters, context.args);
|
||||||
|
const args = filterUnusedArguments(this.parameters, context.args);
|
||||||
|
const filteredContext = new ExpressionEvaluationContext(args, context.pipelineCompiler);
|
||||||
|
return this.evaluator(filteredContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateThatAllRequiredParametersAreSatisfied(
|
||||||
|
parameters: IReadOnlyFunctionParameterCollection,
|
||||||
|
args: IReadOnlyFunctionCallArgumentCollection,
|
||||||
|
) {
|
||||||
|
const requiredParameterNames = parameters
|
||||||
|
.all
|
||||||
|
.filter((parameter) => !parameter.isOptional)
|
||||||
|
.map((parameter) => parameter.name);
|
||||||
|
const missingParameterNames = requiredParameterNames
|
||||||
|
.filter((parameterName) => !args.hasArgument(parameterName));
|
||||||
|
if (missingParameterNames.length) {
|
||||||
|
throw new Error(
|
||||||
|
`argument values are provided for required parameters: "${missingParameterNames.join('", "')}"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterUnusedArguments(
|
||||||
|
parameters: IReadOnlyFunctionParameterCollection,
|
||||||
|
allFunctionArgs: IReadOnlyFunctionCallArgumentCollection,
|
||||||
|
): IReadOnlyFunctionCallArgumentCollection {
|
||||||
|
const specificCallArgs = new FunctionCallArgumentCollection();
|
||||||
|
parameters.all
|
||||||
|
.filter((parameter) => allFunctionArgs.hasArgument(parameter.name))
|
||||||
|
.map((parameter) => allFunctionArgs.getArgument(parameter.name))
|
||||||
|
.forEach((argument) => specificCallArgs.addArgument(argument));
|
||||||
|
return specificCallArgs;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { IReadOnlyFunctionCallArgumentCollection } from '../../Function/Call/Argument/IFunctionCallArgumentCollection';
|
||||||
|
import { IPipelineCompiler } from '../Pipes/IPipelineCompiler';
|
||||||
|
import { PipelineCompiler } from '../Pipes/PipelineCompiler';
|
||||||
|
|
||||||
|
export interface IExpressionEvaluationContext {
|
||||||
|
readonly args: IReadOnlyFunctionCallArgumentCollection;
|
||||||
|
readonly pipelineCompiler: IPipelineCompiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExpressionEvaluationContext implements IExpressionEvaluationContext {
|
||||||
|
constructor(
|
||||||
|
public readonly args: IReadOnlyFunctionCallArgumentCollection,
|
||||||
|
public readonly pipelineCompiler: IPipelineCompiler = new PipelineCompiler(),
|
||||||
|
) {
|
||||||
|
if (!args) {
|
||||||
|
throw new Error('missing args, send empty collection instead.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
export class ExpressionPosition {
|
||||||
|
constructor(
|
||||||
|
public readonly start: number,
|
||||||
|
public readonly end: number,
|
||||||
|
) {
|
||||||
|
if (start === end) {
|
||||||
|
throw new Error(`no length (start = end = ${start})`);
|
||||||
|
}
|
||||||
|
if (start > end) {
|
||||||
|
throw Error(`start (${start}) after end (${end})`);
|
||||||
|
}
|
||||||
|
if (start < 0) {
|
||||||
|
throw Error(`negative start position: ${start}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { IReadOnlyFunctionParameterCollection } from '../../Function/Parameter/IFunctionParameterCollection';
|
||||||
|
import { ExpressionPosition } from './ExpressionPosition';
|
||||||
|
import { IExpressionEvaluationContext } from './ExpressionEvaluationContext';
|
||||||
|
|
||||||
|
export interface IExpression {
|
||||||
|
readonly position: ExpressionPosition;
|
||||||
|
readonly parameters: IReadOnlyFunctionParameterCollection;
|
||||||
|
evaluate(context: IExpressionEvaluationContext): string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { IExpressionEvaluationContext, ExpressionEvaluationContext } from '@/application/Parser/Script/Compiler/Expressions/Expression/ExpressionEvaluationContext';
|
||||||
|
import { IReadOnlyFunctionCallArgumentCollection } from '../Function/Call/Argument/IFunctionCallArgumentCollection';
|
||||||
|
import { IExpressionsCompiler } from './IExpressionsCompiler';
|
||||||
|
import { IExpression } from './Expression/IExpression';
|
||||||
|
import { IExpressionParser } from './Parser/IExpressionParser';
|
||||||
|
import { CompositeExpressionParser } from './Parser/CompositeExpressionParser';
|
||||||
|
|
||||||
|
export class ExpressionsCompiler implements IExpressionsCompiler {
|
||||||
|
public constructor(
|
||||||
|
private readonly extractor: IExpressionParser = new CompositeExpressionParser(),
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public compileExpressions(
|
||||||
|
code: string | undefined,
|
||||||
|
args: IReadOnlyFunctionCallArgumentCollection,
|
||||||
|
): string {
|
||||||
|
if (!args) {
|
||||||
|
throw new Error('missing args, send empty collection instead.');
|
||||||
|
}
|
||||||
|
if (!code) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
const expressions = this.extractor.findExpressions(code);
|
||||||
|
ensureParamsUsedInCodeHasArgsProvided(expressions, args);
|
||||||
|
const context = new ExpressionEvaluationContext(args);
|
||||||
|
const compiledCode = compileExpressions(expressions, code, context);
|
||||||
|
return compiledCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function compileExpressions(
|
||||||
|
expressions: readonly IExpression[],
|
||||||
|
code: string,
|
||||||
|
context: IExpressionEvaluationContext,
|
||||||
|
) {
|
||||||
|
let compiledCode = '';
|
||||||
|
const sortedExpressions = expressions
|
||||||
|
.slice() // copy the array to not mutate the parameter
|
||||||
|
.sort((a, b) => b.position.start - a.position.start);
|
||||||
|
let index = 0;
|
||||||
|
while (index !== code.length) {
|
||||||
|
const nextExpression = sortedExpressions.pop();
|
||||||
|
if (nextExpression) {
|
||||||
|
compiledCode += code.substring(index, nextExpression.position.start);
|
||||||
|
const expressionCode = nextExpression.evaluate(context);
|
||||||
|
compiledCode += expressionCode;
|
||||||
|
index = nextExpression.position.end;
|
||||||
|
} else {
|
||||||
|
compiledCode += code.substring(index, code.length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return compiledCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractRequiredParameterNames(
|
||||||
|
expressions: readonly IExpression[],
|
||||||
|
): string[] {
|
||||||
|
return expressions
|
||||||
|
.map((e) => e.parameters.all
|
||||||
|
.filter((p) => !p.isOptional)
|
||||||
|
.map((p) => p.name))
|
||||||
|
.filter(Boolean) // Remove empty or undefined
|
||||||
|
.flat()
|
||||||
|
.filter((name, index, array) => array.indexOf(name) === index); // Remove duplicates
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureParamsUsedInCodeHasArgsProvided(
|
||||||
|
expressions: readonly IExpression[],
|
||||||
|
providedArgs: IReadOnlyFunctionCallArgumentCollection,
|
||||||
|
): void {
|
||||||
|
const usedParameterNames = extractRequiredParameterNames(expressions);
|
||||||
|
if (!usedParameterNames?.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const notProvidedParameters = usedParameterNames
|
||||||
|
.filter((parameterName) => !providedArgs.hasArgument(parameterName));
|
||||||
|
if (notProvidedParameters.length) {
|
||||||
|
throw new Error(`parameter value(s) not provided for: ${printList(notProvidedParameters)} but used in code`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function printList(list: readonly string[]): string {
|
||||||
|
return `"${list.join('", "')}"`;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { IReadOnlyFunctionCallArgumentCollection } from '../Function/Call/Argument/IFunctionCallArgumentCollection';
|
||||||
|
|
||||||
|
export interface IExpressionsCompiler {
|
||||||
|
compileExpressions(
|
||||||
|
code: string | undefined,
|
||||||
|
args: IReadOnlyFunctionCallArgumentCollection): string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { IExpression } from '../Expression/IExpression';
|
||||||
|
import { ParameterSubstitutionParser } from '../SyntaxParsers/ParameterSubstitutionParser';
|
||||||
|
import { WithParser } from '../SyntaxParsers/WithParser';
|
||||||
|
import { IExpressionParser } from './IExpressionParser';
|
||||||
|
|
||||||
|
const Parsers = [
|
||||||
|
new ParameterSubstitutionParser(),
|
||||||
|
new WithParser(),
|
||||||
|
];
|
||||||
|
|
||||||
|
export class CompositeExpressionParser implements IExpressionParser {
|
||||||
|
public constructor(private readonly leafs: readonly IExpressionParser[] = Parsers) {
|
||||||
|
if (!leafs) {
|
||||||
|
throw new Error('missing leafs');
|
||||||
|
}
|
||||||
|
if (leafs.some((leaf) => !leaf)) {
|
||||||
|
throw new Error('missing leaf');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public findExpressions(code: string): IExpression[] {
|
||||||
|
return this.leafs.flatMap(
|
||||||
|
(parser) => parser.findExpressions(code) || [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||