├── .editorconfig ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ └── bevry.yml ├── .gitignore ├── .npmignore ├── .prettierignore ├── CONTRIBUTING.md ├── HISTORY.md ├── LICENSE.md ├── README.md ├── SECURITY.md ├── index.cjs ├── package-lock.json ├── package.json ├── source ├── index.ts ├── logger.ts ├── test.ts ├── transform.ts └── transforms │ ├── browser.ts │ ├── filter.ts │ └── human.ts ├── test.cjs └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # 2023 June 22 2 | # https://github.com/bevry/base 3 | 4 | root = true 5 | 6 | [*] 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = false 11 | indent_style = tab 12 | 13 | [{*.mk,*.py}] 14 | indent_style = tab 15 | indent_size = 4 16 | 17 | [*.md] 18 | indent_style = space 19 | indent_size = 4 20 | 21 | [{*.json,*.lsrules,*.yaml,*.yml,*.bowerrc,*.babelrc,*.code-workspace}] 22 | indent_style = space 23 | indent_size = 2 24 | 25 | [{*.json,*.lsrules}] 26 | insert_final_newline = true 27 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [balupton] 2 | patreon: bevry 3 | open_collective: bevry 4 | ko_fi: balupton 5 | liberapay: bevry 6 | tidelift: npm/caterpillar 7 | custom: ['https://bevry.me/fund'] 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | day: sunday 8 | time: '00:00' 9 | timezone: Australia/Perth 10 | - package-ecosystem: npm 11 | directory: / 12 | schedule: 13 | interval: weekly 14 | day: sunday 15 | time: '00:00' 16 | timezone: Australia/Perth 17 | open-pull-requests-limit: 0 18 | -------------------------------------------------------------------------------- /.github/workflows/bevry.yml: -------------------------------------------------------------------------------- 1 | name: bevry 2 | 'on': 3 | - push 4 | - pull_request 5 | jobs: 6 | test: 7 | strategy: 8 | matrix: 9 | os: 10 | - ubuntu-latest 11 | - macos-latest 12 | - windows-latest 13 | node: 14 | - '16' 15 | - '18' 16 | - '20' 17 | - '21' 18 | runs-on: ${{ matrix.os }} 19 | continue-on-error: ${{ contains('macos-latest windows-latest', matrix.os) }} 20 | steps: 21 | - uses: actions/checkout@v4 22 | - name: Install Deno 23 | uses: denoland/setup-deno@v2 24 | with: 25 | deno-version: vx.x.x 26 | - name: Install desired Node.js version 27 | uses: actions/setup-node@v4 28 | with: 29 | node-version: '20' 30 | - name: Verify Node.js Versions 31 | run: >- 32 | printf '%s' 'node: ' && node --version && printf '%s' 'npm: ' && npm 33 | --version && node -e 'console.log(process.versions)' 34 | - run: npm run our:setup 35 | - run: npm run our:compile 36 | - run: npm run our:verify 37 | - name: Install targeted Node.js 38 | if: ${{ matrix.node != 20 }} 39 | uses: actions/setup-node@v4 40 | with: 41 | node-version: ${{ matrix.node }} 42 | - name: Verify Node.js Versions 43 | run: >- 44 | printf '%s' 'node: ' && node --version && printf '%s' 'npm: ' && npm 45 | --version && node -e 'console.log(process.versions)' 46 | - run: npm test 47 | publish: 48 | if: ${{ github.event_name == 'push' }} 49 | needs: test 50 | runs-on: ubuntu-latest 51 | steps: 52 | - uses: actions/checkout@v4 53 | - name: Install Deno 54 | uses: denoland/setup-deno@v2 55 | with: 56 | deno-version: vx.x.x 57 | - name: Install desired Node.js version 58 | uses: actions/setup-node@v4 59 | with: 60 | node-version: '20' 61 | - name: Verify Node.js Versions 62 | run: >- 63 | printf '%s' 'node: ' && node --version && printf '%s' 'npm: ' && npm 64 | --version && node -e 'console.log(process.versions)' 65 | - run: npm run our:setup 66 | - run: npm run our:compile 67 | - run: npm run our:meta 68 | - name: publish to npm 69 | uses: bevry-actions/npm@v1.1.7 70 | with: 71 | npmAuthToken: ${{ secrets.NPM_AUTH_TOKEN }} 72 | npmBranchTag: ':next' 73 | - name: publish to surge 74 | uses: bevry-actions/surge@v1.1.0 75 | with: 76 | surgeLogin: ${{ secrets.SURGE_LOGIN }} 77 | surgeToken: ${{ secrets.SURGE_TOKEN }} 78 | automerge: 79 | permissions: 80 | contents: write 81 | pull-requests: write 82 | runs-on: ubuntu-latest 83 | if: github.actor == 'dependabot[bot]' 84 | steps: 85 | - name: Enable auto-merge for Dependabot PRs 86 | run: gh pr merge --auto --squash "$PR_URL" 87 | env: 88 | PR_URL: ${{github.event.pull_request.html_url}} 89 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 90 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2020 June 3 2 | # https://github.com/bevry/base 3 | 4 | # System Files 5 | **/.DS_Store 6 | 7 | # Temp Files 8 | **/.docpad.db 9 | **/*.log 10 | **/*.cpuprofile 11 | **/*.heapsnapshot 12 | 13 | # Editor Files 14 | .c9/ 15 | .vscode/ 16 | 17 | # Yarn Files 18 | .yarn/* 19 | !.yarn/releases 20 | !.yarn/plugins 21 | !.yarn/sdks 22 | !.yarn/versions 23 | .pnp.* 24 | .pnp/ 25 | 26 | # Private Files 27 | .env 28 | .idea 29 | .cake_task_cache 30 | 31 | # Build Caches 32 | build/ 33 | bower_components/ 34 | node_modules/ 35 | .next/ 36 | 37 | # ------------------------------------- 38 | # CDN Inclusions, Git Exclusions 39 | 40 | # Build Outputs 41 | **/out.* 42 | **/*.out.* 43 | **/out/ 44 | **/output/ 45 | *compiled* 46 | edition*/ 47 | coffeejs/ 48 | coffee/ 49 | es5/ 50 | es2015/ 51 | esnext/ 52 | docs/ 53 | 54 | # ===================================== 55 | # CUSTOM 56 | 57 | # None 58 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # 2020 May 5 2 | # https://github.com/bevry/base 3 | 4 | # System Files 5 | **/.DS_Store 6 | 7 | # Temp Files 8 | **/.docpad.db 9 | **/*.log 10 | **/*.cpuprofile 11 | **/*.heapsnapshot 12 | 13 | # Editor Files 14 | .c9/ 15 | .vscode/ 16 | 17 | # Private Files 18 | .env 19 | .idea 20 | .cake_task_cache 21 | 22 | # Build Caches 23 | build/ 24 | components/ 25 | bower_components/ 26 | node_modules/ 27 | .pnp/ 28 | .pnp.js 29 | 30 | # Ecosystem Files 31 | .dependabout 32 | .github 33 | 34 | # ------------------------------------- 35 | # CDN Inclusions, Package Exclusions 36 | 37 | # Documentation Files 38 | docs/ 39 | guides/ 40 | BACKERS.md 41 | CONTRIBUTING.md 42 | HISTORY.md 43 | 44 | # Development Files 45 | web/ 46 | **/example* 47 | **/test* 48 | .babelrc* 49 | .editorconfig 50 | .eslintrc* 51 | .jshintrc 52 | .jscrc 53 | coffeelint* 54 | .travis* 55 | nakefile* 56 | Cakefile 57 | Makefile 58 | 59 | # Other Package Definitions 60 | template.js 61 | component.json 62 | bower.json 63 | 64 | # ===================================== 65 | # CUSTOM MODIFICATIONS 66 | 67 | # None 68 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # 2023 November 13 2 | # https://github.com/bevry/base 3 | 4 | # VCS Files 5 | .git 6 | .svn 7 | .hg 8 | 9 | # System Files 10 | **/.DS_Store 11 | 12 | # Temp Files 13 | **/.docpad.db 14 | **/*.log 15 | **/*.cpuprofile 16 | **/*.heapsnapshot 17 | 18 | # Yarn Files 19 | .yarn/* 20 | !.yarn/releases 21 | !.yarn/plugins 22 | !.yarn/sdks 23 | !.yarn/versions 24 | .pnp.* 25 | .pnp/ 26 | 27 | # Build Caches 28 | build/ 29 | components/ 30 | bower_components/ 31 | node_modules/ 32 | 33 | # Build Outputs 34 | **/*.cjs 35 | **/*.mjs 36 | **/out.* 37 | **/*.out.* 38 | **/out/ 39 | **/output/ 40 | *compiled* 41 | edition*/ 42 | coffeejs/ 43 | coffee/ 44 | es5/ 45 | es2015/ 46 | esnext/ 47 | docs/ 48 | 49 | # Development Files 50 | test/ 51 | **/*fixtures* 52 | 53 | # Ecosystem Caches 54 | .trunk/*/ 55 | 56 | # ===================================== 57 | # CUSTOM 58 | 59 | # None 60 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Before You Post! 7 | 8 | ## Support 9 | 10 | We offer support through our [Official Support Channels](https://bevry.me/support). Do not use GitHub Issues for support, your issue will be closed. 11 | 12 | ## Contribute 13 | 14 | Our [Contributing Guide](https://bevry.me/contribute) contains useful tips and suggestions for how to contribute to this project, it's worth the read. 15 | 16 | ## Development 17 | 18 | ### Setup 19 | 20 | 1. [Install Node.js](https://bevry.me/install/node) 21 | 22 | 1. Fork the project and clone your fork - [guide](https://help.github.com/articles/fork-a-repo/) 23 | 24 | 1. Setup the project for development 25 | 26 | ```bash 27 | npm run our:setup 28 | ``` 29 | 30 | ### Developing 31 | 32 | 1. Compile changes 33 | 34 | ```bash 35 | npm run our:compile 36 | ``` 37 | 38 | 1. Run tests 39 | 40 | ```bash 41 | npm test 42 | ``` 43 | 44 | ### Publishing 45 | 46 | Follow these steps in order to implement your changes/improvements into your desired project: 47 | 48 | #### Preparation 49 | 50 | 1. Make sure your changes are on their own branch that is branched off from master. 51 | 52 | 1. You can do this by: `git checkout master; git checkout -b your-new-branch` 53 | 1. And push the changes up by: `git push origin your-new-branch` 54 | 55 | 1. Ensure all tests pass: 56 | 57 | ```bash 58 | npm test 59 | ``` 60 | 61 | > If possible, add tests for your change, if you don't know how, mention this in your pull request 62 | 63 | 1. Ensure the project is ready for publishing: 64 | 65 | ``` 66 | npm run our:release:prepare 67 | ``` 68 | 69 | #### Pull Request 70 | 71 | To send your changes for the project owner to merge in: 72 | 73 | 1. Submit your pull request 74 | 1. When submitting, if the original project has a `dev` or `integrate` branch, use that as the target branch for your pull request instead of the default `master` 75 | 1. By submitting a pull request you agree for your changes to have the same license as the original plugin 76 | 77 | #### Publish 78 | 79 | To publish your changes as the project owner: 80 | 81 | 1. Switch to the master branch: 82 | 83 | ```bash 84 | git checkout master 85 | ``` 86 | 87 | 1. Merge in the changes of the feature branch (if applicable) 88 | 89 | 1. Increment the version number in the `package.json` file according to the [semantic versioning](http://semver.org) standard, that is: 90 | 91 | 1. `x.0.0` MAJOR version when you make incompatible API changes (note: DocPad plugins must use v2 as the major version, as v2 corresponds to the current DocPad v6.x releases) 92 | 1. `x.y.0` MINOR version when you add functionality in a backwards-compatible manner 93 | 1. `x.y.z` PATCH version when you make backwards-compatible bug fixes 94 | 95 | 1. Add an entry to the changelog following the format of the previous entries, an example of this is: 96 | 97 | ```markdown 98 | ## v6.29.0 2013 April 1 99 | 100 | - Progress on [issue #474](https://github.com/docpad/docpad/issues/474) 101 | - DocPad will now set permissions based on the process's ability 102 | - Thanks to [Avi Deitcher](https://github.com/deitch), [Stephan Lough](https://github.com/stephanlough) for [issue #165](https://github.com/docpad/docpad/issues/165) 103 | - Updated dependencies 104 | ``` 105 | 106 | 1. Commit the changes with the commit title set to something like `v6.29.0. Bugfix. Improvement.` and commit description set to the changelog entry 107 | 108 | 1. Ensure the project is ready for publishing: 109 | 110 | ``` 111 | npm run our:release:prepare 112 | ``` 113 | 114 | 1. Prepare the release and publish it to npm and git: 115 | 116 | ```bash 117 | npm run our:release 118 | ``` 119 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # History 2 | 3 | ## v8.2.0 2023 December 29 4 | 5 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 6 | - Thank you to the sponsors: [Andrew Nesbitt](https://nesbitt.io), [Balsa](https://balsa.com), [Codecov](https://codecov.io), [Poonacha Medappa](https://poonachamedappa.com), [Rob Morris](https://github.com/Rob-Morris), [Sentry](https://sentry.io), [Syntax](https://syntax.fm) 7 | 8 | ## v8.1.0 2023 December 27 9 | 10 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 11 | - Thank you to the sponsors: [Andrew Nesbitt](https://nesbitt.io), [Balsa](https://balsa.com), [Codecov](https://codecov.io/), [Poonacha Medappa](https://poonachamedappa.com), [Rob Morris](https://github.com/Rob-Morris), [Sentry](https://sentry.io), [Syntax](https://syntax.fm) 12 | 13 | ## v8.0.0 2023 December 6 14 | 15 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 16 | - Minimum required Node.js version changed from `node: >=6` to `node: >=4` adapting to ecosystem changes 17 | 18 | ## v7.0.0 2023 November 24 19 | 20 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 21 | - Minimum required Node.js version changed from `node: >=10` to `node: >=6` adapting to ecosystem changes 22 | 23 | ## v6.12.0 2023 November 21 24 | 25 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 26 | 27 | ## v6.11.0 2023 November 15 28 | 29 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 30 | 31 | ## v6.10.0 2023 November 14 32 | 33 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 34 | 35 | ## v6.9.0 2023 November 14 36 | 37 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 38 | - Updated license from [`MIT`](http://spdx.org/licenses/MIT.html) to [`Artistic-2.0`](http://spdx.org/licenses/Artistic-2.0.html) 39 | 40 | ## v6.8.0 2021 July 30 41 | 42 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 43 | 44 | ## v6.7.0 2021 July 29 45 | 46 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 47 | 48 | ## v6.6.0 2020 October 29 49 | 50 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 51 | 52 | ## v6.5.0 2020 September 4 53 | 54 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 55 | 56 | ## v6.4.0 2020 August 18 57 | 58 | - Updated for [`get-current-line` v6](https://github.com/bevry/get-current-line), which also returns the character position of the line 59 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 60 | 61 | ## v6.3.0 2020 August 18 62 | 63 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 64 | 65 | ## v6.2.0 2020 August 18 66 | 67 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 68 | 69 | ## v6.1.0 2020 August 4 70 | 71 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 72 | 73 | ## v6.0.2 2020 July 22 74 | 75 | - Updated Transform documentation and renamed `Writeable` internal type to `Pipeable` to reflect its purpose better 76 | 77 | ## v6.0.1 2020 July 22 78 | 79 | - Updated dependencies, fixes Node.js due to missing `semver` dependency under `editions` 80 | - Closes [issue #80](https://github.com/bevry/caterpillar/issues/80) 81 | 82 | ## v6.0.0 2020 July 24 83 | 84 | - Breaking Changes: Caterpillar has been rewritten for performance, ease of use, and deno compatibility (now it is compatible with Node.js, Deno, and Web Browsers) 85 | - The Caterpillar Transforms by Bevry, are now embedded into the `caterpillar` package: 86 | - `caterpillar-filter` is now `import { Filter } from 'caterpillar'` 87 | - `caterpillar-human` is now `import { Human } from 'caterpillar'` 88 | - `caterpillar-browser` is now `import { Browser } from 'caterpillar'` 89 | - `*.create()` aliases for `new *()` are now removed, please just use `new *(config)` 90 | - Each Caterpillar Transform now maintains its own configuration, which is specified via their constructor 91 | - As such, instead of doing `new *().setConfig(opts)` now jsut do `new *(opts)` 92 | - the default log level is now a configuration option, rather than an entry in the levels map 93 | - `level` configuration option has been renamed to `filterLevel`, which must now be specified directly on the filter transform 94 | - the logger transform now accepts a new `lineLevel` configuration option, which will limit fetching line info for only log levels equal to or below that the `lineLevel` value, by default it is `-1` which disables fetching line info 95 | - This is a dramatic performance improvement for large applications, as fetching line levels for every log entry, even ones filtered out, was not performant 96 | - Closes [issue #16](https://github.com/bevry/caterpillar/issues/16) 97 | - Caterpillar Transforms are no longer Node.js Streams, as using them was a major performance overhead 98 | - We can still pipe to Caterpillar Transforms as well as to Node.js streams and WHATWG/Deno streams 99 | - Closes [issue #17](https://github.com/bevry/caterpillar/issues/17) 100 | - Add `error`, `warn`, `info`, `debug` aliases to the logger 101 | - Thanks to [Kirill Chernyshov](https://github.com/DeLaGuardo) for [issue #12](https://github.com/bevry/caterpillar/issues/12) 102 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 103 | 104 | ## v5.15.0 2020 July 22 105 | 106 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 107 | 108 | ## v5.14.0 2020 July 22 109 | 110 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 111 | 112 | ## v5.13.0 2020 July 3 113 | 114 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 115 | 116 | ## v5.12.0 2020 July 3 117 | 118 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 119 | 120 | ## v5.11.0 2020 June 25 121 | 122 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 123 | 124 | ## v5.10.0 2020 June 21 125 | 126 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 127 | 128 | ## v5.9.0 2020 June 21 129 | 130 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 131 | 132 | ## v5.8.0 2020 June 20 133 | 134 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 135 | 136 | ## v5.7.0 2020 June 20 137 | 138 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 139 | 140 | ## v5.6.0 2020 June 10 141 | 142 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 143 | 144 | ## v5.5.0 2020 June 10 145 | 146 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 147 | 148 | ## v5.4.0 2020 May 22 149 | 150 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 151 | 152 | ## v5.3.0 2020 May 21 153 | 154 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 155 | 156 | ## v5.2.0 2020 May 11 157 | 158 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 159 | 160 | ## v5.1.2 2020 May 8 161 | 162 | - Added Logger as a default export, for better compat with the filters 163 | 164 | ## v5.1.1 2020 May 8 165 | 166 | - Fix some types on the Transform class 167 | 168 | ## v5.1.0 2020 May 8 169 | 170 | - Merge source code into a single file, and export the various types 171 | 172 | ## v5.0.0 2020 May 8 173 | 174 | - Converted from JavaScript to TypeScript, no functionality changes here 175 | - Extracted the current line functionality into [get-current-line](https://github.com/bevry/get-current-line), which uses [a different means of calculating the offset](https://github.com/bevry/get-current-line/blob/master/HISTORY.md#v500-2020-may-8) which **you should refer to if you ever used custom offsets** 176 | - Extracted the log level functionality into [rfc-log-levels](https://github.com/bevry/rfc-log-levels), existing functionality is retained 177 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 178 | - Minimum required node version changed from `node: >=8` to `node: >=10` to keep up with mandatory ecosystem changes 179 | 180 | ## v4.0.0 2019 December 1 181 | 182 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 183 | - Minimum required node version changed from `node: >=0.10` to `node: >=8` to keep up with mandatory ecosystem changes 184 | 185 | ## v3.3.0 2019 November 13 186 | 187 | - Updated dependencies, [base files](https://github.com/bevry/base), and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 188 | 189 | ## v3.2.0 2019 January 1 190 | 191 | - Updated [base files](https://github.com/bevry/base) and [editions](https://editions.bevry.me) using [boundation](https://github.com/bevry/boundation) 192 | 193 | ## v3.1.2 2018 September 3 194 | 195 | - Updated base files and [editions](https://github.com/bevry/editions) using [boundation](https://github.com/bevry/boundation) 196 | 197 | ## v3.1.1 2018 August 19 198 | 199 | - Readded support for node 0.10 200 | 201 | ## v3.1.0 2018 August 17 202 | 203 | - Now uses [rfc-log-levels](https://github.com/bevry/log-levels) for the initial log levels 204 | - Moved type linting from flow to jsdoc & typescript, which also results in better documentation for you, and visual studio code intellisense` 205 | - Updated base files and [editions](https://github.com/bevry/editions) using [boundation](https://github.com/bevry/boundation) 206 | 207 | ## v3.0.1 2016 October 20 208 | 209 | - Fixed flow type errors with newer flow versions 210 | 211 | ## v3.0.0 2016 May 4 212 | 213 | - Converted from CoffeeScript to JavaScript 214 | - `.createLogger()` and `.createTransform()` now removed in favour of `Logger.create()` and `Transform.create()` 215 | - `require('caterpillar').create()` alias added 216 | - Logger no longer inherits from Transform 217 | 218 | ## v2.0.9 2015 February 18 219 | 220 | - Fixed an issue when fetching `(new Error()).stack` would fail 221 | - More robust stack parsing 222 | 223 | ## v2.0.8 2015 February 7 224 | 225 | - Updated dependencies 226 | 227 | ## v2.0.7 2013 December 12 228 | 229 | - Use native streams if available, otherwise fallback to [readable-stream](https://npmjs.org/package/readable-stream) 230 | - Repackaged 231 | 232 | ## v2.0.6 2013 October 23 233 | 234 | - `Logger:log` is now permantely bound to the logger instance, for easy passing around 235 | 236 | ## v2.0.5 2013 October 23 237 | 238 | - Added `create` API to make life easier when doing one liners 239 | - Project meta data files are now maintained by [Projectz](https://github.com/bevry/projectz) 240 | - Updated dependencies 241 | 242 | ## v2.0.4 2013 July 23 243 | 244 | - Added `lineOffset` configuration offset to allow you to detect the correct line of the reporting when using wrappers 245 | - Updated dependencies 246 | 247 | ## v2.0.3 2013 May 19 248 | 249 | - iOS support (iOS devices do not have `new Error().stack`) 250 | 251 | ## v2.0.2 2013 May 7 252 | 253 | - Fixed defaulting the log level - Closes [issue #6](https://github.com/bevry/caterpillar/issues/6) reported by [Erik Dasque](https://github.com/edasque) 254 | 255 | ## v2.0.1 2013 April 25 256 | 257 | - Node 0.8 support 258 | 259 | ## v2.0.0 2013 April 25 260 | 261 | - Rewrote using streams 262 | 263 | ## v1.1.4 2013 March 25 264 | 265 | - Repackaged 266 | 267 | ## v1.1.3 2012 October 18 268 | 269 | - Updated cli-color from 0.1 to 0.2 270 | - Make cli-color an optional dependency 271 | 272 | ## v1.1.2 2012 August 10 273 | 274 | - Rejigged directory structure 275 | - Re-added markdown files to npm distribution as they are required for the npm website 276 | 277 | ## v1.1.1 2012 May 4 278 | 279 | - Fixed dependency overwrite 280 | 281 | ## v1.1.0 2012 May 4 282 | 283 | - Caterpillar now pre-compiles, so the coffee-script dependency is no longer needed 284 | 285 | ## v1.0.0 2012 February 11 286 | 287 | - Modularised 288 | - Added [docco](http://jashkenas.github.com/docco/) docs 289 | - Debug line is now only outputted if the log level is 7 290 | - Added `setLevel(level)` 291 | - Added `History.md` 292 | - Added new screenshots 293 | - `cli-color` dependency now accepts revisions 294 | 295 | ## v0.1 2011 September 5 296 | 297 | - Initial commit 298 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # License 4 | 5 | Unless stated otherwise all works are: 6 | 7 | - Copyright © [Benjamin Lupton](https://balupton.com) 8 | 9 | and licensed under: 10 | 11 | - [Artistic License 2.0](http://spdx.org/licenses/Artistic-2.0.html) 12 | 13 | ## The Artistic License 2.0 14 | 15 |
 16 | Copyright (c) 2000-2006, The Perl Foundation.
 17 | 
 18 | Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
 19 | 
 20 | Preamble
 21 | 
 22 | This license establishes the terms under which a given free software Package may be copied, modified, distributed, and/or redistributed. The intent is that the Copyright Holder maintains some artistic control over the development of that Package while still keeping the Package available as open source and free software.
 23 | 
 24 | You are always permitted to make arrangements wholly outside of this license directly with the Copyright Holder of a given Package.  If the terms of this license do not permit the full use that you propose to make of the Package, you should contact the Copyright Holder and seek a different licensing arrangement.
 25 | 
 26 | Definitions
 27 | 
 28 |      "Copyright Holder" means the individual(s) or organization(s) named in the copyright notice for the entire Package.
 29 | 
 30 |      "Contributor" means any party that has contributed code or other material to the Package, in accordance with the Copyright Holder's procedures.
 31 | 
 32 |      "You" and "your" means any person who would like to copy, distribute, or modify the Package.
 33 | 
 34 |      "Package" means the collection of files distributed by the Copyright Holder, and derivatives of that collection and/or of those files. A given Package may consist of either the Standard Version, or a Modified Version.
 35 | 
 36 |      "Distribute" means providing a copy of the Package or making it accessible to anyone else, or in the case of a company or organization, to others outside of your company or organization.
 37 | 
 38 |      "Distributor Fee" means any fee that you charge for Distributing this Package or providing support for this Package to another party.  It does not mean licensing fees.
 39 | 
 40 |      "Standard Version" refers to the Package if it has not been modified, or has been modified only in ways explicitly requested by the Copyright Holder.
 41 | 
 42 |      "Modified Version" means the Package, if it has been changed, and such changes were not explicitly requested by the Copyright Holder.
 43 | 
 44 |      "Original License" means this Artistic License as Distributed with the Standard Version of the Package, in its current version or as it may be modified by The Perl Foundation in the future.
 45 | 
 46 |      "Source" form means the source code, documentation source, and configuration files for the Package.
 47 | 
 48 |      "Compiled" form means the compiled bytecode, object code, binary, or any other form resulting from mechanical transformation or translation of the Source form.
 49 | 
 50 | Permission for Use and Modification Without Distribution
 51 | 
 52 | (1) You are permitted to use the Standard Version and create and use Modified Versions for any purpose without restriction, provided that you do not Distribute the Modified Version.
 53 | 
 54 | Permissions for Redistribution of the Standard Version
 55 | 
 56 | (2) You may Distribute verbatim copies of the Source form of the Standard Version of this Package in any medium without restriction, either gratis or for a Distributor Fee, provided that you duplicate all of the original copyright notices and associated disclaimers.  At your discretion, such verbatim copies may or may not include a Compiled form of the Package.
 57 | 
 58 | (3) You may apply any bug fixes, portability changes, and other modifications made available from the Copyright Holder.  The resulting Package will still be considered the Standard Version, and as such will be subject to the Original License.
 59 | 
 60 | Distribution of Modified Versions of the Package as Source
 61 | 
 62 | (4) You may Distribute your Modified Version as Source (either gratis or for a Distributor Fee, and with or without a Compiled form of the Modified Version) provided that you clearly document how it differs from the Standard Version, including, but not limited to, documenting any non-standard features, executables, or modules, and provided that you do at least ONE of the following:
 63 | 
 64 |      (a) make the Modified Version available to the Copyright Holder of the Standard Version, under the Original License, so that the Copyright Holder may include your modifications in the Standard Version.
 65 |      (b) ensure that installation of your Modified Version does not prevent the user installing or running the Standard Version. In addition, the Modified Version must bear a name that is different from the name of the Standard Version.
 66 |      (c) allow anyone who receives a copy of the Modified Version to make the Source form of the Modified Version available to others under
 67 | 
 68 |           (i) the Original License or
 69 |           (ii) a license that permits the licensee to freely copy, modify and redistribute the Modified Version using the same licensing terms that apply to the copy that the licensee received, and requires that the Source form of the Modified Version, and of any works derived from it, be made freely available in that license fees are prohibited but Distributor Fees are allowed.
 70 | 
 71 | Distribution of Compiled Forms of the Standard Version or Modified Versions without the Source
 72 | 
 73 | (5)  You may Distribute Compiled forms of the Standard Version without the Source, provided that you include complete instructions on how to get the Source of the Standard Version.  Such instructions must be valid at the time of your distribution.  If these instructions, at any time while you are carrying out such distribution, become invalid, you must provide new instructions on demand or cease further distribution. If you provide valid instructions or cease distribution within thirty days after you become aware that the instructions are invalid, then you do not forfeit any of your rights under this license.
 74 | 
 75 | (6)  You may Distribute a Modified Version in Compiled form without the Source, provided that you comply with Section 4 with respect to the Source of the Modified Version.
 76 | 
 77 | Aggregating or Linking the Package
 78 | 
 79 | (7)  You may aggregate the Package (either the Standard Version or Modified Version) with other packages and Distribute the resulting aggregation provided that you do not charge a licensing fee for the Package.  Distributor Fees are permitted, and licensing fees for other components in the aggregation are permitted. The terms of this license apply to the use and Distribution of the Standard or Modified Versions as included in the aggregation.
 80 | 
 81 | (8) You are permitted to link Modified and Standard Versions with other works, to embed the Package in a larger work of your own, or to build stand-alone binary or bytecode versions of applications that include the Package, and Distribute the result without restriction, provided the result does not expose a direct interface to the Package.
 82 | 
 83 | Items That are Not Considered Part of a Modified Version
 84 | 
 85 | (9) Works (including, but not limited to, modules and scripts) that merely extend or make use of the Package, do not, by themselves, cause the Package to be a Modified Version.  In addition, such works are not considered parts of the Package itself, and are not subject to the terms of this license.
 86 | 
 87 | General Provisions
 88 | 
 89 | (10)  Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license.
 90 | 
 91 | (11)  If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license.
 92 | 
 93 | (12)  This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder.
 94 | 
 95 | (13)  This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed.
 96 | 
 97 | (14)  Disclaimer of Warranty:
 98 | THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 99 | 
100 | 101 | 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Caterpillar 4 | 5 | 6 | 7 | 8 | 9 | Status of the GitHub Workflow: bevry 10 | NPM version 11 | NPM downloads 12 |
13 | GitHub Sponsors donate button 14 | ThanksDev donate button 15 | Patreon donate button 16 | Liberapay donate button 17 | Buy Me A Coffee donate button 18 | Open Collective donate button 19 | crypto donate button 20 | PayPal donate button 21 |
22 | Discord server badge 23 | Twitch community badge 24 | 25 | 26 | 27 | 28 | 29 | Caterpillar is the ultimate logging system for Deno, Node.js, and Web Browsers. Log levels are implemented to the RFC standard. Log entries can be filtered and piped to various streams, including coloured output to the terminal, the browser's console, and debug files. You can even write your own transforms. 30 | 31 | 32 | 33 | 34 | ## Usage 35 | 36 | [Complete API Documentation.](http://master.caterpillar.bevry.surge.sh/docs/) 37 | 38 | ### Examples 39 | 40 | - [Deno Example](https://repl.it/@balupton/caterpillar-deno) 41 | - [Node.js Example](https://repl.it/@balupton/caterpillar-node) 42 | - [Web Browser Example](https://repl.it/@balupton/caterpillar-browser) 43 | - [Writing a Custom Transform](https://repl.it/@balupton/caterpillar-custom-transform) 44 | 45 | ### Overview 46 | 47 | The RFC Log Levels are provided by the [`rfc-log-levels` package](https://github.com/bevry/rfc-log-levels) which follows [RFC 3164 - The BSD Syslog Protocol](http://www.faqs.org/rfcs/rfc3164.html). 48 | 49 | [Log Entries](http://master.caterpillar.bevry.surge.sh/docs/interfaces/logentry.html) that are within the [lineLevel](http://master.caterpillar.bevry.surge.sh/docs/classes/logger.html#linelevel) range, will have their line information fetched using the [`get-current-line` package](https://github.com/bevry/get-current-line). 50 | 51 | The [`Logger`](http://master.caterpillar.bevry.surge.sh/docs/classes/logger.html) is what you write your log messages to, which you then pipe to destinations and transforms. 52 | 53 | The [`Filter` transport](http://master.caterpillar.bevry.surge.sh/docs/classes/filter.html) is used to filter out log levels that we do not want to pass onto the next destination. 54 | 55 | The [`Human` transport](http://master.caterpillar.bevry.surge.sh/docs/classes/human.html) is used to convert the Log Entries into a human readable and colourful output. 56 | 57 | The [`Browser` transport](https://github.com/bevry/caterpillar/blob/master/source/transforms/browser.ts) is used to send the human output, including colours, to the Web Browser console. 58 | 59 | The [`Transform`](http://master.caterpillar.bevry.surge.sh/docs/classes/transform.html) is used to write your own transforms, and is what all the others are based from. 60 | 61 | ### Node.js Guide 62 | 63 | To get started for Node.js, setup a new Node.js project for this guide and install Caterpillar. 64 | 65 | ```bash 66 | mkdir caterpillar-guide 67 | cd caterpillar-guide 68 | npm init 69 | npm install --save caterpillar 70 | touch index.js 71 | ``` 72 | 73 | Then edit our `index.js` file with the following, that will output all the log messages in JSON format to stdout, and can be run via `node index.js`: 74 | 75 | ```javascript 76 | const { Logger } = require('caterpillar') 77 | const logger = new Logger() 78 | 79 | logger.pipe(process.stdout) 80 | 81 | logger.log('warn', 'this is a warning, which is level', 4) 82 | logger.warn('this is a warning, which is level', 4) 83 | logger.log('debug', 'this is a debug message, which is level', 7) 84 | logger.warn('this is a debug message, which is level', 7) 85 | ``` 86 | 87 | Outputting in JSON format is not a nice experience, instead we can do better by using the [`Human` transport](http://master.caterpillar.bevry.surge.sh/docs/classes/human.html) such that it is human readable. 88 | 89 | ```javascript 90 | const { Logger, Human } = require('caterpillar') 91 | const logger = new Logger() 92 | 93 | logger.pipe(new Human()).pipe(process.stdout) 94 | 95 | logger.log('warn', 'this is a warning, which is level', 4) 96 | logger.warn('this is a warning, which is level', 4) 97 | logger.log('debug', 'this is a debug message, which is level', 7) 98 | logger.warn('this is a debug message, which is level', 7) 99 | ``` 100 | 101 | However, perhaps we want to still store the JSON format for querying later. We can pipe the human format to stdout as before, but we can pipe the raw output to a debug file. 102 | 103 | ```javascript 104 | const { Logger, Human } = require('caterpillar') 105 | const logger = new Logger() 106 | 107 | const { createWriteStream } = require('fs') 108 | logger.pipe(createWriteStream('./debug.log')) 109 | 110 | logger.pipe(new Human()).pipe(process.stdout) 111 | 112 | logger.log('warn', 'this is a warning, which is level', 4) 113 | logger.warn('this is a warning, which is level', 4) 114 | logger.log('debug', 'this is a debug message, which is level', 7) 115 | logger.warn('this is a debug message, which is level', 7) 116 | ``` 117 | 118 | Now let's stay for some reason, we want to capitalise all the log messages that are warning levels and higher, we can do this by making our own transport by extending the [`Transform`](http://master.caterpillar.bevry.surge.sh/docs/classes/transform.html). 119 | 120 | ```javascript 121 | const { Logger, Transform, Human } = require('caterpillar') 122 | const logger = new Logger() 123 | 124 | const { createWriteStream } = require('fs') 125 | logger.pipe(createWriteStream('./debug.log')) 126 | 127 | class Uppercase extends Transform { 128 | format(entry) { 129 | if (entry.levelNumber <= 4) { 130 | entry.args.forEach(function (value, index) { 131 | if (typeof value === 'string') { 132 | entry.args[index] = value.toUpperCase() 133 | } 134 | }) 135 | } 136 | return entry 137 | } 138 | } 139 | 140 | logger.pipe(new Uppercase()).pipe(new Human()).pipe(process.stdout) 141 | 142 | logger.log('warn', 'this is a warning, which is level', 4) 143 | logger.warn('this is a warning, which is level', 4) 144 | logger.log('debug', 'this is a debug message, which is level', 7) 145 | logger.warn('this is a debug message, which is level', 7) 146 | ``` 147 | 148 | Futhermore, the user probably doesn't need to see debug messages, even though they are useful for debugging. We can filter out the debug messages for the user, but maintain them for the `debug.log` file by applying the [`Filter` transport](http://master.caterpillar.bevry.surge.sh/docs/classes/filter.html) to the pipe that goes to stdout. 149 | 150 | ```javascript 151 | const { Logger, Transform, Filter, Human } = require('caterpillar') 152 | const logger = new Logger() 153 | 154 | const { createWriteStream } = require('fs') 155 | logger.pipe(createWriteStream('./debug.log')) 156 | 157 | class Uppercase extends Transform { 158 | format(entry) { 159 | if (entry.levelNumber <= 4) { 160 | entry.args.forEach(function (value, index) { 161 | if (typeof value === 'string') { 162 | entry.args[index] = value.toUpperCase() 163 | } 164 | }) 165 | } 166 | return entry 167 | } 168 | } 169 | 170 | logger 171 | .pipe(new Filter({ filterLevel: 5 })) 172 | .pipe(new Uppercase()) 173 | .pipe(new Human()) 174 | .pipe(process.stdout) 175 | 176 | logger.log('warn', 'this is a warning, which is level', 4) 177 | logger.warn('this is a warning, which is level', 4) 178 | logger.log('debug', 'this is a debug message, which is level', 7) 179 | logger.warn('this is a debug message, which is level', 7) 180 | ``` 181 | 182 | As fetching line information is computationally expensive process, for large applications for performance we probably only want to fetch the line information for messages that we actually show to the user. As such, we should make the [`filterLevel`](http://master.caterpillar.bevry.surge.sh/docs/classes/filter.html#filterlevel) and the [`lineLevel`](http://master.caterpillar.bevry.surge.sh/docs/classes/logger.html#linelevel) the same. 183 | 184 | ```javascript 185 | const { Logger, Transform, Filter, Human } = require('caterpillar') 186 | const level = 5 187 | const logger = new Logger({ lineLevel: level }) 188 | 189 | const { createWriteStream } = require('fs') 190 | logger.pipe(createWriteStream('./debug.log')) 191 | 192 | class Uppercase extends Transform { 193 | format(entry) { 194 | if (entry.levelNumber <= 4) { 195 | entry.args.forEach(function (value, index) { 196 | if (typeof value === 'string') { 197 | entry.args[index] = value.toUpperCase() 198 | } 199 | }) 200 | } 201 | return entry 202 | } 203 | } 204 | 205 | logger 206 | .pipe(new Filter({ filterLevel: 5 })) 207 | .pipe(new Uppercase()) 208 | .pipe(new Human()) 209 | .pipe(process.stdout) 210 | 211 | logger.log('warn', 'this is a warning, which is level', 4) 212 | logger.warn('this is a warning, which is level', 4) 213 | logger.log('debug', 'this is a debug message, which is level', 7) 214 | logger.warn('this is a debug message, which is level', 7) 215 | ``` 216 | 217 | Finally, if we are using Caterpillar in web browser environments, instead of Node.js, instead of doing: 218 | 219 | ```javascript 220 | const { Logger, Transform, Filter, Human } = require('caterpillar') 221 | // ... 222 | logger.pipe(new Human()).pipe(process.stdout) 223 | // ... 224 | ``` 225 | 226 | We would pipe to the Browser transform instead of to stdout. 227 | 228 | ```javascript 229 | const { Logger, Transform, Filter, Human, Browser } = require('caterpillar') 230 | // ... 231 | logger.pipe(new Human()).pipe(new Browser()) 232 | // ... 233 | ``` 234 | 235 | With this, you now have enough information to leverage the cross-platform power of Caterpillar for most purposes, and the power to write your own custom transforms which can be published as their own packages and shared. 236 | 237 | 238 | 239 | ## Install 240 | 241 | ### [npm](https://npmjs.com "npm is a package manager for javascript") 242 | 243 | - Install: `npm install --save caterpillar` 244 | - Import: `import * as pkg from ('caterpillar')` 245 | - Require: `const pkg = require('caterpillar')` 246 | 247 | ### [Deno](https://deno.land "Deno is a secure runtime for JavaScript and TypeScript, it is an alternative for Node.js") 248 | 249 | ``` typescript 250 | import * as pkg from 'https://unpkg.com/caterpillar@^8.2.0/edition-deno/index.ts' 251 | ``` 252 | ### [Skypack](https://www.skypack.dev "Skypack is a JavaScript Delivery Network for modern web apps") 253 | 254 | ``` html 255 | 258 | ``` 259 | ### [unpkg](https://unpkg.com "unpkg is a fast, global content delivery network for everything on npm") 260 | 261 | ``` html 262 | 265 | ``` 266 | ### [jspm](https://jspm.io "Native ES Modules CDN") 267 | 268 | ``` html 269 | 272 | ``` 273 | ### [Editions](https://editions.bevry.me "Editions are the best way to produce and consume packages you care about.") 274 | 275 | This package is published with the following editions: 276 | - `caterpillar` aliases `caterpillar/index.cjs` which uses the [Editions Autoloader](https://github.com/bevry/editions "You can use the Editions Autoloader to autoload the appropriate edition for your consumers environment") to automatically select the correct edition for the consumer's environment 277 | - `caterpillar/source/index.ts` is [TypeScript](https://www.typescriptlang.org/ "TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.") source code with [Import](https://babeljs.io/docs/learn-es2015/#modules "ECMAScript Modules") for modules 278 | - `caterpillar/edition-browsers/index.js` is [TypeScript](https://www.typescriptlang.org/ "TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.") compiled against [ES2022](https://en.wikipedia.org/wiki/ES2022 "ECMAScript 2022") for web browsers with [Import](https://babeljs.io/docs/learn-es2015/#modules "ECMAScript Modules") for modules 279 | - `caterpillar/edition-es2022/index.js` is [TypeScript](https://www.typescriptlang.org/ "TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.") compiled against [ES2022](https://en.wikipedia.org/wiki/ES2022 "ECMAScript 2022") for [Node.js](https://nodejs.org "Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine") 14 || 16 || 18 || 20 || 21 with [Require](https://nodejs.org/dist/latest-v5.x/docs/api/modules.html "Node/CJS Modules") for modules 280 | - `caterpillar/edition-es2017/index.js` is [TypeScript](https://www.typescriptlang.org/ "TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.") compiled against [ES2017](https://en.wikipedia.org/wiki/ES2017 "ECMAScript 2017") for [Node.js](https://nodejs.org "Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine") 8 || 10 || 12 || 14 || 16 || 18 || 20 || 21 with [Require](https://nodejs.org/dist/latest-v5.x/docs/api/modules.html "Node/CJS Modules") for modules 281 | - `caterpillar/edition-es2015/index.js` is [TypeScript](https://www.typescriptlang.org/ "TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.") compiled against [ES2015](https://babeljs.io/docs/en/learn#ecmascript-2015-features "ECMAScript 2015") for [Node.js](https://nodejs.org "Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine") 6 || 8 || 10 || 12 || 14 || 16 || 18 || 20 || 21 with [Require](https://nodejs.org/dist/latest-v5.x/docs/api/modules.html "Node/CJS Modules") for modules 282 | - `caterpillar/edition-es5/index.js` is [TypeScript](https://www.typescriptlang.org/ "TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.") compiled against ES5 for [Node.js](https://nodejs.org "Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine") 4 || 6 || 8 || 10 || 12 || 14 || 16 with [Require](https://nodejs.org/dist/latest-v5.x/docs/api/modules.html "Node/CJS Modules") for modules 283 | - `caterpillar/edition-es2017-esm/index.js` is [TypeScript](https://www.typescriptlang.org/ "TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.") compiled against [ES2017](https://en.wikipedia.org/wiki/ES2017 "ECMAScript 2017") for [Node.js](https://nodejs.org "Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine") 12 || 14 || 16 || 18 || 20 || 21 with [Import](https://babeljs.io/docs/learn-es2015/#modules "ECMAScript Modules") for modules 284 | - `caterpillar/edition-types/index.d.ts` is [TypeScript](https://www.typescriptlang.org/ "TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.") compiled Types with [Import](https://babeljs.io/docs/learn-es2015/#modules "ECMAScript Modules") for modules 285 | - `caterpillar/edition-deno/index.ts` is [TypeScript](https://www.typescriptlang.org/ "TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.") source code made to be compatible with [Deno](https://deno.land "Deno is a secure runtime for JavaScript and TypeScript, it is an alternative to Node.js") 286 | 287 | 288 | 289 | 290 | 291 | ## History 292 | 293 | [Discover the release history by heading on over to the `HISTORY.md` file.](https://github.com/bevry/caterpillar/blob/HEAD/HISTORY.md#files) 294 | 295 | 296 | 297 | 298 | 299 | ## Backers 300 | 301 | ### Code 302 | 303 | [Discover how to contribute via the `CONTRIBUTING.md` file.](https://github.com/bevry/caterpillar/blob/HEAD/CONTRIBUTING.md#files) 304 | 305 | #### Authors 306 | 307 | - [Benjamin Lupton](https://balupton.com) — Accelerating collaborative wisdom. 308 | 309 | #### Maintainers 310 | 311 | - [Benjamin Lupton](https://balupton.com) — Accelerating collaborative wisdom. 312 | 313 | #### Contributors 314 | 315 | - [Benjamin Lupton](https://github.com/balupton) — [view contributions](https://github.com/bevry/caterpillar/commits?author=balupton "View the GitHub contributions of Benjamin Lupton on repository bevry/caterpillar") 316 | - [t-visualappeal](https://github.com/t-visualappeal) — [view contributions](https://github.com/bevry/caterpillar/commits?author=t-visualappeal "View the GitHub contributions of t-visualappeal on repository bevry/caterpillar") 317 | - [Tim Helfensdörfer](https://github.com/thelfensdrfer) — [view contributions](https://github.com/bevry/caterpillar/commits?author=thelfensdrfer "View the GitHub contributions of Tim Helfensdörfer on repository bevry/caterpillar") 318 | 319 | ### Finances 320 | 321 | GitHub Sponsors donate button 322 | ThanksDev donate button 323 | Patreon donate button 324 | Liberapay donate button 325 | Buy Me A Coffee donate button 326 | Open Collective donate button 327 | crypto donate button 328 | PayPal donate button 329 | 330 | #### Sponsors 331 | 332 | - [Andrew Nesbitt](https://nesbitt.io) — Software engineer and researcher 333 | - [Balsa](https://balsa.com) — We're Balsa, and we're building tools for builders. 334 | - [Codecov](https://codecov.io) — Empower developers with tools to improve code quality and testing. 335 | - [Poonacha Medappa](https://poonachamedappa.com) 336 | - [Rob Morris](https://github.com/Rob-Morris) 337 | - [Sentry](https://sentry.io) — Real-time crash reporting for your web apps, mobile apps, and games. 338 | - [Syntax](https://syntax.fm) — Syntax Podcast 339 | 340 | #### Donors 341 | 342 | - [Andrew Nesbitt](https://nesbitt.io) 343 | - [Armen Mkrtchian](https://mogoni.dev) 344 | - [Balsa](https://balsa.com) 345 | - [Chad](https://opencollective.com/chad8) 346 | - [Codecov](https://codecov.io) 347 | - [dr.dimitru](https://veliovgroup.com) 348 | - [Elliott Ditman](https://elliottditman.com) 349 | - [entroniq](https://gitlab.com/entroniq) 350 | - [GitHub](https://github.com/about) 351 | - [Hunter Beast](https://cryptoquick.com) 352 | - [Jean-Luc Geering](https://github.com/jlgeering) 353 | - [Michael Duane Mooring](https://mdm.cc) 354 | - [Michael Harry Scepaniak](https://michaelscepaniak.com) 355 | - [Mohammed Shah](https://github.com/smashah) 356 | - [Mr. Henry](https://mrhenry.be) 357 | - [Nermal](https://arjunaditya.vercel.app) 358 | - [Pleo](https://pleo.io) 359 | - [Poonacha Medappa](https://poonachamedappa.com) 360 | - [Rob Morris](https://github.com/Rob-Morris) 361 | - [Robert de Forest](https://github.com/rdeforest) 362 | - [Sentry](https://sentry.io) 363 | - [ServieJS](https://github.com/serviejs) 364 | - [Skunk Team](https://skunk.team) 365 | - [Syntax](https://syntax.fm) 366 | - [WriterJohnBuck](https://github.com/WriterJohnBuck) 367 | 368 | 369 | 370 | 371 | 372 | ## License 373 | 374 | Unless stated otherwise all works are: 375 | 376 | - Copyright © [Benjamin Lupton](https://balupton.com) 377 | 378 | and licensed under: 379 | 380 | - [Artistic License 2.0](http://spdx.org/licenses/Artistic-2.0.html) 381 | 382 | 383 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Security Practices 4 | 5 | This project meets standardized secure software development practices, including 2FA for all members, password managers with monitoring, secure secret retrieval instead of storage. [Learn about our practices.](https://tidelift.com/funding/github/npm/caterpillar) 6 | 7 | ## Supported Versions 8 | 9 | This project uses [Bevry's automated tooling](https://github.com/bevry/boundation) to deliver the latest updates, fixes, and improvements inside the latest release while still maintaining widespread ecosystem compatibility. 10 | 11 | [Refer to supported ecosystem versions: `Editions` section in `README.md`](https://github.com/bevry/caterpillar/blob/master/README.md#Editions) 12 | 13 | [Refer to automated support of ecosystem versions: `boundation` entries in `HISTORY.md`](https://github.com/bevry/caterpillar/blob/master/HISTORY.md) 14 | 15 | Besides testing and verification, out CI also [auto-merges](https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions) [Dependabot security updates](https://docs.github.com/en/code-security/dependabot/dependabot-security-updates/about-dependabot-security-updates) and [auto-publishes](https://github.com/bevry-actions/npm) successful builds of the [`master` branch](https://github.com/bevry/wait/actions?query=branch%3Amaster) to the [`next` version tag](https://www.npmjs.com/package/caterpillar?activeTab=versions), offering immediate resolutions before scheduled maintenance releases. 16 | 17 | ## Reporting a Vulnerability 18 | 19 | [Report the vulnerability to the project owners.](https://github.com/bevry/caterpillar/security/advisories) 20 | 21 | [Report the vulnerability to Tidelift.](https://tidelift.com/security) 22 | -------------------------------------------------------------------------------- /index.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // auto-generated by boundation, do not update manually 3 | /** @type {typeof import("./edition-types/index.d.ts") } */ 4 | module.exports = require('editions').requirePackage(__dirname, require, 'index.js') -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Caterpillar", 3 | "name": "caterpillar", 4 | "version": "8.2.0", 5 | "license": "Artistic-2.0", 6 | "description": "Caterpillar is the ultimate logging system for Deno, Node.js, and Web Browsers. Log levels are implemented to the RFC standard. Log entries can be filtered and piped to various streams, including coloured output to the terminal, the browser's console, and debug files. You can even write your own transforms.", 7 | "homepage": "https://github.com/bevry/caterpillar", 8 | "funding": "https://bevry.me/fund", 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/bevry/caterpillar.git" 12 | }, 13 | "bugs": { 14 | "url": "https://github.com/bevry/caterpillar/issues" 15 | }, 16 | "keywords": [ 17 | "browser", 18 | "caterpillar", 19 | "console", 20 | "debug", 21 | "deno", 22 | "deno-edition", 23 | "deno-entry", 24 | "denoland", 25 | "dom", 26 | "es2015", 27 | "es2017", 28 | "es2022", 29 | "es5", 30 | "log", 31 | "logger", 32 | "logging", 33 | "module", 34 | "node", 35 | "stream", 36 | "transform", 37 | "typed", 38 | "types", 39 | "typescript" 40 | ], 41 | "badges": { 42 | "list": [ 43 | "githubworkflow", 44 | "npmversion", 45 | "npmdownloads", 46 | "---", 47 | "githubsponsors", 48 | "thanksdev", 49 | "patreon", 50 | "liberapay", 51 | "buymeacoffee", 52 | "opencollective", 53 | "crypto", 54 | "paypal", 55 | "---", 56 | "discord", 57 | "twitch" 58 | ], 59 | "config": { 60 | "githubWorkflow": "bevry", 61 | "githubSponsorsUsername": "balupton", 62 | "thanksdevGithubUsername": "bevry", 63 | "buymeacoffeeUsername": "balupton", 64 | "cryptoURL": "https://bevry.me/crypto", 65 | "flattrUsername": "balupton", 66 | "liberapayUsername": "bevry", 67 | "opencollectiveUsername": "bevry", 68 | "patreonUsername": "bevry", 69 | "paypalURL": "https://bevry.me/paypal", 70 | "wishlistURL": "https://bevry.me/wishlist", 71 | "discordServerID": "1147436445783560193", 72 | "discordServerInvite": "nQuXddV7VP", 73 | "twitchUsername": "balupton", 74 | "githubUsername": "bevry", 75 | "githubRepository": "caterpillar", 76 | "githubSlug": "bevry/caterpillar", 77 | "npmPackageName": "caterpillar" 78 | } 79 | }, 80 | "author": "Benjamin Lupton (https://balupton.com) (https://github.com/balupton)", 81 | "authors": [ 82 | "Benjamin Lupton (https://balupton.com) (https://github.com/balupton): Accelerating collaborative wisdom." 83 | ], 84 | "maintainers": [ 85 | "Benjamin Lupton (https://balupton.com) (https://github.com/balupton): Accelerating collaborative wisdom." 86 | ], 87 | "contributors": [ 88 | "Benjamin Lupton (https://balupton.com) (https://github.com/balupton)", 89 | "t-visualappeal (https://github.com/t-visualappeal)", 90 | "Tim Helfensdörfer (https://thelfensdrfer.de) (https://github.com/thelfensdrfer)" 91 | ], 92 | "sponsors": [ 93 | "Andrew Nesbitt (https://nesbitt.io) (https://github.com/andrew): Software engineer and researcher", 94 | "Balsa (https://balsa.com) (https://github.com/balsa): We're Balsa, and we're building tools for builders.", 95 | "Codecov (https://codecov.io) (https://github.com/codecov): Empower developers with tools to improve code quality and testing.", 96 | "Poonacha Medappa (https://poonachamedappa.com) (https://github.com/km-Poonacha)", 97 | "Rob Morris (https://github.com/Rob-Morris)", 98 | "Sentry (https://sentry.io) (https://github.com/getsentry): Real-time crash reporting for your web apps, mobile apps, and games.", 99 | "Syntax (https://syntax.fm) (https://github.com/syntaxfm): Syntax Podcast" 100 | ], 101 | "donors": [ 102 | "Andrew Nesbitt (https://nesbitt.io) (https://github.com/andrew)", 103 | "Armen Mkrtchian (https://mogoni.dev) (https://github.com/Armenm)", 104 | "Balsa (https://balsa.com) (https://github.com/balsa)", 105 | "Chad (https://opencollective.com/chad8)", 106 | "Codecov (https://codecov.io) (https://github.com/codecov)", 107 | "dr.dimitru (https://veliovgroup.com) (https://github.com/dr-dimitru)", 108 | "Elliott Ditman (https://elliottditman.com) (https://github.com/elliottditman)", 109 | "entroniq (https://gitlab.com/entroniq) (https://thanks.dev/d/gl/entroniq)", 110 | "GitHub (https://github.com/about) (https://github.com/github)", 111 | "Hunter Beast (https://cryptoquick.com) (https://github.com/cryptoquick)", 112 | "Jean-Luc Geering (https://github.com/jlgeering) (https://opencollective.com/jlgeering) (https://twitter.com/jlgeering)", 113 | "Michael Duane Mooring (https://mdm.cc) (https://github.com/mikeumus) (https://opencollective.com/mikeumus) (https://twitter.com/mikeumus)", 114 | "Michael Harry Scepaniak (https://michaelscepaniak.com) (https://github.com/hispanic)", 115 | "Mohammed Shah (https://github.com/smashah) (https://thanks.dev/d/gh/smashah) (https://twitter.com/smashah)", 116 | "Mr. Henry (https://mrhenry.be) (https://github.com/mrhenry)", 117 | "Nermal (https://arjunaditya.vercel.app) (https://github.com/nermalcat69)", 118 | "Pleo (https://pleo.io) (https://github.com/pleo-io)", 119 | "Poonacha Medappa (https://poonachamedappa.com) (https://github.com/km-Poonacha)", 120 | "Rob Morris (https://github.com/Rob-Morris)", 121 | "Robert de Forest (https://github.com/rdeforest)", 122 | "Sentry (https://sentry.io) (https://github.com/getsentry)", 123 | "ServieJS (https://github.com/serviejs) (https://thanks.dev/d/gh/serviejs)", 124 | "Skunk Team (https://skunk.team) (https://github.com/skunkteam)", 125 | "Syntax (https://syntax.fm) (https://github.com/syntaxfm)", 126 | "WriterJohnBuck (https://github.com/WriterJohnBuck)" 127 | ], 128 | "engines": { 129 | "node": ">=4" 130 | }, 131 | "editions": [ 132 | { 133 | "description": "TypeScript source code with Import for modules", 134 | "directory": "source", 135 | "entry": "index.ts", 136 | "tags": [ 137 | "source", 138 | "typescript", 139 | "import" 140 | ], 141 | "engines": false 142 | }, 143 | { 144 | "description": "TypeScript compiled against ES2022 for web browsers with Import for modules", 145 | "directory": "edition-browsers", 146 | "entry": "index.js", 147 | "tags": [ 148 | "compiled", 149 | "javascript", 150 | "import" 151 | ], 152 | "engines": { 153 | "node": false, 154 | "browsers": "defaults" 155 | } 156 | }, 157 | { 158 | "description": "TypeScript compiled against ES2022 for Node.js 14 || 16 || 18 || 20 || 21 with Require for modules", 159 | "directory": "edition-es2022", 160 | "entry": "index.js", 161 | "tags": [ 162 | "compiled", 163 | "javascript", 164 | "es2022", 165 | "require" 166 | ], 167 | "engines": { 168 | "node": "14 || 16 || 18 || 20 || 21", 169 | "browsers": false 170 | } 171 | }, 172 | { 173 | "description": "TypeScript compiled against ES2017 for Node.js 8 || 10 || 12 || 14 || 16 || 18 || 20 || 21 with Require for modules", 174 | "directory": "edition-es2017", 175 | "entry": "index.js", 176 | "tags": [ 177 | "compiled", 178 | "javascript", 179 | "es2017", 180 | "require" 181 | ], 182 | "engines": { 183 | "node": "8 || 10 || 12 || 14 || 16 || 18 || 20 || 21", 184 | "browsers": false 185 | } 186 | }, 187 | { 188 | "description": "TypeScript compiled against ES2015 for Node.js 6 || 8 || 10 || 12 || 14 || 16 || 18 || 20 || 21 with Require for modules", 189 | "directory": "edition-es2015", 190 | "entry": "index.js", 191 | "tags": [ 192 | "compiled", 193 | "javascript", 194 | "es2015", 195 | "require" 196 | ], 197 | "engines": { 198 | "node": "6 || 8 || 10 || 12 || 14 || 16 || 18 || 20 || 21", 199 | "browsers": false 200 | } 201 | }, 202 | { 203 | "description": "TypeScript compiled against ES5 for Node.js 4 || 6 || 8 || 10 || 12 || 14 || 16 with Require for modules", 204 | "directory": "edition-es5", 205 | "entry": "index.js", 206 | "tags": [ 207 | "compiled", 208 | "javascript", 209 | "es5", 210 | "require" 211 | ], 212 | "engines": { 213 | "node": "4 || 6 || 8 || 10 || 12 || 14 || 16", 214 | "browsers": false 215 | } 216 | }, 217 | { 218 | "description": "TypeScript compiled against ES2017 for Node.js 12 || 14 || 16 || 18 || 20 || 21 with Import for modules", 219 | "directory": "edition-es2017-esm", 220 | "entry": "index.js", 221 | "tags": [ 222 | "compiled", 223 | "javascript", 224 | "es2017", 225 | "import" 226 | ], 227 | "engines": { 228 | "node": "12 || 14 || 16 || 18 || 20 || 21", 229 | "browsers": false 230 | } 231 | }, 232 | { 233 | "description": "TypeScript compiled Types with Import for modules", 234 | "directory": "edition-types", 235 | "entry": "index.d.ts", 236 | "tags": [ 237 | "compiled", 238 | "types", 239 | "import" 240 | ], 241 | "engines": false 242 | }, 243 | { 244 | "description": "TypeScript source code made to be compatible with Deno", 245 | "directory": "edition-deno", 246 | "entry": "index.ts", 247 | "tags": [ 248 | "typescript", 249 | "import", 250 | "deno" 251 | ], 252 | "engines": { 253 | "deno": true, 254 | "browsers": true 255 | } 256 | } 257 | ], 258 | "types": "edition-types/index.d.ts", 259 | "type": "module", 260 | "main": "index.cjs", 261 | "exports": { 262 | "node": { 263 | "types": "./edition-types/index.d.ts", 264 | "import": "./edition-es2017-esm/index.js", 265 | "default": "./index.cjs", 266 | "require": "./edition-es2022/index.js" 267 | }, 268 | "browser": { 269 | "types": "./edition-types/index.d.ts", 270 | "import": "./edition-browsers/index.js" 271 | } 272 | }, 273 | "deno": "edition-deno/index.ts", 274 | "browser": "edition-browsers/index.js", 275 | "module": "edition-browsers/index.js", 276 | "dependencies": { 277 | "@bevry/ansi": "^6.9.0", 278 | "editions": "^6.21.0", 279 | "get-current-line": "^7.3.0", 280 | "rfc-log-levels": "^4.2.0" 281 | }, 282 | "devDependencies": { 283 | "@types/node": "^20.10.5", 284 | "@typescript-eslint/eslint-plugin": "^6.16.0", 285 | "@typescript-eslint/parser": "^6.16.0", 286 | "assert-helpers": "^11.12.0", 287 | "eslint": "^8.56.0", 288 | "eslint-config-bevry": "^5.3.0", 289 | "eslint-config-prettier": "^9.1.0", 290 | "eslint-plugin-prettier": "^5.1.2", 291 | "kava": "^7.8.0", 292 | "make-deno-edition": "^2.2.0", 293 | "prettier": "^3.1.1", 294 | "projectz": "^4.1.1", 295 | "typedoc": "^0.25.4", 296 | "typescript": "5.3.3", 297 | "valid-directory": "^4.8.0", 298 | "valid-module": "^2.6.0" 299 | }, 300 | "scripts": { 301 | "our:clean": "rm -rf ./docs ./edition* ./es2015 ./es5 ./out ./.next", 302 | "our:compile": "npm run our:compile:deno && npm run our:compile:edition-browsers && npm run our:compile:edition-es2015 && npm run our:compile:edition-es2017 && npm run our:compile:edition-es2017-esm && npm run our:compile:edition-es2022 && npm run our:compile:edition-es5 && npm run our:compile:edition-types", 303 | "our:compile:deno": "make-deno-edition --attempt", 304 | "our:compile:edition-browsers": "tsc --module ESNext --target ES2022 --outDir ./edition-browsers --project tsconfig.json && ( test ! -d edition-browsers/source || ( mv edition-browsers/source edition-temp && rm -rf edition-browsers && mv edition-temp edition-browsers ) )", 305 | "our:compile:edition-es2015": "tsc --module commonjs --target ES2015 --outDir ./edition-es2015 --project tsconfig.json && ( test ! -d edition-es2015/source || ( mv edition-es2015/source edition-temp && rm -rf edition-es2015 && mv edition-temp edition-es2015 ) ) && printf '%s' '{\"type\": \"commonjs\"}' > edition-es2015/package.json", 306 | "our:compile:edition-es2017": "tsc --module commonjs --target ES2017 --outDir ./edition-es2017 --project tsconfig.json && ( test ! -d edition-es2017/source || ( mv edition-es2017/source edition-temp && rm -rf edition-es2017 && mv edition-temp edition-es2017 ) ) && printf '%s' '{\"type\": \"commonjs\"}' > edition-es2017/package.json", 307 | "our:compile:edition-es2017-esm": "tsc --module ESNext --target ES2017 --outDir ./edition-es2017-esm --project tsconfig.json && ( test ! -d edition-es2017-esm/source || ( mv edition-es2017-esm/source edition-temp && rm -rf edition-es2017-esm && mv edition-temp edition-es2017-esm ) ) && printf '%s' '{\"type\": \"module\"}' > edition-es2017-esm/package.json", 308 | "our:compile:edition-es2022": "tsc --module commonjs --target ES2022 --outDir ./edition-es2022 --project tsconfig.json && ( test ! -d edition-es2022/source || ( mv edition-es2022/source edition-temp && rm -rf edition-es2022 && mv edition-temp edition-es2022 ) ) && printf '%s' '{\"type\": \"commonjs\"}' > edition-es2022/package.json", 309 | "our:compile:edition-es5": "tsc --module commonjs --target ES5 --outDir ./edition-es5 --project tsconfig.json && ( test ! -d edition-es5/source || ( mv edition-es5/source edition-temp && rm -rf edition-es5 && mv edition-temp edition-es5 ) ) && printf '%s' '{\"type\": \"commonjs\"}' > edition-es5/package.json", 310 | "our:compile:edition-types": "tsc --emitDeclarationOnly --declaration --declarationMap --declarationDir ./edition-types --project tsconfig.json && ( test ! -d edition-types/source || ( mv edition-types/source edition-temp && rm -rf edition-types && mv edition-temp edition-types ) )", 311 | "our:deploy": "printf '%s\n' 'no need for this project'", 312 | "our:meta": "npm run our:meta:docs && npm run our:meta:projectz", 313 | "our:meta:docs": "npm run our:meta:docs:typedoc", 314 | "our:meta:docs:typedoc": "rm -rf ./docs && typedoc --exclude '**/+(*test*|node_modules)' --excludeExternals --out ./docs ./source", 315 | "our:meta:projectz": "projectz --offline", 316 | "our:release": "npm run our:release:prepare && npm run our:release:check-changelog && npm run our:release:check-dirty && npm run our:release:tag && npm run our:release:push", 317 | "our:release:check-changelog": "cat ./HISTORY.md | grep \"v$npm_package_version\" || (printf '%s\n' \"add a changelog entry for v$npm_package_version\" && exit -1)", 318 | "our:release:check-dirty": "git diff --exit-code", 319 | "our:release:prepare": "npm run our:clean && npm run our:compile && npm run our:test && npm run our:meta", 320 | "our:release:push": "git push origin && git push origin --tags", 321 | "our:release:tag": "export MESSAGE=$(cat ./HISTORY.md | sed -n \"/## v$npm_package_version/,/##/p\" | sed 's/## //' | awk 'NR>1{print buf}{buf = $0}') && test \"$MESSAGE\" || (printf '%s\n' 'proper changelog entry not found' && exit -1) && git tag \"v$npm_package_version\" -am \"$MESSAGE\"", 322 | "our:setup": "npm run our:setup:install", 323 | "our:setup:install": "npm install", 324 | "our:test": "npm run our:verify && npm test", 325 | "our:verify": "npm run our:verify:eslint && npm run our:verify:module && npm run our:verify:prettier", 326 | "our:verify:eslint": "eslint --fix --ignore-pattern '**/*.d.ts' --ignore-pattern '**/vendor/' --ignore-pattern '**/node_modules/' --ext .mjs,.js,.jsx,.ts,.tsx ./source", 327 | "our:verify:module": "valid-module", 328 | "our:verify:prettier": "prettier --write .", 329 | "test": "node ./test.cjs" 330 | }, 331 | "boundation": { 332 | "dom": true 333 | }, 334 | "eslintConfig": { 335 | "extends": [ 336 | "bevry" 337 | ], 338 | "rules": { 339 | "class-methods-use-this": "off" 340 | } 341 | }, 342 | "prettier": { 343 | "semi": false, 344 | "singleQuote": true, 345 | "trailingComma": "es5", 346 | "endOfLine": "lf" 347 | } 348 | } 349 | -------------------------------------------------------------------------------- /source/index.ts: -------------------------------------------------------------------------------- 1 | export * from './transform.js' 2 | export * from './logger.js' 3 | export * from './transforms/filter.js' 4 | export * from './transforms/human.js' 5 | export * from './transforms/browser.js' 6 | -------------------------------------------------------------------------------- /source/logger.ts: -------------------------------------------------------------------------------- 1 | import getLogLevel, { rfcLogLevels, LevelInfo, LevelsMap } from 'rfc-log-levels' 2 | import getCurrentLine, { Offset, Location } from 'get-current-line' 3 | import { Transform } from './transform.js' 4 | 5 | /** The log entry that Caterpillar creates and forwards to its transforms */ 6 | export interface LogEntry extends LevelInfo, Location { 7 | /** the iso string of when the log occured */ 8 | date: string 9 | /** all the arguments that were after the log level */ 10 | args: any[] 11 | } 12 | 13 | /** Configuration for the Caterpillar Logger */ 14 | export interface LoggerOptions { 15 | /** Use to override the default value of {@link Logger.lineOffset} */ 16 | lineOffset?: Offset 17 | 18 | /** Use to override the default value of {@link Logger.levels} */ 19 | levels?: LevelsMap 20 | 21 | /** Use to override the default value of {@link Logger.defaultLevelInfo} */ 22 | defaultLevel?: number | string 23 | 24 | /** Use to override the default value of {@link Logger.lineLevel} */ 25 | lineLevel?: number 26 | } 27 | 28 | /** 29 | * Logger. 30 | * This is what we write to. 31 | * @example Creation 32 | * ``` javascript 33 | * // Via class 34 | * import { Logger } from 'caterpillar' 35 | * const logger = new Logger() 36 | * ``` 37 | */ 38 | export class Logger extends Transform { 39 | /** 40 | * The configuration to use for the line offset. 41 | * This defaults to any file path that includes `logger`, and any method that includes the word `log`. 42 | */ 43 | public lineOffset: Offset = { 44 | file: /logger/i, 45 | method: /log/i, 46 | } 47 | 48 | /** 49 | * The mapping of log level names to log level numbers. 50 | * Defaults to the RFC Log Level configuration. 51 | */ 52 | public levels: LevelsMap = rfcLogLevels 53 | 54 | /** 55 | * The log level information to use when the log level was unable to be determined. 56 | * Defaults to the info log level. 57 | */ 58 | public defaultLevelInfo!: LevelInfo 59 | 60 | /** Set the default level info via a level number or name. */ 61 | public set defaultLevel(value: number | string) { 62 | const levelInfo = this.getLogLevel(value) 63 | if (levelInfo == null) { 64 | throw new Error( 65 | `caterpillar: the intended value of ${value} for the default log level not found in the configured levels` 66 | ) 67 | } 68 | this.defaultLevelInfo = levelInfo 69 | } 70 | 71 | /** 72 | * Only fetch line information for entries that have a log level equal to, or below this number. 73 | * You should only specify this if you need it, as fFetching line information for thousands of log entries, which is typical in large applications, will slow your application down dramatically. 74 | * If not specified, defaults to `-Infinity` which effect is to ignore gathering line information for all log levels. 75 | */ 76 | public lineLevel: number = -Infinity 77 | 78 | /** Create our instance and apply our configuraiton options. */ 79 | constructor(opts?: LoggerOptions) { 80 | super() 81 | 82 | // options 83 | if (opts?.lineOffset != null) this.lineOffset = opts.lineOffset 84 | if (opts?.levels != null) this.levels = opts.levels 85 | if (opts?.lineLevel != null) this.lineLevel = opts.lineLevel 86 | 87 | // options: default level 88 | this.defaultLevel = opts?.defaultLevel ?? 'info' 89 | 90 | // dereference 91 | this.levels = Object.assign({}, this.levels) 92 | } 93 | 94 | /** Alias for {@link getLogLevel} using the configured logger levels as reference. */ 95 | getLogLevel(value: number | string) { 96 | return getLogLevel(value, this.levels) 97 | } 98 | 99 | /** Takes an arguments array and tranforms it into a log entry. */ 100 | format(args: any): LogEntry { 101 | // fetch the level 102 | const level = args.shift() 103 | let levelInfo = 104 | level === 'default' ? this.defaultLevelInfo : this.getLogLevel(level) 105 | if (levelInfo == null) { 106 | // fallback to the default log level 107 | levelInfo = this.defaultLevelInfo 108 | // as the level (first param) was not actually a level, put it back 109 | args.unshift(level) 110 | } 111 | 112 | // fetch the date 113 | const date = new Date().toISOString() 114 | 115 | // fetch the line information 116 | const lineInfo = 117 | levelInfo.levelNumber <= this.lineLevel 118 | ? getCurrentLine(this.lineOffset) 119 | : { 120 | line: -1, 121 | char: -1, 122 | method: '', 123 | file: '', 124 | } 125 | 126 | // put it all together 127 | return Object.assign({ date, args }, levelInfo, lineInfo) 128 | } 129 | 130 | /** 131 | * Log the arguments into the logger stream as formatted data with debugging information. 132 | * Such that our transformers can deal with it intelligently. 133 | * 134 | * @example Inputs 135 | * ``` javascript 136 | * logger.log('note', 'this is working swell') 137 | * ``` 138 | * ``` javascript 139 | * logger.log('this', 'worked', 'swell') 140 | * ``` 141 | * 142 | * @example Results 143 | * ``` json 144 | * { 145 | * "args": ["this is working swell"], 146 | * "date": "2013-04-25T10:18:25.722Z", 147 | * "levelNumber": 5, 148 | * "levelName": "notice", 149 | * "line": "59", 150 | * "method": "Object.", 151 | * "file": "/Users/balupton/some-project/calling-file.js" 152 | * } 153 | * ``` 154 | * ``` json 155 | * { 156 | * "args": ["this", "worked", "well"], 157 | * "date": "2013-04-25T10:18:26.539Z", 158 | * "levelNumber": 6, 159 | * "levelName": "info", 160 | * "line": "60", 161 | * "method": "Object.", 162 | * "file": "/Users/balupton/some-project/calling-file.js" 163 | * } 164 | * ``` 165 | */ 166 | log(...args: any) { 167 | this.write(args) 168 | } 169 | 170 | /** Alias for log which prefixes the error log level */ 171 | error(...args: any) { 172 | this.write(['error', ...args]) 173 | } 174 | 175 | /** Alias for log which prefixes the warn log level */ 176 | warn(...args: any) { 177 | this.write(['warn', ...args]) 178 | } 179 | 180 | /** Alias for log which prefixes the info log level */ 181 | info(...args: any) { 182 | this.write(['info', ...args]) 183 | } 184 | 185 | /** Alias for log which prefixes the debug log level */ 186 | debug(...args: any) { 187 | this.write(['debug', ...args]) 188 | } 189 | } 190 | 191 | export default Logger 192 | -------------------------------------------------------------------------------- /source/test.ts: -------------------------------------------------------------------------------- 1 | import { Logger, Filter, Human, Browser, LogEntry } from './index.js' 2 | import { PassThrough } from 'stream' 3 | import kava from 'kava' 4 | import { equal, deepEqual } from 'assert-helpers' 5 | import { ok } from 'assert' 6 | 7 | // Prepare 8 | function cleanChangingLogger(item: any) { 9 | item = JSON.parse(item) 10 | item.date = item.date.replace( 11 | /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/, 12 | 'date' 13 | ) 14 | try { 15 | if (item.file.includes('test.js') === false) 16 | ok(false, 'file info was not as expected') 17 | if (item.method.includes('testCaller') === false) 18 | ok(false, 'method info was not as expected') 19 | if (item.line === -1) ok(false, 'line info was not found') 20 | if (item.char === -1) ok(false, 'char info was not found') 21 | } catch (err) { 22 | console.error(item) 23 | throw err 24 | } 25 | item.file = 'file' 26 | item.line = 'line' 27 | item.char = 'char' 28 | item.method = 'method' 29 | return item 30 | } 31 | function cleanChangingHuman(item: string) { 32 | item = item 33 | .replace(/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}Z\]/, 'date') 34 | .replace(/\[.+?:\d+:\d+\]/, 'file:line:char') 35 | .replace(/\[.+?\]/, 'method') 36 | return item 37 | } 38 | 39 | // Test 40 | kava.suite('caterpillar', function (suite) { 41 | suite('logger', function (suite, test) { 42 | const logger = new Logger({ lineLevel: 7 }) 43 | const output = new PassThrough() 44 | const actual: string[] = [] 45 | const expected: string[] = [ 46 | '{"date":"2013-05-07T11:12:43.982Z","args":["this is emergency and is level 0"],"levelNumber":0,"levelName":"emergency","line":"1","char":"20","method":"testCaller","file":"my/test.js"}', 47 | '{"date":"2013-05-07T11:12:43.986Z","args":["this is alert and is level 1"],"levelNumber":1,"levelName":"alert","line":"1","char":"20","method":"testCaller","file":"my/test.js"}', 48 | '{"date":"2013-05-07T11:12:43.987Z","args":["this is critical and is level 2"],"levelNumber":2,"levelName":"critical","line":"1","char":"20","method":"testCaller","file":"my/test.js"}', 49 | '{"date":"2013-05-07T11:12:43.987Z","args":["this is error and is level 3"],"levelNumber":3,"levelName":"error","line":"1","char":"20","method":"testCaller","file":"my/test.js"}', 50 | '{"date":"2013-05-07T11:12:43.988Z","args":["this is warning and is level 4"],"levelNumber":4,"levelName":"warning","line":"1","char":"20","method":"testCaller","file":"my/test.js"}', 51 | '{"date":"2013-05-07T11:12:43.988Z","args":["this is notice and is level 5"],"levelNumber":5,"levelName":"notice","line":"1","char":"20","method":"testCaller","file":"my/test.js"}', 52 | '{"date":"2013-05-07T11:12:43.989Z","args":["this is info and is level 6"],"levelNumber":6,"levelName":"info","line":"1","char":"20","method":"testCaller","file":"my/test.js"}', 53 | '{"date":"2013-05-07T11:12:43.990Z","args":["this is debug and is level 7"],"levelNumber":7,"levelName":"debug","line":"1","char":"20","method":"testCaller","file":"my/test.js"}', 54 | '{"date":"2013-05-07T11:12:43.991Z","args":["this is emerg and is level 0"],"levelNumber":0,"levelName":"emergency","line":"1","char":"20","method":"testCaller","file":"my/test.js"}', 55 | '{"date":"2013-05-07T11:12:43.991Z","args":["this is crit and is level 2"],"levelNumber":2,"levelName":"critical","line":"1","char":"20","method":"testCaller","file":"my/test.js"}', 56 | '{"date":"2013-05-07T11:12:43.992Z","args":["this is err and is level 3"],"levelNumber":3,"levelName":"error","line":"1","char":"20","method":"testCaller","file":"my/test.js"}', 57 | '{"date":"2013-05-07T11:12:43.992Z","args":["this is warn and is level 4"],"levelNumber":4,"levelName":"warning","line":"1","char":"20","method":"testCaller","file":"my/test.js"}', 58 | '{"date":"2013-05-07T11:12:43.992Z","args":["this is note and is level 5"],"levelNumber":5,"levelName":"notice","line":"1","char":"20","method":"testCaller","file":"my/test.js"}', 59 | '{"date":"2013-05-07T11:12:43.995Z","args":["this is unknown and is level 6"],"levelNumber":6,"levelName":"info","line":"1","char":"20","method":"testCaller","file":"my/test.js"}', 60 | ] 61 | 62 | output.on('data', function (chunk) { 63 | actual.push(chunk.toString()) 64 | }) 65 | 66 | test('should pipe correctly', function () { 67 | logger.pipe(output) 68 | }) 69 | 70 | test('should log messages', function testCaller() { 71 | const levels = logger.levels 72 | for (const name in levels) { 73 | if (levels.hasOwnProperty(name)) { 74 | const number = levels[name] 75 | const message = `this is ${name} and is level ${number}` 76 | logger.log(name, message) 77 | } 78 | } 79 | logger.log('this is unknown and is level 6') 80 | }) 81 | 82 | test('should provide the expected output', function (done) { 83 | output.on('end', function () { 84 | const actualCleaned = actual.map(cleanChangingLogger) 85 | const expectedCleaned = expected.map(cleanChangingLogger) 86 | console.dir(actualCleaned) 87 | console.dir(expectedCleaned) 88 | equal(actualCleaned.length, expectedCleaned.length, 'lengths') 89 | actualCleaned.forEach(function (result, index) { 90 | deepEqual(result, expectedCleaned[index], 'results') 91 | }) 92 | done() 93 | }) 94 | logger.end() 95 | }) 96 | }) 97 | 98 | suite('filter', function (suite, test) { 99 | const logger = new Logger() 100 | const filter = new Filter({ filterLevel: 5 }) 101 | const output = new PassThrough() 102 | const result: LogEntry[] = [] 103 | 104 | output.on('data', function (chunk) { 105 | result.push(JSON.parse(chunk.toString())) 106 | }) 107 | 108 | test('should pipe correctly', function () { 109 | logger.pipe(filter).pipe(output) 110 | }) 111 | 112 | test('should log messages', function testCaller() { 113 | logger.log(5, 'first') 114 | logger.log(6, 'second') // this one should be excluded 115 | logger.log(5, 'third') 116 | }) 117 | 118 | test('should provide the expected output', function (done) { 119 | output.on('end', function () { 120 | equal(result.length, 2, 'length') 121 | equal(result[0].args[0], 'first') 122 | equal(result[1].args[0], 'third') 123 | done() 124 | }) 125 | logger.end() 126 | }) 127 | }) 128 | 129 | suite('human', function (suite) { 130 | suite('instantiation', function (suite, test) { 131 | test('should instantiate correctly', function () { 132 | const human = new Human() 133 | equal(human.color, true, 'default color was applied correctly') 134 | }) 135 | test('should instantiate correctly, with config', function () { 136 | const human = new Human({ color: false }) 137 | equal(human.color, false, 'custom color was applied correctly') 138 | }) 139 | }) 140 | 141 | function addSuite( 142 | name: string, 143 | config: { color?: boolean; lineLevel?: number }, 144 | expected: string[], 145 | cleaner?: typeof cleanChangingHuman 146 | ) { 147 | suite(name, function (suite, test) { 148 | const logger = new Logger(config) 149 | const human = new Human(config) 150 | const output = new PassThrough() 151 | let actual: string[] = [] 152 | if (cleaner) expected = expected.map(cleaner) 153 | 154 | output.on('data', function (chunk) { 155 | actual.push(chunk.toString()) 156 | }) 157 | 158 | test('should pipe correctly', function () { 159 | logger.pipe(human).pipe(output) 160 | }) 161 | 162 | test('should log messages', function () { 163 | const levels = logger.levels 164 | Object.keys(levels).forEach(function testCaller(name) { 165 | const code = levels[name] 166 | const message = `this is ${name} and is level ${code}` 167 | logger.log(name, message) 168 | }) 169 | }) 170 | 171 | test('should provide the expected output', function (done) { 172 | output.on('end', function () { 173 | if (cleaner) actual = actual.map(cleaner) 174 | equal(actual.length, expected.length, 'length was expected') 175 | actual.forEach(function (result, index) { 176 | equal(result, expected[index], 'result was expected') 177 | }) 178 | done() 179 | }) 180 | logger.end() 181 | }) 182 | }) 183 | } 184 | 185 | addSuite('logging without colors', { color: false }, [ 186 | 'emergency: this is emergency and is level 0\n', 187 | 'alert: this is alert and is level 1\n', 188 | 'critical: this is critical and is level 2\n', 189 | 'error: this is error and is level 3\n', 190 | 'warning: this is warning and is level 4\n', 191 | 'notice: this is notice and is level 5\n', 192 | 'info: this is info and is level 6\n', 193 | 'debug: this is debug and is level 7\n', 194 | 'emergency: this is emerg and is level 0\n', 195 | 'critical: this is crit and is level 2\n', 196 | 'error: this is err and is level 3\n', 197 | 'warning: this is warn and is level 4\n', 198 | 'notice: this is note and is level 5\n', 199 | ]) 200 | 201 | addSuite('logging with colors', {}, [ 202 | '\u001b[31memergency:\u001b[39m this is emergency and is level 0\n', 203 | '\u001b[31malert:\u001b[39m this is alert and is level 1\n', 204 | '\u001b[31mcritical:\u001b[39m this is critical and is level 2\n', 205 | '\u001b[31merror:\u001b[39m this is error and is level 3\n', 206 | '\u001b[33mwarning:\u001b[39m this is warning and is level 4\n', 207 | '\u001b[33mnotice:\u001b[39m this is notice and is level 5\n', 208 | '\u001b[32minfo:\u001b[39m this is info and is level 6\n', 209 | '\u001b[32mdebug:\u001b[39m this is debug and is level 7\n', 210 | '\u001b[31memergency:\u001b[39m this is emerg and is level 0\n', 211 | '\u001b[31mcritical:\u001b[39m this is crit and is level 2\n', 212 | '\u001b[31merror:\u001b[39m this is err and is level 3\n', 213 | '\u001b[33mwarning:\u001b[39m this is warn and is level 4\n', 214 | '\u001b[33mnotice:\u001b[39m this is note and is level 5\n', 215 | ]) 216 | 217 | addSuite( 218 | 'logging with colors', 219 | { lineLevel: 7 }, 220 | [ 221 | '\u001b[31memergency:\u001b[39m this is emergency and is level 0\n \u001b[2m→ [2013-05-06 20:39:46.119] [my/test.js:1:20] [testCaller]\u001b[22m\n', 222 | '\u001b[31malert:\u001b[39m this is alert and is level 1\n \u001b[2m→ [2013-05-06 20:39:46.120] [my/test.js:1:20] [testCaller]\u001b[22m\n', 223 | '\u001b[31mcritical:\u001b[39m this is critical and is level 2\n \u001b[2m→ [2013-05-06 20:39:46.120] [my/test.js:1:20] [testCaller]\u001b[22m\n', 224 | '\u001b[31merror:\u001b[39m this is error and is level 3\n \u001b[2m→ [2013-05-06 20:39:46.121] [my/test.js:1:20] [testCaller]\u001b[22m\n', 225 | '\u001b[33mwarning:\u001b[39m this is warning and is level 4\n \u001b[2m→ [2013-05-06 20:39:46.121] [my/test.js:1:20] [testCaller]\u001b[22m\n', 226 | '\u001b[33mnotice:\u001b[39m this is notice and is level 5\n \u001b[2m→ [2013-05-06 20:39:46.122] [my/test.js:1:20] [testCaller]\u001b[22m\n', 227 | '\u001b[32minfo:\u001b[39m this is info and is level 6\n \u001b[2m→ [2013-05-06 20:39:46.122] [my/test.js:1:20] [testCaller]\u001b[22m\n', 228 | '\u001b[32mdebug:\u001b[39m this is debug and is level 7\n \u001b[2m→ [2013-05-06 20:39:46.123] [my/test.js:1:20] [testCaller]\u001b[22m\n', 229 | '\u001b[31memergency:\u001b[39m this is emerg and is level 0\n \u001b[2m→ [2013-05-06 20:39:46.123] [my/test.js:1:20] [testCaller]\u001b[22m\n', 230 | '\u001b[31mcritical:\u001b[39m this is crit and is level 2\n \u001b[2m→ [2013-05-06 20:39:46.124] [my/test.js:1:20] [testCaller]\u001b[22m\n', 231 | '\u001b[31merror:\u001b[39m this is err and is level 3\n \u001b[2m→ [2013-05-06 20:39:46.124] [my/test.js:1:20] [testCaller]\u001b[22m\n', 232 | '\u001b[33mwarning:\u001b[39m this is warn and is level 4\n \u001b[2m→ [2013-05-06 20:39:46.125] [my/test.js:1:20] [testCaller]\u001b[22m\n', 233 | '\u001b[33mnotice:\u001b[39m this is note and is level 5\n \u001b[2m→ [2013-05-06 20:39:46.126] [my/test.js:1:20] [testCaller]\u001b[22m\n', 234 | ], 235 | cleanChangingHuman 236 | ) 237 | }) 238 | 239 | // Test 240 | suite('human', function (suite) { 241 | suite('instantiation', function (suite, test) { 242 | test('should instantiate correctly', function () { 243 | const browser = new Browser() 244 | equal(browser.color, true, 'default color was applied correctly') 245 | }) 246 | 247 | test('should instantiate correctly, with config', function () { 248 | const browser = new Browser({ color: false }) 249 | equal(browser.color, false, 'custom color was applied correctly') 250 | }) 251 | }) 252 | 253 | function addSuite( 254 | name: string, 255 | config: { color?: boolean; lineLevel?: number }, 256 | expected: string[], 257 | cleaner?: typeof cleanChangingHuman 258 | ) { 259 | suite(name, function (suite, test) { 260 | const logger = new Logger(config) 261 | const human = new Human(config) 262 | const browser = new Browser(config) 263 | const output = new PassThrough() 264 | let actual: string[] = [] 265 | if (cleaner) expected = expected.map(cleaner) 266 | 267 | output.on('data', function (chunk) { 268 | actual.push(chunk.toString()) 269 | }) 270 | 271 | test('should pipe correctly', function () { 272 | logger.pipe(human).pipe(browser).pipe(output) 273 | }) 274 | 275 | test('should log messages', function () { 276 | const levels = logger.levels 277 | Object.keys(levels).forEach(function testCaller(name) { 278 | const code = levels[name] 279 | const message = `this is ${name} and is level ${code}` 280 | logger.log(name, message) 281 | }) 282 | }) 283 | 284 | test('should provide the expected output', function (done) { 285 | output.on('end', function () { 286 | if (cleaner) actual = actual.map(cleaner) 287 | equal(actual.length, expected.length, 'length was expected') 288 | actual.forEach(function (result, index) { 289 | equal(result, expected[index], 'result was expected') 290 | }) 291 | done() 292 | }) 293 | logger.end() 294 | }) 295 | }) 296 | } 297 | 298 | addSuite( 299 | 'logging without colors in debug mode', 300 | { color: false, lineLevel: 7 }, 301 | [ 302 | '["emergency: this is emergency and is level 0\\n → [2013-05-06 20:41:13.973] [my/test.js:1:20] [testCaller]"]', 303 | '["alert: this is alert and is level 1\\n → [2013-05-06 20:41:13.978] [my/test.js:1:20] [testCaller]"]', 304 | '["critical: this is critical and is level 2\\n → [2013-05-06 20:41:13.978] [my/test.js:1:20] [testCaller]"]', 305 | '["error: this is error and is level 3\\n → [2013-05-06 20:41:13.979] [my/test.js:1:20] [testCaller]"]', 306 | '["warning: this is warning and is level 4\\n → [2013-05-06 20:41:13.979] [my/test.js:1:20] [testCaller]"]', 307 | '["notice: this is notice and is level 5\\n → [2013-05-06 20:41:13.980] [my/test.js:1:20] [testCaller]"]', 308 | '["info: this is info and is level 6\\n → [2013-05-06 20:41:13.980] [my/test.js:1:20] [testCaller]"]', 309 | '["debug: this is debug and is level 7\\n → [2013-05-06 20:41:13.981] [my/test.js:1:20] [testCaller]"]', 310 | '["emergency: this is emerg and is level 0\\n → [2013-05-06 20:41:13.982] [my/test.js:1:20] [testCaller]"]', 311 | '["critical: this is crit and is level 2\\n → [2013-05-06 20:41:13.982] [my/test.js:1:20] [testCaller]"]', 312 | '["error: this is err and is level 3\\n → [2013-05-06 20:41:13.982] [my/test.js:1:20] [testCaller]"]', 313 | '["warning: this is warn and is level 4\\n → [2013-05-06 20:41:13.983] [my/test.js:1:20] [testCaller]"]', 314 | '["notice: this is note and is level 5\\n → [2013-05-06 20:41:13.983] [my/test.js:1:20] [testCaller]"]', 315 | ], 316 | cleanChangingHuman 317 | ) 318 | 319 | addSuite( 320 | 'logging with colors in debug mode', 321 | { lineLevel: 7 }, 322 | [ 323 | '["%c%s%c this is emergency and is level 0\\n %c%s%c","color:red","emergency:","color:default; font:default; text-decoration:default","color:lightGray","→ [2013-05-06 21:17:14.550] [my/test.js:1:20] [testCaller]","color:default; font:default; text-decoration:default"]', 324 | '["%c%s%c this is alert and is level 1\\n %c%s%c","color:red","alert:","color:default; font:default; text-decoration:default","color:lightGray","→ [2013-05-06 21:17:14.551] [my/test.js:1:20] [testCaller]","color:default; font:default; text-decoration:default"]', 325 | '["%c%s%c this is critical and is level 2\\n %c%s%c","color:red","critical:","color:default; font:default; text-decoration:default","color:lightGray","→ [2013-05-06 21:17:14.551] [my/test.js:1:20] [testCaller]","color:default; font:default; text-decoration:default"]', 326 | '["%c%s%c this is error and is level 3\\n %c%s%c","color:red","error:","color:default; font:default; text-decoration:default","color:lightGray","→ [2013-05-06 21:17:14.552] [my/test.js:1:20] [testCaller]","color:default; font:default; text-decoration:default"]', 327 | '["%c%s%c this is warning and is level 4\\n %c%s%c","color:orange","warning:","color:default; font:default; text-decoration:default","color:lightGray","→ [2013-05-06 21:17:14.552] [my/test.js:1:20] [testCaller]","color:default; font:default; text-decoration:default"]', 328 | '["%c%s%c this is notice and is level 5\\n %c%s%c","color:orange","notice:","color:default; font:default; text-decoration:default","color:lightGray","→ [2013-05-06 21:17:14.553] [my/test.js:1:20] [testCaller]","color:default; font:default; text-decoration:default"]', 329 | '["%c%s%c this is info and is level 6\\n %c%s%c","color:green","info:","color:default; font:default; text-decoration:default","color:lightGray","→ [2013-05-06 21:17:14.553] [my/test.js:1:20] [testCaller]","color:default; font:default; text-decoration:default"]', 330 | '["%c%s%c this is debug and is level 7\\n %c%s%c","color:green","debug:","color:default; font:default; text-decoration:default","color:lightGray","→ [2013-05-06 21:17:14.554] [my/test.js:1:20] [testCaller]","color:default; font:default; text-decoration:default"]', 331 | '["%c%s%c this is emerg and is level 0\\n %c%s%c","color:red","emergency:","color:default; font:default; text-decoration:default","color:lightGray","→ [2013-05-06 21:17:14.554] [my/test.js:1:20] [testCaller]","color:default; font:default; text-decoration:default"]', 332 | '["%c%s%c this is crit and is level 2\\n %c%s%c","color:red","critical:","color:default; font:default; text-decoration:default","color:lightGray","→ [2013-05-06 21:17:14.555] [my/test.js:1:20] [testCaller]","color:default; font:default; text-decoration:default"]', 333 | '["%c%s%c this is err and is level 3\\n %c%s%c","color:red","error:","color:default; font:default; text-decoration:default","color:lightGray","→ [2013-05-06 21:17:14.555] [my/test.js:1:20] [testCaller]","color:default; font:default; text-decoration:default"]', 334 | '["%c%s%c this is warn and is level 4\\n %c%s%c","color:orange","warning:","color:default; font:default; text-decoration:default","color:lightGray","→ [2013-05-06 21:17:14.556] [my/test.js:1:20] [testCaller]","color:default; font:default; text-decoration:default"]', 335 | '["%c%s%c this is note and is level 5\\n %c%s%c","color:orange","notice:","color:default; font:default; text-decoration:default","color:lightGray","→ [2013-05-06 21:17:14.556] [my/test.js:1:20] [testCaller]","color:default; font:default; text-decoration:default"]', 336 | ], 337 | cleanChangingHuman 338 | ) 339 | }) 340 | }) 341 | -------------------------------------------------------------------------------- /source/transform.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Caterpillar supports piping to anything that supports this interface. 3 | * Which includes: 4 | * - {@link Transform Caterpillar Transforms} 5 | * - [Deno Writer Streams](https://doc.deno.land/https/github.com/denoland/deno/releases/latest/download/lib.deno.d.ts#Deno.Writer), e.g. 6 | * - [Deno.stdout](https://doc.deno.land/https/github.com/denoland/deno/releases/latest/download/lib.deno.d.ts#Deno.stdout) 7 | * - [Node.js Writable Streams](https://nodejs.org/dist/latest-v14.x/docs/api/stream.html#stream_writable_streams), e.g. 8 | * - [process.stdout](https://nodejs.org/dist/latest-v14.x/docs/api/process.html#process_process_stdout) 9 | * - [fs.createWriteStream](https://nodejs.org/dist/latest-v14.x/docs/api/fs.html#fs_fs_createwritestream_path_options) 10 | * - [WhatWG Writable Streams](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream) 11 | */ 12 | export interface Pipeable { 13 | write(chunk: any): any 14 | end?(cb?: () => void): void 15 | close?(): Promise | void 16 | } 17 | 18 | /** 19 | * Caterpillar Transform Class. 20 | * Provides the methods needed to provide a pipable Caterpillar Transform. 21 | * Such that all you need to do is write your {@link Transform.format} method. 22 | * It can pipe to anything that provides a {@link Pipeable.write} method. 23 | * @example [Writing a Custom Transform](https://repl.it/@balupton/caterpillar-custom-transform) 24 | */ 25 | export class Transform implements Pipeable { 26 | /** Where is this Transform piping to? */ 27 | private pipes: Array = [] 28 | 29 | /** 30 | * Format the received log entry representation. 31 | * Your transformer should extend this. 32 | */ 33 | format(message: any): any { 34 | return message 35 | } 36 | 37 | /** Pipe future log entries into a caterpillar transform or a stream. */ 38 | pipe(to: T) { 39 | this.pipes.push(to) 40 | return to 41 | } 42 | 43 | /** Maintain a write queue such that multiple Deno writes do not stall */ 44 | private writer = Promise.resolve() 45 | 46 | /** Write to the child pipes. */ 47 | write(chunk: any) { 48 | // format now, so that we have the correct stack 49 | const data = this.format(chunk) 50 | // exclude filtered entries 51 | if (data == null) return this.writer 52 | // now delegate back to the pipe 53 | this.writer = this.writer.then(async () => { 54 | // pipe to child transforms and streams 55 | for (const pipe of this.pipes) { 56 | if (pipe instanceof Transform) { 57 | // compatibility with caterpillar transforms 58 | await pipe.write(data) 59 | } else { 60 | const str = typeof data === 'string' ? data : JSON.stringify(data) 61 | // requires typescript dom lib to define TextEncoder global 62 | if (typeof TextEncoder !== 'undefined') { 63 | // compatibility with deno and later node streams 64 | await pipe.write(new TextEncoder().encode(str)) 65 | } else { 66 | // compatibility with earlier node streams 67 | await pipe.write(str) 68 | } 69 | } 70 | } 71 | }) 72 | return this.writer 73 | } 74 | 75 | /** Close the child pipes. */ 76 | async close(): Promise { 77 | await Promise.all( 78 | this.pipes.map((pipe) => { 79 | if (pipe.close) { 80 | return pipe.close() 81 | } else if (pipe.end) { 82 | return new Promise(function (resolve) { 83 | pipe.end!(resolve) 84 | }) 85 | } else { 86 | return Promise.resolve() 87 | } 88 | }) 89 | ) 90 | } 91 | 92 | /* Callback alias for close */ 93 | end(cb?: () => void) { 94 | const p = this.close() 95 | if (cb) p.finally(cb) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /source/transforms/browser.ts: -------------------------------------------------------------------------------- 1 | // this cannot be located at source/brower.ts as otherwise it would become the browser entry 2 | 3 | // Imports 4 | import { Transform } from '../transform.js' 5 | 6 | /** Mapping of ANSI color codes into a CSS style */ 7 | export interface Styles { 8 | [key: string]: { 9 | /** The ANSI color code used to start the style */ 10 | start: string 11 | /** The ANSI color code used to end the style */ 12 | end: string 13 | /** The CSS style value */ 14 | value: string 15 | } 16 | } 17 | 18 | /** Configuration optons for the Caterpillar Browser Transform */ 19 | export interface BrowserOptions { 20 | /** Use to override the default value of {@link Filter.color} */ 21 | color?: boolean 22 | /** Use to override the default value of {@link Filter.console} */ 23 | output?: boolean 24 | /** Use to override the default value of {@link Filter.styles} */ 25 | styles?: Styles 26 | } 27 | 28 | /** 29 | * Convert human readable Caterpillar entries into browser compatible entries. 30 | * @example 31 | * ``` javascript 32 | * import { Logger, Human, Browser } from 'caterpillar' 33 | * const logger = new Logger() 34 | * logger.pipe(new Human()).pipe(new Browser()) 35 | * logger.log('info', 'some', {data: 'oh yeah'}, 42) 36 | * ``` 37 | */ 38 | export class Browser extends Transform { 39 | /** Whether or not we should use color. */ 40 | public color: boolean = true 41 | 42 | /** Whether or not we should write to the browser console. */ 43 | public output: boolean = true 44 | 45 | /** The objects that tell our browser transform how to convert terminal colours into console colours. */ 46 | public styles: Styles = { 47 | red: { 48 | start: '31', 49 | end: '39', 50 | value: 'color:red', 51 | }, 52 | yellow: { 53 | start: '33', 54 | end: '39', 55 | value: 'color:orange', 56 | }, 57 | green: { 58 | start: '32', 59 | end: '39', 60 | value: 'color:green', 61 | }, 62 | bright: { 63 | start: '1', 64 | end: '22', 65 | value: 'font-weight:bold', 66 | }, 67 | dim: { 68 | start: '2', 69 | end: '22', 70 | value: 'color:lightGray', 71 | }, 72 | italic: { 73 | start: '3', 74 | end: '23', 75 | value: 'font-style:italic', 76 | }, 77 | underline: { 78 | start: '4', 79 | end: '24', 80 | value: 'text-decoration:underline', 81 | }, 82 | } 83 | 84 | /** Create our instance and apply our configuraiton options. */ 85 | constructor(opts?: BrowserOptions) { 86 | super() 87 | 88 | // options 89 | if (opts?.color != null) this.color = opts.color 90 | if (opts?.output != null) this.output = opts.output 91 | if (opts?.styles != null) this.styles = opts.styles 92 | } 93 | 94 | /** 95 | * Convert a human readable Caterpillar entry into a format that browser consoles can understand. 96 | * And if the `write` config property is `true` (it is by default), write the result to the browser console. 97 | */ 98 | format(message: string): string[] { 99 | // Prepare 100 | const { color, styles, output } = this 101 | 102 | // Replace caterpillar-human formatted entry 103 | /* eslint no-control-regex:0 */ 104 | const args: string[] = [] 105 | const result = message.replace( 106 | /\u001b\[([0-9]+)m(.+?)\u001b\[([0-9]+)m/g, 107 | function (match, start, content, end) { 108 | // Check 109 | if (color === false) return content 110 | 111 | // Prepare 112 | let matchedStyle, style 113 | 114 | // Find the matcing style for this combination 115 | for (const key in styles) { 116 | if (styles.hasOwnProperty(key)) { 117 | style = styles[key] 118 | if ( 119 | String(style.start) === String(start) && 120 | String(style.end) === String(end) 121 | ) { 122 | matchedStyle = style 123 | break 124 | } 125 | } 126 | } 127 | 128 | // Check 129 | if (!matchedStyle) return content 130 | 131 | // Push the style 132 | args.push(matchedStyle.value) 133 | args.push(content) 134 | args.push('color:default; font:default; text-decoration:default') 135 | return '%c%s%c' 136 | } 137 | ) 138 | 139 | // Final format 140 | const parts = [result.trim()].concat(args) 141 | 142 | // Write 143 | /* eslint no-console:0 */ 144 | if (output) console.log(...parts) 145 | 146 | // Return 147 | return parts 148 | } 149 | } 150 | 151 | export default Browser 152 | -------------------------------------------------------------------------------- /source/transforms/filter.ts: -------------------------------------------------------------------------------- 1 | import { LogEntry } from '../logger.js' 2 | import { Transform } from '../transform.js' 3 | 4 | /** Configuration options for the Caterpillar Filter Transform */ 5 | export interface FilterOptions { 6 | /** Use to override the default value of {@link Filter.filterLevel} */ 7 | filterLevel?: number 8 | } 9 | 10 | /** 11 | * Caterpillar Filter Transform. 12 | * Filters the log entries, keeping only those equal to or below the specified `filterLevel`. 13 | * @example 14 | * ``` javascript 15 | * import { Logger, Filter } from 'caterpillar' 16 | * const logger = new Logger() 17 | * const filter = new Filter({ filterLevel: 6 }) 18 | * logger.pipe(filter).pipe(process.stdout) 19 | * logger.log('info', 'this will be outputted') 20 | * logger.log('debug', 'this will be ignored') 21 | * filter.filterLevel = 5 22 | * logger.log('info', 'now even this will be ignored') 23 | * logger.log('note', 'but not this') 24 | * ``` 25 | */ 26 | export class Filter extends Transform { 27 | /** 28 | * Only display entries that have a log level below or equal to this number. 29 | * Defaults to `6`, which by default is the info log level. 30 | */ 31 | public filterLevel: number = 6 32 | 33 | /** Create our instance and apply our configuraiton options. */ 34 | constructor(opts?: FilterOptions) { 35 | super() 36 | 37 | // options 38 | if (opts?.filterLevel != null) this.filterLevel = opts.filterLevel 39 | } 40 | 41 | /** Retain only log entries that are equal to or less than the specified filter level. */ 42 | format(entry: LogEntry): LogEntry | null { 43 | return entry.levelNumber <= this.filterLevel ? entry : null 44 | } 45 | } 46 | 47 | export default Filter 48 | -------------------------------------------------------------------------------- /source/transforms/human.ts: -------------------------------------------------------------------------------- 1 | // Imports 2 | import { LogEntry } from '../logger.js' 3 | import { Transform } from '../transform.js' 4 | import { inspect } from 'util' 5 | import * as ansi from '@bevry/ansi' 6 | 7 | /** 8 | * Return the given argument. 9 | * Used for when there is no formatter. 10 | */ 11 | function ansiNoop(a: string): string { 12 | return a 13 | } 14 | 15 | /** A mapping of log level numbers to their intended colours */ 16 | interface LevelsToColorsMap { 17 | [logLevelNumber: string]: ansi.ANSIApplier 18 | } 19 | 20 | /** Configuration options for the Caterpillar Human Transform */ 21 | export interface HumanOptions { 22 | /** Use to override the default value of {@link Human.color} */ 23 | color?: boolean 24 | 25 | /** Use to override the default value of {@link Human.colors} */ 26 | colors?: LevelsToColorsMap 27 | } 28 | 29 | /** 30 | * Convert Logger entries into human readable format. 31 | * @extends Transform 32 | * @example 33 | * ``` javascript 34 | * import { Logger, Human } from 'caterpillar' 35 | * const logger = new Logger() 36 | * const human = new Human() 37 | * logger.pipe(human).pipe(process.stdout) 38 | * logger.log('info', 'some', {data: 'oh yeah'}, 42) 39 | * ``` 40 | */ 41 | export class Human extends Transform { 42 | /** Whether or not to use colors? */ 43 | public color: boolean = true 44 | 45 | /** Mapping of which log level numbers correspond to which colours */ 46 | public colors: LevelsToColorsMap = { 47 | '0': 'red', 48 | '1': 'red', 49 | '2': 'red', 50 | '3': 'red', 51 | '4': 'yellow', 52 | '5': 'yellow', 53 | '6': 'green', 54 | '7': 'green', 55 | } 56 | 57 | /** Create our instance and apply our configuration options. */ 58 | constructor(opts?: HumanOptions) { 59 | super() 60 | 61 | // options 62 | if (opts?.color != null) this.color = opts.color 63 | if (opts?.colors != null) this.colors = opts.colors 64 | } 65 | 66 | /** Get the color for the log level */ 67 | getColor(levelNumber: number): ansi.ANSIApplier | false { 68 | // Determine 69 | const color = this.colors[levelNumber] || false 70 | 71 | // Return 72 | return color 73 | } 74 | 75 | /** Pad the left of some content if need be with the specified padding to make the content reach a certain size */ 76 | padLeft(padding: string, size: number, content: string | number): string { 77 | // Prepare 78 | padding = String(padding) 79 | content = String(content) 80 | 81 | // Handle 82 | if (content.length < size) { 83 | for (let i = 0, n = size - content.length; i < n; ++i) { 84 | content = padding + content 85 | } 86 | } 87 | 88 | // Return 89 | return content 90 | } 91 | 92 | /** Convert logger entry arguments into a human readable string */ 93 | formatArguments(args: any[]): string { 94 | return args 95 | .map((value) => 96 | typeof value === 'string' 97 | ? value 98 | : inspect(value, { 99 | showHidden: false, 100 | depth: 10, 101 | colors: this.color, 102 | }) 103 | ) 104 | .join(' ') 105 | } 106 | 107 | /** Convert a datetime into a human readable format */ 108 | formatDate(datetime: Date | number | string): string { 109 | // Prepare 110 | const now = new Date(datetime) 111 | const year = now.getFullYear() 112 | const month = this.padLeft('0', 2, now.getMonth() + 1) 113 | const date = this.padLeft('0', 2, now.getDate()) 114 | const hours = this.padLeft('0', 2, now.getHours()) 115 | const minutes = this.padLeft('0', 2, now.getMinutes()) 116 | const seconds = this.padLeft('0', 2, now.getSeconds()) 117 | const ms = this.padLeft('0', 3, now.getMilliseconds()) 118 | 119 | // Apply 120 | const result = `${year}-${month}-${date} ${hours}:${minutes}:${seconds}.${ms}` 121 | 122 | // Return 123 | return result 124 | } 125 | 126 | /** Convert a logger entry into a human readable format */ 127 | format(entry: LogEntry): string { 128 | // Prepare 129 | const { color } = this 130 | const useLine = entry.line !== -1 131 | let result: string 132 | 133 | // Format 134 | const format = { 135 | color: this.getColor(entry.levelNumber), 136 | timestamp: this.formatDate(entry.date), 137 | text: this.formatArguments(entry.args), 138 | } 139 | 140 | // Check 141 | if (format.text) { 142 | // Formatters 143 | const levelFormatter = 144 | (color && format.color && ansi[format.color]) || ansiNoop 145 | const lineFormatter = (useLine && color && ansi.dim) || ansiNoop 146 | 147 | // Message 148 | // @ts-ignore 149 | const levelString = levelFormatter(`${entry.levelName}:`) 150 | const entryString = format.text 151 | const messageString = `${levelString} ${entryString}` 152 | 153 | // Format 154 | if (useLine) { 155 | // Line Information 156 | const seperator = '\n ' 157 | const debugString = lineFormatter( 158 | `→ [${format.timestamp}] [${entry.file}:${entry.line}:${entry.char}] [${entry.method}]` 159 | ) 160 | 161 | // Result 162 | result = `${messageString}${seperator}${debugString}\n` 163 | } else { 164 | // Result 165 | result = `${messageString}\n` 166 | } 167 | } else { 168 | result = format.text 169 | } 170 | 171 | // Return 172 | return result 173 | } 174 | } 175 | 176 | export default Human 177 | -------------------------------------------------------------------------------- /test.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // auto-generated by boundation, do not update manually 3 | /** @type {typeof import("./edition-types/test.d.ts") } */ 4 | module.exports = require('editions').requirePackage(__dirname, require, 'test.js') -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "downlevelIteration": true, 5 | "esModuleInterop": true, 6 | "isolatedModules": true, 7 | "lib": ["DOM.Iterable", "DOM"], 8 | "maxNodeModuleJsDepth": 5, 9 | "module": "ESNext", 10 | "moduleResolution": "Node", 11 | "strict": true, 12 | "target": "ES2022" 13 | }, 14 | "include": ["source"] 15 | } 16 | --------------------------------------------------------------------------------