├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .eslintignore ├── .eslintrc.json ├── .github ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md └── workflows │ ├── CodeQL.yml │ ├── ESLint.yml │ └── Publish.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── .stack-version ├── .yarn ├── plugins │ └── @yarnpkg │ │ ├── plugin-typescript.cjs │ │ └── plugin-workspace-tools.cjs └── releases │ └── yarn-3.4.1.cjs ├── .yarnrc.yml ├── LICENSE ├── README.md ├── SECURITY.md ├── package.json ├── renovate.json ├── scripts ├── build.js ├── ci │ ├── eslint.js │ └── vitest.js ├── publish.js ├── ts-runner.js ├── upload-packages.sh └── util │ ├── exec.js │ ├── log.js │ └── stack-version.js ├── src ├── config │ ├── .eslintrc.json │ ├── README.md │ ├── index.ts │ ├── package.json │ ├── src │ │ ├── loaders │ │ │ ├── base.loader.ts │ │ │ ├── index.ts │ │ │ ├── json.loader.ts │ │ │ ├── toml.loader.ts │ │ │ └── yaml.loader.ts │ │ └── service.ts │ ├── tests │ │ ├── __fixtures__ │ │ │ └── example.json │ │ └── service.spec.ts │ ├── tsconfig.json │ ├── typings.d.ts │ └── vitest.config.ts ├── http │ ├── .eslintrc.json │ ├── README.md │ ├── src │ │ └── index.ts │ └── vitest.config.ts ├── lilith │ ├── .eslintrc.json │ ├── README.md │ ├── index.ts │ ├── package.json │ ├── src │ │ ├── container.ts │ │ ├── create-lilith.ts │ │ ├── crypto-utils.ts │ │ ├── decorators │ │ │ ├── Inject.ts │ │ │ ├── Service.ts │ │ │ ├── Variable.ts │ │ │ └── index.ts │ │ ├── functions.ts │ │ ├── types.ts │ │ └── use-container.ts │ ├── tests │ │ ├── __fixtures__ │ │ │ ├── dos.service.ts │ │ │ └── uno.service.ts │ │ └── container.spec.ts │ ├── tsconfig.json │ ├── typings.d.ts │ └── vitest.config.ts ├── logging │ ├── .eslintrc.json │ ├── README.md │ ├── index.ts │ ├── package.json │ ├── src │ │ ├── backends │ │ │ └── base.backend.ts │ │ ├── decorators │ │ │ └── Log.ts │ │ ├── factory.ts │ │ ├── logger.ts │ │ ├── service.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── tests │ │ ├── __fixtures__ │ │ │ ├── console.backend.ts │ │ │ ├── logger-factory.ts │ │ │ └── logger.ts │ │ ├── service.spec.ts │ │ └── utils.spec.ts │ ├── tsconfig.json │ ├── typings.d.ts │ └── vitest.config.ts └── winston │ ├── .eslintrc.json │ ├── README.md │ ├── index.ts │ ├── package.json │ ├── src │ ├── WinstonBackend.ts │ ├── WinstonLogger.ts │ ├── WinstonLoggerFactory.ts │ └── types.ts │ ├── tests │ └── main.spec.ts │ ├── tsconfig.json │ ├── typings.d.ts │ └── vitest.config.ts └── yarn.lock /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 2 | # Copyright (c) 2021-2022 Noelware 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in all 12 | # copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | # SOFTWARE. 21 | 22 | FROM auguwu/devcontainers:latest 23 | 24 | ARG NODE_VERSION=18.9.0 25 | USER ${USERNAME} 26 | 27 | RUN mkdir $HOME/.nvm && \ 28 | curl --silent -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.36.0/install.sh | bash && \ 29 | source $HOME/.nvm/nvm.sh && \ 30 | nvm install ${NODE_VERSION} && \ 31 | nvm alias default ${NODE_VERSION} && \ 32 | nvm use ${NODE_VERSION} && \ 33 | echo "export NVM_DIR=\"$HOME/.nvm\"" >> $HOME/.bashrc && \ 34 | echo '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"' >> $HOME/.bashrc && \ 35 | npm i -g yarn && \ 36 | sudo git lfs install --system 37 | 38 | CMD ["sleep", "infinity"] 39 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Lilith", 3 | "dockerFile": "Dockerfile", 4 | "remoteUser": "noel", 5 | "workspaceFolder": "/workspace", 6 | "extensions": ["dbaeumer.vscode-eslint"] 7 | } 8 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .gitignore -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["prettier", "@augu/eslint-config/ts.js"] 3 | } 4 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @auguwu @IceeMC 2 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at team@noelware.org. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | a gay dog will edit this soon, stay tuned. :) 2 | -------------------------------------------------------------------------------- /.github/workflows/CodeQL.yml: -------------------------------------------------------------------------------- 1 | # 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 2 | # Copyright (c) 2021-2022 Noelware 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in all 12 | # copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | # SOFTWARE. 21 | 22 | name: GitHub CodeQL 23 | on: 24 | workflow_dispatch: 25 | push: 26 | branches: 27 | - master 28 | 29 | paths-ignore: 30 | - '.github/**' 31 | - '.vscode/**' 32 | - 'assets/**' 33 | - 'docker/**' 34 | - '.idea/**' 35 | - '.dockerignore' 36 | - '.gitignore' 37 | - '**.md' 38 | - 'LICENSE' 39 | - 'renovate.json' 40 | 41 | pull_request: 42 | branches: 43 | - master 44 | 45 | paths-ignore: 46 | - '.github/**' 47 | - '.vscode/**' 48 | - 'assets/**' 49 | - 'docker/**' 50 | - '.idea/**' 51 | - '.dockerignore' 52 | - '.gitignore' 53 | - '**.md' 54 | - 'LICENSE' 55 | - 'renovate.json' 56 | 57 | schedule: 58 | - cron: '21 2 * * 4' 59 | 60 | jobs: 61 | analyze: 62 | name: Analyze 63 | runs-on: ubuntu-latest 64 | permissions: 65 | actions: read 66 | contents: read 67 | security-events: write 68 | strategy: 69 | fail-fast: false 70 | matrix: 71 | language: 72 | - javascript 73 | steps: 74 | - name: Checkout repository 75 | uses: actions/checkout@v3 76 | 77 | - name: Setup Node.js v18 78 | uses: actions/setup-node@v3 79 | with: 80 | node-version: 18.x 81 | 82 | - name: Setup Yarn and node-modules cache 83 | id: yarn-cache 84 | uses: auguwu/node-pm-action@master 85 | 86 | - name: Install dependencies 87 | if: steps.yarn-cache.outputs.cache-hit != 'true' || steps.yarn-cache.outputs.node-modules-cache-hit != 'true' 88 | run: yarn install --immutable 89 | 90 | - name: Initialize CodeQL 91 | uses: github/codeql-action/init@v2 92 | with: 93 | language: ${{ matrix.language }} 94 | 95 | - name: Lint project 96 | run: yarn lint 97 | 98 | - name: CodeQL! 99 | uses: github/codeql-action/analyze@v2 100 | -------------------------------------------------------------------------------- /.github/workflows/ESLint.yml: -------------------------------------------------------------------------------- 1 | # 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 2 | # Copyright (c) 2021-2022 Noelware 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in all 12 | # copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | # SOFTWARE. 21 | 22 | name: ESLint 23 | on: 24 | workflow_dispatch: 25 | push: 26 | branches: 27 | - master 28 | 29 | paths-ignore: 30 | - '.github/**' 31 | - '.vscode/**' 32 | - 'assets/**' 33 | - 'docker/**' 34 | - '.idea/**' 35 | - '.dockerignore' 36 | - '.gitignore' 37 | - '**.md' 38 | - 'LICENSE' 39 | - 'renovate.json' 40 | 41 | pull_request: 42 | branches: 43 | - master 44 | 45 | paths-ignore: 46 | - '.github/**' 47 | - '.vscode/**' 48 | - 'assets/**' 49 | - 'docker/**' 50 | - '.idea/**' 51 | - '.dockerignore' 52 | - '.gitignore' 53 | - '**.md' 54 | - 'LICENSE' 55 | - 'renovate.json' 56 | jobs: 57 | lint: 58 | name: ESLint 59 | runs-on: ubuntu-latest 60 | steps: 61 | - name: Checkout repository 62 | uses: actions/checkout@v3 63 | 64 | - name: Setup Node.js v18 65 | uses: actions/setup-node@v3 66 | with: 67 | node-version: 18.x 68 | 69 | - name: Setup Yarn and node-modules cache 70 | id: yarn-cache 71 | uses: auguwu/node-pm-action@master 72 | 73 | - name: Install dependencies 74 | if: steps.yarn-cache.outputs.cache-hit != 'true' || steps.yarn-cache.outputs.node-modules-cache-hit != 'true' 75 | run: yarn install --immutable 76 | 77 | - name: Lint project 78 | run: yarn lint:ci 79 | 80 | - name: Build project 81 | run: yarn build 82 | 83 | # - name: Check for any compile errors 84 | # run: tsc --noEmit 85 | 86 | - name: Run tests 87 | run: yarn test:ci 88 | -------------------------------------------------------------------------------- /.github/workflows/Publish.yml: -------------------------------------------------------------------------------- 1 | # 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 2 | # Copyright (c) 2021-2022 Noelware 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in all 12 | # copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | # SOFTWARE. 21 | 22 | name: Release a new version 23 | on: 24 | release: 25 | types: 26 | - published 27 | jobs: 28 | release: 29 | name: Release a new version 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout repository 33 | uses: actions/checkout@v3 34 | 35 | - name: Setup Node.js v18 36 | uses: actions/setup-node@v3 37 | with: 38 | node-version: 18.x 39 | 40 | - name: Setup Yarn and node-modules cache 41 | id: yarn-cache 42 | uses: auguwu/node-pm-action@master 43 | 44 | - name: Install dependencies 45 | if: steps.yarn-cache.outputs.cache-hit != 'true' || steps.yarn-cache.outputs.node-modules-cache-hit != 'true' 46 | run: yarn install --immutable 47 | 48 | - name: Build distributions 49 | run: node scripts/publish 50 | 51 | - name: Upload each package 52 | run: chmod +x ./scripts/upload-packages.sh && ./scripts/upload-packages.sh 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/JetBrains+All,VisualStudioCode,VisualStudio,Node,Yarn 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=JetBrains+All,VisualStudioCode,VisualStudio,Node,Yarn 3 | 4 | ### JetBrains+all ### 5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 7 | 8 | # User-specific stuff 9 | .idea/**/workspace.xml 10 | .idea/**/tasks.xml 11 | .idea/**/usage.statistics.xml 12 | .idea/**/dictionaries 13 | .idea/**/shelf 14 | 15 | # AWS User-specific 16 | .idea/**/aws.xml 17 | 18 | # Generated files 19 | .idea/**/contentModel.xml 20 | 21 | # Sensitive or high-churn files 22 | .idea/**/dataSources/ 23 | .idea/**/dataSources.ids 24 | .idea/**/dataSources.local.xml 25 | .idea/**/sqlDataSources.xml 26 | .idea/**/dynamic.xml 27 | .idea/**/uiDesigner.xml 28 | .idea/**/dbnavigator.xml 29 | 30 | # Gradle 31 | .idea/**/gradle.xml 32 | .idea/**/libraries 33 | 34 | # Gradle and Maven with auto-import 35 | # When using Gradle or Maven with auto-import, you should exclude module files, 36 | # since they will be recreated, and may cause churn. Uncomment if using 37 | # auto-import. 38 | # .idea/artifacts 39 | # .idea/compiler.xml 40 | # .idea/jarRepositories.xml 41 | # .idea/modules.xml 42 | # .idea/*.iml 43 | # .idea/modules 44 | # *.iml 45 | # *.ipr 46 | 47 | # CMake 48 | cmake-build-*/ 49 | 50 | # Mongo Explorer plugin 51 | .idea/**/mongoSettings.xml 52 | 53 | # File-based project format 54 | *.iws 55 | 56 | # IntelliJ 57 | out/ 58 | 59 | # mpeltonen/sbt-idea plugin 60 | .idea_modules/ 61 | 62 | # JIRA plugin 63 | atlassian-ide-plugin.xml 64 | 65 | # Cursive Clojure plugin 66 | .idea/replstate.xml 67 | 68 | # SonarLint plugin 69 | .idea/sonarlint/ 70 | 71 | # Crashlytics plugin (for Android Studio and IntelliJ) 72 | com_crashlytics_export_strings.xml 73 | crashlytics.properties 74 | crashlytics-build.properties 75 | fabric.properties 76 | 77 | # Editor-based Rest Client 78 | .idea/httpRequests 79 | 80 | # Android studio 3.1+ serialized cache file 81 | .idea/caches/build_file_checksums.ser 82 | 83 | ### JetBrains+all Patch ### 84 | # Ignore everything but code style settings and run configurations 85 | # that are supposed to be shared within teams. 86 | 87 | .idea/* 88 | 89 | !.idea/codeStyles 90 | !.idea/runConfigurations 91 | 92 | ### Node ### 93 | # Logs 94 | logs 95 | *.log 96 | npm-debug.log* 97 | yarn-debug.log* 98 | yarn-error.log* 99 | lerna-debug.log* 100 | .pnpm-debug.log* 101 | 102 | # Diagnostic reports (https://nodejs.org/api/report.html) 103 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 104 | 105 | # Runtime data 106 | pids 107 | *.pid 108 | *.seed 109 | *.pid.lock 110 | 111 | # Directory for instrumented libs generated by jscoverage/JSCover 112 | lib-cov 113 | 114 | # Coverage directory used by tools like istanbul 115 | coverage 116 | *.lcov 117 | 118 | # nyc test coverage 119 | .nyc_output 120 | 121 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 122 | .grunt 123 | 124 | # Bower dependency directory (https://bower.io/) 125 | bower_components 126 | 127 | # node-waf configuration 128 | .lock-wscript 129 | 130 | # Compiled binary addons (https://nodejs.org/api/addons.html) 131 | build/Release 132 | 133 | # Dependency directories 134 | node_modules/ 135 | jspm_packages/ 136 | 137 | # Snowpack dependency directory (https://snowpack.dev/) 138 | web_modules/ 139 | 140 | # TypeScript cache 141 | *.tsbuildinfo 142 | 143 | # Optional npm cache directory 144 | .npm 145 | 146 | # Optional eslint cache 147 | .eslintcache 148 | 149 | # Optional stylelint cache 150 | .stylelintcache 151 | 152 | # Microbundle cache 153 | .rpt2_cache/ 154 | .rts2_cache_cjs/ 155 | .rts2_cache_es/ 156 | .rts2_cache_umd/ 157 | 158 | # Optional REPL history 159 | .node_repl_history 160 | 161 | # Output of 'npm pack' 162 | *.tgz 163 | 164 | # Yarn Integrity file 165 | .yarn-integrity 166 | 167 | # dotenv environment variable files 168 | .env 169 | .env.development.local 170 | .env.test.local 171 | .env.production.local 172 | .env.local 173 | 174 | # parcel-bundler cache (https://parceljs.org/) 175 | .cache 176 | .parcel-cache 177 | 178 | # Next.js build output 179 | .next 180 | out 181 | 182 | # Nuxt.js build / generate output 183 | .nuxt 184 | dist 185 | 186 | # Gatsby files 187 | .cache/ 188 | # Comment in the public line in if your project uses Gatsby and not Next.js 189 | # https://nextjs.org/blog/next-9-1#public-directory-support 190 | # public 191 | 192 | # vuepress build output 193 | .vuepress/dist 194 | 195 | # vuepress v2.x temp and cache directory 196 | .temp 197 | 198 | # Docusaurus cache and generated files 199 | .docusaurus 200 | 201 | # Serverless directories 202 | .serverless/ 203 | 204 | # FuseBox cache 205 | .fusebox/ 206 | 207 | # DynamoDB Local files 208 | .dynamodb/ 209 | 210 | # TernJS port file 211 | .tern-port 212 | 213 | # Stores VSCode versions used for testing VSCode extensions 214 | .vscode-test 215 | 216 | # yarn v2 217 | .yarn/cache 218 | .yarn/unplugged 219 | .yarn/build-state.yml 220 | .yarn/install-state.gz 221 | .pnp.* 222 | 223 | ### Node Patch ### 224 | # Serverless Webpack directories 225 | .webpack/ 226 | 227 | # Optional stylelint cache 228 | 229 | # SvelteKit build / generate output 230 | .svelte-kit 231 | 232 | ### VisualStudioCode ### 233 | .vscode/* 234 | !.vscode/settings.json 235 | !.vscode/tasks.json 236 | !.vscode/launch.json 237 | !.vscode/extensions.json 238 | !.vscode/*.code-snippets 239 | 240 | # Local History for Visual Studio Code 241 | .history/ 242 | 243 | # Built Visual Studio Code Extensions 244 | *.vsix 245 | 246 | ### VisualStudioCode Patch ### 247 | # Ignore all local history of files 248 | .history 249 | .ionide 250 | 251 | # Support for Project snippet scope 252 | .vscode/*.code-snippets 253 | 254 | # Ignore code-workspaces 255 | *.code-workspace 256 | 257 | ### yarn ### 258 | # https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored 259 | 260 | .yarn/* 261 | !.yarn/releases 262 | !.yarn/patches 263 | !.yarn/plugins 264 | !.yarn/sdks 265 | !.yarn/versions 266 | 267 | # if you are NOT using Zero-installs, then: 268 | # comment the following lines 269 | !.yarn/cache 270 | 271 | # and uncomment the following lines 272 | # .pnp.* 273 | 274 | ### VisualStudio ### 275 | ## Ignore Visual Studio temporary files, build results, and 276 | ## files generated by popular Visual Studio add-ons. 277 | ## 278 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 279 | 280 | # User-specific files 281 | *.rsuser 282 | *.suo 283 | *.user 284 | *.userosscache 285 | *.sln.docstates 286 | 287 | # User-specific files (MonoDevelop/Xamarin Studio) 288 | *.userprefs 289 | 290 | # Mono auto generated files 291 | mono_crash.* 292 | 293 | # Build results 294 | [Dd]ebug/ 295 | [Dd]ebugPublic/ 296 | [Rr]elease/ 297 | [Rr]eleases/ 298 | x64/ 299 | x86/ 300 | [Ww][Ii][Nn]32/ 301 | [Aa][Rr][Mm]/ 302 | [Aa][Rr][Mm]64/ 303 | bld/ 304 | [Bb]in/ 305 | [Oo]bj/ 306 | [Ll]og/ 307 | [Ll]ogs/ 308 | 309 | # Visual Studio 2015/2017 cache/options directory 310 | .vs/ 311 | # Uncomment if you have tasks that create the project's static files in wwwroot 312 | #wwwroot/ 313 | 314 | # Visual Studio 2017 auto generated files 315 | Generated\ Files/ 316 | 317 | # MSTest test Results 318 | [Tt]est[Rr]esult*/ 319 | [Bb]uild[Ll]og.* 320 | 321 | # NUnit 322 | *.VisualState.xml 323 | TestResult.xml 324 | nunit-*.xml 325 | 326 | # Build Results of an ATL Project 327 | [Dd]ebugPS/ 328 | [Rr]eleasePS/ 329 | dlldata.c 330 | 331 | # Benchmark Results 332 | BenchmarkDotNet.Artifacts/ 333 | 334 | # .NET Core 335 | project.lock.json 336 | project.fragment.lock.json 337 | artifacts/ 338 | 339 | # ASP.NET Scaffolding 340 | ScaffoldingReadMe.txt 341 | 342 | # StyleCop 343 | StyleCopReport.xml 344 | 345 | # Files built by Visual Studio 346 | *_i.c 347 | *_p.c 348 | *_h.h 349 | *.ilk 350 | *.meta 351 | *.obj 352 | *.iobj 353 | *.pch 354 | *.pdb 355 | *.ipdb 356 | *.pgc 357 | *.pgd 358 | *.rsp 359 | *.sbr 360 | *.tlb 361 | *.tli 362 | *.tlh 363 | *.tmp 364 | *.tmp_proj 365 | *_wpftmp.csproj 366 | *.tlog 367 | *.vspscc 368 | *.vssscc 369 | .builds 370 | *.pidb 371 | *.svclog 372 | *.scc 373 | 374 | # Chutzpah Test files 375 | _Chutzpah* 376 | 377 | # Visual C++ cache files 378 | ipch/ 379 | *.aps 380 | *.ncb 381 | *.opendb 382 | *.opensdf 383 | *.sdf 384 | *.cachefile 385 | *.VC.db 386 | *.VC.VC.opendb 387 | 388 | # Visual Studio profiler 389 | *.psess 390 | *.vsp 391 | *.vspx 392 | *.sap 393 | 394 | # Visual Studio Trace Files 395 | *.e2e 396 | 397 | # TFS 2012 Local Workspace 398 | $tf/ 399 | 400 | # Guidance Automation Toolkit 401 | *.gpState 402 | 403 | # ReSharper is a .NET coding add-in 404 | _ReSharper*/ 405 | *.[Rr]e[Ss]harper 406 | *.DotSettings.user 407 | 408 | # TeamCity is a build add-in 409 | _TeamCity* 410 | 411 | # DotCover is a Code Coverage Tool 412 | *.dotCover 413 | 414 | # AxoCover is a Code Coverage Tool 415 | .axoCover/* 416 | !.axoCover/settings.json 417 | 418 | # Coverlet is a free, cross platform Code Coverage Tool 419 | coverage*.json 420 | coverage*.xml 421 | coverage*.info 422 | 423 | # Visual Studio code coverage results 424 | *.coverage 425 | *.coveragexml 426 | 427 | # NCrunch 428 | _NCrunch_* 429 | .*crunch*.local.xml 430 | nCrunchTemp_* 431 | 432 | # MightyMoose 433 | *.mm.* 434 | AutoTest.Net/ 435 | 436 | # Web workbench (sass) 437 | .sass-cache/ 438 | 439 | # Installshield output folder 440 | [Ee]xpress/ 441 | 442 | # DocProject is a documentation generator add-in 443 | DocProject/buildhelp/ 444 | DocProject/Help/*.HxT 445 | DocProject/Help/*.HxC 446 | DocProject/Help/*.hhc 447 | DocProject/Help/*.hhk 448 | DocProject/Help/*.hhp 449 | DocProject/Help/Html2 450 | DocProject/Help/html 451 | 452 | # Click-Once directory 453 | publish/ 454 | 455 | # Publish Web Output 456 | *.[Pp]ublish.xml 457 | *.azurePubxml 458 | # Note: Comment the next line if you want to checkin your web deploy settings, 459 | # but database connection strings (with potential passwords) will be unencrypted 460 | *.pubxml 461 | *.publishproj 462 | 463 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 464 | # checkin your Azure Web App publish settings, but sensitive information contained 465 | # in these scripts will be unencrypted 466 | PublishScripts/ 467 | 468 | # NuGet Packages 469 | *.nupkg 470 | # NuGet Symbol Packages 471 | *.snupkg 472 | # The packages folder can be ignored because of Package Restore 473 | **/[Pp]ackages/* 474 | # except build/, which is used as an MSBuild target. 475 | !**/[Pp]ackages/build/ 476 | # Uncomment if necessary however generally it will be regenerated when needed 477 | #!**/[Pp]ackages/repositories.config 478 | # NuGet v3's project.json files produces more ignorable files 479 | *.nuget.props 480 | *.nuget.targets 481 | 482 | # Microsoft Azure Build Output 483 | csx/ 484 | *.build.csdef 485 | 486 | # Microsoft Azure Emulator 487 | ecf/ 488 | rcf/ 489 | 490 | # Windows Store app package directories and files 491 | AppPackages/ 492 | BundleArtifacts/ 493 | Package.StoreAssociation.xml 494 | _pkginfo.txt 495 | *.appx 496 | *.appxbundle 497 | *.appxupload 498 | 499 | # Visual Studio cache files 500 | # files ending in .cache can be ignored 501 | *.[Cc]ache 502 | # but keep track of directories ending in .cache 503 | !?*.[Cc]ache/ 504 | 505 | # Others 506 | ClientBin/ 507 | ~$* 508 | *~ 509 | *.dbmdl 510 | *.dbproj.schemaview 511 | *.jfm 512 | *.pfx 513 | *.publishsettings 514 | orleans.codegen.cs 515 | 516 | # Including strong name files can present a security risk 517 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 518 | #*.snk 519 | 520 | # Since there are multiple workflows, uncomment next line to ignore bower_components 521 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 522 | #bower_components/ 523 | 524 | # RIA/Silverlight projects 525 | Generated_Code/ 526 | 527 | # Backup & report files from converting an old project file 528 | # to a newer Visual Studio version. Backup files are not needed, 529 | # because we have git ;-) 530 | _UpgradeReport_Files/ 531 | Backup*/ 532 | UpgradeLog*.XML 533 | UpgradeLog*.htm 534 | ServiceFabricBackup/ 535 | *.rptproj.bak 536 | 537 | # SQL Server files 538 | *.mdf 539 | *.ldf 540 | *.ndf 541 | 542 | # Business Intelligence projects 543 | *.rdl.data 544 | *.bim.layout 545 | *.bim_*.settings 546 | *.rptproj.rsuser 547 | *- [Bb]ackup.rdl 548 | *- [Bb]ackup ([0-9]).rdl 549 | *- [Bb]ackup ([0-9][0-9]).rdl 550 | 551 | # Microsoft Fakes 552 | FakesAssemblies/ 553 | 554 | # GhostDoc plugin setting file 555 | *.GhostDoc.xml 556 | 557 | # Node.js Tools for Visual Studio 558 | .ntvs_analysis.dat 559 | 560 | # Visual Studio 6 build log 561 | *.plg 562 | 563 | # Visual Studio 6 workspace options file 564 | *.opt 565 | 566 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 567 | *.vbw 568 | 569 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 570 | *.vbp 571 | 572 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 573 | *.dsw 574 | *.dsp 575 | 576 | # Visual Studio 6 technical files 577 | 578 | # Visual Studio LightSwitch build output 579 | **/*.HTMLClient/GeneratedArtifacts 580 | **/*.DesktopClient/GeneratedArtifacts 581 | **/*.DesktopClient/ModelManifest.xml 582 | **/*.Server/GeneratedArtifacts 583 | **/*.Server/ModelManifest.xml 584 | _Pvt_Extensions 585 | 586 | # Paket dependency manager 587 | .paket/paket.exe 588 | paket-files/ 589 | 590 | # FAKE - F# Make 591 | .fake/ 592 | 593 | # CodeRush personal settings 594 | .cr/personal 595 | 596 | # Python Tools for Visual Studio (PTVS) 597 | __pycache__/ 598 | *.pyc 599 | 600 | # Cake - Uncomment if you are using it 601 | # tools/** 602 | # !tools/packages.config 603 | 604 | # Tabs Studio 605 | *.tss 606 | 607 | # Telerik's JustMock configuration file 608 | *.jmconfig 609 | 610 | # BizTalk build output 611 | *.btp.cs 612 | *.btm.cs 613 | *.odx.cs 614 | *.xsd.cs 615 | 616 | # OpenCover UI analysis results 617 | OpenCover/ 618 | 619 | # Azure Stream Analytics local run output 620 | ASALocalRun/ 621 | 622 | # MSBuild Binary and Structured Log 623 | *.binlog 624 | 625 | # NVidia Nsight GPU debugger configuration file 626 | *.nvuser 627 | 628 | # MFractors (Xamarin productivity tool) working folder 629 | .mfractor/ 630 | 631 | # Local History for Visual Studio 632 | .localhistory/ 633 | 634 | # Visual Studio History (VSHistory) files 635 | .vshistory/ 636 | 637 | # BeatPulse healthcheck temp database 638 | healthchecksdb 639 | 640 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 641 | MigrationBackup/ 642 | 643 | # Ionide (cross platform F# VS Code tools) working folder 644 | .ionide/ 645 | 646 | # Fody - auto-generated XML schema 647 | FodyWeavers.xsd 648 | 649 | # VS Code files for those working on multiple tools 650 | 651 | # Local History for Visual Studio Code 652 | 653 | # Windows Installer files from build outputs 654 | *.cab 655 | *.msi 656 | *.msix 657 | *.msm 658 | *.msp 659 | 660 | # JetBrains Rider 661 | *.sln.iml 662 | 663 | ### VisualStudio Patch ### 664 | # Additional files built by Visual Studio 665 | 666 | !.yarn/releases 667 | .yarn/cache 668 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .gitignore -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "tabWidth": 2, 4 | "singleQuote": true, 5 | "endOfLine": "lf", 6 | "printWidth": 120, 7 | "trailingComma": "none", 8 | "bracketSpacing": true 9 | } 10 | -------------------------------------------------------------------------------- /.stack-version: -------------------------------------------------------------------------------- 1 | # This file must be updated each version. Though, this can probably be 2 | # in the root package.json! 3 | 4 | 6.0.0 5 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | plugins: 4 | - path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs 5 | spec: "@yarnpkg/plugin-typescript" 6 | - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs 7 | spec: "@yarnpkg/plugin-workspace-tools" 8 | 9 | yarnPath: .yarn/releases/yarn-3.4.1.cjs 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2022 Noelware 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🧵 Lilith 2 | 3 | > _Application framework for TypeScript to build robust, and simple services_ 4 | 5 | **Lilith** is Noelware's framework to build JavaScript-based microservices with TypeScript! It is used to build robust applications with TypeScript with the tools you need to build it! Lilith comes with pre-built libraries that makes it easy to work with: 6 | 7 | - `@noelware/lilith-logging` **~** Package to handle logging scenarios, combined with the `@noelware/lilith-config` package also! 8 | - `@noelware/lilith-config` **~** Package to handle different configuration sources, that are easily injectable with the `@Variable` decorator. 9 | 10 | 11 | ## Usage 12 | 13 | > **Warning** — Lilith v6 and above is not compatible with older versions of **Lilith**, so you will need to 14 | > refractor your whole application that was built upon Lilith. You can read up on the [migration](#migrating-to-v6) section. 15 | 16 | ```sh 17 | $ npm install @noelware/lilith 18 | $ yarn add @noelware/lilith 19 | $ pnpm install @noelware/lilith 20 | ``` 21 | 22 | ```ts 23 | import { createLilith, singleton, service, inject } from '@noelware/lilith'; 24 | 25 | const container = createLilith({ 26 | singletons: [ 27 | // Defines a path to load singletons from 28 | { path: './path/to/singletons' }, 29 | singleton({ 30 | provide() { return Logger.create(); } 31 | onLoad(logger /*: Logger */) { /* ran once singleton is loaded into the container */ }, 32 | onDestroy(logger /*: Logger */) { /* destroy singleton */ } 33 | }) 34 | ], 35 | 36 | services: [ 37 | // Defines a path to load services from 38 | { path: './path/to/services' }, 39 | service({ 40 | name: 'myservice', 41 | children: [/* list of children that this service has control over */], 42 | onLoad() { /* called for loading the service (i.e, start http server/load config) */ }, 43 | onDestroy() { /* called for destroying the service (i.e, stopping http service) */ }, 44 | onChildLoad(child /*: any */) { /* called for loading children into the service scope */ }, 45 | onChildDestroy(child /*: any */) { /* called for destroying children into the service scope */ }, 46 | }) 47 | ] 48 | }); 49 | 50 | const logger = inject('logger'); 51 | // => Logger 52 | 53 | const service = inject('myservice'); 54 | // => Service 55 | ``` 56 | 57 | ## Packages 58 | 59 | ### @noelware/lilith 60 | 61 | **@noelware/lilith** is the main library that ties together with the new packages like **@noelware/lilith-logging**. You use **@noelware/lilith** to manage the lifecycle of the managed IoC container to do dependency injection out of the box. 62 | 63 | **@noelware/lilith** doesn't need any peer dependencies, but you will need to have `reflect-metadata` loaded before using **@noelware/lilith** because it depends on it! 64 | 65 | ```sh 66 | $ npm install @noelware/lilith 67 | $ yarn add @noelware/lilith 68 | $ pnpm install @noelware/lilith 69 | ``` 70 | 71 | ```ts 72 | import { Service, Inject, createLilith } from '@noelware/lilith'; 73 | 74 | @Service({ name: 'a name' }) 75 | class MyService { 76 | @Inject 77 | private readonly other!: OtherService; 78 | 79 | onLoad() { 80 | console.log('I have loaded!'); 81 | } 82 | } 83 | 84 | @Service({ name: 'another name', priority: 10 }) 85 | class OtherService { 86 | private readonly lucky: number = 42; 87 | 88 | get luckyNumber() { 89 | return this.lucky; 90 | } 91 | } 92 | 93 | const container = createLilith({ 94 | services: [MyService, OtherService] 95 | }); 96 | 97 | container.start(); 98 | ``` 99 | 100 | - **Lilith** will construct the container as a export of **Container**, 101 | - When you call **start**, the IoC hooks will be attached to the services in this minimal example, 102 | - Since services can be a higher priority, the services that have a high priority will be initialized first 103 | - You can't inject components that are even a higher priority in a high priority service since services are not 104 | lazily constructed (i.e, needed when it needs to be used). They're loaded automatically, only **singletons** are 105 | lazily loaded. 106 | - You can't have service names with the same name, you will get a `TypeError` thrown. 107 | 108 | ``` 109 | | ~~~~~~~~~~~~~~ | /----> | another name | 110 | | ioc container | - / \ 111 | | ~~~~~~~~~~~~~~ | \----> | a name | 112 | ``` 113 | 114 | ### @noelware/lilith-logging 115 | 116 | **@noelware/lilith-logging** is a service package that lets you inject a `LoggerFactoryService` into your services and creates a **Logger** for using logging. This package requires a peer dependency on **winston**: 117 | 118 | ```sh 119 | $ npm install @noelware/lilith @noelware/lilith-logging @noelware/lilith-logging-winston winston 120 | $ yarn add @noelware/lilith @noelware/lilith-logging @noelware/lilith-logging-winston winston 121 | $ pnpm install @noelware/lilith @noelware/lilith-logging @noelware/lilith-logging-winston winston 122 | ``` 123 | 124 | ```ts 125 | import { Service, Inject, createLilith } from '@noelware/lilith'; 126 | import { LogService, type Logger } from '@noelware/lilith-logging'; 127 | import { WinstonBackend } from '@noelware/lilith-logging-winston'; 128 | import winston from 'winston'; 129 | 130 | @Service({ name: 'my service name' }) 131 | class MyService { 132 | @Inject 133 | private readonly logging!: LogService; 134 | private readonly logger!: Logger; 135 | 136 | onLoad() { 137 | this.logger = this.logging.loggerFactory.get('my', 'service', 'info'); 138 | this.logger.info('I am loading stuff!'); 139 | } 140 | } 141 | 142 | const container = createLilith({ 143 | services: [ 144 | new LogService({ 145 | defaultLevel: 'debug', 146 | backend: new WinstonBackend({ 147 | transports: [new winston.transports.Console()] 148 | }) 149 | }), 150 | MyService 151 | ] 152 | }); 153 | 154 | container.start(); 155 | ``` 156 | 157 | ### @noelware/lilith-config 158 | 159 | **@noelware/lilith-config** is a service that gives you a way to simplify configuration files with a **Zod** schema. It has loaders for: 160 | 161 | - YAML (requires `js-yaml`) 162 | - JSON (no extra dependencies) 163 | - TOML (requires `@ltd/j-toml`) 164 | 165 | 166 | **@noelware/lilith-config** has a peer dependency on **zod** if you wish to have schema validation. It is not required to have **zod** installed, it'll just ignore the configuration schema if provided. 167 | 168 | ```sh 169 | $ npm install @noelware/lilith @noelware/lilith-config zod 170 | $ yarn add @noelware/lilith @noelware/lilith-config zod 171 | $ pnpm install @noelware/lilith @noelware/lilith-config zod 172 | ``` 173 | 174 | ```ts 175 | import { Service, Inject, createLilith } from '@noelware/lilith'; 176 | import { ConfigService, YamlLoader } from '@noelware/lilith-config'; 177 | import z from 'zod'; 178 | 179 | export interface Config { 180 | lol: boolean; 181 | } 182 | 183 | @Service({ name: 'my service' }) 184 | class MyService { 185 | @Inject 186 | private readonly config!: ConfigService; 187 | 188 | onLoad() { 189 | const shouldLol = this.config.get('lol'); 190 | if (shouldLol) { 191 | console.log('lol!!!!'); 192 | } 193 | } 194 | } 195 | 196 | const container = createLilith({ 197 | services: [ 198 | new ConfigService({ 199 | schema: z 200 | .object({ 201 | lol: z.boolean() 202 | }) 203 | .strict(), 204 | 205 | loader: new YamlLoader() 206 | }), 207 | MyService 208 | ] 209 | }); 210 | 211 | container.start(); 212 | const service = container.inject(MyService); 213 | ``` 214 | 215 | 334 | 335 | ## Contributing 336 | 337 | Thanks for considering contributing to **Lilith**! Before you boop your heart out on your keyboard ✧ ─=≡Σ((( つ•̀ω•́)つ, we recommend you to do the following: 338 | 339 | - Read the [Code of Conduct](./.github/CODE_OF_CONDUCT.md) 340 | - Read the [Contributing Guide](./.github/CONTRIBUTING.md) 341 | 342 | If you read both if you're a new time contributor, now you can do the following: 343 | 344 | - [Fork me! *\*♡( ⁎ᵕᴗᵕ⁎ )](https://github.com/Noelware/Lilith/fork) 345 | - Clone your fork on your machine: `git clone https://github.com/your-username/Lilith` 346 | - Create a new branch: `git checkout -b some-branch-name` 347 | - BOOP THAT KEYBOARD!!!! ♡┉ˏ͛ (❛ 〰 ❛)ˊˎ┉♡ 348 | - Commit your changes onto your branch: `git commit -am "add features (。>‿‿<。 )"` 349 | - Push it to the fork you created: `git push -u origin some-branch-name` 350 | - Submit a Pull Request and then cry! 。・゚゚・(థ Д థ。)・゚゚・。 351 | 352 | ## License 353 | 354 | **Lilith** is released under the **MIT License** with love by Noelware. :3 355 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | This page will be edited soon. You can contact Noelware by email (`team@noelware.org`) and we will sort it out via email. 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lilith/root", 3 | "description": "Root workspace for @lilith/ packages", 4 | "private": true, 5 | "version": "0.0.0-dev.0", 6 | "author": "Noel ", 7 | "packageManager": "yarn@3.4.1", 8 | "workspaces": [ 9 | "src/winston", 10 | "src/logging", 11 | "src/lilith", 12 | "src/config" 13 | ], 14 | "scripts": { 15 | "test:ci": "yarn fmt && yarn lint && node scripts/ci/vitest", 16 | "lint:ci": "yarn fmt && yarn lint && node scripts/ci/eslint", 17 | "build": "yarn lint && yarn fmt && node scripts/build", 18 | "lint": "yarn workspaces foreach -pt run lint", 19 | "test": "yarn fmt && yarn lint && yarn workspaces foreach -pt run test", 20 | "fmt": "prettier --config ./.prettierrc.json --write **/*.{ts,tsx,js,md,json,yaml,yml}" 21 | }, 22 | "devDependencies": { 23 | "@actions/core": "1.10.0", 24 | "@augu/eslint-config": "4.0.1", 25 | "@noelware/utils": "2.2.1", 26 | "@types/eslint": "8.4.6", 27 | "@types/rimraf": "3.0.2", 28 | "eslint": "8.24.0", 29 | "eslint-config-prettier": "8.6.0", 30 | "leeks.js": "0.2.4", 31 | "log-symbols": "5.1.0", 32 | "prettier": "2.7.1", 33 | "rimraf": "4.1.2", 34 | "tsup": "6.6.3", 35 | "vitest": "0.23.4" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "automerge": true, 3 | "extends": ["config:base", "default:timezone(America/Phoenix)"], 4 | "vulnerabilityAlerts": { 5 | "labels": ["security"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /scripts/build.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | // @ts-check 25 | 26 | const { copyFile, readFile, writeFile } = require('fs/promises'); 27 | const LoggerFactory = require('./util/log'); 28 | const { colors } = require('leeks.js'); 29 | const { build } = require('tsup'); 30 | const { join } = require('path'); 31 | 32 | const log = LoggerFactory.get('build'); 33 | async function main() { 34 | const stackVersion = await readFile(join(process.cwd(), '.stack-version'), 'utf-8').then((f) => 35 | f 36 | .split('\n') 37 | .filter((f) => f && !f.startsWith('#')) 38 | .at(0) 39 | ); 40 | 41 | log.info(`Building for stack version v${stackVersion}!`); 42 | 43 | for (const library of ['lilith', 'config', 'logging', 'winston']) { 44 | log.info(`Building library distribution ${colors.gray(`@lilith/${library === 'lilith' ? 'core' : library}`)}...`); 45 | const now = Date.now(); 46 | await build({ 47 | sourcemap: true, 48 | treeshake: true, 49 | tsconfig: join(process.cwd(), 'src', library, 'tsconfig.json'), 50 | platform: 'node', 51 | target: 'node16', 52 | format: ['cjs', 'esm'], 53 | outDir: join(process.cwd(), 'src', library, 'dist'), 54 | minify: (process.env.NODE_ENV && process.env.NODE_ENV === 'production') || false, 55 | bundle: true, 56 | clean: true, 57 | entry: [join(process.cwd(), 'src', library, 'index.ts')], 58 | name: `@lilith/${library === 'lilith' ? 'core' : library}`, 59 | dts: false 60 | }); 61 | 62 | await copyFile( 63 | join(process.cwd(), 'src', library, 'typings.d.ts'), 64 | join(process.cwd(), 'src', library, 'dist', 'index.d.ts') 65 | ); 66 | 67 | let contents = await readFile(join(process.cwd(), 'src', library, 'dist', 'index.d.ts'), 'utf-8'); 68 | contents = contents.replace('@lilith/{library}', `@lilith/${library === 'lilith' ? 'core' : library}`); 69 | 70 | // @ts-expect-error 71 | contents = contents.replace('{version}', stackVersion); 72 | 73 | await writeFile(join(process.cwd(), 'src', library, 'dist', 'index.d.ts'), contents); 74 | log.success( 75 | `Took ${colors.gray(`${Date.now() - now}ms`)} to build distribution for library ${colors.gray( 76 | `@lilith/${library === 'lilith' ? 'core' : library}` 77 | )}!` 78 | ); 79 | } 80 | } 81 | 82 | main().catch((ex) => { 83 | log.error(ex); 84 | process.exit(1); 85 | }); 86 | -------------------------------------------------------------------------------- /scripts/ci/eslint.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | // @ts-check 25 | 26 | const { info, warning, error } = require('@actions/core'); 27 | const { readdir } = require('@noelware/utils'); 28 | const { ESLint } = require('eslint'); 29 | const { join } = require('path'); 30 | 31 | const LIBRARIES = ['lilith', 'config', 'logging']; 32 | 33 | const main = async () => { 34 | info('starting linter...'); 35 | 36 | const eslint = new ESLint({ 37 | useEslintrc: true 38 | }); 39 | 40 | for (const library of LIBRARIES) { 41 | const srcDir = join(process.cwd(), 'src', library); 42 | info(`Linting in directory [${srcDir}]`); 43 | 44 | const results = await eslint.lintFiles(await readdir(join(srcDir, 'src'), { extensions: ['.ts', '.tsx'] })); 45 | for (const result of results) { 46 | for (const message of result.messages) { 47 | const fn = message.severity === 1 ? warning : error; 48 | fn(`${result.filePath}:${message.line}:${message.column} [${message.ruleId}] :: ${message.message}`, { 49 | file: result.filePath, 50 | endColumn: message.endColumn, 51 | endLine: message.endLine, 52 | startColumn: message.column, 53 | startLine: message.line, 54 | title: `[${message.ruleId}] ${message.message}` 55 | }); 56 | } 57 | } 58 | } 59 | }; 60 | 61 | main().catch((ex) => { 62 | console.error(ex); 63 | process.exit(0); 64 | }); 65 | -------------------------------------------------------------------------------- /scripts/ci/vitest.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | // @ts-check 25 | 26 | const { info, error } = require('@actions/core'); 27 | const { colors } = require('leeks.js'); 28 | const { spawn } = require('child_process'); 29 | const { join } = require('path'); 30 | 31 | async function main() { 32 | // for (const project of ['lilith', 'config', 'fastify', 'http', 'logging', 'nextjs', 'vite']) { 33 | for (const project of ['lilith', 'config', 'logging', 'winston']) { 34 | const proc = spawn('yarn', ['vitest', '--run', '--reporter=json'], { cwd: join(process.cwd(), 'src', project) }); 35 | const chunks = []; 36 | 37 | proc.stdout.on('data', (chunk) => chunks.push(chunk)); 38 | proc.stderr.on('data', (chunk) => console.error(chunk.toString())); 39 | 40 | // let vitest run, and wait for exit. 41 | await new Promise((resolve) => proc.on('exit', resolve)); 42 | 43 | const buf = chunks[0]; 44 | if (buf === undefined || buf === null) throw new Error('Missing vitest report!'); 45 | 46 | const json = JSON.parse(buf.toString('utf-8').trim()); 47 | info( 48 | [ 49 | `Ran all tests with ${colors.gray('vitest')} in project ${colors.gray( 50 | `@lilith/${project === 'lilith' ? 'core' : project}` 51 | )}!`, 52 | ` - Total Test Suites: ${json.numTotalTestSuites}`, 53 | ` - Successful Tests: ${json.numPassedTests}`, 54 | ` - Failed Tests: ${json.numFailedTests}`, 55 | '', 56 | `It took ${Date.now() - json.startTime}ms to complete all tests!`, 57 | '' 58 | ].join('\n') 59 | ); 60 | 61 | for (const result of json.testResults) { 62 | info(`Suite in path [${result.name}] has ${result.status} in ${result.endTime - result.startTime}ms.`); 63 | for (const assertion of result.assertionResults) { 64 | if (assertion.status === 'failed') { 65 | for (const message of assertion.failureMessages) { 66 | error(`${assertion.title} in suite ${assertion.ancestorTitles[1]} :: FAILED [${message}]`, { 67 | file: result.name, 68 | startLine: assertion.location.line, 69 | startColumn: assertion.location.column 70 | }); 71 | } 72 | } else { 73 | info(`✔️ ${assertion.title} :: PASSED`); 74 | } 75 | } 76 | } 77 | } 78 | } 79 | 80 | main() 81 | .catch((ex) => { 82 | error(ex); 83 | process.exit(1); 84 | }); 85 | -------------------------------------------------------------------------------- /scripts/publish.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | // @ts-check 25 | 26 | const { copyFile, writeFile, mkdir, rm, readFile } = require('fs/promises'); 27 | const getStackVersion = require('./util/stack-version'); 28 | const { existsSync } = require('fs'); 29 | const LoggerFactory = require('./util/log'); 30 | const { colors } = require('leeks.js'); 31 | const { join } = require('path'); 32 | const { build } = require('tsup'); 33 | 34 | const LIBRARIES = ['lilith', 'config', 'logging', 'winston']; 35 | 36 | const log = LoggerFactory.get('publish'); 37 | async function main() { 38 | log.info('Preparing for publishing!'); 39 | 40 | const stackVersion = await getStackVersion(); 41 | log.info(`Stack Version: v${stackVersion}`); 42 | 43 | if (existsSync(join(process.cwd(), 'dist'))) await rm(join(process.cwd(), 'dist'), { recursive: true, force: true }); 44 | for (const library of LIBRARIES) { 45 | const dir = join(process.cwd(), 'dist', library === 'lilith' ? 'core' : library); 46 | await mkdir(dir, { recursive: true }); 47 | 48 | log.info(`Building library distribution ${colors.gray(`@lilith/${library === 'lilith' ? 'core' : library}`)}...`); 49 | const now = Date.now(); 50 | await build({ 51 | sourcemap: true, 52 | treeshake: true, 53 | tsconfig: join(process.cwd(), 'src', library, 'tsconfig.json'), 54 | platform: 'node', 55 | target: 'node16', 56 | format: ['cjs', 'esm'], 57 | outDir: join(dir, 'dist'), 58 | minify: true, 59 | bundle: true, 60 | clean: true, 61 | entry: [join(process.cwd(), 'src', library, 'index.ts')], 62 | name: `@lilith/${library === 'lilith' ? 'core' : library}`, 63 | dts: false 64 | }); 65 | 66 | await copyFile(join(process.cwd(), 'LICENSE'), join(dir, 'LICENSE')); 67 | await copyFile(join(process.cwd(), 'src', library, 'README.md'), join(dir, 'README.md')); 68 | await copyFile(join(process.cwd(), 'src', library, 'package.json'), join(dir, 'package.json')); 69 | await copyFile(join(process.cwd(), 'src', library, 'typings.d.ts'), join(dir, 'dist', 'index.d.ts')); 70 | 71 | const pkgContents = await readFile(join(dir, 'package.json'), 'utf-8'); 72 | // @ts-ignore 73 | const newPkgContents = pkgContents.replace('0.0.0-dev.0', stackVersion); 74 | await writeFile(join(dir, 'package.json'), newPkgContents); 75 | 76 | log.success( 77 | `Took ${colors.gray(`${Date.now() - now}ms`)} to build distribution for library ${colors.gray( 78 | `@lilith/${library === 'lilith' ? 'core' : library}` 79 | )}!` 80 | ); 81 | } 82 | } 83 | 84 | main().catch((ex) => { 85 | log.error(ex); 86 | process.exit(1); 87 | }); 88 | -------------------------------------------------------------------------------- /scripts/ts-runner.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* 4 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 5 | * Copyright (c) 2021-2022 Noelware 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | // @ts-check 27 | 28 | const main = async() => { 29 | return 0; 30 | }; 31 | 32 | main().catch(ex => { 33 | console.error('TypeScript scripting runner failed:', ex); 34 | process.exit(1); 35 | }); 36 | -------------------------------------------------------------------------------- /scripts/upload-packages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 4 | # Copyright (c) 2021-2022 Noelware 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | if [ -z "${NPM_TOKEN:-}" ]; then 25 | echo "Missing NPM_TOKEN environment variable!" 26 | exit 1 27 | fi 28 | 29 | npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}" 30 | 31 | LIBRARIES=( 32 | "core" 33 | "vite" 34 | "nextjs" 35 | "config" 36 | "fastify" 37 | "logging" 38 | "http" 39 | ) 40 | 41 | for lib in "${LIBRARIES[@]}"; do 42 | echo "$ npm publish $(realpath ./dist/$lib) --access=public" 43 | npm publish $(realpath ./dist/$lib) --access=public 44 | done 45 | -------------------------------------------------------------------------------- /scripts/util/exec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | // @ts-check 25 | 26 | const { exec: _exec } = require('child_process'); 27 | const LogFactory = require('./log'); 28 | 29 | /** 30 | * Executes a command asynchronously. 31 | * @param {string} command The command to use 32 | * @param {string[]} args Remaining arguments to use 33 | * @returns {Promise<{ stdout: string; stderr: string; }>} Object of the standard output and error. 34 | */ 35 | const exec = (command, args) => { 36 | const log = LogFactory.get(`exec:(${command})`); 37 | log.info(`Executing command [$ ${command} ${args.join(' ')}]`); 38 | 39 | return new Promise((resolve, reject) => 40 | _exec(`${command} ${args.join(' ')}`, (error, stdout, stderr) => { 41 | if (error !== null) return reject(error); 42 | resolve({ stdout, stderr }); 43 | }) 44 | ); 45 | }; 46 | 47 | module.exports = exec; 48 | -------------------------------------------------------------------------------- /scripts/util/log.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | // @ts-check 25 | 26 | const { colors, styles } = require('leeks.js'); 27 | const logSymbols = require('log-symbols'); 28 | const { format } = require('util'); 29 | 30 | /** Represents a factory for constructing loggers. */ 31 | class LoggerFactory { 32 | constructor() { 33 | /** @type {LogLevelObject} */ 34 | this.levels = { 35 | success: { color: colors.green, symbol: logSymbols.success }, 36 | error: { color: colors.red, symbol: logSymbols.error }, 37 | info: { color: colors.cyan, symbol: logSymbols.info }, 38 | warn: { color: colors.yellow, symbol: logSymbols.warning } 39 | }; 40 | } 41 | 42 | /** 43 | * Returns a new logger for the name specified. 44 | * @param {string} name The name of the logger. 45 | * @returns {Logger} The logger object 46 | */ 47 | get(name) { 48 | return { 49 | name, 50 | success: (...args) => 51 | console.log( 52 | `[${colors.gray(name)}] ${this.levels.success.color(this.levels.success.symbol)} ${this.levels.success.color( 53 | styles.bold('SUCCESS') 54 | )} ~> ${format(args[0], ...args.slice(1))}` 55 | ), 56 | 57 | error: (...args) => 58 | console.error( 59 | `[${colors.gray(name)}] ${this.levels.error.color(this.levels.error.symbol)} ${this.levels.error.color( 60 | styles.bold('ERROR') 61 | )} ~> ${format(args[0], ...args.slice(1))}` 62 | ), 63 | 64 | warn: (...args) => 65 | console.error( 66 | `[${colors.gray(name)}] ${this.levels.warn.color(this.levels.warn.symbol)} ${this.levels.warn.color( 67 | styles.bold('WARN') 68 | )} ~> ${format(args[0], ...args.slice(1))}` 69 | ), 70 | 71 | info: (...args) => 72 | console.error( 73 | `[${colors.gray(name)}] ${this.levels.info.color(this.levels.info.symbol)} ${this.levels.info.color( 74 | styles.bold('INFO') 75 | )} ~> ${format(args[0], ...args.slice(1))}` 76 | ) 77 | }; 78 | } 79 | } 80 | 81 | module.exports = new LoggerFactory(); 82 | 83 | /** 84 | * @typedef {{ [x in 'info' | 'error' | 'success' | 'warn']: LogLevelInfo; }} LogLevelObject 85 | * @typedef {object} LogLevelInfo 86 | * @prop {(t: string) => string} color The color function to use 87 | * @prop {string} symbol The log symbol to use 88 | * 89 | * @typedef {{ [x in 'info' | 'error' | 'success' | 'warn' ]: (...args: any[]) => void; } & { name: string }} Logger 90 | */ 91 | -------------------------------------------------------------------------------- /scripts/util/stack-version.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | // @ts-check 25 | const { readFile } = require('fs/promises'); 26 | const { join } = require('path'); 27 | 28 | const getStackVersion = () => 29 | readFile(join(process.cwd(), '.stack-version'), 'utf-8').then((f) => 30 | f 31 | .split('\n') 32 | .filter((f) => f.length !== 0 && !f.startsWith('#')) 33 | .at(0) 34 | ); 35 | 36 | module.exports = getStackVersion; 37 | -------------------------------------------------------------------------------- /src/config/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["prettier", "@augu/eslint-config/ts.js"] 3 | } 4 | -------------------------------------------------------------------------------- /src/config/README.md: -------------------------------------------------------------------------------- 1 | # 🧵 @noelware/lilith-config 2 | 3 | **@noelware/lilith-config** is a service library to implement configuration within the Lilith application lifecycle. Read more on the [root repository](https://github.com/Noelware/Lilith/blob/master/README.md) to know more. 4 | -------------------------------------------------------------------------------- /src/config/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | export { ConfigService } from './src/service'; 25 | export * from './src/loaders'; 26 | -------------------------------------------------------------------------------- /src/config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@noelware/lilith-config", 3 | "description": "🧵 Service to use different configuration providers in a single Lilith service!", 4 | "version": "0.0.0-dev.0", 5 | "main": "dist/index.js", 6 | "author": "Noel , Noelware Team ", 7 | "license": "MIT", 8 | "repository": "https://github.com/Noelware/Lilith/tree/master/src/config", 9 | "bugs": "https://github.com/Noelware/Lilith/issues", 10 | "types": "dist/index.d.ts", 11 | "files": [ 12 | "dist/" 13 | ], 14 | "exports": { 15 | ".": { 16 | "require": "./dist/index.js", 17 | "default": "./dist/index.js", 18 | "import": "./dist/index.mjs" 19 | } 20 | }, 21 | "scripts": { 22 | "test": "vitest --run", 23 | "lint": "eslint src --ext .ts,.tsx --fix" 24 | }, 25 | "dependencies": { 26 | "dot-prop": "7.2.0" 27 | }, 28 | "devDependencies": { 29 | "@augu/eslint-config": "4.0.1", 30 | "@augu/tsconfig": "1.1.1", 31 | "@types/js-yaml": "4.0.5", 32 | "@types/node": "18.13.0", 33 | "@typescript-eslint/eslint-plugin": "5.52.0", 34 | "@typescript-eslint/parser": "5.52.0", 35 | "eslint": "8.24.0", 36 | "eslint-config-prettier": "8.6.0", 37 | "type-fest": "3.6.0", 38 | "typescript": "4.9.5", 39 | "vitest": "0.23.4" 40 | }, 41 | "optionalDependencies": { 42 | "@ltd/j-toml": "1.38.0", 43 | "@noelware/lilith": "workspace:src/lilith", 44 | "js-yaml": "4.1.0", 45 | "zod": "3.20.6" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/config/src/loaders/base.loader.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | /** 25 | * Represents a base class to load the configuration file from, then serialize it 26 | * to the {@link Config} object. 27 | */ 28 | export abstract class BaseLoader { 29 | /** 30 | * Deserializes the content provided and returns it as a {@link Config} object. 31 | * @param contents The contents to deserialize 32 | */ 33 | abstract deserialize(contents: string): Config; 34 | 35 | /** 36 | * Serializes the configuration object into a string! 37 | * @param config The configuration to serialize 38 | */ 39 | abstract serialize(config: Config): string; 40 | } 41 | -------------------------------------------------------------------------------- /src/config/src/loaders/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | export * from './yaml.loader'; 25 | export * from './toml.loader'; 26 | export * from './json.loader'; 27 | export * from './base.loader'; 28 | -------------------------------------------------------------------------------- /src/config/src/loaders/json.loader.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import { BaseLoader } from './base.loader'; 25 | 26 | export class JsonLoader extends BaseLoader { 27 | override deserialize(contents: string): Config { 28 | return JSON.parse(contents); 29 | } 30 | 31 | override serialize(config: Config): string { 32 | return JSON.stringify(config, null, 4); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/config/src/loaders/toml.loader.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import { BaseLoader } from './base.loader'; 25 | import { tryRequire } from '@noelware/utils'; 26 | 27 | type Toml = typeof import('@ltd/j-toml'); 28 | 29 | export class TomlLoader extends BaseLoader { 30 | private _library: Toml = null!; 31 | constructor() { 32 | super(); 33 | this._library = tryRequire('@ltd/j-toml'); 34 | } 35 | 36 | override deserialize(contents: string): Config { 37 | return this._library.parse(contents) as Config; 38 | } 39 | 40 | override serialize(config: Config): string { 41 | return this._library.stringify(config as any, { indent: 4 }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/config/src/loaders/yaml.loader.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import { BaseLoader } from './base.loader'; 25 | import { tryRequire } from '@noelware/utils'; 26 | 27 | type YAML = typeof import('js-yaml'); 28 | 29 | export class YamlLoader extends BaseLoader { 30 | private _library: YAML; 31 | constructor() { 32 | super(); 33 | 34 | this._library = tryRequire('js-yaml'); 35 | } 36 | 37 | override deserialize(contents: string): Config { 38 | return this._library.load(contents) as unknown as Config; 39 | } 40 | 41 | override serialize(config: Config): string { 42 | return this._library.dump(config, { noArrayIndent: false, indent: 4 }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/config/src/service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import { ChildLifecycleEvents, Service, useContainer } from '@lilith/core'; 25 | import type { BaseLoader } from './loaders'; 26 | import { existsSync } from 'fs'; 27 | import { get, set } from 'dot-prop'; 28 | import { readFile } from 'fs/promises'; 29 | import { isObject } from '@noelware/utils'; 30 | import type { Get } from 'type-fest'; 31 | import type z from 'zod'; 32 | 33 | export interface ConfigServiceOptions< 34 | Config, 35 | Schema extends z.ZodObject> = z.ZodObject> 36 | > { 37 | schema?: Schema; 38 | loader: BaseLoader; 39 | file: string; 40 | } 41 | 42 | /** 43 | * Represents a service to load a configuration file with a specific loader and you can 44 | * use variables from the container to inject from this service. 45 | * 46 | * ## Example 47 | * ```ts 48 | * import { Inject, Service, createLilith } from '@lilith/core'; 49 | * import { ConfigService, YamlLoader } from '@lilith/config'; 50 | * 51 | * interface Config { 52 | * beeps: true 53 | * } 54 | * 55 | * (@)Service({ name: 'my service' }) 56 | * class MyService { 57 | * (@)Inject private readonly config!: ConfigService; 58 | * 59 | * onLoad() { 60 | * this.config.get('beeps'); 61 | * // => boolean 62 | * } 63 | * } 64 | * 65 | * const container = createLilith({ 66 | * singletons: [MySingleton], 67 | * services: [ 68 | * new ConfigService({ loader: new YamlLoader(), file: './my/file/path.yaml' }) 69 | * ] 70 | * }); 71 | * 72 | * container.start(); 73 | * ``` 74 | */ 75 | @Service({ name: 'lilith:config', priority: 100 }) 76 | export class ConfigService implements ChildLifecycleEvents { 77 | private readonly _schema?: z.ZodObject>; 78 | private readonly _loader: BaseLoader; 79 | private readonly _path: string; 80 | private _config: Config = null!; 81 | 82 | constructor({ schema, loader, file }: ConfigServiceOptions) { 83 | this._schema = schema; 84 | this._loader = loader; 85 | this._path = file; 86 | } 87 | 88 | async onLoad() { 89 | // Check if the file exists 90 | if (!existsSync(this._path)) throw new Error(`Path [${this._path}] doesn't exist!`); 91 | 92 | // Now, let's see if we can load it from the loader 93 | const contents = await readFile(this._path, 'utf-8'); 94 | this._config = this._loader.deserialize(contents); 95 | 96 | // Check if Zod is installed 97 | if (this._schema !== undefined) { 98 | const isAvailable = (() => { 99 | try { 100 | require('zod'); 101 | return true; 102 | } catch { 103 | return false; 104 | } 105 | })(); 106 | 107 | if (!isAvailable) throw new Error('Schema was provided but `zod` was not installed?'); 108 | await this._schema!.safeParseAsync(this._config); 109 | } 110 | 111 | const recurse = (obj: T, current?: string) => { 112 | const results: Record = {}; 113 | for (const key in obj) { 114 | const value = obj[key]; 115 | const newKey = current ? `${current}.${key}` : key; 116 | if (value && isObject(value)) { 117 | recurse(value, newKey); 118 | } else { 119 | results[newKey] = value; 120 | } 121 | } 122 | 123 | return results; 124 | }; 125 | 126 | const results = recurse(this._config); 127 | const container = useContainer(); 128 | for (const [key, value] of Object.entries(results)) { 129 | container.addVariable(key as unknown as any, value); 130 | } 131 | } 132 | 133 | get>(path: Path): unknown extends Value ? null : Value { 134 | const result = get(this._config, path); 135 | if (result === undefined) return null as any; 136 | 137 | return result as any; 138 | } 139 | 140 | set>(path: Path, value: Value): Value { 141 | set(this._config, path, value); 142 | return this.get(path)!; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/config/tests/__fixtures__/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "users": ["noel", "ice"], 3 | "boops": true 4 | } 5 | -------------------------------------------------------------------------------- /src/config/tests/service.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import 'reflect-metadata'; 25 | 26 | import { beforeAll, describe, expect, test } from 'vitest'; 27 | import { Container, createLilith } from '@lilith/core'; 28 | import { ConfigService } from '../src/service'; 29 | import { JsonLoader } from 'src/loaders'; 30 | import { join } from 'path'; 31 | 32 | interface Config { 33 | users: string[]; 34 | boops: boolean; 35 | } 36 | 37 | describe('ConfigService (@lilith/config)', () => { 38 | let container!: Container; 39 | beforeAll(() => { 40 | container = createLilith({ 41 | services: [ 42 | // @ts-ignore 43 | new ConfigService({ 44 | loader: new JsonLoader(), 45 | file: join(process.cwd(), 'tests', '__fixtures__', 'example.json') 46 | }) 47 | ] 48 | }); 49 | 50 | container.on('debug', console.log); 51 | }); 52 | 53 | test('should load', async () => { 54 | await expect(container.start()).resolves.toBeUndefined(); 55 | }); 56 | 57 | test('should inject config service', () => { 58 | const service = container.inject>(ConfigService); 59 | expect(service).not.toBeNull(); 60 | 61 | expect(service!.get('users')).toStrictEqual(['noel', 'ice']); 62 | expect(service!.get('boops')).toBeTruthy(); 63 | }); 64 | 65 | test('should lookup from variable table', () => { 66 | expect(container.variable('boops')).not.toBeNull(); 67 | expect(container.variable('boops')).toBeTruthy(); 68 | expect(container.variable('users')).toStrictEqual(['noel', 'ice']); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /src/config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@augu/tsconfig/dom.json", 3 | "compilerOptions": { 4 | "moduleResolution": "node", 5 | "removeComments": true, 6 | "types": ["node", "reflect-metadata"], 7 | "baseUrl": ".", 8 | "noEmit": true // tsup does this for us 9 | }, 10 | "exclude": ["node_modules"], 11 | "include": ["**/*.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /src/config/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | /* 25 | * TypeScript declarations for @lilith/{library} v{version} 26 | * 27 | * ## Maintainers 28 | * - Noel (https://floofy.dev) 29 | * 30 | * ## Contributors 31 | * - Noel (https://floofy.dev) 32 | */ 33 | 34 | import { ChildLifecycleEvents } from '@lilith/core'; 35 | import { Get } from 'type-fest'; 36 | import z from 'zod'; 37 | 38 | declare namespace Config { 39 | /** 40 | * Represents a service to load a configuration file with a specific loader and you can 41 | * use variables from the container to inject from this service. 42 | * 43 | * ## Example 44 | * ```ts 45 | * import { Inject, Service, createLilith } from '@lilith/core'; 46 | * import { ConfigService, YamlLoader } from '@lilith/config'; 47 | * 48 | * interface Config { 49 | * beeps: true 50 | * } 51 | * 52 | * (@)Service({ name: 'my service' }) 53 | * class MyService { 54 | * (@)Inject private readonly config!: ConfigService; 55 | * 56 | * onLoad() { 57 | * this.config.get('beeps'); 58 | * // => boolean 59 | * } 60 | * } 61 | * 62 | * const container = createLilith({ 63 | * singletons: [MySingleton], 64 | * services: [ 65 | * new ConfigService({ loader: new YamlLoader(), file: './my/file/path.yaml' }) 66 | * ] 67 | * }); 68 | * 69 | * container.start(); 70 | * ``` 71 | */ 72 | export class ConfigService implements ChildLifecycleEvents { 73 | onLoad(): Promise; 74 | get>(path: Path): unknown extends Value ? null : Value; 75 | set>(path: Path, value: Value): Value; 76 | } 77 | 78 | /** 79 | * Represents a base class to load the configuration file from, then serialize it 80 | * to the {@link Config} object. 81 | */ 82 | export abstract class BaseLoader { 83 | /** 84 | * Deserializes the content provided and returns it as a {@link Config} object. 85 | * @param contents The contents to deserialize 86 | */ 87 | abstract deserialize(contents: string): Config; 88 | 89 | /** 90 | * Serializes the configuration object into a string! 91 | * @param config The configuration to serialize 92 | */ 93 | abstract serialize(config: Config): string; 94 | } 95 | 96 | export class JsonLoader extends BaseLoader { 97 | /** 98 | * Deserializes the content provided and returns it as a {@link Config} object. 99 | * @param contents The contents to deserialize 100 | */ 101 | deserialize(contents: string): Config; 102 | 103 | /** 104 | * Serializes the configuration object into a string! 105 | * @param config The configuration to serialize 106 | */ 107 | serialize(config: Config): string; 108 | } 109 | 110 | export class YamlLoader extends BaseLoader { 111 | /** 112 | * Deserializes the content provided and returns it as a {@link Config} object. 113 | * @param contents The contents to deserialize 114 | */ 115 | deserialize(contents: string): Config; 116 | 117 | /** 118 | * Serializes the configuration object into a string! 119 | * @param config The configuration to serialize 120 | */ 121 | serialize(config: Config): string; 122 | } 123 | 124 | export class TomlLoader extends BaseLoader { 125 | /** 126 | * Deserializes the content provided and returns it as a {@link Config} object. 127 | * @param contents The contents to deserialize 128 | */ 129 | deserialize(contents: string): Config; 130 | 131 | /** 132 | * Serializes the configuration object into a string! 133 | * @param config The configuration to serialize 134 | */ 135 | serialize(config: Config): string; 136 | } 137 | } 138 | 139 | export as namespace Config; 140 | export = Config; 141 | -------------------------------------------------------------------------------- /src/config/vitest.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import { defineConfig } from 'vitest/config'; 25 | 26 | export default defineConfig({ 27 | test: { dir: './tests' } 28 | }); 29 | -------------------------------------------------------------------------------- /src/http/.eslintrc.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noel-archive/Lilith/0914f7d37e842653c5078f5dad0ee985975d41a2/src/http/.eslintrc.json -------------------------------------------------------------------------------- /src/http/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noel-archive/Lilith/0914f7d37e842653c5078f5dad0ee985975d41a2/src/http/README.md -------------------------------------------------------------------------------- /src/http/src/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noel-archive/Lilith/0914f7d37e842653c5078f5dad0ee985975d41a2/src/http/src/index.ts -------------------------------------------------------------------------------- /src/http/vitest.config.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noel-archive/Lilith/0914f7d37e842653c5078f5dad0ee985975d41a2/src/http/vitest.config.ts -------------------------------------------------------------------------------- /src/lilith/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["prettier", "@augu/eslint-config/ts.js"] 3 | } 4 | -------------------------------------------------------------------------------- /src/lilith/README.md: -------------------------------------------------------------------------------- 1 | # 🧵 @noelware/lilith 2 | 3 | **@noelware/lilith** is the main package for using the **Lilith** platform. Read more on the [root repository](https://github.com/Noelware/Lilith/blob/master/README.md) to know more. 4 | -------------------------------------------------------------------------------- /src/lilith/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import { 25 | ChildLifecycleEvents, 26 | ContainerEventNames, 27 | ContainerEvents, 28 | LifecycleEvents, 29 | LilithObject, 30 | Load, 31 | MetadataKeys, 32 | ReplaceColonWithDash, 33 | Service, 34 | Singleton, 35 | Variable 36 | } from './src/types'; 37 | 38 | export * from './src/create-lilith'; 39 | export * from './src/use-container'; 40 | export * from './src/decorators'; 41 | export * from './src/container'; 42 | export * from './src/functions'; 43 | export { ContainerEvents }; 44 | 45 | export type { 46 | ContainerEventNames, 47 | LifecycleEvents, 48 | LilithObject, 49 | Load, 50 | MetadataKeys, 51 | ReplaceColonWithDash, 52 | Service, 53 | Singleton, 54 | Variable 55 | }; 56 | -------------------------------------------------------------------------------- /src/lilith/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@noelware/lilith", 3 | "description": "🧵 Application framework for TypeScript to build robust, and simple services", 4 | "version": "0.0.0-dev.0", 5 | "main": "dist/index.js", 6 | "author": "Noel , Noelware Team ", 7 | "license": "MIT", 8 | "repository": "https://github.com/Noelware/Lilith/tree/master/src/lilith", 9 | "bugs": "https://github.com/Noelware/Lilith/issues", 10 | "types": "dist/index.d.ts", 11 | "files": [ 12 | "dist/" 13 | ], 14 | "exports": { 15 | ".": { 16 | "require": "./dist/index.js", 17 | "default": "./dist/index.js", 18 | "import": "./dist/index.mjs" 19 | } 20 | }, 21 | "scripts": { 22 | "test": "vitest --run", 23 | "lint": "eslint src --ext .ts,.tsx --fix" 24 | }, 25 | "dependencies": { 26 | "@noelware/utils": "2.2.1", 27 | "reflect-metadata": "0.1.13" 28 | }, 29 | "devDependencies": { 30 | "@augu/eslint-config": "4.0.1", 31 | "@augu/tsconfig": "1.1.1", 32 | "@types/node": "18.13.0", 33 | "@typescript-eslint/eslint-plugin": "5.52.0", 34 | "@typescript-eslint/parser": "5.52.0", 35 | "eslint": "8.24.0", 36 | "eslint-config-prettier": "8.6.0", 37 | "type-fest": "3.6.0", 38 | "typescript": "4.9.5", 39 | "vitest": "0.23.4" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/lilith/src/create-lilith.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import { Container, type ContainerOptions } from './container'; 25 | 26 | /** 27 | * Simpiled version of creating a {@link Container}. 28 | * @param options The container options. 29 | */ 30 | export const createLilith = ( 31 | options: ContainerOptions = { 32 | services: [], 33 | singletons: [], 34 | variables: {} as Variables 35 | } 36 | ) => new Container(options); 37 | -------------------------------------------------------------------------------- /src/lilith/src/crypto-utils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import { isNode } from '@noelware/utils'; 25 | 26 | export function randomBytes(len = 32) { 27 | if (isNode) { 28 | const crypto: typeof import('crypto') = require('crypto'); 29 | return crypto.randomBytes(len).toString('hex'); 30 | } 31 | 32 | // TODO: find way to support browsers for this 33 | throw new Error('@lilith/core/randomBytes is not supported in browser environments!'); 34 | 35 | // if (!window.crypto) throw new Error('@lilith/core requires window.crypto to generate a secure hash!'); 36 | 37 | // const buf = new Uint32Array(len); 38 | // window.crypto.getRandomValues(buf); 39 | 40 | // return parseHex(buf); 41 | } 42 | -------------------------------------------------------------------------------- /src/lilith/src/decorators/Inject.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import type { InjectionMeta } from '.'; 25 | import { MetadataKeys } from '../types'; 26 | 27 | /** Represents a type-alias of the injection metadata that is stored on the class. */ 28 | export type InjectInjectionMeta = InjectionMeta<{ $ref: any }>; 29 | 30 | /** 31 | * Injects a service or singleton into the parent class by the constructor, or a property of that 32 | * class. Injections happen automatically when you start your container, so be wary of that. 33 | * 34 | * Injections are also scoped to the service that it is targeting. Before Lilith v6, injections were 35 | * scoped to the global object, so you could use the `global` object to get all the injection references. Now, 36 | * they're stored in the service's class metadata. 37 | * 38 | * @example 39 | * ```ts 40 | * import { Service, singleton, createLilith } from '@lilith/core'; 41 | * 42 | * class SingletonThing { 43 | * doSomething(): void { console.log('Hello, world!'); } 44 | * } 45 | * 46 | * (@)Service({ name: 'my service name', priority: 200 }) 47 | * class MyService { 48 | * (@)Inject 49 | * private readonly singleton!: SingletonThing; 50 | * 51 | * onLoad() { 52 | * this.singleton.doSomething(); 53 | * } 54 | * } 55 | * 56 | * const container = createLilith({ 57 | * singletons: [SingletonThing], 58 | * services: [MyService] 59 | * }); 60 | * 61 | * // start the container so injections can apply 62 | * container.start(); 63 | * 64 | * // you should see 'Hello, world!' printed to the terminal. 65 | * container.destroy(); // Destroy the container and release the instance away. 66 | * ``` 67 | */ 68 | export const Inject: PropertyDecorator = (target, property) => { 69 | const $ref = Reflect.getMetadata('design:type', target, property); 70 | if (!$ref) throw new Error(`Unable to infer object instance in property ${String(property)}`); 71 | 72 | const injections: InjectInjectionMeta[] = Reflect.getMetadata(MetadataKeys.Injections, target) ?? []; 73 | injections.push({ 74 | property, 75 | $ref 76 | }); 77 | 78 | Reflect.defineMetadata(MetadataKeys.Injections, injections, target.constructor); 79 | }; 80 | -------------------------------------------------------------------------------- /src/lilith/src/decorators/Service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import { Load, MetadataKeys } from '../types'; 25 | 26 | /** 27 | * Represents the options for the {@link Service @Service} decorator. 28 | */ 29 | export interface ServiceDecoratorDeclaration { 30 | /** The priority of this service to be loaded. The higher the service priority is, the more chances it'll be loaded first. */ 31 | priority?: number; 32 | 33 | /** Children objects to load into this service. This can be any piece of data, even services! */ 34 | children?: Load[]; 35 | 36 | /** The name of the service to identify itself. */ 37 | name: string; 38 | } 39 | 40 | /** 41 | * Provides metadata about the target class that should be treated as a service that can inject 42 | * other services. 43 | * 44 | * You can retrieve all the metadata about all services in the {@link Reflect} object, as an example: 45 | * ```ts 46 | * // You are required to have `reflect-metadata` installed. It is a peer dependency to @lilith/core. 47 | * import { Service, MetadataKeys } from '@lilith/core'; 48 | * 49 | * (@)Service({ name: 'my service name', priority: 100 }) 50 | * class MyService {} 51 | * 52 | * const metadata = Reflect.getMetadata(MetadataKeys.Service, MyService); 53 | * // => { name: String, children: any[], priority: integer } | undefined 54 | * ``` 55 | * 56 | * Lilith does injecting from the constructor, or any properties automatically when you initialized 57 | * your container, so you don't have to do it yourself. You can also use the [[Container#addService]] to 58 | * add a service at runtime. 59 | * 60 | * @param param0 The {@link ServiceDecoratorDeclaration} object. 61 | * @param param0.priority The priority of this service to be loaded. The higher the service priority is, the more chances it'll be loaded first. 62 | * @param param0.children A {@link Load} type that can represents an absolute path, array of classes/instances. 63 | * @param param0.name The name of the service, to identify itself. 64 | * @returns A {@link ClassDecorator}. 65 | */ 66 | export const Service = ({ children, name, priority }: ServiceDecoratorDeclaration): ClassDecorator => { 67 | return (target) => { 68 | Reflect.defineMetadata(MetadataKeys.Service, { priority, children, name }, target); 69 | }; 70 | }; 71 | -------------------------------------------------------------------------------- /src/lilith/src/decorators/Variable.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import type { InjectionMeta } from '.'; 25 | import { MetadataKeys } from '../types'; 26 | 27 | /** Represents a type-alias of the variable injection metadata that is stored on the class. */ 28 | export type VariableInjectionMeta = InjectionMeta<{ key: string | symbol }>; 29 | 30 | /** 31 | * The **Variable** decorator allows you to reference container variables in your services. It is also 32 | * available as a constructor or property injection, refer to the [[@Inject]] decorator documentation 33 | * on how to use it, but replace [[@Inject]] with [[@Variable]] 34 | * 35 | * @param key The key that is scoped to a variable in the container. 36 | */ 37 | export const Variable = (key: string | symbol): PropertyDecorator => { 38 | return (target, property) => { 39 | const variables: VariableInjectionMeta[] = Reflect.getMetadata(MetadataKeys.Variable, target) ?? []; 40 | 41 | variables.push({ property, key }); 42 | Reflect.defineMetadata(MetadataKeys.Variable, variables, target); 43 | }; 44 | }; 45 | -------------------------------------------------------------------------------- /src/lilith/src/decorators/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | export interface InjectionMetaObject { 25 | property: string | symbol; 26 | } 27 | 28 | export type InjectionMeta = InjectionMetaObject & T; 29 | 30 | export * from './Variable'; 31 | export * from './Service'; 32 | export * from './Inject'; 33 | -------------------------------------------------------------------------------- /src/lilith/src/functions.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import type { LilithObject, Service, Singleton, LifecycleEvents } from './types'; 25 | import { Lazy, isObject, hasOwnProperty } from '@noelware/utils'; 26 | import { randomBytes } from './crypto-utils'; 27 | 28 | /** Represents a type-alis signature to create a service from the {@link service} function. */ 29 | export type CreateServiceOptions = Omit & { priority?: number }; 30 | 31 | /** Represents a interface for the {@link singleton} function. */ 32 | export interface CreateSingletonOptions extends LifecycleEvents<[T]> { 33 | provide(): T; 34 | } 35 | 36 | /** 37 | * Creates a new {@link Singleton} object. 38 | * @param provide The lazily evaluated function. 39 | * @returns A {@link Singleton} object. 40 | */ 41 | export function singleton(provide: () => T): Singleton; 42 | 43 | /** 44 | * Creates a new {@link Singleton} object with lifecycle hooks. 45 | * @param param0 The options for adding lifecycle hooks and the lazily evaluated data. 46 | * @param param0.provide The lazily evaluated function. 47 | * @param param0.onLoad Lifecycle hook when this singleton is being initialized. 48 | * @param param0.onDestroy Lifecycle hook when this singleton is being destroyed. 49 | * @returns A {@link Singleton} object. 50 | */ 51 | export function singleton({ provide, onLoad, onDestroy }: CreateSingletonOptions): Singleton; 52 | export function singleton(value: any): Singleton { 53 | if (isObject(value)) { 54 | const name = randomBytes(4); 55 | 56 | return { 57 | onDestroy: hasOwnProperty(value, 'onDestroy') ? (value as any).onDestroy : undefined, 58 | onLoad: hasOwnProperty(value, 'onLoad') ? (value as any).onLoad : undefined, 59 | $ref: new Lazy((value as any).provide), 60 | type: 'singleton', 61 | name 62 | }; 63 | } 64 | 65 | if (typeof value !== 'function') 66 | throw new TypeError( 67 | `Expecting \`function\`, received ${typeof value === 'object' ? 'array/object/null' : typeof value}` 68 | ); 69 | 70 | const name = randomBytes(4); 71 | return { 72 | onDestroy: undefined, 73 | onLoad: undefined, 74 | $ref: new Lazy(value), 75 | type: 'singleton', 76 | name 77 | }; 78 | } 79 | 80 | /** 81 | * Creates a new {@link Service} object. 82 | * @param param0 The options for creating a {@link Service}. 83 | * @param param0.name The name of the service 84 | * @param param0.children The children dependencies that the service should load. 85 | * @param param0.onChildDestroy Lifecycle method to do extra stuff when a child is being destroyed. 86 | * @param param0.onChildLoad Lifecycle method to do extra stuff when a child is being initialized. 87 | * @param param0.onDestroy Lifecycle method to do extra stuff when this service is being destroyed. 88 | * @param param0.onLoad Lifecycle method to do extra stuff when this service is being initialized. 89 | * @returns A {@link Service} object. 90 | */ 91 | export function service({ 92 | name, 93 | children, 94 | priority, 95 | onChildDestroy, 96 | onChildLoad, 97 | onDestroy, 98 | onLoad 99 | }: CreateServiceOptions): Service { 100 | return { 101 | onChildDestroy, 102 | onChildLoad, 103 | onDestroy, 104 | priority: priority ?? 0, 105 | children, 106 | onLoad, 107 | $ref: undefined, 108 | name, 109 | type: 'service' 110 | }; 111 | } 112 | 113 | /** 114 | * Assertion function to check if the object represents is a {@link LilithObject} or not. 115 | * @param key The key to check, since all objects have a `'type'` property. 116 | * @param value The object itself. 117 | */ 118 | export function isLilithObject(key: O['type'], value: unknown): value is O { 119 | if (!isObject(value)) return false; 120 | if (!hasOwnProperty(value as LilithObject, 'type')) return false; 121 | 122 | return (value as LilithObject).type === key; 123 | } 124 | 125 | /** 126 | * Assertion function to check if the value represented is a {@link Singleton} object or not. 127 | * @param value The value that is represented as a {@link Singleton} object. 128 | */ 129 | export function isSingleton(value: unknown): value is Singleton { 130 | return isLilithObject>('singleton', value); 131 | } 132 | 133 | /** 134 | * Assertion function to check if the value represented is a {@link Service} object or not. 135 | * @param value The value that is represented as a {@link Service} object. 136 | */ 137 | export function isService(value: unknown): value is Service { 138 | return ( 139 | isLilithObject('service', value) && Array.isArray(value.children) && hasOwnProperty(value, 'priority') 140 | ); 141 | } 142 | -------------------------------------------------------------------------------- /src/lilith/src/types.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import type { CamelCase, Constructor, Replace } from 'type-fest'; 25 | import type { Lazy } from '@noelware/utils'; 26 | 27 | /** Represents a type-alias for all the container event names. */ 28 | export type ContainerEventNames = keyof ContainerEvents; 29 | 30 | /** Uses the [[Replace ]] type for replacing all `:` with `-`. */ 31 | export type ReplaceColonWithDash = S extends string ? Replace : never; 32 | 33 | /** Represents a type-alias for loading Lilith objects. */ 34 | export type Load = any | Constructor | { path: string }; 35 | 36 | /** Represents all the events that a [[Container]] can listen to. */ 37 | export interface ContainerEvents { 38 | /** 39 | * Emitted when a child service is being registered in a parent service. 40 | * 41 | * @event service:child:register 42 | * @param parent The parent service. 43 | * @param child The child service. 44 | */ 45 | 'service:child:register'(parent: any, child: any): void; 46 | 47 | /** 48 | * Emitted when a child service is being destroyed in a parent service. This event 49 | * is useful to check what services have teardown support. 50 | * 51 | * @event service:child:destroy 52 | * @param parent The parent service. 53 | * @param child The child service. 54 | */ 55 | 'service:child:destroy'(parent: any, child: any): void; 56 | 57 | /** 58 | * Emitted when a singleton is being registered and initialized in the [[Container]]. This is useful 59 | * for logging which singletons are available. 60 | * 61 | * @event singleton:register 62 | * @param singleton The singleton object. 63 | */ 64 | 'singleton:register'(singleton: any): void; 65 | 66 | /** 67 | * Emitted when a singleton is being destroyed and deinitialized in the [[Container]]. This is useful 68 | * for logging which singletons have teardown support. 69 | * 70 | * @event singleton:destroy 71 | * @param singleton The singleton object. 72 | */ 73 | 'singleton:destroy'(singleton: any): void; 74 | 75 | /** 76 | * Emitted when a service is being registered and initialized in the [[Container]]. This is useful 77 | * for logging which services are available. 78 | * 79 | * @event service:destroy 80 | * @param service The service object. 81 | */ 82 | 'service:register'(service: any): void; 83 | 84 | /** 85 | * Emitted when a service is being destroyed and deinitialized in the [[Container]]. This is useful 86 | * for logging which singletons have teardown support. 87 | * 88 | * @event service:destroy 89 | * @param service The service object. 90 | */ 91 | 'service:destroy'(service: any): void; 92 | 93 | /** 94 | * Emitted when the container has logs it wants to send to the end user with this event. Useful 95 | * for debugging the container. 96 | * 97 | * @event debug 98 | * @param message The debug message. 99 | */ 100 | debug(message: string): void; 101 | } 102 | 103 | /** Represents a base object that represents any piece (i.e, service, singleton, variable, injectable) */ 104 | export interface LilithObject { 105 | type: string; 106 | } 107 | 108 | /** Represents a interface of the basic lifecycles a {@link LilithObject} has. */ 109 | export interface LifecycleEvents { 110 | onDestroy?(...args: A): void; 111 | onLoad?(...args: A): Promise | void; 112 | } 113 | 114 | /** Represents an extension of {@link LifecycleEvents} to include children lifecycles. */ 115 | export interface ChildLifecycleEvents extends LifecycleEvents { 116 | onChildDestroy?(child: LilithObject): void; 117 | onChildLoad?(child: LilithObject): Promise | void; 118 | } 119 | 120 | /** 121 | * Represents a service, the fundemental piece of **Lilith**. Services are a way to encaspulate 122 | * modules that can be shared with different services. Services can be injectable easily with 123 | * the [[Container#inject]] method, or with the [[inject]] function call. 124 | * 125 | * @example 126 | * ```ts 127 | * import { Service, Inject, createLilith } from '@lilith/core'; 128 | * 129 | * (@)Service('my service name') 130 | * class MyService { 131 | * private readonly someOtherService: SomeService; 132 | * constructor(@Inject someOtherService: SomeService) { 133 | * this.someOtherService = someOtherService; 134 | * } 135 | * 136 | * onLoad() { 137 | * this.someOtherService.doSomething(); 138 | * } 139 | * } 140 | * 141 | * (@)Service('my other service') 142 | * class SomeService { 143 | * doSomething(): void { console.log('i did something!'); } 144 | * } 145 | * 146 | * const container = createLilith({ 147 | * // order matters! 148 | * services: [SomeService, MyService] 149 | * }); 150 | * 151 | * container.start(); 152 | * // you should have "i did something!" printed in the console 153 | * 154 | * container.destroy(); 155 | * ``` 156 | */ 157 | export interface Service extends LilithObject, ChildLifecycleEvents { 158 | priority: number; 159 | children: any[]; 160 | $ref?: any; // this is only here for classes that are services, the `services()` function ignore this. 161 | type: 'service'; 162 | name: string; 163 | } 164 | 165 | /** 166 | * Represents a singleton. A singleton is a way to share objects within Lilith services, and since singletons 167 | * are injectable, you can use the [[Container#inject]], [[inject]], or the [[@Inject]] decorator to inject 168 | * it into services. 169 | * 170 | * Singletons can only be objects, not primitives (i.e, string, number, boolean). Singletons must be constructed 171 | * without any prior knowledge on how to construct itself. Singletons doesn't have lifecycle events also! 172 | * 173 | * Singletons are also **lazily evaluated**, they're only loaded once they are needed. Otherwise, you might 174 | * want to opt in for **variables** that are loaded automatically. 175 | * 176 | * @example 177 | * ```ts 178 | * import { singleton, createLilith } from '@lilith/core'; 179 | * 180 | * class SomeObjectToHold { 181 | * doSomething(): void { console.log('I did something!'); } 182 | * } 183 | * 184 | * const container = createLilith({ 185 | * singletons: [ 186 | * singleton(() => new SomeObjectToHold()) 187 | * ] 188 | * }); 189 | * 190 | * container.start(); 191 | * 192 | * const singleton = container.inject(SomeObjectToHold); 193 | * singleton.doSomething(); 194 | * // console printed "I did something!" 195 | * 196 | * container.destroy(); 197 | * ``` 198 | */ 199 | export interface Singleton extends LilithObject, LifecycleEvents<[T]> { 200 | name: string; 201 | $ref: Lazy; 202 | type: 'singleton'; 203 | } 204 | 205 | /** 206 | * Represents a variable that can be used immediately. This is only useful in the `@lilith/config` library, 207 | * or you can create variables. **Variables** are different from singletons, **Variables** can hold 208 | * any data type (string, boolean, number, etc), but singletons can only hold **objects** that are 209 | * constructed. 210 | * 211 | * You might want to use **variables** over **singletons** for: 212 | * - Configuration injection (i.e, with `@lilith/config`) 213 | * - Easy lookup for values that you might need in services. 214 | * 215 | * You might want to avoid **variables** for: 216 | * - Objects that should be lazily-loaded, while **variables** are loaded and kept automatically 217 | * in the container tree, rather than lazily evaluated when looked up. 218 | * 219 | * @warning Variables are loaded even if **Container#start** was not called. 220 | * @example 221 | * ```ts 222 | * import { createLilith } from '@lilith/core'; 223 | * 224 | * export interface VariableContainer { 225 | * key: 'value'; 226 | * } 227 | * 228 | * const container = createLilith({ 229 | * variables: { 230 | * key: 'value' 231 | * } 232 | * }); 233 | * 234 | * const value = container.variable('key'); 235 | * // => 'value' 236 | * ``` 237 | */ 238 | export interface Variable extends LilithObject { 239 | value: V; 240 | type: 'variable'; 241 | key: K; 242 | } 243 | 244 | /** 245 | * Represents all the metadata keys that Lilith uses. This is meant for internal 246 | * purposes. 247 | * 248 | * @internal 249 | */ 250 | export enum MetadataKeys { 251 | Injections = '$lilith::injections', 252 | Variable = '$lilith::variable', 253 | Service = '$lilith::service' 254 | } 255 | 256 | export const ContainerEvents: Record>, ContainerEventNames> = { 257 | serviceChildRegister: 'service:child:register', 258 | serviceChildDestroy: 'service:child:destroy', 259 | singletonRegister: 'singleton:register', 260 | singletonDestroy: 'singleton:destroy', 261 | serviceRegister: 'service:register', 262 | serviceDestroy: 'service:destroy', 263 | debug: 'debug' 264 | }; 265 | -------------------------------------------------------------------------------- /src/lilith/src/use-container.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import { Container } from './container'; 25 | 26 | /** 27 | * Hook to retrieve the {@link Container} instance, if it was ever constructed. 28 | * @returns A {@link Container} if any, or a `Error` thrown. 29 | */ 30 | // this is only here because the container requires functions from './functions' and it will 31 | // recursively check until it stackoverflows. 32 | export function useContainer(): Container { 33 | if (Container.instance === undefined) 34 | throw new Error('Container has not been initialized, please construct a Container!'); 35 | 36 | return Container.instance as Container; 37 | } 38 | -------------------------------------------------------------------------------- /src/lilith/tests/__fixtures__/dos.service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import type { ChildLifecycleEvents } from '../../src/types'; 25 | import { Inject, Service } from '../../src/decorators'; 26 | import Uno from './uno.service'; 27 | 28 | @Service({ name: 'dos' }) 29 | export default class MyOtherService implements ChildLifecycleEvents { 30 | @Inject 31 | private readonly uno!: Uno; 32 | private readonly i: number = 2; 33 | 34 | get int() { 35 | return this.i; 36 | } 37 | 38 | add() { 39 | return this.uno.int + this.i; 40 | } 41 | 42 | onLoad() { 43 | console.log('Uno!'); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/lilith/tests/__fixtures__/uno.service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import type { ChildLifecycleEvents } from '../../src/types'; 25 | import { Service } from '../../src/decorators'; 26 | 27 | @Service({ name: 'uno', priority: 10 }) 28 | export default class MyService implements ChildLifecycleEvents { 29 | private readonly i: number = 1; 30 | 31 | get int() { 32 | return this.i; 33 | } 34 | 35 | onLoad() { 36 | console.log('Uno!'); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/lilith/tests/container.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import 'reflect-metadata'; 25 | 26 | import { describe, expect, test } from 'vitest'; 27 | import { useContainer } from '../src/use-container'; 28 | import { isSingleton } from '../src/functions'; 29 | import { Container } from '../src/container'; 30 | 31 | describe('@lilith/core - Container', () => { 32 | const container = new Container(); 33 | 34 | test('create new container', () => { 35 | expect(container.inject('blah')).toBeNull(); 36 | expect(Container.instance === container).toBeTruthy(); 37 | }); 38 | 39 | test('singleton - no primitives', () => { 40 | container.addSingleton({ 41 | provide() { 42 | return 'beep'; 43 | } 44 | }); 45 | 46 | expect(() => container['singletons'][0].$ref.get()).toThrowError(); 47 | expect(() => container['singletons'][0].$ref.get()).toThrowErrorMatchingInlineSnapshot( 48 | `"Value beep is not a object, received \`string\`"` 49 | ); 50 | }); 51 | 52 | test('singleton - objects', () => { 53 | class SomeInlineClass { 54 | private readonly value: string = 'get'; 55 | get() { 56 | return this.value; 57 | } 58 | } 59 | 60 | container.addSingleton(() => new SomeInlineClass()); 61 | 62 | expect(container['singletons'].length).toBe(2); 63 | expect(() => container['singletons'][1].$ref.get()).not.toThrow(); 64 | expect(container['singletons'][1].$ref.get()).toBeInstanceOf(SomeInlineClass); 65 | expect((container['singletons'][1].$ref.get() as SomeInlineClass).get()).toBe('get'); 66 | expect(isSingleton(container['singletons'][1])).toBeTruthy(); 67 | }); 68 | 69 | test('useContainer method', () => { 70 | let c!: Container<{}>; 71 | expect(() => (c = useContainer())).not.toThrowError(); 72 | expect(Container.instance === c).toBeTruthy(); 73 | }); 74 | 75 | test('services - not valid objects', async () => { 76 | const c = useContainer(); 77 | c.destroy(); 78 | 79 | expect(() => useContainer()).toThrowError(); 80 | 81 | // Create a new container 82 | let newContainer = new Container({ 83 | services: [Date] 84 | }); 85 | 86 | newContainer.on('debug', console.log); 87 | await expect(() => newContainer.start()).rejects.toThrowErrorMatchingInlineSnapshot( 88 | '"Class instance Function doesn\'t contain a @Service decorator!"' 89 | ); 90 | 91 | newContainer.destroy(); 92 | 93 | // Create a new container that uses plain objects like {} 94 | newContainer = new Container({ services: [{}] }); 95 | newContainer.on('debug', console.log); 96 | 97 | await expect(() => newContainer.start()).rejects.toThrowErrorMatchingInlineSnapshot( 98 | '"Object [object Object] doesn\'t use the @Service decorator."' 99 | ); 100 | 101 | newContainer.destroy(); 102 | 103 | // Create a new container that succeeds 104 | // this fails for some reason! 105 | // 106 | // newContainer = new Container({ services: [{ path: join(process.cwd(), 'tests', '__fixtures__') }] }); 107 | // newContainer.on('debug', console.log); 108 | 109 | // await expect(newContainer.start()).resolves.toBeUndefined(); 110 | }); 111 | }); 112 | -------------------------------------------------------------------------------- /src/lilith/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@augu/tsconfig/dom.json", 3 | "compilerOptions": { 4 | "moduleResolution": "node", 5 | "removeComments": true, 6 | "types": ["node", "reflect-metadata"], 7 | "baseUrl": ".", 8 | "noEmit": true // tsup does this for us 9 | }, 10 | "exclude": ["node_modules"], 11 | "include": ["**/*.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /src/lilith/vitest.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import { defineConfig } from 'vitest/config'; 25 | 26 | export default defineConfig({ 27 | test: { dir: './tests' } 28 | }); 29 | -------------------------------------------------------------------------------- /src/logging/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["prettier", "@augu/eslint-config/ts.js"] 3 | } 4 | -------------------------------------------------------------------------------- /src/logging/README.md: -------------------------------------------------------------------------------- 1 | # 🧵 @noelware/lilith-logging* 2 | 3 | **@noelware/lilith-logging** is a service library that acts like a logging facade that is injectable to any service. Read more on the [root repository](https://github.com/Noelware/Lilith/blob/master/README.md) to know more. 4 | 5 | ## Available Backends 6 | 7 | - [@lilith/logging-winston](../logging-winston) 8 | 10 | 11 | ## Implementing your own backend 12 | 13 | This library exposes a **BaseBackend** abstract class, in which, you can use any logger with it. You will need to add it to the `backendClass` property in **LoggingServiceOptions**: 14 | 15 | ```ts 16 | import { LogRecord, BaseBackend, LogService, Log, Logger, LoggerFactory } from '@lilith/logging'; 17 | import { Service, Inject, createLilith } from '@lilith/core'; 18 | 19 | class MyBackend extends BaseBackend<{}> { 20 | constructor(options: {} = {}) { 21 | // do stuff here 22 | } 23 | 24 | override write({ name, timestamp, message, extra }: LogRecord) { 25 | // write to the logger of your choice 26 | } 27 | } 28 | 29 | class MyService { 30 | // @Log is a custom decorator to create a logger by the logger factory 31 | // to give the name of `::MyService` 32 | @Log private readonly log!: Logger; 33 | 34 | // directly create a logger 35 | constructor() { 36 | this.log = LoggerFactory.get('name of logger'); 37 | } 38 | 39 | onLoad() { 40 | log.info('Hello, world!'); 41 | } 42 | } 43 | 44 | const container = createLilith({ 45 | services: [ 46 | MyService, 47 | new LogService({ 48 | backend: { 49 | cls: MyBackend, 50 | opts: { 51 | /* options that the backend has! */ 52 | } 53 | } 54 | }) 55 | ] 56 | }); 57 | 58 | container.start(); 59 | // You should get no logs if you copied this, but please use a backend! 60 | ``` 61 | -------------------------------------------------------------------------------- /src/logging/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | export * from './src/backends/base.backend'; 25 | export * from './src/service'; 26 | export * from './src/utils'; 27 | export * from './src/types'; 28 | -------------------------------------------------------------------------------- /src/logging/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@noelware/lilith-logging", 3 | "description": "🧵 Service library that acts like a logging facade that is injectable to any service", 4 | "version": "0.0.0-dev.0", 5 | "main": "dist/index.js", 6 | "author": "Noel , Noelware Team ", 7 | "license": "MIT", 8 | "repository": "https://github.com/Noelware/Lilith/tree/master/src/logging", 9 | "bugs": "https://github.com/Noelware/Lilith/issues", 10 | "types": "dist/index.d.ts", 11 | "files": [ 12 | "dist/" 13 | ], 14 | "exports": { 15 | ".": { 16 | "require": "./dist/index.js", 17 | "default": "./dist/index.js", 18 | "import": "./dist/index.mjs" 19 | } 20 | }, 21 | "scripts": { 22 | "test": "vitest --run", 23 | "lint": "eslint src --ext .ts,.tsx --fix" 24 | }, 25 | "dependencies": { 26 | "leeks.js": "0.2.4" 27 | }, 28 | "devDependencies": { 29 | "@augu/eslint-config": "4.0.1", 30 | "@augu/tsconfig": "1.1.1", 31 | "@types/node": "18.13.0", 32 | "@typescript-eslint/eslint-plugin": "5.52.0", 33 | "@typescript-eslint/parser": "5.52.0", 34 | "eslint": "8.24.0", 35 | "eslint-config-prettier": "8.6.0", 36 | "type-fest": "3.6.0", 37 | "typescript": "4.9.5", 38 | "vitest": "0.23.4" 39 | }, 40 | "optionalDependencies": { 41 | "@noelware/lilith": "workspace:src/lilith" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/logging/src/backends/base.backend.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import { LogLevel, LogLevelInt } from '../types'; 25 | import type { LoggerFactory } from '../factory'; 26 | 27 | export type BaseBackendOptions = _BaseBackendOptions & O; 28 | 29 | export interface _BaseBackendOptions { 30 | defaultLevel?: LogLevel | LogLevelInt; 31 | } 32 | 33 | /** 34 | * Represents a base implementation of creating your own log backend. This is responsible for: 35 | * - writing data to a logger, 36 | * - creating the logger factory, 37 | * - creating loggers internally. 38 | */ 39 | export abstract class BaseBackend { 40 | constructor(public options: BaseBackendOptions) {} 41 | 42 | /** 43 | * Returns the default level for this {@link BaseBackend backend} to use when 44 | * writing to loggers. 45 | */ 46 | get defaultLevel() { 47 | if (!this.options.defaultLevel) return 40; 48 | if (typeof this.options.defaultLevel === 'string') return LogLevel[this.options.defaultLevel]; 49 | 50 | return this.options.defaultLevel; 51 | } 52 | 53 | /** 54 | * Returns the logger factory that this backend is responsible for. 55 | */ 56 | abstract getLoggerFactory(): LF; 57 | 58 | /** 59 | * Closes and flushes any previous cached log records to the appenders it was configured 60 | * to use. This should be an idempotent call, so subsequent calls can't prevent this being 61 | * closed already. 62 | */ 63 | close() { 64 | /* do nothing */ 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/logging/src/decorators/Log.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import { InjectionMeta } from '@lilith/core'; 25 | 26 | /** @internal */ 27 | export const LogMetaKey = '$lilith::logging::log'; 28 | 29 | interface _LogInjectionMeta { 30 | name: string[]; 31 | } 32 | 33 | /** @internal */ 34 | export type LogInjectionMeta = InjectionMeta<_LogInjectionMeta>; 35 | 36 | /** 37 | * Constructs a logger in this property. This is useful if you want to construct it 38 | * on the fly. 39 | * 40 | * @param names The names for the logger that are created on the fly 41 | */ 42 | export const Log = (...names: string[]): PropertyDecorator => { 43 | return (target, property) => { 44 | const annotations: LogInjectionMeta[] = Reflect.getMetadata(LogMetaKey, target) ?? []; 45 | annotations.push({ 46 | property, 47 | name: names !== undefined ? names : [typeof target === 'function' ? target.name : '(unknown)'] 48 | }); 49 | 50 | Reflect.defineMetadata(LogMetaKey, annotations, target); 51 | }; 52 | }; 53 | -------------------------------------------------------------------------------- /src/logging/src/factory.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import { Logger } from './logger'; 25 | 26 | /** 27 | * Represents a factory for constructing loggers! You should probably use the `LogFactory` 28 | * class to use this interface. 29 | */ 30 | export interface LoggerFactory { 31 | /** 32 | * Returns this factory's log seperator, which can be changed at runtime. 33 | */ 34 | get seperator(): string; 35 | 36 | /** 37 | * Sets the factory's log seperator, and update every logger's name to use that 38 | * seperator. If this was changed while logs were committed, only new flushed logs 39 | * will use the seperator. 40 | * 41 | * @param value The seperator to use. 42 | */ 43 | set seperator(value: string); 44 | 45 | /** 46 | * Returns a logger by the given paths. All paths that are given use `.` as the seperator! You can 47 | * configure the seperator using `LogFactory.seperator = ';'` at runtime if you wish. 48 | * 49 | * @param paths The paths that are conjuctioned with the factory's seperator. 50 | * @example 51 | * ```ts 52 | * import { LogFactory } from '@lilith/logging'; 53 | * 54 | * LogFactory.get('a', 'log', 'name'); 55 | * // => Logger { name: 'a.log.name' } 56 | * ``` 57 | */ 58 | get(...paths: string[]): Logger; 59 | } 60 | -------------------------------------------------------------------------------- /src/logging/src/logger.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | /** 25 | * Represents a logger that can be used to log anything. You can create a logger in the following ways: 26 | * 27 | * - (1) By using the `@Log` annotation, 28 | * - (2) By using the **LoggerFactory#get** method. 29 | * 30 | * Using this interface is not recommended, but it's public API! 31 | */ 32 | export interface Logger 33 | extends Record<'info' | 'debug' | 'error' | 'fatal' | 'trace' | 'warn', (...messages: unknown[]) => void> { 34 | /** Returns the name of the logger */ 35 | name: string; 36 | } 37 | -------------------------------------------------------------------------------- /src/logging/src/service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import { Service, useContainer, type ChildLifecycleEvents } from '@lilith/core'; 25 | import { LogLevel, LogLevelInt } from './types'; 26 | import type { LoggerFactory } from './factory'; 27 | import { BaseBackend } from './backends/base.backend'; 28 | import { Logger } from './logger'; 29 | 30 | export interface LogServiceOptions { 31 | attachDebugToLogger?: boolean; 32 | backend: B; 33 | level?: LogLevel | LogLevelInt; 34 | } 35 | 36 | // the log service should have a higher priority since the configuration service 37 | // might use it. 38 | @Service({ name: 'lilith:logging', priority: 1000 }) 39 | export class LogService implements ChildLifecycleEvents { 40 | private _defaultLevel: number; 41 | private _factory: LoggerFactory; 42 | private _backend: B; 43 | private _options: LogServiceOptions; 44 | private _logger: Logger; 45 | 46 | constructor(options: LogServiceOptions) { 47 | this._defaultLevel = (typeof options.level === 'string' ? LogLevel[options.level] : options.level) ?? 40; 48 | this._factory = options.backend.getLoggerFactory(); 49 | this._backend = options.backend; 50 | this._options = options; 51 | this._logger = this._factory.get('root'); 52 | } 53 | 54 | get loggerFactory(): B extends BaseBackend ? LF : never { 55 | return this._factory as unknown as any; 56 | } 57 | 58 | onLoad() { 59 | const container = useContainer(); 60 | 61 | // It will attach to the root logger! 62 | if (this._options.attachDebugToLogger !== undefined && this._options.attachDebugToLogger) { 63 | this._logger.debug('Attaching container event [debug] to root logger!'); 64 | container.on('debug', (message) => this._logger.debug(message)); 65 | } 66 | 67 | // container.on('service:register', (service) => { 68 | // container.emit('debug', `Registering @Log decorators for service ${service.$ref.constructor.name}!`); 69 | 70 | // const injections: LogInjectionMeta[] = Reflect.getMetadata(LogMetaKey, service.$ref.constructor) ?? []; 71 | // for (const inject of injections) { 72 | // const self = this; // eslint-disable-line 73 | // console.log(service.$ref, inject); 74 | // Object.defineProperty(service.$ref, inject.property, { 75 | // get() { 76 | // return self._factory.get(...inject.name); 77 | // }, 78 | 79 | // set() { 80 | // throw new Error('@Log decorators are not mutable.'); 81 | // }, 82 | 83 | // configurable: false, 84 | // enumerable: true 85 | // }); 86 | // } 87 | // }); 88 | } 89 | 90 | onDestroy() { 91 | this._backend.close(); 92 | } 93 | 94 | enabled(level: number) { 95 | return this._defaultLevel <= level; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/logging/src/types.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | /** Represents the log levels as strings. Use {@link LogLevelInt} to get the number representing this level. */ 25 | export type LogLevel = 'info' | 'error' | 'fatal' | 'warn' | 'debug' | 'trace'; 26 | 27 | /** 28 | * Represents the log level as integers: 29 | * 30 | * - 10: fatal 31 | * - 20: error 32 | * - 30: warning 33 | * - 40: info 34 | * - 50: debug 35 | * - 60: trace 36 | */ 37 | export type LogLevelInt = 10 | 20 | 30 | 40 | 50 | 60; 38 | export const LogLevel: { [x in LogLevel]: LogLevelInt } = { 39 | info: 40, 40 | error: 20, 41 | fatal: 10, 42 | warn: 30, 43 | debug: 40, 44 | trace: 50 45 | }; 46 | 47 | export const LogLevelInt: { [x in LogLevelInt]: LogLevel } = { 48 | 10: 'fatal', 49 | 20: 'error', 50 | 30: 'warn', 51 | 40: 'info', 52 | 50: 'debug', 53 | 60: 'trace' 54 | }; 55 | -------------------------------------------------------------------------------- /src/logging/src/utils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 🧵 Lilith: Application framework for TypeScript to build robust, and simple services 3 | * Copyright (c) 2021-2022 Noelware 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import { colors, styles, supportsColour } from 'leeks.js'; 25 | import { hasOwnProperty } from '@noelware/utils'; 26 | import { basename } from 'path'; 27 | import { inspect } from 'util'; 28 | 29 | // I/O can be consuming a lot of cpu/memory, so this is caching the results. 30 | // 31 | // For now, it'll be a regular object but this might be changed to a LRU cache. 32 | const cachedFileContents: Record = {}; 33 | 34 | export const formatMessages = (showColors = supportsColour, ...messages: unknown[]) => 35 | messages 36 | .map((message) => { 37 | if (message instanceof Date) return showColors ? styles.dim(message.toISOString()) : message.toISOString(); 38 | if (message instanceof Error) return prettifyError(message, showColors, 3); 39 | if (!['function', 'object'].includes(typeof message)) return (message as any).toString(); 40 | 41 | return inspect(message, { depth: 3, colors: showColors, showProxy: true }); 42 | }) 43 | .join('\n'); 44 | 45 | export const getCallSites = (error?: Error) => { 46 | const err = error ?? new Error(); 47 | const _capture = Error.prepareStackTrace; 48 | 49 | Error.prepareStackTrace = (_, stack) => stack; 50 | const stack = (err.stack as any)?.slice(1) as unknown as NodeJS.CallSite[]; 51 | 52 | Error.prepareStackTrace = _capture; 53 | return stack; 54 | }; 55 | 56 | export const prettifyError = ( 57 | error: Error, 58 | showColors = supportsColour, 59 | causeDepth = 3, 60 | showCode = !!process.env.NODE_ENV && process.env.NODE_ENV === 'development' 61 | ) => { 62 | const content = [`${error.name} :: ${error.message}`]; 63 | const stack = getCallSites(error); 64 | const stackToFile: Record = {}; 65 | 66 | // Ignore node internals, might make them gray coloured. 67 | for (const s of stack.filter((s) => !s.getFileName()?.startsWith('node:'))) { 68 | const fileName = s.getFileName(); 69 | if (hasOwnProperty(stackToFile, fileName ?? '