├── .codeclimate.yml
├── .editorconfig
├── .eslintrc.json
├── .gitattributes
├── .github
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
├── dependabot.yml
└── workflows
│ ├── build.yml
│ ├── coverage.yml
│ └── publish.yml
├── .gitignore
├── .huskyrc
├── .lintstagedrc
├── .mocharc.json
├── .npmrc
├── .nycrc
├── .vscode
├── extensions.json
├── launch.json
├── settings.json
└── tasks.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── api-extractor.json
├── docs
├── api.md
├── cli.md
├── examples
│ ├── check-es5-js.md
│ ├── check-js-ts.md
│ ├── cli.md
│ ├── create-es5-js.md
│ ├── create-js-ts.md
│ ├── createDirHash-es5-js.md
│ ├── createDirHash-js-ts.md
│ ├── createFileHash-es5-js.md
│ ├── createFileHash-js-ts.md
│ ├── createFilesHash-es5-js.md
│ ├── createFilesHash-js-ts.md
│ ├── getExclusionsFromIgnoreFile-es5-js.md
│ ├── getExclusionsFromIgnoreFile-js-ts.md
│ ├── getIntegrityOptionsFromConfig-es5-js.md
│ ├── getIntegrityOptionsFromConfig-js-ts.md
│ ├── getManifestIntegrity-es5-js.md
│ ├── getManifestIntegrity-js-ts.md
│ ├── persist-es5-js.md
│ ├── persist-js-ts.md
│ ├── updateManifestIntegrity-es5-js.md
│ └── updateManifestIntegrity-js-ts.md
└── importation.md
├── media
├── integrity_file.png
├── integrity_file.svg
├── nsri-logo.png
└── nsri-logo.svg
├── package-lock.json
├── package.json
├── rollup.config.js
├── src
├── abstractions
│ └── baseLogger.ts
├── app
│ ├── integrity.ts
│ └── schemas
│ │ └── v1
│ │ └── schema.json
├── cli.ts
├── cli
│ └── index.ts
├── common
│ ├── configExplorer.ts
│ ├── constants.ts
│ ├── enums.ts
│ ├── fsAsync.ts
│ ├── logger.ts
│ ├── utils.ts
│ └── yargsParser.ts
├── index.ts
└── interfaces
│ ├── arguments.ts
│ ├── configOptions.ts
│ ├── cryptoOptions.ts
│ ├── hashObject.ts
│ ├── integrityObject.ts
│ ├── integrityOptions.ts
│ ├── manifestInfo.ts
│ ├── normalizedCryptoOptions.ts
│ ├── normalizedIntegrityOptions.ts
│ ├── parsedArgs.ts
│ ├── spinner.ts
│ └── verboseHashObject.ts
├── test
├── api
│ ├── behavior.test.ts
│ ├── check.test.ts
│ ├── create.test.ts
│ ├── createDirHash.test.ts
│ ├── createFileHash.test.ts
│ ├── createFilesHash.test.ts
│ ├── getExclusionsFromIgnoreFile.test.ts
│ ├── getIntegrityOptionsFromConfig.test.ts
│ ├── getManifestIntegrity.test.ts
│ ├── persist.test.ts
│ └── updateManifestIntegrity.test.ts
├── cli
│ └── index.test.ts
├── common
│ ├── configExplorer.test.ts
│ ├── logger.test.ts
│ ├── utils.test.ts
│ └── yargsParser.test.ts
├── cosmiconfig
│ ├── configjs
│ │ └── nsri.config.js
│ ├── js
│ │ └── .nsrirc.js
│ ├── json
│ │ └── .nsrirc.json
│ ├── packagejson
│ │ └── package.json
│ ├── rc
│ │ └── .nsrirc
│ ├── yaml
│ │ └── .nsrirc.yaml
│ └── yml
│ │ └── .nsrirc.yml
├── fixtures
│ ├── .integrity.json
│ ├── directory.1
│ │ ├── .integrity.json
│ │ ├── anotherFileToHash.txt
│ │ └── otherFileToHash.txt
│ ├── directory
│ │ ├── .integrity.json
│ │ ├── anotherFileToHash.txt
│ │ └── otherFileToHash.txt
│ ├── fileToHash.txt
│ ├── fixtures
│ │ ├── .integrity.json
│ │ ├── directory.1
│ │ │ ├── .integrity.json
│ │ │ ├── anotherFileToHash.txt
│ │ │ └── otherFileToHash.txt
│ │ ├── directory
│ │ │ ├── .integrity.json
│ │ │ └── anotherFileToHash.txt
│ │ └── fileToHash.txt
│ └── sameContentWithFileToHash.txt
├── helper.test.ts
├── helper.ts
├── ignoreFile
│ └── .nsriignore
└── typings.d.ts
├── tsconfig.dev.json
├── tsconfig.json
└── tsconfig.strict.dev.json
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | version: "2"
2 | checks:
3 | file-lines:
4 | enabled: false
5 | method-complexity:
6 | enabled: false
7 | method-lines:
8 | enabled: false
9 | similar-code:
10 | enabled: false
11 | plugins:
12 | markdownlint:
13 | enabled: true
14 | checks:
15 | MD003:
16 | enabled: false
17 | MD013:
18 | enabled: false
19 | MD022:
20 | enabled: false
21 | MD033:
22 | enabled: false
23 | MD036:
24 | enabled: false
25 | nodesecurity:
26 | enabled: true
27 | tslint:
28 | enabled: false
29 | exclude_patterns:
30 | - "!src/**/*.ts"
31 | - "!test/**/*.ts"
32 | - "src/cli.ts"
33 | - "test/"
34 | - ".github/"
35 | - ".vscode/"
36 | - "*.json"
37 | - ".*.yml"
38 | - "*.lock"
39 | - ".editorconfig"
40 | - ".git*"
41 | - "**/*.d.ts"
42 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [.integrity.json]
12 | insert_final_newline = false
13 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "es6": true,
4 | "node": true
5 | },
6 | "extends": [
7 | "eslint:recommended",
8 | "plugin:@typescript-eslint/eslint-recommended",
9 | "plugin:@typescript-eslint/recommended",
10 | "plugin:@typescript-eslint/recommended-requiring-type-checking",
11 | "plugin:import/errors",
12 | "plugin:import/warnings",
13 | "plugin:import/typescript"
14 | ],
15 | "ignorePatterns": [
16 | "node_modules",
17 | "lib",
18 | "out",
19 | "coverage",
20 | "/.*",
21 | "/*.*",
22 | ".*",
23 | "*.d.ts"
24 | ],
25 | "parser": "@typescript-eslint/parser",
26 | "parserOptions": {
27 | "project": "tsconfig.dev.json",
28 | "sourceType": "module"
29 | },
30 | "plugins": [
31 | "@typescript-eslint",
32 | "import"
33 | ],
34 | "rules": {
35 | "@typescript-eslint/array-type": [
36 | "error",
37 | {
38 | "default": "array-simple"
39 | }
40 | ],
41 | "@typescript-eslint/ban-ts-ignore": "off",
42 | "@typescript-eslint/ban-ts-comment": [
43 | "error",
44 | {
45 | "ts-ignore": false
46 | }
47 | ],
48 | "@typescript-eslint/consistent-type-definitions": "error",
49 | "@typescript-eslint/explicit-member-accessibility": [
50 | "error",
51 | {
52 | "accessibility": "explicit"
53 | }
54 | ],
55 | "@typescript-eslint/indent": [
56 | "error",
57 | 2
58 | ],
59 | "@typescript-eslint/member-ordering": "error",
60 | "@typescript-eslint/naming-convention": "error",
61 | "@typescript-eslint/no-empty-function": "error",
62 | "@typescript-eslint/no-use-before-define": "error",
63 | "@typescript-eslint/parameter-properties": "error",
64 | "@typescript-eslint/prefer-for-of": "error",
65 | "@typescript-eslint/prefer-function-type": "error",
66 | "@typescript-eslint/quotes": [
67 | "error",
68 | "single",
69 | {
70 | "allowTemplateLiterals": true
71 | }
72 | ],
73 | "@typescript-eslint/semi": "error",
74 | "@typescript-eslint/unified-signatures": "error",
75 | "import/no-unresolved": "error",
76 | "import/no-extraneous-dependencies": [
77 | "error",
78 | {
79 | "devDependencies": [
80 | "**/test/**"
81 | ]
82 | }
83 | ],
84 | "import/no-internal-modules": [
85 | "error",
86 | {
87 | "allow": [
88 | "**/src/**"
89 | ]
90 | }
91 | ],
92 | "arrow-body-style": "error",
93 | "arrow-parens": [
94 | "error",
95 | "as-needed"
96 | ],
97 | "comma-dangle": [
98 | "error",
99 | "always-multiline"
100 | ],
101 | "complexity": "off",
102 | "curly": "error",
103 | "dot-notation": "error",
104 | "eol-last": "error",
105 | "eqeqeq": [
106 | "error",
107 | "smart"
108 | ],
109 | "guard-for-in": "error",
110 | "id-blacklist": [
111 | "error",
112 | "any",
113 | "number",
114 | "string",
115 | "boolean"
116 | ],
117 | "id-match": "error",
118 | "max-classes-per-file": [
119 | "error",
120 | 1
121 | ],
122 | "max-len": [
123 | "error",
124 | {
125 | "code": 120
126 | }
127 | ],
128 | "new-parens": "error",
129 | "no-bitwise": "error",
130 | "no-caller": "error",
131 | "no-console": "error",
132 | "no-duplicate-imports": "error",
133 | "no-eval": "error",
134 | "no-extra-bind": "error",
135 | "no-invalid-this": "error",
136 | "no-multiple-empty-lines": "error",
137 | "no-new-func": "error",
138 | "no-new-wrappers": "error",
139 | "no-return-await": "error",
140 | "no-sequences": "error",
141 | "no-shadow": [
142 | "error",
143 | {
144 | "hoist": "all",
145 | "allow": ["CryptoEncoding"]
146 | }
147 | ],
148 | "no-template-curly-in-string": "error",
149 | "no-throw-literal": "error",
150 | "no-trailing-spaces": "error",
151 | "no-undef-init": "error",
152 | "no-underscore-dangle": "error",
153 | "no-unused-expressions": "error",
154 | "no-nonoctal-decimal-escape": "error",
155 | "no-unsafe-optional-chaining": "error",
156 | "object-shorthand": "error",
157 | "one-var": [
158 | "error",
159 | "never"
160 | ],
161 | "prefer-arrow-callback": "error",
162 | "prefer-object-spread": "error",
163 | "quote-props": [
164 | "error",
165 | "as-needed"
166 | ],
167 | "radix": "error",
168 | "space-before-function-paren": [
169 | "error",
170 | {
171 | "anonymous": "always",
172 | "named": "never",
173 | "asyncArrow": "always"
174 | }
175 | ],
176 | "space-in-parens": [
177 | "error",
178 | "never"
179 | ],
180 | "spaced-comment": [
181 | "error",
182 | "always",
183 | {
184 | "markers": [
185 | "#region",
186 | "#endregion"
187 | ]
188 | }
189 | ]
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text eol=lf
5 |
6 | ###############################################################################
7 | # Denote all files that are truly binary and should not be modified.
8 | ###############################################################################
9 | *.psd binary
10 | *.ai binary
11 | *.png binary
12 | *.gif binary
13 | *.jpg binary
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at jimikar@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribution Guidelines
2 |
3 | **Issue:**
4 |
5 | 1. Do a [search](https://github.com/JimiC/nsri/issues?q=is%3Aopen+is%3Aissue) before opening a new issue. If it exists, add a comment to it instead of opening a new one.
6 | 1. Fill in the provided issue template.
7 | 1. Include screenshots or GIFs whenever possible.
8 | 1. Don't be rude, describe your problem cool.
9 |
10 | **Pull Request:**
11 |
12 | 1. Fill in the provided pull request template.
13 | 1. Reference issues whenever possible.
14 |
15 | **Code Contribution:**
16 |
17 | 1. Keep your code clean.
18 | 1. Follow the existing coding style.
19 | 1. Obey the TSLint Rules.
20 | 1. Add test cases for every new functionality.
21 | 1. Follow commit message [guidelines](https://gist.github.com/robertpainsi/b632364184e70900af4ab688decf6f53).
22 | 1. Make sure that the project builts before submitting a PR.
23 |
24 | Thanks for helping out! :smile:
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | **Non-duplicate confirmation**
5 |
6 | - [ ] I'm sure this issue is not a _duplicate_.
7 |
8 | **Environment**
9 |
10 | - OS:
11 | - Version:
12 | - Package version:
13 |
14 | **Expected behavior**
15 |
16 | - A clear and concise description of what you expected to happen
17 |
18 | **Actual behavior**
19 |
20 | - A clear and concise description of what you actually happen
21 |
22 | **Screenshots**
23 |
24 | - If applicable, add screenshots to help explain your issue
25 |
26 | **Issue reproduction steps**
27 |
28 | - Do '...'
29 | - ...
30 | - See error
31 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | _**Fixes #IssueNumber**_
4 |
5 | **Proposed changes:**
6 |
7 | - [ ] Add
8 | - [ ] Delete
9 | - [ ] Fix
10 | - [ ] Prepare
11 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "npm"
4 | directory: "/"
5 | schedule:
6 | interval: "daily"
7 | ignore:
8 | - dependency-name: "detect-indent"
9 | - dependency-name: "@types/node"
10 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | push:
5 | tags-ignore:
6 | - '*'
7 | branches:
8 | - main
9 | pull_request:
10 | branches:
11 | - main
12 |
13 | concurrency:
14 | group: ${{ github.ref }}
15 | cancel-in-progress: true
16 |
17 | jobs:
18 | test:
19 | name: Node.js ${{ matrix.node-version }} | ${{ matrix.os }} | ${{ matrix.arch }}
20 |
21 | timeout-minutes: 5
22 |
23 | runs-on: ${{ matrix.os }}
24 |
25 | strategy:
26 | max-parallel: 3
27 | fail-fast: false
28 | matrix:
29 | # os:
30 | # - ubuntu-latest
31 | # - macOS-latest
32 | # - windows-latest
33 | # arch:
34 | # - x64
35 | # node-version:
36 | # - 18
37 | # experimental:
38 | # - false
39 | # send_coverage:
40 | # - false
41 | include:
42 | - os: ubuntu-latest
43 | arch: x64
44 | node-version: 20 # latest
45 | experimental: true
46 | node_minimum: false
47 | - os: macOS-latest
48 | arch: x64
49 | node-version: 20 # latest
50 | experimental: true
51 | send_coverage: false
52 | - os: windows-latest
53 | arch: x64
54 | node-version: 20 # latest
55 | experimental: true
56 | send_coverage: false
57 |
58 | - os: ubuntu-latest
59 | node-version: 18 # minimum
60 | arch: x64
61 | experimental: false
62 | send_coverage: true
63 | - os: macOS-latest
64 | arch: x64
65 | node-version: 18 # minimum
66 | experimental: false
67 | send_coverage: false
68 | - os: windows-latest
69 | arch: x64
70 | node-version: 18 # minimum
71 | experimental: false
72 | send_coverage: false
73 |
74 | steps:
75 | - name: Setting up Node.js ${{ matrix.node-version }}
76 | uses: actions/setup-node@v3
77 | with:
78 | node-version: ${{ matrix.node-version }}
79 |
80 | - name: Build info
81 | run: |
82 | node --version
83 | npm --version
84 |
85 | - name: Cloning repository
86 | uses: actions/checkout@v3
87 | with:
88 | fetch-depth: 5
89 |
90 | - name: Installing dependencies
91 | run: npm ci
92 |
93 | - name: Running tests
94 | continue-on-error: ${{ matrix.experimental }}
95 | run: npm test
96 |
97 | - name: Uploading code coverage report
98 | if: >
99 | (
100 | success() &&
101 | matrix.send_coverage &&
102 | github.event_name == 'push' &&
103 | github.repository_owner == 'JimiC' &&
104 | github.actor != 'dependabot[bot]'
105 | )
106 | uses: paambaati/codeclimate-action@v5
107 | env:
108 | CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
109 |
110 | - name: Uploading code coverage artifact
111 | if: >
112 | (
113 | success() &&
114 | matrix.send_coverage &&
115 | github.event_name == 'pull_request' &&
116 | github.actor != 'dependabot[bot]'
117 | )
118 | uses: actions/upload-artifact@v3
119 | with:
120 | name: code-coverage-report
121 | path: coverage/lcov.info
122 | retention-days: 1
123 |
--------------------------------------------------------------------------------
/.github/workflows/coverage.yml:
--------------------------------------------------------------------------------
1 | name: code coverage
2 |
3 | on:
4 | workflow_run:
5 | workflows:
6 | - build
7 | types:
8 | - completed
9 |
10 | jobs:
11 | code-climate:
12 | name: Send code coverage report to Code Climate
13 | runs-on: ubuntu-latest
14 | if: >
15 | (
16 | github.event.workflow_run.conclusion == 'success' &&
17 | github.event.workflow_run.event == 'pull_request' &&
18 | github.repository_owner == 'JimiC' &&
19 | github.actor != 'dependabot[bot]'
20 | )
21 | steps:
22 | - name: Cloning repository
23 | uses: actions/checkout@v3
24 | with:
25 | ref: ${{ github.event.workflow_run.head_sha }}
26 |
27 | - name: Download artifact
28 | uses: dawidd6/action-download-artifact@v2
29 | with:
30 | run_id: ${{ github.event.workflow_run.id }}
31 | name: code-coverage-report
32 |
33 | - name: Uploading code coverage report
34 | uses: paambaati/codeclimate-action@v5
35 | with:
36 | coverageLocations: lcov.info:lcov
37 | env:
38 | CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
39 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: publish
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*'
7 |
8 | jobs:
9 | publish:
10 | name: Publish
11 |
12 | if: github.repository_owner == 'JimiC'
13 |
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - name: Cloning repository
18 | uses: actions/checkout@v3
19 | with:
20 | fetch-depth: 5
21 |
22 | - name: "Setting up Node.js: 18"
23 | uses: actions/setup-node@v3
24 | with:
25 | node-version: 18
26 |
27 | - name: Build info
28 | run: |
29 | node --version
30 | npm --version
31 |
32 | - name: Installing dependencies
33 | run: npm ci
34 |
35 | - name: Publishing to NPM
36 | uses: JS-DevTools/npm-publish@v1
37 | with:
38 | token: ${{ secrets.NPM_AUTH_TOKEN }}
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .nyc_output
2 | .DS_Store
3 | node_modules
4 | coverage
5 | out
6 | lib
7 | dist
8 | npm-debug.*
9 | *.lcov
10 | /.integrity.json
11 |
--------------------------------------------------------------------------------
/.huskyrc:
--------------------------------------------------------------------------------
1 | {
2 | "skipCI": true,
3 | "hooks": {
4 | "pre-commit": "lint-staged"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.lintstagedrc:
--------------------------------------------------------------------------------
1 | {
2 | "*.ts": [
3 | "eslint --ext .ts ."
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/.mocharc.json:
--------------------------------------------------------------------------------
1 | {
2 | "spec": "./out/test",
3 | "ui": "bdd",
4 | "exit": true,
5 | "colors": true,
6 | "recursive": true,
7 | "retries": 2,
8 | "timeout": 15000
9 | }
10 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
2 | fund=false
3 |
--------------------------------------------------------------------------------
/.nycrc:
--------------------------------------------------------------------------------
1 | {
2 | "exclude": ["!test/**"],
3 | "exclude-after-remap": false
4 | }
5 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "dbaeumer.vscode-eslint",
4 | "EditorConfig.EditorConfig",
5 | "DavidAnson.vscode-markdownlint"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "program": "${workspaceFolder}/out/src/cli.js",
12 | "args": [
13 | "create",
14 | "-s",
15 | ".",
16 | "-x",
17 | "*",
18 | "*/",
19 | "!test/fixtures"
20 | ],
21 | "preLaunchTask": "npm: build - dev",
22 | "stopOnEntry": false,
23 | "sourceMaps": true,
24 | "outFiles": [
25 | "${workspaceFolder}/out/**/*.js"
26 | ]
27 | },
28 | {
29 | "type": "node",
30 | "request": "launch",
31 | "name": "Mocha Tests",
32 | "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
33 | "args": [
34 | "${workspaceFolder}/out/test"
35 | ],
36 | "internalConsoleOptions": "openOnSessionStart",
37 | "preLaunchTask": "npm: build - dev",
38 | "stopOnEntry": false,
39 | "sourceMaps": true,
40 | "outFiles": [
41 | "${workspaceFolder}/out/**/*.js"
42 | ]
43 | }
44 | ]
45 | }
46 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.codeActionsOnSave": {
3 | "source.fixAll.eslint": true
4 | },
5 | "files.associations": {
6 | ".huskyrc": "json",
7 | ".lintstagedrc": "json",
8 | ".nycrc": "json",
9 | "api-extractor.json": "jsonc",
10 | },
11 | "typescript.tsdk": "node_modules/typescript/lib"
12 | }
13 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | // A task runner that calls a custom npm script that compiles the extension.
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | {
5 | "version": "2.0.0",
6 | "tasks": [
7 | {
8 | "label": "npm: build - dev",
9 | // the command is a shell script
10 | "type": "shell",
11 | // we want to run npm
12 | "command": "npm",
13 | // show the output window only if unrecognized errors occur.
14 | "presentation": {
15 | "reveal": "silent"
16 | },
17 | // we run the custom script "build" as defined in package.json
18 | "args": [
19 | "run",
20 | "build:dev",
21 | "--loglevel",
22 | "silent"
23 | ],
24 | // The tsc compiler is running in the background
25 | "isBackground": true,
26 | // use the standard tsc in watch mode problem matcher to find compile problems in the output.
27 | "problemMatcher": "$tsc-watch"
28 | }
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 Jimi (Dimitris) Charalampidis
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # nsri (NodeJS Subresource Integrity)
3 |
4 | ### General Info
5 |
6 | [](https://raw.githubusercontent.com/jimic/nsri/main/LICENSE)
7 | 
8 | 
9 |
10 | ### Release Info
11 |
12 | 
13 | 
14 | 
15 |
16 | ### Development Info
17 |
18 | 
19 | 
20 |
21 | 
22 |
23 | [](https://codeclimate.com/github/jimic/nsri/maintainability)
24 | [](https://codeclimate.com/github/jimic/nsri/test_coverage)
25 |
26 | [](https://snyk.io/test/github/jimic/nsri?targetFile=package.json)
27 |
28 | ---
29 |
30 | A [Node.js](https://nodejs.org) utility tool that creates an integrity object containing the hash checksums of a file or a directory structure, that can be saved to an `.integrity.json` file [
], or put inside the project's manifest file (`project.json`).
31 |
32 | The hashes are computed using, by default, the `sha1` algorithm for files and `sha512` algorithm for directories, with `base64` encoding, complying to [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) spec, but other [Node.js crypto](https://nodejs.org/api/crypto.html) supported [algorithms](https://nodejs.org/api/crypto.html#cryptogethashes) and [encodings](https://nodejs.org/api/crypto.html#hashdigestencoding) can be used.
33 |
34 | ## Instalation
35 |
36 | To install as a dependency, simply type:
37 |
38 | ```sh
39 | npm i nsri --save
40 | ```
41 |
42 | To install for global use, simply type:
43 |
44 | ```sh
45 | npm i nsri -g
46 | ```
47 |
48 | ## Behavior
49 |
50 | **NOTE:**
51 |
52 | - The `.integrity.json` file itself is being excluded in all computations.
53 | - The `node_modules`, `.git*`, `.svn*`, `.hg*` directories are excluded by default.
54 |
55 | ### Files
56 |
57 | **Hashes are the same when:**
58 |
59 | - File names and contents are the same
60 |
61 | **Hashes are different when:**
62 |
63 | - File names are different and contents are the same
64 | - File contents are different and names are the same
65 |
66 | ### Directories
67 |
68 | Contents: The file names (and their data contents) and subdirectories names (with their contents) of the directory
69 |
70 | **Hashes are the same when:**
71 |
72 | - Directory names and contents are the same `(strict: true)`
73 | - Only root directory names are different and subdirectory names and all contents are the same `(strict: false)`
74 |
75 | **Hashes are different when:**
76 |
77 | - Directory names are different and contents are the same `(strict: true)`
78 | - Directory contents are different and names are the same
79 |
80 | ## Usage
81 |
82 | ### CLI
83 |
84 | `nsri` has a built-in command-line inteface.
85 |
86 | ```sh
87 | nsri [options]
88 | ```
89 |
90 | To see the available `commands` type:
91 |
92 | ```sh
93 | nsri -h
94 | ```
95 |
96 | and for available `command` options type:
97 |
98 | ```sh
99 | nsri -h
100 | ```
101 |
102 | More info an be found at the [CLI](https://github.com/JimiC/nsri/blob/main/docs/cli.md) section.
103 |
104 | ### API
105 |
106 | `nsri` can also be used programatically ([TypeScript](https://www.typescriptlang.org/) types are included).
107 |
108 | More info can be found at the [API](https://github.com/JimiC/nsri/blob/main/docs/api.md) section.
109 |
110 | ### Configuration
111 |
112 | #### Config File
113 |
114 | `nsri` supports [cosmiconfig](https://github.com/davidtheclark/cosmiconfig) configuration.
115 |
116 | Valid config filenames are: `.nsrirc`, `.nsrirc.js`, `.nsrirc.json`, `.nsrirc.yaml`, `.nsrirc.yml`, `.nsrirc.config.js`. In `package.json` the property name MUST be `nsri`.
117 |
118 | **NOTE:** Configurations set via `CLI` are overriding configurations set via `cosmiconfig`. To avoid confusion use one or the other.
119 |
120 | #### Ignore File
121 |
122 | Exclusions also can be set via an ignore file (`.nsriignore`), which supports the [gitignore](https://git-scm.com/docs/gitignore#_pattern_format) pattern format.
123 |
124 | **NOTE:** ExclusionsExclutionsExclutions set via `CLI` or `cosmiconfig` are getting merged with those in the ignore file and from those only unique entries are assigned.
125 |
126 | ### Integrity object schema
127 |
128 | ```json
129 | {
130 | "version": ... schema version,
131 | "hashes": ... verbosely or non-verbosely computed hashes
132 | }
133 | ```
134 |
135 | More info on the used schema can be found [here](https://github.com/JimiC/nsri/blob/main/src/schemas).
136 |
137 | #### Verbosely hashes schema
138 |
139 | ```json
140 | {
141 | "directoryName": {
142 | "contents": {
143 | "aFileName": ... file computed hash string,
144 | "anotherFileName": ... file computed hash string
145 | },
146 | "hash": ... directory computed hash string
147 | }
148 | }
149 | ```
150 |
151 | Examples of a verbosely computed hash integrity file can be found [here](https://github.com/JimiC/nsri/blob/main/test/fixtures).
152 |
153 | #### Non-verbosely hashes schema
154 |
155 | ```json
156 | {
157 | "fileOrDirectoryName": ... file or directory computed hash string
158 | }
159 | ```
160 |
161 | ### Examples
162 |
163 | Examples on how to use `nsri`, via `CLI` or `API`, can be found at the [examples](https://github.com/JimiC/nsri/blob/main/docs/examples) section.
164 |
165 | If you believe that the examples are incomplete or incorrect, please submit an issue or better yet a PR.
166 |
167 | ## Contributing
168 |
169 | If you like to contribute make sure to check-out the [Contribution Guidelines](https://github.com/JimiC/nsri/blob/main/.github/CONTRIBUTING.md) section.
170 |
171 | ## License
172 |
173 | This project is licensed under the [MIT](https://github.com/JimiC/nsri/blob/main/LICENSE) license.
174 |
175 | ## Versioning
176 |
177 | This project follows [Semantic Versioning 2.0.0](https://semver.org).
178 |
--------------------------------------------------------------------------------
/api-extractor.json:
--------------------------------------------------------------------------------
1 | /**
2 | * Config file for API Extractor. For more info, please visit: https://api-extractor.com
3 | */
4 | {
5 | "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
6 |
7 | /**
8 | * Optionally specifies another JSON config file that this file extends from. This provides a way for
9 | * standard settings to be shared across multiple projects.
10 | *
11 | * If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains
12 | * the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be
13 | * resolved using NodeJS require().
14 | *
15 | * SUPPORTED TOKENS: none
16 | * DEFAULT VALUE: ""
17 | */
18 | // "extends": "./shared/api-extractor-base.json"
19 | // "extends": "my-package/include/api-extractor-base.json"
20 |
21 | /**
22 | * Determines the "" token that can be used with other config file settings. The project folder
23 | * typically contains the tsconfig.json and package.json config files, but the path is user-defined.
24 | *
25 | * The path is resolved relative to the folder of the config file that contains the setting.
26 | *
27 | * The default value for "projectFolder" is the token "", which means the folder is determined by traversing
28 | * parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder
29 | * that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error
30 | * will be reported.
31 | *
32 | * SUPPORTED TOKENS:
33 | * DEFAULT VALUE: ""
34 | */
35 | // "projectFolder": "..",
36 |
37 | /**
38 | * (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor
39 | * analyzes the symbols exported by this module.
40 | *
41 | * The file extension must be ".d.ts" and not ".ts".
42 | *
43 | * The path is resolved relative to the folder of the config file that contains the setting; to change this,
44 | * prepend a folder token such as "".
45 | *
46 | * SUPPORTED TOKENS: , ,
47 | */
48 | "mainEntryPointFilePath": "/dist/out/index.d.ts",
49 |
50 | /**
51 | * A list of NPM package names whose exports should be treated as part of this package.
52 | *
53 | * For example, suppose that Webpack is used to generate a distributed bundle for the project "library1",
54 | * and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part
55 | * of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly
56 | * imports library2. To avoid this, we can specify:
57 | *
58 | * "bundledPackages": [ "library2" ],
59 | *
60 | * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been
61 | * local files for library1.
62 | */
63 | "bundledPackages": [ ],
64 |
65 | /**
66 | * Determines how the TypeScript compiler engine will be invoked by API Extractor.
67 | */
68 | "compiler": {
69 | /**
70 | * Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project.
71 | *
72 | * The path is resolved relative to the folder of the config file that contains the setting; to change this,
73 | * prepend a folder token such as "".
74 | *
75 | * Note: This setting will be ignored if "overrideTsconfig" is used.
76 | *
77 | * SUPPORTED TOKENS: , ,
78 | * DEFAULT VALUE: "/tsconfig.json"
79 | */
80 | // "tsconfigFilePath": "/tsconfig.json",
81 |
82 | /**
83 | * Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk.
84 | * The object must conform to the TypeScript tsconfig schema:
85 | *
86 | * http://json.schemastore.org/tsconfig
87 | *
88 | * If omitted, then the tsconfig.json file will be read from the "projectFolder".
89 | *
90 | * DEFAULT VALUE: no overrideTsconfig section
91 | */
92 | // "overrideTsconfig": {
93 | // . . .
94 | // }
95 |
96 | /**
97 | * This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended
98 | * and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when
99 | * dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses
100 | * for its analysis. Where possible, the underlying issue should be fixed rather than relying on skipLibCheck.
101 | *
102 | * DEFAULT VALUE: false
103 | */
104 | // "skipLibCheck": true,
105 | },
106 |
107 | /**
108 | * Configures how the API report file (*.api.md) will be generated.
109 | */
110 | "apiReport": {
111 | /**
112 | * (REQUIRED) Whether to generate an API report.
113 | */
114 | "enabled": false
115 |
116 | /**
117 | * The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce
118 | * a full file path.
119 | *
120 | * The file extension should be ".api.md", and the string should not contain a path separator such as "\" or "/".
121 | *
122 | * SUPPORTED TOKENS: ,
123 | * DEFAULT VALUE: ".api.md"
124 | */
125 | // "reportFileName": ".api.md",
126 |
127 | /**
128 | * Specifies the folder where the API report file is written. The file name portion is determined by
129 | * the "reportFileName" setting.
130 | *
131 | * The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy,
132 | * e.g. for an API review.
133 | *
134 | * The path is resolved relative to the folder of the config file that contains the setting; to change this,
135 | * prepend a folder token such as "".
136 | *
137 | * SUPPORTED TOKENS: , ,
138 | * DEFAULT VALUE: "/etc/"
139 | */
140 | // "reportFolder": "/etc/",
141 |
142 | /**
143 | * Specifies the folder where the temporary report file is written. The file name portion is determined by
144 | * the "reportFileName" setting.
145 | *
146 | * After the temporary file is written to disk, it is compared with the file in the "reportFolder".
147 | * If they are different, a production build will fail.
148 | *
149 | * The path is resolved relative to the folder of the config file that contains the setting; to change this,
150 | * prepend a folder token such as "".
151 | *
152 | * SUPPORTED TOKENS: , ,
153 | * DEFAULT VALUE: "/temp/"
154 | */
155 | // "reportTempFolder": "/temp/"
156 | },
157 |
158 | /**
159 | * Configures how the doc model file (*.api.json) will be generated.
160 | */
161 | "docModel": {
162 | /**
163 | * (REQUIRED) Whether to generate a doc model file.
164 | */
165 | "enabled": false
166 |
167 | /**
168 | * The output path for the doc model file. The file extension should be ".api.json".
169 | *
170 | * The path is resolved relative to the folder of the config file that contains the setting; to change this,
171 | * prepend a folder token such as "".
172 | *
173 | * SUPPORTED TOKENS: , ,
174 | * DEFAULT VALUE: "/temp/.api.json"
175 | */
176 | // "apiJsonFilePath": "/temp/.api.json"
177 | },
178 |
179 | /**
180 | * Configures how the .d.ts rollup file will be generated.
181 | */
182 | "dtsRollup": {
183 | /**
184 | * (REQUIRED) Whether to generate the .d.ts rollup file.
185 | */
186 | "enabled": true,
187 |
188 | /**
189 | * Specifies the output path for a .d.ts rollup file to be generated without any trimming.
190 | * This file will include all declarations that are exported by the main entry point.
191 | *
192 | * If the path is an empty string, then this file will not be written.
193 | *
194 | * The path is resolved relative to the folder of the config file that contains the setting; to change this,
195 | * prepend a folder token such as "".
196 | *
197 | * SUPPORTED TOKENS: , ,
198 | * DEFAULT VALUE: "/dist/.d.ts"
199 | */
200 | "untrimmedFilePath": "/lib/.d.ts"
201 |
202 | /**
203 | * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release.
204 | * This file will include only declarations that are marked as "@public" or "@beta".
205 | *
206 | * The path is resolved relative to the folder of the config file that contains the setting; to change this,
207 | * prepend a folder token such as "".
208 | *
209 | * SUPPORTED TOKENS: , ,
210 | * DEFAULT VALUE: ""
211 | */
212 | // "betaTrimmedFilePath": "/dist/-beta.d.ts",
213 |
214 |
215 | /**
216 | * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release.
217 | * This file will include only declarations that are marked as "@public".
218 | *
219 | * If the path is an empty string, then this file will not be written.
220 | *
221 | * The path is resolved relative to the folder of the config file that contains the setting; to change this,
222 | * prepend a folder token such as "".
223 | *
224 | * SUPPORTED TOKENS: , ,
225 | * DEFAULT VALUE: ""
226 | */
227 | // "publicTrimmedFilePath": "/dist/-public.d.ts",
228 |
229 | /**
230 | * When a declaration is trimmed, by default it will be replaced by a code comment such as
231 | * "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the
232 | * declaration completely.
233 | *
234 | * DEFAULT VALUE: false
235 | */
236 | // "omitTrimmingComments": true
237 | },
238 |
239 | /**
240 | * Configures how the tsdoc-metadata.json file will be generated.
241 | */
242 | "tsdocMetadata": {
243 | /**
244 | * Whether to generate the tsdoc-metadata.json file.
245 | *
246 | * DEFAULT VALUE: true
247 | */
248 | "enabled": false
249 |
250 | /**
251 | * Specifies where the TSDoc metadata file should be written.
252 | *
253 | * The path is resolved relative to the folder of the config file that contains the setting; to change this,
254 | * prepend a folder token such as "".
255 | *
256 | * The default value is "", which causes the path to be automatically inferred from the "tsdocMetadata",
257 | * "typings" or "main" fields of the project's package.json. If none of these fields are set, the lookup
258 | * falls back to "tsdoc-metadata.json" in the package folder.
259 | *
260 | * SUPPORTED TOKENS: , ,
261 | * DEFAULT VALUE: ""
262 | */
263 | // "tsdocMetadataFilePath": "/dist/tsdoc-metadata.json"
264 | },
265 |
266 | /**
267 | * Specifies what type of newlines API Extractor should use when writing output files. By default, the output files
268 | * will be written with Windows-style newlines. To use POSIX-style newlines, specify "lf" instead.
269 | * To use the OS's default newline kind, specify "os".
270 | *
271 | * DEFAULT VALUE: "crlf"
272 | */
273 | "newlineKind": "lf",
274 |
275 | /**
276 | * Configures how API Extractor reports error and warning messages produced during analysis.
277 | *
278 | * There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages.
279 | */
280 | "messages": {
281 | /**
282 | * Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing
283 | * the input .d.ts files.
284 | *
285 | * TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551"
286 | *
287 | * DEFAULT VALUE: A single "default" entry with logLevel=warning.
288 | */
289 | "compilerMessageReporting": {
290 | /**
291 | * Configures the default routing for messages that don't match an explicit rule in this table.
292 | */
293 | "default": {
294 | /**
295 | * Specifies whether the message should be written to the the tool's output log. Note that
296 | * the "addToApiReportFile" property may supersede this option.
297 | *
298 | * Possible values: "error", "warning", "none"
299 | *
300 | * Errors cause the build to fail and return a nonzero exit code. Warnings cause a production build fail
301 | * and return a nonzero exit code. For a non-production build (e.g. when "api-extractor run" includes
302 | * the "--local" option), the warning is displayed but the build will not fail.
303 | *
304 | * DEFAULT VALUE: "warning"
305 | */
306 | "logLevel": "warning"
307 |
308 | /**
309 | * When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md),
310 | * then the message will be written inside that file; otherwise, the message is instead logged according to
311 | * the "logLevel" option.
312 | *
313 | * DEFAULT VALUE: false
314 | */
315 | // "addToApiReportFile": false
316 | }
317 |
318 | // "TS2551": {
319 | // "logLevel": "warning",
320 | // "addToApiReportFile": true
321 | // },
322 | //
323 | // . . .
324 | },
325 |
326 | /**
327 | * Configures handling of messages reported by API Extractor during its analysis.
328 | *
329 | * API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag"
330 | *
331 | * DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings
332 | */
333 | "extractorMessageReporting": {
334 | "default": {
335 | "logLevel": "warning"
336 | // "addToApiReportFile": false
337 | }
338 |
339 | // "ae-extra-release-tag": {
340 | // "logLevel": "warning",
341 | // "addToApiReportFile": true
342 | // },
343 | //
344 | // . . .
345 | },
346 |
347 | /**
348 | * Configures handling of messages reported by the TSDoc parser when analyzing code comments.
349 | *
350 | * TSDoc message identifiers start with "tsdoc-". For example: "tsdoc-link-tag-unescaped-text"
351 | *
352 | * DEFAULT VALUE: A single "default" entry with logLevel=warning.
353 | */
354 | "tsdocMessageReporting": {
355 | "default": {
356 | "logLevel": "warning"
357 | // "addToApiReportFile": false
358 | }
359 |
360 | // "tsdoc-link-tag-unescaped-text": {
361 | // "logLevel": "warning",
362 | // "addToApiReportFile": true
363 | // },
364 | //
365 | // . . .
366 | }
367 | }
368 |
369 | }
370 |
--------------------------------------------------------------------------------
/docs/api.md:
--------------------------------------------------------------------------------
1 | # API
2 |
3 | All `API` calls are `static` members of the `Integrity` class.
4 |
5 | ---
6 |
7 | >## CurrentSchemaVersion
8 |
9 | `Description`: Constant value of the current schema version used.
10 |
11 | `Return Type`: `string`
12 |
13 | ---
14 |
15 | >## check
16 |
17 | `Description`: Checks the integrity of a directory or a file.
18 |
19 | `Info`: The `fileOrDirPath` can be an absolute or relative path.
20 |
21 | `Return Type`: `Promise`
22 |
23 | `Parameters`:
24 |
25 | |Name|Type|Attribute|Default|Description|
26 | |:---:|:---:|:---:|:---:|:---:|
27 | |fileOrDirPath|string|||the path of the file or directory to check|
28 | |integrity|string|||the path of the directory containing the integrity file or the path to the integrity file or a stringified integrity JSON or a hash string, to check against|
29 | |options|IntegrityOptions|optional|see [options](#options) section|the `integrity` options to use|
30 |
31 | `Examples`
32 |
33 | - [ES5](https://github.com/JimiC/nsri/blob/main/docs/examples/check-es5-js.md)
34 | - [ES6+, Typescript](https://github.com/JimiC/nsri/blob/main/docs/examples/check-js-ts.md)
35 |
36 | ---
37 |
38 | >## create
39 |
40 | `Description`: Creates an integrity object of a directory or file.
41 |
42 | `Info`: `create` is a top-level helper function designed to internally determine whether to use `createDirHash` or `createFileHash`, saving you the coding hassle. Usually, `create` will be the function you are going to use, when you want to create an `integrity` object. The `fileOrDirPath` can be an absolute or relative path.
43 |
44 | `Return Type`: `Promise`
45 |
46 | `Parameters`:
47 |
48 | |Name|Type|Attribute|Default|Description|
49 | |:---:|:---:|:---:|:---:|:---:|
50 | |fileOrDirPath|string|||the path of the file or directory to hash|
51 | |options|IntegrityOptions|optional|see [options](#options) section|the `integrity` options to use|
52 |
53 | `Examples`
54 |
55 | - [ES5](https://github.com/JimiC/nsri/blob/main/docs/examples/create-es5-js.md)
56 | - [ES6+, Typescript](https://github.com/JimiC/nsri/blob/main/docs/examples/create-js-ts.md)
57 |
58 | ---
59 |
60 | >## createDirHash
61 |
62 | `Description`: Creates a hash object of a directory.
63 |
64 | `Info`: `createDirHash` is a function designed to create an `integrity` object for a directory. The `dirPath` can be an absolute or relative path. Creating a non-verbosely `integrity` object will compute all contents hashes combined.
65 |
66 | `Return Type`: `Promise`
67 |
68 | `Parameters`:
69 |
70 | |Name|Type|Attribute|Default|Description|
71 | |:---:|:---:|:---:|:---:|:---:|
72 | |dirPath|string|||the path of the directory to hash|
73 | |options|IntegrityOptions|optional|see [options](#options) section|the `integrity` options to use|
74 |
75 | `Examples`
76 |
77 | - [ES5](https://github.com/JimiC/nsri/blob/main/docs/examples/check-es5-js.md)
78 | - [ES6+, Typescript](https://github.com/JimiC/nsri/blob/main/docs/examples/check-js-ts.md)
79 |
80 | ---
81 |
82 | >## createFileHash
83 |
84 | `Description`: Creates a hash object of a file.
85 |
86 | `Info`: `createFileHash` is a function designed to create an `integrity` object for a file. The `filePath` can be an absolute or relative path.
87 |
88 | `Return Type`: `Promise`
89 |
90 | `Parameters`:
91 |
92 | |Name|Type|Attribute|Default|Description|
93 | |:---:|:---:|:---:|:---:|:---:|
94 | |filePath|string|||the path of the file to hash|
95 | |options|CryptoOptions|optional|see [options](#options) section|the `crypto` options to use|
96 |
97 | `Examples`
98 |
99 | - [ES5](https://github.com/JimiC/nsri/blob/main/docs/examples/createFileHash-es5-js.md)
100 | - [ES6+, Typescript](https://github.com/JimiC/nsri/blob/main/docs/examples/createFileHash-js-ts.md)
101 |
102 | ---
103 |
104 | >## createFilesHash
105 |
106 | `Description`: Creates a hash object of a list of files.
107 |
108 | `Info`: `createFilesHash` is a function designed to create an `integrity` object for a list of files. The `filenames` can be absolute or relative paths.
109 |
110 | `Return Type`: `Promise`
111 |
112 | `Parameters`:
113 |
114 | |Name|Type|Attribute|Default|Description|
115 | |:---:|:---:|:---:|:---:|:---:|
116 | |filenames|string[]|||the list of the file paths to hash|
117 | |options|CryptoOptions|optional|see [options](#options) section|the `crypto` options to use|
118 |
119 | `Examples`
120 |
121 | - [ES5](https://github.com/JimiC/nsri/blob/main/docs/examples/createFilesHash-es5-js.md)
122 | - [ES6+, Typescript](https://github.com/JimiC/nsri/blob/main/docs/examples/createFilesHash-js-ts.md)
123 |
124 | ---
125 |
126 | >## persist
127 |
128 | `Description`: Persists the integrity object on disk.
129 |
130 | `Info`: `persist` is a function designed to persist the created integrity object on disk. Usually, you will use `persist` whenever you create an `integrity` object. If the `dirPath` parameter is omitted, the `integrity` object will be persisted at the root directory, from where the function gets called. The `dirPath` can be an absolute or relative path.
131 |
132 | `Return Type`: `Promise`
133 |
134 | `Parameters`:
135 |
136 | |Name|Type|Attribute|Default|Description|
137 | |:---:|:---:|:---:|:---:|:---:|
138 | |intObj|IntegrityObject|||the integrity object to persist|
139 | |dirPath|string|optional|`./`|the path of the directory to persist the data to|
140 | |prettify|boolean|optional|false|data are formatted with indentation|
141 |
142 | `Examples`
143 |
144 | - [ES5](https://github.com/JimiC/nsri/blob/main/docs/examples/persist-es5-js.md)
145 | - [ES6+, Typescript](https://github.com/JimiC/nsri/blob/main/docs/examples/persist-js-ts.md)
146 |
147 | ---
148 |
149 | >## getManifestIntegrity
150 |
151 | `Description`: Gets the integrity object from the manifest file.
152 |
153 | `Info`: `getManifestIntegrity` is a function designed to retrieve a stringified version of the integrity object from the project's manifest file (`project.json`).
154 |
155 | `Return Type`: `Promise`
156 |
157 | `Parameters`:
158 |
159 | |Name|Type|Attribute|Default|Description|
160 | |:---:|:---:|:---:|:---:|:---:|
161 | |dirPath|string|optional|`./`|the path of the directory to the manifest file|
162 |
163 | `Examples`
164 |
165 | - [ES5](https://github.com/JimiC/nsri/blob/main/docs/examples/getManifestIntegrity-es5-js.md)
166 | - [ES6+, Typescript](https://github.com/JimiC/nsri/blob/main/docs/examples/getManifestIntegrity-js-ts.md)
167 |
168 | ---
169 |
170 | >## updateManifestIntegrity
171 |
172 | `Description`: Updates the manifest file (`project.json`) with the integrity object.
173 |
174 | `Info`: `updateManifestIntegrity` is a function designed to update the project's manifest file (`project.json`) with the integrity object.
175 |
176 | `Return Type`: `Promise`
177 |
178 | `Parameters`:
179 |
180 | |Name|Type|Attribute|Default|Description|
181 | |:---:|:---:|:---:|:---:|:---:|
182 | |intObj|IntegrityObject|||the integrity object to persist|
183 | |dirPath|string|optional|`./`|the path of the directory to the manifest file|
184 |
185 | `Examples`
186 |
187 | - [ES5](https://github.com/JimiC/nsri/blob/main/docs/examples/updateManifestIntegrity-es5-js.md)
188 | - [ES6+, Typescript](https://github.com/JimiC/nsri/blob/main/docs/examples/updateManifestIntegrity-js-ts.md)
189 |
190 | ---
191 |
192 | >## getIntegrityOptionsFromConfig
193 |
194 | `Description`:
195 |
196 | `Info`: `getIntegrityOptionsFromConfig` is a function designed to retrieve the integrity options from a `cosmiconfig` compatible configuration section.
197 |
198 | `Return Type`: `Promise`
199 |
200 | `Parameters`: None
201 |
202 | `Examples`
203 |
204 | - [ES5](https://github.com/JimiC/nsri/blob/main/docs/examples/getIntegrityOptionsFromConfig-es5-js.md)
205 | - [ES6+, Typescript](https://github.com/JimiC/nsri/blob/main/docs/examples/getIntegrityOptionsFromConfig-js-ts.md)
206 |
207 | ---
208 |
209 | >## getExclusionsFromIgnoreFile
210 |
211 | `Description`:
212 |
213 | `Info`: `getExclusionsFromIgnoreFile` is a function designed to retrieve the exclusions from a `.nsriignore` file.
214 |
215 | `Return Type`: `Promise`
216 |
217 | `Parameters`:
218 |
219 | |Name|Type|Attribute|Default|Description|
220 | |:---:|:---:|:---:|:---:|:---:|
221 | |dirPath|string|optional|`./`|the path of the directory to the ignore file|
222 |
223 | `Examples`
224 |
225 | - [ES5](https://github.com/JimiC/nsri/blob/main/docs/examples/getExclusionsFromIgnoreFile-es5-js.md)
226 | - [ES6+, Typescript](https://github.com/JimiC/nsri/blob/main/docs/examples/getExclusionsFromIgnoreFile-js-ts.md)
227 |
228 | ---
229 |
230 | ## Options
231 |
232 | ### IntegrityOptions
233 |
234 | |Name|Type|Attribute|Default|Description|
235 | |:---:|:---:|:---:|:---:|:---:|
236 | |cryptoOptions|CryptoOptions|optional|see `CryptoOptions` |the `crypto` options to use|
237 | |strict|boolean|optional|false|whether the computed hashes are strictly using the directory name|
238 | |verbose|boolean|optional|false|whether the computed hashes are returned in a verbosely or non-verbosely structure|
239 | |exclude|string[]|optional|[]|the paths to be excluded, supports also `glob` expressions (positive & negative)|
240 |
241 | ### CryptoOptions
242 |
243 | |Name|Type|Attribute|Default|Description|
244 | |:---:|:---:|:---:|:---:|:---:|
245 | |dirAlgorithm|string|optional|`sha512`|the `crypto` algorithm to use for directories|
246 | |encoding|BinaryToTextEncoding|optional|`base64`|the `crypto` encoding to use|
247 | |fileAlgorithm|string|optional|`sha1`|the `crypto` algorithm to use for files|
248 |
249 | ---
250 |
251 | ## Importation
252 |
253 | See [here](https://github.com/JimiC/nsri/blob/main/docs/importation.md) how to import the library.
254 |
--------------------------------------------------------------------------------
/docs/cli.md:
--------------------------------------------------------------------------------
1 | # CLI
2 |
3 | ## Commands
4 |
5 | ---
6 |
7 | >## create
8 |
9 | `Description`: Creates integrity hash from the provided source.
10 |
11 | ---
12 |
13 | >## check
14 |
15 | `Description`: Checks integrity hash against the provided source.
16 |
17 | ---
18 |
19 | ## Options
20 |
21 | ---
22 |
23 | >## --diralgorithm
24 |
25 | `Alias`: -da
26 |
27 | `Description`: The algorithm to use for directory hashing.
28 |
29 | `Default`: sha512
30 |
31 | `Type`: string
32 |
33 | ---
34 |
35 | >## --encoding
36 |
37 | `Alias`: -e
38 |
39 | `Description`: The encoding to use for hashing.
40 |
41 | `Default`: base64
42 |
43 | `Type`: string
44 |
45 | ---
46 |
47 | >## --exclude
48 |
49 | `Alias`: -x
50 |
51 | `Description`: Files and/or directories paths to exclude.
52 |
53 | `Default`: []
54 |
55 | `Type`: array
56 |
57 | ---
58 |
59 | >## --filealgorithm
60 |
61 | `Alias`: -fa
62 |
63 | `Description`: The algorithm to use for file hashing.
64 |
65 | `Default`: sha1
66 |
67 | `Type`: string
68 |
69 | ---
70 |
71 | >## --integrity
72 |
73 | `Alias`: -i
74 |
75 | `Description`: The integrity hash, JSON, file or directory path, to check against ([required] when 'manifest' option not specified).
76 |
77 | `Type`: string
78 |
79 | ---
80 |
81 | >## --manifest
82 |
83 | `Alias`: -m
84 |
85 | `Description`: The integrity hash gets persisted to, or read from, the project's manifest (package.json).
86 |
87 | `Default`: undefined
88 |
89 | `Type`: boolean
90 |
91 | ---
92 |
93 | >## --output
94 |
95 | `Alias`: -o
96 |
97 | `Description`: The directory path where to persist the created integrity file (ignored when 'manifest' option specified).
98 |
99 | `Type`: string
100 |
101 | ---
102 |
103 | >## --source
104 |
105 | `Alias`: -s
106 |
107 | `Description`: The path to the file or directory to hash.
108 |
109 | `Mandatory`: true
110 |
111 | `Type`: string
112 |
113 | ---
114 |
115 | >## --verbose
116 |
117 | `Alias`: -v
118 |
119 | `Description`: Verbosely create hashes of a directory.
120 |
121 | `Default`: false
122 |
123 | `Type`: boolean
124 |
125 | ---
126 |
127 | >## --strict
128 |
129 | `Alias`: -st
130 |
131 | `Description`: Strictly compares names and contents.
132 |
133 | `Default`: false
134 |
135 | `Type`: boolean
136 |
137 | ---
138 |
139 | >## --help
140 |
141 | `Alias`: -h
142 |
143 | `Description`: Show help.
144 |
145 | ---
146 |
147 | >## --version
148 |
149 | `Alias`: -V
150 |
151 | `Description`: Show version number.
152 |
153 | ---
154 |
--------------------------------------------------------------------------------
/docs/examples/check-es5-js.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.check` (ECMAScript 5)
4 |
5 | > Check the integrity of the root directory, using a root 'integrity' file
6 |
7 | ```js
8 | Integrity.check('./', './.integrity.json')
9 | .then(pass => console.info('Integrity check ' + pass ? 'passed': 'failed'))
10 | .catch(error => console.error(error))
11 | ```
12 |
13 | ---
14 |
15 | > Check the integrity of a subdirectory, using a subdirectory 'integrity' file.
16 |
17 | ```js
18 | Integrity.check('./sub', './sub/.integrity.json')
19 | .then(pass => console.info('Integrity check ' + pass ? 'passed': 'failed'))
20 | .catch(error => console.error(error))
21 | ```
22 |
23 | ---
24 |
25 | > Check the integrity of a root directory file, using a root 'integrity' file
26 |
27 | ```js
28 | Integrity.check('./fileToCheck.txt', './.integrity.json')
29 | .then(pass => console.info('Integrity check ' + pass ? 'passed': 'failed'))
30 | .catch(error => console.error(error))
31 | ```
32 |
33 | ---
34 |
35 | > Check the integrity of a subdirectory file, using a root 'integrity' file.
36 |
37 | ```js
38 | Integrity.check('./sub/fileToCheck.txt', './.integrity.json')
39 | .then(pass => console.info('Integrity check ' + pass ? 'passed': 'failed'))
40 | .catch(error => console.error(error))
41 | ```
42 |
43 | ---
44 |
45 | > Check the integrity of a subdirectory file, using a subdirectory 'integrity' file.
46 |
47 | ```js
48 | Integrity.check('./sub/fileToCheck.txt', './sub/.integrity.json')
49 | .then(pass => console.info('Integrity check ' + pass ? 'passed': 'failed'))
50 | .catch(error => console.error(error))
51 | ```
52 |
--------------------------------------------------------------------------------
/docs/examples/check-js-ts.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.check` (ECMAScript 6+, TypeScript)
4 |
5 | > Check the integrity of the root directory, using a root 'integrity' file
6 |
7 | `ES6+`
8 |
9 | ```js
10 | const pass = await Integrity.check('./', './.integrity.json')
11 | ```
12 |
13 | `TypeScript`
14 |
15 | ```ts
16 | const pass: boolean = await Integrity.check('./', './.integrity.json');
17 | ```
18 |
19 | ---
20 |
21 | > Check the integrity of a subdirectory, using a subdirectory 'integrity' file.
22 |
23 | `ES6+`
24 |
25 | ```js
26 | const pass = await Integrity.check('./sub', './sub/.integrity.json')
27 | ```
28 |
29 | `TypeScript`
30 |
31 | ```ts
32 | const pass: boolean = await Integrity.check('./sub', './sub/.integrity.json');
33 | ```
34 |
35 | ---
36 |
37 | > Check the integrity of a root directory file, using a root 'integrity' file
38 |
39 | `ES6+`
40 |
41 | ```js
42 | const pass = await Integrity.check('./fileToCheck.txt', './.integrity.json')
43 | ```
44 |
45 | `TypeScript`
46 |
47 | ```ts
48 | const pass: boolean = await Integrity.check('./fileToCheck.txt', './.integrity.json');
49 | ```
50 |
51 | ---
52 |
53 | > Check the integrity of a subdirectory file, using a root 'integrity' file.
54 |
55 | `ES6+`
56 |
57 | ```js
58 | const pass = await Integrity.check('./sub/fileToCheck.txt', './.integrity.json')
59 | ```
60 |
61 | `TypeScript`
62 |
63 | ```ts
64 | const pass: boolean = await Integrity.check('./sub/fileToCheck.txt', './.integrity.json');
65 | ```
66 |
67 | ---
68 |
69 | > Check the integrity of a subdirectory file, using a subdirectory 'integrity' file.
70 |
71 | `ES6+`
72 |
73 | ```js
74 | const pass = await Integrity.check('./sub/fileToCheck.txt', './sub/.integrity.json')
75 | ```
76 |
77 | `TypeScript`
78 |
79 | ```ts
80 | const pass: boolean = await Integrity.check('./sub/fileToCheck.txt', './sub/.integrity.json');
81 | ```
82 |
--------------------------------------------------------------------------------
/docs/examples/cli.md:
--------------------------------------------------------------------------------
1 | # CLI Examples
2 |
3 | ## `check`
4 |
5 | > Check the integrity of the root directory, using a root 'integrity' file
6 |
7 | ```sh
8 | nsri check -s ./ -i ./.integrity.json
9 |
10 | ```
11 |
12 | > Check the integrity of the root directory, using the 'integrity' from a manifest file
13 |
14 | ```sh
15 | nsri check -m -s ./
16 | ```
17 |
18 | ---
19 |
20 | ## `create`
21 |
22 | > Create a non-verbosely integrity file of the root directory
23 |
24 | ```sh
25 | nsri create -s ./
26 | ```
27 |
28 | ---
29 |
30 | > Create a verbosely integrity file of the root directory
31 |
32 | ```sh
33 | nsri create -v -s ./
34 | ```
35 |
36 | ---
37 |
38 | > Create a non-verbosely integrity file of a sub directory and persist it on the subdirectory
39 |
40 | ```sh
41 | nsri create -s ./sub -o ./sub
42 | ```
43 |
44 | ---
45 |
46 | > Create a non-verbosely integrity file of the root directory and persist it on the manifest file
47 |
48 | ```sh
49 | nsri create -m -s ./
50 | ```
51 |
--------------------------------------------------------------------------------
/docs/examples/create-es5-js.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.create` (ECMAScript 5)
4 |
5 | >Create a verbosely integrity object of the root directory, using the default `sha1` and `sha512` algorithm and `hex` encoding
6 |
7 | ```js
8 | Integrity.create('./')
9 | .then(intObj => {
10 | // Do something with the integrity object here
11 | // It's advised not to modify them
12 | // as it will certainly lead to integrity check failure
13 | // when you'll try to check against them
14 | })
15 | .catch(error => console.error(error))
16 | ```
17 |
18 | ---
19 |
20 | > Create a verbosely integrity object of the root directory, using the `sha256` algorithm and `base64` encoding
21 |
22 | ```js
23 | var options = { cryptoOptions: { dirAlgorithm: 'sha256', encoding: 'base64', fileAlgorithm: "sha256" } }
24 | Integrity.create('./', options)
25 | .then(intObj => {
26 | // Do something with the integrity object here
27 | // It's advised not to modify them
28 | // as it will certainly lead to integrity check failure
29 | // when you'll try to check against them
30 | })
31 | .catch(error => console.error(error))
32 | ```
33 |
34 | ---
35 |
36 | > Create a verbosely integrity object of a subdirectory
37 |
38 | ```js
39 | Integrity.create('./sub')
40 | .then(intObj => {
41 | // Do something with the integrity object here
42 | // It's advised not to modify them
43 | // as it will certainly lead to integrity check failure
44 | // when you'll try to check against them
45 | })
46 | .catch(error => console.error(error))
47 | ```
48 |
49 | ---
50 |
51 | > Create a non-verbosely integrity object of a file
52 |
53 | ```js
54 | var options = { verbose: false }
55 | Integrity.create('./fileToHash.txt', options)
56 | .then(intObj => {
57 | // Do something with the integrity object here
58 | // It's advised not to modify them
59 | // as it will certainly lead to integrity check failure
60 | // when you'll try to check against them
61 | })
62 | .catch(error => console.error(error))
63 | ```
64 |
65 | ---
66 |
67 | > Create a verbosely integrity object of a subdirectory file
68 |
69 | ```js
70 | Integrity.create('./sub/fileToHash.txt')
71 | .then(intObj => {
72 | // Do something with the integrity object here
73 | // It's advised not to modify them
74 | // as it will certainly lead to integrity check failure
75 | // when you'll try to check against them
76 | })
77 | .catch(error => console.error(error))
78 | ```
79 |
80 | ---
81 |
82 | > Create a verbosely integrity object of a directory excluding a file
83 |
84 | ```js
85 | var options = { exclude: ['fileToExclude.txt'] }
86 | Integrity.create('./dir', options)
87 | .then(intObj => {
88 | // Do something with the integrity object here
89 | // It's advised not to modify them
90 | // as it will certainly lead to integrity check failure
91 | // when you'll try to check against them
92 | })
93 | .catch(error => console.error(error))
94 | ```
95 |
96 | ---
97 |
98 | > Create a verbosely integrity object of a directory excluding a subdirectory
99 |
100 | ```js
101 | var options = { exclude: ['sub'] }
102 | Integrity.create('./dir', options)
103 | .then(intObj => {
104 | // Do something with the integrity object here
105 | // It's advised not to modify them
106 | // as it will certainly lead to integrity check failure
107 | // when you'll try to check against them
108 | })
109 | .catch(error => console.error(error))
110 | ```
111 |
--------------------------------------------------------------------------------
/docs/examples/create-js-ts.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.create` (ECMAScript 6+, TypeScript)
4 |
5 | ## Using with `async/await`
6 |
7 | > Create a verbosely integrity object of the root directory, using the default `sha1` and `sha512` algorithm and `base64` encoding
8 |
9 | `ES6+`
10 |
11 | ```js
12 | const intObj = await Integrity.create('./')
13 |
14 | // Do something with the integrity object here
15 | // It's advised not to modify them
16 | // as it will certainly lead to integrity check failure
17 | // when you'll try to check against them
18 | ```
19 |
20 | `TypeScript`
21 |
22 | ```ts
23 | const intObj: IntegrityObject = await Integrity.create('./');
24 |
25 | // Do something with the integrity object here
26 | // It's advised not to modify them
27 | // as it will certainly lead to integrity check failure
28 | // when you'll try to check against them
29 | ```
30 |
31 | ---
32 |
33 | > Create a verbosely integrity object of the root directory, using the `sha256` algorithm and `base64` encoding
34 |
35 | `ES6+`
36 |
37 | ```js
38 | const options = { cryptoOptions: { dirAlgorithm: 'sha256', encoding: 'base64', fileAlgorithm: 'sha256' } }
39 | const intObj = await Integrity.create('./', options)
40 |
41 | // Do something with the integrity object here
42 | // It's advised not to modify them
43 | // as it will certainly lead to integrity check failure
44 | // when you'll try to check against them
45 | ```
46 |
47 | `TypeScript`
48 |
49 | ```ts
50 | const options: IntegrityOptions = { cryptoOptions: { dirAlgorithm: 'sha256', encoding: 'base64', fileAlgorithm: 'sha256' } };
51 | const intObj: IntegrityObject = await Integrity.create('./', options);
52 |
53 | // Do something with the integrity object here
54 | // It's advised not to modify them
55 | // as it will certainly lead to integrity check failure
56 | // when you'll try to check against them
57 | ```
58 |
59 | ---
60 |
61 | > Create a verbosely integrity object of a subdirectory
62 |
63 | `ES6+`
64 |
65 | ```ts
66 | const intObj = await Integrity.create('./sub')
67 |
68 | // Do something with the integrity object here
69 | // It's advised not to modify them
70 | // as it will certainly lead to integrity check failure
71 | // when you'll try to check against them
72 | ```
73 |
74 | `TypeScript`
75 |
76 | ```ts
77 | const intObj: IntegrityObject = await Integrity.create('./sub');
78 |
79 | // Do something with the integrity object here
80 | // It's advised not to modify them
81 | // as it will certainly lead to integrity check failure
82 | // when you'll try to check against them
83 | ```
84 |
85 | ---
86 |
87 | > Create a non-verbosely integrity object of a file
88 |
89 | `ES6+`
90 |
91 | ```js
92 | const options = { verbose: false }
93 | const intObj = await Integrity.create('./fileToHash.txt', options)
94 |
95 | // Do something with the integrity object here
96 | // It's advised not to modify them
97 | // as it will certainly lead to integrity check failure
98 | // when you'll try to check against them
99 | ```
100 |
101 | `TypeScript`
102 |
103 | ```ts
104 | const options: IntegrityOptions = { verbose: false };
105 | const intObj: IntegrityObject = await Integrity.create('./fileToHash.txt', options);
106 |
107 | // Do something with the integrity object here
108 | // It's advised not to modify them
109 | // as it will certainly lead to integrity check failure
110 | // when you'll try to check against them
111 | ```
112 |
113 | ---
114 |
115 | > Create a verbosely integrity object of a subdirectory file
116 |
117 | `ES6+`
118 |
119 | ```js
120 | const intObj = await Integrity.create('./sub/fileToHash.txt')
121 |
122 | // Do something with the integrity object here
123 | // It's advised not to modify them
124 | // as it will certainly lead to integrity check failure
125 | // when you'll try to check against them
126 | ```
127 |
128 | `TypeScript`
129 |
130 | ```ts
131 | const intObj: IntegrityObject = await Integrity.create('./sub/fileToHash.txt');
132 |
133 | // Do something with the integrity object here
134 | // It's advised not to modify them
135 | // as it will certainly lead to integrity check failure
136 | // when you'll try to check against them
137 | ```
138 |
139 | ---
140 |
141 | > Create a verbosely integrity object of a directory excluding a file
142 |
143 | `ES6+`
144 |
145 | ```js
146 | const options = { exclude: ['fileToExclude.txt'] }
147 | const intObj = await Integrity.create('./dir', options)
148 |
149 | // Do something with the integrity object here
150 | // It's advised not to modify them
151 | // as it will certainly lead to integrity check failure
152 | // when you'll try to check against them
153 | ```
154 |
155 | `TypeScript`
156 |
157 | ```ts
158 | const options: IntegrityOptions = { exclude: ['fileToExclude.txt'] };
159 | const intObj: IntegrityObject = await Integrity.create('./dir', options);
160 |
161 | // Do something with the integrity object here
162 | // It's advised not to modify them
163 | // as it will certainly lead to integrity check failure
164 | // when you'll try to check against them
165 | ```
166 |
167 | ---
168 |
169 | > Create a verbosely integrity object of a directory excluding a subdirectory
170 |
171 | `ES6+`
172 |
173 | ```js
174 | const options = { exclude: ['sub'] }
175 | const intObj = await Integrity.create('./dir', options)
176 |
177 | // Do something with the integrity object here
178 | // It's advised not to modify them
179 | // as it will certainly lead to integrity check failure
180 | // when you'll try to check against them
181 | ```
182 |
183 | `TypeScript`
184 |
185 | ```ts
186 | const options: IntegrityOptions = { exclude: ['sub'] };
187 | const intObj: IntegrityObject = await Integrity.create('./dir', options);
188 |
189 | // Do something with the integrity object here
190 | // It's advised not to modify them
191 | // as it will certainly lead to integrity check failure
192 | // when you'll try to check against them
193 | ```
194 |
195 | ---
196 |
197 | ## Using with `then/catch`
198 |
199 | All above examples can be also used with the `then/catch` coding pattern.
200 |
201 | Here is how the first example will look like:
202 |
203 | >Create a verbosely integrity object of the root directory, using the default `sha1` and `sha512` algorithm and `base64` encoding
204 |
205 | `ES6+`
206 |
207 | ```js
208 | Integrity.create('./')
209 | .then(intObj => {
210 | // Do something with the integrity object here
211 | // It's advised not to modify them
212 | // as it will certainly lead to integrity check failure
213 | // when you'll try to check against them
214 | })
215 | .catch(error => console.error(error))
216 | ```
217 |
218 | `TypeScript`
219 |
220 | ```ts
221 | Integrity.create('./')
222 | .then(intObj => {
223 | // Do something with the integrity object here
224 | // It's advised not to modify them
225 | // as it will certainly lead to integrity check failure
226 | // when you'll try to check against them
227 | })
228 | .catch(error => console.error(error));
229 | ```
230 |
--------------------------------------------------------------------------------
/docs/examples/createDirHash-es5-js.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.createDirHash` (ECMAScript 5)
4 |
5 | >Create a verbosely integrity object of the root directory, using the default `md5` algorithm and `hex` encoding
6 |
7 | ```js
8 | Integrity.createDirHash('./')
9 | .then(hashes => {
10 | // Do something with the hashes here
11 | // It's advised not to modify them
12 | // as it will certainly lead to integrity check failure
13 | // when you'll try to check against them
14 | })
15 | .catch(error => console.error(error))
16 | ```
17 |
18 | ---
19 |
20 | > Create a verbosely integrity object of the root directory, using the `sha1` algorithm and `base64` encoding
21 |
22 | ```js
23 | var options = { cryptoOptions: { algorithm: 'sha1', encoding: 'base64' } }
24 | Integrity.createDirHash('./', options)
25 | .then(hashes => {
26 | // Do something with the hashes here
27 | // It's advised not to modify them
28 | // as it will certainly lead to integrity check failure
29 | // when you'll try to check against them
30 | })
31 | .catch(error => console.error(error))
32 | ```
33 |
34 | ---
35 |
36 | > Create a verbosely integrity object of a subdirectory
37 |
38 | ```js
39 | Integrity.createDirHash('./sub')
40 | .then(hashes => {
41 | // Do something with the hashes here
42 | // It's advised not to modify them
43 | // as it will certainly lead to integrity check failure
44 | // when you'll try to check against them
45 | })
46 | .catch(error => console.error(error))
47 | ```
48 |
49 | ---
50 |
51 | > Create a verbosely integrity object of a directory excluding a file
52 |
53 | ```js
54 | var options = { exclude: ['fileToExclude.txt'] }
55 | Integrity.createDirHash('./dir', options)
56 | .then(hashes => {
57 | // Do something with the hashes here
58 | // It's advised not to modify them
59 | // as it will certainly lead to integrity check failure
60 | // when you'll try to check against them
61 | })
62 | .catch(error => console.error(error))
63 | ```
64 |
65 | ---
66 |
67 | > Create a verbosely integrity object of a directory excluding a subdirectory
68 |
69 | ```js
70 | var options = { exclude: ['sub'] }
71 | Integrity.createDirHash('./dir', options)
72 | .then(hashes => {
73 | // Do something with the hashes here
74 | // It's advised not to modify them
75 | // as it will certainly lead to integrity check failure
76 | // when you'll try to check against them
77 | })
78 | .catch(error => console.error(error))
79 | ```
80 |
--------------------------------------------------------------------------------
/docs/examples/createDirHash-js-ts.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.createDirHash` (ECMAScript 6+, TypeScript)
4 |
5 | > Create a verbosely integrity object of the root directory, using the default `md5` algorithm and `hex` encoding
6 |
7 | `ES6+`
8 |
9 | ```js
10 | const hashes = await Integrity.createDirHash('./')
11 |
12 | // Do something with the hashes here
13 | // It's advised not to modify them
14 | // as it will certainly lead to integrity check failure
15 | // when you'll try to check against them
16 | ```
17 |
18 | `TypeScript`
19 |
20 | ```ts
21 | const hashes: IHashObject = await Integrity.createDirHash('./');
22 |
23 | // Do something with the hashes here
24 | // It's advised not to modify them
25 | // as it will certainly lead to integrity check failure
26 | // when you'll try to check against them
27 | ```
28 |
29 | ---
30 |
31 | > Create a verbosely integrity object of the root directory, using the `sha1` algorithm and `base64` encoding
32 |
33 | `ES6+`
34 |
35 | ```js
36 | const options = { cryptoOptions: { algorithm: 'sha1', encoding: 'base64' } }
37 | const hashes = await Integrity.createDirHash('./', options)
38 |
39 | // Do something with the hashes here
40 | // It's advised not to modify them
41 | // as it will certainly lead to integrity check failure
42 | // when you'll try to check against them
43 | ```
44 |
45 | `TypeScript`
46 |
47 | ```ts
48 | const options: IntegrityOptions = { cryptoOptions: { algorithm: 'sha1', encoding: 'base64' } };
49 | const hashes: IHashObject = await Integrity.createDirHash('./', options);
50 |
51 | // Do something with the hashes here
52 | // It's advised not to modify them
53 | // as it will certainly lead to integrity check failure
54 | // when you'll try to check against them
55 | ```
56 |
57 | ---
58 |
59 | > Create a verbosely integrity object of a subdirectory
60 |
61 | `ES6+`
62 |
63 | ```ts
64 | const hashes = await Integrity.createDirHash('./sub')
65 |
66 | // Do something with the hashes here
67 | // It's advised not to modify them
68 | // as it will certainly lead to integrity check failure
69 | // when you'll try to check against them
70 | ```
71 |
72 | `TypeScript`
73 |
74 | ```ts
75 | const hashes: IHashObject = await Integrity.createDirHash('./sub');
76 |
77 | // Do something with the hashes here
78 | // It's advised not to modify them
79 | // as it will certainly lead to integrity check failure
80 | // when you'll try to check against them
81 | ```
82 |
83 | ---
84 |
85 | > Create a verbosely integrity object of a directory excluding a file
86 |
87 | `ES6+`
88 |
89 | ```js
90 | const options = { exclude: ['fileToExclude.txt'] }
91 | const hashes = await Integrity.createDirHash('./dir', options)
92 |
93 | // Do something with the hashes here
94 | // It's advised not to modify them
95 | // as it will certainly lead to integrity check failure
96 | // when you'll try to check against them
97 | ```
98 |
99 | `TypeScript`
100 |
101 | ```ts
102 | const options: IntegrityOptions = { exclude: ['fileToExclude.txt'] };
103 | const hashes: IHashObject = await Integrity.createDirHash('./dir', options);
104 |
105 | // Do something with the hashes here
106 | // It's advised not to modify them
107 | // as it will certainly lead to integrity check failure
108 | // when you'll try to check against them
109 | ```
110 |
111 | ---
112 |
113 | > Create a verbosely integrity object of a directory excluding a subdirectory
114 |
115 | `ES6+`
116 |
117 | ```js
118 | const options = { exclude: ['sub'] }
119 | const hashes = await Integrity.createDirHash('./dir', options)
120 |
121 | // Do something with the hashes here
122 | // It's advised not to modify them
123 | // as it will certainly lead to integrity check failure
124 | // when you'll try to check against them
125 | ```
126 |
127 | `TypeScript`
128 |
129 | ```ts
130 | const options: IntegrityOptions = { exclude: ['sub'] };
131 | const hashes: IHashObject = await Integrity.createDirHash('./dir', options);
132 |
133 | // Do something with the hashes here
134 | // It's advised not to modify them
135 | // as it will certainly lead to integrity check failure
136 | // when you'll try to check against them
137 | ```
138 |
--------------------------------------------------------------------------------
/docs/examples/createFileHash-es5-js.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.createFileHash` (ECMAScript 5)
4 |
5 | > Create a verbosely integrity object of a file
6 |
7 | ```js
8 | Integrity.createFileHash('/path/to/fileToHash.txt')
9 | .then(hashes => {
10 | // Do something with the hashes here
11 | // It's advised not to modify them
12 | // as it will certainly lead to integrity check failure
13 | // when you'll try to check against them
14 | })
15 | .catch(error => console.error(error))
16 | ```
17 |
--------------------------------------------------------------------------------
/docs/examples/createFileHash-js-ts.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.createFileHash` (ECMAScript 6+, TypeScript)
4 |
5 | > Create a verbosely integrity object of a file
6 |
7 | `ES6+`
8 |
9 | ```js
10 | const hashes = await Integrity.createFileHash('/path/to/fileToHash.txt')
11 |
12 | // Do something with the hashes here
13 | // It's advised not to modify them
14 | // as it will certainly lead to integrity check failure
15 | // when you'll try to check against them
16 | ```
17 |
18 | `TypeScript`
19 |
20 | ```ts
21 | const hashes: IHashObject = await Integrity.createFileHash('/path/to/fileToHash.txt');
22 |
23 | // Do something with the hashes here
24 | // It's advised not to modify them
25 | // as it will certainly lead to integrity check failure
26 | // when you'll try to check against them
27 | ```
28 |
--------------------------------------------------------------------------------
/docs/examples/createFilesHash-es5-js.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.createFilesHash` (ECMAScript 5)
4 |
5 | > Create a verbosely integrity object of a list of files
6 |
7 | ```js
8 | const listOfFiles = ['/path/to/file1.txt', './path/to/file2.txt']
9 | Integrity.createFilesHash(listOfFiles)
10 | .then(hashes => {
11 | // Do something with the hashes here
12 | // It's advised not to modify them
13 | // as it will certainly lead to integrity check failure
14 | // when you'll try to check against them
15 | })
16 | .catch(error => console.error(error))
17 | ```
18 |
--------------------------------------------------------------------------------
/docs/examples/createFilesHash-js-ts.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.createFilesHash` (ECMAScript 6+, TypeScript)
4 |
5 | > Create a verbosely integrity object of a list of files
6 |
7 | `ES6+`
8 |
9 | ```js
10 | const listOfFiles = ['/path/to/file1.txt', './path/to/file2.txt']
11 | const hashes = await Integrity.createFilesHash(listOfFiles)
12 |
13 | // Do something with the hashes here
14 | // It's advised not to modify them
15 | // as it will certainly lead to integrity check failure
16 | // when you'll try to check against them
17 | ```
18 |
19 | `TypeScript`
20 |
21 | ```ts
22 | const listOfFiles = ['/path/to/file1.txt', './path/to/file2.txt'];
23 | const hashes: IHashObject = await Integrity.createFilesHash(listOfFiles);
24 |
25 | // Do something with the hashes here
26 | // It's advised not to modify them
27 | // as it will certainly lead to integrity check failure
28 | // when you'll try to check against them
29 | ```
30 |
--------------------------------------------------------------------------------
/docs/examples/getExclusionsFromIgnoreFile-es5-js.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.getExclusionsFromIgnoreFile` (ECMAScript 5)
4 |
5 | > Get the exclusions from a `.nsriignore` file
6 |
7 | ```js
8 | Integrity.getExclusionsFromIgnoreFile()
9 | .then((excl) => console.log(excl))
10 | .catch(error => console.error(error))
11 | ```
12 |
--------------------------------------------------------------------------------
/docs/examples/getExclusionsFromIgnoreFile-js-ts.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.getExclusionsFromIgnoreFile` (ECMAScript 6+, TypeScript)
4 |
5 | > Get the exclusions from a `.nsriignore` file
6 |
7 | `ES6+`
8 |
9 | ```js
10 | const exclusions = await Integrity.getExclusionsFromIgnoreFile()
11 | console.log(exclusions)
12 | ```
13 |
14 | `TypeScript`
15 |
16 | ```ts
17 | const exclusions: string[] = await Integrity.getExclusionsFromIgnoreFile();
18 | console.log(exclusions);
19 | ```
20 |
--------------------------------------------------------------------------------
/docs/examples/getIntegrityOptionsFromConfig-es5-js.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.getIntegrityOptionsFromConfig` (ECMAScript 5)
4 |
5 | > Get the integrity options from a `cosmiconfig` compatible section
6 |
7 | ```js
8 | Integrity.getIntegrityOptionsFromConfig()
9 | .then((options) => console.log(options))
10 | .catch(error => console.error(error))
11 | ```
12 |
--------------------------------------------------------------------------------
/docs/examples/getIntegrityOptionsFromConfig-js-ts.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.getIntegrityOptionsFromConfig` (ECMAScript 6+, TypeScript)
4 |
5 | > Get the integrity options from a `cosmiconfig` compatible section
6 |
7 | `ES6+`
8 |
9 | ```js
10 | const options = await Integrity.getIntegrityOptionsFromConfig()
11 | console.log(options)
12 | ```
13 |
14 | `TypeScript`
15 |
16 | ```ts
17 | const options: IntegrityOptions = await Integrity.getIntegrityOptionsFromConfig();
18 | console.log(options);
19 | ```
20 |
--------------------------------------------------------------------------------
/docs/examples/getManifestIntegrity-es5-js.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.getManifestIntegrity` (ECMAScript 5)
4 |
5 | > Get the integrity object from the manifest file
6 |
7 | ```js
8 | Integrity.getManifestIntegrity()
9 | .then((intObj) => console.log(intObj))
10 | .catch(error => console.error(error))
11 | ```
12 |
--------------------------------------------------------------------------------
/docs/examples/getManifestIntegrity-js-ts.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.getManifestIntegrity` (ECMAScript 6+, TypeScript)
4 |
5 | > Get the integrity object from the manifest file
6 |
7 | `ES6+`
8 |
9 | ```js
10 | const integrity = await Integrity.getManifestIntegrity()
11 | console.log(integrity)
12 | ```
13 |
14 | `TypeScript`
15 |
16 | ```ts
17 | const integrity: string = await Integrity.getManifestIntegrity();
18 | console.log(integrity);
19 | ```
20 |
--------------------------------------------------------------------------------
/docs/examples/persist-es5-js.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.persist` (ECMAScript 5)
4 |
5 | > Persist the integrity object on the current working directory
6 |
7 | ```js
8 | // Assuming you have previously created an integrity object
9 |
10 | // Persist it on disk
11 | Integrity.persist(intObj)
12 | .then(() => console.log('Integrity file saved'))
13 | .catch(error => console.error(error))
14 | ```
15 |
16 | ---
17 |
18 | > Persist the integrity object on a specific directory (absolute path)
19 |
20 | ```js
21 | // Assuming you have previously created an integrity object
22 |
23 | // Persist it on disk
24 | Integrity.persist(intObj, '/dir/to/persist/the/integrity/object')
25 | .then(() => console.log('Integrity file saved'))
26 | .catch(error => console.error(error))
27 | ```
28 |
29 | ---
30 |
31 | > Persist the integrity object on a specific directory (relative path)
32 |
33 | ```js
34 | // Assuming you have previously created an integrity object
35 |
36 | // Persist it on disk
37 | Integrity.persist(intObj, './dir/to/persist/the/integrity/object')
38 | .then(() => console.log('Integrity file saved'))
39 | .catch(error => console.error(error))
40 | ```
41 |
--------------------------------------------------------------------------------
/docs/examples/persist-js-ts.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.persist` (ECMAScript 6+, TypeScript)
4 |
5 | > Persist the integrity object on the current working directory
6 |
7 | `ES6+`
8 |
9 | ```js
10 | // Assuming you have previously created an integrity object
11 |
12 | // Persist it on disk
13 | await Integrity.persist(intObj)
14 | console.log('Integrity file saved')
15 | ```
16 |
17 | `TypeScript`
18 |
19 | ```ts
20 | // Assuming you have previously created an integrity object
21 |
22 | // Persist it on disk
23 | await Integrity.persist(intObj);
24 | console.log('Integrity file saved');
25 | ```
26 |
27 | ---
28 |
29 | `ES6+`
30 |
31 | > Persist the integrity object on a specific directory (absolute path)
32 |
33 | ```js
34 | // Assuming you have previously created an integrity object
35 |
36 | // Persist it on disk
37 | await Integrity.persist(intObj, '/dir/to/persist/the/integrity/object')
38 | console.log('Integrity file saved')
39 | ```
40 |
41 | `TypeScript`
42 |
43 | ```ts
44 | // Assuming you have previously created an integrity object
45 |
46 | // Persist it on disk
47 | await Integrity.persist(intObj, '/dir/to/persist/the/integrity/object');
48 | console.log('Integrity file saved');
49 | ```
50 |
51 | ---
52 |
53 | > Persist the integrity object on a specific directory (relative path)
54 |
55 | `ES6+`
56 |
57 | ```js
58 | // Assuming you have previously created an integrity object
59 |
60 | // Persist it on disk
61 | await Integrity.persist(intObj, './dir/to/persist/the/integrity/object')
62 | console.log('Integrity file saved')
63 | ```
64 |
65 | `TypeScript`
66 |
67 | ```ts
68 | // Assuming you have previously created an integrity object
69 |
70 | // Persist it on disk
71 | await Integrity.persist(intObj, './dir/to/persist/the/integrity/object');
72 | console.log('Integrity file saved');
73 | ```
74 |
--------------------------------------------------------------------------------
/docs/examples/updateManifestIntegrity-es5-js.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.updateManifestIntegrity` (ECMAScript 5)
4 |
5 | > Update the integrity object on the manifest file
6 |
7 | ```js
8 | // Assuming you have previously created an integrity object
9 |
10 | // Persist it on the manifest file
11 | Integrity.updateManifestIntegrity(intObj)
12 | .then(() => console.log('Integrity hash created -> Manifest updated'))
13 | .catch(error => console.error(error))
14 | ```
15 |
--------------------------------------------------------------------------------
/docs/examples/updateManifestIntegrity-js-ts.md:
--------------------------------------------------------------------------------
1 | # API Examples
2 |
3 | ## `.updateManifestIntegrity` (ECMAScript 6+, TypeScript)
4 |
5 | > Update the integrity object on the manifest file
6 |
7 | `ES6+`
8 |
9 | ```js
10 | // Assuming you have previously created an integrity object
11 |
12 | // Persist it on the manifest file
13 | await Integrity.updateManifestIntegrity(intObj)
14 | console.log('Integrity hash created -> Manifest updated')
15 | ```
16 |
17 | `TypeScript`
18 |
19 | ```ts
20 | // Assuming you have previously created an integrity object
21 |
22 | // Persist it on the manifest file
23 | await Integrity.updateManifestIntegrity(intObj);
24 | console.log('Integrity hash created -> Manifest updated');
25 | ```
26 |
--------------------------------------------------------------------------------
/docs/importation.md:
--------------------------------------------------------------------------------
1 | # Importation
2 |
3 | ## ES5
4 |
5 | **Import as a `Class`**
6 |
7 | ```js
8 | var Integrity = require('nsri').Integrity;
9 | ```
10 |
11 | **Import as a `Namespace`**
12 |
13 | ```js
14 | var nsri = require('nsri');
15 |
16 | // and use as
17 | nsri.Integrity. ...
18 | ```
19 |
20 | ## ES6+, Typescript
21 |
22 | **Import as a `Class`**
23 |
24 | All examples require to import the `Integrity` class before you will be able to use them.
25 |
26 | Additionally `CryptoOptions`, `IntegrityOptions`, `IntegrityObject`, `HashObject` and `VerboseHashObject` are also available types.
27 |
28 | ```ts
29 | import { Integrity } from 'nsri';
30 | ```
31 |
32 | **Import as a `Namespace`**
33 |
34 | You can also import it as a namespace.
35 |
36 | ```ts
37 | import * as nsri from 'nsri';
38 | ```
39 |
40 | In that case, all function calls should be modified to use `nsri` before the classes or types.
41 |
42 | ```ts
43 | nsri.Integrity. ...
44 |
45 | nsri.ICryptoOptions. ...
46 |
47 | nsri.IntegrityObject. ...
48 |
49 | nsri.IntegrityOptions. ...
50 | ```
51 |
--------------------------------------------------------------------------------
/media/integrity_file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JimiC/nsri/8df3c54a8bcc909d6e130eab95c131b75371c2e2/media/integrity_file.png
--------------------------------------------------------------------------------
/media/integrity_file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/media/nsri-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JimiC/nsri/8df3c54a8bcc909d6e130eab95c131b75371c2e2/media/nsri-logo.png
--------------------------------------------------------------------------------
/media/nsri-logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nsri",
3 | "version": "8.0.0",
4 | "description": "Node.js utility tool for creating and checking subresource integrity",
5 | "license": "MIT",
6 | "author": {
7 | "email": "jimikar@gmail.com",
8 | "name": "Jimi (Dimitris) Charalampidis"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "JimiC/nsri"
13 | },
14 | "bugs": {
15 | "url": "https://github.com/JimiC/nsri/issues"
16 | },
17 | "homepage": "https://github.com/JimiC/nsri#readme",
18 | "engines": {
19 | "node": ">=18"
20 | },
21 | "keywords": [
22 | "node",
23 | "nodejs",
24 | "cli",
25 | "integrity",
26 | "checker",
27 | "check",
28 | "validation",
29 | "file",
30 | "files",
31 | "folder",
32 | "forders",
33 | "directory",
34 | "directories",
35 | "structure",
36 | "hash",
37 | "checksum",
38 | "checksums",
39 | "nsri"
40 | ],
41 | "main": "lib/index.js",
42 | "module": "lib/index.es.js",
43 | "bin": {
44 | "nsri": "lib/cli.js"
45 | },
46 | "files": [
47 | "lib"
48 | ],
49 | "types": "lib/nsri.d.ts",
50 | "scripts": {
51 | "reinstall": "rimraf ./package-lock.json ./node_modules && npm i",
52 | "start": "node ./out/src/cli.js",
53 | "lint": "eslint --ext .ts .",
54 | "prebuild:dev": "npm run lint",
55 | "build:dev": "npm run build -- -p tsconfig.dev.json",
56 | "prebuild": "npm run cleanup",
57 | "build": "tsc",
58 | "cleanup": "rimraf ./.nyc_output ./coverage ./out ./lib ./dist",
59 | "pretest": "npm run build:dev",
60 | "test": "nyc mocha",
61 | "posttest": "nyc report -r lcov",
62 | "prepublishOnly": "npm run cleanup && npx rollup -c --bundleConfigAsCjs"
63 | },
64 | "dependencies": {
65 | "ajv": "^8.12.0",
66 | "cosmiconfig": "^8.2.0",
67 | "detect-indent": "^6.1.0",
68 | "minimatch": "^9.0.3",
69 | "yargs": "^17.7.2"
70 | },
71 | "devDependencies": {
72 | "@microsoft/api-extractor": "^7.36.3",
73 | "@rollup/plugin-terser": "^0.4.3",
74 | "@rollup/plugin-typescript": "^11.1.2",
75 | "@types/chai": "^4.3.5",
76 | "@types/mocha": "^10.0.1",
77 | "@types/node": "18",
78 | "@types/sinon": "^10.0.16",
79 | "@types/yargs": "^17.0.24",
80 | "@typescript-eslint/eslint-plugin": "^6.2.1",
81 | "@typescript-eslint/parser": "^6.2.1",
82 | "builtin-modules": "^3.3.0",
83 | "chai": "^4.3.7",
84 | "eslint": "^8.46.0",
85 | "eslint-plugin-import": "^2.28.0",
86 | "husky": "^8.0.3",
87 | "lint-staged": "^13.2.3",
88 | "mocha": "^10.2.0",
89 | "nyc": "^15.1.0",
90 | "rimraf": "^5.0.1",
91 | "rollup": "^3.27.1",
92 | "rollup-plugin-api-extractor": "0.2.5",
93 | "rollup-plugin-copy": "^3.4.0",
94 | "rollup-plugin-preserve-shebangs": "^0.2.0",
95 | "sinon": "^15.2.0",
96 | "tslib": "^2.6.1",
97 | "typescript": "^5.1.6"
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import terser from '@rollup/plugin-terser';
2 | import ts from '@rollup/plugin-typescript';
3 | import builtins from 'builtin-modules';
4 | import { apiExtractor } from "rollup-plugin-api-extractor";
5 | import copy from 'rollup-plugin-copy';
6 | import { preserveShebangs } from 'rollup-plugin-preserve-shebangs';
7 | import typescript from 'typescript';
8 | import pkg from './package.json';
9 |
10 | export default [
11 | {
12 | input: 'src/index.ts',
13 | output: [
14 | {
15 | dir: 'dist',
16 | entryFileNames: pkg.main,
17 | format: 'cjs',
18 | },
19 | {
20 | dir: 'dist',
21 | entryFileNames: pkg.module,
22 | format: 'es',
23 | },
24 | ],
25 | external: [
26 | ...Object.keys(pkg.dependencies || {}),
27 | ...Object.keys(pkg.peerDependencies || {}),
28 | ...builtins,
29 | ],
30 | plugins: [
31 | ts({
32 | typescript,
33 | }),
34 | terser(),
35 | copy({
36 | targets: [
37 | { src: 'src/app/schemas', dest: 'lib' },
38 | { src: 'dist/lib', dest: '.' },
39 | ],
40 | copyOnce: true,
41 | hook: 'writeBundle'
42 | }),
43 | apiExtractor({
44 | configuration: {
45 | projectFolder: ".",
46 | },
47 | configFile: "./api-extractor.json",
48 | cleanUpRollup: false
49 | })
50 | ],
51 | },
52 | {
53 | input: 'src/cli.ts',
54 | output: [
55 | {
56 | dir: 'dist',
57 | entryFileNames: pkg.bin.nsri,
58 | format: 'cjs',
59 | }
60 | ],
61 | external: [
62 | ...Object.keys(pkg.dependencies || {}),
63 | ...Object.keys(pkg.peerDependencies || {}),
64 | ...builtins,
65 | ],
66 | plugins: [
67 | ts({
68 | typescript,
69 | }),
70 | preserveShebangs(),
71 | terser(),
72 | copy({
73 | targets: [
74 | { src: 'src/app/schemas', dest: 'lib' },
75 | { src: 'dist/lib', dest: '.' },
76 | ],
77 | copyOnce: true,
78 | hook: 'writeBundle'
79 | }),
80 | apiExtractor({
81 | configuration: {
82 | projectFolder: ".",
83 | },
84 | configFile: "./api-extractor.json",
85 | cleanUpRollup: false
86 | })
87 | ],
88 | }
89 | ]
90 |
--------------------------------------------------------------------------------
/src/abstractions/baseLogger.ts:
--------------------------------------------------------------------------------
1 | import { Spinner } from '../interfaces/spinner';
2 |
3 | /** @internal */
4 | export abstract class BaseLogger {
5 | public abstract log(...args: unknown[]): void;
6 |
7 | public abstract error(...args: unknown[]): void;
8 |
9 | public abstract updateLog(...args: unknown[]): void;
10 |
11 | public abstract spinnerLogStart(...args: unknown[]): Spinner;
12 |
13 | public abstract spinnerLogStop(spinner: Spinner, ...args: unknown[]): void;
14 | }
15 |
--------------------------------------------------------------------------------
/src/app/schemas/v1/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-07/schema#",
3 | "title": "Integrity",
4 | "description": "Integrity object schema",
5 | "definitions": {
6 | "entity": {
7 | "title": "The contents or computed hash of the directory",
8 | "type": [
9 | "object",
10 | "string"
11 | ],
12 | "properties": {
13 | "hash": {
14 | "$ref": "#/definitions/hash"
15 | },
16 | "contents": {
17 | "$ref": "#/definitions/hashes"
18 | },
19 | "additionalProperties": false
20 | },
21 | "additionalProperties": false,
22 | "required": [
23 | "hash",
24 | "contents"
25 | ]
26 | },
27 | "hash": {
28 | "title": "The computed hash of the directory",
29 | "type": "string"
30 | },
31 | "hashes": {
32 | "title": "The computed hashes",
33 | "type": "object",
34 | "patternProperties": {
35 | "^[\\w\\-.]+$": {
36 | "$ref": "#/definitions/entity"
37 | }
38 | },
39 | "additionalProperties": false
40 | }
41 | },
42 | "type": "object",
43 | "properties": {
44 | "version": {
45 | "title": "The schema version",
46 | "type": "string",
47 | "pattern": "^[0-9](?:\\.[0-9])?$"
48 | },
49 | "hashes": {
50 | "$ref": "#/definitions/hashes"
51 | },
52 | "additionalProperties": false
53 | },
54 | "required": [
55 | "version",
56 | "hashes"
57 | ]
58 | }
59 |
--------------------------------------------------------------------------------
/src/cli.ts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | import cli from './cli/index';
3 |
4 | void cli();
5 |
--------------------------------------------------------------------------------
/src/cli/index.ts:
--------------------------------------------------------------------------------
1 | import { BinaryToTextEncoding } from 'crypto';
2 | import { Integrity, IntegrityObject, IntegrityOptions } from '../';
3 | import { ConfigExplorer } from '../common/configExplorer';
4 | import { Logger } from '../common/logger';
5 | import { normalizeEntries, unique } from '../common/utils';
6 | import { YargsParser } from '../common/yargsParser';
7 | import { ParsedArgs } from '../interfaces/parsedArgs';
8 | import { Spinner } from '../interfaces/spinner';
9 |
10 | /** @internal */
11 | export default (async (): Promise => {
12 | const id = 'nsri';
13 | const logger = new Logger();
14 | logger.eventEmitter.on('SIGINT', (): void => logger.handleForcedExit(!!logger));
15 | let spinner: Spinner = { timer: setInterval((): void => void 0), line: 1 };
16 | let command = '';
17 | let message = '';
18 | try {
19 | await new ConfigExplorer().assignArgs();
20 | const parser = new YargsParser();
21 | const pargs: ParsedArgs = await parser.parse();
22 | let exclusions: string[] = await Integrity.getExclusionsFromIgnoreFile();
23 | exclusions = unique([...exclusions, ...normalizeEntries(pargs.exclude)]);
24 | const options: IntegrityOptions = {
25 | cryptoOptions: {
26 | dirAlgorithm: pargs.dirAlgorithm,
27 | encoding: pargs.encoding as BinaryToTextEncoding,
28 | fileAlgorithm: pargs.fileAlgorithm,
29 | },
30 | exclude: exclusions,
31 | strict: pargs.strict,
32 | verbose: pargs.verbose,
33 | };
34 | command = pargs.command;
35 | if (command === 'create') {
36 | spinner = logger.spinnerLogStart('Creating integrity hash', id);
37 | const intObj: IntegrityObject = await Integrity.create(pargs.inPath, options);
38 | if (!pargs.manifest) {
39 | await Integrity.persist(intObj, pargs.outPath, pargs.pretty);
40 | message = 'Integrity hash file created';
41 | } else {
42 | await Integrity.updateManifestIntegrity(intObj);
43 | message = 'Integrity hash created -> Manifest updated';
44 | }
45 | }
46 | if (command === 'check') {
47 | spinner = logger.spinnerLogStart(`Checking integrity of: '${pargs.inPath}'`, id);
48 | const integrity: string = pargs.manifest ? await Integrity.getManifestIntegrity() : pargs.integrity;
49 | const passed: boolean = await Integrity.check(pargs.inPath, integrity, options);
50 | message = `Integrity ${passed ? 'validated' : 'check failed'}`;
51 | }
52 | logger.spinnerLogStop(spinner, message, id);
53 | } catch (error) {
54 | const err = error as Error;
55 | logger.spinnerLogStop(spinner, `Failed to ${command} integrity hash`, id);
56 | logger.updateLog(`Error: ${err.message || err.toString()}`);
57 | } finally {
58 | process.exit();
59 | }
60 | });
61 |
--------------------------------------------------------------------------------
/src/common/configExplorer.ts:
--------------------------------------------------------------------------------
1 | import { cosmiconfig } from 'cosmiconfig';
2 | import { ConfigOptions } from '../interfaces/configOptions';
3 |
4 | /** @internal */
5 | type Explorer = ReturnType;
6 |
7 | /** @internal */
8 | export class ConfigExplorer {
9 | private explorer: Explorer;
10 | public constructor() {
11 | this.explorer = cosmiconfig('nsri');
12 | }
13 |
14 | public async assignArgs(): Promise {
15 | const config = await this.getConfig();
16 | if (!Object.keys(config).length) {
17 | return Promise.resolve();
18 | }
19 | if (!this.existsArg(['-m', 'manifest']) && config.manifest !== undefined) {
20 | process.argv.push('-m', config.manifest.toString());
21 | }
22 | if (!this.existsArg(['-s', 'source']) && config.source) {
23 | process.argv.push('-s', config.source);
24 | }
25 | if (!this.existsArg(['-v', 'verbose']) && config.verbose !== undefined) {
26 | process.argv.push('-v', config.verbose.toString());
27 | }
28 | if (!this.existsArg(['-st', 'strict']) && config.strict !== undefined) {
29 | process.argv.push('-st', config.strict.toString());
30 | }
31 | if (!this.existsArg(['-da', 'diralgorithm']) && config.cryptoOptions?.dirAlgorithm) {
32 | process.argv.push('-da', config.cryptoOptions.dirAlgorithm);
33 | }
34 | if (!this.existsArg(['-fa', 'filealgorithm']) && config.cryptoOptions?.fileAlgorithm) {
35 | process.argv.push('-fa', config.cryptoOptions.fileAlgorithm);
36 | }
37 | if (!this.existsArg(['-e', 'encoding']) && config.cryptoOptions?.encoding) {
38 | process.argv.push('-e', config.cryptoOptions.encoding);
39 | }
40 | if (!this.existsArg(['-x', 'exclude']) && config.exclude) {
41 | process.argv.push('-x', ...config.exclude);
42 | }
43 | if (!this.existsArg(['-i', 'integrity']) && config.integrity) {
44 | process.argv.push('-i', config.integrity);
45 | }
46 | if (!this.existsArg(['-o', 'output']) && config.output) {
47 | process.argv.push('-o', config.output);
48 | }
49 | }
50 |
51 | public async getConfig(fromPath?: string): Promise {
52 | if (!this.explorer) {
53 | return Promise.reject(new Error('CosmiConfig not initialized'));
54 | }
55 | this.explorer.clearSearchCache();
56 | const result = await this.explorer.search(fromPath);
57 | return (result ? result.config : {}) as ConfigOptions;
58 | }
59 |
60 | private existsArg(args: string[]): boolean {
61 | return args.some((arg: string): boolean =>
62 | process.argv.some((argv: string): boolean => argv === arg));
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/common/constants.ts:
--------------------------------------------------------------------------------
1 | import { BinaryToTextEncoding } from 'crypto';
2 | import { CryptoEncoding } from './enums';
3 |
4 | /** @internal */
5 | export const integrityFilename = '.integrity.json';
6 |
7 | /** @internal */
8 | export const manifestFile = 'package.json';
9 |
10 | /** @internal */
11 | export const defaultFileCryptoAlgorithm = 'sha1';
12 |
13 | /** @internal */
14 | export const defaultDirCryptoAlgorithm = 'sha512';
15 |
16 | /** @internal */
17 | export const defaultCryptoEncoding: BinaryToTextEncoding = CryptoEncoding.base64;
18 |
19 | /** @internal */
20 | export const ignoreFile = '.nsriignore';
21 |
22 | /** @internal */
23 | export const defaultExclusions = [
24 | `**/${integrityFilename}`,
25 | '**/.git*',
26 | '**/.hg*',
27 | '**/.svn*',
28 | '**/node_modules/**',
29 | ];
30 |
--------------------------------------------------------------------------------
/src/common/enums.ts:
--------------------------------------------------------------------------------
1 | /** @internal */
2 | export enum CryptoEncoding {
3 | hex = 'hex',
4 | base64 = 'base64',
5 | base64url = 'base64url',
6 | }
7 |
--------------------------------------------------------------------------------
/src/common/fsAsync.ts:
--------------------------------------------------------------------------------
1 | import { exists, lstat, readdir, readFile, writeFile } from 'fs';
2 | import { promisify } from 'util';
3 |
4 | /** @internal */
5 | export const existsAsync = promisify(exists);
6 |
7 | /** @internal */
8 | export const lstatAsync = promisify(lstat);
9 |
10 | /** @internal */
11 | export const readdirAsync = promisify(readdir);
12 |
13 | /** @internal */
14 | export const readFileAsync = promisify(readFile);
15 |
16 | /** @internal */
17 | export const writeFileAsync = promisify(writeFile);
18 |
--------------------------------------------------------------------------------
/src/common/logger.ts:
--------------------------------------------------------------------------------
1 | import { clearLine, createInterface, cursorTo, moveCursor, ReadLine } from 'readline';
2 | import { BaseLogger } from '../abstractions/baseLogger';
3 | import { Spinner } from '../interfaces/spinner';
4 |
5 | /** @internal */
6 | export class Logger extends BaseLogger {
7 |
8 | public eventEmitter: ReadLine;
9 | public frames: string[];
10 | public showSpinnerInFront: boolean;
11 | public spinnerInterval: number;
12 |
13 | private countLines: number;
14 |
15 | public constructor() {
16 | super();
17 | this.eventEmitter = createInterface(process.stdin, process.stdout);
18 | this.frames = ['- ', '\\ ', '| ', '/ '];
19 | this.showSpinnerInFront = true;
20 | this.spinnerInterval = 80;
21 | this.countLines = 1;
22 | }
23 |
24 | public log(message: string, groupId?: string): void {
25 | process.stdout.write(`${this.getHeader(groupId)}${message}\n`);
26 | this.countLines++;
27 | }
28 |
29 | public error(message: string, groupId?: string): void {
30 | process.stderr.write(`${this.getHeader(groupId)}${message}\n`);
31 | this.countLines++;
32 | }
33 |
34 | public updateLog(message: string, groupId?: string): void;
35 | public updateLog(message: string, line?: number, groupId?: string): void;
36 | public updateLog(message: string, lineOrGroupId?: number | string, groupId?: string): void {
37 | groupId = (typeof lineOrGroupId === 'string' && Number.isNaN(Number.parseInt(lineOrGroupId, 10)))
38 | ? lineOrGroupId
39 | : groupId;
40 |
41 | if (!process.stdout.isTTY) {
42 | process.stdout.write(`${this.getHeader(groupId)}${message}\n`);
43 | return;
44 | }
45 |
46 | const line = (typeof lineOrGroupId === 'number' && !Number.isNaN(lineOrGroupId))
47 | ? lineOrGroupId
48 | : 1;
49 | this.moveCursorTo(-line);
50 | clearLine(process.stdout, 0);
51 | process.stdout.write(`${this.getHeader(groupId)}${message}`);
52 | this.moveCursorTo(line);
53 | }
54 |
55 | public spinnerLogStart(message: string, groupId?: string): Spinner {
56 | const line = this.countLines;
57 | this.log(message, groupId);
58 | return { timer: this.spin(message, groupId, line), line };
59 | }
60 |
61 | public spinnerLogStop(spinner: Spinner, message: string, groupId?: string): void {
62 | clearInterval(spinner.timer);
63 | this.updateLog(message, this.countLines - spinner.line, groupId);
64 | if (!process.stdout.isTTY) {
65 | return;
66 | }
67 | this.cursorShow();
68 | }
69 |
70 | public handleForcedExit(hasInfoLogging: boolean): void {
71 | if (!process.stdout.isTTY) {
72 | return process.exit();
73 | }
74 | const moveAndClear = (): void => {
75 | this.moveCursorTo(-1);
76 | clearLine(process.stdout, 0);
77 | };
78 | clearLine(process.stdout, 0);
79 | this.updateLog('');
80 | if (hasInfoLogging) {
81 | this.updateLog('', 2);
82 | moveAndClear();
83 | }
84 | moveAndClear();
85 | this.cursorShow();
86 | process.exit();
87 | }
88 |
89 | public moveCursorTo(line: number): void {
90 | if (!process.stdout.isTTY) {
91 | return;
92 | }
93 | cursorTo(process.stdout, 0);
94 | moveCursor(process.stdout, 0, line);
95 | }
96 |
97 | private spin(message: string, groupId: string | undefined, line: number): NodeJS.Timer {
98 | if (!process.stdout.isTTY) {
99 | return setInterval((): void => void 0);
100 | }
101 | let index = 0;
102 | this.cursorHide();
103 | const iteration = (): void => {
104 | const frame = this.frames[index = ++index % this.frames.length];
105 | const msg = this.showSpinnerInFront
106 | ? `${this.getHeader(groupId)}${frame}${message}`
107 | : `${this.getHeader(groupId)}${message}${frame}`;
108 | this.updateLog(msg, this.countLines - line);
109 | };
110 | iteration();
111 | return setInterval(iteration, this.spinnerInterval);
112 | }
113 |
114 | private cursorShow(): void {
115 | process.stdout.write('\u001B[?25h');
116 | }
117 |
118 | private cursorHide(): void {
119 | process.stdout.write('\u001B[?25l');
120 | }
121 |
122 | private getHeader(groupId?: string): string {
123 | return groupId ? `[${groupId}]: ` : '';
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/common/utils.ts:
--------------------------------------------------------------------------------
1 | import { getHashes } from 'crypto';
2 | import detectIndent from 'detect-indent';
3 |
4 | /** @internal */
5 | export const hexRegexPattern = /^(?:[a-f0-9])+$/;
6 |
7 | /** @internal */
8 | export const base64RegexPattern = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
9 |
10 | /** @internal */
11 | export const base64urlRegexPattern = /^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}|[A-Za-z0-9-_]{3})?$/;
12 |
13 | /** @internal */
14 | export function isSupportedHash(algorithm: string): boolean {
15 | return getHashes().some((hash: string): boolean => hash.toUpperCase() === algorithm.toUpperCase());
16 | }
17 |
18 | /** @internal */
19 | export function parseJSONSafe(data: string | Buffer): T {
20 | try {
21 | const text = Buffer.isBuffer(data) ? data.toString() : data;
22 | return JSON.parse(text) as T;
23 | } catch {
24 | return {} as T;
25 | }
26 | }
27 |
28 | /** @internal */
29 | export function sortObject(obj: Record): Record {
30 | return Object.keys(obj)
31 | .sort()
32 | .reduce((p: Record, c: string): Record => {
33 | p[c] = obj[c];
34 | return p;
35 | }, {});
36 | }
37 |
38 | /** @internal */
39 | export function getIndentation(text: string): detectIndent.Indent {
40 | return detectIndent(text);
41 | }
42 |
43 | /** @internal */
44 | export function normalizeEntries(entries: string[]): string[] {
45 | return entries
46 | .map((entry: string): string => entry.trim())
47 | .filter((entry: string): boolean => !!entry && !/^\s*#/.test(entry));
48 | }
49 |
50 | /** @internal */
51 | export function unique(entries: string[]): string[] {
52 | return entries.filter((entry: string, index: number, array: string[]): boolean =>
53 | array.indexOf(entry) === index);
54 | }
55 |
--------------------------------------------------------------------------------
/src/common/yargsParser.ts:
--------------------------------------------------------------------------------
1 | import { existsSync, statSync } from 'fs';
2 | import { dirname } from 'path';
3 | import y, { Arguments } from 'yargs';
4 | import { Arguments as Args } from '../interfaces/arguments';
5 | import { ParsedArgs } from '../interfaces/parsedArgs';
6 | import { sortObject } from './utils';
7 |
8 |
9 | /** @internal */
10 | export class YargsParser {
11 | private readonly commonOptions: { [key: string]: y.Options } = {
12 | diralgorithm: {
13 | alias: 'da',
14 | default: 'sha512',
15 | description: 'The algorithm to use for directory hashing',
16 | type: 'string',
17 | },
18 | encoding: {
19 | alias: 'e',
20 | default: 'base64',
21 | description: 'The encoding to use for hashing',
22 | type: 'string',
23 | },
24 | exclude: {
25 | alias: 'x',
26 | default: [],
27 | description: 'Files and/or directories paths to exclude',
28 | type: 'array',
29 | },
30 | filealgorithm: {
31 | alias: 'fa',
32 | default: 'sha1',
33 | description: 'The algorithm to use for file hashing',
34 | type: 'string',
35 | },
36 | manifest: {
37 | alias: 'm',
38 | default: undefined,
39 | description: `The integrity hash gets persisted to, or read from, the project's manifest (package.json)`,
40 | type: 'boolean',
41 | },
42 | source: {
43 | alias: 's',
44 | demandOption: true,
45 | description: 'The path to the file or directory to hash',
46 | type: 'string',
47 | },
48 | strict: {
49 | alias: 'st',
50 | default: false,
51 | description: 'Use directory name in root hash',
52 | type: 'boolean',
53 | },
54 | verbose: {
55 | alias: 'v',
56 | default: false,
57 | description: 'Verbosely create hashes of a directory',
58 | type: 'boolean',
59 | },
60 | };
61 |
62 | private readonly createOptions: { [key: string]: y.Options } = {
63 | output: {
64 | alias: 'o',
65 | description: 'The directory path where to persist the created integrity file' +
66 | ` (ignored when 'manifest' option specified)`,
67 | type: 'string',
68 | },
69 | pretty: {
70 | alias: 'p',
71 | default: false,
72 | description: 'Prettify the integrity object',
73 | type: 'boolean',
74 | },
75 | };
76 |
77 | private readonly checkOptions: { [key: string]: y.Options } = {
78 | integrity: {
79 | alias: 'i',
80 | conflicts: 'manifest',
81 | description: 'The integrity hash, JSON, file or directory path, to check against' +
82 | ` ([required] when 'manifest' option not specified)`,
83 | type: 'string',
84 | },
85 | };
86 |
87 | public constructor() {
88 | y
89 | .usage('Usage: $0 {command} [options]')
90 | .command('create [options]',
91 | `Creates integrity hash from the provided source (use '--help' for [options] details)`,
92 | sortObject({ ...this.createOptions, ...this.commonOptions }) as { [key: string]: y.Options })
93 | .command('check [options]',
94 | `Checks integrity hash against the provided source (use '--help' for [options] details)`,
95 | sortObject({ ...this.checkOptions, ...this.commonOptions }) as { [key: string]: y.Options })
96 | .demandCommand(1, 'Missing command')
97 | .recommendCommands()
98 | .version(false)
99 | .options({
100 | help: {
101 | alias: 'h',
102 | description: 'Show help',
103 | global: true,
104 | },
105 | version: {
106 | alias: 'V',
107 | description: 'Show version number',
108 | global: false,
109 | },
110 | })
111 | .check((argv: y.Arguments): boolean => this.validate(argv))
112 | .strict();
113 | }
114 |
115 | public async parse(): Promise {
116 | const pargs: Arguments = await y.parseAsync(process.argv.slice(2));
117 | // Set 'output' dir same as 'source' when not provided
118 | if (!pargs.output) {
119 | const source = pargs.source as string;
120 | pargs.output = statSync(source).isFile()
121 | ? dirname(source)
122 | : pargs.source;
123 | }
124 | return {
125 | command: pargs._[0] as string,
126 | dirAlgorithm: pargs.diralgorithm as string,
127 | encoding: pargs.encoding as string,
128 | exclude: pargs.exclude as string[],
129 | fileAlgorithm: pargs.filealgorithm as string,
130 | inPath: pargs.source as string,
131 | integrity: pargs.integrity as string,
132 | manifest: pargs.manifest as boolean,
133 | outPath: pargs.output as string,
134 | pretty: pargs.pretty as boolean,
135 | strict: pargs.strict as boolean,
136 | verbose: pargs.verbose as boolean,
137 | };
138 | }
139 |
140 | private validate(argv: y.Arguments): boolean {
141 | let errorMsg = '';
142 | if (!existsSync(argv.source as string)) {
143 | errorMsg = `ENOENT: no such file or directory, '${argv.source as string}'`;
144 | }
145 | if (argv._[0] === 'check' && !argv.manifest && !argv.integrity) {
146 | errorMsg = 'Missing required argument: integrity';
147 | }
148 | if (errorMsg) {
149 | throw new Error(errorMsg);
150 | }
151 | return true;
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './app/integrity';
2 | export * from './interfaces/cryptoOptions';
3 | export * from './interfaces/hashObject';
4 | export * from './interfaces/integrityObject';
5 | export * from './interfaces/integrityOptions';
6 | export * from './interfaces/verboseHashObject';
7 |
--------------------------------------------------------------------------------
/src/interfaces/arguments.ts:
--------------------------------------------------------------------------------
1 | /** @internal */
2 | export interface Arguments {
3 | _: Array<(string | number)>;
4 | $0: string;
5 | diralgorithm?: string;
6 | source?: string;
7 | encoding?: string;
8 | exclude?: string[];
9 | filealgorithm?: string;
10 | integrity?: string;
11 | manifest?: boolean;
12 | output?: string;
13 | pretty?: boolean;
14 | strict?: boolean;
15 | verbose?: boolean;
16 | }
17 |
--------------------------------------------------------------------------------
/src/interfaces/configOptions.ts:
--------------------------------------------------------------------------------
1 | import { CryptoOptions } from './cryptoOptions';
2 |
3 | /** @internal */
4 | export interface ConfigOptions {
5 | manifest?: boolean;
6 | source?: string;
7 | verbose?: boolean;
8 | cryptoOptions? : CryptoOptions;
9 | exclude?: string[];
10 | integrity?: string;
11 | output?: string;
12 | strict?: boolean;
13 | }
14 |
--------------------------------------------------------------------------------
/src/interfaces/cryptoOptions.ts:
--------------------------------------------------------------------------------
1 | import { BinaryToTextEncoding } from 'crypto';
2 |
3 | /** @public */
4 | export interface CryptoOptions {
5 | dirAlgorithm?: string;
6 | encoding?: BinaryToTextEncoding;
7 | fileAlgorithm?: string;
8 | }
9 |
--------------------------------------------------------------------------------
/src/interfaces/hashObject.ts:
--------------------------------------------------------------------------------
1 | import { VerboseHashObject } from './verboseHashObject';
2 |
3 | /** @public */
4 | export interface HashObject {
5 | [key: string]: string | VerboseHashObject;
6 | }
7 |
--------------------------------------------------------------------------------
/src/interfaces/integrityObject.ts:
--------------------------------------------------------------------------------
1 | import { HashObject } from './hashObject';
2 |
3 | /** @public */
4 | export interface IntegrityObject extends Record {
5 | version: string;
6 | hashes: HashObject;
7 | }
8 |
--------------------------------------------------------------------------------
/src/interfaces/integrityOptions.ts:
--------------------------------------------------------------------------------
1 | import { CryptoOptions } from './cryptoOptions';
2 |
3 | /** @public */
4 | export interface IntegrityOptions {
5 | cryptoOptions?: CryptoOptions;
6 | verbose?: boolean;
7 | exclude?: string[];
8 | strict?: boolean;
9 | }
10 |
--------------------------------------------------------------------------------
/src/interfaces/manifestInfo.ts:
--------------------------------------------------------------------------------
1 | import detectIndent from 'detect-indent';
2 |
3 | /** @internal */
4 | export interface ManifestInfo {
5 | indentation: detectIndent.Indent;
6 | manifest: Record ;
7 | }
8 |
--------------------------------------------------------------------------------
/src/interfaces/normalizedCryptoOptions.ts:
--------------------------------------------------------------------------------
1 | import { BinaryToTextEncoding } from 'crypto';
2 |
3 | /** @internal */
4 | export interface NormalizedCryptoOptions {
5 | dirAlgorithm: string;
6 | encoding: BinaryToTextEncoding;
7 | fileAlgorithm: string;
8 | }
9 |
--------------------------------------------------------------------------------
/src/interfaces/normalizedIntegrityOptions.ts:
--------------------------------------------------------------------------------
1 | import { CryptoOptions } from './cryptoOptions';
2 |
3 | /** @internal */
4 | export interface NormalizedIntegrityOptions {
5 | cryptoOptions: CryptoOptions;
6 | verbose: boolean;
7 | strict: boolean;
8 | exclude: string[];
9 | include: string[];
10 | }
11 |
--------------------------------------------------------------------------------
/src/interfaces/parsedArgs.ts:
--------------------------------------------------------------------------------
1 | /** @internal */
2 | export interface ParsedArgs {
3 | command: string;
4 | dirAlgorithm: string;
5 | encoding: string;
6 | exclude: string[];
7 | fileAlgorithm: string;
8 | inPath: string;
9 | integrity: string;
10 | manifest: boolean;
11 | outPath: string;
12 | pretty: boolean;
13 | strict: boolean;
14 | verbose: boolean;
15 | }
16 |
--------------------------------------------------------------------------------
/src/interfaces/spinner.ts:
--------------------------------------------------------------------------------
1 | /** @internal */
2 | export interface Spinner {
3 | timer: NodeJS.Timer;
4 | line: number;
5 | }
6 |
--------------------------------------------------------------------------------
/src/interfaces/verboseHashObject.ts:
--------------------------------------------------------------------------------
1 | import { HashObject } from './hashObject';
2 |
3 | /** @public */
4 | export interface VerboseHashObject {
5 | contents: HashObject;
6 | hash: string;
7 | }
8 |
--------------------------------------------------------------------------------
/test/api/behavior.test.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | import { expect } from 'chai';
3 | import path from 'path';
4 | import { Integrity } from '../../src/app/integrity';
5 |
6 | describe('Integrity: behavior tests', (): void => {
7 |
8 | context('expects', (): void => {
9 |
10 | let anotherFileToHashFilename: string;
11 | let fileToHashFilename: string;
12 | let directoryDirPath: string;
13 | let directoryOneDirPath: string;
14 | let fixturesDirPath: string;
15 | let anotherFileToHashFilePath: string;
16 | let fileToHashFilePath: string;
17 |
18 | before((): void => {
19 | anotherFileToHashFilename = 'anotherFileToHash.txt';
20 | fileToHashFilename = 'fileToHash.txt';
21 | });
22 |
23 | beforeEach((): void => {
24 | fixturesDirPath = path.resolve(__dirname, '../../../test/fixtures');
25 | directoryDirPath = path.resolve(fixturesDirPath, 'directory');
26 | directoryOneDirPath = path.resolve(fixturesDirPath, 'directory.1');
27 | anotherFileToHashFilePath = path.resolve(directoryDirPath, anotherFileToHashFilename);
28 | fileToHashFilePath = path.resolve(fixturesDirPath, fileToHashFilename);
29 | });
30 |
31 | context('to pass integrity check when', (): void => {
32 |
33 | it('files have the same name and the same content',
34 | async (): Promise => {
35 | const sutFilePath = path.resolve(fixturesDirPath, './fixtures/directory/anotherFileToHash.txt');
36 | const hash = await Integrity.create(sutFilePath);
37 | const sut = await Integrity.check(anotherFileToHashFilePath, JSON.stringify(hash));
38 | expect(hash.hashes).to.haveOwnProperty(anotherFileToHashFilename);
39 | expect(sut).to.be.a('boolean').and.to.be.true;
40 | });
41 |
42 | it('directories have the same name and the same content',
43 | async (): Promise => {
44 | const sutDirPath = path.resolve(fixturesDirPath, './fixtures/directory.1');
45 | const hash = await Integrity.create(sutDirPath);
46 | const sut = await Integrity.check(directoryOneDirPath, JSON.stringify(hash));
47 | expect(hash.hashes).to.haveOwnProperty('.');
48 | expect(sut).to.be.a('boolean').and.to.be.true;
49 | });
50 |
51 | it('directories have the same content but different names',
52 | async (): Promise => {
53 | const sutDirPath = path.resolve(fixturesDirPath, './directory.1');
54 | const hash = await Integrity.create(sutDirPath, { strict: false });
55 | const sut = await Integrity.check(directoryDirPath, JSON.stringify(hash));
56 | expect(hash.hashes).to.haveOwnProperty('.');
57 | expect(sut).to.be.a('boolean').and.to.be.true;
58 | });
59 |
60 | });
61 |
62 | context('to fail integrity check when', (): void => {
63 |
64 | it('files have the same content but different names',
65 | async (): Promise => {
66 | const sutFilePath = path.resolve(fixturesDirPath, './sameContentWithFileToHash.txt');
67 | const hash = await Integrity.create(sutFilePath);
68 | const sut = await Integrity.check(fileToHashFilePath, JSON.stringify(hash));
69 | expect(hash.hashes).to.haveOwnProperty('sameContentWithFileToHash.txt');
70 | expect(sut).to.be.a('boolean').and.to.be.false;
71 | });
72 |
73 | it('files have the same name but different content',
74 | async (): Promise => {
75 | const sutFilePath = path.resolve(fixturesDirPath, './fixtures/fileToHash.txt');
76 | const hash = await Integrity.create(sutFilePath);
77 | const sut = await Integrity.check(fileToHashFilePath, JSON.stringify(hash));
78 | expect(hash.hashes).to.haveOwnProperty(fileToHashFilename);
79 | expect(sut).to.be.a('boolean').and.to.be.false;
80 | });
81 |
82 | it('directories have the same name but different content',
83 | async (): Promise => {
84 | const sutDirPath = path.resolve(fixturesDirPath, './fixtures/directory');
85 | const hash = await Integrity.create(sutDirPath);
86 | const sut = await Integrity.check(directoryDirPath, JSON.stringify(hash));
87 | expect(hash.hashes).to.haveOwnProperty('.');
88 | expect(sut).to.be.a('boolean').and.to.be.false;
89 | });
90 |
91 | it(`directories have the same content but different names in 'strict' mode`,
92 | async (): Promise => {
93 | const sutDirPath = path.resolve(fixturesDirPath, './directory.1');
94 | const hash = await Integrity.create(sutDirPath, { strict: true });
95 | const sut = await Integrity.check(directoryDirPath, JSON.stringify(hash));
96 | expect(hash.hashes).to.haveOwnProperty('directory.1');
97 | expect(sut).to.be.a('boolean').and.to.be.false;
98 | });
99 |
100 | });
101 |
102 | });
103 |
104 | });
105 |
--------------------------------------------------------------------------------
/test/api/create.test.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | import Ajv from 'ajv';
3 | import { expect } from 'chai';
4 | import fs from 'fs';
5 | import path from 'path';
6 | import sinon, { createSandbox } from 'sinon';
7 | import { Integrity } from '../../src/app/integrity';
8 | import * as fsAsync from '../../src/common/fsAsync';
9 | import { IntegrityOptions } from '../../src/interfaces/integrityOptions';
10 | import * as schema from '../../src/app/schemas/v1/schema.json';
11 |
12 | describe(`Integrity: function 'create' tests`, (): void => {
13 |
14 | context('expects', (): void => {
15 |
16 | let fileToHashFilename: string;
17 | let fixturesDirPath: string;
18 | let fileToHashFilePath: string;
19 | let schemaValidator: Ajv;
20 | let sandbox: sinon.SinonSandbox;
21 | let fsStatsMock: sinon.SinonStubbedInstance;
22 |
23 | before((): void => {
24 | schemaValidator = new Ajv({ allowUnionTypes: true });
25 | fileToHashFilename = 'fileToHash.txt';
26 | });
27 |
28 | let options: IntegrityOptions;
29 |
30 | beforeEach((): void => {
31 | sandbox = createSandbox();
32 | fsStatsMock = sandbox.createStubInstance(fs.Stats);
33 | fixturesDirPath = path.resolve(__dirname, '../../../test/fixtures');
34 | fileToHashFilePath = path.resolve(fixturesDirPath, fileToHashFilename);
35 | options = {
36 | cryptoOptions: undefined,
37 | exclude: undefined,
38 | verbose: undefined,
39 | };
40 | });
41 |
42 | afterEach((): void => {
43 | sandbox.restore();
44 | });
45 |
46 | it('to return an empty object when path is not a file or directory',
47 | async (): Promise => {
48 | fsStatsMock.isDirectory.returns(false);
49 | fsStatsMock.isFile.returns(false);
50 | const lstatStub = sandbox.stub(fsAsync, 'lstatAsync')
51 | .resolves(fsStatsMock);
52 | const sut = await Integrity.create(fixturesDirPath);
53 | lstatStub.restore();
54 | expect(lstatStub.calledOnce).to.be.true;
55 | expect(sut).to.be.an('object');
56 | expect(sut).to.haveOwnProperty('hashes').that.is.empty;
57 | expect(sut).to.haveOwnProperty('version').that.matches(/\d/);
58 | });
59 |
60 | context('to produce a valid schema when hashing', (): void => {
61 |
62 | it('a directory non-verbosely',
63 | async (): Promise => {
64 | options.verbose = false;
65 | const sut = await Integrity.create(fixturesDirPath, options);
66 | expect(schemaValidator.validate(schema, sut)).to.be.true;
67 | expect(schemaValidator.errors).to.be.null;
68 | });
69 |
70 | it('a directory verbosely',
71 | async (): Promise => {
72 | const sut = await Integrity.create(fixturesDirPath);
73 | expect(schemaValidator.validate(schema, sut)).to.be.true;
74 | expect(schemaValidator.errors).to.be.null;
75 | });
76 |
77 | it('a file',
78 | async (): Promise => {
79 | const sut = await Integrity.create(fileToHashFilePath);
80 | expect(schemaValidator.validate(schema, sut)).to.be.true;
81 | expect(schemaValidator.errors).to.be.null;
82 | });
83 |
84 | });
85 |
86 | });
87 |
88 | });
89 |
--------------------------------------------------------------------------------
/test/api/createFileHash.test.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | import { expect } from 'chai';
3 | import { BinaryToTextEncoding } from 'crypto';
4 | import fs, { ReadStream } from 'fs';
5 | import path from 'path';
6 | import sinon, { createSandbox } from 'sinon';
7 | import { Integrity } from '../../src/app/integrity';
8 | import * as utils from '../../src/common/utils';
9 | import { CryptoOptions } from '../../src/interfaces/cryptoOptions';
10 | import { checker } from '../helper';
11 |
12 | describe(`Integrity: function 'createFileHash' tests`, (): void => {
13 |
14 | context('expects', (): void => {
15 |
16 | let sandbox: sinon.SinonSandbox;
17 | let fileToHashFilename: string;
18 | let integrityTestFilename: string;
19 | let fixturesDirPath: string;
20 | let fileToHashFilePath: string;
21 | let integrityTestFilePath: string;
22 | let md5Length: number;
23 | let sha1Length: number;
24 |
25 | before((): void => {
26 | fileToHashFilename = 'fileToHash.txt';
27 | integrityTestFilename = '.integrity.json';
28 |
29 | md5Length = 32;
30 | sha1Length = 40;
31 | });
32 |
33 | beforeEach((): void => {
34 | sandbox = createSandbox();
35 | fixturesDirPath = path.resolve(__dirname, '../../../test/fixtures');
36 | fileToHashFilePath = path.resolve(fixturesDirPath, fileToHashFilename);
37 | integrityTestFilePath = path.resolve(fixturesDirPath, integrityTestFilename);
38 | });
39 |
40 | afterEach((): void => {
41 | sandbox.restore();
42 | });
43 |
44 | context('to throw an Error when', (): void => {
45 |
46 | it('the provided algorithm is not supported',
47 | async (): Promise => {
48 | const cryptoOptions = { fileAlgorithm: 'md1' };
49 | try {
50 | await Integrity.createFileHash(fileToHashFilePath, cryptoOptions);
51 | } catch (error) {
52 | expect(error).to.be.an.instanceof(Error).that.matches(/ENOSUP:/);
53 | }
54 | });
55 |
56 | it('the provided encoding is not supported',
57 | async (): Promise => {
58 | const cryptoOptions: CryptoOptions = { encoding: 'ascii' as BinaryToTextEncoding };
59 | try {
60 | await Integrity.createFileHash(fileToHashFilePath, cryptoOptions);
61 | } catch (error) {
62 | expect(error).to.be.an.instanceof(Error).that.matches(/ENOSUP:/);
63 | }
64 | });
65 |
66 | it('the provided path is not a file',
67 | async (): Promise => {
68 | try {
69 | await Integrity.createFileHash(fixturesDirPath);
70 | } catch (error) {
71 | expect(error).to.be.an.instanceof(Error).that.matches(/ENOTFILE:/);
72 | }
73 | });
74 |
75 | it('the provided path is not allowed',
76 | async (): Promise => {
77 | try {
78 | await Integrity.createFileHash(integrityTestFilePath);
79 | } catch (error) {
80 | expect(error).to.be.an.instanceof(Error).that.matches(/ENOTALW:/);
81 | }
82 | });
83 |
84 | it('the file can not be read',
85 | async (): Promise => {
86 | sandbox.stub(fs, 'createReadStream').returns({
87 | pipe: sandbox.stub().returnsThis(),
88 | on: sandbox.stub().callsFake((_, cb: (err: Error) => void) =>
89 | cb(new Error('Failed reading file'))),
90 | } as unknown as ReadStream);
91 | try {
92 | await Integrity.createFileHash(fileToHashFilePath);
93 | } catch (error) {
94 | expect(error).to.be.an.instanceof(Error).and.match(/Failed reading file/);
95 | }
96 | });
97 |
98 | });
99 |
100 | it(`to return by default an 'sha1' and 'base64' encoded hash string`,
101 | async (): Promise => {
102 | const sut = await Integrity.createFileHash(fileToHashFilePath);
103 | expect(sut).to.be.an('object')
104 | .and.to.haveOwnProperty(fileToHashFilename)
105 | .and.to.satisfy((hash: string): boolean =>
106 | checker(hash, utils.base64RegexPattern, 'H58mYNjbMJTkiNvvNfj2YKl3ck0='));
107 | });
108 |
109 | it(`to return a 'sha1' and 'hex' encoded hash string`,
110 | async (): Promise => {
111 | const sut = await Integrity.createFileHash(fileToHashFilePath, { encoding: 'hex' });
112 | expect(sut).to.be.an('object')
113 | .and.to.haveOwnProperty(fileToHashFilename)
114 | .and.to.satisfy((hash: string): boolean =>
115 | checker(hash, utils.hexRegexPattern,
116 | '1f9f2660d8db3094e488dbef35f8f660a977724d',
117 | 'sha1', sha1Length));
118 | });
119 |
120 | it(`to return a 'sha1' and 'base64url' encoded hash string`,
121 | async (): Promise => {
122 | const sut = await Integrity.createFileHash(fileToHashFilePath, { encoding: 'base64url' });
123 | expect(sut).to.be.an('object')
124 | .and.to.haveOwnProperty(fileToHashFilename)
125 | .and.to.satisfy((hash: string): boolean =>
126 | checker(hash, utils.base64urlRegexPattern,
127 | 'H58mYNjbMJTkiNvvNfj2YKl3ck0',
128 | 'sha1'));
129 | });
130 |
131 | it(`to return an 'md5' and 'base64' encoded hash string`,
132 | async (): Promise => {
133 | const sut = await Integrity.createFileHash(fileToHashFilePath, { fileAlgorithm: 'md5' });
134 | expect(sut).to.be.an('object')
135 | .and.to.haveOwnProperty(fileToHashFilename)
136 | .and.to.satisfy((hash: string): boolean =>
137 | checker(hash, utils.base64RegexPattern, 'ej1bR1vQeukEH6sqEz9AxA==', 'md5'));
138 | });
139 |
140 | it(`to return an 'md5' and 'hex' encoded hash string`,
141 | async (): Promise => {
142 | const sut = await Integrity.createFileHash(
143 | fileToHashFilePath,
144 | { fileAlgorithm: 'md5', encoding: 'hex' });
145 | expect(sut).to.be.an('object')
146 | .and.to.haveOwnProperty(fileToHashFilename)
147 | .and.to.satisfy((hash: string): boolean =>
148 | checker(hash, utils.base64RegexPattern, '7a3d5b475bd07ae9041fab2a133f40c4', 'md5', md5Length));
149 | });
150 |
151 | it(`to support relative paths`,
152 | async (): Promise => {
153 | const sut = await Integrity.createFileHash('test/fixtures/fileToHash.txt');
154 |
155 | expect(sut).to.be.an('object')
156 | .and.to.haveOwnProperty(fileToHashFilename)
157 | .and.to.satisfy((hash: string): boolean =>
158 | checker(hash, utils.base64RegexPattern, 'H58mYNjbMJTkiNvvNfj2YKl3ck0='));
159 | });
160 |
161 | });
162 |
163 | });
164 |
--------------------------------------------------------------------------------
/test/api/createFilesHash.test.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | import { expect } from 'chai';
3 | import { BinaryToTextEncoding } from 'crypto';
4 | import path from 'path';
5 | import { Integrity } from '../../src/app/integrity';
6 | import * as utils from '../../src/common/utils';
7 | import { CryptoOptions } from '../../src/interfaces/cryptoOptions';
8 | import { checker } from '../helper';
9 |
10 | describe(`Integrity: function 'createFilesHash' tests`, (): void => {
11 |
12 | context('expects', (): void => {
13 |
14 | let anotherFileToHashFilename: string;
15 | let otherFileToHashFilename: string;
16 | let fileToHashFilename: string;
17 | let directoryDirPath: string;
18 | let fixturesDirPath: string;
19 | let anotherFileToHashFilePath: string;
20 | let otherFileToHashFilePath: string;
21 | let fileToHashFilePath: string;
22 | let md5Length: number;
23 | let sha1Length: number;
24 |
25 | before((): void => {
26 | anotherFileToHashFilename = 'anotherFileToHash.txt';
27 | otherFileToHashFilename = 'otherFileToHash.txt';
28 | fileToHashFilename = 'fileToHash.txt';
29 |
30 | md5Length = 32;
31 | sha1Length = 40;
32 | });
33 |
34 | beforeEach((): void => {
35 | fixturesDirPath = path.resolve(__dirname, '../../../test/fixtures');
36 | directoryDirPath = path.resolve(fixturesDirPath, 'directory');
37 | anotherFileToHashFilePath = path.resolve(directoryDirPath, anotherFileToHashFilename);
38 | otherFileToHashFilePath = path.resolve(directoryDirPath, otherFileToHashFilename);
39 | fileToHashFilePath = path.resolve(fixturesDirPath, fileToHashFilename);
40 | });
41 |
42 | context('to throw an Error when', (): void => {
43 |
44 | it('the provided algorithm is not supported',
45 | async (): Promise => {
46 | const cryptoOptions = { fileAlgorithm: 'md1' };
47 | try {
48 | await Integrity.createFilesHash([fileToHashFilePath], cryptoOptions);
49 | } catch (error) {
50 | expect(error).to.be.an.instanceof(Error).that.matches(/ENOSUP:/);
51 | }
52 | });
53 |
54 | it('the provided encoding is not supported',
55 | async (): Promise => {
56 | const cryptoOptions: CryptoOptions = { encoding: 'ascii' as BinaryToTextEncoding };
57 | try {
58 | await Integrity.createFilesHash([fileToHashFilePath], cryptoOptions);
59 | } catch (error) {
60 | expect(error).to.be.an.instanceof(Error).that.matches(/ENOSUP:/);
61 | }
62 | });
63 |
64 | });
65 |
66 | it(`to return by default a 'sha1' and 'base64' encoded hash JSON`,
67 | async (): Promise => {
68 | const files = [anotherFileToHashFilePath, otherFileToHashFilePath];
69 | const sut = await Integrity.createFilesHash(files);
70 | expect(sut).to.be.an('object');
71 | expect(sut).to.haveOwnProperty(anotherFileToHashFilename)
72 | .and.to.satisfy((hash: string): boolean =>
73 | checker(hash, utils.base64RegexPattern, 'EZ2w0rsSmXBOddIoz2IoOIuxGaQ='));
74 | expect(sut).to.haveOwnProperty(otherFileToHashFilename)
75 | .and.to.satisfy((hash: string): boolean =>
76 | checker(hash, utils.base64RegexPattern, 'B8FJ4uKgHESSgMvJUyrj3ix2uG8='));
77 | });
78 |
79 | it(`to return a 'sha1' and 'hex' encoded hash JSON`,
80 | async (): Promise => {
81 | const files = [anotherFileToHashFilePath, otherFileToHashFilePath];
82 | const sut = await Integrity.createFilesHash(files, { encoding: 'hex' });
83 | expect(sut).to.be.an('object');
84 | expect(sut).to.haveOwnProperty(anotherFileToHashFilename)
85 | .and.to.satisfy((hash: string): boolean =>
86 | checker(hash, utils.hexRegexPattern,
87 | '119db0d2bb1299704e75d228cf6228388bb119a4',
88 | 'sha1', sha1Length));
89 | expect(sut).to.haveOwnProperty(otherFileToHashFilename)
90 | .and.to.satisfy((hash: string): boolean =>
91 | checker(hash, utils.hexRegexPattern,
92 | '07c149e2e2a01c449280cbc9532ae3de2c76b86f',
93 | 'sha1', sha1Length));
94 | });
95 |
96 | it(`to return a 'sha1' and 'base64url' encoded hash JSON`,
97 | async (): Promise => {
98 | const files = [anotherFileToHashFilePath, otherFileToHashFilePath];
99 | const sut = await Integrity.createFilesHash(files, { encoding: 'base64url' });
100 | expect(sut).to.be.an('object');
101 | expect(sut).to.haveOwnProperty(anotherFileToHashFilename)
102 | .and.to.satisfy((hash: string): boolean =>
103 | checker(hash, utils.base64urlRegexPattern,
104 | 'EZ2w0rsSmXBOddIoz2IoOIuxGaQ',
105 | 'sha1'));
106 | expect(sut).to.haveOwnProperty(otherFileToHashFilename)
107 | .and.to.satisfy((hash: string): boolean =>
108 | checker(hash, utils.base64urlRegexPattern,
109 | 'B8FJ4uKgHESSgMvJUyrj3ix2uG8',
110 | 'sha1'));
111 | });
112 |
113 | it(`to return an 'md5' and 'base64' encoded hash JSON`,
114 | async (): Promise => {
115 | const files = [anotherFileToHashFilePath, otherFileToHashFilePath];
116 | const sut = await Integrity.createFilesHash(files, { fileAlgorithm: 'md5' });
117 | expect(sut).to.be.an('object');
118 | expect(sut).to.haveOwnProperty(anotherFileToHashFilename)
119 | .and.to.satisfy((hash: string): boolean =>
120 | checker(hash, utils.base64RegexPattern, '6FwJAV4629O2chl9ZbDgEQ==', 'md5'));
121 | expect(sut).to.haveOwnProperty(otherFileToHashFilename)
122 | .and.to.satisfy((hash: string): boolean =>
123 | checker(hash, utils.base64RegexPattern, 'qrJbDxeJ/oiVXO5tI3Dntw==', 'md5'));
124 | });
125 |
126 | it(`to return an 'md5' and 'hex' encoded hash JSON`,
127 | async (): Promise => {
128 | const files = [anotherFileToHashFilePath, otherFileToHashFilePath];
129 | const sut = await Integrity.createFilesHash(files, { fileAlgorithm: 'md5', encoding: 'hex' });
130 | expect(sut).to.be.an('object');
131 | expect(sut).to.haveOwnProperty(anotherFileToHashFilename)
132 | .and.to.satisfy((hash: string): boolean =>
133 | checker(hash, utils.hexRegexPattern, 'e85c09015e3adbd3b672197d65b0e011', 'md5', md5Length));
134 | expect(sut).to.haveOwnProperty(otherFileToHashFilename)
135 | .and.to.satisfy((hash: string): boolean =>
136 | checker(hash, utils.hexRegexPattern, 'aab25b0f1789fe88955cee6d2370e7b7', 'md5', md5Length));
137 | });
138 |
139 | });
140 |
141 | });
142 |
--------------------------------------------------------------------------------
/test/api/getExclusionsFromIgnoreFile.test.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | import { expect } from 'chai';
3 | import { resolve } from 'path';
4 | import sinon, { createSandbox } from 'sinon';
5 | import { Integrity } from '../../src/app/integrity';
6 |
7 | describe(`Integrity: function 'getExclusionsFromIgnoreFile' tests`, (): void => {
8 |
9 | context('expects', (): void => {
10 |
11 | let sandbox: sinon.SinonSandbox;
12 | let baseIgnoreFilePath: string;
13 |
14 | beforeEach((): void => {
15 | sandbox = createSandbox();
16 | baseIgnoreFilePath = resolve(__dirname, '../../../test/ignoreFile');
17 | });
18 |
19 | afterEach((): void => {
20 | sandbox.restore();
21 | });
22 |
23 | it('to return an empty array, when failing to find the ignore file',
24 | async (): Promise => {
25 | const exclusions = await Integrity.getExclusionsFromIgnoreFile();
26 | expect(exclusions).to.be.an('array').and.to.be.empty;
27 | });
28 |
29 | it('to return an array of exclution entries',
30 | async (): Promise => {
31 | const expectedEntries = ['*', '*/', '!dist'];
32 | const exclusions = await Integrity.getExclusionsFromIgnoreFile(baseIgnoreFilePath);
33 | expect(exclusions).to.be.an('array').and.to.eql(expectedEntries);
34 | });
35 |
36 | });
37 |
38 | });
39 |
--------------------------------------------------------------------------------
/test/api/getIntegrityOptionsFromConfig.test.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | import { expect } from 'chai';
3 | import sinon, { createSandbox } from 'sinon';
4 | import { Integrity } from '../../src/app/integrity';
5 | import { ConfigExplorer } from '../../src/common/configExplorer';
6 | import { ConfigOptions } from '../../src/interfaces/configOptions';
7 | import { IntegrityOptions } from '../../src/interfaces/integrityOptions';
8 |
9 | describe(`Integrity: function 'getIntegrityOptionsFromConfig' tests`, (): void => {
10 |
11 | context('expects', (): void => {
12 |
13 | let sandbox: sinon.SinonSandbox;
14 | let getConfigStub: sinon.SinonStub<[(string | undefined)?], Promise>;
15 |
16 | beforeEach((): void => {
17 | sandbox = createSandbox();
18 | getConfigStub = sandbox.stub(ConfigExplorer.prototype, 'getConfig');
19 | });
20 |
21 | afterEach((): void => {
22 | sandbox.restore();
23 | });
24 |
25 | const expected = (options: ConfigOptions): IntegrityOptions => (
26 | !Object.keys(options).length
27 | ? options
28 | : {
29 | cryptoOptions: options.cryptoOptions,
30 | exclude: options.exclude,
31 | verbose: options.verbose,
32 | strict: options.strict,
33 | }
34 | );
35 |
36 | let rc: ConfigOptions = {
37 | cryptoOptions: {
38 | dirAlgorithm: 'mr',
39 | encoding: 'hex',
40 | fileAlgorithm: 'rm',
41 | },
42 | exclude: ['dir', 'file'],
43 | source: '.',
44 | verbose: true,
45 | strict: true,
46 | };
47 |
48 | it('to return an empty object, when failing to find a configuration',
49 | async (): Promise => {
50 | rc = {};
51 | getConfigStub.resolves(rc);
52 | const config = await Integrity.getIntegrityOptionsFromConfig();
53 | expect(config).to.eql(expected(rc));
54 | });
55 |
56 | context('to return the integrity options', (): void => {
57 |
58 | it('with all options',
59 | async (): Promise => {
60 | getConfigStub.resolves(rc);
61 | const config = await Integrity.getIntegrityOptionsFromConfig();
62 | expect(config).to.eql(expected(rc));
63 | });
64 |
65 | it('without `cryptoOptions`',
66 | async (): Promise => {
67 | rc.cryptoOptions = undefined;
68 | getConfigStub.resolves(rc);
69 | const config = await Integrity.getIntegrityOptionsFromConfig();
70 | expect(config).to.eql(expected(rc));
71 | });
72 |
73 | it('with `dirAlgorithm` `undefined`',
74 | async (): Promise => {
75 | rc.cryptoOptions = {
76 | dirAlgorithm: undefined,
77 | encoding: 'hex',
78 | fileAlgorithm: 'rm',
79 | };
80 | getConfigStub.resolves(rc);
81 | const config = await Integrity.getIntegrityOptionsFromConfig();
82 | expect(config).to.eql(expected(rc));
83 | });
84 |
85 | it('with `encoding` `undefined`',
86 | async (): Promise => {
87 | rc.cryptoOptions = {
88 | dirAlgorithm: 'mr',
89 | encoding: undefined,
90 | fileAlgorithm: 'rm',
91 | };
92 | getConfigStub.resolves(rc);
93 | const config = await Integrity.getIntegrityOptionsFromConfig();
94 | expect(config).to.eql(expected(rc));
95 | });
96 |
97 | it('with `fileAlgorithm` `undefined`',
98 | async (): Promise => {
99 | rc.cryptoOptions = {
100 | dirAlgorithm: 'mr',
101 | encoding: 'hex',
102 | fileAlgorithm: undefined,
103 | };
104 | getConfigStub.resolves(rc);
105 | const config = await Integrity.getIntegrityOptionsFromConfig();
106 | expect(config).to.eql(expected(rc));
107 | });
108 |
109 | });
110 |
111 | });
112 |
113 | });
114 |
--------------------------------------------------------------------------------
/test/api/getManifestIntegrity.test.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | import { expect } from 'chai';
3 | import { ObjectEncodingOptions, PathLike } from 'fs';
4 | import sinon, { createSandbox } from 'sinon';
5 | import { Integrity } from '../../src/app/integrity';
6 | import * as fsAsync from '../../src/common/fsAsync';
7 |
8 | describe(`Integrity: function 'getManifestIntegrity' tests`, (): void => {
9 |
10 | context('expects', (): void => {
11 |
12 | type ReadFileType = [PathLike | number,
13 | (ObjectEncodingOptions & { flag?: string | undefined } | BufferEncoding | null)?];
14 |
15 | let sandbox: sinon.SinonSandbox;
16 | let existsAsyncStub: sinon.SinonStub<[PathLike], Promise>;
17 | let readFileAsyncStub: sinon.SinonStub>;
18 |
19 | beforeEach((): void => {
20 | sandbox = createSandbox();
21 | existsAsyncStub = sandbox.stub(fsAsync, 'existsAsync');
22 | readFileAsyncStub = sandbox.stub(fsAsync, 'readFileAsync');
23 | });
24 |
25 | afterEach((): void => {
26 | sandbox.restore();
27 | });
28 |
29 | context(`to return an 'Error' when`, (): void => {
30 |
31 | it('the manifest file is not found',
32 | async (): Promise => {
33 | try {
34 | await Integrity.getManifestIntegrity('../');
35 | } catch (error) {
36 | expect(existsAsyncStub.calledOnce).to.be.true;
37 | expect(error).to.match(/Error: 'package\.json' not found/);
38 | }
39 | });
40 |
41 | it(`the manifest is NOT valid`,
42 | async (): Promise => {
43 | existsAsyncStub.resolves(true);
44 | readFileAsyncStub.resolves('');
45 | try {
46 | await Integrity.getManifestIntegrity();
47 | } catch (error) {
48 | expect(existsAsyncStub.calledOnce).to.be.true;
49 | expect(readFileAsyncStub.calledOnce).to.be.true;
50 | expect(error).to.match(/Error: Manifest not found/);
51 | }
52 | });
53 |
54 | it(`the manifest is an empty JSON`,
55 | async (): Promise => {
56 | existsAsyncStub.resolves(true);
57 | readFileAsyncStub.resolves('{\n }');
58 | try {
59 | await Integrity.getManifestIntegrity();
60 | } catch (error) {
61 | expect(existsAsyncStub.calledOnce).to.be.true;
62 | expect(readFileAsyncStub.calledOnce).to.be.true;
63 | expect(error).to.match(/Error: Manifest not found/);
64 | }
65 | });
66 |
67 | });
68 |
69 | context('to get the manifest integrity object', (): void => {
70 | let getManifestStub: sinon.SinonStub;
71 |
72 | beforeEach((): void => {
73 | // @ts-ignore
74 | getManifestStub = sandbox.stub(Integrity, 'getManifestInfo');
75 | });
76 |
77 | it(`when it's found`,
78 | async (): Promise => {
79 | getManifestStub.restore();
80 | existsAsyncStub.resolves(true);
81 | readFileAsyncStub.resolves('{\n "integrity": {}\n}');
82 | const sut = await Integrity.getManifestIntegrity();
83 | expect(existsAsyncStub.calledOnce).to.be.true;
84 | expect(readFileAsyncStub.calledOnce).to.be.true;
85 | expect(sut).to.be.equal('{}');
86 | });
87 |
88 | it('using the indentation indent',
89 | async (): Promise => {
90 | getManifestStub.resolves({ manifest: { integrity: { hash: '' } }, indentation: { indent: ' ' } });
91 | const sut = await Integrity.getManifestIntegrity();
92 | getManifestStub.restore();
93 | expect(getManifestStub.calledOnce).to.be.true;
94 | expect(sut).to.equal('{\n "hash": ""\n}');
95 | });
96 |
97 | it('using the indentation amount',
98 | async (): Promise => {
99 | // @ts-ignore
100 | getManifestStub.resolves({ manifest: { integrity: { hash: '' } }, indentation: { amount: 2 } });
101 | const sut = await Integrity.getManifestIntegrity();
102 | getManifestStub.restore();
103 | expect(getManifestStub.calledOnce).to.be.true;
104 | expect(sut).to.equal('{\n "hash": ""\n}');
105 | });
106 |
107 | });
108 |
109 | });
110 |
111 | });
112 |
--------------------------------------------------------------------------------
/test/api/persist.test.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | import { expect } from 'chai';
3 | import { PathLike, WriteFileOptions } from 'fs';
4 | import path from 'path';
5 | import sinon, { createSandbox } from 'sinon';
6 | import { Integrity } from '../../src/app/integrity';
7 | import * as fsAsync from '../../src/common/fsAsync';
8 | import { IntegrityObject } from '../../src/interfaces/integrityObject';
9 |
10 | describe(`Integrity: function 'persist' tests`, (): void => {
11 |
12 | context('expects', (): void => {
13 |
14 | type WriteFileType = [PathLike | number, string | NodeJS.ArrayBufferView, (WriteFileOptions)?];
15 |
16 | let sandbox: sinon.SinonSandbox;
17 | let writeFileAsyncStub: sinon.SinonStub>;
18 | let integrityTestFilename: string;
19 | let integrityTestObject: IntegrityObject;
20 | let fixturesDirPath: string;
21 |
22 | before((): void => {
23 | integrityTestFilename = '.integrity.json';
24 | });
25 |
26 | beforeEach((): void => {
27 | sandbox = createSandbox();
28 | writeFileAsyncStub = sandbox.stub(fsAsync, 'writeFileAsync');
29 | fixturesDirPath = path.resolve(__dirname, '../../../test/fixtures');
30 | integrityTestObject = { hashes: {}, version: '' };
31 | });
32 |
33 | afterEach((): void => {
34 | sandbox.restore();
35 | });
36 |
37 | context('to persist the created hash file', (): void => {
38 |
39 | it('on the provided path',
40 | async (): Promise => {
41 | const dirPath = path.resolve(fixturesDirPath, integrityTestFilename);
42 | const data = '{\n "hashes": {},\n "version": ""\n}';
43 | await Integrity.persist(integrityTestObject, fixturesDirPath, true);
44 | expect(writeFileAsyncStub.calledOnceWithExactly(dirPath, data)).to.be.true;
45 | });
46 |
47 | it('on the default path',
48 | async (): Promise => {
49 | const dirPath = path.resolve('./', integrityTestFilename);
50 | const data = '{\n "hashes": {},\n "version": ""\n}';
51 | await Integrity.persist(integrityTestObject, undefined, true);
52 | expect(writeFileAsyncStub.calledOnceWithExactly(dirPath, data)).to.be.true;
53 | });
54 |
55 | it('without prettifying the integrity object',
56 | async (): Promise => {
57 | const dirPath = path.resolve(fixturesDirPath, integrityTestFilename);
58 | const data = '{"hashes":{},"version":""}';
59 | await Integrity.persist(integrityTestObject, fixturesDirPath);
60 | expect(writeFileAsyncStub.calledOnceWithExactly(dirPath, data)).to.be.true;
61 | });
62 | });
63 |
64 | });
65 |
66 | });
67 |
--------------------------------------------------------------------------------
/test/api/updateManifestIntegrity.test.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | import { expect } from 'chai';
3 | import { PathLike, WriteFileOptions } from 'fs';
4 | import sinon, { createSandbox, match } from 'sinon';
5 | import { Integrity } from '../../src/app/integrity';
6 | import * as fsAsync from '../../src/common/fsAsync';
7 | import { IntegrityObject } from '../../src/interfaces/integrityObject';
8 |
9 | describe(`Integrity: function 'updateManifestIntegrity' tests`, (): void => {
10 |
11 | context('expects', (): void => {
12 |
13 | type WriteFileType = [PathLike | number, string | NodeJS.ArrayBufferView, (WriteFileOptions)?];
14 |
15 | context('to update the manifest with the integrity object', (): void => {
16 |
17 | let sandbox: sinon.SinonSandbox;
18 | let writeFileAsyncStub: sinon.SinonStub>;
19 | let getManifestStub: sinon.SinonStub;
20 | let integrityTestObject: IntegrityObject;
21 |
22 | beforeEach((): void => {
23 | sandbox = createSandbox();
24 | writeFileAsyncStub = sandbox.stub(fsAsync, 'writeFileAsync');
25 | // @ts-ignore
26 | getManifestStub = sandbox.stub(Integrity, 'getManifestInfo');
27 | integrityTestObject = { hashes: {}, version: '' };
28 | });
29 |
30 | afterEach((): void => {
31 | sandbox.restore();
32 | });
33 |
34 | it('using the indentation indent',
35 | async (): Promise => {
36 | getManifestStub.resolves({ manifest: {}, indentation: { indent: ' ' } });
37 | await Integrity.updateManifestIntegrity(integrityTestObject);
38 | expect(getManifestStub.calledOnce).to.be.true;
39 | expect(writeFileAsyncStub.calledOnceWithExactly(match(/package\.json/),
40 | '{\n "integrity": {\n "hashes": {},\n "version": ""\n }\n}'))
41 | .to.be.true;
42 | });
43 |
44 | it('using the indentation amount',
45 | async (): Promise => {
46 | getManifestStub.resolves({ manifest: {}, indentation: { amount: 2 } });
47 | await Integrity.updateManifestIntegrity(integrityTestObject);
48 | expect(getManifestStub.calledOnce).to.be.true;
49 | expect(writeFileAsyncStub.calledOnceWithExactly(match(/package\.json/),
50 | '{\n "integrity": {\n "hashes": {},\n "version": ""\n }\n}'))
51 | .to.be.true;
52 | });
53 |
54 | it('replacing the existing manifest integrity property',
55 | async (): Promise => {
56 | getManifestStub.resolves({ manifest: { integrity: { hash: '' } }, indentation: { amount: 2 } });
57 | await Integrity.updateManifestIntegrity(integrityTestObject);
58 | expect(getManifestStub.calledOnce).to.be.true;
59 | expect(writeFileAsyncStub.calledOnceWithExactly(match(/package\.json/),
60 | '{\n "integrity": {\n "hashes": {},\n "version": ""\n }\n}'))
61 | .to.be.true;
62 | });
63 | });
64 |
65 | });
66 |
67 | });
68 |
--------------------------------------------------------------------------------
/test/cli/index.test.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | import { expect } from 'chai';
3 | import { EventEmitter } from 'events';
4 | import readline, { ReadLine } from 'readline';
5 | import sinon, { createSandbox } from 'sinon';
6 | import { IntegrityOptions } from '../../src';
7 | import { Integrity } from '../../src/app/integrity';
8 | import nsri from '../../src/cli/index';
9 | import { ConfigExplorer } from '../../src/common/configExplorer';
10 | import { Logger } from '../../src/common/logger';
11 | import { YargsParser } from '../../src/common/yargsParser';
12 | import { ParsedArgs } from '../../src/interfaces//parsedArgs';
13 | import { IntegrityObject } from '../../src/interfaces/integrityObject';
14 |
15 | describe('CLI: tests', (): void => {
16 |
17 | let sandbox: sinon.SinonSandbox;
18 | let pargs: ParsedArgs;
19 | let configExplorerStub: sinon.SinonStub<[], Promise>;
20 | let ypParseStub: sinon.SinonStub<[], Promise>;
21 | let icCreateStub: sinon.SinonStub<[string, (IntegrityOptions | undefined)?], Promise>;
22 | let icCheckStub: sinon.SinonStub<[string, string, IntegrityOptions?], Promise>;
23 | let isTTY: boolean;
24 |
25 | beforeEach((): void => {
26 | pargs = {
27 | command: '',
28 | dirAlgorithm: 'md5',
29 | encoding: 'hex',
30 | exclude: [],
31 | fileAlgorithm: 'md5',
32 | inPath: '',
33 | integrity: '',
34 | manifest: false,
35 | outPath: './',
36 | pretty: false,
37 | strict: false,
38 | verbose: false,
39 | };
40 | sandbox = createSandbox();
41 | ypParseStub = sandbox.stub(YargsParser.prototype, 'parse').resolves(pargs);
42 | configExplorerStub = sandbox.stub(ConfigExplorer.prototype, 'assignArgs').resolves();
43 | icCreateStub = sandbox.stub(Integrity, 'create');
44 | icCheckStub = sandbox.stub(Integrity, 'check');
45 | sandbox.stub(Integrity, 'persist');
46 | sandbox.stub(Integrity, 'updateManifestIntegrity');
47 | sandbox.stub(Integrity, 'getManifestIntegrity');
48 | isTTY = process.stdout.isTTY;
49 | process.stdout.setMaxListeners(Infinity);
50 | process.stdin.setMaxListeners(Infinity);
51 | });
52 |
53 | afterEach((): void => {
54 | process.stdout.isTTY = isTTY;
55 | sandbox.restore();
56 | });
57 |
58 | context('expects', (): void => {
59 |
60 | context('to log', (): void => {
61 |
62 | context('process messages', (): void => {
63 |
64 | it('when creating an integrity file',
65 | async (): Promise => {
66 | pargs.command = 'create';
67 | pargs.manifest = false;
68 | const exitStub = sandbox.stub(process, 'exit');
69 | const consoleLogStub = sandbox.stub(console, 'log');
70 | const stdoutStub = sandbox.stub(process.stdout, 'write');
71 | const spinnerLogStartSpy = sandbox.spy(Logger.prototype, 'spinnerLogStart');
72 | const spinnerLogStopSpy = sandbox.spy(Logger.prototype, 'spinnerLogStop');
73 | await nsri();
74 | stdoutStub.restore();
75 | consoleLogStub.restore();
76 | exitStub.restore();
77 | expect(spinnerLogStopSpy.calledOnce).to.be.true;
78 | expect(spinnerLogStartSpy.calledOnce).to.be.true;
79 | expect(spinnerLogStartSpy.calledBefore(spinnerLogStopSpy)).to.be.true;
80 | expect(spinnerLogStartSpy.calledWith('Creating integrity hash')).to.be.true;
81 | const returnValue = spinnerLogStartSpy.returnValues[0];
82 | expect(spinnerLogStopSpy.calledWith(returnValue, 'Integrity hash file created')).to.be.true;
83 | });
84 |
85 | it('when creating an integrity property in manifest',
86 | async (): Promise => {
87 | pargs.command = 'create';
88 | pargs.manifest = true;
89 | const exitStub = sandbox.stub(process, 'exit');
90 | const consoleLogStub = sandbox.stub(console, 'log');
91 | const stdoutStub = sandbox.stub(process.stdout, 'write');
92 | const spinnerLogStartSpy = sandbox.spy(Logger.prototype, 'spinnerLogStart');
93 | const spinnerLogStopSpy = sandbox.spy(Logger.prototype, 'spinnerLogStop');
94 | await nsri();
95 | stdoutStub.restore();
96 | consoleLogStub.restore();
97 | exitStub.restore();
98 | expect(spinnerLogStopSpy.calledOnce).to.be.true;
99 | expect(spinnerLogStartSpy.calledOnce).to.be.true;
100 | expect(spinnerLogStartSpy.calledBefore(spinnerLogStopSpy)).to.be.true;
101 | expect(spinnerLogStartSpy.calledWith('Creating integrity hash')).to.be.true;
102 | const returnValue = spinnerLogStartSpy.returnValues[0];
103 | expect(spinnerLogStopSpy.calledWith(returnValue, 'Integrity hash created -> Manifest updated')).to.be.true;
104 | });
105 |
106 | context('when integrity check passes against an integrity', (): void => {
107 |
108 | it('file',
109 | async (): Promise => {
110 | pargs.command = 'check';
111 | pargs.manifest = false;
112 | icCheckStub.resolves(true);
113 | const exitStub = sandbox.stub(process, 'exit');
114 | const consoleLogStub = sandbox.stub(console, 'log');
115 | const stdoutStub = sandbox.stub(process.stdout, 'write');
116 | const spinnerLogStartSpy = sandbox.spy(Logger.prototype, 'spinnerLogStart');
117 | const spinnerLogStopSpy = sandbox.spy(Logger.prototype, 'spinnerLogStop');
118 | await nsri();
119 | stdoutStub.restore();
120 | consoleLogStub.restore();
121 | exitStub.restore();
122 | expect(spinnerLogStopSpy.calledOnce).to.be.true;
123 | expect(spinnerLogStartSpy.calledOnce).to.be.true;
124 | expect(spinnerLogStartSpy.calledBefore(spinnerLogStopSpy)).to.be.true;
125 | expect(spinnerLogStartSpy.calledWith(`Checking integrity of: '${pargs.inPath}'`)).to.be.true;
126 | const returnValue = spinnerLogStartSpy.returnValues[0];
127 | expect(spinnerLogStopSpy.calledWith(returnValue, 'Integrity validated')).to.be.true;
128 | });
129 |
130 | it('in manifest',
131 | async (): Promise => {
132 | pargs.command = 'check';
133 | pargs.manifest = true;
134 | icCheckStub.resolves(true);
135 | const exitStub = sandbox.stub(process, 'exit');
136 | const consoleLogStub = sandbox.stub(console, 'log');
137 | const stdoutStub = sandbox.stub(process.stdout, 'write');
138 | const spinnerLogStartSpy = sandbox.spy(Logger.prototype, 'spinnerLogStart');
139 | const spinnerLogStopSpy = sandbox.spy(Logger.prototype, 'spinnerLogStop');
140 | await nsri();
141 | stdoutStub.restore();
142 | consoleLogStub.restore();
143 | exitStub.restore();
144 | expect(spinnerLogStopSpy.calledOnce).to.be.true;
145 | expect(spinnerLogStartSpy.calledOnce).to.be.true;
146 | expect(spinnerLogStartSpy.calledBefore(spinnerLogStopSpy)).to.be.true;
147 | expect(spinnerLogStartSpy.calledWith(`Checking integrity of: '${pargs.inPath}'`)).to.be.true;
148 | const returnValue = spinnerLogStartSpy.returnValues[0];
149 | expect(spinnerLogStopSpy.calledWith(returnValue, 'Integrity validated')).to.be.true;
150 | });
151 |
152 | });
153 |
154 | it('when integrity check fails',
155 | async (): Promise => {
156 | pargs.command = 'check';
157 | icCheckStub.resolves(false);
158 | const exitStub = sandbox.stub(process, 'exit');
159 | const consoleLogStub = sandbox.stub(console, 'log');
160 | const stdoutStub = sandbox.stub(process.stdout, 'write');
161 | const spinnerLogStartSpy = sandbox.spy(Logger.prototype, 'spinnerLogStart');
162 | const spinnerLogStopSpy = sandbox.spy(Logger.prototype, 'spinnerLogStop');
163 | await nsri();
164 | stdoutStub.restore();
165 | consoleLogStub.restore();
166 | exitStub.restore();
167 | expect(spinnerLogStopSpy.calledOnce).to.be.true;
168 | expect(spinnerLogStartSpy.calledOnce).to.be.true;
169 | expect(spinnerLogStartSpy.calledBefore(spinnerLogStopSpy)).to.be.true;
170 | expect(spinnerLogStartSpy.calledWith(`Checking integrity of: '${pargs.inPath}'`)).to.be.true;
171 | const returnValue = spinnerLogStartSpy.returnValues[0];
172 | expect(spinnerLogStopSpy.calledWith(returnValue, 'Integrity check failed')).to.be.true;
173 | });
174 |
175 | });
176 |
177 | it('informative messages',
178 | async (): Promise => {
179 | process.stdout.isTTY = true;
180 | pargs.command = 'check';
181 | const exitStub = sandbox.stub(process, 'exit');
182 | const consoleLogStub = sandbox.stub(console, 'log');
183 | const stdoutStub = sandbox.stub(process.stdout, 'write');
184 | const loggerLogSpy = sandbox.spy(Logger.prototype, 'log');
185 | const loggerUpdateLogSpy = sandbox.spy(Logger.prototype, 'updateLog');
186 | await nsri();
187 | stdoutStub.restore();
188 | consoleLogStub.restore();
189 | exitStub.restore();
190 | expect(loggerLogSpy.called).to.be.true;
191 | expect(loggerUpdateLogSpy.callCount).to.equal(2);
192 | });
193 |
194 | it('Error messages',
195 | async (): Promise => {
196 | process.stdout.isTTY = true;
197 | pargs.command = 'check';
198 | const error = new Error('test');
199 | icCheckStub.throws(error);
200 | const exitStub = sandbox.stub(process, 'exit');
201 | const consoleLogStub = sandbox.stub(console, 'log');
202 | const stdoutStub = sandbox.stub(process.stdout, 'write');
203 | const loggerSpinnerLogStopSpy = sandbox.spy(Logger.prototype, 'spinnerLogStop');
204 | const loggerUpdateLogSpy = sandbox.spy(Logger.prototype, 'updateLog');
205 | await nsri();
206 | stdoutStub.restore();
207 | consoleLogStub.restore();
208 | exitStub.restore();
209 | expect(loggerSpinnerLogStopSpy.calledOnce).to.be.true;
210 | expect(loggerUpdateLogSpy.callCount).to.equal(3);
211 | expect(loggerUpdateLogSpy.thirdCall.calledWithMatch(error.message)).to.be.true;
212 | });
213 |
214 | it('Error',
215 | async (): Promise => {
216 | process.stdout.isTTY = true;
217 | pargs.command = 'check';
218 | const error = new Error();
219 | icCheckStub.throws(error);
220 | const exitStub = sandbox.stub(process, 'exit');
221 | const consoleLogStub = sandbox.stub(console, 'log');
222 | const stdoutStub = sandbox.stub(process.stdout, 'write');
223 | const loggerSpinnerLogStopSpy = sandbox.spy(Logger.prototype, 'spinnerLogStop');
224 | const loggerUpdateLogSpy = sandbox.spy(Logger.prototype, 'updateLog');
225 | await nsri();
226 | stdoutStub.restore();
227 | consoleLogStub.restore();
228 | exitStub.restore();
229 | expect(loggerSpinnerLogStopSpy.calledOnce).to.be.true;
230 | expect(loggerUpdateLogSpy.callCount).to.equal(3);
231 | expect(loggerUpdateLogSpy.thirdCall.calledWithMatch(error.toString())).to.be.true;
232 | });
233 |
234 | });
235 |
236 | context('to call', (): void => {
237 |
238 | it(`the ConfigExplorer 'assignArgs' function`,
239 | async (): Promise => {
240 | pargs.command = 'check';
241 | const exitStub = sandbox.stub(process, 'exit');
242 | const consoleLogStub = sandbox.stub(console, 'log');
243 | const stdoutStub = sandbox.stub(process.stdout, 'write');
244 | await nsri();
245 | stdoutStub.restore();
246 | consoleLogStub.restore();
247 | exitStub.restore();
248 | expect(configExplorerStub.calledOnce).to.be.true;
249 | });
250 |
251 | it(`the YargsParser 'parse' function`,
252 | async (): Promise => {
253 | pargs.command = 'check';
254 | const exitStub = sandbox.stub(process, 'exit');
255 | const consoleLogStub = sandbox.stub(console, 'log');
256 | const stdoutStub = sandbox.stub(process.stdout, 'write');
257 | await nsri();
258 | stdoutStub.restore();
259 | consoleLogStub.restore();
260 | exitStub.restore();
261 | expect(ypParseStub.calledOnce).to.be.true;
262 | });
263 |
264 | it(`the Integrity 'create' function`,
265 | async (): Promise => {
266 | pargs.command = 'create';
267 | const exitStub = sandbox.stub(process, 'exit');
268 | const consoleLogStub = sandbox.stub(console, 'log');
269 | const stdoutStub = sandbox.stub(process.stdout, 'write');
270 | await nsri();
271 | stdoutStub.restore();
272 | consoleLogStub.restore();
273 | exitStub.restore();
274 | expect(icCreateStub.calledOnce).to.be.true;
275 | });
276 |
277 | it(`the Integrity 'check' function`,
278 | async (): Promise => {
279 | pargs.command = 'check';
280 | const exitStub = sandbox.stub(process, 'exit');
281 | const consoleLogStub = sandbox.stub(console, 'log');
282 | const stdoutStub = sandbox.stub(process.stdout, 'write');
283 | await nsri();
284 | stdoutStub.restore();
285 | consoleLogStub.restore();
286 | exitStub.restore();
287 | expect(icCheckStub.calledOnce).to.be.true;
288 | });
289 |
290 | });
291 |
292 | context('when signaled to exit', (): void => {
293 |
294 | it(`to call 'handleForcedExit'`,
295 | (): Promise => {
296 | pargs.command = 'create';
297 | const exitStub = sandbox.stub(process, 'exit');
298 | const consoleLogStub = sandbox.stub(console, 'log');
299 | const stdoutStub = sandbox.stub(process.stdout, 'write');
300 | const handleForcedExitStub = sandbox.stub(Logger.prototype, 'handleForcedExit');
301 | const emitter = new EventEmitter();
302 | sandbox.stub(readline, 'createInterface').returns(emitter as ReadLine);
303 | const promise = nsri().then((): void => {
304 | consoleLogStub.restore();
305 | stdoutStub.restore();
306 | exitStub.restore();
307 | expect(handleForcedExitStub.called).to.be.true;
308 | });
309 | emitter.emit('SIGINT');
310 | return promise;
311 | });
312 |
313 | });
314 |
315 | });
316 |
317 | });
318 |
--------------------------------------------------------------------------------
/test/common/configExplorer.test.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | import { expect } from 'chai';
3 | import { join, resolve } from 'path';
4 | import sinon, { createSandbox } from 'sinon';
5 | import { ConfigExplorer } from '../../src/common/configExplorer';
6 | import { ConfigOptions } from '../../src/interfaces/configOptions';
7 |
8 | describe('ConfigExplorer: tests', (): void => {
9 |
10 | context('expects', (): void => {
11 |
12 | let sandbox: sinon.SinonSandbox;
13 | let configExplorer: ConfigExplorer;
14 | let getConfigStub: sinon.SinonStub<[(string | undefined)?], Promise>;
15 | let baseConfigDirPath: string;
16 |
17 | beforeEach((): void => {
18 | sandbox = createSandbox();
19 | configExplorer = new ConfigExplorer();
20 | baseConfigDirPath = resolve(__dirname, '../../../test/cosmiconfig/');
21 | });
22 |
23 | afterEach((): void => {
24 | sandbox.restore();
25 | });
26 |
27 | context('when calling', (): void => {
28 |
29 | context(`function 'assignArgs'`, (): void => {
30 | beforeEach((): void => {
31 | getConfigStub = sandbox.stub(ConfigExplorer.prototype, 'getConfig');
32 | sandbox.stub(process, 'argv').value([]);
33 | });
34 |
35 | it('to simply return when no configuration section is found',
36 | async (): Promise => {
37 | getConfigStub.resolves({});
38 | await configExplorer.assignArgs();
39 | expect(process.argv).to.eql([]);
40 | });
41 |
42 | context('to assign to the process arguments', (): void => {
43 |
44 | it(`the 'manifest' value`,
45 | async (): Promise => {
46 | getConfigStub.resolves({ manifest: true });
47 | await configExplorer.assignArgs();
48 | expect(process.argv).to.contain('-m').and.to.contain('true');
49 | });
50 |
51 | it(`the 'source' value`,
52 | async (): Promise => {
53 | getConfigStub.resolves({ source: '/some/path/' });
54 | await configExplorer.assignArgs();
55 | expect(process.argv).to.contain('-s').and.to.contain('/some/path/');
56 | });
57 |
58 | it(`the 'verbose' value`,
59 | async (): Promise => {
60 | getConfigStub.resolves({ verbose: true });
61 | await configExplorer.assignArgs();
62 | expect(process.argv).to.contain('-v').and.to.contain('true');
63 | });
64 |
65 | it(`the 'strict' value`,
66 | async (): Promise => {
67 | getConfigStub.resolves({ strict: true });
68 | await configExplorer.assignArgs();
69 | expect(process.argv).to.contain('-st').and.to.contain('true');
70 | });
71 |
72 | it(`the 'diralgorithm' value`,
73 | async (): Promise => {
74 | getConfigStub.resolves({ cryptoOptions: { dirAlgorithm: 'sha512' } });
75 | await configExplorer.assignArgs();
76 | expect(process.argv).to.contain('-da').and.to.contain('sha512');
77 | });
78 |
79 | it(`the 'filealgorithm' value`,
80 | async (): Promise => {
81 | getConfigStub.resolves({ cryptoOptions: { fileAlgorithm: 'sha1' } });
82 | await configExplorer.assignArgs();
83 | expect(process.argv).to.contain('-fa').and.to.contain('sha1');
84 | });
85 |
86 | it(`the 'encoding' value`,
87 | async (): Promise => {
88 | getConfigStub.resolves({ cryptoOptions: { encoding: 'base64' } });
89 | await configExplorer.assignArgs();
90 | expect(process.argv).to.contain('-e').and.to.contain('base64');
91 | });
92 |
93 | it(`the 'exclude' value`,
94 | async (): Promise => {
95 | getConfigStub.resolves({ exclude: ['dir', 'file'] });
96 | await configExplorer.assignArgs();
97 | expect(process.argv).to.contain('-x').and.to.contain('dir').and.to.contain('file');
98 | });
99 |
100 | it(`the 'integrity' value`,
101 | async (): Promise => {
102 | getConfigStub.resolves({ integrity: '/path/to/integrity/' });
103 | await configExplorer.assignArgs();
104 | expect(process.argv).to.contain('-i').and.to.contain('/path/to/integrity/');
105 | });
106 |
107 | it(`the 'output' value`,
108 | async (): Promise => {
109 | getConfigStub.resolves({ output: '/' });
110 | await configExplorer.assignArgs();
111 | expect(process.argv).to.contain('-o').and.to.contain('/');
112 | });
113 |
114 | });
115 |
116 | });
117 |
118 | context(`function 'getConfig'`, (): void => {
119 |
120 | it(`to throw an Error when 'explorer' is not initialized`,
121 | async (): Promise => {
122 | // @ts-ignore
123 | sandbox.stub(configExplorer, 'explorer').value(undefined);
124 | try {
125 | await configExplorer.getConfig();
126 | } catch (error) {
127 | expect(error).to.be.an.instanceOf(Error).and.match(/CosmiConfig not initialized/);
128 | }
129 | });
130 |
131 | it(`to return an empty object when 'explorer' returns 'null'`,
132 | async (): Promise => {
133 | // @ts-ignore
134 | sandbox.stub(configExplorer.explorer, 'search').resolves(null);
135 | const config = await configExplorer.getConfig();
136 | expect(config).to.be.an('object').that.is.empty;
137 | });
138 |
139 | context('to retrieve the configuration values from', (): void => {
140 |
141 | it(`a 'package.json' file`,
142 | async (): Promise => {
143 | const dirPath = join(baseConfigDirPath, 'packagejson');
144 | const config = await configExplorer.getConfig(dirPath);
145 | expect(config).to.haveOwnProperty('source');
146 | expect(config).to.haveOwnProperty('verbose');
147 | expect(config).to.haveOwnProperty('exclude');
148 | });
149 |
150 | it(`an 'rc' file`,
151 | async (): Promise => {
152 | const dirPath = join(baseConfigDirPath, 'rc');
153 | const config = await configExplorer.getConfig(dirPath);
154 | expect(config).to.haveOwnProperty('source');
155 | expect(config).to.haveOwnProperty('verbose');
156 | expect(config).to.haveOwnProperty('exclude');
157 | });
158 |
159 | it(`a '.config.js' file`,
160 | async (): Promise => {
161 | const dirPath = join(baseConfigDirPath, 'configjs');
162 | const config = await configExplorer.getConfig(dirPath);
163 | expect(config).to.haveOwnProperty('source');
164 | expect(config).to.haveOwnProperty('verbose');
165 | expect(config).to.haveOwnProperty('exclude');
166 | });
167 |
168 | it(`a 'json' file`,
169 | async (): Promise => {
170 | const dirPath = join(baseConfigDirPath, 'json');
171 | const config = await configExplorer.getConfig(dirPath);
172 | expect(config).to.haveOwnProperty('verbose');
173 | expect(config).to.haveOwnProperty('exclude');
174 | });
175 |
176 | it(`a 'js' file`,
177 | async (): Promise => {
178 | const dirPath = join(baseConfigDirPath, 'js');
179 | const config = await configExplorer.getConfig(dirPath);
180 | expect(config).to.haveOwnProperty('source');
181 | expect(config).to.haveOwnProperty('verbose');
182 | expect(config).to.haveOwnProperty('exclude');
183 | });
184 |
185 | it(`a 'yaml' file`,
186 | async (): Promise => {
187 | const dirPath = join(baseConfigDirPath, 'yaml');
188 | const config = await configExplorer.getConfig(dirPath);
189 | expect(config).to.haveOwnProperty('source');
190 | expect(config).to.haveOwnProperty('verbose');
191 | expect(config).to.haveOwnProperty('exclude');
192 | });
193 |
194 | it(`a 'yml' file`,
195 | async (): Promise => {
196 | const dirPath = join(baseConfigDirPath, 'yml');
197 | const config = await configExplorer.getConfig(dirPath);
198 | expect(config).to.haveOwnProperty('source');
199 | expect(config).to.haveOwnProperty('verbose');
200 | expect(config).to.haveOwnProperty('exclude');
201 | });
202 |
203 | });
204 |
205 | });
206 |
207 | });
208 |
209 | });
210 |
211 | });
212 |
--------------------------------------------------------------------------------
/test/common/utils.test.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | import { expect } from 'chai';
3 | import * as utils from '../../src/common/utils';
4 |
5 | describe('Utils: tests', (): void => {
6 |
7 | context('expects', (): void => {
8 |
9 | context(`function 'parseJSONSafe'`, (): void => {
10 |
11 | context('to return a JSON when passed parameter is of type', (): void => {
12 |
13 | it('string',
14 | (): void => {
15 | const data = '{"some": "valid JSON"}';
16 | expect(utils.parseJSONSafe(data)).to.eql({ some: 'valid JSON' });
17 | });
18 |
19 | it('Buffer',
20 | (): void => {
21 | const data = Buffer.from('{"some": "valid JSON"}');
22 | expect(utils.parseJSONSafe(data)).to.eql({ some: 'valid JSON' });
23 | });
24 |
25 | });
26 |
27 | it(`to return an empty JSON when provided text is not a valid JSON`,
28 | (): void => {
29 | const text = 'some invalid json';
30 | expect(utils.parseJSONSafe(text)).to.be.empty;
31 | });
32 |
33 | });
34 |
35 | context(`function 'sortObject'`, (): void => {
36 |
37 | it('to sort the object properties',
38 | (): void => {
39 | const sut = { c: [], a: '', d: {}, b: 0 };
40 | const expectedObj = { a: '', b: 0, c: [], d: {} };
41 | expect(utils.sortObject(sut)).to.eql(expectedObj);
42 | });
43 |
44 | });
45 |
46 | context(`function 'getIndentation'`, (): void => {
47 |
48 | it('to return indent info',
49 | (): void => {
50 | const info = utils.getIndentation(' ');
51 | expect(info.amount).to.equal(2);
52 | expect(info.indent).to.equal(' ');
53 | expect(info.type).to.equal('space');
54 | });
55 |
56 | });
57 |
58 | context(`function 'normalizeEntries'`, (): void => {
59 |
60 | it('to return the provided entries normalized',
61 | (): void => {
62 | const entries = [
63 | [' *', '#comment', ' # another comment', '*/ ', ' !dist', ' ', ''],
64 | ['*', '*/', '!dist'],
65 | ];
66 | const normalEntries = utils.normalizeEntries(entries[0]);
67 | expect(normalEntries).to.eql(entries[1]);
68 | });
69 |
70 | context(`function 'unique'`, (): void => {
71 |
72 | it('to return unique entries',
73 | (): void => {
74 | const entries = [
75 | ['one', 'two', 'one'],
76 | ['one', 'two'],
77 | ];
78 |
79 | const uniqueEntries = utils.unique(entries[0]);
80 | expect(uniqueEntries).to.eql(entries[1]);
81 | });
82 |
83 | });
84 |
85 | });
86 |
87 | });
88 |
89 | });
90 |
--------------------------------------------------------------------------------
/test/common/yargsParser.test.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | import { expect } from 'chai';
3 | import fs from 'fs';
4 | import path from 'path';
5 | import sinon, { createSandbox } from 'sinon';
6 | import { YargsParser } from '../../src/common/yargsParser';
7 |
8 | describe('YargsParser: tests', (): void => {
9 |
10 | let sandbox: sinon.SinonSandbox;
11 | let argv: sinon.SinonStub;
12 | let parser: YargsParser;
13 | let args: string[];
14 | let fsStatsMock: sinon.SinonStubbedInstance;
15 |
16 | beforeEach((): void => {
17 | sandbox = createSandbox();
18 | argv = sandbox.stub(process, 'argv');
19 | fsStatsMock = sandbox.createStubInstance(fs.Stats);
20 | parser = new YargsParser();
21 | args = ['node', 'nsri', 'create', '-s', '.'];
22 | });
23 |
24 | afterEach((): void => {
25 | sandbox.restore();
26 | });
27 |
28 | context('expects', (): void => {
29 |
30 | it('the returned parsed arguments object to have the correct properties',
31 | async (): Promise => {
32 | argv.value([...args]);
33 | const sut = await parser.parse();
34 | const props = ['dirAlgorithm', 'fileAlgorithm', 'command', 'encoding',
35 | 'exclude', 'inPath', 'integrity', 'manifest', 'outPath', 'pretty',
36 | 'strict', 'verbose'];
37 | expect(sut).to.be.an('object');
38 | props.forEach((prop: string): Chai.Assertion => expect(sut).to.be.haveOwnProperty(prop));
39 | expect(Object.keys(sut)).with.length(props.length);
40 | });
41 |
42 | it(`that the 'fileAlgorithm' option gets parsed correctly`,
43 | async (): Promise => {
44 | args = [...args, '--fa', 'sha'];
45 | argv.value(args);
46 | const pargs = await parser.parse();
47 | expect(pargs).to.be.have.property('fileAlgorithm', args[6]);
48 | });
49 |
50 | it(`that the 'dirAlgorithm' option gets parsed correctly`,
51 | async (): Promise => {
52 | args = [...args, '--da', 'sha'];
53 | argv.value(args);
54 | const pargs = await parser.parse();
55 | expect(pargs).to.be.have.property('dirAlgorithm', args[6]);
56 | });
57 |
58 | it(`that the 'command' gets parsed correctly`,
59 | async (): Promise => {
60 | argv.value([...args]);
61 | const pargs = await parser.parse();
62 | expect(pargs).to.be.have.property('command', args[2]);
63 | });
64 |
65 | it(`that the 'encoding' option gets parsed correctly`,
66 | async (): Promise => {
67 | args = [...args, '-e', 'base64'];
68 | argv.value(args);
69 | const pargs = await parser.parse();
70 | expect(pargs).to.be.have.property('encoding', args[6]);
71 | });
72 |
73 | it(`that the 'exclude' option gets parsed correctly`,
74 | async (): Promise => {
75 | args = [...args, '-x', 'some/path'];
76 | argv.value(args);
77 | const pargs = await parser.parse();
78 | expect(pargs).to.be.have.property('exclude').with.members([args[6]]);
79 | });
80 |
81 | it(`that the 'inPath' option gets parsed correctly`,
82 | async (): Promise => {
83 | args = [...args];
84 | argv.value(args);
85 | const pargs = await parser.parse();
86 | expect(pargs).to.be.have.property('inPath', args[4]);
87 | });
88 |
89 | it(`that the 'integrity' option gets parsed correctly`,
90 | async (): Promise => {
91 | args.splice(2, 1, 'check');
92 | args = [...args, '-i', '123456789'];
93 | argv.value(args);
94 | const pargs = await parser.parse();
95 | expect(pargs).to.be.have.property('integrity', args[6]);
96 | });
97 |
98 | it(`that the 'manifest' option gets parsed correctly`,
99 | async (): Promise => {
100 | args = [...args, '-m', 'true'];
101 | argv.value(args);
102 | const pargs = await parser.parse();
103 | expect(pargs).to.be.have.property('manifest', true);
104 | });
105 |
106 | it(`that the 'outPath' option gets parsed correctly`,
107 | async (): Promise => {
108 | args = [...args, '-o', './out'];
109 | argv.value(args);
110 | const pargs = await parser.parse();
111 | expect(pargs).to.be.have.property('outPath', args[6]);
112 | });
113 |
114 | it(`that the 'pretty' option gets parsed correctly`,
115 | async (): Promise => {
116 | args = [...args, '-p', 'true'];
117 | argv.value(args);
118 | const pargs = await parser.parse();
119 | expect(pargs).to.be.have.property('pretty', true);
120 | });
121 |
122 | it(`that the 'strict' option gets parsed correctly`,
123 | async (): Promise => {
124 | args = [...args, '--st', 'true'];
125 | argv.value(args);
126 | const pargs = await parser.parse();
127 | expect(pargs).to.be.have.property('strict', true);
128 | });
129 |
130 | it(`that the 'verbose' option gets parsed correctly`,
131 | async (): Promise => {
132 | args = [...args, '-v', 'true'];
133 | argv.value(args);
134 | const pargs = await parser.parse();
135 | expect(pargs).to.be.have.property('verbose', true);
136 | });
137 |
138 | it('to throw an Error on invalid file path',
139 | async (): Promise => {
140 | const consoleErrorStub = sandbox.stub(console, 'error');
141 | const stderrStub = sandbox.stub(process.stderr, 'write');
142 | const exitStub = sandbox.stub(process, 'exit');
143 | fsStatsMock.isFile.returns(true);
144 | const statStub = sandbox.stub(fs, 'statSync').returns(fsStatsMock);
145 | args.pop();
146 | args.push('file.io');
147 | argv.value(args);
148 | await parser.parse();
149 | expect(consoleErrorStub.called).to.be.true;
150 | expect(consoleErrorStub.thirdCall
151 | .calledWithExactly(`ENOENT: no such file or directory, 'file.io'`))
152 | .to.be.true;
153 | expect(exitStub.called).to.be.true;
154 | statStub.restore();
155 | stderrStub.restore();
156 | consoleErrorStub.restore();
157 | exitStub.restore();
158 | });
159 |
160 | it(`to throw an Error on invalid use of 'manifest' and 'integrity' options with the 'check' command`,
161 | async (): Promise => {
162 | const consoleErrorStub = sandbox.stub(console, 'error');
163 | const stderrStub = sandbox.stub(process.stderr, 'write');
164 | const exitStub = sandbox.stub(process, 'exit');
165 | fsStatsMock.isFile.returns(true);
166 | const statStub = sandbox.stub(fs, 'statSync').returns(fsStatsMock);
167 | args.splice(2, 1, 'check');
168 | args.push(...['-m', '-i', '.']);
169 | argv.value(args);
170 | await parser.parse();
171 | expect(consoleErrorStub.called).to.be.true;
172 | expect(consoleErrorStub.thirdCall
173 | .calledWithExactly('Arguments integrity and manifest are mutually exclusive'))
174 | .to.be.true;
175 | expect(exitStub.called).to.be.true;
176 | statStub.restore();
177 | stderrStub.restore();
178 | consoleErrorStub.restore();
179 | exitStub.restore();
180 | });
181 |
182 | it(`to throw an Error on invalid use of 'integrity' options with the 'check' command`,
183 | async (): Promise => {
184 | const consoleErrorStub = sandbox.stub(console, 'error');
185 | const stderrStub = sandbox.stub(process.stderr, 'write');
186 | const exitStub = sandbox.stub(process, 'exit');
187 | fsStatsMock.isFile.returns(true);
188 | const statStub = sandbox.stub(fs, 'statSync').returns(fsStatsMock);
189 | args.splice(2, 1, 'check');
190 | argv.value(args);
191 | await parser.parse();
192 | expect(consoleErrorStub.called).to.be.true;
193 | expect(consoleErrorStub.thirdCall
194 | .calledWithExactly('Missing required argument: integrity'))
195 | .to.be.true;
196 | expect(exitStub.called).to.be.true;
197 | statStub.restore();
198 | stderrStub.restore();
199 | consoleErrorStub.restore();
200 | exitStub.restore();
201 | });
202 |
203 | context(`that the 'outPath' gets assigned to`, (): void => {
204 |
205 | it(`the input directory when 'input' is a file`,
206 | async (): Promise => {
207 | const filePath = path.resolve(__dirname, '../../../test/fixtures/fileToHash.txt');
208 | args.pop();
209 | args.push(filePath);
210 | argv.value(args);
211 | const pargs = await parser.parse();
212 | expect(pargs.outPath).to.equal(path.dirname(filePath));
213 | });
214 |
215 | it(`the input when 'input' is a directory`,
216 | async (): Promise => {
217 | const dirPath = path.resolve(__dirname, '../../../test/fixtures');
218 | args.pop();
219 | args.push(dirPath);
220 | argv.value(args);
221 | const pargs = await parser.parse();
222 | expect(pargs.outPath).to.equal(dirPath);
223 | });
224 |
225 | });
226 |
227 | });
228 |
229 | });
230 |
--------------------------------------------------------------------------------
/test/cosmiconfig/configjs/nsri.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | source: '',
3 | exclude: [],
4 | verbose: false
5 | };
6 |
--------------------------------------------------------------------------------
/test/cosmiconfig/js/.nsrirc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | source: '',
3 | exclude: [],
4 | verbose: false
5 | };
6 |
--------------------------------------------------------------------------------
/test/cosmiconfig/json/.nsrirc.json:
--------------------------------------------------------------------------------
1 | {
2 | "source": "",
3 | "exclude": [],
4 | "verbose": false
5 | }
6 |
--------------------------------------------------------------------------------
/test/cosmiconfig/packagejson/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "nsri": {
3 | "source": "",
4 | "exclude": [],
5 | "verbose": false
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/test/cosmiconfig/rc/.nsrirc:
--------------------------------------------------------------------------------
1 | {
2 | source: '',
3 | verbose: false,
4 | exclude: []
5 | }
6 |
--------------------------------------------------------------------------------
/test/cosmiconfig/yaml/.nsrirc.yaml:
--------------------------------------------------------------------------------
1 | source: ''
2 | verbose: true
3 | exclude: []
4 |
--------------------------------------------------------------------------------
/test/cosmiconfig/yml/.nsrirc.yml:
--------------------------------------------------------------------------------
1 | source: ''
2 | verbose: true
3 | exclude: []
4 |
--------------------------------------------------------------------------------
/test/fixtures/.integrity.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1",
3 | "hashes": {
4 | ".": {
5 | "contents": {
6 | "directory": {
7 | "contents": {
8 | "anotherFileToHash.txt": "sha1-EZ2w0rsSmXBOddIoz2IoOIuxGaQ=",
9 | "otherFileToHash.txt": "sha1-B8FJ4uKgHESSgMvJUyrj3ix2uG8="
10 | },
11 | "hash": "sha512-Ze62278vNFKc3izakn2FgyvHIZEbnsuqKogaZLA1ihM1zk95RKlz+z7qk1XEysMaoJlpDNqSWx4PoPp2cFNBPw=="
12 | },
13 | "directory.1": {
14 | "contents": {
15 | "anotherFileToHash.txt": "sha1-EZ2w0rsSmXBOddIoz2IoOIuxGaQ=",
16 | "otherFileToHash.txt": "sha1-B8FJ4uKgHESSgMvJUyrj3ix2uG8="
17 | },
18 | "hash": "sha512-AwzUWMMnbbRf+pow1dBJlkRAxtQwgm59xDMLa2Gm1pcKDolXTPfPGjRc4a0muGENgeZiM4tf17HfIdEOIlS78Q=="
19 | },
20 | "fileToHash.txt": "sha1-H58mYNjbMJTkiNvvNfj2YKl3ck0=",
21 | "fixtures": {
22 | "contents": {
23 | "directory": {
24 | "contents": {
25 | "anotherFileToHash.txt": "sha1-EZ2w0rsSmXBOddIoz2IoOIuxGaQ="
26 | },
27 | "hash": "sha512-a7F1/1x1ZVAWMdcx7X9Dnzd9M4TY9wU21vNCt3ALW0+0npq85MKn7uhz8yGqjDbSmAUDf14uxgqjk2tMtkjK9w=="
28 | },
29 | "directory.1": {
30 | "contents": {
31 | "anotherFileToHash.txt": "sha1-EZ2w0rsSmXBOddIoz2IoOIuxGaQ=",
32 | "otherFileToHash.txt": "sha1-B8FJ4uKgHESSgMvJUyrj3ix2uG8="
33 | },
34 | "hash": "sha512-AwzUWMMnbbRf+pow1dBJlkRAxtQwgm59xDMLa2Gm1pcKDolXTPfPGjRc4a0muGENgeZiM4tf17HfIdEOIlS78Q=="
35 | },
36 | "fileToHash.txt": "sha1-t56X7IQ267Hza0qjpSpqb9UPcfE="
37 | },
38 | "hash": "sha512-rDNKFYBCOuaCzpomiZEGyRLAmc3+IU/HoNj7NiKXqLG90rNko74LwpZ1DYKx+/aJptGTKCr/9mP8ggnl4QVNNw=="
39 | },
40 | "sameContentWithFileToHash.txt": "sha1-l5sOr3meWkHyZWPi2Ln4GM7/lrg="
41 | },
42 | "hash": "sha512-WlFP+kAPdHyGd9E8SgkFfxuGvz9l/cqjt8gAhrHDdWLBIkkZGxgxxgpWZuARLVD7ACCxq8rVeNbwNL7NKyeWsA=="
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/test/fixtures/directory.1/.integrity.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1",
3 | "hashes": {
4 | ".": {
5 | "contents": {
6 | "anotherFileToHash.txt": "sha1-EZ2w0rsSmXBOddIoz2IoOIuxGaQ=",
7 | "otherFileToHash.txt": "sha1-B8FJ4uKgHESSgMvJUyrj3ix2uG8="
8 | },
9 | "hash": "sha512-AwzUWMMnbbRf+pow1dBJlkRAxtQwgm59xDMLa2Gm1pcKDolXTPfPGjRc4a0muGENgeZiM4tf17HfIdEOIlS78Q=="
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/test/fixtures/directory.1/anotherFileToHash.txt:
--------------------------------------------------------------------------------
1 | Content of another file to hash
2 |
--------------------------------------------------------------------------------
/test/fixtures/directory.1/otherFileToHash.txt:
--------------------------------------------------------------------------------
1 | Content of other file to hash
2 |
--------------------------------------------------------------------------------
/test/fixtures/directory/.integrity.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1",
3 | "hashes": {
4 | ".": {
5 | "contents": {
6 | "anotherFileToHash.txt": "sha1-EZ2w0rsSmXBOddIoz2IoOIuxGaQ=",
7 | "otherFileToHash.txt": "sha1-B8FJ4uKgHESSgMvJUyrj3ix2uG8="
8 | },
9 | "hash": "sha512-Ze62278vNFKc3izakn2FgyvHIZEbnsuqKogaZLA1ihM1zk95RKlz+z7qk1XEysMaoJlpDNqSWx4PoPp2cFNBPw=="
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/test/fixtures/directory/anotherFileToHash.txt:
--------------------------------------------------------------------------------
1 | Content of another file to hash
2 |
--------------------------------------------------------------------------------
/test/fixtures/directory/otherFileToHash.txt:
--------------------------------------------------------------------------------
1 | Content of other file to hash
2 |
--------------------------------------------------------------------------------
/test/fixtures/fileToHash.txt:
--------------------------------------------------------------------------------
1 | Content of file to hash
2 |
--------------------------------------------------------------------------------
/test/fixtures/fixtures/.integrity.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1",
3 | "hashes": {
4 | ".": {
5 | "contents": {
6 | "directory": {
7 | "contents": {
8 | "anotherFileToHash.txt": "sha1-EZ2w0rsSmXBOddIoz2IoOIuxGaQ="
9 | },
10 | "hash": "sha512-a7F1/1x1ZVAWMdcx7X9Dnzd9M4TY9wU21vNCt3ALW0+0npq85MKn7uhz8yGqjDbSmAUDf14uxgqjk2tMtkjK9w=="
11 | },
12 | "directory.1": {
13 | "contents": {
14 | "anotherFileToHash.txt": "sha1-EZ2w0rsSmXBOddIoz2IoOIuxGaQ=",
15 | "otherFileToHash.txt": "sha1-B8FJ4uKgHESSgMvJUyrj3ix2uG8="
16 | },
17 | "hash": "sha512-AwzUWMMnbbRf+pow1dBJlkRAxtQwgm59xDMLa2Gm1pcKDolXTPfPGjRc4a0muGENgeZiM4tf17HfIdEOIlS78Q=="
18 | },
19 | "fileToHash.txt": "sha1-t56X7IQ267Hza0qjpSpqb9UPcfE="
20 | },
21 | "hash": "sha512-rDNKFYBCOuaCzpomiZEGyRLAmc3+IU/HoNj7NiKXqLG90rNko74LwpZ1DYKx+/aJptGTKCr/9mP8ggnl4QVNNw=="
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/test/fixtures/fixtures/directory.1/.integrity.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1",
3 | "hashes": {
4 | ".": {
5 | "contents": {
6 | "anotherFileToHash.txt": "sha1-EZ2w0rsSmXBOddIoz2IoOIuxGaQ=",
7 | "otherFileToHash.txt": "sha1-B8FJ4uKgHESSgMvJUyrj3ix2uG8="
8 | },
9 | "hash": "sha512-AwzUWMMnbbRf+pow1dBJlkRAxtQwgm59xDMLa2Gm1pcKDolXTPfPGjRc4a0muGENgeZiM4tf17HfIdEOIlS78Q=="
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/test/fixtures/fixtures/directory.1/anotherFileToHash.txt:
--------------------------------------------------------------------------------
1 | Content of another file to hash
2 |
--------------------------------------------------------------------------------
/test/fixtures/fixtures/directory.1/otherFileToHash.txt:
--------------------------------------------------------------------------------
1 | Content of other file to hash
2 |
--------------------------------------------------------------------------------
/test/fixtures/fixtures/directory/.integrity.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1",
3 | "hashes": {
4 | ".": {
5 | "contents": {
6 | "anotherFileToHash.txt": "sha1-EZ2w0rsSmXBOddIoz2IoOIuxGaQ="
7 | },
8 | "hash": "sha512-a7F1/1x1ZVAWMdcx7X9Dnzd9M4TY9wU21vNCt3ALW0+0npq85MKn7uhz8yGqjDbSmAUDf14uxgqjk2tMtkjK9w=="
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/test/fixtures/fixtures/directory/anotherFileToHash.txt:
--------------------------------------------------------------------------------
1 | Content of another file to hash
2 |
--------------------------------------------------------------------------------
/test/fixtures/fixtures/fileToHash.txt:
--------------------------------------------------------------------------------
1 | Different content of file to hash
2 |
--------------------------------------------------------------------------------
/test/fixtures/sameContentWithFileToHash.txt:
--------------------------------------------------------------------------------
1 | Content of file to hash
2 |
--------------------------------------------------------------------------------
/test/helper.test.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | import { expect } from 'chai';
3 | import { hexRegexPattern } from '../src/common/utils';
4 | import { checker } from './helper';
5 |
6 | describe(`Helper: function 'checker' tests`, (): void => {
7 |
8 | it('to fail when hash is not of SRI',
9 | (): void => {
10 | const sut = checker('1f9f2660d8db3094e488dbef35f8f660a977724d', hexRegexPattern, '');
11 | expect(sut).to.be.false;
12 | });
13 |
14 | });
15 |
--------------------------------------------------------------------------------
/test/helper.ts:
--------------------------------------------------------------------------------
1 | export function checker(
2 | hash: string, encodingPattern: RegExp, hashToMatch: string,
3 | algorithmToMatch = 'sha1', lengthToMatch = 0,
4 | ): boolean {
5 | const members = /^([a-zA-Z0-9]*)-([\s\S]*)/.exec(hash);
6 | if (!members || !members.length) {
7 | return false;
8 | }
9 | const matchAlgorithm = members[1] === algorithmToMatch;
10 | const matchEncoding = encodingPattern.test(members[2]) && members[2] === hashToMatch;
11 | const matchLength = lengthToMatch > 1 ? members[2].length === lengthToMatch : true;
12 | return matchAlgorithm && matchEncoding && matchLength;
13 | }
14 |
--------------------------------------------------------------------------------
/test/ignoreFile/.nsriignore:
--------------------------------------------------------------------------------
1 | *
2 | */
3 | !dist
4 | #comment
5 | # another comment
6 |
--------------------------------------------------------------------------------
/test/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.json';
2 |
--------------------------------------------------------------------------------
/tsconfig.dev.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "diagnostics": true,
4 | "esModuleInterop": true,
5 | "lib": [
6 | "es2020"
7 | ],
8 | "target": "es2020",
9 | "module": "commonjs",
10 | "moduleResolution": "node",
11 | "newLine": "lf",
12 | "noUnusedLocals": true,
13 | "noUnusedParameters": true,
14 | "outDir": "out",
15 | "pretty": true,
16 | "resolveJsonModule": true,
17 | "rootDir": ".",
18 | "skipLibCheck": true,
19 | "sourceMap": true,
20 | "strict": true,
21 | "importHelpers": true,
22 | "forceConsistentCasingInFileNames": true
23 | },
24 | "include": [
25 | "src/**/*.ts",
26 | "test/**/*.ts"
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.dev.json",
3 | "compilerOptions": {
4 | "lib": [
5 | "es5"
6 | ],
7 | "target": "es5",
8 | "declaration": true,
9 | "diagnostics": false,
10 | "sourceMap": false,
11 | "stripInternal": true,
12 | "removeComments": false,
13 | "outDir": "dist/out",
14 | "rootDir": "src"
15 | },
16 | "exclude": ["test/**/*.ts"]
17 | }
18 |
--------------------------------------------------------------------------------
/tsconfig.strict.dev.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "diagnostics": true,
4 | "esModuleInterop": true,
5 | "lib": [
6 | "es2020"
7 | ],
8 | "target": "es2020",
9 | "module": "commonjs",
10 | "moduleResolution": "node",
11 | "newLine": "lf",
12 | "noUnusedLocals": true,
13 | "noUnusedParameters": true,
14 | "outDir": "out",
15 | "pretty": true,
16 | "resolveJsonModule": true,
17 | "rootDir": ".",
18 | "skipLibCheck": true,
19 | "sourceMap": true,
20 | "strict": true,
21 | "importHelpers": true,
22 | "forceConsistentCasingInFileNames": true,
23 | "allowUnusedLabels": false,
24 | "allowUnreachableCode": false,
25 | "exactOptionalPropertyTypes": true,
26 | "noFallthroughCasesInSwitch": true,
27 | "noImplicitOverride": true,
28 | "noImplicitReturns": true,
29 | "noPropertyAccessFromIndexSignature": true,
30 | "noUncheckedIndexedAccess": true,
31 | "importsNotUsedAsValues": "error",
32 | "checkJs": true
33 | },
34 | "include": [
35 | "src/**/*.ts",
36 | "test/**/*.ts"
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------