Compare commits
142 Commits
0.12.4
...
dead-urls-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6720a46d2e | ||
|
|
287b8e61a0 | ||
|
|
e7218850ba | ||
|
|
adc2089887 | ||
|
|
4ac1425f76 | ||
|
|
a721e82a4f | ||
|
|
98845e6cae | ||
|
|
19645248ab | ||
|
|
255c51c8a0 | ||
|
|
bbf3490e9c | ||
|
|
c7fa4b6d02 | ||
|
|
17152c84dc | ||
|
|
894687c0e0 | ||
|
|
fb08f03765 | ||
|
|
faa7a38a7d | ||
|
|
d5bbc321f9 | ||
|
|
ebd82853dd | ||
|
|
6142f3a297 | ||
|
|
63366a4ec2 | ||
|
|
55fa7eae71 | ||
|
|
a54e16488c | ||
|
|
b9c89b701f | ||
|
|
311fcb1813 | ||
|
|
aa4205ff7a | ||
|
|
937f4593d1 | ||
|
|
4da306b9f7 | ||
|
|
a5ffed4cd6 | ||
|
|
6ab6dacd1b | ||
|
|
d277139dd5 | ||
|
|
7af8daa341 | ||
|
|
d67100ad5e | ||
|
|
10829d65aa | ||
|
|
cd425502ae | ||
|
|
541f9aa5ee | ||
|
|
c6ebba85fb | ||
|
|
f94a1ffe11 | ||
|
|
6ada8d425c | ||
|
|
f03fc24098 | ||
|
|
756c736e21 | ||
|
|
e09db0f1bd | ||
|
|
c546a33eff | ||
|
|
da4be500da | ||
|
|
b404a91ada | ||
|
|
728584240c | ||
|
|
3b1a89ce86 | ||
|
|
c84a1bb74c | ||
|
|
bf7fb0732c | ||
|
|
dc30825232 | ||
|
|
40f5eb8334 | ||
|
|
fac72edd55 | ||
|
|
cdc32d1f12 | ||
|
|
8f4b34f8f1 | ||
|
|
86fde6d7dc | ||
|
|
2f06043559 | ||
|
|
fc9dd234e9 | ||
|
|
645c333787 | ||
|
|
efa05f42bc | ||
|
|
940febc3e8 | ||
|
|
3f62bb2d6e | ||
|
|
e95b2ba217 | ||
|
|
20633972e9 | ||
|
|
3457fe18cf | ||
|
|
fe3de498c8 | ||
|
|
15134ea04b | ||
|
|
a9851272ae | ||
|
|
916c9d62d9 | ||
|
|
47b4823bc5 | ||
|
|
c72f9f5016 | ||
|
|
e747ee5cbc | ||
|
|
ba5b29a35d | ||
|
|
daa6230fc9 | ||
|
|
4765752ee3 | ||
|
|
25e23c89c3 | ||
|
|
08dbfead7c | ||
|
|
8f5d7ed3cf | ||
|
|
807ae6a8f8 | ||
|
|
5a7d7d88ff | ||
|
|
40ae8a8add | ||
|
|
6488e81901 | ||
|
|
d328f08952 | ||
|
|
bcad357017 | ||
|
|
9845a7cd68 | ||
|
|
7c632f7388 | ||
|
|
1442f62633 | ||
|
|
7f7a84e3ba | ||
|
|
dee3279f85 | ||
|
|
094dbb01b8 | ||
|
|
e299d40fa1 | ||
|
|
cb42f11b97 | ||
|
|
4531645b4c | ||
|
|
bf3426f91b | ||
|
|
3864f04218 | ||
|
|
e541a35e86 | ||
|
|
bd383ed273 | ||
|
|
949fac1a7c | ||
|
|
7ab16ecccb | ||
|
|
58cd551a30 | ||
|
|
7770a9b521 | ||
|
|
aab0f7ea46 | ||
|
|
ea41f4f503 | ||
|
|
af7219f6e1 | ||
|
|
8ccaec7af6 | ||
|
|
b2ffc90da7 | ||
|
|
72e4d0b896 | ||
|
|
5bb13e34f8 | ||
|
|
0466b86f10 | ||
|
|
ca81f68ff1 | ||
|
|
4995e49c46 | ||
|
|
77123d8c92 | ||
|
|
e72c1c13ea | ||
|
|
e775d68a9b | ||
|
|
f8e5f1a5a2 | ||
|
|
f4a74f058d | ||
|
|
80821fca07 | ||
|
|
dfd4451561 | ||
|
|
8570b02dde | ||
|
|
d6da406c61 | ||
|
|
060e789662 | ||
|
|
e40b9a3cf5 | ||
|
|
237d9944f9 | ||
|
|
79b46bf210 | ||
|
|
98a26f9ae4 | ||
|
|
dbe3c5cfb9 | ||
|
|
25d7f7b2a4 | ||
|
|
b76e99ac0f | ||
|
|
67c3677621 | ||
|
|
bab6316e76 | ||
|
|
48730bca05 | ||
|
|
698b570ee6 | ||
|
|
a3f11dff18 | ||
|
|
5e359c2fb8 | ||
|
|
2147eae687 | ||
|
|
286295128d | ||
|
|
8501495c17 | ||
|
|
888c9166fc | ||
|
|
e5f6edf405 | ||
|
|
e8a52f717d | ||
|
|
d45750428c | ||
|
|
cf55ca9e28 | ||
|
|
3e5239f7d3 | ||
|
|
7669985f8e | ||
|
|
5047c9b6e7 |
@@ -1,7 +1,33 @@
|
||||
[*.{js,jsx,ts,tsx,vue,sh}]
|
||||
root = true # Top-most EditorConfig file
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
|
||||
[*.{js,jsx,ts,tsx,vue,sh,scss}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
max_line_length = 100
|
||||
|
||||
[{Dockerfile}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.py]
|
||||
indent_size = 4 # PEP 8 (the official Python style guide) recommends using 4 spaces per indentation level
|
||||
indent_style = space
|
||||
max_line_length = 100
|
||||
|
||||
[*.ps1]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{scss}] # SASS guidelines: https://archive.today/2024.02.16-232553/https://sass-guidelin.es/
|
||||
indent_style = space
|
||||
indent_size = 2 # Recommended by SASS guidelines
|
||||
max_line_length = 100 # Recommended by SASS guidelines
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
@@ -9,14 +9,15 @@ module.exports = {
|
||||
es2022: true, // add globals and sets parserOptions.ecmaVersion to 2022
|
||||
},
|
||||
extends: [
|
||||
// Vue specific rules, eslint-plugin-vue
|
||||
'plugin:vue/essential',
|
||||
// Vue specific base rules, `eslint-plugin-vue`
|
||||
'plugin:vue/vue3-recommended',
|
||||
|
||||
// Extends eslint-config-airbnb
|
||||
// Extends `eslint-config-airbnb`
|
||||
'@vue/eslint-config-airbnb-with-typescript',
|
||||
|
||||
// Extends @typescript-eslint/recommended
|
||||
// Uses the recommended rules from the @typescript-eslint/eslint-plugin
|
||||
// - Sets base parser and plugin options.
|
||||
// - Includes `plugin:@typescript-eslint/recommended`. But incompatible with
|
||||
// `strict-type-checked` and `stylistic-type-checked`, see https://github.com/vuejs/eslint-config-typescript/issues/67.
|
||||
'@vue/typescript/recommended',
|
||||
],
|
||||
rules: {
|
||||
|
||||
@@ -5,71 +5,56 @@ 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).
|
||||
Thank you for contributing to privacy.sexy! 🌟
|
||||
For guidance, see our script guidelines: https://github.com/undergroundwires/privacy.sexy/blob/master/docs/script-guidelines.md.
|
||||
Consider submitting a PR for faster implementation: https://github.com/undergroundwires/privacy.sexy/blob/master/CONTRIBUTING.md#extend-scripts.
|
||||
-->
|
||||
|
||||
### OS
|
||||
### Operating system
|
||||
|
||||
<!--
|
||||
Which OS will the new script configure?
|
||||
One of the supported OSes: "Windows", "macOS" or "Linux".
|
||||
Specify the OS: Windows, macOS, or Linux.
|
||||
-->
|
||||
|
||||
### Name
|
||||
|
||||
<!--
|
||||
The name of the script.
|
||||
It should start with an imperative noun such as "disable", "turn off" , "clear"...
|
||||
E.g. "Disable webcam telemetry"
|
||||
Suggest a name for the script.
|
||||
Naming conventions: https://github.com/undergroundwires/privacy.sexy/blob/master/docs/script-guidelines.md#name.
|
||||
-->
|
||||
|
||||
### Script code
|
||||
### Code
|
||||
|
||||
<!--
|
||||
Code that will be executed when script is selected.
|
||||
Try to keep it as simple and backwards-compatible as possible.
|
||||
Allowed languages:
|
||||
- Windows: PowerShell (ps1) or batchfile
|
||||
- 💡 Prioritize the one that's simpler, batchfile if similar.
|
||||
- macOS: bash (sh)
|
||||
- Linux: bash (sh) or Python 3
|
||||
- 💡 Prioritize the one that's simpler, bash if similar.
|
||||
Provide or explain the code to execute when the script runs.
|
||||
Code guidelines: https://github.com/undergroundwires/privacy.sexy/blob/master/docs/script-guidelines.md#code.
|
||||
-->
|
||||
|
||||
### 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).
|
||||
Include code to revert changes to the default state.
|
||||
Leave blank for non-reversible scripts.
|
||||
-->
|
||||
|
||||
### Suggested category
|
||||
### 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.
|
||||
Suggest a category for the script.
|
||||
If unsure, leave blank for maintainers to decide.
|
||||
-->
|
||||
|
||||
### Suggested recommendation level
|
||||
### 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.
|
||||
Suggest a recommendation level: STANDARD (non-breaking), STRICT (limits functionality), or NONE (for advanced users).
|
||||
If unsure, leave blank for maintainers to decide.
|
||||
-->
|
||||
|
||||
### Additional documentation/references
|
||||
### 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.
|
||||
<!--
|
||||
Provide any relevant documentation or references.
|
||||
Prefer high-quality sources such as vendor documentation.
|
||||
Documentation guidelines: https://github.com/undergroundwires/privacy.sexy/blob/master/docs/script-guidelines.md#documentation.
|
||||
-->
|
||||
|
||||
@@ -8,4 +8,5 @@ runs:
|
||||
-
|
||||
name: Run `npm ci` with retries
|
||||
shell: bash
|
||||
run: npm run install-deps -- --ci --root-directory "${{ inputs.working-directory }}"
|
||||
run: npm run install-deps -- --ci
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
4
.github/actions/setup-node/action.yml
vendored
4
.github/actions/setup-node/action.yml
vendored
@@ -3,6 +3,6 @@ runs:
|
||||
steps:
|
||||
-
|
||||
name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: 18.x
|
||||
|
||||
36
.github/workflows/checks.build.yaml
vendored
36
.github/workflows/checks.build.yaml
vendored
@@ -1,4 +1,4 @@
|
||||
name: build-checks
|
||||
name: checks.build
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Setup node
|
||||
uses: ./.github/actions/setup-node
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Setup node
|
||||
uses: ./.github/actions/setup-node
|
||||
@@ -68,3 +68,33 @@ jobs:
|
||||
-
|
||||
name: Verify bundled desktop build artifacts
|
||||
run: npm run check:verify-build-artifacts -- --electron-bundled
|
||||
|
||||
build-docker:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ macos, ubuntu ] # Windows runners do not support Linux containers
|
||||
fail-fast: false # Allows to see results from other combinations
|
||||
runs-on: ${{ matrix.os }}-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Install Docker on macOS
|
||||
if: matrix.os == 'macos' # macOS runner is missing Docker
|
||||
run: |-
|
||||
# Install Docker
|
||||
brew install docker
|
||||
# Docker on macOS misses daemon due to licensing, so install colima as runtime
|
||||
brew install colima
|
||||
# Start the daemon
|
||||
colima start
|
||||
-
|
||||
name: Build Docker image
|
||||
run: docker build -t undergroundwires/privacy.sexy:latest .
|
||||
-
|
||||
name: Run Docker image on port 8080
|
||||
run: docker run -d -p 8080:80 --rm --name privacy.sexy undergroundwires/privacy.sexy:latest
|
||||
-
|
||||
name: Check server is up and returns HTTP 200
|
||||
run: node ./scripts/verify-web-server-status.js --url http://localhost:8080
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Setup node
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
7
.github/workflows/checks.external-urls.yaml
vendored
7
.github/workflows/checks.external-urls.yaml
vendored
@@ -1,6 +1,7 @@
|
||||
name: checks.external-urls
|
||||
|
||||
on:
|
||||
push:
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # at 00:00 on every Sunday
|
||||
|
||||
@@ -10,7 +11,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Setup node
|
||||
uses: ./.github/actions/setup-node
|
||||
@@ -20,3 +21,7 @@ jobs:
|
||||
-
|
||||
name: Test
|
||||
run: npm run check:external-urls
|
||||
env:
|
||||
RANDOMIZED_URL_CHECK_LIMIT: "${{ github.event_name == 'push' && '10' || '' }}"
|
||||
# - Scheduled checks has no limits, ensuring thorough testing.
|
||||
# - For push events, triggered by code changes, the amount of URLs are limited to provide quick feedback.
|
||||
|
||||
2
.github/workflows/checks.quality.yaml
vendored
2
.github/workflows/checks.quality.yaml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Setup node
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
4
.github/workflows/checks.scripts.yaml
vendored
4
.github/workflows/checks.scripts.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Setup node
|
||||
uses: ./.github/actions/setup-node
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Setup node
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: security-checks
|
||||
name: checks.security.dependencies
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Setup node
|
||||
uses: ./.github/actions/setup-node
|
||||
42
.github/workflows/checks.security.sast.yaml
vendored
Normal file
42
.github/workflows/checks.security.sast.yaml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: checks.security.sast
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # at 00:00 on every Sunday
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [
|
||||
javascript # analyzes code written in JavaScript, TypeScript and both.
|
||||
]
|
||||
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: +security-and-quality
|
||||
-
|
||||
name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
-
|
||||
name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
||||
3
.github/workflows/release.desktop.yaml
vendored
3
.github/workflows/release.desktop.yaml
vendored
@@ -14,12 +14,13 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}-latest
|
||||
steps:
|
||||
-
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: master # otherwise it defaults to the version tag missing bump commit
|
||||
fetch-depth: 0 # fetch all history
|
||||
-
|
||||
name: Checkout to bump commit
|
||||
shell: bash
|
||||
run: git checkout "$(git rev-list "${{ github.event.release.tag_name }}"..master | tail -1)"
|
||||
-
|
||||
name: Setup node
|
||||
|
||||
6
.github/workflows/release.site.yaml
vendored
6
.github/workflows/release.site.yaml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: "Infrastructure: Checkout"
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: aws
|
||||
repository: undergroundwires/aws-static-site-with-cd
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
working-directory: aws
|
||||
-
|
||||
name: "App: Checkout"
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: app
|
||||
ref: master # otherwise we don't get version bump commit
|
||||
@@ -102,7 +102,7 @@ jobs:
|
||||
-
|
||||
name: "App: Deploy to S3"
|
||||
shell: bash
|
||||
run: >-
|
||||
run: |-
|
||||
declare web_output_dir
|
||||
if ! web_output_dir=$(cd app && node scripts/print-dist-dir.js --web); then
|
||||
echo 'Error: Could not determine distribution directory.'
|
||||
|
||||
40
.github/workflows/tests.e2e.yaml
vendored
40
.github/workflows/tests.e2e.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Setup node
|
||||
uses: ./.github/actions/setup-node
|
||||
@@ -24,3 +24,41 @@ jobs:
|
||||
-
|
||||
name: Run e2e tests
|
||||
run: npm run test:cy:run
|
||||
-
|
||||
name: Output artifact directories
|
||||
id: artifacts
|
||||
if: always() # Run even if previous steps fail because test run video is always captured
|
||||
shell: bash
|
||||
run: |-
|
||||
declare -r dirs_json_file='cypress-dirs.json'
|
||||
if [ ! -f "${dirs_json_file}" ]; then
|
||||
echo "${dirs_json_file} does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SCREENSHOTS_DIR=$(jq -r '.screenshots' "${dirs_json_file}")
|
||||
VIDEOS_DIR=$(jq -r '.videos' "${dirs_json_file}")
|
||||
|
||||
for dir in "${SCREENSHOTS_DIR}" "${VIDEOS_DIR}"; do
|
||||
if [ "${dir}" = 'null' ] || [ -z "${dir}" ]; then
|
||||
echo "One or more directories are null or not specified in cypress-dirs.json"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "SCREENSHOTS_DIR=${SCREENSHOTS_DIR}" >> "${GITHUB_OUTPUT}"
|
||||
echo "VIDEOS_DIR=${VIDEOS_DIR}" >> "${GITHUB_OUTPUT}"
|
||||
-
|
||||
name: Upload screenshots
|
||||
if: failure() # Run only if previous steps fail because screenshots will be generated only if E2E test failed
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: e2e-screenshots-${{ matrix.os }}
|
||||
path: ${{ steps.artifacts.outputs.SCREENSHOTS_DIR }}
|
||||
-
|
||||
name: Upload videos
|
||||
if: always() # Run even if previous steps fail because test run video is always captured
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: e2e-videos-${{ matrix.os }}
|
||||
path: ${{ steps.artifacts.outputs.VIDEOS_DIR }}
|
||||
|
||||
2
.github/workflows/tests.integration.yaml
vendored
2
.github/workflows/tests.integration.yaml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Setup node
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
2
.github/workflows/tests.unit.yaml
vendored
2
.github/workflows/tests.unit.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set-up node
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
17
.gitignore
vendored
17
.gitignore
vendored
@@ -1,5 +1,16 @@
|
||||
node_modules
|
||||
# Application build artifacts
|
||||
/dist-*/
|
||||
.vs
|
||||
|
||||
# npm
|
||||
node_modules
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/**/*
|
||||
!.vscode/extensions.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# draw.io
|
||||
*.bkp
|
||||
*.dtmp
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@@ -16,7 +16,8 @@
|
||||
// Scripting
|
||||
"timonwong.shellcheck", // Lints bash files.
|
||||
"ms-vscode.powershell", // Lints PowerShell files.
|
||||
"ms-python.python", // Lints Python files.
|
||||
"ms-python.python", // Python IntelliSense, debugging, and basic linting.
|
||||
"ms-python.pylint", // Lints Python files
|
||||
// Distribution
|
||||
"ms-azuretools.vscode-docker" // Adds Docker support.
|
||||
]
|
||||
|
||||
178
CHANGELOG.md
178
CHANGELOG.md
@@ -1,5 +1,183 @@
|
||||
# Changelog
|
||||
|
||||
## 0.13.0 (2024-02-11)
|
||||
|
||||
* win: add disabling clipboard features #251, #247 | [c6ebba8](https://github.com/undergroundwires/privacy.sexy/commit/c6ebba85fb1b362be0d81d3078f19db71e0528b2)
|
||||
* win: improve search privacy scripts #117 | [541f9aa](https://github.com/undergroundwires/privacy.sexy/commit/541f9aa5ee1b5f4885063b65beaf6cd873f0d786)
|
||||
* win: add disabling Windows Copilot #263, #266 | [cd42550](https://github.com/undergroundwires/privacy.sexy/commit/cd425502ae882bba9642dc2171c2b5771946b5a9)
|
||||
* win: add Dropbox telemetry blocking #125, #118 | [10829d6](https://github.com/undergroundwires/privacy.sexy/commit/10829d65aa3fb0df937bb8829244e6290bb748c7)
|
||||
* Improve selection type documentation | [7af8daa](https://github.com/undergroundwires/privacy.sexy/commit/7af8daa3411b24efb6385c7876a49bd372753f38)
|
||||
* Expand script names to take full available width | [d277139](https://github.com/undergroundwires/privacy.sexy/commit/d277139dd50eeb4e4057b0a7d8fc4ac2d70785de)
|
||||
* Limit tooltip width for improved readability | [6ab6dac](https://github.com/undergroundwires/privacy.sexy/commit/6ab6dacd1be2d7bf1863b07b121d86f2a379ac67)
|
||||
* Add markdown support for script/category names | [a5ffed4](https://github.com/undergroundwires/privacy.sexy/commit/a5ffed4cd60d9d058d5374145c1176b10fad1660)
|
||||
* Normalize and improve font sizes | [4da306b](https://github.com/undergroundwires/privacy.sexy/commit/4da306b9f79b0bb7a64bb197fb246258cf435b8d)
|
||||
* Change 'revert' button to title case | [937f459](https://github.com/undergroundwires/privacy.sexy/commit/937f4593d1a91081ab6b1bcb8f85d03879d7cf07)
|
||||
* Remove playful emojis (🍑🍆) | [aa4205f](https://github.com/undergroundwires/privacy.sexy/commit/aa4205ff7af7d05cfb5e82bf541b521d49bbd1c8)
|
||||
* Improve UI code styling for all platforms | [311fcb1](https://github.com/undergroundwires/privacy.sexy/commit/311fcb18133d1343f6a9ae5bd7a25795a1d12c49)
|
||||
* Render bracket references as superscript text | [b9c89b7](https://github.com/undergroundwires/privacy.sexy/commit/b9c89b701fc77d20dcc706419a8659ad156c4fc2)
|
||||
* Change slogan and refactor project info naming | [a54e164](https://github.com/undergroundwires/privacy.sexy/commit/a54e16488ce32219bcf811b5da85f06584b293fb)
|
||||
* Add 'Revert All Selection' feature #68 | [55fa7ea](https://github.com/undergroundwires/privacy.sexy/commit/55fa7eae71031357d6f03f0d349a09cd446270d3)
|
||||
* win, mac, linux: add privacy.sexy cleanup scripts | [63366a4](https://github.com/undergroundwires/privacy.sexy/commit/63366a4ec2533a376849d692211e9972b56ab4a8)
|
||||
* Extend search by including documentation content | [6142f3a](https://github.com/undergroundwires/privacy.sexy/commit/6142f3a2973d20493f784f323f3be57fa8deaeef)
|
||||
* Remove 'preview' label from Linux options | [ebd8285](https://github.com/undergroundwires/privacy.sexy/commit/ebd82853ddc56f1cc2fc9be3fe0b3001b07f0186)
|
||||
* Change fonts for improved readability | [d5bbc32](https://github.com/undergroundwires/privacy.sexy/commit/d5bbc321f902dc60618ffdfda0d583a4a433f7af)
|
||||
* Apply global styles for visual consistency | [faa7a38](https://github.com/undergroundwires/privacy.sexy/commit/faa7a38a7d16390f27e4a3e51017b81665cf85ca)
|
||||
* Add UI animations for expand/collapse actions | [fb08f03](https://github.com/undergroundwires/privacy.sexy/commit/fb08f037651e1a7d453b9a6af724cbccecc5b903)
|
||||
* win: relocate service disabling and improve docs | [894687c](https://github.com/undergroundwires/privacy.sexy/commit/894687c0e0375a24f40bcd720ea69c9b2aa62a58)
|
||||
* win: add host blocking category #26 | [17152c8](https://github.com/undergroundwires/privacy.sexy/commit/17152c84dc639e75560998a6feddfd46e0f713ce)
|
||||
* Update meta title and description | [c7fa4b6](https://github.com/undergroundwires/privacy.sexy/commit/c7fa4b6d020ac6fd3bf72bb4e57022dffb1ba921)
|
||||
|
||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.12.10...0.13.0)
|
||||
|
||||
## 0.12.10 (2024-01-17)
|
||||
|
||||
* Fix CSP for Vue, Ace, Vite, Safari compatibility | [940febc](https://github.com/undergroundwires/privacy.sexy/commit/940febc3e80cfd0c01b5cc8282ebaab6b024d1b5)
|
||||
* Improve security by isolating code execution more | [efa05f4](https://github.com/undergroundwires/privacy.sexy/commit/efa05f42bc53c44a352152b7c272bc0bda363070)
|
||||
* Fix unresponsive circle icon in revert button | [645c333](https://github.com/undergroundwires/privacy.sexy/commit/645c33378769969c525a1552c65f8d0005e25fcf)
|
||||
* Improve documentation for contribution guidelines | [fc9dd23](https://github.com/undergroundwires/privacy.sexy/commit/fc9dd234e9c749247f42289432ebb92dbe0a5f64)
|
||||
* Bump Node.js environment to 18.x | [2f06043](https://github.com/undergroundwires/privacy.sexy/commit/2f0604355988a421690bb275375c3df280af7ee6)
|
||||
* Fix button inconsistencies and macOS layout shifts | [86fde6d](https://github.com/undergroundwires/privacy.sexy/commit/86fde6d7dc61bbeeb3088cd24e37451181cc4e01)
|
||||
* win: fix language dependent delete script #149 | [8f4b34f](https://github.com/undergroundwires/privacy.sexy/commit/8f4b34f8f156476f56fb7dde8e7c762f4455518b)
|
||||
* Improve desktop script runs with timestamps & logs | [cdc32d1](https://github.com/undergroundwires/privacy.sexy/commit/cdc32d1f12c938966238c9569c91b64b23cd6f26)
|
||||
* win: improve store app docs and add research #279 | [fac72ed](https://github.com/undergroundwires/privacy.sexy/commit/fac72edd551264320ed97194e7ecb3fcc34139f7)
|
||||
* Fix handling special chars in script paths | [40f5eb8](https://github.com/undergroundwires/privacy.sexy/commit/40f5eb8334b27e958eee63e2141ded7d5861d960)
|
||||
* Fix macOS detection in desktop app and Chromium | [dc30825](https://github.com/undergroundwires/privacy.sexy/commit/dc30825232a1355a325e364c8cd9fde78ffa3b1a)
|
||||
* Bump ESLint Typescript dependencies to latest | [bf7fb07](https://github.com/undergroundwires/privacy.sexy/commit/bf7fb0732c52745521c1a89b963bdbf3394d9e63)
|
||||
* Fix script deletion during execution on desktop | [c84a1bb](https://github.com/undergroundwires/privacy.sexy/commit/c84a1bb74ccb7a53bd493684b63a9e04f40e0b8b)
|
||||
* Fix script execution for Linux VSCode development | [3b1a89c](https://github.com/undergroundwires/privacy.sexy/commit/3b1a89ce863c18c32be7d0b22dba566f692d81d1)
|
||||
* Fix touch, cursor and accessibility in slider | [7285842](https://github.com/undergroundwires/privacy.sexy/commit/728584240cae6b3857abca4d3ddaaa7f6bb4a66e)
|
||||
* Fix invisible script execution on Windows #264 | [b404a91](https://github.com/undergroundwires/privacy.sexy/commit/b404a91ada509e19a287d026d55db0035ff6233b)
|
||||
* win: add missing extension apps, improve docs #279 | [da4be50](https://github.com/undergroundwires/privacy.sexy/commit/da4be500da7b0b5897a8b3e0525d9e50c9159fe0)
|
||||
* Show native save dialogs in desktop app #50, #264 | [c546a33](https://github.com/undergroundwires/privacy.sexy/commit/c546a33eff7506550c7bcf03bb1f227a7c091816)
|
||||
* Show save/execution error dialogs on desktop #264 | [e09db0f](https://github.com/undergroundwires/privacy.sexy/commit/e09db0f1bd73503204d8e5375a9cfe693f174a57)
|
||||
* Add Windows save instructions UI and fix URL #296 | [756c736](https://github.com/undergroundwires/privacy.sexy/commit/756c736e21d713b8d2651cf2a9d7cf0678badde0)
|
||||
* Add AD detection on desktop app #264, #304 | [f03fc24](https://github.com/undergroundwires/privacy.sexy/commit/f03fc2409832ddf904bc6bd4e19274a8d40745dc)
|
||||
* Improve script error dialogs #304 | [6ada8d4](https://github.com/undergroundwires/privacy.sexy/commit/6ada8d425c4a7df88490756187c84b5c57ed1dcc)
|
||||
|
||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.12.9...0.12.10)
|
||||
|
||||
## 0.12.9 (2023-12-16)
|
||||
|
||||
* win: improve docs and category of jump lists #146 | [40ae8a8](https://github.com/undergroundwires/privacy.sexy/commit/40ae8a8addaeb834ee26eabd330fda5cbb495324)
|
||||
* mac: improve clearing privacy permissions | [5a7d7d8](https://github.com/undergroundwires/privacy.sexy/commit/5a7d7d88ff2f3e8862b18c94d062f692ee4b690b)
|
||||
* win: fix logic for terminating processes | [807ae6a](https://github.com/undergroundwires/privacy.sexy/commit/807ae6a8f8ca724d781169f3ecb40f43ccd3fe10)
|
||||
* win: improve documentation for "Get Help" app #280 | [8f5d7ed](https://github.com/undergroundwires/privacy.sexy/commit/8f5d7ed3cfa57f66dded9b72374006c9b6df2ce9)
|
||||
* Centralize log file and refactor desktop logging | [08dbfea](https://github.com/undergroundwires/privacy.sexy/commit/08dbfead7ca7b55fe85f7dded01f2d4b88906c72)
|
||||
* win: fix revert and improve docs for SAM enum #255 | [25e23c8](https://github.com/undergroundwires/privacy.sexy/commit/25e23c89c3f86897d5661a24a774997c924d3b2d)
|
||||
* Improve security and reliability of macOS updates | [4765752](https://github.com/undergroundwires/privacy.sexy/commit/4765752ee3a36301b3d97317c570432424de8460)
|
||||
* win: fix Win 11 Windows Security app removal #195 | [daa6230](https://github.com/undergroundwires/privacy.sexy/commit/daa6230fc96f2cf7210bc8c165106c0d5544e5fb)
|
||||
* Improve security and privacy with strict meta tags | [ba5b29a](https://github.com/undergroundwires/privacy.sexy/commit/ba5b29a35dd7665aeea430aec4aaa8ff5ca811de)
|
||||
* win: document and discourage admin shares #249 | [e747ee5](https://github.com/undergroundwires/privacy.sexy/commit/e747ee5cbc7cf5f0fe28a87fe7d02457d777373e)
|
||||
* win: discourage XboxIdentityProvider #64, #79 #181 | [c72f9f5](https://github.com/undergroundwires/privacy.sexy/commit/c72f9f501680c1d880a0b560d02451a9e31063b4)
|
||||
* win: improve disabling update healing #272 | [47b4823](https://github.com/undergroundwires/privacy.sexy/commit/47b4823bc5e487188b12cbea67db2525260af497)
|
||||
* Fix tooltip overflow on smaller screens | [916c9d6](https://github.com/undergroundwires/privacy.sexy/commit/916c9d62d9fce27c3cd3feaf90c66df584d4f04a)
|
||||
* Fix touch state not being activated in iOS Safari | [a985127](https://github.com/undergroundwires/privacy.sexy/commit/a9851272ae14eb1b374767b0eed3eb68e6dd1560)
|
||||
* Fix tree view alignment and padding issues | [15134ea](https://github.com/undergroundwires/privacy.sexy/commit/15134ea04bc46e8cb13977d75b788f5ff71c800e)
|
||||
* win: improve disabling of Application Experience | [fe3de49](https://github.com/undergroundwires/privacy.sexy/commit/fe3de498c8a1394efd6517d436797a08f938bb57)
|
||||
* Fix OS switching not working on tree view UI | [3457fe1](https://github.com/undergroundwires/privacy.sexy/commit/3457fe18cf8193883f45b50ecbc9638c91ace2fb)
|
||||
* Fix touch-enabled Chromium highlight on tree nodes | [2063397](https://github.com/undergroundwires/privacy.sexy/commit/20633972e9b56bdc102357129e74df30a95cefa9)
|
||||
* win: add scripts to postpone auto-updates #272 | [e95b2ba](https://github.com/undergroundwires/privacy.sexy/commit/e95b2ba2179e40c0033a51b0087871dbfdc32d78)
|
||||
|
||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.12.8...0.12.9)
|
||||
|
||||
## 0.12.8 (2023-11-27)
|
||||
|
||||
* Remove duplicated `index.html` file | [aab0f7e](https://github.com/undergroundwires/privacy.sexy/commit/aab0f7ea4680f377c610066bd0e99011eed8b506)
|
||||
* Refactor DI for simplicity and type safety | [7770a9b](https://github.com/undergroundwires/privacy.sexy/commit/7770a9b5211d7208cfb2bfa5f737d46dc90b7946)
|
||||
* Refactor user selection state handling using hook | [58cd551](https://github.com/undergroundwires/privacy.sexy/commit/58cd551a304a03e42637e6858982f8c5dfd9f598)
|
||||
* Refactor watch sources for reliability | [7ab16ec](https://github.com/undergroundwires/privacy.sexy/commit/7ab16ecccb31b2d54e5b634520a8246fbbc248c1)
|
||||
* Refactor to enforce strictNullChecks | [949fac1](https://github.com/undergroundwires/privacy.sexy/commit/949fac1a7cbc962ed63058e6a896695cfb4d35c8)
|
||||
* Fix icon tooltip alignment on instructions modal | [bd383ed](https://github.com/undergroundwires/privacy.sexy/commit/bd383ed273ca95c10ea1cce765c0aa6836ec508c)
|
||||
* Fix mobile layout overflow caused by tooltips | [e541a35](https://github.com/undergroundwires/privacy.sexy/commit/e541a35e86c0eff83f84dd002b46de7c55ebbcac)
|
||||
* win: improve disabling of scheduled tasks | [3864f04](https://github.com/undergroundwires/privacy.sexy/commit/3864f042180f62afe469fdfe36010b018f84f4b3)
|
||||
* Fix card list UI layout shifts (jumps) on load | [bf3426f](https://github.com/undergroundwires/privacy.sexy/commit/bf3426f91b6b7dbcad58d58507222559a8d14242)
|
||||
* Refactor to Vue 3 recommended ESLint rules | [4531645](https://github.com/undergroundwires/privacy.sexy/commit/4531645b4c0c5143f15240652368bb9b9ddb48a4)
|
||||
* Fix code highlighting and optimize category select | [cb42f11](https://github.com/undergroundwires/privacy.sexy/commit/cb42f11b9785e74719338a0a80a50d81dfccb4b6)
|
||||
* Fix layout jumps/shifts and overflow on modals | [e299d40](https://github.com/undergroundwires/privacy.sexy/commit/e299d40fa1d71d921d4dac37e469fe299c9da3af)
|
||||
* win: fix and improve Store app categorization #190 | [094dbb0](https://github.com/undergroundwires/privacy.sexy/commit/094dbb01b83bce9925fafab778b922f64390c2be)
|
||||
* win: fix persistent update disabling /w tasks #272 | [dee3279](https://github.com/undergroundwires/privacy.sexy/commit/dee3279f85c99a9c62201a093b1afa41ec2412ec)
|
||||
* win: discourage IntelliCode disabling #267, #286 | [7f7a84e](https://github.com/undergroundwires/privacy.sexy/commit/7f7a84e3ba259fade22d4838563d16129a1585e6)
|
||||
* Fix spacing in documentation for readability | [1442f62](https://github.com/undergroundwires/privacy.sexy/commit/1442f626335e30e3a8d74e4e13e561c41f073ef8)
|
||||
* win: fix system app removal affecting updates #287 | [7c632f7](https://github.com/undergroundwires/privacy.sexy/commit/7c632f738853b32fd90952bb4ca1ac924f962eb0)
|
||||
* Fix rendering of inline code blocks for docs | [9845a7c](https://github.com/undergroundwires/privacy.sexy/commit/9845a7cd68a9920c96da739b58238bb1fdb1251d)
|
||||
* linux: fix Firefox settings not reverting #282 | [bcad357](https://github.com/undergroundwires/privacy.sexy/commit/bcad357017d9f29ce77e706ca943107dd9caefb6)
|
||||
* Fix incorrect URL rendering in documentation texts | [d328f08](https://github.com/undergroundwires/privacy.sexy/commit/d328f0895244d998e885ad8df335b6444b9ac66b)
|
||||
|
||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.12.7...0.12.8)
|
||||
|
||||
## 0.12.7 (2023-11-07)
|
||||
|
||||
* Add winget download instructions | [b2ffc90](https://github.com/undergroundwires/privacy.sexy/commit/b2ffc90da70367b9e65c82556e8f440f865ceb98)
|
||||
* Fix unresponsive copy button on instructions modal | [8ccaec7](https://github.com/undergroundwires/privacy.sexy/commit/8ccaec7af6ea3ecfd46bab5c13b90f71d55e32c1)
|
||||
* Fix tree node check states not being updated | [af7219f](https://github.com/undergroundwires/privacy.sexy/commit/af7219f6e12ab4a65ce07190f691cf3234e87e35)
|
||||
|
||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.12.6...0.12.7)
|
||||
|
||||
## 0.12.6 (2023-11-03)
|
||||
|
||||
* Bump dependencies to latest | [25d7f7b](https://github.com/undergroundwires/privacy.sexy/commit/25d7f7b2a479e51e092881cc2751e67a7d3f179f)
|
||||
* win: improve system app uninstall cleanup #73 | [dbe3c5c](https://github.com/undergroundwires/privacy.sexy/commit/dbe3c5cfb91ba8a1657838b69117858843c8fbc8)
|
||||
* win: improve system app uninstall /w fallback #260 | [98a26f9](https://github.com/undergroundwires/privacy.sexy/commit/98a26f9ae47af2668aa53f39d1768983036048ce)
|
||||
* Improve performance of rendering during search | [79b46bf](https://github.com/undergroundwires/privacy.sexy/commit/79b46bf21004d96d31551439e5db5d698a3f71f3)
|
||||
* Fix YAML error for site release in CI/CD | [237d994](https://github.com/undergroundwires/privacy.sexy/commit/237d9944f900f5172366868d75219224ff0542b0)
|
||||
* win: fix Microsoft Advertising app removal #200 | [e40b9a3](https://github.com/undergroundwires/privacy.sexy/commit/e40b9a3cf53c341f2e84023a9f0e9680ac08f3fa)
|
||||
* win: improve directory cleanup security | [060e789](https://github.com/undergroundwires/privacy.sexy/commit/060e7896624309aebd25e8b190c127282de177e8)
|
||||
* Centralize Electron entry file path configuration | [d6da406](https://github.com/undergroundwires/privacy.sexy/commit/d6da406c61e5b9f5408851d1302d6d7398157a2e)
|
||||
* win: prevent updates from reinstalling apps #260 | [8570b02](https://github.com/undergroundwires/privacy.sexy/commit/8570b02dde14ffad64863f614682c3fc1f87b6c2)
|
||||
* win: improve script environment robustness #221 | [dfd4451](https://github.com/undergroundwires/privacy.sexy/commit/dfd44515613f38abe5a806bda36f44e7b715b50b)
|
||||
* Fix compiler failing with nested `with` expression | [80821fc](https://github.com/undergroundwires/privacy.sexy/commit/80821fca0769e5fd2c6338918fbdcea12fbe83d2)
|
||||
* win: improve soft file/app delete security #260 | [f4a74f0](https://github.com/undergroundwires/privacy.sexy/commit/f4a74f058db9b5bcbcbe438785db5ec88ecc1657)
|
||||
* Fix incorrect tooltip position after window resize | [f8e5f1a](https://github.com/undergroundwires/privacy.sexy/commit/f8e5f1a5a2afa1f18567e6d965359b6a1f082367)
|
||||
* linux: fix string formatting of Firefox configs | [e775d68](https://github.com/undergroundwires/privacy.sexy/commit/e775d68a9b4a5f9e893ff0e3500dade036185193)
|
||||
* win: improve file delete | [e72c1c1](https://github.com/undergroundwires/privacy.sexy/commit/e72c1c13ea2d73ebfc7a8da5a21254fdfc0e5b59)
|
||||
* win: change system app removal to hard delete #260 | [77123d8](https://github.com/undergroundwires/privacy.sexy/commit/77123d8c929d23676a9cb21d7b697703fd1b6e82)
|
||||
* Improve UI performance by optimizing reactivity | [4995e49](https://github.com/undergroundwires/privacy.sexy/commit/4995e49c469211404dac9fcb79b75eb121f80bce)
|
||||
* Migrate to Vue 3.0 #230 | [ca81f68](https://github.com/undergroundwires/privacy.sexy/commit/ca81f68ff1c3bbe5b22981096ae9220b0b5851c7)
|
||||
* win, linux: unify & improve Firefox clean-up #273 | [0466b86](https://github.com/undergroundwires/privacy.sexy/commit/0466b86f1013341c966a9bbf6513990337b16598)
|
||||
* win: fix store revert for multiple installs #260 | [5bb13e3](https://github.com/undergroundwires/privacy.sexy/commit/5bb13e34f8de2e2a7ba943ff72b12c0569435e62)
|
||||
|
||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.12.5...0.12.6)
|
||||
|
||||
## 0.12.5 (2023-10-13)
|
||||
|
||||
* Fix Docker build and improve checks #220 | [7669985](https://github.com/undergroundwires/privacy.sexy/commit/7669985f8e1446e726a95626ecf35b3ce6b60a16)
|
||||
* Add SAST security checks with SECURITY.md #178 | [3e5239f](https://github.com/undergroundwires/privacy.sexy/commit/3e5239f7d35e57749c01adf3dbbcd365aebb39c8)
|
||||
* Add Scoop download instructions #174 | [cf55ca9](https://github.com/undergroundwires/privacy.sexy/commit/cf55ca9e28b064fa7a516077a9da23e3a8e3f534)
|
||||
* win: fix and improve temp dir cleanup #176, #89 | [d457504](https://github.com/undergroundwires/privacy.sexy/commit/d45750428cca010daf2721b33a8ae3a01b28813b)
|
||||
* win, linux: improve VSCode setting robustness #196 | [e8a52f7](https://github.com/undergroundwires/privacy.sexy/commit/e8a52f717dc799b34ceeb1c27c2b8219391dff6a)
|
||||
* linux: fix obsolete Firefox DPI script #239 | [e5f6edf](https://github.com/undergroundwires/privacy.sexy/commit/e5f6edf405bcec7c29ea4d7932d1910620fa15f8)
|
||||
* win: add removal of Edge assocations #64 | [888c916](https://github.com/undergroundwires/privacy.sexy/commit/888c9166fc66a2094137fa8be739cc21bafef5f6)
|
||||
* win: improve Edge & OneDrive shortcut removal #73 | [8501495](https://github.com/undergroundwires/privacy.sexy/commit/8501495c170af61913288a63dbd369db5bbc5003)
|
||||
* win: relocate and document SecHealthUI #190 | [2862951](https://github.com/undergroundwires/privacy.sexy/commit/286295128d0179358e0c6b7b6415d752175a1aed)
|
||||
* Add developer toolkit UI component | [2147eae](https://github.com/undergroundwires/privacy.sexy/commit/2147eae687b82d05bc43bb4605d9068f148bb92a)
|
||||
* win: fix and improve network data usage reset #265 | [5e359c2](https://github.com/undergroundwires/privacy.sexy/commit/5e359c2fb82a08e6acf7159b70ca86a8234b359b)
|
||||
* win: improve app reversion and docs #260 | [a3f11df](https://github.com/undergroundwires/privacy.sexy/commit/a3f11dff187c821a00910c20dac05e285cda9073)
|
||||
* Fix working directory in CI/CD web release | [698b570](https://github.com/undergroundwires/privacy.sexy/commit/698b570ee6e300d6703015464f4345b5e706f1cb)
|
||||
* Implement new UI component for icons #230 | [48730bc](https://github.com/undergroundwires/privacy.sexy/commit/48730bca0506120bca4bf3a23545d59f2b1a9009)
|
||||
* win: fix and improve AppCompat disabling #255 | [bab6316](https://github.com/undergroundwires/privacy.sexy/commit/bab6316e7625230cf4a4cf67c3aca417347db75c)
|
||||
* win, linux, mac: fix typos and improve naming | [67c3677](https://github.com/undergroundwires/privacy.sexy/commit/67c3677621b201525a813e8a26f07d607176e89b)
|
||||
|
||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.12.4...0.12.5)
|
||||
|
||||
## 0.12.4 (2023-09-25)
|
||||
|
||||
* win: fix Windows spotlight revert, docs, recommend | [659fea7](https://github.com/undergroundwires/privacy.sexy/commit/659fea7afcabcd0ea273cfdcc8c4bae190c126f3)
|
||||
* win: fix Edge telemetry disabling for v116+ #242 | [6d301f9](https://github.com/undergroundwires/privacy.sexy/commit/6d301f99616ed49975876803d0098eafe4d3cb2e)
|
||||
* win: fix, improve disabling automatic updates #252 | [6e9b65d](https://github.com/undergroundwires/privacy.sexy/commit/6e9b65d8b1b481c1471dde90876c37838b4ac4e5)
|
||||
* win: refactor `update.mode` key for VSCode #215 | [c27172c](https://github.com/undergroundwires/privacy.sexy/commit/c27172c32e7c316b7cb0f44cab611eed89ca034e)
|
||||
* Fix wrong action path in website CI deployment | [a1f2497](https://github.com/undergroundwires/privacy.sexy/commit/a1f24973813ccbdd7e1f06c64e1912a991a6bb64)
|
||||
* Fix compiler bug with nested optional arguments | [53222fd](https://github.com/undergroundwires/privacy.sexy/commit/53222fd83c2846089746a217482195806f960d18)
|
||||
* Fix no spacing after lists in documentation text | [f810ed0](https://github.com/undergroundwires/privacy.sexy/commit/f810ed0c147c2a46cae3b70b635ed81128646fff)
|
||||
* Rewrite tooltip UI for efficiency and Vue 3.0 #230 | [8b930fc](https://github.com/undergroundwires/privacy.sexy/commit/8b930fc57c8ee6691ed6165bcb27d97e64a1a0c0)
|
||||
* win: fix uninstallation of newer Edge #236 | [60dde11](https://github.com/undergroundwires/privacy.sexy/commit/60dde11311a2409537f5965f370b0daaaec53339)
|
||||
* win: fix delivery optimization side-effects #173 | [203daeb](https://github.com/undergroundwires/privacy.sexy/commit/203daeb4a2fca0a0295cbc2a736394f9f87725e6)
|
||||
* win: fix Defender scan artifacts removal #246 | [cb21a97](https://github.com/undergroundwires/privacy.sexy/commit/cb21a970b6b867e1476a5eb8a72b9a7fdd53a744)
|
||||
* Fix outdated and broken links in README #161 | [0303ef2](https://github.com/undergroundwires/privacy.sexy/commit/0303ef2fd98b36306523e2a0c5f5ae812a4c6c99)
|
||||
* Fix loss of tree node state when switching views | [8f188ac](https://github.com/undergroundwires/privacy.sexy/commit/8f188acd3c2d93e40c89569c74bc5cff992f0052)
|
||||
* Fix slow appearance of nodes on tree view | [bd2082e](https://github.com/undergroundwires/privacy.sexy/commit/bd2082e8c574db065bb4462f30ea3ace2cb028cb)
|
||||
|
||||
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.12.3...0.12.4)
|
||||
|
||||
## 0.12.3 (2023-09-09)
|
||||
|
||||
* linux: use user.js over prefs.js for Firefox #232 | [dae6d11](https://github.com/undergroundwires/privacy.sexy/commit/dae6d114daab6857d773071211eb57619b136281)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 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,
|
||||
@@ -16,7 +16,7 @@ Your pull requests are actively welcomed. We collaborate using [GitHub flow](htt
|
||||
|
||||
The steps:
|
||||
|
||||
1. Fork the repo and create your branch from master.
|
||||
1. Fork the repository and create your branch from `master`.
|
||||
2. If you've added code that requires testing, add tests. See [tests.md](./docs/tests.md).
|
||||
3. If you've done a major change, update the documentation. See [docs/](./docs/).
|
||||
4. Ensure the test suite passes. See [development.md | Testing](./docs/development.md#testing) for commands.
|
||||
@@ -37,15 +37,44 @@ Automated pipelines will run to control your PR and they will publish your code
|
||||
|
||||
## Extend scripts
|
||||
|
||||
Here's quick information for you who want to add more scripts.
|
||||
If you're interested in adding new scripts to privacy.sexy:
|
||||
|
||||
You have two alternatives:
|
||||
1. Read [guidelines for a good script](./docs/script-guidelines.md)
|
||||
2. Choose one of two ways to contribute:
|
||||
1. [Create an issue](https://github.com/undergroundwires/privacy.sexy/issues/new/choose) requesting the addition of a new script. This allows other contributors to develop and add it for you. This will take longer time.
|
||||
2. Submit a pull request with your script. This is the faster route to seeing your script included in the project. Add your scripts to the appropriate OS directory in the [collections](src/application/collections/) (for syntax guidance, see [collection-files.md](docs/collection-files.md)) folder, and follow the [pull request process](#pull-request-process).
|
||||
|
||||
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).
|
||||
## Commit conventions
|
||||
|
||||
- Adhere to the 50/72 rule:
|
||||
- Commit titles should not exceed 50 characters.
|
||||
- Limit description lines to 72 characters, except for code blocks or inline codes.
|
||||
- Avoid including delta (such as `git diff` information) or a list of changed files in the commit message. This information is redundant as it's already part of the commit.
|
||||
- Focus on explaining the WHY and HOW of the changes, rather than WHAT changes are.
|
||||
- Begin the commit message with a concise summary of what the commit accomplishes.
|
||||
- Use imperative language in the commit title. For example, use "add" instead of "added".
|
||||
- Commit prefixes:
|
||||
- Prefix bug fixes with `fix:` or `Fix ...`.
|
||||
- For commits affecting scripts of specific operating systems:
|
||||
- Prefix the commit title with an OS-specific tag such as `win:` for Windows scripts, `mac:` for macOS scripts, and `linux:` for Linux scripts.
|
||||
- Combine prefixes for commits affecting more than one operating system, e.g., `win, mac: ...`.
|
||||
|
||||
## Versioning
|
||||
|
||||
We base versioning on the release's content rather than strictly following semantic versioning.
|
||||
|
||||
There are two main types of releases:
|
||||
|
||||
1. **Patch Releases:** These focus on minor UI improvements, bug fixes, refactorings, dependency updates, and documentation updates. For scripts, they involve adjusting recommendation levels, enhancing functionality, and dividing scripts for more precise control. Patch releases may ship minor feature additions if they are essential for fixing a bug. For these updates, we increment the patch number in the `MAJOR.MINOR.PATCH`.
|
||||
|
||||
2. **Feature Releases:** These releases bring significant updates that change how users interact with privacy.sexy. They include major UI enhancements, the introduction of new scripts, and features. For these updates, we increment the minor number in the `MAJOR.MINOR.PATCH`.
|
||||
|
||||
Maintainers tag specific commits with a version number to trigger a release, and [bump-everywhere](https://github.com/undergroundwires/bump-everywhere) automates the release process including updating version numbers throughout the project.
|
||||
|
||||
## Refactoring
|
||||
|
||||
Opportunistic refactoring is welcome. If you're adding a feature or fixing a bug, feel free to also clean up and optimize the related code. Your contributions should leave the code in a better state than when you found it.
|
||||
|
||||
## License
|
||||
|
||||
By contributing, you agree that your [GNU General Public License v3.0](./LICENSE) will be the license for your contributions.
|
||||
By contributing to this project, you agree that your contributions are licensed under the [GNU Affero General Public License](./LICENSE) as currently specified. Additionally, you expressly consent to the project maintainers having full authority to modify the licensing terms or relicense your contributions under different terms in the future.
|
||||
|
||||
17
Dockerfile
17
Dockerfile
@@ -1,13 +1,16 @@
|
||||
# Build
|
||||
FROM node:lts-alpine as build-stage
|
||||
FROM node:lts-alpine AS build-stage
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
RUN npm run install-deps
|
||||
RUN npm run build \
|
||||
&& npm run check:verify-build-artifacts -- --web
|
||||
RUN mkdir /dist \
|
||||
&& dist_directory=$(node 'scripts/print-dist-dir.js' --web) \
|
||||
&& cp -a "${dist_directory}/." '/dist'
|
||||
|
||||
# Production stage
|
||||
FROM nginx:stable-alpine as production-stage
|
||||
COPY --from=build-stage /app/dist /usr/share/nginx/html
|
||||
FROM nginx:stable-alpine AS production-stage
|
||||
COPY --from=build-stage /dist /usr/share/nginx/html
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
68
README.md
68
README.md
@@ -1,6 +1,6 @@
|
||||
# privacy.sexy — Now you have the choice
|
||||
# privacy.sexy — Privacy is sexy
|
||||
|
||||
> Enforce privacy & security best-practices on Windows, macOS and Linux, because privacy is sexy 🍑🍆
|
||||
> Enforce privacy & security best-practices on Windows, macOS and Linux, because privacy is sexy.
|
||||
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<p align="center">
|
||||
@@ -16,14 +16,6 @@
|
||||
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" target="_blank" rel="noopener noreferrer">
|
||||
<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" target="_blank" rel="noopener noreferrer">
|
||||
<img
|
||||
alt="Maintainability"
|
||||
@@ -50,6 +42,20 @@
|
||||
src="https://github.com/undergroundwires/privacy.sexy/workflows/e2e-tests/badge.svg"
|
||||
/>
|
||||
</a>
|
||||
<!-- Security checks -->
|
||||
<br />
|
||||
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.security.sast.yaml" target="_blank" rel="noopener noreferrer">
|
||||
<img
|
||||
alt="Status of dependency security checks"
|
||||
src="https://github.com/undergroundwires/privacy.sexy/workflows/checks.security.sast/badge.svg"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.security.dependencies.yaml" target="_blank" rel="noopener noreferrer">
|
||||
<img
|
||||
alt="Status of Static Analysis Security Testing (SAST)"
|
||||
src="https://github.com/undergroundwires/privacy.sexy/workflows/checks.security.dependencies/badge.svg"
|
||||
/>
|
||||
</a>
|
||||
<!-- Checks -->
|
||||
<br />
|
||||
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.quality.yaml" target="_blank" rel="noopener noreferrer">
|
||||
@@ -58,16 +64,10 @@
|
||||
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" target="_blank" rel="noopener noreferrer">
|
||||
<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" target="_blank" rel="noopener noreferrer">
|
||||
<img
|
||||
alt="Build checks status"
|
||||
src="https://github.com/undergroundwires/privacy.sexy/workflows/build-checks/badge.svg"
|
||||
alt="Status of build checks"
|
||||
src="https://github.com/undergroundwires/privacy.sexy/workflows/checks.build/badge.svg"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://github.com/undergroundwires/privacy.sexy/actions/workflows/checks.desktop-runtime-errors.yaml" target="_blank" rel="noopener noreferrer">
|
||||
@@ -122,11 +122,11 @@
|
||||
## Get started
|
||||
|
||||
- 🌍️ **Online**: [https://privacy.sexy](https://privacy.sexy).
|
||||
- 🖥️ **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.12.3/privacy.sexy-Setup-0.12.3.exe), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.12.3/privacy.sexy-0.12.3.dmg), [Linux](https://github.com/undergroundwires/privacy.sexy/releases/download/0.12.3/privacy.sexy-0.12.3.AppImage).
|
||||
- 🖥️ **Offline**: Download directly for: [Windows](https://github.com/undergroundwires/privacy.sexy/releases/download/0.13.0/privacy.sexy-Setup-0.13.0.exe), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.13.0/privacy.sexy-0.13.0.dmg), [Linux](https://github.com/undergroundwires/privacy.sexy/releases/download/0.13.0/privacy.sexy-0.13.0.AppImage). For more options, see [here](#additional-install-options).
|
||||
|
||||
Online version does not require to run any software on your computer. Offline version has more functions such as running the scripts directly.
|
||||
For a detailed comparison of features between the desktop and web versions of privacy.sexy, see [Desktop vs. Web Features](./docs/desktop-vs-web-features.md).
|
||||
|
||||
💡 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.
|
||||
💡 Regularly applying your configuration with privacy.sexy is recommended, especially after each new release and major operating system updates. Each version updates scripts to enhance stability, privacy, and security.
|
||||
|
||||
[](https://privacy.sexy)
|
||||
|
||||
@@ -137,6 +137,7 @@ Online version does not require to run any software on your computer. Offline ve
|
||||
- **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.
|
||||
- **Secure**: Security is a top priority at privacy.sexy with [comprehensive safeguards](./SECURITY.md#security-practices) in place.
|
||||
- **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).
|
||||
@@ -150,6 +151,25 @@ Online version does not require to run any software on your computer. Offline ve
|
||||
|
||||
**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).
|
||||
|
||||
## Additional Install Options
|
||||
|
||||
- Check the [releases page](https://github.com/undergroundwires/privacy.sexy/releases) for all available versions.
|
||||
- Other unofficial channels (not maintained by privacy.sexy) for Windows include:
|
||||
- [Scoop 🥄](https://scoop.sh/#/apps?q=privacy.sexy&s=2&d=1&o=true) (latest version):
|
||||
|
||||
```powershell
|
||||
scoop bucket add extras
|
||||
scoop install privacy.sexy
|
||||
```
|
||||
|
||||
- [winget 🪟](https://winget.run/pkg/undergroundwires/privacy.sexy) (may be outdated):
|
||||
|
||||
```powershell
|
||||
winget install -e --id undergroundwires.privacy.sexy
|
||||
```
|
||||
|
||||
With winget, updates require manual submission; the auto-update feature within privacy.sexy will notify you of new releases post-installation.
|
||||
|
||||
## Development
|
||||
|
||||
Refer to [development.md](./docs/development.md) for Docker usage and reading more about setting up your development environment.
|
||||
@@ -157,3 +177,9 @@ Refer to [development.md](./docs/development.md) for Docker usage and reading mo
|
||||
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.
|
||||
|
||||
[docs/](./docs/) folder includes all other documentation.
|
||||
|
||||
## Security
|
||||
|
||||
Security is a top priority at privacy.sexy.
|
||||
An extensive commitment to security verification ensures this priority.
|
||||
For any security concerns or vulnerabilities, please consult the [Security Policy](./SECURITY.md).
|
||||
|
||||
70
SECURITY.md
Normal file
70
SECURITY.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Security Policy
|
||||
|
||||
Security is a top priority at privacy.sexy.
|
||||
Please report any discovered vulnerabilities responsibly.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Efforts to responsibly disclose findings are greatly appreciated. To report a security vulnerability, follow these steps:
|
||||
|
||||
- For general vulnerabilities, [open an issue](https://github.com/undergroundwires/privacy.sexy/issues/new/choose) using the bug report template.
|
||||
- For sensitive matters, [contact the developer directly](https://undergroundwires.dev).
|
||||
|
||||
## Security Report Handling
|
||||
|
||||
Upon receiving a security report, the process involves:
|
||||
|
||||
- Confirming the report and identifying affected components.
|
||||
- Assessing the impact and severity of the issue.
|
||||
- Fixing the vulnerability and planning a release to address it.
|
||||
- Keeping the reporter informed about progress.
|
||||
|
||||
## Security Practices
|
||||
|
||||
### Application Security
|
||||
|
||||
privacy.sexy adopts a defense in depth strategy to protect users on multiple layers:
|
||||
|
||||
- **Link Protection:**
|
||||
privacy.sexy ensures each external link has special attributes for your privacy and security.
|
||||
These attributes block the new site from accessing the privacy.sexy page, increasing your online safety and privacy.
|
||||
- **Content Security Policies (CSP):**
|
||||
privacy.sexy actively follows security guidelines from the Open Web Application Security Project (OWASP) at strictest level.
|
||||
This approach protects against attacks like Cross Site Scripting (XSS) and data injection.
|
||||
- **Host System Access Control:**
|
||||
The desktop application segregates and isolates code sections based on their access levels through sandboxing.
|
||||
This provides a critical defense mechanism, prevents attackers from introducing harmful code into the app, known as injection attacks.
|
||||
- **Auditing and Transparency:**
|
||||
The desktop application improves security and transparency by logging application activities and retaining files of executed scripts
|
||||
This facilitates detailed auditability and effective troubleshooting, contributing to the integrity and reliability of the application.
|
||||
Recognizing that some users prefer not to keep these records, privacy.sexy provides specialized scripts for deletion of these logs.
|
||||
- **Privilege Management:**
|
||||
The desktop application operates without persistent administrative or `sudo` privileges, reinforcing its security posture. It requests
|
||||
elevation of privileges for system modifications with explicit user consent and logs every action taken with high privileges. This
|
||||
approach actively minimizes potential security risks by limiting privileged operations and aligning with the principle of least privilege.
|
||||
- **Secure Script Execution/Storage:**
|
||||
Before executing any script, the desktop application stores a copy to allow antivirus software to perform scans. This safeguards against
|
||||
any unwanted modifications. Furthermore, the application incorporates integrity checks for tamper protection. If the script file differs from
|
||||
the user's selected script, the application will not execute or save the script, ensuring the processing of authentic scripts.
|
||||
Recognizing that some users prefer not to keep these records, privacy.sexy provides specialized scripts for deletion of these scripts.
|
||||
|
||||
### Update Security and Integrity
|
||||
|
||||
privacy.sexy benefits from automated update processes including security tests. Automated deployments from source code ensure immediate and secure updates, mirroring the latest source code. This aligns the deployed application with the expected source code, enhancing transparency and trust. For more details, see [CI/CD Documentation](./docs/ci-cd.md).
|
||||
|
||||
Every desktop update undergoes a thorough verification process. Updates are cryptographically signed to ensure authenticity and integrity, preventing tampered versions from reaching your device. Version checks are conducted to prevent downgrade attacks.
|
||||
|
||||
### Testing
|
||||
|
||||
privacy.sexy's testing approach includes a mix of automated and community-driven tests.
|
||||
Details on testing practices are available in the [Testing Documentation](./docs/tests.md).
|
||||
|
||||
## Support
|
||||
|
||||
For help or any questions, [submit a GitHub issue](https://github.com/undergroundwires/privacy.sexy/issues/new/choose). Addressing security concerns is a priority, and we ensure the necessary support.
|
||||
|
||||
Support privacy.sexy's commitment to security by [making a donation ❤️](https://github.com/sponsors/undergroundwires). Your contributions aid in maintaining and enhancing the project's security features.
|
||||
|
||||
---
|
||||
|
||||
Active contribution to the safety and security of privacy.sexy is thanked. This collaborative effort keeps the project resilient and trustworthy for all.
|
||||
5
cypress-dirs.json
Normal file
5
cypress-dirs.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"base": "tests/e2e",
|
||||
"videos": "tests/e2e/videos",
|
||||
"screenshots": "tests/e2e/videos"
|
||||
}
|
||||
@@ -1,15 +1,31 @@
|
||||
import { defineConfig } from 'cypress';
|
||||
import ViteConfig from './vite.config';
|
||||
|
||||
const CYPRESS_BASE_DIR = 'tests/e2e/';
|
||||
import cypressDirs from './cypress-dirs.json' assert { type: 'json' };
|
||||
|
||||
export default defineConfig({
|
||||
fixturesFolder: `${CYPRESS_BASE_DIR}/fixtures`,
|
||||
screenshotsFolder: `${CYPRESS_BASE_DIR}/screenshots`,
|
||||
videosFolder: `${CYPRESS_BASE_DIR}/videos`,
|
||||
fixturesFolder: `${cypressDirs.base}/fixtures`,
|
||||
screenshotsFolder: cypressDirs.screenshots,
|
||||
|
||||
video: true,
|
||||
videosFolder: cypressDirs.videos,
|
||||
|
||||
e2e: {
|
||||
baseUrl: `http://localhost:${ViteConfig.server.port}/`,
|
||||
specPattern: `${CYPRESS_BASE_DIR}/**/*.cy.{js,jsx,ts,tsx}`, // Default: cypress/e2e/**/*.cy.{js,jsx,ts,tsx}
|
||||
supportFile: `${CYPRESS_BASE_DIR}/support/e2e.ts`,
|
||||
baseUrl: `http://localhost:${getApplicationPort()}/`,
|
||||
specPattern: `${cypressDirs.base}/**/*.cy.{js,jsx,ts,tsx}`, // Default: cypress/e2e/**/*.cy.{js,jsx,ts,tsx}
|
||||
supportFile: `${cypressDirs.base}/support/e2e.ts`,
|
||||
},
|
||||
|
||||
/*
|
||||
Disabling Chrome's web security to allow for faster DOM queries to access DOM earlier than
|
||||
`cy.get()`. It bypasses the usual same-origin policy constraints
|
||||
*/
|
||||
chromeWebSecurity: false,
|
||||
});
|
||||
|
||||
function getApplicationPort(): number {
|
||||
const port = ViteConfig.server?.port;
|
||||
if (port === undefined) {
|
||||
throw new Error('Unknown application port');
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
@@ -27,13 +27,14 @@ Application uses highly decoupled models & services in different DDD layers:
|
||||
**Domain layer**:
|
||||
|
||||
- Serves as the system's core and central truth.
|
||||
- Facilitates communication between the application and presentation layers through the domain model.
|
||||
- It should be independent of other layers and encapsulate the core business concepts.
|
||||
|
||||
**Infrastructure layer**:
|
||||
|
||||
- Manages technical implementations without dependencies on other layers or domain knowledge.
|
||||
- Provides technical implementations.
|
||||
- Depends on the application and domain layers in terms of interfaces and contracts but should not include business logic.
|
||||
|
||||

|
||||

|
||||
|
||||
### Application state
|
||||
|
||||
|
||||
@@ -1,176 +1,164 @@
|
||||
# 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)
|
||||
privacy.sexy is a data-driven application that reads YAML files.
|
||||
This document details the structure and syntax of the YAML files located in [`application/collections`](./../src/application/collections/), which form the backbone of the application's data model.
|
||||
|
||||
Related documentation:
|
||||
|
||||
- 📖 [`collection.yaml.d.ts`](./../src/application/collections/collection.yaml.d.ts) outlines code types.
|
||||
- 📖 [Script Guidelines](./script-guidelines.md) provide guidance on script creation including best-practices.
|
||||
|
||||
## 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.
|
||||
- Defines categories, scripts, and OS-specific details in a tree structure.
|
||||
- Allows defining common [functions](#function) for code reuse.
|
||||
|
||||
#### `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.
|
||||
- `os:` *`string`* **(required)**
|
||||
- Operating system for the collection.
|
||||
- 📖 See [`OperatingSystem.ts`](./../src/domain/OperatingSystem.ts) for possible values.
|
||||
- `actions: [` ***[`Category`](#category)*** `, ... ]` **(required)**
|
||||
- Each [category](#category) is rendered as different cards in card presentation.
|
||||
- Renders each parent category as cards in the user interface.
|
||||
- ❗ 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.
|
||||
- Optional for code reuse.
|
||||
- `scripting:` ***[`ScriptingDefinition`](#scriptingdefinition)*** **(required)**
|
||||
- Defines the scripting language that the code of other action uses.
|
||||
- Sets the scripting language for all inline code used within the collection.
|
||||
|
||||
### `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.
|
||||
Represents a logical group of scripts and subcategories.
|
||||
|
||||
#### `Category` syntax
|
||||
|
||||
- `category:` *`string`* (**required**)
|
||||
- Name of the category
|
||||
- ❗ Must be unique throughout the [Collection](#collection)
|
||||
- `children: [` ***[`Category`](#category)*** `|` [***`Script`***](#script) `, ... ]` (**required**)
|
||||
- `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.
|
||||
- `docs`: *`string`* | `[`*`string`*`, ... ]`
|
||||
- Documentation pieces related to the category.
|
||||
- Rendered as markdown.
|
||||
- Markdown-formatted documentation related to the category.
|
||||
|
||||
### `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.
|
||||
Represents an individual tweak.
|
||||
|
||||
Types (like [functions](#function)):
|
||||
|
||||
1. Inline script:
|
||||
- Direct code.
|
||||
- ❗ Requires `code` and optional `revertCode`.
|
||||
2. Caller script:
|
||||
- Calls other [functions](#function).
|
||||
- ❗ Requires `call`, but not `code` or `revertCode`.
|
||||
|
||||
📖 For detailed guidelines, see [Script Guidelines](./script-guidelines.md).
|
||||
|
||||
#### `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
|
||||
- `name`: *`string`* **(required)**
|
||||
- Script name.
|
||||
- ❗ Must be unique throughout the [Collection](#collection).
|
||||
- `code`: *`string`* **(conditionally required)**
|
||||
- Code to execute when the user selects the script.
|
||||
- 💡 If defined, it's best practice to also define `revertCode`.
|
||||
- ❗ Cannot co-exist with `call`, define either `code` with optional `revertCode` or `call`.
|
||||
- `revertCode`: *`string`*
|
||||
- Reverts changes made by `code`.
|
||||
- ❗ Cannot co-exist with `call`, define `revertCode` with `code` or `call`.
|
||||
- `call`: ***[`FunctionCall`](#functioncall)*** | `[` ***[`FunctionCall`](#functioncall)*** `, ... ]` **(conditionally required)**
|
||||
- A shared function or sequence of functions to call (called in order).
|
||||
- ❗ Cannot co-exist with `code` or `revertCode`, define `code` with optional `revertCode` or `call`.
|
||||
- `docs`: *`string`* | `[`*`string`*`, ... ]`
|
||||
- Documentation pieces related to the script.
|
||||
- Rendered as markdown.
|
||||
- `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
|
||||
- Markdown-formatted documentation related to the script.
|
||||
- `recommend`: *`"standard"`* | *`"strict"`* | *`undefined`* (default: `undefined`)
|
||||
- Sets recommendation level.
|
||||
- Application will not recommend the script if `undefined`.
|
||||
|
||||
📖 For detailed guidelines, see [Script Guidelines](./script-guidelines.md).
|
||||
|
||||
### `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
|
||||
Specifies a function call. It may require providing argument values to its parameters.
|
||||
|
||||
#### `FunctionCall` syntax
|
||||
|
||||
- `function`: *`string`* (**required**)
|
||||
- `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 with same name must defined in `functions` property of [Collection](#collection).
|
||||
- `parameters`: `[` *`parameterName: parameterValue`* `, ... ]`
|
||||
- Key-value pairs representing function parameters and their corresponding argument values.
|
||||
- 📖 See [parameter substitution](./templating.md#parameter-substitution) for an example usage.
|
||||
- 💡 You can use [expressions (templating)](./templating.md#expressions) when providing argument values for parameters.
|
||||
|
||||
### `Function`
|
||||
|
||||
- Functions allow re-usable code throughout the defined scripts.
|
||||
- Enables reusable code in 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)):
|
||||
- A function can be of two different types (like [scripts](#script)):
|
||||
1. Inline function: a function with an inline code.
|
||||
- Must define `code` property and optionally `revertCode` but not `call`.
|
||||
- ❗ Requires `code` 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).
|
||||
- ❗ Requires `call`, but not `code` or `revertCode`.
|
||||
- 📖 Read about function expressions in [Templating](./templating.md) with [example usages](./templating.md#parameter-substitution).
|
||||
|
||||
#### `Function` syntax
|
||||
|
||||
- `name`: *`string`* (**required**)
|
||||
- `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
|
||||
- ❗ Function names must be unique.
|
||||
- ❗ Function names must follow camelCase and start with verbs (e.g., `uninstallStoreApp`).
|
||||
- `parameters`: `[` ***[`FunctionParameter`](#functionparameter)*** `, ... ]` **(conditionally required)**
|
||||
- Lists parameters used.
|
||||
- ❗ Required to be able use in [`FunctionCall`](#functioncall) or [expressions (templating)](./templating.md#expressions).
|
||||
- `code`: *`string`* **(conditionally required)**
|
||||
- Code to execute when the user selects the script.
|
||||
- 💡 You can use [expressions (templating)](./templating.md#expressions) in its value.
|
||||
- 💡 If defined, it's best practice to also define `revertCode`.
|
||||
- ❗ Cannot co-exist with `call`, define either `code` with optional `revertCode` or `call`.
|
||||
- `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
|
||||
- Reverts changes made by `code`.
|
||||
- 💡 You can use [expressions (templating)](./templating.md#expressions) in its value.
|
||||
- ❗ Cannot co-exist with `call`, define `revertCode` with `code` or `call`.
|
||||
- `call`: ***[`FunctionCall`](#functioncall)*** | `[` ***[`FunctionCall`](#functioncall)*** `, ... ]` **(conditionally required)**
|
||||
- A shared function or sequence of functions to call (called in order).
|
||||
- 💡 You can use [expressions (templating)](./templating.md#expressions) in argument values provided for parameters.
|
||||
- ❗ Cannot co-exist with `code` or `revertCode`, define `code` with optional `revertCode` or `call`.
|
||||
|
||||
### `FunctionParameter`
|
||||
|
||||
- Defines a parameter that function requires optionally or mandatory.
|
||||
- Its arguments are provided by a [Script](#script) through a [FunctionCall](#functioncall).
|
||||
- Defines a single parameter that may require an argument value optionally or mandatory.
|
||||
- A [`FunctionCall`](#functioncall) provides argument values by a caller.
|
||||
- A caller can be a [Script](#script) or [Function](#function).
|
||||
|
||||
#### `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.
|
||||
- `name`: *`string`* **(required)**
|
||||
- Name of the parameter that the function has.
|
||||
- ❗ Required for [expressions (templating)](./templating.md#expressions).
|
||||
- ❗ Must be unique and consists of alphanumeric characters.
|
||||
- `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;
|
||||
- Indicates the caller must provide and argument 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.
|
||||
- E.g., in a [`with` expression](./templating.md#with).
|
||||
- 💡 Set it to `true` if you will use its argument value 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).
|
||||
Sets global scripting properties for a [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 }}!`
|
||||
- `language:` *`string`* **(required)**
|
||||
- 📖 See [`ScriptingLanguage.ts`](./../src/domain/ScriptingLanguage.ts) enumeration for allowed values.
|
||||
- `startCode:` *`string`* **(required)**
|
||||
- Prepends the given code to the generated script file.
|
||||
- 💡 You can use global variables such as `$homepage`, `$version`, `$date` via [parameter substitution](./templating.md#parameter-substitution) code syntax such as `Welcome to {{ $homepage }}!`.
|
||||
- `endCode:` *`string`* **(required)**
|
||||
- Appends to the given code to the generated script file.
|
||||
- 💡 You can use global variables such as `$homepage`, `$version`, `$date` via [parameter substitution](./templating.md#parameter-substitution) code syntax such as `Welcome to {{ $homepage }}!`.
|
||||
|
||||
99
docs/desktop-vs-web-features.md
Normal file
99
docs/desktop-vs-web-features.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# Desktop vs. Web Features
|
||||
|
||||
This table highlights differences between the desktop and web versions of `privacy.sexy`.
|
||||
|
||||
| Feature | Desktop | Web |
|
||||
| ------- | ------- | --- |
|
||||
| [Usage without installation](#usage-without-installation) | 🔴 Not available | 🟢 Available |
|
||||
| [Offline usage](#offline-usage) | 🟢 Available | 🟡 Partially available |
|
||||
| [Auto-updates](#auto-updates) | 🟢 Available | 🟢 Available |
|
||||
| [Logging](#logging) | 🟢 Available | 🔴 Not available |
|
||||
| [Script execution](#script-execution) | 🟢 Available | 🔴 Not available |
|
||||
| [Error handling](#error-handling) | 🟢 Advanced | 🟡 Limited |
|
||||
| [Native dialogs](#native-dialogs) | 🟢 Available | 🔴 Not available |
|
||||
| [Secure script execution/storage](#secure-script-executionstorage) | 🟢 Available | 🔴 Not available |
|
||||
|
||||
## Feature descriptions
|
||||
|
||||
### Usage without installation
|
||||
|
||||
You can use the web version directly in a browser without installation.
|
||||
The desktop version requires download and installation.
|
||||
|
||||
> **Note for Linux users:** On Linux, privacy.sexy is available as an `AppImage`, a portable format that doesn't need traditional installation.
|
||||
> This allows Linux users to use the desktop version without full installation, akin to the web version.
|
||||
|
||||
### Offline usage
|
||||
|
||||
The web version, once loaded, supports offline use.
|
||||
Desktop version inherently allows offline usage.
|
||||
|
||||
### Auto-updates
|
||||
|
||||
Both the desktop and web versions of privacy.sexy provide timely access to the latest features and security improvements. The updates are automatically deployed from source code, reflecting the latest changes for enhanced security and reliability. For more details, see [CI/CD documentation](./ci-cd.md).
|
||||
|
||||
The desktop version ensures secure delivery through cryptographic signatures and version checks.
|
||||
|
||||
[Security is a top priority](./../SECURITY.md#update-security-and-integrity) at privacy.sexy.
|
||||
|
||||
> **Note for macOS users:** On macOS, the desktop version's auto-update process involves manual steps due to Apple's code signing costs.
|
||||
> Users get notified about updates but might need to complete the installation manually.
|
||||
> Consider [donating](https://github.com/sponsors/undergroundwires) to help improve this process ❤️.
|
||||
|
||||
### Logging
|
||||
|
||||
The desktop version supports logging of activities to aid in troubleshooting.
|
||||
This feature is not available in the web version.
|
||||
|
||||
Log file locations vary by operating system:
|
||||
|
||||
- macOS: `$HOME/Library/Logs/privacy.sexy`
|
||||
- Linux: `$HOME/.config/privacy.sexy/logs`
|
||||
- Windows: `%APPDATA%\privacy.sexy\logs`
|
||||
|
||||
> 💡 privacy.sexy provides scripts to securely erase these logs.
|
||||
|
||||
### Script execution
|
||||
|
||||
The desktop version of privacy.sexy enables direct script execution, providing a seamless and integrated experience.
|
||||
This direct execution capability isn't available in the web version due to inherent browser restrictions.
|
||||
|
||||
**Script execution history:**
|
||||
|
||||
For enhanced auditability and easier troubleshooting, the desktop version keeps a record of executed scripts in designated directories.
|
||||
These locations vary based on the operating system:
|
||||
|
||||
- macOS: `$HOME/Library/Application Support/privacy.sexy/runs`
|
||||
- Linux: `$HOME/.config/privacy.sexy/runs`
|
||||
- Windows: `%APPDATA%\privacy.sexy\runs`
|
||||
|
||||
> 💡 privacy.sexy provides scripts to securely erase your script execution history.
|
||||
|
||||
### Error handling
|
||||
|
||||
The desktop version of privacy.sexy features advanced error handling capabilities.
|
||||
It employs robust and reliable execution strategies, including self-healing mechanisms, and provides guidance and troubleshooting information to resolve issues effectively.
|
||||
In contrast, the web version has more basic error handling due to browser limitations and the nature of web applications.
|
||||
|
||||
### Native dialogs
|
||||
|
||||
The desktop version uses native dialogs, offering more features and reliability compared to the browser's file system dialogs.
|
||||
These native dialogs provide a more integrated and user-friendly experience, aligning with the operating system's standard interface and functionalities.
|
||||
|
||||
### Secure script execution/storage
|
||||
|
||||
**Integrity checks:**
|
||||
|
||||
The desktop version of privacy.sexy implements robust integrity checks for both script execution and storage.
|
||||
Featuring tamper protection, the application actively verifies the integrity of script files before executing or saving them.
|
||||
If the actual contents of a script file do not align with the expected contents, the application refuses to execute or save the script.
|
||||
This proactive approach ensures only unaltered and verified scripts undergo processing, thereby enhancing both security and reliability.
|
||||
Due to browser constraints, this feature is absent in the web version.
|
||||
|
||||
**Error handling:**
|
||||
|
||||
In scenarios where script execution or storage encounters failure, the desktop application initiates automated troubleshooting and self-healing processes.
|
||||
It also guides users through potential issues with filesystem or third-party software, such as antivirus interventions.
|
||||
Specifically, the application is capable of identifying when antivirus software blocks or removes a script, providing users with tailored error messages
|
||||
and detailed resolution steps. This level of proactive error handling and user guidance enhances the application's security and reliability,
|
||||
offering a feature not achievable in the web version due to browser limitations.
|
||||
@@ -13,8 +13,11 @@ See [ci-cd.md](./ci-cd.md) for more information.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Install Node >16.x.
|
||||
- Install Node.js:
|
||||
- Refer to [action.yml](./../.github/actions/setup-node/action.yml) for the minimum required version compatible with the automated workflows.
|
||||
- 💡 Recommended: Use [`nvm`](https://github.com/nvm-sh/nvm) CLI to install and switch between Node.js versions.
|
||||
- Install dependencies using `npm install` (or [`npm run install-deps`](#utility-scripts) for more options).
|
||||
- For Visual Studio Code users, running the configuration script is recommended to optimize the IDE settings, as detailed in [utility scripts](#utility-scripts).
|
||||
|
||||
### Testing
|
||||
|
||||
@@ -60,6 +63,7 @@ See [ci-cd.md](./ci-cd.md) for more information.
|
||||
|
||||
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`
|
||||
3. Application should be available at [`http://localhost:8080`](http://localhost:8080)
|
||||
|
||||
### Building
|
||||
|
||||
@@ -76,19 +80,21 @@ See [ci-cd.md](./ci-cd.md) for more information.
|
||||
- [**`npm run install-deps [-- <options>]`**](../scripts/npm-install.js):
|
||||
- Manages NPM dependency installation, it offers capabilities like doing a fresh install, retries on network errors, and other features.
|
||||
- For example, you can run `npm run install-deps -- --fresh` to do clean installation of dependencies.
|
||||
- [**`./scripts/configure-vscode.sh`**](../scripts/configure-vscode.sh):
|
||||
- This script checks and sets the necessary configurations for VSCode in `settings.json` file.
|
||||
- [**`python ./scripts/configure_vscode.py`**](../scripts/configure_vscode.py):
|
||||
- Optimizes Visual Studio Code settings and installs essential extensions, enhancing the development environment.
|
||||
|
||||
#### Automation scripts
|
||||
|
||||
- [**`node scripts/print-dist-dir.js [-- <options>]`**](../scripts/print-dist-dir.js):
|
||||
- [**`node scripts/print-dist-dir.js [<options>]`**](../scripts/print-dist-dir.js):
|
||||
- Determines the absolute path of a distribution directory based on CLI arguments and outputs its absolute path.
|
||||
- Primarily used by automation scripts.
|
||||
- [**`npm run check:verify-build-artifacts [-- <options>]`**](../scripts/verify-build-artifacts.js):
|
||||
- Verifies the existence and content of build artifacts. Useful for ensuring that the build process is generating the expected output.
|
||||
- [**`node scripts/verify-web-server-status.js --url [URL]`**](../scripts/verify-web-server-status.js):
|
||||
- Checks if a specified server is up with retries and returns an HTTP 200 status code.
|
||||
|
||||
## 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.
|
||||
You can use [VSCode configuration script](#utility-scripts) to automatically install those.
|
||||
|
||||
@@ -11,6 +11,8 @@ The presentation layer uses an event-driven architecture for bidirectional react
|
||||
## Structure
|
||||
|
||||
- [`/src/` **`presentation/`**](./../src/presentation/): Contains Vue and Electron code.
|
||||
- [**`main.ts`**](./../src/presentation/main.ts): Starts Vue app.
|
||||
- [**`index.html`**](./../src/presentation/index.html): The `index.html` entry file, located at the root of the project as required by Vite
|
||||
- [**`bootstrapping/`**](./../src/presentation/bootstrapping/): Registers Vue components and plugins.
|
||||
- [**`components/`**](./../src/presentation/components/): Contains Vue components and helpers.
|
||||
- [**`Shared/`**](./../src/presentation/components/Shared): Contains shared Vue components and helpers.
|
||||
@@ -20,8 +22,7 @@ The presentation layer uses an event-driven architecture for bidirectional react
|
||||
- [**`fonts/`**](./../src/presentation/assets/fonts/): Contains fonts.
|
||||
- [**`styles/`**](./../src/presentation/assets/styles/): Contains shared styles.
|
||||
- [**`components/`**](./../src/presentation/assets/styles/components): Contains styles coupled to Vue components.
|
||||
- [**`main.scss`**](./../src/presentation/assets/styles/main.scss): Main Sass file, imported by other components as single entrypoint.
|
||||
- [**`main.ts`**](./../src/presentation/main.ts): Starts Vue app.
|
||||
- [**`main.scss`**](./../src/presentation/assets/styles/main.scss): Main Sass file, imported by other components as single entrypoint..
|
||||
- [**`electron/`**](./../src/presentation/electron/): Contains Electron code.
|
||||
- [`/main/` **`index.ts`**](./../src/presentation/main.ts): Main entry for Electron, managing application windows and lifecycle events.
|
||||
- [`/preload/` **`index.ts`**](./../src/presentation/main.ts): Script executed before the renderer, securing Node.js features for renderer use.
|
||||
@@ -31,7 +32,12 @@ The presentation layer uses an event-driven architecture for bidirectional react
|
||||
|
||||
## Visual design best-practices
|
||||
|
||||
Add visual clues for clickable items. It should be as clear as possible that they're interactable at first look without hovering. They should also have different visual state when hovering/touching on them that indicates that they are being clicked, which helps with accessibility.
|
||||
- **Clickables**:
|
||||
Add visual clues for clickable items.
|
||||
It should be as clear as possible that they're interactable at first look without hovering.
|
||||
They should also have different visual state when hovering/touching on them that indicates that they are being clicked, which helps with accessibility.
|
||||
- **Borders**:
|
||||
privacy.sexy prefers sharper edges in its design language.
|
||||
|
||||
## Application data
|
||||
|
||||
@@ -70,26 +76,30 @@ To add a new dependency:
|
||||
1. **Define its symbol**: Define an associated symbol for every dependency in [`injectionSymbols.ts`](./../src/presentation/injectionSymbols.ts). Symbols are grouped into:
|
||||
- **Singletons**: Shared across components, instantiated once.
|
||||
- **Transients**: Factories yielding a new instance on every access.
|
||||
2. **Provide the dependency**: Modify the [`provideDependencies`](./../src/presentation/bootstrapping/DependencyProvider.ts) function to include the new dependency. [`App.vue`](./../src/presentation/components/App.vue) calls this function within its `setup()` hook to register the dependencies.
|
||||
3. **Inject the dependency**: Use Vue's `inject` method alongside the defined symbol to incorporate the dependency into components.
|
||||
- For singletons, invoke the factory method: `inject(symbolKey)()`.
|
||||
- For transients, directly inject: `inject(symbolKey)`.
|
||||
2. **Provide the dependency**:
|
||||
Modify the [`provideDependencies`](./../src/presentation/bootstrapping/DependencyProvider.ts) function to include the new dependency.
|
||||
[`App.vue`](./../src/presentation/components/App.vue) calls this function within its `setup()` hook to register the dependencies.
|
||||
3. **Inject the dependency**: Use `injectKey` to inject a dependency. Pass a selector function to `injectKey` that retrieves the appropriate symbol from the provided dependencies.
|
||||
- Example usage: `injectKey((keys) => keys.useCollectionState)`;
|
||||
|
||||
## Shared UI components
|
||||
|
||||
Shared UI components promote consistency and simplifies the creation of the front-end.
|
||||
Shared UI components ensure consistency and streamline front-end development.
|
||||
|
||||
In order to maintain portability and easy maintainability, the preference is towards using homegrown components over third-party ones or comprehensive UI frameworks like Quasar.
|
||||
We use homegrown components over third-party solutions or comprehensive UI frameworks like Quasar to maintain portability and easy maintenance.
|
||||
|
||||
Shared components include:
|
||||
|
||||
- [ModalDialog.vue](./../src/presentation/components/Shared/Modal/ModalDialog.vue) is utilized for rendering modal windows.
|
||||
- [TooltipWrapper.vue](./../src/presentation/components/Shared/TooltipWrapper.vue) acts as a wrapper for rendering tooltips.
|
||||
- [ModalDialog.vue](./../src/presentation/components/Shared/Modal/ModalDialog.vue): Renders modal windows.
|
||||
- [TooltipWrapper.vue](./../src/presentation/components/Shared/TooltipWrapper.vue): Provides tooltip functionality for improved information accessibility.
|
||||
- [FlatButton.vue](./../src/presentation/components/Shared/FlatButton.vue): Creates flat-style buttons for a unified and consistent user interface.
|
||||
|
||||
## Desktop builds
|
||||
|
||||
Desktop builds uses `electron-vite` to bundle the code, and `electron-builder` to build and publish the packages.
|
||||
|
||||
Host system access is strictly controlled. The [`preloader`](./../src/presentation/electron/preload/) isolates logic that interacts with the host system. These functionalities are then securely exposed to the renderer process (Vue application) using context-bridging. [`ApiContextBridge.ts`](./../src/presentation/electron/preload/ContextBridging/ApiContextBridge.ts) handles the configuration of the exposed APIs, ensuring a secure bridge between the Electron and Vue layers.
|
||||
|
||||
## Styles
|
||||
|
||||
### Style location
|
||||
|
||||
24
docs/research/README.md
Normal file
24
docs/research/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Research Documentation
|
||||
|
||||
Welcome to the research section of privacy.sexy.
|
||||
This area houses in-depth technical research and analyses, serving as a resource for developers, contributors, and technology enthusiasts.
|
||||
|
||||
**Structure:**
|
||||
|
||||
This folder organizes research into topic-specific subdirectories like `windows`, `linux`, etc.
|
||||
Each contains materials relevant to its subject.
|
||||
|
||||
**Contents:**
|
||||
|
||||
These documents offer comprehensive insights into the respective topics, supporting development and contributions.
|
||||
|
||||
**Contributing:**
|
||||
|
||||
Contributions to our research documentation are welcome.
|
||||
If your research aligns with privacy.sexy goals, please consider adding it here.
|
||||
See [`CONTRIBUTING.md`](./../../CONTRIBUTING.md) on more information about how to contribute.
|
||||
|
||||
**Usage:**
|
||||
|
||||
This information is available for educational and research purposes.
|
||||
We support knowledge sharing and aim to enhance understanding of privacy and security technologies.
|
||||
84
docs/research/windows/01-windows-10-1909-apps.txt
Normal file
84
docs/research/windows/01-windows-10-1909-apps.txt
Normal file
@@ -0,0 +1,84 @@
|
||||
|
||||
Name PublisherId Category NonRemovable
|
||||
---- ----------- -------- ------------
|
||||
1527c705-839a-4832-9118-54d4Bd6a0c89 cw5n1h2txyewy System True
|
||||
c5e2524a-ea46-4f67-841f-6a9465d9d515 cw5n1h2txyewy System True
|
||||
E2A4F912-2574-4A75-9BB0-0D023378592B cw5n1h2txyewy System True
|
||||
F46D4000-FD22-4DB4-AC8E-4E1DDDE828FE cw5n1h2txyewy System True
|
||||
InputApp cw5n1h2txyewy System True
|
||||
Microsoft.AAD.BrokerPlugin cw5n1h2txyewy System True
|
||||
Microsoft.AccountsControl cw5n1h2txyewy System True
|
||||
Microsoft.AsyncTextService 8wekyb3d8bbwe System True
|
||||
Microsoft.BingWeather 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.BioEnrollment cw5n1h2txyewy System True
|
||||
Microsoft.CredDialogHost cw5n1h2txyewy System True
|
||||
Microsoft.DesktopAppInstaller 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ECApp 8wekyb3d8bbwe System True
|
||||
Microsoft.GetHelp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Getstarted 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.HEIFImageExtension 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.LockApp cw5n1h2txyewy System True
|
||||
Microsoft.Messaging 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Microsoft3DViewer 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftEdge 8wekyb3d8bbwe System True
|
||||
Microsoft.MicrosoftEdgeDevToolsClient 8wekyb3d8bbwe System True
|
||||
Microsoft.MicrosoftOfficeHub 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftSolitaireCollection 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftStickyNotes 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MixedReality.Portal 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MSPaint 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Office.OneNote 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.OneConnect 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.People 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.PPIProjection cw5n1h2txyewy System True
|
||||
Microsoft.Print3D 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ScreenSketch 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.SkypeApp kzf8qxf38zg5c Provisioned False
|
||||
Microsoft.StorePurchaseApp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.VP9VideoExtensions 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Wallet 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WebMediaExtensions 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WebpImageExtension 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Win32WebViewHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Apprep.ChxApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.AssignedAccessLockApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CallingShellApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CapturePicker cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CloudExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ContentDeliveryManager cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Cortana cw5n1h2txyewy System True
|
||||
Microsoft.Windows.NarratorQuickStart 8wekyb3d8bbwe System True
|
||||
Microsoft.Windows.OOBENetworkCaptivePortal cw5n1h2txyewy System True
|
||||
Microsoft.Windows.OOBENetworkConnectionFlow cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ParentalControls cw5n1h2txyewy System True
|
||||
Microsoft.Windows.PeopleExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Photos 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Windows.PinningConfirmationDialog cw5n1h2txyewy System True
|
||||
Microsoft.Windows.SecHealthUI cw5n1h2txyewy System True
|
||||
Microsoft.Windows.SecureAssessmentBrowser cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ShellExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.StartMenuExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.XGpuEjectDialog cw5n1h2txyewy System True
|
||||
Microsoft.WindowsAlarms 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsCalculator 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsCamera 8wekyb3d8bbwe Provisioned False
|
||||
microsoft.windowscommunicationsapps 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsFeedbackHub 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsMaps 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsSoundRecorder 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsStore 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Xbox.TCUI 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxApp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxGameCallableUI cw5n1h2txyewy System True
|
||||
Microsoft.XboxGameOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxGamingOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxIdentityProvider 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxSpeechToTextOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.YourPhone 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ZuneMusic 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ZuneVideo 8wekyb3d8bbwe Provisioned False
|
||||
Windows.CBSPreview cw5n1h2txyewy System True
|
||||
windows.immersivecontrolpanel cw5n1h2txyewy System True
|
||||
Windows.PrintDialog cw5n1h2txyewy System True
|
||||
|
||||
|
||||
85
docs/research/windows/02-windows-10-20H2-apps.txt
Normal file
85
docs/research/windows/02-windows-10-20H2-apps.txt
Normal file
@@ -0,0 +1,85 @@
|
||||
|
||||
Name PublisherId Category NonRemovable
|
||||
---- ----------- -------- ------------
|
||||
1527c705-839a-4832-9118-54d4Bd6a0c89 cw5n1h2txyewy System True
|
||||
c5e2524a-ea46-4f67-841f-6a9465d9d515 cw5n1h2txyewy System True
|
||||
E2A4F912-2574-4A75-9BB0-0D023378592B cw5n1h2txyewy System True
|
||||
F46D4000-FD22-4DB4-AC8E-4E1DDDE828FE cw5n1h2txyewy System True
|
||||
Microsoft.549981C3F5F10 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.AAD.BrokerPlugin cw5n1h2txyewy System True
|
||||
Microsoft.AccountsControl cw5n1h2txyewy System True
|
||||
Microsoft.AsyncTextService 8wekyb3d8bbwe System True
|
||||
Microsoft.BingWeather 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.BioEnrollment cw5n1h2txyewy System True
|
||||
Microsoft.CredDialogHost cw5n1h2txyewy System True
|
||||
Microsoft.DesktopAppInstaller 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ECApp 8wekyb3d8bbwe System True
|
||||
Microsoft.GetHelp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Getstarted 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.HEIFImageExtension 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.LockApp cw5n1h2txyewy System True
|
||||
Microsoft.Microsoft3DViewer 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftEdge 8wekyb3d8bbwe System True
|
||||
Microsoft.MicrosoftEdge.Stable 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftEdgeDevToolsClient 8wekyb3d8bbwe System True
|
||||
Microsoft.MicrosoftOfficeHub 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftSolitaireCollection 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftStickyNotes 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MixedReality.Portal 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MSPaint 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Office.OneNote 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.People 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ScreenSketch 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.SkypeApp kzf8qxf38zg5c Provisioned False
|
||||
Microsoft.StorePurchaseApp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.VCLibs.140.00 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.VP9VideoExtensions 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Wallet 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WebMediaExtensions 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WebpImageExtension 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Win32WebViewHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Apprep.ChxApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.AssignedAccessLockApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CallingShellApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CapturePicker cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CloudExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ContentDeliveryManager cw5n1h2txyewy System True
|
||||
Microsoft.Windows.NarratorQuickStart 8wekyb3d8bbwe System True
|
||||
Microsoft.Windows.OOBENetworkCaptivePortal cw5n1h2txyewy System True
|
||||
Microsoft.Windows.OOBENetworkConnectionFlow cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ParentalControls cw5n1h2txyewy System True
|
||||
Microsoft.Windows.PeopleExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Photos 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Windows.PinningConfirmationDialog cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Search cw5n1h2txyewy System True
|
||||
Microsoft.Windows.SecHealthUI cw5n1h2txyewy System True
|
||||
Microsoft.Windows.SecureAssessmentBrowser cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ShellExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.StartMenuExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.XGpuEjectDialog cw5n1h2txyewy System True
|
||||
Microsoft.WindowsAlarms 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsCalculator 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsCamera 8wekyb3d8bbwe Provisioned False
|
||||
microsoft.windowscommunicationsapps 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsFeedbackHub 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsMaps 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsSoundRecorder 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsStore 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Xbox.TCUI 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxApp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxGameCallableUI cw5n1h2txyewy System True
|
||||
Microsoft.XboxGameOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxGamingOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxIdentityProvider 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxSpeechToTextOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.YourPhone 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ZuneMusic 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ZuneVideo 8wekyb3d8bbwe Provisioned False
|
||||
MicrosoftWindows.Client.CBS cw5n1h2txyewy System True
|
||||
MicrosoftWindows.UndockedDevKit cw5n1h2txyewy System True
|
||||
NcsiUwpApp 8wekyb3d8bbwe System True
|
||||
Windows.CBSPreview cw5n1h2txyewy System True
|
||||
windows.immersivecontrolpanel cw5n1h2txyewy System True
|
||||
Windows.PrintDialog cw5n1h2txyewy System True
|
||||
|
||||
|
||||
85
docs/research/windows/03-windows-10-21H2-apps.txt
Normal file
85
docs/research/windows/03-windows-10-21H2-apps.txt
Normal file
@@ -0,0 +1,85 @@
|
||||
|
||||
Name PublisherId Category NonRemovable
|
||||
---- ----------- -------- ------------
|
||||
1527c705-839a-4832-9118-54d4Bd6a0c89 cw5n1h2txyewy System True
|
||||
c5e2524a-ea46-4f67-841f-6a9465d9d515 cw5n1h2txyewy System True
|
||||
E2A4F912-2574-4A75-9BB0-0D023378592B cw5n1h2txyewy System True
|
||||
F46D4000-FD22-4DB4-AC8E-4E1DDDE828FE cw5n1h2txyewy System True
|
||||
Microsoft.549981C3F5F10 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.AAD.BrokerPlugin cw5n1h2txyewy System True
|
||||
Microsoft.AccountsControl cw5n1h2txyewy System True
|
||||
Microsoft.AsyncTextService 8wekyb3d8bbwe System True
|
||||
Microsoft.BingWeather 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.BioEnrollment cw5n1h2txyewy System True
|
||||
Microsoft.CredDialogHost cw5n1h2txyewy System True
|
||||
Microsoft.DesktopAppInstaller 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ECApp 8wekyb3d8bbwe System True
|
||||
Microsoft.GetHelp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Getstarted 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.HEIFImageExtension 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.LockApp cw5n1h2txyewy System True
|
||||
Microsoft.Microsoft3DViewer 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftEdge 8wekyb3d8bbwe System True
|
||||
Microsoft.MicrosoftEdge.Stable 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftEdgeDevToolsClient 8wekyb3d8bbwe System True
|
||||
Microsoft.MicrosoftOfficeHub 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftSolitaireCollection 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftStickyNotes 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MixedReality.Portal 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MSPaint 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Office.OneNote 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.People 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ScreenSketch 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.SkypeApp kzf8qxf38zg5c Provisioned False
|
||||
Microsoft.StorePurchaseApp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.VCLibs.140.00 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.VP9VideoExtensions 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Wallet 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WebMediaExtensions 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WebpImageExtension 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Win32WebViewHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Apprep.ChxApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.AssignedAccessLockApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CallingShellApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CapturePicker cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CloudExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ContentDeliveryManager cw5n1h2txyewy System True
|
||||
Microsoft.Windows.NarratorQuickStart 8wekyb3d8bbwe System True
|
||||
Microsoft.Windows.OOBENetworkCaptivePortal cw5n1h2txyewy System True
|
||||
Microsoft.Windows.OOBENetworkConnectionFlow cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ParentalControls cw5n1h2txyewy System True
|
||||
Microsoft.Windows.PeopleExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Photos 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Windows.PinningConfirmationDialog cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Search cw5n1h2txyewy System True
|
||||
Microsoft.Windows.SecHealthUI cw5n1h2txyewy System True
|
||||
Microsoft.Windows.SecureAssessmentBrowser cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ShellExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.StartMenuExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.XGpuEjectDialog cw5n1h2txyewy System True
|
||||
Microsoft.WindowsAlarms 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsCalculator 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsCamera 8wekyb3d8bbwe Provisioned False
|
||||
microsoft.windowscommunicationsapps 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsFeedbackHub 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsMaps 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsSoundRecorder 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsStore 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Xbox.TCUI 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxApp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxGameCallableUI cw5n1h2txyewy System True
|
||||
Microsoft.XboxGameOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxGamingOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxIdentityProvider 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxSpeechToTextOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.YourPhone 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ZuneMusic 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ZuneVideo 8wekyb3d8bbwe Provisioned False
|
||||
MicrosoftWindows.Client.CBS cw5n1h2txyewy System True
|
||||
MicrosoftWindows.UndockedDevKit cw5n1h2txyewy System True
|
||||
NcsiUwpApp 8wekyb3d8bbwe System True
|
||||
Windows.CBSPreview cw5n1h2txyewy System True
|
||||
windows.immersivecontrolpanel cw5n1h2txyewy System True
|
||||
Windows.PrintDialog cw5n1h2txyewy System True
|
||||
|
||||
|
||||
85
docs/research/windows/04-windows-10-22H2-apps.txt
Normal file
85
docs/research/windows/04-windows-10-22H2-apps.txt
Normal file
@@ -0,0 +1,85 @@
|
||||
|
||||
Name PublisherId Category NonRemovable
|
||||
---- ----------- -------- ------------
|
||||
1527c705-839a-4832-9118-54d4Bd6a0c89 cw5n1h2txyewy System True
|
||||
c5e2524a-ea46-4f67-841f-6a9465d9d515 cw5n1h2txyewy System True
|
||||
E2A4F912-2574-4A75-9BB0-0D023378592B cw5n1h2txyewy System True
|
||||
F46D4000-FD22-4DB4-AC8E-4E1DDDE828FE cw5n1h2txyewy System True
|
||||
Microsoft.549981C3F5F10 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.AAD.BrokerPlugin cw5n1h2txyewy System True
|
||||
Microsoft.AccountsControl cw5n1h2txyewy System True
|
||||
Microsoft.AsyncTextService 8wekyb3d8bbwe System True
|
||||
Microsoft.BingWeather 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.BioEnrollment cw5n1h2txyewy System True
|
||||
Microsoft.CredDialogHost cw5n1h2txyewy System True
|
||||
Microsoft.DesktopAppInstaller 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ECApp 8wekyb3d8bbwe System True
|
||||
Microsoft.GetHelp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Getstarted 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.HEIFImageExtension 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.LockApp cw5n1h2txyewy System True
|
||||
Microsoft.Microsoft3DViewer 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftEdge 8wekyb3d8bbwe System True
|
||||
Microsoft.MicrosoftEdge.Stable 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftEdgeDevToolsClient 8wekyb3d8bbwe System True
|
||||
Microsoft.MicrosoftOfficeHub 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftSolitaireCollection 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftStickyNotes 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MixedReality.Portal 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MSPaint 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Office.OneNote 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.People 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ScreenSketch 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.SkypeApp kzf8qxf38zg5c Provisioned False
|
||||
Microsoft.StorePurchaseApp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.VCLibs.140.00 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.VP9VideoExtensions 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Wallet 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WebMediaExtensions 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WebpImageExtension 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Win32WebViewHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Apprep.ChxApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.AssignedAccessLockApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CallingShellApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CapturePicker cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CloudExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ContentDeliveryManager cw5n1h2txyewy System True
|
||||
Microsoft.Windows.NarratorQuickStart 8wekyb3d8bbwe System True
|
||||
Microsoft.Windows.OOBENetworkCaptivePortal cw5n1h2txyewy System True
|
||||
Microsoft.Windows.OOBENetworkConnectionFlow cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ParentalControls cw5n1h2txyewy System True
|
||||
Microsoft.Windows.PeopleExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Photos 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Windows.PinningConfirmationDialog cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Search cw5n1h2txyewy System True
|
||||
Microsoft.Windows.SecHealthUI cw5n1h2txyewy System True
|
||||
Microsoft.Windows.SecureAssessmentBrowser cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ShellExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.StartMenuExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.XGpuEjectDialog cw5n1h2txyewy System True
|
||||
Microsoft.WindowsAlarms 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsCalculator 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsCamera 8wekyb3d8bbwe Provisioned False
|
||||
microsoft.windowscommunicationsapps 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsFeedbackHub 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsMaps 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsSoundRecorder 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsStore 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Xbox.TCUI 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxApp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxGameCallableUI cw5n1h2txyewy System True
|
||||
Microsoft.XboxGameOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxGamingOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxIdentityProvider 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxSpeechToTextOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.YourPhone 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ZuneMusic 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ZuneVideo 8wekyb3d8bbwe Provisioned False
|
||||
MicrosoftWindows.Client.CBS cw5n1h2txyewy System True
|
||||
MicrosoftWindows.UndockedDevKit cw5n1h2txyewy System True
|
||||
NcsiUwpApp 8wekyb3d8bbwe System True
|
||||
Windows.CBSPreview cw5n1h2txyewy System True
|
||||
windows.immersivecontrolpanel cw5n1h2txyewy System True
|
||||
Windows.PrintDialog cw5n1h2txyewy System True
|
||||
|
||||
|
||||
88
docs/research/windows/05-windows-11-21H2-apps.txt
Normal file
88
docs/research/windows/05-windows-11-21H2-apps.txt
Normal file
@@ -0,0 +1,88 @@
|
||||
|
||||
Name PublisherId Category NonRemovable
|
||||
---- ----------- -------- ------------
|
||||
1527c705-839a-4832-9118-54d4Bd6a0c89 cw5n1h2txyewy System True
|
||||
c5e2524a-ea46-4f67-841f-6a9465d9d515 cw5n1h2txyewy System True
|
||||
E2A4F912-2574-4A75-9BB0-0D023378592B cw5n1h2txyewy System True
|
||||
F46D4000-FD22-4DB4-AC8E-4E1DDDE828FE cw5n1h2txyewy System True
|
||||
Microsoft.549981C3F5F10 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.AAD.BrokerPlugin cw5n1h2txyewy System True
|
||||
Microsoft.AccountsControl cw5n1h2txyewy System True
|
||||
Microsoft.AsyncTextService 8wekyb3d8bbwe System True
|
||||
Microsoft.BingNews 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.BingWeather 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.BioEnrollment cw5n1h2txyewy System True
|
||||
Microsoft.CredDialogHost cw5n1h2txyewy System True
|
||||
Microsoft.DesktopAppInstaller 8wekyb3d8bbwe Provisioned True
|
||||
Microsoft.ECApp 8wekyb3d8bbwe System True
|
||||
Microsoft.GamingApp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.GetHelp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Getstarted 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.HEIFImageExtension 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.LockApp cw5n1h2txyewy System True
|
||||
Microsoft.MicrosoftEdge 8wekyb3d8bbwe System True
|
||||
Microsoft.MicrosoftEdge.Stable 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftEdgeDevToolsClient 8wekyb3d8bbwe System True
|
||||
Microsoft.MicrosoftOfficeHub 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftSolitaireCollection 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftStickyNotes 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.OneDriveSync 8wekyb3d8bbwe Installed False
|
||||
Microsoft.Paint 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.People 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.PowerAutomateDesktop 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ScreenSketch 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.SecHealthUI 8wekyb3d8bbwe Provisioned True
|
||||
Microsoft.StorePurchaseApp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Todos 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.UI.Xaml.2.4 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.VCLibs.140.00 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.VP9VideoExtensions 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WebMediaExtensions 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WebpImageExtension 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Win32WebViewHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Apprep.ChxApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.AssignedAccessLockApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CallingShellApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CapturePicker cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CloudExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ContentDeliveryManager cw5n1h2txyewy System True
|
||||
Microsoft.Windows.NarratorQuickStart 8wekyb3d8bbwe System True
|
||||
Microsoft.Windows.OOBENetworkCaptivePortal cw5n1h2txyewy System True
|
||||
Microsoft.Windows.OOBENetworkConnectionFlow cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ParentalControls cw5n1h2txyewy System True
|
||||
Microsoft.Windows.PeopleExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Photos 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Windows.PinningConfirmationDialog cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Search cw5n1h2txyewy System True
|
||||
Microsoft.Windows.SecureAssessmentBrowser cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ShellExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.StartMenuExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.XGpuEjectDialog cw5n1h2txyewy System True
|
||||
Microsoft.WindowsAlarms 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsCalculator 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsCamera 8wekyb3d8bbwe Provisioned False
|
||||
microsoft.windowscommunicationsapps 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsFeedbackHub 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsMaps 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsNotepad 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsSoundRecorder 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsStore 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsTerminal 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Xbox.TCUI 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxGameCallableUI cw5n1h2txyewy System True
|
||||
Microsoft.XboxGameOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxGamingOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxIdentityProvider 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxSpeechToTextOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.YourPhone 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ZuneMusic 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ZuneVideo 8wekyb3d8bbwe Provisioned False
|
||||
MicrosoftWindows.Client.CBS cw5n1h2txyewy System True
|
||||
MicrosoftWindows.Client.WebExperience cw5n1h2txyewy Provisioned False
|
||||
MicrosoftWindows.UndockedDevKit cw5n1h2txyewy System True
|
||||
NcsiUwpApp 8wekyb3d8bbwe System True
|
||||
Windows.CBSPreview cw5n1h2txyewy System True
|
||||
windows.immersivecontrolpanel cw5n1h2txyewy System True
|
||||
Windows.PrintDialog cw5n1h2txyewy System True
|
||||
|
||||
|
||||
91
docs/research/windows/06-windows-11-22H2-apps.txt
Normal file
91
docs/research/windows/06-windows-11-22H2-apps.txt
Normal file
@@ -0,0 +1,91 @@
|
||||
|
||||
Name PublisherId Category NonRemovable
|
||||
---- ----------- -------- ------------
|
||||
1527c705-839a-4832-9118-54d4Bd6a0c89 cw5n1h2txyewy System True
|
||||
c5e2524a-ea46-4f67-841f-6a9465d9d515 cw5n1h2txyewy System True
|
||||
Clipchamp.Clipchamp yxz26nhyzhsrt Provisioned False
|
||||
E2A4F912-2574-4A75-9BB0-0D023378592B cw5n1h2txyewy System True
|
||||
F46D4000-FD22-4DB4-AC8E-4E1DDDE828FE cw5n1h2txyewy System True
|
||||
Microsoft.549981C3F5F10 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.AAD.BrokerPlugin cw5n1h2txyewy System True
|
||||
Microsoft.AccountsControl cw5n1h2txyewy System True
|
||||
Microsoft.AsyncTextService 8wekyb3d8bbwe System True
|
||||
Microsoft.BingNews 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.BingWeather 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.BioEnrollment cw5n1h2txyewy System True
|
||||
Microsoft.CredDialogHost cw5n1h2txyewy System True
|
||||
Microsoft.DesktopAppInstaller 8wekyb3d8bbwe Provisioned True
|
||||
Microsoft.ECApp 8wekyb3d8bbwe System True
|
||||
Microsoft.GamingApp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.GetHelp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Getstarted 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.HEIFImageExtension 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.HEVCVideoExtension 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.LockApp cw5n1h2txyewy System True
|
||||
Microsoft.MicrosoftEdge 8wekyb3d8bbwe System True
|
||||
Microsoft.MicrosoftEdge.Stable 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftEdgeDevToolsClient 8wekyb3d8bbwe System True
|
||||
Microsoft.MicrosoftOfficeHub 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftSolitaireCollection 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftStickyNotes 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Paint 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.People 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.PowerAutomateDesktop 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.RawImageExtension 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ScreenSketch 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.SecHealthUI 8wekyb3d8bbwe Provisioned True
|
||||
Microsoft.StorePurchaseApp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Todos 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.VCLibs.140.00 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.VP9VideoExtensions 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WebMediaExtensions 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WebpImageExtension 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Win32WebViewHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Apprep.ChxApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.AssignedAccessLockApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CallingShellApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CapturePicker cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CloudExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ContentDeliveryManager cw5n1h2txyewy System True
|
||||
Microsoft.Windows.NarratorQuickStart 8wekyb3d8bbwe System True
|
||||
Microsoft.Windows.OOBENetworkCaptivePortal cw5n1h2txyewy System True
|
||||
Microsoft.Windows.OOBENetworkConnectionFlow cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ParentalControls cw5n1h2txyewy System True
|
||||
Microsoft.Windows.PeopleExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Photos 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Windows.PinningConfirmationDialog cw5n1h2txyewy System True
|
||||
Microsoft.Windows.PrintQueueActionCenter cw5n1h2txyewy System True
|
||||
Microsoft.Windows.SecureAssessmentBrowser cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ShellExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.StartMenuExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.XGpuEjectDialog cw5n1h2txyewy System True
|
||||
Microsoft.WindowsAlarms 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsCalculator 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsCamera 8wekyb3d8bbwe Provisioned False
|
||||
microsoft.windowscommunicationsapps 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsFeedbackHub 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsMaps 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsNotepad 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsSoundRecorder 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsStore 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsTerminal 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Xbox.TCUI 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxGameCallableUI cw5n1h2txyewy System True
|
||||
Microsoft.XboxGameOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxGamingOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxIdentityProvider 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxSpeechToTextOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.YourPhone 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ZuneMusic 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ZuneVideo 8wekyb3d8bbwe Provisioned False
|
||||
MicrosoftCorporationII.QuickAssist 8wekyb3d8bbwe Provisioned False
|
||||
MicrosoftWindows.Client.CBS cw5n1h2txyewy System True
|
||||
MicrosoftWindows.Client.Core cw5n1h2txyewy System True
|
||||
MicrosoftWindows.Client.WebExperience cw5n1h2txyewy Provisioned False
|
||||
MicrosoftWindows.UndockedDevKit cw5n1h2txyewy System True
|
||||
NcsiUwpApp 8wekyb3d8bbwe System True
|
||||
Windows.CBSPreview cw5n1h2txyewy System True
|
||||
windows.immersivecontrolpanel cw5n1h2txyewy System True
|
||||
Windows.PrintDialog cw5n1h2txyewy System True
|
||||
|
||||
|
||||
91
docs/research/windows/07-windows-11-23H2-apps.txt
Normal file
91
docs/research/windows/07-windows-11-23H2-apps.txt
Normal file
@@ -0,0 +1,91 @@
|
||||
|
||||
Name PublisherId Category NonRemovable
|
||||
---- ----------- -------- ------------
|
||||
1527c705-839a-4832-9118-54d4Bd6a0c89 cw5n1h2txyewy System True
|
||||
c5e2524a-ea46-4f67-841f-6a9465d9d515 cw5n1h2txyewy System True
|
||||
Clipchamp.Clipchamp yxz26nhyzhsrt Provisioned False
|
||||
E2A4F912-2574-4A75-9BB0-0D023378592B cw5n1h2txyewy System True
|
||||
F46D4000-FD22-4DB4-AC8E-4E1DDDE828FE cw5n1h2txyewy System True
|
||||
Microsoft.549981C3F5F10 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.AAD.BrokerPlugin cw5n1h2txyewy System True
|
||||
Microsoft.AccountsControl cw5n1h2txyewy System True
|
||||
Microsoft.AsyncTextService 8wekyb3d8bbwe System True
|
||||
Microsoft.BingNews 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.BingWeather 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.BioEnrollment cw5n1h2txyewy System True
|
||||
Microsoft.CredDialogHost cw5n1h2txyewy System True
|
||||
Microsoft.DesktopAppInstaller 8wekyb3d8bbwe Provisioned True
|
||||
Microsoft.ECApp 8wekyb3d8bbwe System True
|
||||
Microsoft.GamingApp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.GetHelp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Getstarted 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.HEIFImageExtension 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.HEVCVideoExtension 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.LockApp cw5n1h2txyewy System True
|
||||
Microsoft.MicrosoftEdge.Stable 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftEdgeDevToolsClient 8wekyb3d8bbwe System True
|
||||
Microsoft.MicrosoftOfficeHub 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftSolitaireCollection 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.MicrosoftStickyNotes 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Paint 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.People 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.PowerAutomateDesktop 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.RawImageExtension 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ScreenSketch 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.SecHealthUI 8wekyb3d8bbwe Provisioned True
|
||||
Microsoft.StorePurchaseApp 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Todos 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.VCLibs.140.00 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.VP9VideoExtensions 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WebMediaExtensions 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WebpImageExtension 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Win32WebViewHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Apprep.ChxApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.AssignedAccessLockApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CallingShellApp cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CapturePicker cw5n1h2txyewy System True
|
||||
Microsoft.Windows.CloudExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ContentDeliveryManager cw5n1h2txyewy System True
|
||||
Microsoft.Windows.NarratorQuickStart 8wekyb3d8bbwe System True
|
||||
Microsoft.Windows.OOBENetworkCaptivePortal cw5n1h2txyewy System True
|
||||
Microsoft.Windows.OOBENetworkConnectionFlow cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ParentalControls cw5n1h2txyewy System True
|
||||
Microsoft.Windows.PeopleExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.Photos 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Windows.PinningConfirmationDialog cw5n1h2txyewy System True
|
||||
Microsoft.Windows.PrintQueueActionCenter cw5n1h2txyewy System True
|
||||
Microsoft.Windows.SecureAssessmentBrowser cw5n1h2txyewy System True
|
||||
Microsoft.Windows.ShellExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.StartMenuExperienceHost cw5n1h2txyewy System True
|
||||
Microsoft.Windows.XGpuEjectDialog cw5n1h2txyewy System True
|
||||
Microsoft.WindowsAlarms 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsCalculator 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsCamera 8wekyb3d8bbwe Provisioned False
|
||||
microsoft.windowscommunicationsapps 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsFeedbackHub 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsMaps 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsNotepad 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsSoundRecorder 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsStore 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.WindowsTerminal 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.Xbox.TCUI 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxGameCallableUI cw5n1h2txyewy System True
|
||||
Microsoft.XboxGameOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxGamingOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxIdentityProvider 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.XboxSpeechToTextOverlay 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.YourPhone 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ZuneMusic 8wekyb3d8bbwe Provisioned False
|
||||
Microsoft.ZuneVideo 8wekyb3d8bbwe Provisioned False
|
||||
MicrosoftCorporationII.QuickAssist 8wekyb3d8bbwe Provisioned False
|
||||
MicrosoftWindows.Client.CBS cw5n1h2txyewy System True
|
||||
MicrosoftWindows.Client.Core cw5n1h2txyewy System True
|
||||
MicrosoftWindows.Client.FileExp cw5n1h2txyewy System True
|
||||
MicrosoftWindows.Client.WebExperience cw5n1h2txyewy Provisioned False
|
||||
MicrosoftWindows.UndockedDevKit cw5n1h2txyewy System True
|
||||
NcsiUwpApp 8wekyb3d8bbwe System True
|
||||
Windows.CBSPreview cw5n1h2txyewy System True
|
||||
windows.immersivecontrolpanel cw5n1h2txyewy System True
|
||||
Windows.PrintDialog cw5n1h2txyewy System True
|
||||
|
||||
|
||||
46
docs/research/windows/README.md
Normal file
46
docs/research/windows/README.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Research on Windows
|
||||
|
||||
In this section, we maintain a structured approach to our research on Windows.
|
||||
The use of `01` prefixed file names aids in organizing and retrieving search results effectively.
|
||||
|
||||
## Apps
|
||||
|
||||
The PowerShell script below serves as a method for gathering detailed information about Windows packages.
|
||||
|
||||
```ps1
|
||||
$allPackages = @()
|
||||
$provisionedPackages = Get-AppxProvisionedPackage -Online
|
||||
foreach ($installedPackage in Get-AppxPackage -AllUsers) {
|
||||
if ($installedPackage.IsFramework -eq $true) {
|
||||
continue
|
||||
}
|
||||
$allPackages += [PSCustomObject]@{
|
||||
Name = $installedPackage.Name
|
||||
PublisherId = $installedPackage.PublisherId
|
||||
Category = if ($installedPackage.SignatureKind -eq "System") {
|
||||
'System'
|
||||
} elseif ($provisionedPackages | Where-Object { $_.DisplayName -eq $installedPackage.Name }) {
|
||||
'Provisioned'
|
||||
} else {
|
||||
'Installed'
|
||||
}
|
||||
NonRemovable = $installedPackage.NonRemovable
|
||||
}
|
||||
}
|
||||
foreach ($provisionedPackage in $provisionedPackages) {
|
||||
if ($allPackages | Where-Object { $_.Name -eq $provisionedPackage.DisplayName }) {
|
||||
continue
|
||||
}
|
||||
$allPackages += [PSCustomObject]@{
|
||||
Name = $provisionedPackage.DisplayName
|
||||
PublisherId = $provisionedPackage.PackageName -split '_' | Select-Object -Last 1
|
||||
Category = 'Provisioned'
|
||||
NonRemovable = $false
|
||||
}
|
||||
}
|
||||
$allPackages `
|
||||
| Sort-Object Name `
|
||||
| Select-Object Name, PublisherId, Category, NonRemovable `
|
||||
| Format-Table `
|
||||
| Out-File -FilePath "$([System.Environment]::GetFolderPath('Desktop'))\apps.txt"
|
||||
```
|
||||
57
docs/script-guidelines.md
Normal file
57
docs/script-guidelines.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# privacy.sexy Script Guidelines
|
||||
|
||||
Create a script for privacy.sexy by submitting a PR or creating an issue (details in [Extend Scripts](./../CONTRIBUTING.md#extend-scripts)).
|
||||
As scripts are central to privacy.sexy and reach a global audience, their design is critical.
|
||||
|
||||
Key attributes of a good script:
|
||||
|
||||
- ✅ Well-referenced [documentation](#documentation).
|
||||
- ✅ Utilizes [shared functions](#shared-functions).
|
||||
- ✅ Has a [simple name](#name).
|
||||
|
||||
## Name
|
||||
|
||||
- Choose a title that is easy to understand for all users, regardless of technical skill, yet remains technically accurate.
|
||||
- Focus on privacy implications, avoiding complex or overly technical jargon.
|
||||
- Maintain consistency in naming, avoiding linguistic variations.
|
||||
- Use action-oriented language for clarity and directness. Use an instruction format like "do this, do that" for clear, direct guidance.
|
||||
- Respect the official casing of brand names.
|
||||
- Choose clear and uncomplicated language.
|
||||
- It should start with an imperative noun.
|
||||
- Start with action verbs like `Clear`, `Disable`, `Remove`, `Configure`, `Minimize`, `Maximize`. While exceptions exist, these prefixes help maintain naming consistency.
|
||||
- The scripts that modify hosts file should start with `Block ..`.
|
||||
- Favor the terms:
|
||||
- `Disable` over `Turn off`, `Stop`, `Prevent`
|
||||
- `Configure` over `Set up`
|
||||
- `Clear` over `Erase`, `Clean`
|
||||
- `Minimize` over `Limit`, `Reduce`
|
||||
- `Maximize` over `Extend`, `Delay`, `Postpone`, `Prolong`
|
||||
- `Remove` over `Uninstall`
|
||||
- Structure your phrases for clarity, examples:
|
||||
- Prefer `Disable XX telemetry` over `Disable telemetry in XX`
|
||||
- Prefer `Clear XX data` over `Clear data from XX`, or `Clear data of XX`.
|
||||
- Use sentence case rather than Title Case.
|
||||
|
||||
## Documentation
|
||||
|
||||
- Use credible and reputable sources for references.
|
||||
- Use archived links by using [archive.org](https://archive.org) or [archive.today](https://archive.today).
|
||||
- Format archive.today links fully, for example: `https://archive.today/YYYYMMDDhhmmss/https://privacy.sexy`.
|
||||
- Explain the default behavior if the script is not executed.
|
||||
|
||||
## Shared functions
|
||||
|
||||
Use existing shared functions when possible, like `DisableService` for disabling services,.
|
||||
|
||||
- 📖 Learn about templates in [templating.md](./templating.md).
|
||||
- 📖 For syntax, see [collection-files.md](collection-files.md).
|
||||
|
||||
## Code
|
||||
|
||||
- Prefer [shared functions](#shared-functions); avoid custom code unless necessary.
|
||||
- Keep code simple and compatible with older systems.
|
||||
- Focus on reliability, ensuring the script is error-resistant, works on different locales and handles unexpected situations.
|
||||
- Language selection:
|
||||
- Windows: Use batch when simpler, otherwise PowerShell.
|
||||
- macOS/Linux: Use bash when simpler, otherwise Python.
|
||||
- Provide revert code to restore original/default settings when applicable.
|
||||
@@ -2,79 +2,142 @@
|
||||
|
||||
## 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.
|
||||
- **Code sharing:** Share code across scripts for consistent practices and easier maintenance.
|
||||
- **Script independence:** Generate self-contained scripts, eliminating the need for external code.
|
||||
- **Cleaner code:** Use pipes for complex operations, resulting in more readable and streamlined code.
|
||||
|
||||
## 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) but not the same.
|
||||
- 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).
|
||||
- Expressions inside expressions (nested templates) are supported.
|
||||
- An expression can output another expression that will also be compiled.
|
||||
- E.g. following would compile first [with expression](#with), and then [parameter substitution](#parameter-substitution) in its output.
|
||||
**Syntax:**
|
||||
|
||||
```go
|
||||
{{ with $condition }}
|
||||
echo {{ $text }}
|
||||
{{ end }}
|
||||
```
|
||||
Expressions are enclosed within `{{` and `}}`.
|
||||
Example: `Hello {{ $name }}!`.
|
||||
They are a core component of templating, enhancing scripts with dynamic capabilities and functionality.
|
||||
|
||||
**Syntax similarity:**
|
||||
|
||||
The syntax shares similarities with [Go Templates ❤️](https://pkg.go.dev/text/template), but with some differences:
|
||||
|
||||
**Function definitions:**
|
||||
|
||||
You can use expressions in function definition.
|
||||
Refer to [Function](./collection-files.md#function) for more details.
|
||||
|
||||
Example usage:
|
||||
|
||||
```yaml
|
||||
name: GreetFunction
|
||||
parameters:
|
||||
- name: name
|
||||
code: Hello {{ $name }}!
|
||||
```
|
||||
|
||||
If you assign `name` the value `world`, invoking `GreetFunction` would result in `Hello world!`.
|
||||
|
||||
**Function arguments:**
|
||||
|
||||
You can also use expressions in arguments in nested function calls.
|
||||
Refer to [`Function | collection-files.md`](./collection-files.md#functioncall) for more details.
|
||||
|
||||
Example with nested function calls:
|
||||
|
||||
```yaml
|
||||
-
|
||||
name: PrintMessageFunction
|
||||
parameters:
|
||||
- name: message
|
||||
code: echo "{{ $message }}"
|
||||
-
|
||||
name: GreetUserFunction
|
||||
parameters:
|
||||
- name: userName
|
||||
call:
|
||||
name: PrintMessageFunction
|
||||
parameters:
|
||||
argument: 'Hello, {{ $userName }}!'
|
||||
```
|
||||
|
||||
Here, if `userName` is `Alice`, invoking `GreetUserFunction` would execute `echo "Hello, Alice!"`.
|
||||
|
||||
**Nested templates:**
|
||||
|
||||
You can nest expressions inside expressions (also called "nested templates").
|
||||
This means that an expression can output another expression where compiler will compile both.
|
||||
|
||||
For example, following would compile first [with expression](#with), and then [parameter substitution](#parameter-substitution) in its output:
|
||||
|
||||
```go
|
||||
{{ with $condition }}
|
||||
echo {{ $text }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
### Parameter substitution
|
||||
|
||||
A simple function example:
|
||||
Parameter substitution dynamically replaces variable references with their corresponding values in the script.
|
||||
|
||||
**Example function:**
|
||||
|
||||
```yaml
|
||||
function: EchoArgument
|
||||
name: DisplayTextFunction
|
||||
parameters:
|
||||
- name: 'argument'
|
||||
code: Hello {{ $argument }} !
|
||||
- name: 'text'
|
||||
code: echo {{ $text }}
|
||||
```
|
||||
|
||||
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 }} !
|
||||
```
|
||||
Invoking `DisplayTextFunction` with `text` set to `"Hello, world!"` would result in `echo "Hello, World!"`.
|
||||
|
||||
### 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 }}` would only output `Hi, I'm a block!` if `parameterName` has any value..
|
||||
The `with` expression enables conditional rendering and provides a context variable for simpler code.
|
||||
|
||||
It binds its context (value of the provided parameter value) as arbitrary `.` value. It allows you to use the argument value of the given parameter when it is provided and not empty such as:
|
||||
**Optional block rendering:**
|
||||
|
||||
If the provided variable is falsy (`false`, `null`, or empty), the compiler skips the enclosed block of code.
|
||||
A "block" lies between the with start (`{{ with .. }}`) and end (`{{ end }}`) expressions, defining its boundaries.
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
{{ with $optionalVariable }}
|
||||
Hello
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
This would display `Hello` if `$optionalVariable` is truthy.
|
||||
|
||||
**Parameter declaration:**
|
||||
|
||||
You should set `optional: true` for the argument if you use it like `{{ with $argument }} .. {{ end }}`.
|
||||
|
||||
Declare parameters used for `with` condition as optional such as:
|
||||
|
||||
```yaml
|
||||
name: ConditionalOutputFunction
|
||||
parameters:
|
||||
- name: 'data'
|
||||
optional: true
|
||||
code: |-
|
||||
{{ with $data }}
|
||||
Data is: {{ . }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
**Context variable:**
|
||||
|
||||
`with` statement binds its context (value of the provided parameter value) as arbitrary `.` value.
|
||||
`{{ . }}` syntax gives you access to the context variable.
|
||||
This is optional to use, and not required to use `with` expressions.
|
||||
|
||||
For example:
|
||||
|
||||
```go
|
||||
{{ with $parameterName }}Parameter value is {{ . }} here {{ end }}
|
||||
```
|
||||
|
||||
It supports multiline text inside the block. You can have something like:
|
||||
**Multiline text:**
|
||||
|
||||
It supports multiline text inside the block. You can write something like:
|
||||
|
||||
```go
|
||||
{{ with $argument }}
|
||||
@@ -83,7 +146,9 @@ It supports multiline text inside the block. You can have something like:
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
You can also use other expressions inside its block, such as [parameter substitution](#parameter-substitution):
|
||||
**Inner expressions:**
|
||||
|
||||
You can also embed other expressions inside its block, such as [parameter substitution](#parameter-substitution):
|
||||
|
||||
```go
|
||||
{{ with $condition }}
|
||||
@@ -91,32 +156,44 @@ You can also use other expressions inside its block, such as [parameter substitu
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
💡 Declare parameters used for `with` condition as optional. Set `optional: true` for the argument if you use it like `{{ with $argument }} .. {{ end }}`.
|
||||
This also includes nesting `with` statements:
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
function: FunctionThatOutputsConditionally
|
||||
parameters:
|
||||
- name: 'argument'
|
||||
optional: true
|
||||
code: |-
|
||||
{{ with $argument }}
|
||||
Value is: {{ . }}
|
||||
```go
|
||||
{{ with $condition1 }}
|
||||
Value of $condition1: {{ . }}
|
||||
{{ with $condition2 }}
|
||||
Value of $condition2: {{ . }}
|
||||
{{ end }}
|
||||
{{ 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 }}`
|
||||
Pipes are functions designed for text manipulation.
|
||||
They allow for a sequential application of operations resembling [Unix pipelines](https://en.wikipedia.org/wiki/Pipeline_(Unix)), also known as "chaining".
|
||||
Each pipeline's output becomes the input of the following pipe.
|
||||
|
||||
**Pre-defined**:
|
||||
|
||||
Pipes are pre-defined by the system.
|
||||
You cannot create pipes in [collection files](./collection-files.md).
|
||||
[A dedicated compiler](./application.md#parsing-and-compiling) provides pre-defined pipes to consume in collection files.
|
||||
|
||||
**Compatibility:**
|
||||
|
||||
You can combine pipes with other expressions such as [parameter substitution](#parameter-substitution) and [with](#with) syntax.
|
||||
|
||||
For example:
|
||||
|
||||
```go
|
||||
{{ with $script }} echo "{{ . | inlinePowerShell | escapeDoubleQuotes }}" {{ end }}
|
||||
```
|
||||
|
||||
**Naming:**
|
||||
|
||||
❗ Pipe names must be camelCase without any space or special characters.
|
||||
|
||||
**Available pipes:**
|
||||
|
||||
- `inlinePowerShell`: Converts a multi-lined PowerShell script to a single line.
|
||||
- `escapeDoubleQuotes`: Escapes `"` characters for batch command execution, allows you to use them inside double quotes (`"`).
|
||||
|
||||
@@ -29,7 +29,9 @@ There are different types of tests executed:
|
||||
|
||||
- Evaluate individual components in isolation.
|
||||
- Located in [`./tests/unit`](./../tests/unit).
|
||||
- Achieve isolation using [stubs](./../tests/unit/shared/Stubs).
|
||||
- Achieve isolation using stubs where you place:
|
||||
- Common stubs in [`./shared/Stubs`](./../tests/unit/shared/Stubs),
|
||||
- Component-specific stubs in same folder as test file.
|
||||
- Include Vue component tests, enabled by `@vue/test-utils`.
|
||||
|
||||
#### Unit tests naming
|
||||
@@ -56,6 +58,11 @@ These checks validate various qualities like runtime execution, building process
|
||||
- Use [various tools](./../package.json) and [scripts](./../scripts).
|
||||
- Are automatically executed as [GitHub workflows](./../.github/workflows).
|
||||
|
||||
### Security checks
|
||||
|
||||
- [`checks.security.sast`](./../.github/workflows/checks.security.sast.yaml): Utilizes CodeQL to conduct Static Analysis Security Testing (SAST) to ensure the secure integrity of the codebase.
|
||||
- [`checks.security.dependencies`](./../.github/workflows/checks.security.dependencies.yaml): Performs audits on third-party dependencies to identify and mitigate potential vulnerabilities, safeguarding the project from exploitable weaknesses.
|
||||
|
||||
## Tests structure
|
||||
|
||||
- [`package.json`](./../package.json): Defines test commands and includes tools used in tests.
|
||||
@@ -63,21 +70,23 @@ These checks validate various qualities like runtime execution, building process
|
||||
- [`./src/`](./../src/): Contains the code subject to testing.
|
||||
- [`./tests/shared/`](./../tests/shared/): Contains code shared by different test categories.
|
||||
- [`bootstrap/setup.ts`](./../tests/shared/bootstrap/setup.ts): Initializes unit and integration tests.
|
||||
- [`Assertions/`](./../tests/shared/Assertions/): Contains common assertion functions, prefixed with `expect`.
|
||||
- [`./tests/unit/`](./../tests/unit/)
|
||||
- Stores unit test code.
|
||||
- The directory structure mirrors [`./src/`](./../src).
|
||||
- E.g., tests for [`./src/application/ApplicationFactory.ts`](./../src/application/ApplicationFactory.ts) reside in [`./tests/unit/application/ApplicationFactory.spec.ts`](./../tests/unit/application/ApplicationFactory.spec.ts).
|
||||
- [`shared/`](./../tests/unit/shared/)
|
||||
- Contains shared unit test functionalities.
|
||||
- [`Assertions/`](./../tests/unit/shared/Assertions): Contains common assertion functions, prefixed with `expect`.
|
||||
- [`TestCases/`](./../tests/unit/shared/TestCases/)
|
||||
- Shared test cases.
|
||||
- Functions that calls `it()` from [Vitest](https://vitest.dev/) should have `it` prefix.
|
||||
- [`Stubs/`](./../tests/unit/shared/Stubs): Maintains stubs for component isolation, equipped with basic functionalities and, when necessary, spying or mocking capabilities.
|
||||
- [`./tests/integration/`](./../tests/integration/): Contains integration test files.
|
||||
- [`cypress.config.ts`](./../cypress.config.ts): Cypress (E2E tests) configuration file.
|
||||
- [`cypress-dirs.json`](./../cypress-dirs.json): A central definition of directories used by Cypress, designed for reuse across different configurations.
|
||||
- [`./tests/e2e/`](./../tests/e2e/): Base Cypress folder, includes tests with `.cy.ts` extension.
|
||||
- [`/support/e2e.ts`](./../tests/e2e/support/e2e.ts): Support file, runs before every single spec file.
|
||||
- [`/tsconfig.json`]: TypeScript configuration for file Cypress code, improves IDE support, recommended to have by official documentation.
|
||||
- *(git ignored)* `/videos`: Asset folder for videos taken during tests.
|
||||
- *(git ignored)* `/screenshots`: Asset folder for Screenshots taken during tests.
|
||||
- [`/support/e2e.ts`](./../tests/e2e/support/e2e.ts): Support file, runs before every single test file.
|
||||
- [`/support/interactions/`](./../tests/e2e/support/interactions/): Contains reusable functions for simulating user interactions, enhancing test readability and maintainability.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable no-template-curly-in-string */
|
||||
|
||||
const { join } = require('path');
|
||||
const { join } = require('node:path');
|
||||
const { electronBundled, electronUnbundled } = require('./dist-dirs.json');
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
import { resolve } from 'path';
|
||||
import { mergeConfig, UserConfig } from 'vite';
|
||||
import { resolve } from 'node:path';
|
||||
import { mergeConfig, type UserConfig } from 'vite';
|
||||
import { defineConfig, externalizeDepsPlugin } from 'electron-vite';
|
||||
import { getAliasesFromTsConfig, getClientEnvironmentVariables } from './vite-config-helper';
|
||||
import { getAliases, getClientEnvironmentVariables } from './vite-config-helper';
|
||||
import { createVueConfig } from './vite.config';
|
||||
import distDirs from './dist-dirs.json' assert { type: 'json' };
|
||||
|
||||
const MAIN_ENTRY_FILE = resolvePathFromProjectRoot('src/presentation/electron/main/index.ts');
|
||||
const PRELOAD_ENTRY_FILE = resolvePathFromProjectRoot('src/presentation/electron/preload/index.ts');
|
||||
const WEB_INDEX_HTML_PATH = resolvePathFromProjectRoot('src/presentation/index.html');
|
||||
const DIST_DIR = resolvePathFromProjectRoot(distDirs.electronUnbundled);
|
||||
const ELECTRON_DIST_SUBDIRECTORIES = {
|
||||
main: resolveElectronDistSubdirectory('main'),
|
||||
preload: resolveElectronDistSubdirectory('preload'),
|
||||
renderer: resolveElectronDistSubdirectory('renderer'),
|
||||
};
|
||||
|
||||
process.env.ELECTRON_ENTRY = resolve(ELECTRON_DIST_SUBDIRECTORIES.main, 'index.cjs');
|
||||
|
||||
export default defineConfig({
|
||||
main: getSharedElectronConfig({
|
||||
distDirSubfolder: 'main',
|
||||
distDirSubfolder: ELECTRON_DIST_SUBDIRECTORIES.main,
|
||||
entryFilePath: MAIN_ENTRY_FILE,
|
||||
}),
|
||||
preload: getSharedElectronConfig({
|
||||
distDirSubfolder: 'preload',
|
||||
distDirSubfolder: ELECTRON_DIST_SUBDIRECTORIES.preload,
|
||||
entryFilePath: PRELOAD_ENTRY_FILE,
|
||||
}),
|
||||
renderer: mergeConfig(
|
||||
@@ -25,7 +31,7 @@ export default defineConfig({
|
||||
}),
|
||||
{
|
||||
build: {
|
||||
outDir: resolve(DIST_DIR, 'renderer'),
|
||||
outDir: ELECTRON_DIST_SUBDIRECTORIES.renderer,
|
||||
rollupOptions: {
|
||||
input: {
|
||||
index: WEB_INDEX_HTML_PATH,
|
||||
@@ -42,13 +48,15 @@ function getSharedElectronConfig(options: {
|
||||
}): UserConfig {
|
||||
return {
|
||||
build: {
|
||||
outDir: resolve(DIST_DIR, options.distDirSubfolder),
|
||||
outDir: options.distDirSubfolder,
|
||||
lib: {
|
||||
entry: options.entryFilePath,
|
||||
},
|
||||
rollupOptions: {
|
||||
output: {
|
||||
entryFileNames: '[name].cjs', // This is needed so `type="module"` works
|
||||
// Mark: electron-esm-support
|
||||
// This is needed so `type="module"` works
|
||||
entryFileNames: '[name].cjs',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -58,12 +66,17 @@ function getSharedElectronConfig(options: {
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
...getAliasesFromTsConfig(),
|
||||
...getAliases(),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function resolvePathFromProjectRoot(pathSegment: string) {
|
||||
function resolvePathFromProjectRoot(pathSegment: string): string {
|
||||
return resolve(__dirname, pathSegment);
|
||||
}
|
||||
|
||||
function resolveElectronDistSubdirectory(subDirectory: string): string {
|
||||
const electronDistDir = resolvePathFromProjectRoot(distDirs.electronUnbundled);
|
||||
return resolve(electronDistDir, subDirectory);
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<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>
|
||||
BIN
img/architecture/app-ddd.drawio.png
Normal file
BIN
img/architecture/app-ddd.drawio.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 29 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 116 KiB |
16113
package-lock.json
generated
16113
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
107
package.json
107
package.json
@@ -1,12 +1,11 @@
|
||||
{
|
||||
"name": "privacy.sexy",
|
||||
"version": "0.12.3",
|
||||
"version": "0.13.0",
|
||||
"private": true,
|
||||
"slogan": "Now you have the choice",
|
||||
"description": "Enforce privacy & security best-practices on Windows, macOS and Linux, because privacy is sexy 🍑🍆",
|
||||
"slogan": "Privacy is sexy",
|
||||
"description": "Enforce privacy & security best-practices on Windows, macOS and Linux, because privacy is sexy.",
|
||||
"author": "undergroundwires",
|
||||
"type": "module",
|
||||
"main": "./dist-electron-unbundled/main/index.cjs",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
@@ -25,7 +24,7 @@
|
||||
"electron:preview": "electron-vite preview",
|
||||
"electron:prebuild": "electron-vite build",
|
||||
"electron:build": "electron-builder",
|
||||
"lint:eslint": "eslint . --ignore-path .gitignore",
|
||||
"lint:eslint": "eslint . --max-warnings=0 --ignore-path .gitignore",
|
||||
"lint:md": "markdownlint **/*.md --ignore node_modules",
|
||||
"lint:md:consistency": "remark . --frail --use remark-preset-lint-consistent",
|
||||
"lint:md:relative-urls": "remark . --frail --use remark-validate-links",
|
||||
@@ -35,77 +34,71 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@floating-ui/vue": "^1.0.2",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.4.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.4.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
||||
"@fortawesome/vue-fontawesome": "^2.0.9",
|
||||
"@juggle/resize-observer": "^3.4.0",
|
||||
"ace-builds": "^1.23.4",
|
||||
"cross-fetch": "^4.0.0",
|
||||
"@types/markdown-it": "^13.0.7",
|
||||
"ace-builds": "^1.30.0",
|
||||
"electron-log": "^5.0.1",
|
||||
"electron-progressbar": "^2.1.0",
|
||||
"electron-updater": "^6.1.4",
|
||||
"file-saver": "^2.0.5",
|
||||
"markdown-it": "^13.0.1",
|
||||
"npm": "^9.8.1",
|
||||
"vue": "^2.7.14"
|
||||
"markdown-it": "^13.0.2",
|
||||
"vue": "^3.3.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@modyfi/vite-plugin-yaml": "^1.0.4",
|
||||
"@rushstack/eslint-patch": "^1.3.2",
|
||||
"@types/ace": "^0.0.48",
|
||||
"@modyfi/vite-plugin-yaml": "^1.1.0",
|
||||
"@rushstack/eslint-patch": "^1.6.1",
|
||||
"@types/ace": "^0.0.49",
|
||||
"@types/file-saver": "^2.0.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
"@vitejs/plugin-legacy": "^4.1.1",
|
||||
"@vitejs/plugin-vue2": "^2.2.0",
|
||||
"@vue/eslint-config-airbnb-with-typescript": "^7.0.0",
|
||||
"@vue/eslint-config-typescript": "^11.0.3",
|
||||
"@vue/test-utils": "^1.3.6",
|
||||
"autoprefixer": "^10.4.15",
|
||||
"cypress": "^12.17.2",
|
||||
"electron": "^25.3.2",
|
||||
"electron-builder": "^24.6.3",
|
||||
"@typescript-eslint/eslint-plugin": "^6.17.0",
|
||||
"@typescript-eslint/parser": "^6.17.0",
|
||||
"@vitejs/plugin-legacy": "^5.3.2",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vue/eslint-config-airbnb-with-typescript": "^8.0.0",
|
||||
"@vue/eslint-config-typescript": "^12.0.0",
|
||||
"@vue/test-utils": "^2.4.1",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"cypress": "^13.3.1",
|
||||
"electron": "^27.0.0",
|
||||
"electron-builder": "^24.6.4",
|
||||
"electron-devtools-installer": "^3.2.0",
|
||||
"electron-icon-builder": "^2.0.1",
|
||||
"electron-log": "^4.4.8",
|
||||
"electron-updater": "^6.1.4",
|
||||
"electron-vite": "^1.0.27",
|
||||
"eslint": "^8.46.0",
|
||||
"eslint-plugin-cypress": "^2.14.0",
|
||||
"eslint-plugin-vue": "^9.6.0",
|
||||
"eslint-plugin-vuejs-accessibility": "^1.2.0",
|
||||
"icon-gen": "^3.0.1",
|
||||
"electron-vite": "^2.1.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-plugin-cypress": "^2.15.1",
|
||||
"eslint-plugin-vue": "^9.19.2",
|
||||
"eslint-plugin-vuejs-accessibility": "^2.2.0",
|
||||
"icon-gen": "^4.0.0",
|
||||
"jsdom": "^22.1.0",
|
||||
"markdownlint-cli": "^0.35.0",
|
||||
"postcss": "^8.4.28",
|
||||
"remark-cli": "^11.0.0",
|
||||
"markdownlint-cli": "^0.37.0",
|
||||
"postcss": "^8.4.31",
|
||||
"remark-cli": "^12.0.0",
|
||||
"remark-lint-no-dead-urls": "^1.1.0",
|
||||
"remark-preset-lint-consistent": "^5.1.2",
|
||||
"remark-validate-links": "^12.1.1",
|
||||
"sass": "^1.64.1",
|
||||
"start-server-and-test": "^2.0.0",
|
||||
"remark-validate-links": "^13.0.0",
|
||||
"sass": "^1.69.3",
|
||||
"start-server-and-test": "^2.0.1",
|
||||
"svgexport": "^0.4.2",
|
||||
"terser": "^5.19.2",
|
||||
"tslib": "~2.4.0",
|
||||
"typescript": "~4.6.2",
|
||||
"vite": "^4.4.9",
|
||||
"vitest": "^0.34.2",
|
||||
"vue-tsc": "^1.8.8",
|
||||
"terser": "^5.21.0",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.1.6",
|
||||
"vitest": "^1.3.1",
|
||||
"vue-tsc": "^1.8.19",
|
||||
"yaml-lint": "^1.7.0"
|
||||
},
|
||||
"//devDependencies": {
|
||||
"terser": "Used by @vitejs/plugin-legacy for minification",
|
||||
"typescript": [
|
||||
"Cannot upgrade to 5.X.X due to unmaintained @vue/cli-plugin-typescript, https://github.com/vuejs/vue-cli/issues/7401",
|
||||
"Cannot upgrade to > 4.6.X otherwise unit tests do not work, https://github.com/evanw/node-source-map-support/issues/252"
|
||||
],
|
||||
"tslib": "Cannot upgrade to > 2.4.X otherwise unit tests do not work, https://github.com/evanw/node-source-map-support/issues/252",
|
||||
"@typescript-eslint/eslint-plugin": "Cannot upgrade to 6.X.X due to @vue/eslint-config-typescript, https://github.com/vuejs/eslint-config-typescript/pull/60",
|
||||
"@typescript-eslint/parser": "Cannot upgrade to 6.X.X due to @vue/eslint-config-typescript, https://github.com/vuejs/eslint-config-typescript/pull/60"
|
||||
"terser": "Used by `@vitejs/plugin-legacy` for minification",
|
||||
"@rushstack/eslint-patch": "Needed by `@vue/eslint-config-typescript` and `@vue/eslint-config-airbnb-with-typescript`"
|
||||
},
|
||||
"homepage": "https://privacy.sexy",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/undergroundwires/privacy.sexy.git"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"dmg-license": "^1.0.11"
|
||||
},
|
||||
"//optionalDependencies": {
|
||||
"dmg-license": "Required by `electron-builder` for DMG builds on macOS, https://github.com/electron-userland/electron-builder/issues/6489, https://github.com/electron-userland/electron-builder/issues/6520"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This script ensures that the '.vscode/settings.json' file exists and is configured correctly for ESLint validation on Vue and JavaScript files.
|
||||
# See https://web.archive.org/web/20230801024405/https://eslint.vuejs.org/user-guide/#visual-studio-code
|
||||
|
||||
declare -r SETTINGS_FILE='.vscode/settings.json'
|
||||
declare -ra CONFIG_KEYS=('vue' 'javascript' 'typescript')
|
||||
declare -r TEMP_FILE="tmp.$$.json"
|
||||
|
||||
main() {
|
||||
ensure_vscode_directory_exists
|
||||
create_or_update_settings
|
||||
}
|
||||
|
||||
ensure_vscode_directory_exists() {
|
||||
local dir_name
|
||||
dir_name=$(dirname "${SETTINGS_FILE}")
|
||||
if [[ ! -d ${dir_name} ]]; then
|
||||
mkdir -p "${dir_name}"
|
||||
echo "🎉 Created directory: ${dir_name}"
|
||||
fi
|
||||
}
|
||||
|
||||
create_or_update_settings() {
|
||||
if [[ ! -f ${SETTINGS_FILE} ]]; then
|
||||
create_default_settings
|
||||
else
|
||||
add_or_update_eslint_validate
|
||||
fi
|
||||
}
|
||||
|
||||
create_default_settings() {
|
||||
local default_validate
|
||||
default_validate=$(printf '%s' "${CONFIG_KEYS[*]}" | jq -R -s -c -M 'split(" ")')
|
||||
echo "{ \"eslint.validate\": ${default_validate} }" | jq '.' > "${SETTINGS_FILE}"
|
||||
echo "🎉 Created default ${SETTINGS_FILE}"
|
||||
}
|
||||
|
||||
add_or_update_eslint_validate() {
|
||||
if ! jq -e '.["eslint.validate"]' "${SETTINGS_FILE}" >/dev/null; then
|
||||
add_default_eslint_validate
|
||||
else
|
||||
update_eslint_validate
|
||||
fi
|
||||
}
|
||||
|
||||
add_default_eslint_validate() {
|
||||
jq --argjson keys "$(printf '%s' "${CONFIG_KEYS[*]}" \
|
||||
| jq -R -s -c 'split(" ")')" '. += {"eslint.validate": $keys}' "${SETTINGS_FILE}" > "${TEMP_FILE}"
|
||||
replace_and_confirm
|
||||
echo "🎉 Added default 'eslint.validate' to ${SETTINGS_FILE}"
|
||||
}
|
||||
|
||||
update_eslint_validate() {
|
||||
local existing_keys
|
||||
existing_keys=$(jq '.["eslint.validate"]' "${SETTINGS_FILE}")
|
||||
for key in "${CONFIG_KEYS[@]}"; do
|
||||
if ! echo "${existing_keys}" | jq 'index("'"${key}"'")' >/dev/null; then
|
||||
jq '.["eslint.validate"] += ["'"${key}"'"]' "${SETTINGS_FILE}" > "${TEMP_FILE}"
|
||||
mv "${TEMP_FILE}" "${SETTINGS_FILE}"
|
||||
echo "🎉 Updated 'eslint.validate' in ${SETTINGS_FILE} for ${key}"
|
||||
else
|
||||
echo "⏩️ No updated needed for ${key} ${SETTINGS_FILE}."
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
replace_and_confirm() {
|
||||
if mv "${TEMP_FILE}" "${SETTINGS_FILE}"; then
|
||||
echo "🎉 Updated ${SETTINGS_FILE}"
|
||||
fi
|
||||
}
|
||||
|
||||
main
|
||||
181
scripts/configure_vscode.py
Executable file
181
scripts/configure_vscode.py
Executable file
@@ -0,0 +1,181 @@
|
||||
"""
|
||||
This script configures project-level VSCode settings in '.vscode/settings.json' for
|
||||
development and installs recommended extensions from '.vscode/extensions.json'.
|
||||
"""
|
||||
# pylint: disable=missing-function-docstring
|
||||
|
||||
import os
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
import re
|
||||
from typing import Any, Optional
|
||||
from shutil import which
|
||||
|
||||
VSCODE_SETTINGS_JSON_FILE: str = '.vscode/settings.json'
|
||||
VSCODE_EXTENSIONS_JSON_FILE: str = '.vscode/extensions.json'
|
||||
|
||||
def main() -> None:
|
||||
ensure_vscode_directory_exists()
|
||||
ensure_setting_file_exists()
|
||||
add_or_update_settings()
|
||||
install_recommended_extensions()
|
||||
|
||||
def ensure_vscode_directory_exists() -> None:
|
||||
vscode_directory_path = os.path.dirname(VSCODE_SETTINGS_JSON_FILE)
|
||||
try:
|
||||
os.makedirs(vscode_directory_path, exist_ok=True)
|
||||
print_success(f"Created or verified directory: {vscode_directory_path}")
|
||||
except OSError as error:
|
||||
print_error(f"Error handling directory {vscode_directory_path}: {error}")
|
||||
|
||||
def ensure_setting_file_exists() -> None:
|
||||
try:
|
||||
if os.path.isfile(VSCODE_SETTINGS_JSON_FILE):
|
||||
print_success(f"VSCode settings file exists: {VSCODE_SETTINGS_JSON_FILE}")
|
||||
return
|
||||
with open(VSCODE_SETTINGS_JSON_FILE, 'w', encoding='utf-8') as file:
|
||||
json.dump({}, file, indent=4)
|
||||
print_success(f"Created empty {VSCODE_SETTINGS_JSON_FILE}")
|
||||
except IOError as error:
|
||||
print_error(f"Error creating file {VSCODE_SETTINGS_JSON_FILE}: {error}")
|
||||
print(f"📄 Created empty {VSCODE_SETTINGS_JSON_FILE}")
|
||||
|
||||
def add_or_update_settings() -> None:
|
||||
configure_setting_key('eslint.validate', ['vue', 'javascript', 'typescript'])
|
||||
# Set ESLint validation for specific file types.
|
||||
# Details: # pylint: disable-next=line-too-long
|
||||
# - https://web.archive.org/web/20230801024405/https://eslint.vuejs.org/user-guide/#visual-studio-code
|
||||
|
||||
configure_setting_key('terminal.integrated.env.linux', {"GTK_PATH": ""})
|
||||
# Unset GTK_PATH on Linux for Electron development in sandboxed environments
|
||||
# like Snap or Flatpak VSCode installations, enabling script execution.
|
||||
# Details: # pylint: disable-next=line-too-long
|
||||
# - https://archive.ph/2024.01.06-003914/https://github.com/microsoft/vscode/issues/179274, https://web.archive.org/web/20240106003915/https://github.com/microsoft/vscode/issues/179274
|
||||
|
||||
def configure_setting_key(configuration_key: str, desired_value: Any) -> None:
|
||||
try:
|
||||
with open(VSCODE_SETTINGS_JSON_FILE, 'r+', encoding='utf-8') as file:
|
||||
settings: dict = json.load(file)
|
||||
if configuration_key in settings:
|
||||
actual_value = settings[configuration_key]
|
||||
if actual_value == desired_value:
|
||||
print_skip(f"Already configured as desired: \"{configuration_key}\"")
|
||||
return
|
||||
settings[configuration_key] = desired_value
|
||||
file.seek(0)
|
||||
json.dump(settings, file, indent=4)
|
||||
file.truncate()
|
||||
print_success(f"Added or updated configuration: {configuration_key}")
|
||||
except json.JSONDecodeError:
|
||||
print_error(f"Failed to update JSON for key {configuration_key}.")
|
||||
|
||||
def install_recommended_extensions() -> None:
|
||||
if not os.path.isfile(VSCODE_EXTENSIONS_JSON_FILE):
|
||||
print_error(
|
||||
f"The extensions.json file does not exist in the path: {VSCODE_EXTENSIONS_JSON_FILE}."
|
||||
)
|
||||
return
|
||||
with open(VSCODE_EXTENSIONS_JSON_FILE, 'r', encoding='utf-8') as file:
|
||||
json_content: str = remove_json_comments(file.read())
|
||||
try:
|
||||
data: dict = json.loads(json_content)
|
||||
extensions: list[str] = data.get("recommendations", [])
|
||||
if not extensions:
|
||||
print_skip(f"No recommendations found in the {VSCODE_EXTENSIONS_JSON_FILE} file.")
|
||||
return
|
||||
vscode_cli_path = locate_vscode_cli()
|
||||
if vscode_cli_path is None:
|
||||
print_error('Visual Studio Code CLI (`code`) tool not found.')
|
||||
return
|
||||
install_vscode_extensions(vscode_cli_path, extensions)
|
||||
except json.JSONDecodeError:
|
||||
print_error(f"Invalid JSON in {VSCODE_EXTENSIONS_JSON_FILE}")
|
||||
|
||||
def locate_vscode_cli() -> Optional[str]:
|
||||
vscode_alias = which('code') # More reliable than using `code`, especially on Windows.
|
||||
if vscode_alias:
|
||||
return vscode_alias
|
||||
potential_vscode_cli_paths = [
|
||||
'/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code' # macOS VS Code may not register 'code' command in PATH
|
||||
]
|
||||
for vscode_cli_candidate_path in potential_vscode_cli_paths:
|
||||
if Path(vscode_cli_candidate_path).is_file():
|
||||
return vscode_cli_candidate_path
|
||||
return None
|
||||
|
||||
def remove_json_comments(json_like: str) -> str:
|
||||
pattern: str = r'(?:"(?:\\.|[^"\\])*"|/\*[\s\S]*?\*/|//.*)|([^:]//.*$)'
|
||||
return re.sub(
|
||||
pattern,
|
||||
lambda m: '' if m.group(1) else m.agroup(0), json_like, flags=re.MULTILINE,
|
||||
)
|
||||
|
||||
def install_vscode_extensions(vscode_cli_path: str, extensions: list[str]) -> None:
|
||||
successful_installations = 0
|
||||
for ext in extensions:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[vscode_cli_path, "--install-extension", ext],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if "already installed" in result.stdout:
|
||||
print_skip(f"Created or verified directory: {ext}")
|
||||
else:
|
||||
print_success(f"Installed extension: {ext}")
|
||||
successful_installations += 1
|
||||
print_subprocess_output(result)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print_subprocess_output(e)
|
||||
print_error(f"Failed to install extension: {ext}")
|
||||
except FileNotFoundError:
|
||||
print_error(' '.join([
|
||||
f"Visual Studio Code CLI tool not found: {vscode_cli_path}."
|
||||
f"Could not install extension: {ext}",
|
||||
]))
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
print_error(' '.join([
|
||||
f"Failed to install extension '{ext}'.",
|
||||
f"Attempted using Visual Studio Code CLI at: '{vscode_cli_path}'.",
|
||||
f"Encountered error: {e}",
|
||||
]))
|
||||
total_extensions = len(extensions)
|
||||
print_installation_results(successful_installations, total_extensions)
|
||||
|
||||
def print_subprocess_output(result: subprocess.CompletedProcess[str]) -> None:
|
||||
output = '\n'.join([text.strip() for text in [result.stdout, result.stderr] if text])
|
||||
if not output:
|
||||
return
|
||||
formatted_output = '\t' + output.strip().replace('\n', '\n\t')
|
||||
print(formatted_output)
|
||||
|
||||
def print_installation_results(successful_installations: int, total_extensions: int) -> None:
|
||||
if successful_installations == total_extensions:
|
||||
print_success(
|
||||
f"Successfully installed or verified all {total_extensions} recommended extensions."
|
||||
)
|
||||
elif successful_installations > 0:
|
||||
print_warning(
|
||||
f"Partially successful: Installed or verified {successful_installations} "
|
||||
f"out of {total_extensions} recommended extensions."
|
||||
)
|
||||
else:
|
||||
print_error("Failed to install any of the recommended extensions.")
|
||||
|
||||
def print_error(message: str) -> None:
|
||||
print(f"💀 Error: {message}", file=sys.stderr)
|
||||
|
||||
def print_success(message: str) -> None:
|
||||
print(f"✅ Success: {message}")
|
||||
|
||||
def print_skip(message: str) -> None:
|
||||
print(f"⏩ Skipped: {message}")
|
||||
|
||||
def print_warning(message: str) -> None:
|
||||
print(f"⚠️ Warning: {message}", file=sys.stderr)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
import { resolve, join } from 'path';
|
||||
import { rm, mkdtemp, stat } from 'fs/promises';
|
||||
import { spawn } from 'child_process';
|
||||
import { URL, fileURLToPath } from 'url';
|
||||
import { resolve, join } from 'node:path';
|
||||
import { rm, mkdtemp, stat } from 'node:fs/promises';
|
||||
import { spawn } from 'node:child_process';
|
||||
import { URL, fileURLToPath } from 'node:url';
|
||||
|
||||
class Paths {
|
||||
constructor(selfDirectory) {
|
||||
|
||||
@@ -35,10 +35,10 @@ Note:
|
||||
Example: npm run install-deps -- --fresh --non-deterministic
|
||||
*/
|
||||
|
||||
import { exec } from 'child_process';
|
||||
import { resolve } from 'path';
|
||||
import { access, rm, unlink } from 'fs/promises';
|
||||
import { constants } from 'fs';
|
||||
import { exec } from 'node:child_process';
|
||||
import { resolve } from 'node:path';
|
||||
import { access, rm, unlink } from 'node:fs/promises';
|
||||
import { constants } from 'node:fs';
|
||||
|
||||
const MAX_RETRIES = 5;
|
||||
const RETRY_DELAY_IN_MS = 5 /* seconds */ * 1000;
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
* --web Path for the web application
|
||||
*/
|
||||
|
||||
import { resolve } from 'path';
|
||||
import { readFile } from 'fs/promises';
|
||||
import { resolve } from 'node:path';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
|
||||
const DIST_DIRS_JSON_FILE_PATH = resolve(process.cwd(), 'dist-dirs.json'); // cannot statically import because ESLint does not support it https://github.com/eslint/eslint/discussions/15305
|
||||
const CLI_ARGUMENTS = process.argv.slice(2);
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
* --web Verify artifacts for the web application.
|
||||
*/
|
||||
|
||||
import { access, readdir } from 'fs/promises';
|
||||
import { exec } from 'child_process';
|
||||
import { resolve } from 'path';
|
||||
import { access, readdir } from 'node:fs/promises';
|
||||
import { exec } from 'node:child_process';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
const PROCESS_ARGUMENTS = process.argv.slice(2);
|
||||
const PRINT_DIST_DIR_SCRIPT_BASE_COMMAND = 'node scripts/print-dist-dir';
|
||||
|
||||
62
scripts/verify-web-server-status.js
Normal file
62
scripts/verify-web-server-status.js
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Description:
|
||||
* This script checks if a server, provided as a CLI argument, is up
|
||||
* and returns an HTTP 200 status code.
|
||||
* It is designed to provide easy verification of server availability
|
||||
* and will retry a specified number of times.
|
||||
*
|
||||
* Usage:
|
||||
* node ./scripts/verify-web-server-status.js --url [URL]
|
||||
*
|
||||
* Options:
|
||||
* --url URL of the server to check
|
||||
*/
|
||||
|
||||
import { get } from 'http';
|
||||
|
||||
const MAX_RETRIES = 30;
|
||||
const RETRY_DELAY_IN_SECONDS = 3;
|
||||
const URL_PARAMETER_NAME = '--url';
|
||||
|
||||
function checkServer(currentRetryCount = 1) {
|
||||
const serverUrl = getServerUrl();
|
||||
console.log(`Requesting ${serverUrl}...`);
|
||||
get(serverUrl, (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
console.log('🎊 Success: The server is up and returned HTTP 200.');
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.log(`Server returned HTTP status code ${res.statusCode}.`);
|
||||
retry(currentRetryCount);
|
||||
}
|
||||
}).on('error', (err) => {
|
||||
console.error('Error making the request:', err);
|
||||
retry(currentRetryCount);
|
||||
});
|
||||
}
|
||||
|
||||
function retry(currentRetryCount) {
|
||||
console.log(`Attempt ${currentRetryCount}/${MAX_RETRIES}:`);
|
||||
console.log(`Retrying in ${RETRY_DELAY_IN_SECONDS} seconds.`);
|
||||
|
||||
const remainingTime = (MAX_RETRIES - currentRetryCount) * RETRY_DELAY_IN_SECONDS;
|
||||
console.log(`Time remaining before timeout: ${remainingTime}s`);
|
||||
|
||||
if (currentRetryCount < MAX_RETRIES) {
|
||||
setTimeout(() => checkServer(currentRetryCount + 1), RETRY_DELAY_IN_SECONDS * 1000);
|
||||
} else {
|
||||
console.log('Failure: The server at did not return HTTP 200 within the allocated time. Exiting.');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function getServerUrl() {
|
||||
const urlIndex = process.argv.indexOf(URL_PARAMETER_NAME);
|
||||
if (urlIndex === -1 || urlIndex === process.argv.length - 1) {
|
||||
console.error(`Parameter "${URL_PARAMETER_NAME}" is not provided.`);
|
||||
process.exit(1);
|
||||
}
|
||||
return process.argv[urlIndex + 1];
|
||||
}
|
||||
|
||||
checkServer();
|
||||
@@ -14,3 +14,35 @@ export type ConstructorArguments<T> =
|
||||
export type FunctionKeys<T> = {
|
||||
[K in keyof T]: T[K] extends (...args: unknown[]) => unknown ? K : never;
|
||||
}[keyof T];
|
||||
|
||||
export function isString(value: unknown): value is string {
|
||||
return typeof value === 'string';
|
||||
}
|
||||
|
||||
export function isNumber(value: unknown): value is number {
|
||||
return typeof value === 'number';
|
||||
}
|
||||
|
||||
export function isBoolean(value: unknown): value is boolean {
|
||||
return typeof value === 'boolean';
|
||||
}
|
||||
|
||||
export function isFunction(value: unknown): value is (...args: unknown[]) => unknown {
|
||||
return typeof value === 'function';
|
||||
}
|
||||
|
||||
export function isArray(value: unknown): value is Array<unknown> {
|
||||
return Array.isArray(value);
|
||||
}
|
||||
|
||||
export function isPlainObject(
|
||||
variable: unknown,
|
||||
): variable is object & Record<string, unknown> {
|
||||
return Boolean(variable) // the data type of null is an object
|
||||
&& typeof variable === 'object'
|
||||
&& !Array.isArray(variable);
|
||||
}
|
||||
|
||||
export function isNullOrUndefined(value: unknown): value is (null | undefined) {
|
||||
return typeof value === 'undefined' || value === null;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { IApplication } from '@/domain/IApplication';
|
||||
import type { IApplication } from '@/domain/IApplication';
|
||||
import { AsyncLazy } from '@/infrastructure/Threading/AsyncLazy';
|
||||
import { IApplicationFactory } from './IApplicationFactory';
|
||||
import { parseApplication } from './Parser/ApplicationParser';
|
||||
import type { IApplicationFactory } from './IApplicationFactory';
|
||||
|
||||
export type ApplicationGetterType = () => IApplication;
|
||||
const ApplicationGetter: ApplicationGetterType = parseApplication;
|
||||
@@ -12,9 +12,6 @@ export class ApplicationFactory implements IApplicationFactory {
|
||||
private readonly getter: AsyncLazy<IApplication>;
|
||||
|
||||
protected constructor(costlyGetter: ApplicationGetterType) {
|
||||
if (!costlyGetter) {
|
||||
throw new Error('missing getter');
|
||||
}
|
||||
this.getter = new AsyncLazy<IApplication>(() => Promise.resolve(costlyGetter()));
|
||||
}
|
||||
|
||||
|
||||
37
src/application/CodeRunner/CodeRunner.ts
Normal file
37
src/application/CodeRunner/CodeRunner.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
export interface CodeRunner {
|
||||
runCode(
|
||||
code: string,
|
||||
fileExtension: string,
|
||||
): Promise<CodeRunOutcome>;
|
||||
}
|
||||
|
||||
export type CodeRunOutcome = SuccessfulCodeRun | FailedCodeRun;
|
||||
|
||||
export type CodeRunErrorType =
|
||||
| 'FileWriteError'
|
||||
| 'FileReadbackVerificationError'
|
||||
| 'FilePathGenerationError'
|
||||
| 'UnsupportedOperatingSystem'
|
||||
| 'FileExecutionError'
|
||||
| 'DirectoryCreationError'
|
||||
| 'UnexpectedError';
|
||||
|
||||
interface CodeRunStatus {
|
||||
readonly success: boolean;
|
||||
readonly error?: CodeRunError;
|
||||
}
|
||||
|
||||
interface SuccessfulCodeRun extends CodeRunStatus {
|
||||
readonly success: true;
|
||||
readonly error?: undefined;
|
||||
}
|
||||
|
||||
export interface FailedCodeRun extends CodeRunStatus {
|
||||
readonly success: false;
|
||||
readonly error: CodeRunError;
|
||||
}
|
||||
|
||||
export interface CodeRunError {
|
||||
readonly type: CodeRunErrorType;
|
||||
readonly message: string;
|
||||
}
|
||||
1
src/application/CodeRunner/ScriptFilename.ts
Normal file
1
src/application/CodeRunner/ScriptFilename.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const ScriptFilename = 'privacy-script' as const;
|
||||
@@ -1,7 +1,5 @@
|
||||
// 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);
|
||||
@@ -12,8 +10,6 @@ export function scrambledEqual<T>(array1: readonly T[], array2: readonly T[]) {
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { isFunction } from '@/TypeHelpers';
|
||||
|
||||
/*
|
||||
Provides a unified and resilient way to extend errors across platforms.
|
||||
|
||||
@@ -20,31 +22,33 @@ export abstract class CustomError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
export const Environment = {
|
||||
interface ErrorPrototypeManipulation {
|
||||
getSetPrototypeOf: () => (typeof Object.setPrototypeOf | undefined);
|
||||
getCaptureStackTrace: () => (typeof Error.captureStackTrace | undefined);
|
||||
}
|
||||
|
||||
export const PlatformErrorPrototypeManipulation: ErrorPrototypeManipulation = {
|
||||
getSetPrototypeOf: () => Object.setPrototypeOf,
|
||||
getCaptureStackTrace: () => Error.captureStackTrace,
|
||||
};
|
||||
|
||||
function fixPrototype(target: Error, prototype: CustomError) {
|
||||
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget
|
||||
const setPrototypeOf = Environment.getSetPrototypeOf();
|
||||
if (!functionExists(setPrototypeOf)) {
|
||||
// This is recommended by TypeScript guidelines.
|
||||
// Source: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget
|
||||
// Snapshots: https://web.archive.org/web/20231111234849/https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget, https://archive.ph/tr7cX#support-for-newtarget
|
||||
const setPrototypeOf = PlatformErrorPrototypeManipulation.getSetPrototypeOf();
|
||||
if (!isFunction(setPrototypeOf)) {
|
||||
return;
|
||||
}
|
||||
setPrototypeOf(target, prototype);
|
||||
}
|
||||
|
||||
function ensureStackTrace(target: Error) {
|
||||
const captureStackTrace = Environment.getCaptureStackTrace();
|
||||
if (!functionExists(captureStackTrace)) {
|
||||
const captureStackTrace = PlatformErrorPrototypeManipulation.getCaptureStackTrace();
|
||||
if (!isFunction(captureStackTrace)) {
|
||||
// captureStackTrace is only available on V8, if it's not available
|
||||
// modern JS engines will usually generate a stack trace on error objects when they're thrown.
|
||||
return;
|
||||
}
|
||||
captureStackTrace(target, target.constructor);
|
||||
}
|
||||
|
||||
function functionExists(func: unknown): boolean {
|
||||
// Not doing truthy/falsy check i.e. if(func) as most values are truthy in JS for robustness
|
||||
return typeof func === 'function';
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { isString } from '@/TypeHelpers';
|
||||
|
||||
// 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>
|
||||
@@ -23,7 +25,7 @@ function parseEnumValue<T extends EnumType, TEnumValue extends EnumType>(
|
||||
if (!value) {
|
||||
throw new Error(`missing ${enumName}`);
|
||||
}
|
||||
if (typeof value !== 'string') {
|
||||
if (!isString(value)) {
|
||||
throw new Error(`unexpected type of ${enumName}: "${typeof value}"`);
|
||||
}
|
||||
const casedValue = getEnumNames(enumVariable)
|
||||
@@ -40,7 +42,7 @@ export function getEnumNames
|
||||
): string[] {
|
||||
return Object
|
||||
.values(enumVariable)
|
||||
.filter((enumMember) => typeof enumMember === 'string') as string[];
|
||||
.filter((enumMember): enumMember is string => isString(enumMember));
|
||||
}
|
||||
|
||||
export function getEnumValues<T extends EnumType, TEnumValue extends EnumType>(
|
||||
@@ -54,9 +56,6 @@ 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`);
|
||||
}
|
||||
|
||||
6
src/application/Common/Log/Logger.ts
Normal file
6
src/application/Common/Log/Logger.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface Logger {
|
||||
info(...params: unknown[]): void;
|
||||
warn(...params: unknown[]): void;
|
||||
error(...params: unknown[]): void;
|
||||
debug(...params: unknown[]): void;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { assertInRange } from '@/application/Common/Enum';
|
||||
import { IScriptingLanguageFactory } from './IScriptingLanguageFactory';
|
||||
import type { IScriptingLanguageFactory } from './IScriptingLanguageFactory';
|
||||
|
||||
type Getter<T> = () => T;
|
||||
|
||||
@@ -9,19 +9,16 @@ export abstract class ScriptingLanguageFactory<T> implements IScriptingLanguageF
|
||||
|
||||
public create(language: ScriptingLanguage): T {
|
||||
assertInRange(language, ScriptingLanguage);
|
||||
if (!this.getters.has(language)) {
|
||||
const getter = this.getters.get(language);
|
||||
if (!getter) {
|
||||
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`);
|
||||
}
|
||||
|
||||
12
src/application/Common/Shuffle.ts
Normal file
12
src/application/Common/Shuffle.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
Shuffle an array of strings, returning a new array with elements in random order.
|
||||
Uses the Fisher-Yates (or Durstenfeld) algorithm.
|
||||
*/
|
||||
export function shuffle<T>(array: readonly T[]): T[] {
|
||||
const shuffledArray = [...array];
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]];
|
||||
}
|
||||
return shuffledArray;
|
||||
}
|
||||
27
src/application/Common/Timing/BatchedDebounce.ts
Normal file
27
src/application/Common/Timing/BatchedDebounce.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { PlatformTimer } from './PlatformTimer';
|
||||
import type { TimeoutType, Timer } from './Timer';
|
||||
|
||||
export function batchedDebounce<T>(
|
||||
callback: (batches: readonly T[]) => void,
|
||||
waitInMs: number,
|
||||
timer: Timer = PlatformTimer,
|
||||
): (arg: T) => void {
|
||||
let lastTimeoutId: TimeoutType | undefined;
|
||||
let batches: Array<T> = [];
|
||||
|
||||
return (arg: T) => {
|
||||
batches.push(arg);
|
||||
|
||||
const later = () => {
|
||||
callback(batches);
|
||||
batches = [];
|
||||
lastTimeoutId = undefined;
|
||||
};
|
||||
|
||||
if (lastTimeoutId !== undefined) {
|
||||
timer.clearTimeout(lastTimeoutId);
|
||||
}
|
||||
|
||||
lastTimeoutId = timer.setTimeout(later, waitInMs);
|
||||
};
|
||||
}
|
||||
7
src/application/Common/Timing/PlatformTimer.ts
Normal file
7
src/application/Common/Timing/PlatformTimer.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { Timer } from './Timer';
|
||||
|
||||
export const PlatformTimer: Timer = {
|
||||
setTimeout: (callback, ms) => setTimeout(callback, ms),
|
||||
clearTimeout: (timeoutId) => clearTimeout(timeoutId),
|
||||
dateNow: () => Date.now(),
|
||||
};
|
||||
@@ -1,47 +1,29 @@
|
||||
export type CallbackType = (..._: unknown[]) => void;
|
||||
import { PlatformTimer } from './PlatformTimer';
|
||||
import type { Timer, TimeoutType } from './Timer';
|
||||
|
||||
export type CallbackType = (..._: readonly unknown[]) => void;
|
||||
|
||||
export function throttle(
|
||||
callback: CallbackType,
|
||||
waitInMs: number,
|
||||
timer: ITimer = NodeTimer,
|
||||
timer: Timer = PlatformTimer,
|
||||
): CallbackType {
|
||||
const throttler = new Throttler(timer, waitInMs, callback);
|
||||
return (...args: unknown[]) => throttler.invoke(...args);
|
||||
}
|
||||
|
||||
// Allows aligning with both NodeJs (NodeJs.Timeout) and Window type (number)
|
||||
export type TimeoutType = ReturnType<typeof setTimeout>;
|
||||
|
||||
export interface ITimer {
|
||||
setTimeout: (callback: () => void, ms: number) => TimeoutType;
|
||||
clearTimeout: (timeoutId: TimeoutType) => void;
|
||||
dateNow(): number;
|
||||
}
|
||||
|
||||
const NodeTimer: ITimer = {
|
||||
setTimeout: (callback, ms) => setTimeout(callback, ms),
|
||||
clearTimeout: (timeoutId) => clearTimeout(timeoutId),
|
||||
dateNow: () => Date.now(),
|
||||
};
|
||||
|
||||
interface IThrottler {
|
||||
invoke: CallbackType;
|
||||
}
|
||||
|
||||
class Throttler implements IThrottler {
|
||||
private queuedExecutionId: TimeoutType;
|
||||
class Throttler {
|
||||
private queuedExecutionId: TimeoutType | undefined;
|
||||
|
||||
private previouslyRun: number;
|
||||
|
||||
constructor(
|
||||
private readonly timer: ITimer,
|
||||
private readonly timer: Timer,
|
||||
private readonly waitInMs: number,
|
||||
private readonly callback: CallbackType,
|
||||
) {
|
||||
if (!timer) { throw new Error('missing timer'); }
|
||||
if (!waitInMs) { throw new Error('missing delay'); }
|
||||
if (waitInMs < 0) { throw new Error('negative delay'); }
|
||||
if (!callback) { throw new Error('missing callback'); }
|
||||
}
|
||||
|
||||
public invoke(...args: unknown[]): void {
|
||||
8
src/application/Common/Timing/Timer.ts
Normal file
8
src/application/Common/Timing/Timer.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// Allows aligning with both NodeJs (NodeJs.Timeout) and Window type (number)
|
||||
export type TimeoutType = ReturnType<typeof setTimeout>;
|
||||
|
||||
export interface Timer {
|
||||
setTimeout: (callback: () => void, ms: number) => TimeoutType;
|
||||
clearTimeout: (timeoutId: TimeoutType) => void;
|
||||
dateNow(): number;
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { IApplication } from '@/domain/IApplication';
|
||||
import type { IApplication } from '@/domain/IApplication';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import type { 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';
|
||||
import type { IApplicationContext, IApplicationContextChangedEvent } from './IApplicationContext';
|
||||
import type { ICategoryCollectionState } from './State/ICategoryCollectionState';
|
||||
|
||||
type StateMachine = Map<OperatingSystem, ICategoryCollectionState>;
|
||||
|
||||
@@ -26,7 +26,6 @@ export class ApplicationContext implements IApplicationContext {
|
||||
public readonly app: IApplication,
|
||||
initialContext: OperatingSystem,
|
||||
) {
|
||||
validateApp(app);
|
||||
this.states = initializeStates(app);
|
||||
this.changeContext(initialContext);
|
||||
}
|
||||
@@ -36,10 +35,8 @@ export class ApplicationContext implements IApplicationContext {
|
||||
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 collection = this.app.getCollection(os);
|
||||
this.collection = collection;
|
||||
const event: IApplicationContextChangedEvent = {
|
||||
newState: this.states[os],
|
||||
oldState: this.states[this.currentOs],
|
||||
@@ -49,12 +46,6 @@ export class ApplicationContext implements IApplicationContext {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -1,27 +1,32 @@
|
||||
import { IApplicationContext } from '@/application/Context/IApplicationContext';
|
||||
import type { IApplicationContext } from '@/application/Context/IApplicationContext';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { IApplication } from '@/domain/IApplication';
|
||||
import { RuntimeEnvironment } from '@/infrastructure/RuntimeEnvironment/RuntimeEnvironment';
|
||||
import { IApplicationFactory } from '../IApplicationFactory';
|
||||
import type { IApplication } from '@/domain/IApplication';
|
||||
import { CurrentEnvironment } from '@/infrastructure/RuntimeEnvironment/RuntimeEnvironmentFactory';
|
||||
import { ApplicationFactory } from '../ApplicationFactory';
|
||||
import { ApplicationContext } from './ApplicationContext';
|
||||
import type { IApplicationFactory } from '../IApplicationFactory';
|
||||
|
||||
export async function buildContext(
|
||||
factory: IApplicationFactory = ApplicationFactory.Current,
|
||||
environment = RuntimeEnvironment.CurrentEnvironment,
|
||||
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.os);
|
||||
return new ApplicationContext(app, os);
|
||||
}
|
||||
|
||||
function getInitialOs(app: IApplication, currentOs: OperatingSystem): OperatingSystem {
|
||||
function getInitialOs(
|
||||
app: IApplication,
|
||||
currentOs: OperatingSystem | undefined,
|
||||
): OperatingSystem {
|
||||
const supportedOsList = app.getSupportedOsList();
|
||||
if (supportedOsList.includes(currentOs)) {
|
||||
if (currentOs !== undefined && supportedOsList.includes(currentOs)) {
|
||||
return currentOs;
|
||||
}
|
||||
return getMostSupportedOs(supportedOsList, app);
|
||||
}
|
||||
|
||||
function getMostSupportedOs(supportedOsList: OperatingSystem[], app: IApplication) {
|
||||
supportedOsList.sort((os1, os2) => {
|
||||
const getPriority = (os: OperatingSystem) => app.getCollection(os).totalScripts;
|
||||
return getPriority(os2) - getPriority(os1);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { IEventSource } from '@/infrastructure/Events/IEventSource';
|
||||
import { IApplication } from '@/domain/IApplication';
|
||||
import { ICategoryCollectionState, IReadOnlyCategoryCollectionState } from './State/ICategoryCollectionState';
|
||||
import type { IEventSource } from '@/infrastructure/Events/IEventSource';
|
||||
import type { IApplication } from '@/domain/IApplication';
|
||||
import type { ICategoryCollectionState, IReadOnlyCategoryCollectionState } from './State/ICategoryCollectionState';
|
||||
|
||||
export interface IReadOnlyApplicationContext {
|
||||
readonly app: IApplication;
|
||||
|
||||
@@ -1,26 +1,51 @@
|
||||
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import type { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { UserFilter } from './Filter/UserFilter';
|
||||
import { IUserFilter } from './Filter/IUserFilter';
|
||||
import { AdaptiveFilterContext } from './Filter/AdaptiveFilterContext';
|
||||
import { ApplicationCode } from './Code/ApplicationCode';
|
||||
import { UserSelection } from './Selection/UserSelection';
|
||||
import { IUserSelection } from './Selection/IUserSelection';
|
||||
import { ICategoryCollectionState } from './ICategoryCollectionState';
|
||||
import { IApplicationCode } from './Code/IApplicationCode';
|
||||
import { UserSelectionFacade } from './Selection/UserSelectionFacade';
|
||||
import type { FilterContext } from './Filter/FilterContext';
|
||||
import type { UserSelection } from './Selection/UserSelection';
|
||||
import type { ICategoryCollectionState } from './ICategoryCollectionState';
|
||||
import type { IApplicationCode } from './Code/IApplicationCode';
|
||||
|
||||
export class CategoryCollectionState implements ICategoryCollectionState {
|
||||
public readonly os: OperatingSystem;
|
||||
|
||||
public readonly code: IApplicationCode;
|
||||
|
||||
public readonly selection: IUserSelection;
|
||||
public readonly selection: UserSelection;
|
||||
|
||||
public readonly filter: IUserFilter;
|
||||
public readonly filter: FilterContext;
|
||||
|
||||
public constructor(readonly collection: ICategoryCollection) {
|
||||
this.selection = new UserSelection(collection, []);
|
||||
this.code = new ApplicationCode(this.selection, collection.scripting);
|
||||
this.filter = new UserFilter(collection);
|
||||
public constructor(
|
||||
public readonly collection: ICategoryCollection,
|
||||
selectionFactory = DefaultSelectionFactory,
|
||||
codeFactory = DefaultCodeFactory,
|
||||
filterFactory = DefaultFilterFactory,
|
||||
) {
|
||||
this.selection = selectionFactory(collection, []);
|
||||
this.code = codeFactory(this.selection.scripts, collection.scripting);
|
||||
this.filter = filterFactory(collection);
|
||||
this.os = collection.os;
|
||||
}
|
||||
}
|
||||
|
||||
export type CodeFactory = (
|
||||
...params: ConstructorParameters<typeof ApplicationCode>
|
||||
) => IApplicationCode;
|
||||
|
||||
const DefaultCodeFactory: CodeFactory = (...params) => new ApplicationCode(...params);
|
||||
|
||||
export type SelectionFactory = (
|
||||
...params: ConstructorParameters<typeof UserSelectionFacade>
|
||||
) => UserSelection;
|
||||
|
||||
const DefaultSelectionFactory: SelectionFactory = (
|
||||
...params
|
||||
) => new UserSelectionFacade(...params);
|
||||
|
||||
export type FilterFactory = (
|
||||
...params: ConstructorParameters<typeof AdaptiveFilterContext>
|
||||
) => FilterContext;
|
||||
|
||||
const DefaultFilterFactory: FilterFactory = (...params) => new AdaptiveFilterContext(...params);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
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 type { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||
import type { SelectedScript } from '@/application/Context/State/Selection/Script/SelectedScript';
|
||||
import type { ReadonlyScriptSelection } from '@/application/Context/State/Selection/Script/ScriptSelection';
|
||||
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';
|
||||
import type { IUserScriptGenerator } from './Generation/IUserScriptGenerator';
|
||||
import type { ICodeChangedEvent } from './Event/ICodeChangedEvent';
|
||||
import type { IApplicationCode } from './IApplicationCode';
|
||||
|
||||
export class ApplicationCode implements IApplicationCode {
|
||||
public readonly changed = new EventSource<ICodeChangedEvent>();
|
||||
@@ -17,15 +17,12 @@ export class ApplicationCode implements IApplicationCode {
|
||||
private scriptPositions = new Map<SelectedScript, CodePosition>();
|
||||
|
||||
constructor(
|
||||
userSelection: IReadOnlyUserSelection,
|
||||
selection: ReadonlyScriptSelection,
|
||||
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(selection.selectedScripts);
|
||||
selection.changed.on((scripts) => {
|
||||
this.setCode(scripts);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition';
|
||||
import { SelectedScript } from '../../Selection/SelectedScript';
|
||||
import { ICodeChangedEvent } from './ICodeChangedEvent';
|
||||
import type { IScript } from '@/domain/IScript';
|
||||
import type { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition';
|
||||
import type { SelectedScript } from '@/application/Context/State/Selection/Script/SelectedScript';
|
||||
import type { ICodeChangedEvent } from './ICodeChangedEvent';
|
||||
|
||||
export class CodeChangedEvent implements ICodeChangedEvent {
|
||||
public readonly code: string;
|
||||
@@ -36,7 +36,18 @@ export class CodeChangedEvent implements ICodeChangedEvent {
|
||||
}
|
||||
|
||||
public getScriptPositionInCode(script: IScript): ICodePosition {
|
||||
return this.scripts.get(script);
|
||||
return this.getPositionById(script.id);
|
||||
}
|
||||
|
||||
private getPositionById(scriptId: string): ICodePosition {
|
||||
const position = [...this.scripts.entries()]
|
||||
.filter(([s]) => s.id === scriptId)
|
||||
.map(([, pos]) => pos)
|
||||
.at(0);
|
||||
if (!position) {
|
||||
throw new Error('Unknown script: Position could not be found for the script');
|
||||
}
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition';
|
||||
import type { IScript } from '@/domain/IScript';
|
||||
import type { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition';
|
||||
|
||||
export interface ICodeChangedEvent {
|
||||
readonly code: string;
|
||||
addedScripts: ReadonlyArray<IScript>;
|
||||
removedScripts: ReadonlyArray<IScript>;
|
||||
changedScripts: ReadonlyArray<IScript>;
|
||||
readonly addedScripts: ReadonlyArray<IScript>;
|
||||
readonly removedScripts: ReadonlyArray<IScript>;
|
||||
readonly changedScripts: ReadonlyArray<IScript>;
|
||||
isEmpty(): boolean;
|
||||
getScriptPositionInCode(script: IScript): ICodePosition;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ICodeBuilder } from './ICodeBuilder';
|
||||
import type { ICodeBuilder } from './ICodeBuilder';
|
||||
|
||||
const TotalFunctionSeparatorChars = 58;
|
||||
|
||||
@@ -16,7 +16,9 @@ export abstract class CodeBuilder implements ICodeBuilder {
|
||||
return this;
|
||||
}
|
||||
const lines = code.match(/[^\r\n]+/g);
|
||||
this.lines.push(...lines);
|
||||
if (lines) {
|
||||
this.lines.push(...lines);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
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';
|
||||
import type { ICodeBuilder } from './ICodeBuilder';
|
||||
import type { ICodeBuilderFactory } from './ICodeBuilderFactory';
|
||||
|
||||
export class CodeBuilderFactory
|
||||
extends ScriptingLanguageFactory<ICodeBuilder>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IScriptingLanguageFactory } from '@/application/Common/ScriptingLanguage/IScriptingLanguageFactory';
|
||||
import { ICodeBuilder } from './ICodeBuilder';
|
||||
import type { IScriptingLanguageFactory } from '@/application/Common/ScriptingLanguage/IScriptingLanguageFactory';
|
||||
import type { ICodeBuilder } from './ICodeBuilder';
|
||||
|
||||
export type ICodeBuilderFactory = IScriptingLanguageFactory<ICodeBuilder>;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition';
|
||||
import type { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition';
|
||||
import type { SelectedScript } from '@/application/Context/State/Selection/Script/SelectedScript';
|
||||
|
||||
export interface IUserScript {
|
||||
code: string;
|
||||
scriptPositions: Map<SelectedScript, ICodePosition>;
|
||||
readonly code: string;
|
||||
readonly scriptPositions: Map<SelectedScript, ICodePosition>;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||
import { IUserScript } from './IUserScript';
|
||||
import type { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||
import type { SelectedScript } from '@/application/Context/State/Selection/Script/SelectedScript';
|
||||
import type { IUserScript } from './IUserScript';
|
||||
|
||||
export interface IUserScriptGenerator {
|
||||
buildCode(
|
||||
selectedScripts: ReadonlyArray<SelectedScript>,
|
||||
scriptingDefinition: IScriptingDefinition): IUserScript;
|
||||
scriptingDefinition: IScriptingDefinition,
|
||||
): IUserScript;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition';
|
||||
import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||
import type { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition';
|
||||
import type { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||
import type { SelectedScript } from '@/application/Context/State/Selection/Script/SelectedScript';
|
||||
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';
|
||||
import type { IUserScriptGenerator } from './IUserScriptGenerator';
|
||||
import type { IUserScript } from './IUserScript';
|
||||
import type { ICodeBuilder } from './ICodeBuilder';
|
||||
import type { ICodeBuilderFactory } from './ICodeBuilderFactory';
|
||||
|
||||
export class UserScriptGenerator implements IUserScriptGenerator {
|
||||
constructor(private readonly codeBuilderFactory: ICodeBuilderFactory = new CodeBuilderFactory()) {
|
||||
@@ -17,8 +17,6 @@ export class UserScriptGenerator implements IUserScriptGenerator {
|
||||
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>() };
|
||||
}
|
||||
@@ -68,8 +66,19 @@ function appendSelection(
|
||||
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;
|
||||
const scriptCode = getSelectedCode(selection);
|
||||
return builder
|
||||
.appendLine()
|
||||
.appendFunction(name, scriptCode);
|
||||
}
|
||||
|
||||
function getSelectedCode(selection: SelectedScript): string {
|
||||
const { code } = selection.script;
|
||||
if (!selection.revert) {
|
||||
return code.execute;
|
||||
}
|
||||
if (!code.revert) {
|
||||
throw new Error('Reverted script lacks revert code.');
|
||||
}
|
||||
return code.revert;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IEventSource } from '@/infrastructure/Events/IEventSource';
|
||||
import { ICodeChangedEvent } from './Event/ICodeChangedEvent';
|
||||
import type { IEventSource } from '@/infrastructure/Events/IEventSource';
|
||||
import type { ICodeChangedEvent } from './Event/ICodeChangedEvent';
|
||||
|
||||
export interface IApplicationCode {
|
||||
readonly changed: IEventSource<ICodeChangedEvent>;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ICodePosition } from './ICodePosition';
|
||||
import type { ICodePosition } from './ICodePosition';
|
||||
|
||||
export class CodePosition implements ICodePosition {
|
||||
public get totalLines(): number {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { EventSource } from '@/infrastructure/Events/EventSource';
|
||||
import type { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import { FilterChange } from './Event/FilterChange';
|
||||
import { LinearFilterStrategy } from './Strategy/LinearFilterStrategy';
|
||||
import type { FilterResult } from './Result/FilterResult';
|
||||
import type { FilterContext } from './FilterContext';
|
||||
import type { FilterChangeDetails } from './Event/FilterChangeDetails';
|
||||
import type { FilterStrategy } from './Strategy/FilterStrategy';
|
||||
|
||||
export class AdaptiveFilterContext implements FilterContext {
|
||||
public readonly filterChanged = new EventSource<FilterChangeDetails>();
|
||||
|
||||
public currentFilter: FilterResult | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly collection: ICategoryCollection,
|
||||
private readonly filterStrategy: FilterStrategy = new LinearFilterStrategy(),
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
public applyFilter(filter: string): void {
|
||||
if (!filter) {
|
||||
throw new Error('Filter must be defined and not empty. Use clearFilter() to remove the filter');
|
||||
}
|
||||
const result = this.filterStrategy.applyFilter(filter, this.collection);
|
||||
this.currentFilter = result;
|
||||
this.filterChanged.notify(FilterChange.forApply(this.currentFilter));
|
||||
}
|
||||
|
||||
public clearFilter(): void {
|
||||
this.currentFilter = undefined;
|
||||
this.filterChanged.notify(FilterChange.forClear());
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,37 @@
|
||||
import { IFilterResult } from '@/application/Context/State/Filter/IFilterResult';
|
||||
import type { FilterResult } from '@/application/Context/State/Filter/Result/FilterResult';
|
||||
import { FilterActionType } from './FilterActionType';
|
||||
import { IFilterChangeDetails, IFilterChangeDetailsVisitor } from './IFilterChangeDetails';
|
||||
import type {
|
||||
FilterChangeDetails, FilterChangeDetailsVisitor,
|
||||
ApplyFilterAction, ClearFilterAction,
|
||||
} from './FilterChangeDetails';
|
||||
|
||||
export class FilterChange implements IFilterChangeDetails {
|
||||
public static forApply(filter: IFilterResult) {
|
||||
if (!filter) {
|
||||
throw new Error('missing filter');
|
||||
}
|
||||
return new FilterChange(FilterActionType.Apply, filter);
|
||||
export class FilterChange implements FilterChangeDetails {
|
||||
public static forApply(
|
||||
filter: FilterResult,
|
||||
): FilterChangeDetails {
|
||||
return new FilterChange({ type: FilterActionType.Apply, filter });
|
||||
}
|
||||
|
||||
public static forClear() {
|
||||
return new FilterChange(FilterActionType.Clear);
|
||||
public static forClear(): FilterChangeDetails {
|
||||
return new FilterChange({ type: FilterActionType.Clear });
|
||||
}
|
||||
|
||||
private constructor(
|
||||
public readonly actionType: FilterActionType,
|
||||
public readonly filter?: IFilterResult,
|
||||
) { }
|
||||
private constructor(public readonly action: ApplyFilterAction | ClearFilterAction) { }
|
||||
|
||||
public visit(visitor: IFilterChangeDetailsVisitor): void {
|
||||
if (!visitor) {
|
||||
throw new Error('missing visitor');
|
||||
}
|
||||
switch (this.actionType) {
|
||||
public visit(visitor: FilterChangeDetailsVisitor): void {
|
||||
switch (this.action.type) {
|
||||
case FilterActionType.Apply:
|
||||
visitor.onApply(this.filter);
|
||||
if (visitor.onApply) {
|
||||
visitor.onApply(this.action.filter);
|
||||
}
|
||||
break;
|
||||
case FilterActionType.Clear:
|
||||
visitor.onClear();
|
||||
if (visitor.onClear) {
|
||||
visitor.onClear();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown action type: ${this.actionType}`);
|
||||
throw new Error(`Unknown action: ${this.action}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { FilterResult } from '@/application/Context/State/Filter/Result/FilterResult';
|
||||
import type { FilterActionType } from './FilterActionType';
|
||||
|
||||
export interface FilterChangeDetails {
|
||||
readonly action: FilterAction;
|
||||
visit(visitor: FilterChangeDetailsVisitor): void;
|
||||
}
|
||||
|
||||
export interface FilterChangeDetailsVisitor {
|
||||
readonly onClear?: () => void;
|
||||
readonly onApply?: (filter: FilterResult) => void;
|
||||
}
|
||||
|
||||
export type ApplyFilterAction = {
|
||||
readonly type: FilterActionType.Apply,
|
||||
readonly filter: FilterResult;
|
||||
};
|
||||
|
||||
export type ClearFilterAction = {
|
||||
readonly type: FilterActionType.Clear,
|
||||
};
|
||||
|
||||
export type FilterAction = ApplyFilterAction | ClearFilterAction;
|
||||
@@ -1,14 +0,0 @@
|
||||
import { IFilterResult } from '@/application/Context/State/Filter/IFilterResult';
|
||||
import { FilterActionType } from './FilterActionType';
|
||||
|
||||
export interface IFilterChangeDetails {
|
||||
readonly actionType: FilterActionType;
|
||||
readonly filter?: IFilterResult;
|
||||
|
||||
visit(visitor: IFilterChangeDetailsVisitor): void;
|
||||
}
|
||||
|
||||
export interface IFilterChangeDetailsVisitor {
|
||||
onClear(): void;
|
||||
onApply(filter: IFilterResult): void;
|
||||
}
|
||||
13
src/application/Context/State/Filter/FilterContext.ts
Normal file
13
src/application/Context/State/Filter/FilterContext.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { IEventSource } from '@/infrastructure/Events/IEventSource';
|
||||
import type { FilterResult } from './Result/FilterResult';
|
||||
import type { FilterChangeDetails } from './Event/FilterChangeDetails';
|
||||
|
||||
export interface ReadonlyFilterContext {
|
||||
readonly currentFilter: FilterResult | undefined;
|
||||
readonly filterChanged: IEventSource<FilterChangeDetails>;
|
||||
}
|
||||
|
||||
export interface FilterContext extends ReadonlyFilterContext {
|
||||
applyFilter(filter: string): void;
|
||||
clearFilter(): void;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { IEventSource } from '@/infrastructure/Events/IEventSource';
|
||||
import { IFilterResult } from './IFilterResult';
|
||||
import { IFilterChangeDetails } from './Event/IFilterChangeDetails';
|
||||
|
||||
export interface IReadOnlyUserFilter {
|
||||
readonly currentFilter: IFilterResult | undefined;
|
||||
readonly filterChanged: IEventSource<IFilterChangeDetails>;
|
||||
}
|
||||
|
||||
export interface IUserFilter extends IReadOnlyUserFilter {
|
||||
applyFilter(filter: string): void;
|
||||
clearFilter(): void;
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { ICategory } from '@/domain/ICategory';
|
||||
import { IFilterResult } from './IFilterResult';
|
||||
import type { IScript } from '@/domain/IScript';
|
||||
import type { ICategory } from '@/domain/ICategory';
|
||||
import type { FilterResult } from './FilterResult';
|
||||
|
||||
export class FilterResult implements IFilterResult {
|
||||
export class AppliedFilterResult implements FilterResult {
|
||||
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 {
|
||||
@@ -1,6 +1,6 @@
|
||||
import { IScript, ICategory } from '@/domain/ICategory';
|
||||
import type { IScript, ICategory } from '@/domain/ICategory';
|
||||
|
||||
export interface IFilterResult {
|
||||
export interface FilterResult {
|
||||
readonly categoryMatches: ReadonlyArray<ICategory>;
|
||||
readonly scriptMatches: ReadonlyArray<IScript>;
|
||||
readonly query: string;
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import type { FilterResult } from '../Result/FilterResult';
|
||||
|
||||
export interface FilterStrategy {
|
||||
applyFilter(
|
||||
filter: string,
|
||||
collection: ICategoryCollection,
|
||||
): FilterResult;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import type { ICategory, IScript } from '@/domain/ICategory';
|
||||
import type { IScriptCode } from '@/domain/IScriptCode';
|
||||
import type { IDocumentable } from '@/domain/IDocumentable';
|
||||
import type { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import { AppliedFilterResult } from '../Result/AppliedFilterResult';
|
||||
import type { FilterStrategy } from './FilterStrategy';
|
||||
import type { FilterResult } from '../Result/FilterResult';
|
||||
|
||||
export class LinearFilterStrategy implements FilterStrategy {
|
||||
applyFilter(filter: string, collection: ICategoryCollection): FilterResult {
|
||||
const filterLowercase = filter.toLocaleLowerCase();
|
||||
const filteredScripts = collection.getAllScripts().filter(
|
||||
(script) => matchesScript(script, filterLowercase),
|
||||
);
|
||||
const filteredCategories = collection.getAllCategories().filter(
|
||||
(category) => matchesCategory(category, filterLowercase),
|
||||
);
|
||||
return new AppliedFilterResult(
|
||||
filteredScripts,
|
||||
filteredCategories,
|
||||
filter,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function matchesCategory(
|
||||
category: ICategory,
|
||||
filterLowercase: string,
|
||||
): boolean {
|
||||
return matchesAny(
|
||||
() => matchName(category.name, filterLowercase),
|
||||
() => matchDocumentation(category, filterLowercase),
|
||||
);
|
||||
}
|
||||
|
||||
function matchesScript(
|
||||
script: IScript,
|
||||
filterLowercase: string,
|
||||
): boolean {
|
||||
return matchesAny(
|
||||
() => matchName(script.name, filterLowercase),
|
||||
() => matchCode(script.code, filterLowercase),
|
||||
() => matchDocumentation(script, filterLowercase),
|
||||
);
|
||||
}
|
||||
|
||||
function matchesAny(
|
||||
...predicates: ReadonlyArray<() => boolean>
|
||||
): boolean {
|
||||
return predicates.some((predicate) => predicate());
|
||||
}
|
||||
|
||||
function matchName(
|
||||
name: string,
|
||||
filterLowercase: string,
|
||||
): boolean {
|
||||
return name.toLowerCase().includes(filterLowercase);
|
||||
}
|
||||
|
||||
function matchCode(
|
||||
code: IScriptCode,
|
||||
filterLowercase: string,
|
||||
): boolean {
|
||||
if (code.execute.toLowerCase().includes(filterLowercase)) {
|
||||
return true;
|
||||
}
|
||||
if (code.revert?.toLowerCase().includes(filterLowercase)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function matchDocumentation(
|
||||
documentable: IDocumentable,
|
||||
filterLowercase: string,
|
||||
): boolean {
|
||||
return documentable.docs.some(
|
||||
(doc) => doc.toLocaleLowerCase().includes(filterLowercase),
|
||||
);
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
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';
|
||||
import { IFilterChangeDetails } from './Event/IFilterChangeDetails';
|
||||
import { FilterChange } from './Event/FilterChange';
|
||||
|
||||
export class UserFilter implements IUserFilter {
|
||||
public readonly filterChanged = new EventSource<IFilterChangeDetails>();
|
||||
|
||||
public currentFilter: IFilterResult | undefined;
|
||||
|
||||
constructor(private collection: ICategoryCollection) {
|
||||
|
||||
}
|
||||
|
||||
public applyFilter(filter: string): void {
|
||||
if (!filter) {
|
||||
throw new Error('Filter must be defined and not empty. Use clearFilter() 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.filterChanged.notify(FilterChange.forApply(this.currentFilter));
|
||||
}
|
||||
|
||||
public clearFilter(): void {
|
||||
this.currentFilter = undefined;
|
||||
this.filterChanged.notify(FilterChange.forClear());
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import type { 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';
|
||||
import type { IApplicationCode } from './Code/IApplicationCode';
|
||||
import type { ReadonlyFilterContext, FilterContext } from './Filter/FilterContext';
|
||||
import type { ReadonlyUserSelection, UserSelection } from './Selection/UserSelection';
|
||||
|
||||
export interface IReadOnlyCategoryCollectionState {
|
||||
readonly code: IApplicationCode;
|
||||
readonly os: OperatingSystem;
|
||||
readonly filter: IReadOnlyUserFilter;
|
||||
readonly selection: IReadOnlyUserSelection;
|
||||
readonly filter: ReadonlyFilterContext;
|
||||
readonly selection: ReadonlyUserSelection;
|
||||
readonly collection: ICategoryCollection;
|
||||
}
|
||||
|
||||
export interface ICategoryCollectionState extends IReadOnlyCategoryCollectionState {
|
||||
readonly filter: IUserFilter;
|
||||
readonly selection: IUserSelection;
|
||||
readonly filter: FilterContext;
|
||||
readonly selection: UserSelection;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user