├── .editorconfig ├── .eslintignore ├── .eslintrc.yaml ├── .gitattributes ├── .github ├── CODEOWNERS ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug.md │ ├── documentation.md │ ├── feature-request.md │ └── question.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── artifactory-check.yml │ ├── codeql-analysis.yml │ ├── deploy.yml │ └── pr-build.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .nvmrc ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── .yarn ├── plugins │ └── @yarnpkg │ │ ├── plugin-constraints.cjs │ │ ├── plugin-interactive-tools.cjs │ │ ├── plugin-typescript.cjs │ │ ├── plugin-version.cjs │ │ └── plugin-workspace-tools.cjs └── releases │ └── yarn-3.2.0.cjs ├── .yarnrc.yml ├── CHANGELOG.md ├── CHANGELOG_2019.md ├── LICENSE ├── README.md ├── constraints.pro ├── docusaurus ├── CHANGELOG.md ├── README.md ├── babel.config.js ├── docs │ ├── index.md │ ├── quick-start.md │ ├── recipes │ │ ├── logging.md │ │ ├── root-imports.md │ │ ├── testing-libraries.md │ │ └── typescript.md │ ├── reference │ │ ├── commands.mdx │ │ ├── mock-server.md │ │ └── workflow-config.md │ ├── tutorial │ │ ├── dependency-management.md │ │ ├── deploy.md │ │ ├── index.md │ │ └── mocks.md │ └── upgrading-workflow.md ├── docusaurus.config.js ├── package.json ├── project.json ├── sidebars.js ├── src │ └── css │ │ └── custom.css └── static │ ├── .nojekyll │ └── img │ ├── favicon.ico │ ├── icon.png │ ├── jest-debug.gif │ ├── mock-data.png │ ├── notification.png │ ├── profile.png │ ├── project-change.gif │ ├── project-structure.png │ └── workflow.png ├── example ├── .eslintrc.yaml ├── CHANGELOG.md ├── package.json ├── project.json ├── project │ ├── app │ │ ├── App.test.tsx │ │ ├── App.tsx │ │ ├── chain.js │ │ ├── components │ │ │ ├── Form.tsx │ │ │ └── form-config.json │ │ ├── form.d.ts │ │ └── index.tsx │ └── config │ │ └── workflow.js └── tsconfig.json ├── nx.json ├── package.json ├── packages ├── mock-data │ ├── CHANGELOG.md │ ├── README.md │ ├── data │ │ ├── admin.json │ │ ├── current-region.json │ │ ├── empty.json │ │ ├── legacy-permissions.json │ │ ├── me.json │ │ ├── organization.json │ │ ├── organizations.json │ │ ├── permissions.json │ │ ├── providers.json │ │ ├── regions.json │ │ ├── slotmachine.json │ │ ├── space.json │ │ └── spaces.json │ ├── index.js │ ├── mock-data.spec.js │ ├── package.json │ ├── project.json │ └── routes.json ├── mock-server │ ├── .eslintrc.yaml │ ├── CHANGELOG.md │ ├── README.md │ ├── config │ │ └── index.js │ ├── index.js │ ├── logger │ │ └── index.js │ ├── middleware │ │ ├── config.js │ │ ├── headers.js │ │ ├── index.js │ │ ├── not.found.js │ │ └── tests │ │ │ ├── headers.spec.js │ │ │ └── mulit-part.spec.js │ ├── models │ │ ├── index.js │ │ ├── request.js │ │ ├── response.js │ │ └── route.js │ ├── package.json │ ├── project.json │ ├── response │ │ ├── get.js │ │ ├── index.js │ │ ├── match.js │ │ ├── patch.js │ │ ├── post.js │ │ ├── result.js │ │ └── tests │ │ │ ├── asynchronous.spec.js │ │ │ ├── behavior.spec.js │ │ │ └── scoring.spec.js │ ├── routes │ │ ├── index.js │ │ └── tests │ │ │ └── routes.spec.js │ └── tests │ │ ├── data │ │ ├── dummy-response-1.json │ │ ├── dummy-response-2.json │ │ ├── dummy-response-3.json │ │ └── dummy-response-4.json │ │ ├── dummy.routes.config.json │ │ ├── helpers.js │ │ ├── server.spec.js │ │ └── test-config.js ├── workflow-logger │ ├── CHANGELOG.md │ ├── README.md │ ├── index.js │ ├── package.json │ └── project.json ├── workflow-upgrade │ ├── CHANGELOG.md │ ├── README.md │ ├── bin.js │ ├── index.js │ ├── package.json │ └── project.json └── workflow │ ├── CHANGELOG.md │ ├── README.md │ ├── helpers │ ├── paths.js │ └── resolve-module.js │ ├── html.js │ ├── index.js │ ├── jest.config.js │ ├── jest │ ├── babel.js │ ├── css.js │ └── file.js │ ├── loaders │ ├── index.js │ ├── loader-css.js │ ├── loader-less.js │ ├── loader-postcss.js │ ├── loader-scss.js │ └── rule-images.js │ ├── package.json │ ├── project.json │ ├── public │ ├── availity.png │ ├── favicon.ico │ └── index.html │ ├── scripts │ ├── about.js │ ├── build.js │ ├── clone-starter.js │ ├── format.js │ ├── init.js │ ├── lint.js │ ├── open.js │ ├── profile.js │ ├── proxy.js │ ├── release.js │ ├── start.js │ ├── stats.js │ ├── test.js │ └── version.js │ ├── settings │ ├── index.js │ └── schema.js │ ├── tests │ ├── __snapshots__ │ │ └── workflowConfigs.spec.js.snap │ └── workflowConfigs.spec.js │ ├── webpack.config.js │ ├── webpack.config.production.js │ └── webpack.config.profile.js ├── scripts ├── artifactory-check.sh ├── check-missing-deps.js └── check-version-strategy.js ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | indent_size = 4 15 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | ./yarn 2 | 3 | coverage/ 4 | 5 | /**/build 6 | /**/dist 7 | /**/node_modules 8 | -------------------------------------------------------------------------------- /.eslintrc.yaml: -------------------------------------------------------------------------------- 1 | extends: availity/browser 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Enforce Unix newlines 2 | .* text eol=lf 3 | 4 | # Mark binary files to avoid Git showing huge diffs 5 | /.yarn/releases/** binary 6 | /.yarn/plugins/** binary 7 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-code-owners#example-of-a-codeowners-file 2 | # These owners will be the default owners for everything in the repo. 3 | # Unless a later match takes precedence, 4 | # these users will be requested for review when someone opens a pull request. 5 | # Order is important; the last matching pattern takes the most precedence. 6 | 7 | * @jordan-a-young @LauRoxx @gregmartDOTin 8 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This is a monorepo managed using [`yarn berry` workspaces](https://yarnpkg.com/features/workspaces). Each package is versioned and published individually. 4 | 5 | ## Adding a New Package 6 | 7 | - ```bash 8 | yarn run new 9 | ``` 10 | 11 | - Add link to new package in README 12 | 13 | ## Installing 14 | 15 | We use [yarn](https://yarnpkg.com/lang/en/) workspaces for developing. If you don't have [yarn](https://yarnpkg.com/lang/en/) you can install it by running 16 | `npm install -g yarn`. Otherwise you can run the below to install all the dependencies. 17 | 18 | ```bash 19 | yarn install 20 | ``` 21 | 22 | Although we are not yet using [Plug'n'Play](https://yarnpkg.com/features/pnp) all subsequent installs should be quick after the first initial one. 23 | 24 | ## Commits 25 | 26 | Once satisfied with your changes, you will need to commit them. 27 | 28 | - Commits should use the [Angular Commit Format](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#type). 29 | - Scope should be one of the un-prefixed name of the packages under `./packages/`, for example, `feat(workflow): msg` would apply to the `@availity/workflow` package. 30 | - If a commit applies to multiple packages, leave out the scope. 31 | - Using conventional commits will help you to determine the appropriate version bump during the versioning process, so following the [Angular Commit Format](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#type) is important! When in doubt, don't hesitate to reach out to the reviewers of your PR for help with commit messages and versioning. 32 | 33 | ## Testing Changes 34 | 35 | There are a few scripts you can use for testing changes. If you are using `vscode` you will be able to run them from the debugger, otherwise they can be run from your CLI. 36 | 37 | ### `yarn start:app` 38 | 39 | Runs the example application 40 | 41 | ### `yarn test:app` 42 | 43 | Tests the example application 44 | 45 | ### `yarn test:integration` 46 | 47 | Runs the integration command on each workspace in this repo. Generally, it will build and test each workspace. 48 | 49 | ### `yarn build:app` 50 | 51 | Builds the example application 52 | 53 | ### Testing Template Changes 54 | 55 | If you need to test changes to the template, you can use the `--branchOverride` command when running `npx @availity/workflow init`. 56 | 57 | ## Versioning 58 | 59 | This repo uses the [yarn release workflow](https://yarnpkg.com/features/release-workflow) for managing versions and releases. We expect you to follow the [Angular Commit Format](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#type) since it will help when determining an appropriate version bump for your PR. 60 | 61 | - Once your changes have been committed and tested, it's time to create a release definition file 62 | - Run 63 | 64 | ```bash 65 | yarn version check --interactive 66 | ``` 67 | 68 | to see a summary of all your changed files, changed workspaces, and dependent workspaces. You will also see checkboxes for each entry, allowing you to pick the release strategy that's appropriate for each workspace. 69 | 70 | This can be tricky sometimes, but your commit messages will help out here. In general, the following is a guide for selecting version bump levels: 71 | 72 | - Major: Any workspace with a commit containing `BREAKING CHANGES:` should receive a major version bump, regardless of commit type. Any dependent workspaces should also receive a major bump. 73 | 74 | - Minor: Any workspace with a commit type of `feat`. Any dependent workspaces can receive a patch version bump. 75 | 76 | - Patch: Any workspace with a commit type of `fix, refactor, perf`. Any dependent workspaces can receive a patch version bump. 77 | 78 | - When in doubt, don't hesitate to reach out to the reviewers of your PR for help determining the right version strategy. 79 | 80 | ## Contributor Workflow 81 | 82 | - `git clone` this repo if you are a member of the Availity organization, otherwise `git fork` it 83 | 84 | - Make and commit any changes, being sure to follow the [Angular Commit Format](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#type) 85 | 86 | - Create or update any necessary tests and run them 87 | 88 | - Version your changes and commit them 89 | 90 | - `git push` those changes to your PR 91 | 92 | - Upon merge to `master`, changelogs will be automatically generated, and your versions will be tagged and published without requiring any further action 93 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug report" 3 | about: Open a new issue here if something isn't working as expected. 4 | --- 5 | 6 | ## 🐛 Bug report 7 | 8 | ### Current Behavior 9 | 10 | 11 | 12 | ### Expected behavior 13 | 14 | 15 | 16 | ### Reproducible example 17 | 18 | 19 | 20 | ### Suggested solution(s) 21 | 22 | 23 | 24 | ### Additional context 25 | 26 | 27 | 28 | ### Your environment 29 | 30 | 31 | ```bash 32 | npx envinfo --npmPackages '@availity/*' --binaries 33 | ``` 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001f4da Documentation" 3 | about: Improvements or suggestions for the gatsby docs. 4 | --- 5 | 6 | ## 📖 Documentation 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680Feature request" 3 | about: Suggest an idea or enhancement to our sdk. 4 | --- 5 | 6 | ## 🚀 Feature request 7 | 8 | ### Current Behavior 9 | 10 | 11 | 12 | ### Desired Behavior 13 | 14 | 15 | 16 | ### Suggested Solution 17 | 18 | 19 | 20 | 21 | 22 | ### Who does this impact? Who is this for? 23 | 24 | 25 | 26 | ### Describe alternatives you've considered 27 | 28 | 29 | 30 | ### Additional context 31 | 32 | 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "❓Question" 3 | about: 'Any other issue that is not bug, feature, or docs related.' 4 | --- 5 | 6 | ## ❓Question 7 | 8 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Before submitting a pull request,** please make sure the following is done: 2 | 3 | 1. If you are part of the Availity organization, clone this repository and create your branch off of `master`. If you are not part of the organization, you will need to fork this repository and the create your branch off of `master`. 4 | 2. Run `yarn` in the repository root. 5 | 3. If you've fixed a bug or added code that should be tested, add tests! 6 | 4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch TestName` is helpful in development. 7 | 5. Make sure your code passed the conventional commits check. Read more about [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: '/' 5 | schedule: 6 | interval: monthly 7 | time: '10:00' 8 | open-pull-requests-limit: 10 9 | groups: 10 | dependencies: 11 | dependency-type: "production" 12 | update-types: 13 | - "minor" 14 | - "patch" 15 | dev-dependencies: 16 | dependency-type: "development" 17 | update-types: 18 | - "minor" 19 | - "patch" 20 | commitlint: 21 | patterns: 22 | - "@commitlint/*" 23 | update-types: 24 | - "major" 25 | conventional-changelog: 26 | patterns: 27 | - "conventional-changelog-*" 28 | update-types: 29 | - "major" 30 | jest: 31 | patterns: 32 | - "jest" 33 | - "*jest*" 34 | update-types: 35 | - "major" 36 | nx: 37 | patterns: 38 | - "nx" 39 | - "@nx/*" 40 | update-types: 41 | - "major" 42 | ignore: 43 | - dependency-name: '@testing-library/react' 44 | versions: 45 | - 11.2.3 46 | - 11.2.5 47 | - dependency-name: elliptic 48 | versions: 49 | - 6.5.4 50 | - dependency-name: webpack 51 | versions: 52 | - 5.11.1 53 | - 5.19.0 54 | - dependency-name: copy-webpack-plugin 55 | versions: 56 | - 7.0.0 57 | - dependency-name: boxen 58 | - dependency-name: chalk 59 | - dependency-name: figures 60 | -------------------------------------------------------------------------------- /.github/workflows/artifactory-check.yml: -------------------------------------------------------------------------------- 1 | name: Artifactory Check CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | types: 8 | - opened 9 | - synchronize 10 | branches: [master] 11 | 12 | jobs: 13 | check: 14 | runs-on: ubuntu-latest 15 | if: ${{ github.actor != 'dependabot[bot]' }} 16 | steps: 17 | - name: Checkout current repo 18 | uses: actions/checkout@v3 19 | 20 | - name: Checkout shared Availity tools 21 | uses: actions/checkout@v3 22 | with: 23 | repository: Availity/.github 24 | token: ${{ secrets.BOT_TOKEN }} 25 | # Relative path under $GITHUB_WORKSPACE to place the repository 26 | path: tools 27 | 28 | - name: Run registry checker 29 | working-directory: tools 30 | run: ./scripts/artifactory-check.sh 31 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '43 9 * * 3' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v3 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v2 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v2 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v2 72 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Publish Release 5 | 6 | on: 7 | push: 8 | branches: [master] 9 | 10 | jobs: 11 | setup: 12 | if: "!contains(github.event.head_commit.message, 'skip ci')" 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout Code 16 | uses: actions/checkout@v3 17 | with: 18 | token: ${{ secrets.BOT_TOKEN }} 19 | fetch-depth: 0 20 | ref: 'master' 21 | 22 | - name: Derive appropriate SHAs for base and head for `nx affected` commands 23 | uses: nrwl/nx-set-shas@v3 24 | with: 25 | main-branch-name: 'master' 26 | 27 | - name: Set Node Version 28 | uses: actions/setup-node@v3 29 | with: 30 | node-version: 22 31 | 32 | - name: Get yarn cache directory path 33 | id: yarn-cache-dir-path 34 | run: | 35 | echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT 36 | 37 | - name: Restore yarn cache 38 | uses: actions/cache@v3 39 | id: yarn-cache 40 | with: 41 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 42 | key: ${{ runner.os }}-22-yarn-${{ hashFiles('**/yarn.lock') }} 43 | restore-keys: | 44 | ${{ runner.os }}-22-yarn- 45 | 46 | - name: Install Dependencies 47 | run: yarn install --immutable 48 | 49 | - name: Check Constraints 50 | run: yarn constraints 51 | 52 | - name: Lint Affected Code 53 | run: yarn nx affected --target=lint 54 | 55 | - name: Unit Test Affected Code 56 | run: yarn nx affected --target=test 57 | 58 | - name: Integration Test Affected Code 59 | run: yarn nx affected --target=integration 60 | 61 | # version-check: 62 | # needs: setup 63 | # runs-on: ubuntu-latest 64 | # steps: 65 | # - uses: actions/checkout@v3 66 | # with: 67 | # token: ${{ secrets.BOT_TOKEN }} 68 | # fetch-depth: 0 69 | # ref: 'master' 70 | # - uses: actions/setup-node@v3 71 | # with: 72 | # node-version: 14 73 | # - name: Get yarn cache directory path 74 | # id: yarn-cache-dir-path 75 | # run: | 76 | # echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT 77 | # - uses: actions/cache@v3 78 | # id: yarn-cache 79 | # with: 80 | # path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 81 | # key: ${{ runner.os }}-14-yarn-${{ hashFiles('**/yarn.lock') }} 82 | # restore-keys: | 83 | # ${{ runner.os }}-14-yarn- 84 | # - run: yarn install --immutable 85 | # - run: yarn version check 86 | # - run: yarn check:versions 87 | 88 | release: 89 | needs: [setup] 90 | runs-on: ubuntu-latest 91 | steps: 92 | - name: Checkout Code 93 | uses: actions/checkout@v3 94 | with: 95 | token: ${{ secrets.BOT_TOKEN }} 96 | fetch-depth: 0 97 | ref: 'master' 98 | 99 | - name: Derive appropriate SHAs for base and head for `nx affected` commands 100 | uses: nrwl/nx-set-shas@v3 101 | with: 102 | main-branch-name: 'master' 103 | 104 | - name: Set Node Version 105 | uses: actions/setup-node@v3 106 | with: 107 | node-version: 22 108 | 109 | - name: Get yarn cache directory path 110 | id: yarn-cache-dir-path 111 | run: | 112 | echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT 113 | 114 | - name: Restore yarn cache 115 | uses: actions/cache@v3 116 | id: yarn-cache 117 | with: 118 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 119 | key: ${{ runner.os }}-22-yarn-${{ hashFiles('**/yarn.lock') }} 120 | restore-keys: | 121 | ${{ runner.os }}-22-yarn- 122 | 123 | - name: Install Dependencies 124 | run: yarn install --immutable 125 | 126 | # TODO: ngx-deploy-npm? Or continue to leverage nx + yarn npm publish command? 127 | - name: Release 128 | run: | 129 | yarn config set npmAuthToken "${{ secrets.NPM_TOKEN }}" 130 | git config --global user.email ${{ secrets.GH_EMAIL }} 131 | git config --global user.name ${{ secrets.GH_USER }} 132 | yarn nx affected --target version --parallel=1 133 | yarn nx affected --target publish --parallel=1 134 | git push && git push --tags 135 | 136 | # TODO: nx caching 137 | - name: Build Docs 138 | run: yarn nx build docusaurus 139 | 140 | - name: Deploy Docs 141 | uses: crazy-max/ghaction-github-pages@v3 142 | with: 143 | target_branch: gh-pages 144 | build_dir: docusaurus/build 145 | commit_message: deployed docs [skip ci] 146 | committer: ${{ secrets.GH_USER }} ${{ secrets.GH_EMAIL }} 147 | env: 148 | GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }} 149 | -------------------------------------------------------------------------------- /.github/workflows/pr-build.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | # If adding a step that requires SECRETS or WRITE ACCESS be sure to have dependabot skip it. 5 | # > if: ${{ github.actor != 'dependabot[bot]' }} 6 | 7 | name: Node.js CI 8 | 9 | on: 10 | pull_request: 11 | branches: [master] 12 | 13 | jobs: 14 | build: 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | matrix: 18 | os: [ubuntu-latest, macos-latest] # TODO: re-enable windows-latest after yarn lint fixed 19 | node: [18, 20, 22] 20 | 21 | steps: 22 | - name: Checkout Code 23 | uses: actions/checkout@v3 24 | with: 25 | fetch-depth: 0 26 | 27 | - name: Derive appropriate SHAs for base and head for `nx affected` commands 28 | uses: nrwl/nx-set-shas@v3 29 | with: 30 | main-branch-name: 'master' 31 | 32 | - name: Set Node Version 33 | uses: actions/setup-node@v3 34 | with: 35 | node-version: ${{ matrix.node }} 36 | 37 | - name: Get yarn cache directory path 38 | id: yarn-cache-dir-path 39 | run: | 40 | echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT 41 | 42 | - name: Restore yarn cache 43 | uses: actions/cache@v3 44 | id: yarn-cache 45 | with: 46 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 47 | key: ${{ runner.os }}-${{ matrix.node }}-yarn-${{ hashFiles('yarn.lock') }} 48 | restore-keys: | 49 | ${{ runner.os }}-${{ matrix.node }}-yarn- 50 | 51 | - name: Install Dependencies 52 | run: yarn install --immutable 53 | 54 | - name: Check Constraints 55 | run: yarn constraints 56 | 57 | - name: Lint Affected Code 58 | run: yarn nx affected --target=lint # FIXME: 'node ./node_modules/.bin/eslint .' is mac/linux specific, but 'yarn eslint .' currently broken 59 | 60 | - name: Unit Test Affected Code 61 | run: yarn nx affected --target=test 62 | 63 | - name: Integration Test Affected Code 64 | run: yarn nx affected --target=integration 65 | 66 | - name: Version Bump Dry Run 67 | run: yarn version:dry-run 68 | 69 | - name: Build Docs 70 | run: yarn build:docs 71 | 72 | - name: Build App 73 | run: yarn build:app 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cache/ 2 | .DS_Store 3 | .idea 4 | .history 5 | 6 | lerna-debug.log 7 | npm-debug.log 8 | yarn-error.log 9 | 10 | coverage/ 11 | reports/ 12 | 13 | /**/node_modules 14 | /**/build 15 | /**/dist 16 | /**/.docusaurus 17 | 18 | .yarn/* 19 | !.yarn/patches 20 | !.yarn/releases 21 | !.yarn/plugins 22 | !.yarn/sdks 23 | !.yarn/versions 24 | .pnp.* 25 | 26 | .nx/cache 27 | .nx/workspace-data 28 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | yarn constraints 2 | yarn commitlint --edit "$1" 3 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | yarn constraints 2 | yarn lint-staged 3 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "orta.vscode-jest", 5 | "gruntfuggly.todo-tree", 6 | "esbenp.prettier-vscode", 7 | "wix.vscode-import-cost", 8 | "nwallace.peep" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "react-app:create", 11 | "program": "${workspaceFolder}/packages/workflow/index.js", 12 | "args": [ 13 | "init", 14 | "my-proj", 15 | "--template", 16 | "https://github.com/Availity/availity-starter-react.git", 17 | "-y" 18 | ] 19 | }, 20 | { 21 | "type": "node", 22 | "name": "react-app:start", 23 | "request": "launch", 24 | "cwd": "${workspaceFolder}/example", 25 | "console": "integratedTerminal", 26 | "internalConsoleOptions": "neverOpen", 27 | "program": "${workspaceFolder}/node_modules/.bin/av", 28 | "args": [ 29 | "start" 30 | ] 31 | }, 32 | { 33 | "type": "node", 34 | "name": "react-app:build", 35 | "request": "launch", 36 | "cwd": "${workspaceFolder}/example", 37 | "console": "integratedTerminal", 38 | "internalConsoleOptions": "neverOpen", 39 | "program": "${workspaceFolder}/node_modules/.bin/av", 40 | "args": [ 41 | "build" 42 | ] 43 | }, 44 | { 45 | "type": "node", 46 | "name": "react-app:release:staging", 47 | "request": "launch", 48 | "cwd": "${workspaceFolder}/example", 49 | "console": "integratedTerminal", 50 | "internalConsoleOptions": "neverOpen", 51 | "program": "${workspaceFolder}/node_modules/.bin/av", 52 | "args": [ 53 | "release" 54 | ], 55 | "env": { 56 | "NODE_ENV": "staging" 57 | } 58 | }, 59 | { 60 | "type": "node", 61 | "name": "docs:start", 62 | "request": "launch", 63 | "cwd": "${workspaceFolder}/docusaurus", 64 | "console": "integratedTerminal", 65 | "internalConsoleOptions": "neverOpen", 66 | "program": "${workspaceFolder}/docusaurus/node_modules/.bin/docusaurus", 67 | "args": [ 68 | "start --no-open" 69 | ] 70 | } 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "**/.git": true, 4 | "**/.svn": true, 5 | "**/.hg": true, 6 | "**/CVS": true, 7 | "**/.DS_Store": true, 8 | }, 9 | "yaml.schemas": { 10 | "https://json.schemastore.org/github-workflow.json": [".github/workflows/*.{yml,yaml}"] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | changesetIgnorePatterns: 2 | - "**/*.test.{js,ts}" 3 | - "**/*.spec.{js,ts}" 4 | - "**/*.{md,mdx}" 5 | - "**/yarn.lock" 6 | - .yarnrc.yml 7 | - .yarn/**/* 8 | 9 | nodeLinker: node-modules 10 | 11 | npmRegistryServer: "https://registry.npmjs.org/" 12 | 13 | plugins: 14 | - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs 15 | spec: "@yarnpkg/plugin-interactive-tools" 16 | - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs 17 | spec: "@yarnpkg/plugin-workspace-tools" 18 | - path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs 19 | spec: "@yarnpkg/plugin-typescript" 20 | - path: .yarn/plugins/@yarnpkg/plugin-constraints.cjs 21 | spec: "@yarnpkg/plugin-constraints" 22 | - path: .yarn/plugins/@yarnpkg/plugin-version.cjs 23 | spec: "@yarnpkg/plugin-version" 24 | 25 | yarnPath: .yarn/releases/yarn-3.2.0.cjs 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Availity 2016-present 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 | -------------------------------------------------------------------------------- /constraints.pro: -------------------------------------------------------------------------------- 1 | % Force all workspace dependencies to be made explicit 2 | % https://yarnpkg.com/features/constraints#force-all-workspace-dependencies-to-be-made-explicit 3 | gen_enforced_dependency(WorkspaceCwd, DependencyIdent, 'workspace:*', DependencyType) :- 4 | workspace_ident(_, DependencyIdent), 5 | workspace_has_dependency(WorkspaceCwd, DependencyIdent, _, DependencyType). 6 | 7 | % Prevent two workspaces from depending on conflicting versions on the same dependency 8 | % This is useful for avoiding resolution bugs when multiple workspaces of the same package are installed 9 | gen_enforced_dependency(WorkspaceCwd, DependencyIdent, DependencyRange2, DependencyType) :- 10 | workspace_has_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, DependencyType), 11 | workspace_has_dependency(OtherWorkspaceCwd, DependencyIdent, DependencyRange2, DependencyType2), 12 | DependencyRange \= DependencyRange2. 13 | 14 | gen_enforced_dependency(WorkspaceCwd, 'lodash', '^4.17.21', DependencyType) :- 15 | workspace_has_dependency(WorkspaceCwd, 'lodash', _, DependencyType). 16 | -------------------------------------------------------------------------------- /docusaurus/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). 4 | 5 | ## [3.0.7](https://github.com/Availity/availity-workflow/compare/@availity/dinosaurdocs@3.0.6...@availity/dinosaurdocs@3.0.7) (2025-04-11) 6 | 7 | 8 | 9 | ## [3.0.6](https://github.com/Availity/availity-workflow/compare/@availity/dinosaurdocs@3.0.5...@availity/dinosaurdocs@3.0.6) (2024-08-26) 10 | 11 | 12 | 13 | ## [3.0.5](https://github.com/Availity/availity-workflow/compare/@availity/dinosaurdocs@3.0.4...@availity/dinosaurdocs@3.0.5) (2024-05-13) 14 | 15 | 16 | 17 | ## [3.0.4](https://github.com/Availity/availity-workflow/compare/@availity/dinosaurdocs@3.0.3...@availity/dinosaurdocs@3.0.4) (2024-02-16) 18 | 19 | 20 | 21 | ## [3.0.3](https://github.com/Availity/availity-workflow/compare/@availity/dinosaurdocs@3.0.2...@availity/dinosaurdocs@3.0.3) (2023-12-05) 22 | 23 | 24 | 25 | ## [3.0.2](https://github.com/Availity/availity-workflow/compare/@availity/dinosaurdocs@3.0.1...@availity/dinosaurdocs@3.0.2) (2023-10-04) 26 | 27 | 28 | 29 | ## [3.0.1](https://github.com/Availity/availity-workflow/compare/@availity/dinosaurdocs@3.0.0...@availity/dinosaurdocs@3.0.1) (2023-08-14) 30 | 31 | 32 | 33 | # [3.0.0](https://github.com/Availity/availity-workflow/compare/@availity/dinosaurdocs@2.1.1...@availity/dinosaurdocs@3.0.0) (2023-08-10) 34 | 35 | 36 | ### Build System 37 | 38 | * upgrade to react 18 ([25a4d85](https://github.com/Availity/availity-workflow/commit/25a4d85716c3444e9d97a4664f5fbcd526e17bea)) 39 | 40 | 41 | ### BREAKING CHANGES 42 | 43 | * see https://react.dev/blog/2022/03/08/react-18-upgrade-guide 44 | 45 | 46 | 47 | ## [2.1.1](https://github.com/Availity/availity-workflow/compare/@availity/dinosaurdocs@2.1.0...@availity/dinosaurdocs@2.1.1) (2023-02-01) 48 | 49 | 50 | 51 | # [2.1.0](https://github.com/Availity/availity-workflow/compare/@availity/dinosaurdocs@2.0.0...@availity/dinosaurdocs@2.1.0) (2022-12-02) 52 | 53 | 54 | ### Features 55 | 56 | * **docusaurus:** upgrade docusaurus to v2 ([c45f492](https://github.com/Availity/availity-workflow/commit/c45f492898fb7b2f9ddca2d60f52933ffc324757)) 57 | 58 | 59 | 60 | # [2.0.0](https://github.com/Availity/availity-workflow/compare/@availity/dinosaurdocs@1.1.1...@availity/dinosaurdocs@2.0.0) (2022-04-14) 61 | 62 | 63 | ### Features 64 | 65 | * adds nx semver plugin for tagging and changelog generation ([990cc5d](https://github.com/Availity/availity-workflow/commit/990cc5d9bc5f24b5058a90c11d05c67d836bbaf1)) 66 | * **workflow:** removes ie 11 support ([478accb](https://github.com/Availity/availity-workflow/commit/478accb39ba5bc1474a44a8577a10b463888fa5f)) 67 | 68 | 69 | ### BREAKING CHANGES 70 | 71 | * **workflow:** IE 11 is officially no longer supported by Availity 72 | 73 | 74 | 75 | ## [1.1.1](https://github.com/Availity/availity-workflow/compare/@availity/dinosaurdocs@1.1.0...@availity/dinosaurdocs@1.1.1) (2021-01-08) 76 | 77 | **Note:** Version bump only for package @availity/dinosaurdocs 78 | 79 | 80 | 81 | 82 | 83 | # 1.1.0 (2021-01-07) 84 | 85 | 86 | ### Bug Fixes 87 | 88 | * **dinosaurdocs:** fixes broken editUrl ([43d36a9](https://github.com/Availity/availity-workflow/commit/43d36a9dce05113e7eb90d3ddd5541ce3704b44d)) 89 | * **dinosaurdocs:** fixes broken editUrl ([c9d6772](https://github.com/Availity/availity-workflow/commit/c9d677271f0b92f3e6553f158f2f66e7a89c1936)) 90 | 91 | 92 | ### Features 93 | 94 | * **dinosaurdocs:** rm gatsby docs, update sidebar, img path ([90ee449](https://github.com/Availity/availity-workflow/commit/90ee449b7d3b5962236b8f684ffe32f3a467eba5)) 95 | * **docs:** add docusaurus docs ([f2a1d71](https://github.com/Availity/availity-workflow/commit/f2a1d71eaa60b645711efb04e30028ff8926f8dc)) 96 | -------------------------------------------------------------------------------- /docusaurus/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator. 4 | 5 | ## Installation 6 | 7 | ```console 8 | yarn install 9 | ``` 10 | 11 | ## Local Development 12 | 13 | ```console 14 | yarn start 15 | ``` 16 | 17 | This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ## Build 20 | 21 | ```console 22 | yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ## Deployment 28 | 29 | ```console 30 | GIT_USER= USE_SSH=true yarn deploy 31 | ``` 32 | 33 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 34 | -------------------------------------------------------------------------------- /docusaurus/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')] 3 | }; 4 | -------------------------------------------------------------------------------- /docusaurus/docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | slug: / 4 | --- 5 | 6 | #### Workflow CLI is a toolkit designed for kick-starting React web projects on the Availity Portal. 7 | 8 | Similar to [CRA](https://reactjs.org/docs/create-a-new-react-app.html) 9 | 10 | ## Get Started 11 | 12 | There are 2 main ways to get started with Workflow CLI: 13 | 14 | 1. [Tutorial:](/tutorial/) Step-by-Step instructions on how to install and start a project. This also includes details on the project structure as well as what each file/folder is used for. 15 | 2. [Quick Start:](/quick-start/) The TLDR guide to kickstarting a project from start to finish. 16 | 17 | 20 | 21 | ## Supported Browsers 22 | 23 | - Google Chrome 24 | - Mozilla Firefox 25 | - Microsoft Edge 26 | -------------------------------------------------------------------------------- /docusaurus/docs/quick-start.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Quick Start 3 | --- 4 | 5 | ## Create a new App using the Workflow CLI 6 | 7 | ```bash 8 | npx @availity/workflow init workflow-app 9 | ``` 10 | 11 | ## Change directories into the App Folder 12 | 13 | ```bash 14 | cd ./workflow-app 15 | ``` 16 | 17 | ## Start Development Enviornment 18 | 19 | ```bash 20 | yarn start 21 | ``` 22 | 23 | Workflow will start a development environment avaiable at `http://localhost:3000`. Any changes inside the `project/app` directory will hot reload the application. 24 | 25 | ## Create a Production Deployment 26 | 27 | The toolkit ships with several release tasks for building your bundle and updating the package version. 28 | 29 | ```bash 30 | yarn production 31 | ``` 32 | 33 | This will bundle the app in the `./dist` directory and create a tag with the appropriate version. Push this to the server. 34 | 35 | ```bash 36 | git push && git push --tags 37 | ``` 38 | 39 | > Note that in order to push the `./dist` folder up you will need to make sure the folder is not ignored inside of your `.gitignore` file. 40 | -------------------------------------------------------------------------------- /docusaurus/docs/recipes/root-imports.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Configuring Root Imports 3 | --- 4 | 5 | > Note that the below recipe only works in Workflow Versions `>=7.0.0` 6 | 7 | By default we include `babel-plugin-import` which allows you to import components using a specific syntax if you have a tree structure that goes past 2 - 3 layers. 8 | 9 | Using the `@/` key we can alias anything from the root of `project/app` inside of our project. We include the eslint config, and tsconfig so that if you are using vscode you will get all the intellisense for free. 10 | 11 | ## Example 12 | 13 | In the below example, we are 14 | 15 | ```jsx hideCopy=true 16 | import React from 'react'; 17 | import Form from '@/components/Form'; 18 | 19 |
{/* Stuff */}
; 20 | ``` 21 | 22 | ## Eslint Config 23 | 24 | Make sure you have the latest `eslint-config-availity` installed. 25 | 26 | ```bash 27 | yarn add eslint-config-availity@latest --dev 28 | ``` 29 | 30 | ```yaml header=.eslintrc.yml 31 | extends: availity/workflow 32 | ``` 33 | 34 | ## TsConfig For Visual Studio Code 35 | 36 | If you want intellisense in vscode to pick up the root imports and allow you to control click into components you will need to make sure your `tsconfig.json` is updated. We have pasted ours below that we use in our starter projects. 37 | 38 | ```json 39 | { 40 | "compilerOptions": { 41 | "target": "es5", 42 | "lib": ["dom", "dom.iterable", "esnext"], 43 | "allowJs": true, 44 | "skipLibCheck": true, 45 | "esModuleInterop": true, 46 | "allowSyntheticDefaultImports": true, 47 | "strict": true, 48 | "forceConsistentCasingInFileNames": true, 49 | "module": "esnext", 50 | "moduleResolution": "node", 51 | "resolveJsonModule": true, 52 | "isolatedModules": true, 53 | "noEmit": true, 54 | "jsx": "react", 55 | "baseUrl": ".", 56 | "paths": { 57 | "@/*": ["./project/app/*"] 58 | } 59 | } 60 | } 61 | ``` 62 | 63 | ## References 64 | 65 | - [babel-plugin-root-import](https://www.npmjs.com/package/babel-plugin-root-import) 66 | - [eslint-root-import-resolver](https://www.npmjs.com/package/eslint-import-resolver-babel-plugin-root-import) 67 | -------------------------------------------------------------------------------- /docusaurus/docs/recipes/testing-libraries.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Testing Libraries 3 | --- 4 | 5 | By default `@testing-library/react` and `@testing-library/jest-dom/extend-expect` are added to the project. Some of their scripts are also automatically added to the `setupFilesAfterEnv` param for jest [here](https://github.com/Availity/availity-workflow/blob/master/packages/workflow/jest.config.js#L38). 6 | 7 | - `@testing-library/react/cleanup-after-each` - Will clean up the DOM after each test has ran. 8 | - `@testing-library/jest-dom/extend-expect` - Custom jest matchers that you can use to extend jest 9 | 10 | If you want to override this you can create a file in the `/app` directory called `jest.init.js` and export whichever modules you want. 11 | 12 | ## Example 13 | 14 | ```javascript 15 | module.exports = ['@testing-library/react/cleanup-after-each', '@testing-library/jest-dom/extend-expect']; 16 | ``` 17 | 18 | More Info on Jest `setupFilesAfterEnv` [here](https://jestjs.io/docs/en/configuration#setupfilesafterenv-array) 19 | 20 | ### Mocking API Responses 21 | 22 | If your tests require data that's supplied by an external data source, you can use the `jest.mock(...)` function to automatically mock the modules used to supply the data. 23 | 24 | Once you've mocked the module, you can provide a `mockResolvedValue` that returns the data you want to use for your test. 25 | 26 | ## Example 27 | 28 | ```javascript 29 | import React from 'react'; 30 | import axiosMock from 'axios'; 31 | import slotmachineResponse from '../data/slotmachine.json'; 32 | 33 | jest.mock('axios'); 34 | 35 | axiosMock.mockResolvedValue({ 36 | config: { polling: false }, 37 | data: slotmachineResponse, 38 | status: 202, 39 | statusText: 'Ok' 40 | }); 41 | ``` 42 | 43 | More info on using mocks in Jest [here](https://jestjs.io/docs/en/mock-functions) 44 | -------------------------------------------------------------------------------- /docusaurus/docs/recipes/typescript.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Adding Typescript Support 3 | --- 4 | 5 | Step by step for adding typescript to workflow. 6 | 7 | > Note that the below recipe only works in Workflow Versions `>=7.0.0` 8 | 9 | Adding in [Typescript](https://www.typescriptlang.org/) to an existing project is close to the same as [create-react-app](https://create-react-app.dev/docs/adding-typescript). 10 | 11 | ## Install Dependencies 12 | 13 | ```bash 14 | yarn add typescript @types/node @types/react @types/react-dom @types/jest --dev 15 | ``` 16 | 17 | Once installed, all files need to be renamed from `.js/.jsx` to `.tsx`. 18 | 19 | ## Adding the TsConfig 20 | 21 | Also noted on the previous section. We use the tsconfig for vscode to let us intellisense the root imports and in this case also experimental decorators. Add the below file to the root of you project workspace. 22 | 23 | ```json header=tsconfig.json 24 | { 25 | "compilerOptions": { 26 | "target": "es5", 27 | "lib": ["dom", "dom.iterable", "esnext"], 28 | "allowJs": true, 29 | "skipLibCheck": true, 30 | "esModuleInterop": true, 31 | "allowSyntheticDefaultImports": true, 32 | "strict": true, 33 | "forceConsistentCasingInFileNames": true, 34 | "module": "esnext", 35 | "moduleResolution": "node", 36 | "resolveJsonModule": true, 37 | "isolatedModules": true, 38 | "noEmit": true, 39 | "jsx": "react", 40 | "baseUrl": ".", 41 | "paths": { 42 | "@/*": ["./project/app/*"] 43 | } 44 | } 45 | } 46 | ``` 47 | 48 | ## Try it Out 49 | -------------------------------------------------------------------------------- /docusaurus/docs/reference/commands.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Reference: CLI Commands' 3 | --- 4 | 5 | Full list of Workflow Commands 6 | 7 | When inside of the current working directory of your application, you can run any of the below commands by running: 8 | 9 | ```bash 10 | yarn 11 | ``` 12 | 13 | ## `help` 14 | 15 | Show help menu for all CLI options. 16 | 17 | ```shell hideCopy=true 18 | > yarn run help 19 | 20 | Usage: av [options] 21 | 22 | Commands: 23 | av init [options] Initialize your project from scratch. 24 | av release Bundle project for distribution (production 25 | or staging) and create a git tag 26 | av start Start the development server 27 | av lint Lint source files using ESLint 28 | av test Run your tests 29 | av profile Analyze Webpack bundles and find what is 30 | contributing their sizes 31 | av build Bundle project for distribution (production 32 | or staging) 33 | av about About @availity/workflow 34 | 35 | Options: 36 | --help, -h Show help [boolean] 37 | --version, -v Show version number [boolean] 38 | 39 | Examples: 40 | av init my-app-name 41 | av start 42 | av lint 43 | 44 | View documentation at https://github.com/availity/workflow 45 | ``` 46 | 47 | ## `init` 48 | 49 | Initializes a new project. Typically this is used in unison with `npx` for kickstarting a new project. See the [Quick Start](/quick-start/) guide more information about creating a new project. 50 | 51 | #### Options 52 | 53 | - `--version`, `-v`: Specify which version of the project you want. [default: "latest"] 54 | - `--current-dir`, `-c`: If you want the project to be created in the current directory 55 | - `--template`, `-t`: The template you want to initialize the project with 56 | 57 | #### Availity Templates 58 | 59 | - [React Starter (Default)](https://github.com/Availity/availity-starter-react) 60 | - [Wizard Starter (React)](https://github.com/Availity/availity-starter-wizard) 61 | - [Typescript Starter](https://github.com/Availity/availity-starter-typescript) 62 | 63 | ```bash 64 | npx @availity/workflow init my-package-name --template https://github.com/Availity/availity-starter-typescript 65 | ``` 66 | 67 | ## `start` 68 | 69 | Start the development server and watches for file changes. Hot-reloading can be toggled via workflow settings. 70 | 71 | ```bash 72 | yarn start 73 | ``` 74 | 75 | #### options 76 | 77 | - `--dry-run`: Start the development server using production settings. 78 | 79 | ## `lint` 80 | 81 | Lint project files using EsLint. 82 | 83 | #### options 84 | 85 | - `--include`: Include additional glob patterns for linting. 86 | - `--ignore-git-untracked`: Ignore files that are not indexed by git. 87 | - `--disable-linter`: Disable linter when creating bundles for production or staging. 88 | 89 | ## `build` 90 | 91 | - Cleans up `/dist` folder 92 | - Bundles project assets into the `/dist` folder 93 | 94 | ## `release` 95 | 96 | - Prompts the user for a version 97 | - Cleans up `/dist` folder 98 | - Bundles project assets into the `/dist` folder 99 | - Tags the version in Git 100 | 101 | There are different forms of releases, `production` and `staging`. 102 | 103 | Production will minify the javascript assets and staging will not. 104 | 105 | ```bash 106 | yarn production 107 | ``` 108 | 109 | #### options 110 | 111 | - `--dry-run` 112 | - Disables committing files to git 113 | - Disables creating a git tag 114 | 115 | Skipped tasks will print a message in the console 116 | 117 | ```bash hideCopy=true 118 | ℹ [ Dry Run ] Skipping version bump 119 | ``` 120 | 121 | ## `profile` 122 | 123 | Analyze Webpack bundles and find what is contributing their sizes. This command generates an html graphical chart that automatically opens in the browser as well as a text summary report in the console. 124 | 125 | ## `test` 126 | 127 | Run the tests for your project. (Jest) 128 | 129 | #### options 130 | 131 | #### `--coverage` 132 | 133 | Indicates that test coverage information should be collected and reported in the output. You can leverage the `--coverage` option from Jest. 134 | 135 | ```shell hideCopy=true 136 | > yarn test --coverage 137 | › Started testing 138 | Chrome 59.0.3071 (Mac OS X 10.12.5): Executed 3 of 3 SUCCESS (0.706 secs / 0.082 secs) 139 | 140 | =============================== Coverage summary =============================== 141 | Statements : 27.69% ( 8620/31134 ) 142 | Branches : 9.81% ( 1980/20189 ) 143 | Functions : 22.15% ( 1381/6235 ) 144 | Lines : 27.23% ( 7955/29217 ) 145 | ================================================================================ 146 | ✔ Finished testing 147 | ``` 148 | 149 | ## `upgrade` 150 | 151 | Upgrades the workflow dependencies to the latest version and in some cases will attempt to fix the breaking chnages. 152 | 153 | ```bash 154 | npx @availity/workflow-upgrade 155 | ``` 156 | -------------------------------------------------------------------------------- /docusaurus/docs/tutorial/deploy.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Creating a Deployment 3 | --- 4 | 5 | If you have made it this far then you must mean business. The application must have 100% test coverage and be ready to be deployed on a Friday afternoon to production. 😉 6 | 7 | In all seriousness let's start on how to get your application in a deploy ready state. 8 | 9 | ## Running the Build Script 10 | 11 | Our CLI ships with two different commands for building your deployments. The first is the `staging` command and the second is `production`. 12 | 13 | Both commands run the same CLI workflow, the only difference is that the `production` command will minify your code for a smaller gzip footprint. 14 | 15 | For testing purposes let's use the `staging` command below to get started: 16 | 17 | ```bash 18 | yarn staging 19 | ``` 20 | 21 | ## Selecting a Version Bump 22 | 23 | You should now be prompted to select what type of version bump you would like: 24 | 25 | ```shell hideCopy=true 26 | ? What type of version bump would you like to do? 27 | 1) patch ( 0.1.0 => 0.1.1 ) 28 | 2) minor ( 0.1.0 => 0.2.0 ) 29 | 3) major ( 0.1.0 => 1.0.0 ) 30 | ────────────── 31 | 4) other 32 | Answer: 1 33 | ``` 34 | 35 | You can choose whichever version bump you wish, typically we follow the [semver versioning](https://docs.npmjs.com/about-semantic-versioning) strategy for what version bump we want to give in our deployment stage, however for projects that aren't using NPM its not a huge deal. 36 | 37 | Once entered, the CLI will bundle the app in the `./dist` directory and create a tag with the appropriate version. 38 | 39 | ## Pushing the Commit 40 | 41 | There will already be a commit created with the version bump and `dist` folder updates included. You will want to push this and the git tag that was auto generated up to your version control. 42 | 43 | > Note that if you are needing the `dist` folder to be committed you will need to double check the folder is not added to the `.gitignore` file. 44 | 45 | ```bash 46 | git push && git push --tags 47 | ``` 48 | 49 | ## Next Steps 50 | 51 | Congratulations! You successfully created your first deployment artifact! You can choose to read more about the additional features that our Workflow toolkit provides in the `Recipes` section. 52 | 53 | Checkout our [React Component Library](https://availity.github.io/availity-react/) for some pre-built reactstrap components to assist in building solid web applications. 54 | -------------------------------------------------------------------------------- /docusaurus/docs/tutorial/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | --- 4 | 5 | import useBaseUrl from '@docusaurus/useBaseUrl'; 6 | 7 | ## Prerequisites 8 | 9 | Throughout the documentation you will see [yarn](https://yarnpkg.com/) used. You are not required to use this however it is our package manager of choice. If you are still wanting to use npm then just note the subtle differences in the commands. `yarn start` would be the same `npm start`. 10 | 11 | The minimum required [Nodejs](https://nodejs.org/) version to run and use our toolkit is `v16.0.0`, but using `v18.0.0` or higher is recommended. 12 | 13 | ## Creating a new App Using the Workflow CLI 14 | 15 | Our toolkit has a built in CLI that will allow you to run multiple commands like you would with any other react project. In addition to the default commands you would see like `start`, `test`, `build` we have an `init` command that will initialize a new project using a [specified default template](/reference/commands/#availity-templates). 16 | 17 | You can run the below command to get started: 18 | 19 | ```bash 20 | npx @availity/workflow init workflow-app 21 | ``` 22 | 23 | What the above command is doing is using [npx](https://www.npmjs.com/package/npx) to download and execute the script without you having a pre-existing `package.json`. The `init` command is the command we send to our package `@availity/workflow`. The last argument, `workflow-app` is the project name that we gave. 24 | 25 | Once the CLI finishes initializing the project we can `cd` into the directory with the below command: 26 | 27 | ```bash 28 | cd ./workflow-app 29 | ``` 30 | 31 | Go ahead and start the application by running the below command: 32 | 33 | ```bash 34 | yarn start 35 | ``` 36 | 37 | ## Project Structure 38 | 39 | Next let's open the project in any one of your favorite editors so we can review the project structure. 40 | 41 | Project Structure 42 | 43 | > Our editor of choice is [Visual Studio Code](https://code.visualstudio.com/) and the above screen shot is depicting what it would look like in here. 44 | 45 | ### Application Code 46 | 47 | All of our application logic is stored in the `project/app` folder. We can see the `index.js` is the main entry point. 48 | 49 | Making any change to the files inside of `project/app` will automatically hot-reload the web page, given some small edge cases. 50 | 51 | Project Change Example 52 | 53 | We recommend you explore the coding styles and libraries we chose in this starter toolkit before deleting as it has some good practices to follow when developing web applications. 54 | 55 | ### Exploring workflow.js 56 | 57 | Inside of the `config` folder there is a file titled `workflow.js`. You can use this file to provide any overrides to the webpack configuration, development dependencies as well as additional proxy configurations you may want. 58 | 59 | Let's open the current file and see what we got. 60 | 61 | ```js header=workflow.js 62 | module.exports = (config) => { 63 | config.development.open = '#/?spaceId=48C607A70B5A46A3864A34E2BDDDEA04'; 64 | 65 | config.development.hotLoader = true; 66 | 67 | return config; 68 | }; 69 | ``` 70 | 71 | The workflow file takes in a `config` object which contains all of the webpack configurations, babel presets and more. We specify the webpack development server to open the application with a path of `#/?spaceId=48C607A70B5A46A3864A34E2BDDDEA04` once it has compiled. 72 | 73 | ```bash hideCopy=true 74 | config.development.open = '#/?spaceId=48C607A70B5A46A3864A34E2BDDDEA04'; 75 | ``` 76 | 77 | Wait, but what is a `spaceId`? We will get into that later but if you want you can skip ahead to `here`. 78 | 79 | Next we have some overrides to the `hotLoader` that will hot reload our app. The experimental flag is enabled so that we can leverage a new feature that the [React Core Team](https://reactjs.org/community/team.html) is working on but is not yet feature complete. 80 | 81 | ```js hideCopy=true 82 | config.development.hotLoader = { 83 | enabled: true, 84 | experimental: true 85 | }; 86 | ``` 87 | 88 | Lastly we have to return the `config` for the rest of the build process to take advantage of the overrides we gave it. 89 | 90 | By now you should have a general grasp on the project strcuture for developing and making changes to your application. Next we will talk about getting data into your application. 91 | -------------------------------------------------------------------------------- /docusaurus/docs/upgrading-workflow.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Upgrading @availity/workflow 3 | --- 4 | 5 | It's important to stay up to date with the latest version of `@availity/workflow`. Keeping your dependencies up to date minimizes security risk and ensures your project maintains compatibility with our build system. You can upgrade `@availity/workflow` manually yourself, or utilize the `workflow-upgrade` script. 6 | 7 | > Note: If using the `workflow-upgrade` script, you will still need to maintain the rest of your dependencies yourself. 8 | 9 | ## The `workflow-upgrade` script 10 | 11 | Navigate to the directory you want to upgrade `@availity/workflow` for and run the following command. 12 | 13 | ```bash 14 | npx @availity/workflow-upgrade 15 | ``` 16 | 17 | ### What does the `workflow-upgrade` script do? 18 | 19 | The `workflow-upgrade` script upgrades your project to the latest version of `@availity/workflow`. Next, it ensures your project is not using deprecated plugins. Finally it upgrades the devDeps and peerDeps associated with `@availity/workflow`. 20 | 21 | ## Resources 22 | 23 | - [The @availity/workflow Changelog](https://github.com/Availity/availity-workflow/blob/master/packages/workflow/CHANGELOG.md) 24 | - [The workflow-upgrade script](https://github.com/Availity/availity-workflow/blob/master/packages/workflow-upgrade/index.js) 25 | -------------------------------------------------------------------------------- /docusaurus/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [], 3 | // themes: [['@docusaurus/theme-search-algolia', { id: '01' }]], 4 | onBrokenLinks: 'log', 5 | title: 'Availity Workflow Docs', 6 | tagline: 'Toolkit for Availity web projects', 7 | url: 'https://availity.github.io', 8 | baseUrl: '/availity-workflow/', 9 | favicon: 'img/favicon.ico', 10 | organizationName: 'availity', 11 | projectName: 'availity-workflow', 12 | themeConfig: { 13 | // algolia: { 14 | // apiKey: 'eec0154a008662c32d440b7de7982cd2', 15 | // indexName: 'availity' 16 | // }, 17 | // announcementBar: { 18 | // id: 'supportus', 19 | // backgroundColor: '#e29f0d', 20 | // textColor: 'black', 21 | // content: 22 | // '⭐️ If you like Availity-React, give it a star on GitHub! ⭐️' 23 | // }, 24 | colorMode: { 25 | defaultMode: 'dark', 26 | // Should we use the prefers-color-scheme media-query, 27 | // using user system preferences, instead of the hardcoded defaultMode 28 | respectPrefersColorScheme: true 29 | }, 30 | 31 | navbar: { 32 | title: 'Availity Docs', 33 | // hideOnScroll: true, 34 | logo: { 35 | alt: 'Availity Docs Logo', 36 | src: 'img/icon.png', 37 | href: 'https://availity.github.io', 38 | target: '_self' 39 | }, 40 | items: [ 41 | { 42 | to: 'https://availity.github.io/availity-react', 43 | target: '_self', 44 | label: 'React', 45 | position: 'right' 46 | }, 47 | { 48 | to: 'https://availity.github.io/sdk-js', 49 | target: '_self', 50 | label: 'SDK-JS', 51 | position: 'right' 52 | }, 53 | { 54 | to: '/', // availity.github.io/availity-workflow, this repo 55 | label: 'Workflow', 56 | position: 'right' 57 | }, 58 | { 59 | href: 'https://github.com/availity/availity-workflow', 60 | position: 'right', 61 | className: 'header-github-link', 62 | 'aria-label': 'GitHub repository' 63 | } 64 | ] 65 | }, 66 | footer: {} 67 | }, 68 | presets: [ 69 | [ 70 | '@docusaurus/preset-classic', 71 | { 72 | docs: { 73 | // It is recommended to set document id as docs home page (`docs/` path). 74 | routeBasePath: '/', 75 | sidebarPath: require.resolve('./sidebars.js'), 76 | editUrl: 'https://github.com/availity/availity-workflow/edit/master/docusaurus/' 77 | }, 78 | 79 | theme: { 80 | customCss: require.resolve('./src/css/custom.css') 81 | } 82 | } 83 | ] 84 | ] 85 | }; 86 | -------------------------------------------------------------------------------- /docusaurus/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@availity/dinosaurdocs", 3 | "version": "3.0.7", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "serve": "docusaurus serve" 12 | }, 13 | "dependencies": { 14 | "@docusaurus/core": "^3.7.0", 15 | "@docusaurus/preset-classic": "^3.7.0", 16 | "@mdx-js/react": "^3.1.0", 17 | "clsx": "^2.1.1", 18 | "react": "^18.3.1", 19 | "react-dom": "^18.3.1" 20 | }, 21 | "browserslist": { 22 | "production": [ 23 | ">0.2%", 24 | "not dead", 25 | "not op_mini all" 26 | ], 27 | "development": [ 28 | "last 1 chrome version", 29 | "last 1 firefox version", 30 | "last 1 safari version" 31 | ] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /docusaurus/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docusaurus", 3 | "$schema": "../node_modules/nx/schemas/project-schema.json", 4 | "projectType": "library", 5 | "targets": { 6 | "version": { 7 | "executor": "@jscutlery/semver:version", 8 | "options": { 9 | "preset": "angular", 10 | "commitMessageFormat": "chore({projectName}): release version ${version}", 11 | "tagPrefix": "@availity/dinosaurdocs@", 12 | "trackDeps": true 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docusaurus/sidebars.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | someSidebar: { 3 | 'Workflow CLI Docs': ['index', 'quick-start', 'upgrading-workflow'], 4 | Tutorial: ['tutorial/index', 'tutorial/mocks', 'tutorial/deploy', 'tutorial/dependency-management'], 5 | Recipe: ['recipes/testing-libraries', 'recipes/root-imports', 'recipes/typescript', 'recipes/logging'], 6 | 'API Reference': ['reference/workflow-config', 'reference/commands', 'reference/mock-server'] 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /docusaurus/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | /** 3 | * Any CSS included here will be global. The classic template 4 | * bundles Infima by default. Infima is a CSS framework designed to 5 | * work well for content-centric websites. 6 | */ 7 | 8 | /* You can override the default Infima variables here. */ 9 | /* :root { 10 | --ifm-color-primary: #e29f0d; 11 | --ifm-color-primary-dark: rgb(33, 175, 144); 12 | --ifm-color-primary-darker: rgb(31, 165, 136); 13 | --ifm-color-primary-darkest: rgb(26, 136, 112); 14 | --ifm-color-primary-light: rgb(70, 203, 174); 15 | --ifm-color-primary-lighter: rgb(102, 212, 189); 16 | --ifm-color-primary-lightest: rgb(146, 224, 208); 17 | --ifm-code-font-size: 95%; 18 | } */ 19 | 20 | .docusaurus-highlight-code-line { 21 | background-color: rgb(72, 77, 91); 22 | display: block; 23 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 24 | padding: 0 var(--ifm-pre-padding); 25 | } 26 | 27 | .header-github-link:hover { 28 | opacity: 0.6; 29 | } 30 | 31 | .header-github-link:before { 32 | content: ''; 33 | width: 24px; 34 | height: 24px; 35 | display: flex; 36 | background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") 37 | no-repeat; 38 | } 39 | html[data-theme='dark'] .header-github-link:before { 40 | background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") 41 | no-repeat; 42 | } 43 | -------------------------------------------------------------------------------- /docusaurus/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Availity/availity-workflow/5be3c71ebf853865359b3a4458ee059be80edcd1/docusaurus/static/.nojekyll -------------------------------------------------------------------------------- /docusaurus/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Availity/availity-workflow/5be3c71ebf853865359b3a4458ee059be80edcd1/docusaurus/static/img/favicon.ico -------------------------------------------------------------------------------- /docusaurus/static/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Availity/availity-workflow/5be3c71ebf853865359b3a4458ee059be80edcd1/docusaurus/static/img/icon.png -------------------------------------------------------------------------------- /docusaurus/static/img/jest-debug.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Availity/availity-workflow/5be3c71ebf853865359b3a4458ee059be80edcd1/docusaurus/static/img/jest-debug.gif -------------------------------------------------------------------------------- /docusaurus/static/img/mock-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Availity/availity-workflow/5be3c71ebf853865359b3a4458ee059be80edcd1/docusaurus/static/img/mock-data.png -------------------------------------------------------------------------------- /docusaurus/static/img/notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Availity/availity-workflow/5be3c71ebf853865359b3a4458ee059be80edcd1/docusaurus/static/img/notification.png -------------------------------------------------------------------------------- /docusaurus/static/img/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Availity/availity-workflow/5be3c71ebf853865359b3a4458ee059be80edcd1/docusaurus/static/img/profile.png -------------------------------------------------------------------------------- /docusaurus/static/img/project-change.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Availity/availity-workflow/5be3c71ebf853865359b3a4458ee059be80edcd1/docusaurus/static/img/project-change.gif -------------------------------------------------------------------------------- /docusaurus/static/img/project-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Availity/availity-workflow/5be3c71ebf853865359b3a4458ee059be80edcd1/docusaurus/static/img/project-structure.png -------------------------------------------------------------------------------- /docusaurus/static/img/workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Availity/availity-workflow/5be3c71ebf853865359b3a4458ee059be80edcd1/docusaurus/static/img/workflow.png -------------------------------------------------------------------------------- /example/.eslintrc.yaml: -------------------------------------------------------------------------------- 1 | extends: availity/workflow 2 | 3 | root: true 4 | 5 | settings: 6 | import/resolver: 7 | typescript: {} 8 | root-import: 9 | rootPathPrefix: '@' 10 | rootPathSuffix: project/app 11 | extensions: 12 | - .js 13 | - .jsx 14 | - .tsx 15 | - .ts 16 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@availity/example", 3 | "private": true, 4 | "version": "4.5.0", 5 | "description": "React project using @availity/workflow", 6 | "main": "index.js", 7 | "scripts": { 8 | "start": "av start", 9 | "av": "av", 10 | "test": "av test", 11 | "test:coverage": "av test --coverage", 12 | "test:watch": "av test --watch", 13 | "integration": "yarn lint && yarn test && yarn build && yarn build:production", 14 | "about": "av about", 15 | "lint": "av lint", 16 | "help": "av help", 17 | "upgrade:workflow": "node ../packages/workflow-upgrade/bin.js", 18 | "profile": "av profile", 19 | "profile:production": "cross-env NODE_ENV=production av profile", 20 | "build": "av build", 21 | "build:production": "cross-env NODE_ENV=production av build", 22 | "build:staging": "cross-env NODE_ENV=staging av build", 23 | "release": "av release", 24 | "production": "cross-env NODE_ENV=production av release", 25 | "staging": "cross-env NODE_ENV=staging av release" 26 | }, 27 | "keywords": [ 28 | "react", 29 | "availity", 30 | "workflow" 31 | ], 32 | "license": "MIT", 33 | "homepage": "https://github.com/availity/availity-workflow#readme", 34 | "dependencies": { 35 | "@availity/api-axios": "^10.0.3", 36 | "@availity/element": "^1.0.32", 37 | "@availity/yup": "^6.0.3", 38 | "@hookform/resolvers": "^4.1.3", 39 | "@tanstack/react-query": "^4.36.1", 40 | "axios": "^1.9.0", 41 | "classnames": "^2.5.1", 42 | "formik": "^2.4.6", 43 | "prop-types": "^15.8.1", 44 | "react": "^18.3.1", 45 | "react-dom": "^18.3.1", 46 | "react-hook-form": "^7.56.1", 47 | "reactstrap": "^8.10.1", 48 | "yup": "^1.6.1" 49 | }, 50 | "devDependencies": { 51 | "@availity/workflow": "workspace:*", 52 | "@testing-library/react": "^14.3.1", 53 | "@types/jest": "^27.5.2", 54 | "@types/node": "^22.14.1", 55 | "@types/react": "^18.3.21", 56 | "@types/react-dom": "^18.3.7", 57 | "@types/reactstrap": "^8.7.2", 58 | "@types/yup": "^0.32.0", 59 | "cross-env": "^7.0.3", 60 | "esbuild-loader": "^3.2.0", 61 | "eslint-config-availity": "^10.0.7", 62 | "jest": "^27.5.1", 63 | "jest-dom": "^4.0.0", 64 | "jest-junit": "^16.0.0", 65 | "typescript": "5.7.3" 66 | }, 67 | "lint-staged": { 68 | "*.(js|ts|jsx|tsx)": [ 69 | "prettier --write", 70 | "git add" 71 | ] 72 | }, 73 | "prettier": { 74 | "printWidth": 120, 75 | "singleQuote": true, 76 | "trailingComma": "es5" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /example/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "$schema": "../node_modules/nx/schemas/project-schema.json", 4 | "projectType": "app", 5 | "targets": { 6 | "version": { 7 | "executor": "@jscutlery/semver:version", 8 | "options": { 9 | "preset": "angular", 10 | "commitMessageFormat": "chore({projectName}): release version ${version}", 11 | "tagPrefix": "@availity/{projectName}@", 12 | "trackDeps": true 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /example/project/app/App.test.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react'; 2 | 3 | import { chain, nullChain } from '@/chain'; 4 | 5 | import App from './App'; 6 | 7 | describe('App', () => { 8 | test('renders without error', () => { 9 | const { getByText } = render(); 10 | expect(getByText('Test Card Header')).toBeDefined(); 11 | }); 12 | 13 | test('optional chaining field has initial value', () => { 14 | // This test makes sure you are at least developing using a newer version of Node 15 | // You will still need to test transpiled code 16 | const { getByDisplayValue } = render(); 17 | expect(getByDisplayValue(chain)).toBeDefined(); 18 | }); 19 | 20 | test('nullish coalesced field has initial value', () => { 21 | // This test makes sure you are at least developing using a newer version of Node 22 | // You will still need to test transpiled code 23 | const { getByDisplayValue } = render(); 24 | expect(getByDisplayValue(nullChain)).toBeDefined(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /example/project/app/App.tsx: -------------------------------------------------------------------------------- 1 | import { ThemeProvider, PageHeader, Card, Container } from '@availity/element'; 2 | import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; 3 | 4 | import { SearchForm } from '@/components/Form'; 5 | 6 | const client = new QueryClient(); 7 | 8 | const App = () => ( 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /example/project/app/chain.js: -------------------------------------------------------------------------------- 1 | // Simple .js file to test compiling and linting for @babel/plugin-proposal-optional-chaining and @babel/plugin-proposal-nullish-coalescing-operator 2 | 3 | const chainObject = { 4 | org: { 5 | types: [ 6 | { 7 | name: 'Availity', 8 | }, 9 | ], 10 | }, 11 | }; 12 | 13 | export const chain = chainObject.org?.types?.[0]?.name; 14 | export const nullChain = chainObject.org?.types?.[0]?.value ?? 'iWasCoalesced!'; 15 | -------------------------------------------------------------------------------- /example/project/app/components/Form.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { 3 | Alert, 4 | BlockUi, 5 | Button, 6 | CardActions, 7 | CardContent, 8 | CardHeader, 9 | Collapse, 10 | FormProvider, 11 | ControlledTextField, 12 | useForm, 13 | } from '@availity/element'; 14 | import { yupResolver } from '@hookform/resolvers/yup'; 15 | import { object, string } from 'yup'; 16 | 17 | import { chain, nullChain } from '@/chain'; 18 | 19 | import config from './form-config.json'; 20 | 21 | const initialValues = { 22 | formField: '', 23 | chainedField: chain, 24 | nullishCoalescedField: nullChain, 25 | }; 26 | 27 | const schema = object().shape({ 28 | formField: string().required('This field is required.'), 29 | chainedField: string().required('This field is required.'), 30 | nullishCoalescedField: string().required('This field is required.'), 31 | }); 32 | 33 | async function sleep(duration = 2500) { 34 | await new Promise((resolve) => { 35 | setTimeout(resolve, duration); 36 | }); 37 | } 38 | 39 | export function SearchForm() { 40 | const [loading, setLoading] = useState(false); 41 | const [isSubmitted, setIsSubmitted] = useState(false); 42 | 43 | const handleOnSubmit = async () => { 44 | setLoading(true); 45 | await sleep(); 46 | setLoading(false); 47 | setIsSubmitted(true); 48 | }; 49 | 50 | const methods = useForm({ 51 | defaultValues: initialValues, 52 | mode: 'onBlur', 53 | resolver: yupResolver(schema), 54 | }); 55 | 56 | return ( 57 | 58 |
59 | 60 | 61 | 62 | 63 | setIsSubmitted(false)} severity="success"> 64 | Form submitted! 65 | 66 | 67 | 68 | {config.fields.map((fieldProps) => ( 69 | 70 | ))} 71 | 72 | 73 | 74 | 77 | 80 | 81 | 82 |
83 | ); 84 | } 85 | -------------------------------------------------------------------------------- /example/project/app/components/form-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "header": "Test Card Header", 3 | "fields": [ 4 | { 5 | "name": "formField", 6 | "label": "Some Field Label" 7 | }, 8 | { 9 | "name": "chainedField", 10 | "label": "Another Field Label" 11 | }, 12 | { 13 | "name": "nullishCoalescedField", 14 | "label": "Yet Another Field Label" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /example/project/app/form.d.ts: -------------------------------------------------------------------------------- 1 | export interface FormValues { 2 | formField: string; 3 | } 4 | -------------------------------------------------------------------------------- /example/project/app/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import '@availity/yup'; 4 | import App from './App'; 5 | 6 | const container = document.getElementById('root') as HTMLElement; 7 | const root = createRoot(container); 8 | 9 | root.render(); 10 | -------------------------------------------------------------------------------- /example/project/config/workflow.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | 4 | // TODO: add more complex workflow features for building/testing 5 | module.exports = (config) => { 6 | config.development.open = '/'; 7 | 8 | config.development.hotLoader = true; 9 | 10 | config.development.stats = { 11 | level: 'normal', 12 | }; 13 | 14 | config.development.infrastructureLogging = { 15 | level: 'info', 16 | }; 17 | 18 | config.ekko.enabled = false; 19 | 20 | // specify how to resolve node_modules since yarn hoists them to top-level 21 | config.modifyWebpackConfig = (webpackConfig, settings) => { 22 | webpackConfig.resolveLoader.modules.push(path.join(settings.project(), '../node_modules')); 23 | webpackConfig.plugins.push( 24 | new webpack.ProvidePlugin({ 25 | Buffer: ['buffer', 'Buffer'], 26 | }) 27 | ); 28 | }; 29 | 30 | return config; 31 | }; 32 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true, 4 | "allowJs": true, 5 | "baseUrl": ".", 6 | "paths": { 7 | "@/*": ["./project/app/*"] 8 | }, 9 | "target": "es5", 10 | "lib": ["dom", "dom.iterable", "esnext"], 11 | "skipLibCheck": true, 12 | "esModuleInterop": true, 13 | "allowSyntheticDefaultImports": true, 14 | "strict": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "module": "esnext", 17 | "jsx": "react-jsx", 18 | "moduleResolution": "node", 19 | "resolveJsonModule": true, 20 | "isolatedModules": true, 21 | "noEmit": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /nx.json: -------------------------------------------------------------------------------- 1 | { 2 | "targetDefaults": { 3 | "build": { 4 | "cache": true, 5 | "dependsOn": [ 6 | { 7 | "target": "build", 8 | "dependencies": true 9 | } 10 | ] 11 | }, 12 | "test": { 13 | "cache": true 14 | }, 15 | "lint": { 16 | "cache": true 17 | }, 18 | "package": { 19 | "cache": true, 20 | "dependsOn": [ 21 | { 22 | "target": "package", 23 | "dependencies": true 24 | } 25 | ] 26 | }, 27 | "prepare": { 28 | "cache": true, 29 | "dependsOn": [ 30 | { 31 | "target": "prepare", 32 | "dependencies": true 33 | } 34 | ] 35 | } 36 | }, 37 | "extends": "@nx/workspace/presets/npm.json", 38 | "useInferencePlugins": false, 39 | "defaultBase": "master", 40 | "pluginsConfig": { 41 | "@nx/js": { 42 | "analyzeSourceFiles": true 43 | } 44 | }, 45 | "useLegacyCache": true 46 | } 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "availity-workflow", 4 | "engines": { 5 | "yarn": "^3.0.0", 6 | "node": "^18.0.0 || ^20.0.0 || ^22.0.0" 7 | }, 8 | "workspaces": [ 9 | "packages/*", 10 | "docusaurus", 11 | "example" 12 | ], 13 | "scripts": { 14 | "build:app": "yarn workspace @availity/example build", 15 | "build:docs": "yarn workspace @availity/dinosaurdocs build", 16 | "changelog:generate": "cd $INIT_CWD && conventional-changelog -p angular -i CHANGELOG.md -s --commit-path=$INIT_CWD --skip-unstable", 17 | "check:packages": "sh ./scripts/artifactory-check.sh", 18 | "check:dependencies": "node scripts/check-missing-deps.js", 19 | "check:versions": "node scripts/check-version-strategy.js", 20 | "format": "prettier --write 'packages/*/*.(js|ts|tsx|jsx)' 'packages/*/!(node_modules|dist)/**/*.(js|ts|jsx|tsx)'", 21 | "lint": "node ./node_modules/.bin/eslint .", 22 | "postinstall": "is-ci || husky install", 23 | "start": "yarn workspace @availity/dinosaurdocs start --no-open", 24 | "start:app": "yarn workspace @availity/example start", 25 | "start:docs": "yarn workspace @availity/dinosaurdocs start", 26 | "test": "jest --silent", 27 | "test:app": "yarn workspace @availity/example test", 28 | "test:integration": "yarn workspaces foreach -i -v run integration --verbose", 29 | "version": "nx affected --target version --parallel=1", 30 | "version:dry-run": "nx affected --target version --dryRun --verbose --parallel=1" 31 | }, 32 | "devDependencies": { 33 | "@commitlint/cli": "^19.8.0", 34 | "@commitlint/config-angular": "^19.8.0", 35 | "@commitlint/config-conventional": "^19.8.0", 36 | "@jscutlery/semver": "^5.6.0", 37 | "@nx/js": "20.8.1", 38 | "@nx/workspace": "20.8.1", 39 | "@types/is-ci": "^3.0.4", 40 | "conventional-changelog-cli": "^5.0.0", 41 | "conventional-changelog-conventionalcommits": "^8.0.0", 42 | "dependency-check": "^4.1.0", 43 | "eslint-config-availity": "^10.0.7", 44 | "gh-pages": "^5.0.0", 45 | "husky": "^9.1.7", 46 | "is-ci": "^3.0.1", 47 | "jest": "^27.5.1", 48 | "lint-staged": "^15.5.2", 49 | "nx": "20.8.1", 50 | "prettier": "^3.5.3", 51 | "typescript": "5.7.3" 52 | }, 53 | "resolutions": { 54 | "socks": "2.7.3" 55 | }, 56 | "commitlint": { 57 | "extends": [ 58 | "@commitlint/config-conventional" 59 | ], 60 | "rules": { 61 | "header-max-length": [ 62 | 0, 63 | "always", 64 | 85 65 | ] 66 | } 67 | }, 68 | "prettier": { 69 | "printWidth": 120, 70 | "singleQuote": true, 71 | "trailingComma": "none" 72 | }, 73 | "jest": { 74 | "transformIgnorePatterns": [ 75 | "[/\\\\]node_modules[/\\\\](?!@availity).+\\.(js|jsx|ts|tsx)$" 76 | ], 77 | "testPathIgnorePatterns": [ 78 | "/node_modules/", 79 | "/docusaurus/" 80 | ], 81 | "testRegex": ".*.spec.js$" 82 | }, 83 | "packageManager": "yarn@3.2.0" 84 | } 85 | -------------------------------------------------------------------------------- /packages/mock-data/README.md: -------------------------------------------------------------------------------- 1 | # @availity/mock-data 2 | 3 | > Mock data for Availity API rest services 4 | 5 | ## Installation 6 | 7 | ```bash 8 | ❯ npm install @availity/mock-data --save-dev 9 | ``` 10 | 11 | 12 | ## Persona 13 | 14 | A fake user by the name of Jane Smith is used through the mocks with the following attributes: 15 | 16 | - **email**: jane.smith@example.com 17 | - **id**: aka12345789 18 | 19 | ## Routes 20 | 21 | - `/sdk/platform/v1/users/me` 22 | - `/sdk/platform/v1/users/aka123456789` (Jane Smith) 23 | - `/sdk/platform/v1/users/aka987654321` (Abraham Lincoln) 24 | - `/sdk/platform/v1/organizations` 25 | - `/sdk/platform/v1/permissions` 26 | - `/sdk/platform/v1/regions` 27 | - `/sdk/platform/v1/spaces` 28 | - `/v1/internal/providers` 29 | - `/v1/log-messages` 30 | 31 | 32 | 33 | ## Disclaimer 34 | 35 | Open source software components distributed or made available in the Availity Materials are licensed to Company under the terms of the applicable open source license agreements, which may be found in text files included in the Availity Materials. 36 | 37 | ## License 38 | [MIT](../../LICENSE) 39 | -------------------------------------------------------------------------------- /packages/mock-data/data/admin.json: -------------------------------------------------------------------------------- 1 | { 2 | "links": { 3 | "regions": { 4 | "href": "${context}/sdk/platform/v1/regions?userId=aka987654321" 5 | }, 6 | "organizations": { 7 | "href": "${context}/sdk/platform/v1/organizations?userId=aka987654321" 8 | }, 9 | "self": { 10 | "href": "${context}/sdk/platform/v1/users/aka987654321" 11 | } 12 | }, 13 | "id": "aka987654321", 14 | "lastName": "Lincoln", 15 | "firstName": "Abraham", 16 | "email": "abraham.lincoln@availity.com" 17 | } 18 | -------------------------------------------------------------------------------- /packages/mock-data/data/current-region.json: -------------------------------------------------------------------------------- 1 | { 2 | "totalCount": 1, 3 | "count": 1, 4 | "offset": 0, 5 | "limit": 50, 6 | "regionAggregations": [ 7 | { 8 | "type": "hits", 9 | "hits": [ 10 | { 11 | "key": "userRegionCount", 12 | "count": 56 13 | } 14 | ] 15 | } 16 | ], 17 | "links": { 18 | "self": { 19 | "href": "${context}/sdk/platform/v1/regions?currentlySelected=true" 20 | }, 21 | "user": { 22 | "href": "${context}/sdk/platform/v1/users/aka123456789" 23 | } 24 | }, 25 | "regions": [ 26 | { 27 | "links": { 28 | "self": { 29 | "href": "${context}/sdk/platform/v1/regions/FL" 30 | } 31 | }, 32 | "id": "FL", 33 | "value": "Florida", 34 | "currentlySelected": true 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /packages/mock-data/data/empty.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Availity/availity-workflow/5be3c71ebf853865359b3a4458ee059be80edcd1/packages/mock-data/data/empty.json -------------------------------------------------------------------------------- /packages/mock-data/data/legacy-permissions.json: -------------------------------------------------------------------------------- 1 | { 2 | "totalCount": 9, 3 | "count": 9, 4 | "offset": 0, 5 | "limit": 50, 6 | "axiUserPermissions": [ 7 | { 8 | "id": "6292", 9 | "description": "Claim Research Tool", 10 | "organizations": [ 11 | { 12 | "id": "1111", 13 | "name": "Acme Medical Center", 14 | "customerId": "2222" 15 | } 16 | ], 17 | "geographies": ["OK", "IL", "NM", "TX"] 18 | }, 19 | { 20 | "id": "7078", 21 | "description": "Reporting and Analytics", 22 | "organizations": [ 23 | { 24 | "id": "1111", 25 | "name": "Acme Medical Center", 26 | "customerId": "2222" 27 | } 28 | ] 29 | }, 30 | { 31 | "id": "7050", 32 | "description": "Care Reminders", 33 | "organizations": [ 34 | { 35 | "id": "1111", 36 | "name": "Acme Medical Center", 37 | "customerId": "2222" 38 | } 39 | ] 40 | }, 41 | { 42 | "id": "1250", 43 | "description": "Authorization Request", 44 | "organizations": [ 45 | { 46 | "id": "1111", 47 | "name": "Acme Medical Center", 48 | "customerId": "2222" 49 | } 50 | ] 51 | }, 52 | { 53 | "id": "2139", 54 | "description": "Fee Schedules", 55 | "organizations": [ 56 | { 57 | "id": "1111", 58 | "name": "Acme Medical Center", 59 | "customerId": "2222" 60 | } 61 | ] 62 | }, 63 | { 64 | "id": "7105", 65 | "description": "Referral Request", 66 | "organizations": [ 67 | { 68 | "id": "1111", 69 | "name": "Acme Medical Center", 70 | "customerId": "2222" 71 | } 72 | ] 73 | }, 74 | { 75 | "id": "484", 76 | "description": "Claim Status Inquiry", 77 | "organizations": [ 78 | { 79 | "id": "1111", 80 | "name": "Acme Medical Center", 81 | "customerId": "2222" 82 | } 83 | ] 84 | }, 85 | { 86 | "id": "472", 87 | "description": "Authorization and Referral Inquiry", 88 | "organizations": [ 89 | { 90 | "id": "1111", 91 | "name": "Acme Medical Center", 92 | "customerId": "2222" 93 | } 94 | ] 95 | }, 96 | { 97 | "id": "7166", 98 | "description": "Eligibility and Benefits Inquiry", 99 | "organizations": [ 100 | { 101 | "id": "1111", 102 | "name": "Acme Medical Center", 103 | "customerId": "2222" 104 | } 105 | ] 106 | } 107 | ] 108 | } 109 | -------------------------------------------------------------------------------- /packages/mock-data/data/me.json: -------------------------------------------------------------------------------- 1 | { 2 | "links": { 3 | "regions": { 4 | "href": "${context}/sdk/platform/v1/regions?userId=aka123456789" 5 | }, 6 | "organizations": { 7 | "href": "${context}/sdk/platform/v1/organizations?userId=aka123456789" 8 | }, 9 | "self": { 10 | "href": "${context}/sdk/platform/v1/users/aka123456789" 11 | } 12 | }, 13 | "id": "aka123456789", 14 | "userId": "janesmith", 15 | "akaname": "aka123456789", 16 | "lastName": "Smith", 17 | "firstName": "Jane", 18 | "legalFirstName": "Jane", 19 | "email": "jane.smith@example.com", 20 | "phone": "9045555555 x555", 21 | "phoneTypeCd": 2, 22 | "jobTitle": "JT_CM", 23 | "userValidated": true, 24 | "userHasSecurityException": false, 25 | "currentRegion": "MN", 26 | "createDate": "2017-01-01T12:00:00.000+0000" 27 | } 28 | -------------------------------------------------------------------------------- /packages/mock-data/data/providers.json: -------------------------------------------------------------------------------- 1 | { 2 | "totalCount": 2, 3 | "count": 2, 4 | "offset": 0, 5 | "limit": 50, 6 | "links": { 7 | "next": { 8 | "href": "${context}/internal/v1/providers?customerId=2222&offset=50&limit=50" 9 | }, 10 | "last": { 11 | "href": "${context}/internal/v1/providers?customerId=2222&offset=1050&limit=50" 12 | }, 13 | "self": { 14 | "href": "${context}/internal/v1/providers?customerId=2222" 15 | } 16 | }, 17 | "providers": [ 18 | { 19 | "id": "999999-9999-999999", 20 | "businessName": "Acme Provider LLC", 21 | "uiDisplayName": "Acme Provider LLC", 22 | "atypical": false, 23 | "npi": "1234567893", 24 | "customerIds": ["2222"], 25 | "roles": [ 26 | { 27 | "code": "BILL", 28 | "value": "Billing" 29 | }, 30 | { 31 | "code": "OPRG", 32 | "value": "Operating Physician" 33 | }, 34 | { 35 | "code": "PAY", 36 | "value": "Pay to Provider" 37 | }, 38 | { 39 | "code": "RFAC", 40 | "value": "Referred to Facility" 41 | }, 42 | { 43 | "code": "RFRD", 44 | "value": "Referred to Provider" 45 | }, 46 | { 47 | "code": "RFRG", 48 | "value": "Referring Provider" 49 | }, 50 | { 51 | "code": "RNDG", 52 | "value": "Rendering Provider" 53 | }, 54 | { 55 | "code": "RQST", 56 | "value": "Requesting Provider" 57 | }, 58 | { 59 | "code": "SFAC", 60 | "value": "Service Facility" 61 | } 62 | ], 63 | "primaryPhone": { 64 | "internationalCellularCode": "+1", 65 | "areaCode": "555", 66 | "phoneNumber": "555-5555" 67 | }, 68 | "primaryAddress": { 69 | "line1": "10752 Deerwood Pk. Blvd. S.", 70 | "city": "Jacksonville", 71 | "state": "Florida", 72 | "stateCode": "FL", 73 | "zip": { 74 | "code": "32256", 75 | "addon": "3342" 76 | } 77 | }, 78 | "payerAssignedIdentifiers": [ 79 | { 80 | "payerId": "ACMEMEDICAID", 81 | "identifier": "12345" 82 | } 83 | ] 84 | }, 85 | { 86 | "id": "272761-5555-251185", 87 | "lastName": "Farrell", 88 | "firstName": "Joe", 89 | "middleName": "L", 90 | "uiDisplayName": "Joe L. Farrell", 91 | "atypical": false, 92 | "npi": "1770889999", 93 | "customerIds": ["2222"], 94 | "roles": [ 95 | { 96 | "code": "BILL", 97 | "value": "Billing" 98 | }, 99 | { 100 | "code": "OPRG", 101 | "value": "Operating Physician" 102 | }, 103 | { 104 | "code": "PAY", 105 | "value": "Pay to Provider" 106 | }, 107 | { 108 | "code": "RFAC", 109 | "value": "Referred to Facility" 110 | }, 111 | { 112 | "code": "RFRD", 113 | "value": "Referred to Provider" 114 | }, 115 | { 116 | "code": "RFRG", 117 | "value": "Referring Provider" 118 | }, 119 | { 120 | "code": "RNDG", 121 | "value": "Rendering Provider" 122 | }, 123 | { 124 | "code": "RQST", 125 | "value": "Requesting Provider" 126 | }, 127 | { 128 | "code": "SFAC", 129 | "value": "Service Facility" 130 | } 131 | ], 132 | "primaryPhone": { 133 | "internationalCellularCode": "+1", 134 | "areaCode": "555", 135 | "phoneNumber": "555-5555" 136 | }, 137 | "primaryAddress": { 138 | "line1": "7000 Space Moon Dr.", 139 | "city": "Jacksonville", 140 | "state": "Florida", 141 | "stateCode": "FL", 142 | "zip": { 143 | "code": "32256", 144 | "addon": "3342" 145 | } 146 | }, 147 | "payerAssignedIdentifiers": [ 148 | { 149 | "payerId": "ACMEID", 150 | "identifier": "1234K" 151 | } 152 | ] 153 | } 154 | ] 155 | } 156 | -------------------------------------------------------------------------------- /packages/mock-data/data/slotmachine.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "spaces": { 4 | "totalCount": 1, 5 | "page": 1, 6 | "perPage": 50, 7 | "spaces": [ 8 | { 9 | "id": "48C607A70B5A46A3864A34E2BDDDEA04", 10 | "name": "My Health Plan", 11 | "link": { 12 | "text": "My Health Plan", 13 | "url": "/public/apps/spaces/#/48C607A70B5A46A3864A34E2BDDDEA04" 14 | }, 15 | "type": "space" 16 | } 17 | ] 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/mock-data/data/space.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "48C607A70B5A46A3864A34E2BDDDEA04", 3 | "name": "My Health Plan", 4 | "link": { 5 | "text": "My Health Plan", 6 | "url": "/public/apps/spaces/#/48C607A70B5A46A3864A34E2BDDDEA04" 7 | }, 8 | "type": "space" 9 | } 10 | -------------------------------------------------------------------------------- /packages/mock-data/data/spaces.json: -------------------------------------------------------------------------------- 1 | { 2 | "totalCount": 1, 3 | "count": 1, 4 | "offset": 0, 5 | "limit": 50, 6 | "links": { 7 | "self": { 8 | "href": "${context}/api/sdk/platform/v1/spaces?id=48C607A70B5A46A3864A34E2BDDDEA04" 9 | } 10 | }, 11 | "spaces": [ 12 | { 13 | "id": "48C607A70B5A46A3864A34E2BDDDEA04", 14 | "name": "My Health Plan", 15 | "link": { 16 | "text": "My Health Plan", 17 | "url": "/public/apps/spaces/#/48C607A70B5A46A3864A34E2BDDDEA04" 18 | }, 19 | "type": "space" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /packages/mock-data/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | data: path.join(__dirname, './data'), 5 | routes: path.join(__dirname, './routes.json') 6 | }; 7 | -------------------------------------------------------------------------------- /packages/mock-data/mock-data.spec.js: -------------------------------------------------------------------------------- 1 | const mocks = require('.'); 2 | 3 | describe('mock data', () => { 4 | it('should be defined', () => { 5 | expect(mocks.data).toBeDefined(); 6 | expect(mocks.routes).toBeDefined(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /packages/mock-data/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@availity/mock-data", 3 | "version": "7.1.0", 4 | "description": "Mock data for Availity API rest services", 5 | "main": "index.js", 6 | "scripts": { 7 | "generateChangelog": "yarn changelog:generate --tag-prefix $npm_package_name --lerna-package $npm_package_name", 8 | "generateTag": "yarn exec git tag --message \"${npm_package_name}@${npm_package_version}\" --annotate \"${npm_package_name}@${npm_package_version}\"", 9 | "publish": "yarn npm publish --tolerate-republish --access public", 10 | "publish:canary": "yarn npm publish --access public --tag canary" 11 | }, 12 | "keywords": [ 13 | "mock", 14 | "rest", 15 | "availity", 16 | "api" 17 | ], 18 | "engines": { 19 | "node": "^18.0.0 || ^20.0.0 || ^22.0.0" 20 | }, 21 | "author": "Robert McGuinness ", 22 | "license": "MIT", 23 | "publishConfig": { 24 | "access": "public" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/mock-data/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mock-data", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "projectType": "library", 5 | "targets": { 6 | "version": { 7 | "executor": "@jscutlery/semver:version", 8 | "options": { 9 | "preset": "angular", 10 | "commitMessageFormat": "chore({projectName}): release version ${version}", 11 | "tagPrefix": "@availity/{projectName}@", 12 | "trackDeps": true 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/mock-data/routes.json: -------------------------------------------------------------------------------- 1 | { 2 | "v1/log-messages": { 3 | "file": "empty.json" 4 | }, 5 | "internal/v1/providers": { 6 | "get": [ 7 | { 8 | "file": "providers.json", 9 | "params": { 10 | "customerId": "2222" 11 | } 12 | } 13 | ] 14 | }, 15 | "internal/v1/axi-user-permissions": { 16 | "file": "legacy-permissions.json" 17 | }, 18 | "sdk/platform/v1/users/me": { 19 | "file": "me.json" 20 | }, 21 | "sdk/platform/v1/users/aka123456789": { 22 | "file": "me.json" 23 | }, 24 | "sdk/platform/v1/users/aka987654321": { 25 | "file": "admin.json" 26 | }, 27 | "sdk/platform/v1/permissions": { 28 | "get": [ 29 | { 30 | "file": "permissions.json", 31 | "params": { 32 | "userId": "aka123456789" 33 | } 34 | } 35 | ] 36 | }, 37 | "sdk/platform/v1/organizations/1111": { 38 | "file": "organization.json" 39 | }, 40 | "sdk/platform/v1/organizations": { 41 | "file": "organizations.json", 42 | "get": [ 43 | { 44 | "file": "organizations.json", 45 | "params": { 46 | "userId": "aka123456789" 47 | } 48 | } 49 | ] 50 | }, 51 | "sdk/platform/v1/regions": { 52 | "get": [ 53 | { 54 | "file": "regions.json", 55 | "params": { 56 | "userId": "aka123456789" 57 | } 58 | }, 59 | { 60 | "file": "current-region.json", 61 | "params": { 62 | "currentlySelected": "true" 63 | } 64 | } 65 | ] 66 | }, 67 | "sdk/platform/v1/spaces/48C607A70B5A46A3864A34E2BDDDEA04": { 68 | "file": "space.json" 69 | }, 70 | "sdk/platform/v1/spaces": { 71 | "get": [ 72 | { 73 | "file": "spaces.json", 74 | "params": { 75 | "id": "48C607A70B5A46A3864A34E2BDDDEA04" 76 | } 77 | } 78 | ] 79 | }, 80 | "/ms/api/availity/internal/core/vault/upload/v1/resumable/nYrlabBv": { 81 | "post": { 82 | "status": 201, 83 | "responseHeaders": { 84 | "tus-resumable": "1.0.0", 85 | "upload-expires": "Fri, 12 Jan 2030 15:54:39 GMT", 86 | "transfer-encoding": "chunked", 87 | "location": "4611142db7c049bbbe37376583a3f46b" 88 | } 89 | } 90 | }, 91 | "ms/api/availity/internal/platform/slotmachine/graphql": { 92 | "file": "slotmachine.json" 93 | }, 94 | "/ms/api/availity/internal/core/vault/upload/v1/resumable/nYrlabBv/4611142db7c049bbbe37376583a3f46b": { 95 | "patch": { 96 | "status": 204, 97 | "responseHeaders": { 98 | "tus-resumable": "1.0.0", 99 | "upload-expires": "Fri, 12 Jan 2030 15:54:39 GMT", 100 | "transfer-encoding": "chunked", 101 | "AV-Scan-Result": "accepted", 102 | "references": "[\"/files/105265/9ee77f6d-9779-4b96-a995-0df47657e504\"]" 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /packages/mock-server/.eslintrc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | extends: availity 3 | rules: 4 | import/no-dynamic-require: 0 5 | global-require: 0 6 | unicorn/catch-error-name: off 7 | -------------------------------------------------------------------------------- /packages/mock-server/config/index.js: -------------------------------------------------------------------------------- 1 | const merge = require('lodash/merge'); 2 | const chalk = require('chalk'); 3 | 4 | const logger = require('../logger').getInstance(); 5 | 6 | class Configuration { 7 | constructor() { 8 | this.server = null; 9 | this.app = null; 10 | this.router = null; 11 | this.routes = []; 12 | this.cache = []; 13 | this.path = null; 14 | this.addressInUse = null; 15 | } 16 | 17 | /** 18 | * Set the path of the configuration object 19 | * 20 | * @param {Sring} path full path to configuration. Ex: path.join(__dirname, 'config.js') 21 | 22 | */ 23 | 24 | defaultConfig(path) { 25 | return this.path ? require(path) : this; 26 | } 27 | 28 | /** 29 | * Sets the configuration object from the configuration file and command line overrides. 30 | * 31 | * @param {Object} options configuration object with production|development|testing settings. 32 | */ 33 | set(_options) { 34 | const options = _options || {}; 35 | 36 | logger.setProvider(options.logProvider); 37 | 38 | // Get the config object by path or from root 39 | if (this.path) { 40 | logger.info(`Using ${chalk.blue(this.path)}`); 41 | } 42 | let config = this.path ? require(this.path) : this.defaultConfig(); 43 | 44 | // Allow programmatic overrides for environment 45 | config = merge(config, options); 46 | 47 | // Save to `this.options` 48 | this.options = config; 49 | } 50 | } 51 | 52 | module.exports = new Configuration(); 53 | -------------------------------------------------------------------------------- /packages/mock-server/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const http = require('http'); 3 | const chalk = require('chalk'); 4 | const logger = require('./logger'); 5 | const config = require('./config'); 6 | const middleware = require('./middleware'); 7 | 8 | class Ekko { 9 | constructor(ekkoConfig) { 10 | if (ekkoConfig) { 11 | const isString = typeof ekkoConfig === 'string'; 12 | 13 | if (isString) { 14 | this.configPath = ekkoConfig; 15 | } else { 16 | this.middleware(ekkoConfig); 17 | } 18 | } 19 | } 20 | 21 | middleware(options) { 22 | config.path = this.configPath; 23 | config.set(options); 24 | 25 | config.app = express(); 26 | config.router = new express.Router(); 27 | 28 | middleware.headers(); 29 | middleware.config(); 30 | 31 | return config.router; 32 | } 33 | 34 | async start(options) { 35 | this.middleware(options); 36 | 37 | const port = config.options.port || 0; 38 | const host = config.options.host || 'localhost'; 39 | config.app.set('port', port); 40 | config.server = http.createServer(config.app); 41 | 42 | return new Promise((resolve, reject) => { 43 | config.server.listen(config.options.port, host, () => { 44 | const url = `http://${host}:${config.server.address().port}`; 45 | logger.getInstance().info(`Ekko server started at ${chalk.green(url)}`); 46 | resolve(true); 47 | }); 48 | 49 | config.server.on('error', (e) => { 50 | if (e.errno === 'EADDRINUSE') { 51 | logger 52 | .getInstance() 53 | .error(`Cannot start Ekko server on PORT ${config.options.port}. Check if port is already in use.`); 54 | } else { 55 | logger.getInstance().error(`Failed to start Ekko server on PORT ${config.options.port}`); 56 | } 57 | 58 | reject(new Error(e)); 59 | }); 60 | }); 61 | } 62 | 63 | async stop() { 64 | return new Promise((resolve) => { 65 | if (config.server && config.server.close) { 66 | config.server.close(() => { 67 | resolve(true); 68 | }); 69 | } else { 70 | resolve(true); 71 | } 72 | }); 73 | } 74 | 75 | config() { 76 | return config; 77 | } 78 | } 79 | 80 | module.exports = Ekko; 81 | -------------------------------------------------------------------------------- /packages/mock-server/logger/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-classes-per-file */ 2 | /* eslint-disable no-console */ 3 | const chalk = require('chalk'); 4 | 5 | let loggerInstance; 6 | 7 | class DefaultLogger { 8 | constructor(options) { 9 | this.options = options; 10 | } 11 | 12 | warn(entry) { 13 | this.record(entry, 'yellow'); 14 | } 15 | 16 | error(entry) { 17 | this.record(entry, 'red'); 18 | } 19 | 20 | info(entry) { 21 | this.record(entry); 22 | } 23 | 24 | debug(entry) { 25 | this.record(entry); 26 | } 27 | 28 | log(entry) { 29 | this.record(entry); 30 | } 31 | 32 | // › Starting server 33 | record(entry, color) { 34 | const defaultColor = entry instanceof Error ? 'red' : 'gray'; 35 | const crayoloa = color || defaultColor; 36 | 37 | console.log(`${chalk[crayoloa](entry)}`); 38 | } 39 | } 40 | 41 | class Logger { 42 | constructor() { 43 | this.provider = null; 44 | this.setProvider(() => new DefaultLogger()); 45 | } 46 | 47 | canLog() { 48 | return process.env.NODE_ENV !== 'testing'; 49 | } 50 | 51 | setProvider(fn) { 52 | if (fn) { 53 | this.provider = fn(); 54 | } 55 | } 56 | 57 | warn(entry) { 58 | if (this.canLog()) { 59 | this.provider.warn(entry); 60 | } 61 | } 62 | 63 | error(entry) { 64 | if (this.canLog()) { 65 | this.provider.error(entry); 66 | } 67 | } 68 | 69 | info(entry) { 70 | if (this.canLog()) { 71 | this.provider.info(entry); 72 | } 73 | } 74 | 75 | debug(entry) { 76 | if (this.canLog()) { 77 | this.provider.debug(entry); 78 | } 79 | } 80 | 81 | log(entry) { 82 | if (this.canLog()) { 83 | this.provider.log(entry); 84 | } 85 | } 86 | } 87 | 88 | module.exports = { 89 | // singleton 90 | getInstance() { 91 | if (!loggerInstance) { 92 | loggerInstance = new Logger(); 93 | } 94 | 95 | return loggerInstance; 96 | } 97 | }; 98 | -------------------------------------------------------------------------------- /packages/mock-server/middleware/config.js: -------------------------------------------------------------------------------- 1 | const errorhandler = require('errorhandler'); 2 | const compression = require('compression'); 3 | const methodOverride = require('method-override'); 4 | const cors = require('cors'); 5 | const path = require('path'); 6 | const bodyParser = require('body-parser'); 7 | const busboy = require('connect-busboy'); 8 | const get = require('lodash/get'); 9 | const onFinished = require('on-finished'); 10 | const chalk = require('chalk'); 11 | 12 | const config = require('../config'); 13 | const routes = require('../routes'); 14 | const logger = require('../logger').getInstance(); 15 | 16 | const notFoundHandler = require('./not.found'); 17 | 18 | // Custom request logger 19 | module.exports = function development() { 20 | if (logger.canLog()) { 21 | config.router.use((req, res, next) => { 22 | function logRequest() { 23 | const method = `${chalk.white(req.method)}`; 24 | const url = `${chalk.dim(req.originalUrl || req.url)}`; 25 | const code = res._header ? String(res.statusCode) : ''; 26 | const file = chalk.dim(res.avFile || ''); 27 | 28 | logger.log(`${method} ${url} ${chalk.white(code)} ${chalk.blue(path.basename(file))}`); 29 | } 30 | 31 | // Callback is called at the end of request cycle after headers are set 32 | onFinished(res, logRequest); 33 | 34 | next(); 35 | }); 36 | } 37 | 38 | config.router.use(errorhandler()); 39 | config.router.use(compression()); 40 | config.router.use(cors()); 41 | 42 | // pretty print json 43 | config.app.set('json spaces', 2); 44 | 45 | config.router.use(methodOverride('X-HTTP-Method-Override')); 46 | 47 | config.router.use( 48 | bodyParser.json({ 49 | limit: get(config, 'options.limit', '50mb') 50 | }) 51 | ); // parse application/json 52 | 53 | config.router.use( 54 | bodyParser.urlencoded({ 55 | extended: true, 56 | limit: config.options.limit 57 | }) 58 | ); // // parse application/x-www-form-urlencoded 59 | 60 | config.router.use(busboy({ immediate: false })); 61 | 62 | config.app.use('/', config.router); 63 | routes.init(); 64 | 65 | config.app.use(notFoundHandler()); 66 | }; 67 | -------------------------------------------------------------------------------- /packages/mock-server/middleware/headers.js: -------------------------------------------------------------------------------- 1 | const config = require('../config'); 2 | 3 | module.exports = function headers() { 4 | config.app.disable('x-powered-by'); 5 | config.app.use((req, res, next) => { 6 | res.setHeader('X-Powered-By', 'Availity Mock Server'); 7 | next(); 8 | }); 9 | }; 10 | -------------------------------------------------------------------------------- /packages/mock-server/middleware/index.js: -------------------------------------------------------------------------------- 1 | const headers = require('./headers'); 2 | const config = require('./config'); 3 | 4 | module.exports = { 5 | headers, 6 | config 7 | }; 8 | -------------------------------------------------------------------------------- /packages/mock-server/middleware/not.found.js: -------------------------------------------------------------------------------- 1 | module.exports = function notfound() { 2 | return (req, res) => { 3 | res.status(404); 4 | 5 | const message = 'Not Found'; 6 | 7 | if (req.accepts('html')) { 8 | res 9 | .type('html') 10 | .status(404) 11 | .end( 12 | ' Dummy Mock Server Response

Not Found

' 13 | ); 14 | return; 15 | } 16 | 17 | // respond with json 18 | if (req.accepts('json')) { 19 | res.type('json').send({ error: message }); 20 | return; 21 | } 22 | 23 | // respond with json 24 | if (req.accepts('xml')) { 25 | res.type('xml').send(`${message}`); 26 | return; 27 | } 28 | 29 | // default to plain-text. send() 30 | res.type('txt').send({ error: message }); 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /packages/mock-server/middleware/tests/headers.spec.js: -------------------------------------------------------------------------------- 1 | const request = require('superagent'); 2 | const isEqual = require('lodash/isEqual'); 3 | const helper = require('../../tests/helpers'); 4 | 5 | describe('Headers', () => { 6 | helper.serverSpecHelper(); 7 | 8 | it('should respond with custom headers', async () => { 9 | const res = await request.get(helper.getUrl('/v1/route7')); 10 | expect(res.status).toBe(200); 11 | expect(isEqual(res.body, { b: 2 })).toBeTruthy(); 12 | expect(res.header.ping).toBe('pong'); 13 | }); 14 | 15 | it('route 2 should respond with dummy-response2.json for POST with X-HTTP-Method-Override:GET', async () => { 16 | const res = await request 17 | .post(helper.getUrl('/internal/v2/route2')) 18 | .set('X-HTTP-Method-Override', 'GET') 19 | .send({ bar: 'baz' }); 20 | 21 | expect(res.status).toBe(200); 22 | expect(isEqual(res.body, { b: 2 })).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/mock-server/middleware/tests/mulit-part.spec.js: -------------------------------------------------------------------------------- 1 | const request = require('superagent'); 2 | const isEqual = require('lodash/isEqual'); 3 | const helper = require('../../tests/helpers'); 4 | 5 | describe('Multi-part', () => { 6 | helper.serverSpecHelper(); 7 | 8 | it('should respond with dummy-response-1.json for empty form fields and one file attachment', async () => { 9 | const res = await request 10 | .post(helper.getUrl('/v1/route5')) 11 | .type('multipart/form-data') 12 | .attach('attatchment', helper.getFile('dummy-response-1.json')); 13 | 14 | expect(res.status).toBe(200); 15 | expect(isEqual(res.body, { a: 1 })).toBeTruthy(); 16 | }); 17 | 18 | it('should respond with dummy-response-2.json for empty form fields and one file attachment', async () => { 19 | const res = await request 20 | .post(helper.getUrl('/v1/route5')) 21 | .type('multipart/form-data') 22 | .attach('attachment', helper.getFile('dummy-response-2.json')); 23 | 24 | expect(res.status).toBe(200); 25 | expect(isEqual(res.body, { b: 2 })).toBeTruthy(); 26 | }); 27 | 28 | it('should respond with dummy-response-2.json for 1 matching form field and one file attachment', async () => { 29 | const res = await request 30 | .post(helper.getUrl('/v1/route5')) 31 | .type('multipart/form-data') 32 | .attach('attachment', helper.getFile('dummy-response-2.json')) 33 | .field('a', '1'); 34 | 35 | expect(res.status).toBe(200); 36 | expect(isEqual(res.body, { b: 2 })).toBeTruthy(); 37 | }); 38 | 39 | it('should respond with dummy-response-3.json for 2 matching form fields and one file attachment', async () => { 40 | const res = await request 41 | .post(helper.getUrl('/v1/route5')) 42 | .type('multipart/form-data') 43 | .attach('attachment', helper.getFile('dummy-response-3.json')) 44 | .field('a', '1') 45 | .field('b', '2'); 46 | 47 | expect(res.status).toBe(200); 48 | expect(isEqual(res.body, { c: 3 })).toBeTruthy(); 49 | }); 50 | 51 | it('should respond with dummy-response-4.json for field name that matches file input name', async () => { 52 | const res = await request 53 | .post(helper.getUrl('/v1/route5')) 54 | .type('multipart/form-data') 55 | .attach('attachment', helper.getFile('dummy-response-4.json')) 56 | .field('a', '1') 57 | .field('b', '2'); 58 | 59 | expect(res.status).toBe(200); 60 | expect(isEqual(res.body, { d: 4 })).toBeTruthy(); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /packages/mock-server/models/index.js: -------------------------------------------------------------------------------- 1 | const Request = require('./request'); 2 | const Response = require('./response'); 3 | const Route = require('./route'); 4 | 5 | module.exports = { 6 | Request, 7 | Response, 8 | Route 9 | }; 10 | -------------------------------------------------------------------------------- /packages/mock-server/models/request.js: -------------------------------------------------------------------------------- 1 | const uniqueId = require('lodash/uniqueId'); 2 | 3 | class Request { 4 | constructor() { 5 | this.params = null; 6 | this.headers = null; 7 | this.responses = []; 8 | this.id = uniqueId('request'); 9 | } 10 | 11 | addResponse(response) { 12 | this.responses.push(response); 13 | } 14 | } 15 | 16 | module.exports = Request; 17 | -------------------------------------------------------------------------------- /packages/mock-server/models/response.js: -------------------------------------------------------------------------------- 1 | const uniqueId = require('lodash/uniqueId'); 2 | 3 | class Response { 4 | constructor(response) { 5 | this.headers = null; 6 | this.file = null; 7 | this.url = null; 8 | this.latency = null; 9 | this.status = null; 10 | this.repeat = 1; 11 | 12 | if (response) { 13 | this.set(response); 14 | } 15 | 16 | this.id = uniqueId('response'); 17 | } 18 | 19 | set(response) { 20 | this.headers = response.headers || this.headers; 21 | this.file = response.file || this.file; 22 | this.url = response.url || this.url; 23 | this.responseHeaders = response.responseHeaders || this.responseHeaders; 24 | this.latency = response.latency || this.latency; 25 | this.status = response.status || this.status; 26 | this.repeat = response.repeat || this.repeat; 27 | } 28 | } 29 | 30 | module.exports = Response; 31 | -------------------------------------------------------------------------------- /packages/mock-server/models/route.js: -------------------------------------------------------------------------------- 1 | const each = require('lodash/each'); 2 | const isArray = require('lodash/isArray'); 3 | const isPlainObject = require('lodash/isPlainObject'); 4 | const isString = require('lodash/isString'); 5 | const uniqueId = require('lodash/uniqueId'); 6 | 7 | const Request = require('./request'); 8 | const Response = require('./response'); 9 | 10 | class Route { 11 | constructor(url, endpoint, dataPath) { 12 | this.url = url; 13 | this.methods = {}; 14 | this.id = uniqueId('route'); 15 | this.dataPath = dataPath; 16 | 17 | this.init(endpoint); 18 | } 19 | 20 | init(endpoint) { 21 | // Allow for mime type extension in the url like /v1/users/me.json 22 | this.url = `${this.url}.:format?`; 23 | this.url = this.url.charAt(0) !== '/' ? `/${this.url}` : this.url; 24 | 25 | // Attach default file response for each http method 26 | each(['get', 'post', 'put', 'delete', 'head', 'patch'], (method) => { 27 | this.addMethod(method, endpoint); 28 | }); 29 | } 30 | 31 | addMethod(method, endpoint) { 32 | if (!this.methods[method]) { 33 | // creates property get|post|put|delete... on route object 34 | this.methods[method] = []; 35 | } 36 | 37 | // Handle simple config that defines a file response for all methods 38 | // 39 | // EX: 40 | // 41 | // "v2/route2": { 42 | // "latency": 250, 43 | // "file": "example2.json" 44 | // } 45 | let response = new Response(); 46 | response.set(endpoint); 47 | 48 | // Handle the syntactic sugar case where you can just define a method 49 | // and a file response in one line. 50 | // 51 | // EX: 52 | // 53 | // "v1/route4": { 54 | // "get": "example1.json", 55 | // "put": "example2.json", 56 | // "post": "example3.json", 57 | // "delete": "example4.json" 58 | // } 59 | if (isString(endpoint[method])) { 60 | response.file = endpoint[method]; 61 | } 62 | 63 | // "v1/route4": { 64 | // "get": {"file": "example1.json"} 65 | // "put": {"file": "example2.json", "status": 202} 66 | // "post": "example3.json", 67 | // "delete": "example4.json" 68 | // } 69 | if (isPlainObject(endpoint[method])) { 70 | const config = endpoint[method]; 71 | endpoint[method] = [config]; 72 | } 73 | 74 | let request = new Request(); 75 | request.addResponse(response); 76 | this.methods[method].push(request); 77 | 78 | // "get": [ 79 | // { 80 | // "file": "example2.json", 81 | // ... 82 | // }... 83 | if (isArray(endpoint[method])) { 84 | each(endpoint[method], (_request) => { 85 | // Handle default response. 86 | // 87 | // EX: 88 | // 89 | // "post": [ 90 | // { 91 | // "file": "example2.json" 92 | // } 93 | // ] 94 | if (!_request.params && !_request.headers && !isArray(_request.response)) { 95 | // If necessary, override the default response from the routes global response. 96 | if (this.methods[method][0]) { 97 | this.methods[method][0].responses[0].set(_request); 98 | // this.methods[method][0].responses[0].url = _request.file; 99 | } 100 | 101 | return; 102 | } 103 | 104 | // { 105 | // "file": "example3.json", 106 | // "status": 202, 107 | // "params": { 108 | // "a": 1, 109 | // "b": "sample.pdf" 110 | // } 111 | // } 112 | request = new Request(); 113 | request.params = _request.params; 114 | request.headers = _request.headers; 115 | // if a request configuration has both a file and response attribute defined, ignore the file 116 | // attribute and continue...this is most likely an async configuration 117 | if ((_request.file || _request.url || _request.status) && !isArray(_request.response)) { 118 | response = new Response(_request); 119 | request.responses.push(response); 120 | } 121 | 122 | // "response": [ 123 | // { 124 | // "status": 202, 125 | // "file": "example1.json" 126 | // }, 127 | // { 128 | // "status": 201, 129 | // "file": "example2.json" 130 | // } 131 | // ]... 132 | if (isArray(_request.response)) { 133 | each(_request.response, (_response) => { 134 | const res = new Response(_request); 135 | res.set(_response); 136 | request.responses.push(res); 137 | }); 138 | } 139 | 140 | this.methods[method].push(request); 141 | }); 142 | } 143 | } 144 | } 145 | 146 | module.exports = Route; 147 | -------------------------------------------------------------------------------- /packages/mock-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@availity/mock-server", 3 | "version": "9.1.0", 4 | "description": "Mock server simulating Availity API rest services", 5 | "main": "index.js", 6 | "scripts": { 7 | "generateChangelog": "yarn changelog:generate --tag-prefix $npm_package_name --lerna-package $npm_package_name", 8 | "generateTag": "yarn exec git tag --message \"${npm_package_name}@${npm_package_version}\" --annotate \"${npm_package_name}@${npm_package_version}\"", 9 | "publish": "yarn npm publish --tolerate-republish --access public", 10 | "publish:canary": "yarn npm publish --access public --tag canary" 11 | }, 12 | "keywords": [ 13 | "mock", 14 | "server", 15 | "availity" 16 | ], 17 | "engines": { 18 | "node": "^18.0.0 || ^20.0.0 || ^22.0.0" 19 | }, 20 | "author": "Robert McGuinness ", 21 | "license": "MIT", 22 | "dependencies": { 23 | "@availity/mock-data": "workspace:*", 24 | "body-parser": "^1.20.3", 25 | "chalk": "^4.1.2", 26 | "compression": "^1.8.0", 27 | "connect-busboy": "1.0.0", 28 | "cors": "^2.8.5", 29 | "delay": "^5.0.0", 30 | "errorhandler": "^1.5.1", 31 | "express": "^4.21.2", 32 | "lodash": "^4.17.21", 33 | "method-override": "^3.0.0", 34 | "on-finished": "^2.4.1" 35 | }, 36 | "devDependencies": { 37 | "superagent": "^9.0.2" 38 | }, 39 | "publishConfig": { 40 | "access": "public" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/mock-server/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mock-server", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "projectType": "library", 5 | "targets": { 6 | "version": { 7 | "executor": "@jscutlery/semver:version", 8 | "options": { 9 | "preset": "angular", 10 | "commitMessageFormat": "chore({projectName}): release version ${version}", 11 | "tagPrefix": "@availity/{projectName}@", 12 | "trackDeps": true 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/mock-server/response/get.js: -------------------------------------------------------------------------------- 1 | const match = require('./match'); 2 | const result = require('./result'); 3 | 4 | const get = { 5 | send(req, res) { 6 | match.set(req, res); 7 | result.send(req, res); 8 | } 9 | }; 10 | 11 | module.exports = get; 12 | -------------------------------------------------------------------------------- /packages/mock-server/response/index.js: -------------------------------------------------------------------------------- 1 | const get = require('./get'); 2 | const post = require('./post'); 3 | const patch = require('./patch'); 4 | 5 | const response = { 6 | get: get.send, 7 | 8 | head: get.send, 9 | 10 | delete: get.send, 11 | 12 | post: post.send, 13 | 14 | patch: patch.send, 15 | 16 | put: post.send, 17 | 18 | send(req, res, next) { 19 | const method = req.method.toLowerCase(); 20 | 21 | if (this[method]) { 22 | this[method](req, res, next); 23 | return; 24 | } 25 | 26 | next(); 27 | } 28 | }; 29 | 30 | module.exports = response; 31 | -------------------------------------------------------------------------------- /packages/mock-server/response/match.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable unicorn/no-this-assignment */ 2 | const difference = require('lodash/difference'); 3 | const each = require('lodash/each'); 4 | const get = require('lodash/get'); 5 | const intersection = require('lodash/intersection'); 6 | const isArray = require('lodash/isArray'); 7 | const isEmpty = require('lodash/isEmpty'); 8 | const isObject = require('lodash/isObject'); 9 | const toArray = require('lodash/toArray'); 10 | 11 | const match = { 12 | scoreHeaders(score, _request, headers) { 13 | // Note: variables prefixed with "_" underscore signify config object|key|value 14 | 15 | const reqHeaders = _request.headers; 16 | 17 | each(reqHeaders, (_headerValue, _headerKey) => { 18 | const headerValue = get(headers, _headerKey); 19 | 20 | if (_headerValue === headerValue) { 21 | score.hits += 1; // values are equal 22 | } else if (!headerValue) { 23 | score.misses -= 1; 24 | } else { 25 | score.valid = false; 26 | } 27 | }); 28 | 29 | return score; 30 | }, 31 | 32 | scoreParam(score, _paramValue, paramValue) { 33 | // Note: variables prefixed with "_" underscore signify config object|key|value 34 | 35 | if (_paramValue === paramValue) { 36 | score.hits += 1; // perfect match 37 | } else if (!paramValue) { 38 | score.misses += 1; // request config is looking for a param but actual request doesn't have it 39 | } else { 40 | score.valid = false; // request config {a:1} not match value of request params {a:10} 41 | } 42 | }, 43 | 44 | scorePattern(score, _paramValue, paramValue) { 45 | // Note: variables prefixed with "_" underscore signify config object|key|value 46 | 47 | const regex = new RegExp(_paramValue.pattern, _paramValue.flags || 'i'); 48 | 49 | if (regex.test(paramValue)) { 50 | score.hits += 1; // perfect match 51 | } else if (!paramValue) { 52 | score.misses += 1; // request config is looking for a param but actual request doesn't have it 53 | } else { 54 | score.valid = false; // request config {a:1} not match value of request params {a:10} 55 | } 56 | }, 57 | 58 | scoreArray(score, _paramValue, __paramValue) { 59 | // Note: variables prefixed with "_" underscore signify config object|key|value 60 | 61 | const paramValue = toArray(__paramValue); 62 | 63 | const hits = intersection(_paramValue, paramValue); 64 | score.hits += hits.length; 65 | 66 | const misses = difference(_paramValue, paramValue); 67 | score.misses += misses.length; 68 | 69 | return score; 70 | }, 71 | 72 | scoreParams(_request, params, method) { 73 | const self = this; 74 | 75 | // Note: variables prefixed with "_" underscore signify config object|key|value 76 | 77 | const score = { 78 | hits: 0, // Matching parameters 79 | misses: 0, // Parameters specified in route, but not present in query 80 | valid: true // False if a parameter is specified in route and query, but they are not equal and therefore should never match 81 | }; 82 | 83 | const reqParams = _request.params; 84 | 85 | each(reqParams, (value, key) => { 86 | const paramValue = method === 'get' ? params[key] : get(params, key); 87 | 88 | if (isArray(value)) { 89 | self.scoreArray(score, value, paramValue); 90 | } else if (isObject(value) && value.pattern) { 91 | self.scorePattern(score, value, paramValue); 92 | } else { 93 | self.scoreParam(score, value, paramValue); 94 | } 95 | }); 96 | 97 | return score; 98 | }, 99 | 100 | /** 101 | * Sets the res.locals.request object that nearly matches to the http request object. 102 | * 103 | * @param {[type]} req http request object 104 | * @param {[type]} res http response object 105 | */ 106 | set(req, res) { 107 | // Note: variables prefixed with "_" underscore signify config object object|key|value 108 | 109 | const self = this; 110 | 111 | const { route } = res.locals; 112 | const method = req.method.toLowerCase(); 113 | const requests = route.methods[method]; 114 | 115 | const params = isEmpty(req.query) ? req.body : req.query; 116 | // const headers = req.headers; 117 | 118 | const topScore = { 119 | hits: 0, 120 | misses: 0 121 | }; 122 | 123 | // set the default request 124 | [res.locals.request] = requests; 125 | 126 | each(requests, (_request) => { 127 | const score = self.scoreParams(_request, params, method); 128 | self.scoreHeaders(score, _request, req.headers); 129 | 130 | if (!score.valid) { 131 | return; 132 | } 133 | 134 | // Top Score: 135 | // 136 | // 1. Top hits 137 | // 2. Unless top hits are equal the one with least amount of misses 138 | // 3. Unless both hits and misses are equal last configuration should win 139 | if ( 140 | score.hits > topScore.hits || 141 | (score.hits === topScore.hits && score.misses < topScore.misses) || 142 | (score.hits === topScore.hits && score.misses === topScore.misses) 143 | ) { 144 | topScore.hits = score.hits; 145 | topScore.misses = score.misses; 146 | res.locals.request = _request; 147 | } 148 | }); 149 | } 150 | }; 151 | 152 | module.exports = match; 153 | -------------------------------------------------------------------------------- /packages/mock-server/response/patch.js: -------------------------------------------------------------------------------- 1 | const match = require('./match'); 2 | const result = require('./result'); 3 | 4 | const patch = { 5 | tus(req, res) { 6 | if (req.is('application/offset+octet-stream') && req.headers['tus-resumable']) { 7 | res.set('upload-offset', req.headers['content-length']); 8 | } 9 | }, 10 | 11 | send(req, res) { 12 | try { 13 | patch.tus(req, res); 14 | match.set(req, res); 15 | result.send(req, res); 16 | } catch { 17 | res.status(500).send({ error: 'mock server error' }); 18 | } 19 | } 20 | }; 21 | 22 | module.exports = patch; 23 | -------------------------------------------------------------------------------- /packages/mock-server/response/post.js: -------------------------------------------------------------------------------- 1 | const isEmpty = require('lodash/isEmpty'); 2 | 3 | const logger = require('../logger').getInstance(); 4 | const match = require('./match'); 5 | const result = require('./result'); 6 | 7 | const post = { 8 | multipart(req) { 9 | return new Promise((resolve, reject) => { 10 | if (!req.is('multipart')) { 11 | resolve(true); 12 | return; 13 | } 14 | 15 | req.busboy.on('file', (fieldname, file, fileParams) => { 16 | file.on('error', (error) => { 17 | logger.error('Something went wrong uploading the file', error); 18 | }); 19 | file.on('end', () => { 20 | // Treat the file name as a field so we can match and score 21 | logger.info('File finished %s:', fileParams.filename); 22 | req.body[fieldname] = fileParams.filename; 23 | }); 24 | // `file` is a `ReadableStream`...always do something with it 25 | // else busboy won't fire the 'finish' even. At minimum do: 26 | file.resume(); 27 | }); 28 | 29 | req.busboy.on('field', (key, value) => { 30 | if (isEmpty(value)) { 31 | return; 32 | } 33 | 34 | logger.info(`${key}, ${value}`); 35 | 36 | req.body[key] = value; 37 | }); 38 | 39 | req.busboy.on('error', (err) => { 40 | logger.error(err); 41 | reject(err); 42 | }); 43 | 44 | req.busboy.on('close', () => { 45 | logger.info('finished request'); 46 | resolve(true); 47 | }); 48 | 49 | req.pipe(req.busboy); 50 | }); 51 | }, 52 | 53 | send(req, res) { 54 | return post.multipart(req).then( 55 | () => { 56 | match.set(req, res); 57 | result.send(req, res); 58 | return null; 59 | }, 60 | () => { 61 | res.status(500).send({ error: 'mock server error' }); 62 | } 63 | ); 64 | } 65 | }; 66 | 67 | module.exports = post; 68 | -------------------------------------------------------------------------------- /packages/mock-server/response/result.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const chalk = require('chalk'); 4 | const delay = require('delay'); 5 | 6 | const config = require('../config'); 7 | const logger = require('../logger').getInstance(); 8 | 9 | const result = { 10 | cache: {}, 11 | 12 | sendFile(req, res, status, response, filePath) { 13 | res.status(status).sendFile(filePath, (err) => { 14 | if (err) { 15 | logger.error(`NOT FOUND ${filePath} `); 16 | 17 | res.sendStatus(404); 18 | } else { 19 | const file = chalk.blue(filePath); 20 | const relativeFile = path.relative(process.cwd(), file); 21 | 22 | // Attach file path to response object for logging at the end of request cycle 23 | res.avFile = relativeFile; 24 | } 25 | }); 26 | }, 27 | 28 | parseJSON(contents) { 29 | let replacedContents; 30 | try { 31 | replacedContents = JSON.parse(contents); 32 | } catch { 33 | replacedContents = contents; 34 | } 35 | 36 | return replacedContents; 37 | }, 38 | 39 | sendJson(req, res, status, response, filePath) { 40 | try { 41 | const contents = fs.readFileSync(filePath, 'utf8'); 42 | const regex = /\${context}/g; 43 | // eslint-disable-next-line unicorn/prefer-string-replace-all 44 | const replacedContents = contents.replace(regex, config.options.pluginContext); 45 | const json = this.parseJSON(replacedContents); 46 | 47 | const relativeFile = path.relative(process.cwd(), filePath); 48 | // Attach file path to response object for logging at the end of request cycle 49 | res.avFile = relativeFile; 50 | 51 | res.status(status).json(json); 52 | } catch { 53 | logger.error(`NOT FOUND ${filePath} `); 54 | res.sendStatus(404); 55 | } 56 | }, 57 | 58 | file(req, res, response, dataPath) { 59 | // eslint-disable-next-line promise/catch-or-return 60 | delay(response.latency || 200).then(() => { 61 | const filePath = path.join(dataPath, response.file); 62 | const status = response.status || 200; 63 | 64 | const headers = response.responseHeaders || {}; 65 | for (const key of Object.keys(headers)) { 66 | res.set(key, response.responseHeaders[key]); 67 | } 68 | 69 | // eslint-disable-next-line promise/always-return 70 | if (path.extname(filePath) === '.json') { 71 | this.sendJson(req, res, status, response, filePath); 72 | } else if (path.extname(filePath) === '.js') { 73 | this.sendFunction(req, res, status, response, filePath); 74 | } else { 75 | this.sendFile(req, res, status, response, filePath); 76 | } 77 | }); 78 | }, 79 | 80 | noContent(req, res, response) { 81 | if (response.responseHeaders) { 82 | res.set(response.responseHeaders); 83 | } 84 | 85 | res.status(response.status).send(); 86 | }, 87 | 88 | url(req, res, response) { 89 | res.redirect(response.url); 90 | }, 91 | 92 | send(req, res) { 93 | const { route, request } = res.locals; 94 | 95 | const routeId = route.id; 96 | const requestId = request.id; 97 | 98 | // cache: { 99 | // 'route1_request2': [0,0] // position 0 === response index; position 1 === repeat index 100 | // } 101 | const cacheKey = `${routeId}_${requestId}`; 102 | 103 | let indexes = result.cache[cacheKey]; 104 | 105 | if (!indexes) { 106 | // empty cache so hydrate 107 | indexes = [0, 0]; 108 | result.cache[cacheKey] = [0, 0]; 109 | } 110 | 111 | let responseIndex = indexes[0]; 112 | let repeatIndex = indexes[1]; 113 | 114 | let response = request.responses[responseIndex]; 115 | 116 | if (repeatIndex >= response.repeat) { 117 | responseIndex = (responseIndex + 1) % request.responses.length; 118 | repeatIndex = 0; 119 | } 120 | 121 | repeatIndex += 1; 122 | 123 | // cache the latest index for the next request 124 | indexes[0] = responseIndex; 125 | indexes[1] = repeatIndex; 126 | result.cache[cacheKey] = indexes; 127 | 128 | // return the appropriate response object 129 | response = request.responses[responseIndex]; 130 | 131 | if (response.file) { 132 | this.file(req, res, response, route.dataPath); 133 | return; 134 | } 135 | 136 | if (response.status && !response.file && !response.url) { 137 | this.noContent(req, res, response); 138 | return; 139 | } 140 | 141 | if (response.url) { 142 | this.url(req, res, response); 143 | return; 144 | } 145 | 146 | res.sendStatus(404); 147 | } 148 | }; 149 | 150 | module.exports = result; 151 | -------------------------------------------------------------------------------- /packages/mock-server/response/tests/asynchronous.spec.js: -------------------------------------------------------------------------------- 1 | const request = require('superagent'); 2 | const isEqual = require('lodash/isEqual'); 3 | 4 | const helper = require('../../tests/helpers'); 5 | 6 | describe('Asynchronous', () => { 7 | helper.serverSpecHelper(); 8 | 9 | it('should respond with 202 then 201', async () => { 10 | let res = await request.get(helper.getUrl('/v1/route6')); 11 | expect(res.status).toBe(202); 12 | expect(isEqual(res.body, { c: 3 })).toBeTruthy(); 13 | 14 | res = await request.get(helper.getUrl('/v1/route6')); 15 | expect(res.status).toBe(201); 16 | expect(isEqual(res.body, { d: 4 })).toBeTruthy(); 17 | }); 18 | 19 | it('should repeat dummy-response1.json x3, dummy-response2.json x1 then followed by a dummy-response2 x1', async () => { 20 | let res = await request.get(helper.getUrl('/v1/route8')); 21 | expect(res.status).toBe(202); 22 | expect(isEqual(res.body, { a: 1 })).toBeTruthy(); 23 | 24 | res = await request.get(helper.getUrl('/v1/route8')); 25 | expect(res.status).toBe(202); 26 | expect(isEqual(res.body, { a: 1 })).toBeTruthy(); 27 | 28 | res = await request.get(helper.getUrl('/v1/route8')); 29 | expect(res.status).toBe(202); 30 | expect(isEqual(res.body, { a: 1 })).toBeTruthy(); 31 | 32 | res = await request.get(helper.getUrl('/v1/route8')); 33 | expect(res.status).toBe(202); 34 | expect(isEqual(res.body, { b: 2 })).toBeTruthy(); 35 | 36 | res = await request.get(helper.getUrl('/v1/route8')); 37 | expect(res.status).toBe(201); 38 | expect(isEqual(res.body, { c: 3 })).toBeTruthy(); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /packages/mock-server/response/tests/behavior.spec.js: -------------------------------------------------------------------------------- 1 | const request = require('superagent'); 2 | const helper = require('../../tests/helpers'); 3 | 4 | describe('Behavior', () => { 5 | helper.serverSpecHelper(); 6 | 7 | it('should respond with 404 for undefined route', async () => { 8 | await expect(request.get(helper.getUrl('/dummy/route'))).rejects.toThrow('Not Found'); 9 | }); 10 | 11 | it('should respond with 404 when file does not exist', async () => { 12 | await expect(request.get(helper.getUrl('/bad/file'))).rejects.toThrow('Not Found'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/mock-server/response/tests/scoring.spec.js: -------------------------------------------------------------------------------- 1 | const request = require('superagent'); 2 | const isEqual = require('lodash/isEqual'); 3 | 4 | const helper = require('../../tests/helpers'); 5 | 6 | describe('Scoring', () => { 7 | helper.serverSpecHelper(); 8 | 9 | describe('Parameters', () => { 10 | it('route 3 should respond with dummy-response2.json for GET with partial parameters', async () => { 11 | const res = await request.get(helper.getUrl('/v1/route3?param1=1')); 12 | expect(res.status).toBe(200); 13 | expect( 14 | isEqual(res.body, { 15 | b: 2 16 | }) 17 | ).toBeTruthy(); 18 | }); 19 | 20 | it('route 3 should respond with dummy-response2.json for GET with specified parameters', async () => { 21 | const res = await request.get(helper.getUrl('/v1/route3?param1=1¶m2=2')); 22 | expect(res.status).toBe(200); 23 | expect( 24 | isEqual(res.body, { 25 | b: 2 26 | }) 27 | ).toBeTruthy(); 28 | }); 29 | 30 | it("route 3 should respond with dummy-response3.json for GET with '.' in parameters", async () => { 31 | const res = await request.get(helper.getUrl('/v1/route3?param1=1¶m2=2¶m3=3')); 32 | expect(res.status).toBe(200); 33 | expect( 34 | isEqual(res.body, { 35 | c: 3 36 | }) 37 | ).toBeTruthy(); 38 | }); 39 | 40 | it('route 3 should respond with dummy-response3.json for GET with specified parameters', async () => { 41 | const res = await request.get(helper.getUrl('/v1/route3?param1.2=abc')); 42 | expect(res.status).toBe(200); 43 | expect( 44 | isEqual(res.body, { 45 | c: 3 46 | }) 47 | ).toBeTruthy(); 48 | }); 49 | 50 | it('route 3 should respond with dummy-response4.json for GET with no matching parameters', async () => { 51 | const res = await request.get(helper.getUrl('/v1/route3?param1=452')); 52 | expect(res.status).toBe(200); 53 | expect( 54 | isEqual(res.body, { 55 | d: 4 56 | }) 57 | ).toBeTruthy(); 58 | }); 59 | 60 | it('should respond with base file for undefined query route', async () => { 61 | const res = await request.get(helper.getUrl('/internal/v2/route2?dummy=true')); 62 | expect(res.status).toBe(200); 63 | expect( 64 | isEqual(res.body, { 65 | b: 2 66 | }) 67 | ).toBeTruthy(); 68 | }); 69 | }); 70 | 71 | describe('Array Parameters', () => { 72 | it('should respond with dummy-response-2.json for GET with 3 matching params (1 non-array, 2 array)', async () => { 73 | const res = await request.get(helper.getUrl('/v1/route4?param1=a¶m2=c¶m2=d')); 74 | expect(res.status).toBe(200); 75 | expect( 76 | isEqual(res.body, { 77 | b: 2 78 | }) 79 | ).toBeTruthy(); 80 | }); 81 | 82 | it('should respond with dummy-response-3.json for GET with 4 matching params (2 array, 2 array)', async () => { 83 | const res = await request.get(helper.getUrl('/v1/route4?param1=a¶m1=b¶m2=c¶m2=d')); 84 | expect(res.status).toBe(200); 85 | expect( 86 | isEqual(res.body, { 87 | c: 3 88 | }) 89 | ).toBeTruthy(); 90 | }); 91 | 92 | it('should respond with dummy-response-4.json for GET with 2 matching params (2 array)', async () => { 93 | const res = await request.get(helper.getUrl('/v1/route4?param2=c¶m2=d')); 94 | expect(res.status).toBe(200); 95 | expect( 96 | isEqual(res.body, { 97 | d: 4 98 | }) 99 | ).toBeTruthy(); 100 | }); 101 | 102 | it('should respond dummy-response-4.json when GET request has query params but the configuration for queries is undefined', async () => { 103 | const res = await request.get(helper.getUrl('/no/params?param1=a¶m2=b')); 104 | expect(res.status).toBe(200); 105 | expect( 106 | isEqual(res.body, { 107 | d: 4 108 | }) 109 | ).toBeTruthy(); 110 | }); 111 | }); 112 | 113 | describe('Regex Parameters', () => { 114 | it('should respond with dummy-response-2.json for GET with regex pattern', async () => { 115 | const res = await request.get(helper.getUrl('/v1/route10?a=1')); 116 | expect(res.status).toBe(200); 117 | expect( 118 | isEqual(res.body, { 119 | b: 2 120 | }) 121 | ).toBeTruthy(); 122 | }); 123 | 124 | it('should NOT response with dummy-response-2.json for GET with regex pattern', async () => { 125 | const res = await request.get(helper.getUrl('/v1/route10?a=4')); 126 | expect(res.status).toBe(200); 127 | expect( 128 | isEqual(res.body, { 129 | b: 2 130 | }) 131 | ).toBeFalsy(); 132 | }); 133 | }); 134 | 135 | describe('Headers', () => { 136 | it('should respond dummy-response-1.json when GET headers has pair a:1', async () => { 137 | const res = await request.get(helper.getUrl('/v1/route10')).set('a', '1'); 138 | 139 | expect(res.status).toBe(200); 140 | expect( 141 | isEqual(res.body, { 142 | a: 1 143 | }) 144 | ).toBeTruthy(); 145 | }); 146 | }); 147 | }); 148 | -------------------------------------------------------------------------------- /packages/mock-server/routes/index.js: -------------------------------------------------------------------------------- 1 | const each = require('lodash/each'); 2 | const forEach = require('lodash/forEach'); 3 | const isArray = require('lodash/isArray'); 4 | const keys = require('lodash/keys'); 5 | const merge = require('lodash/merge'); 6 | const fs = require('fs'); 7 | const chalk = require('chalk'); 8 | 9 | const config = require('../config'); 10 | const response = require('../response'); 11 | const models = require('../models'); 12 | const logger = require('../logger').getInstance(); 13 | 14 | const { Route } = models; 15 | 16 | const routes = { 17 | /** 18 | * Initialize the Express routes from the endpoints in the configurations file. 19 | */ 20 | init() { 21 | // eslint-disable-next-line unicorn/no-this-assignment 22 | const self = this; 23 | 24 | const { router } = config; 25 | 26 | // Add default route. Configurations should be allowed to override this if needed. 27 | router.get('/', (req, res) => { 28 | const pkg = require('../package.json'); 29 | res.send({ 30 | name: pkg.name, 31 | description: pkg.description, 32 | version: pkg.version 33 | }); 34 | }); 35 | 36 | config.options.endpoints = {}; 37 | 38 | // Load plugins that contain data and routes 39 | this.plugins(); 40 | 41 | // Load local data and routes. Local data and routes should override the plugins. 42 | const routePaths = config.options.routes; 43 | const dataPath = config.options.data; 44 | 45 | // Local data can be missing if only plugins are use for route + data 46 | if (routePaths && dataPath) { 47 | this.routes(routePaths, dataPath); 48 | } 49 | 50 | each(config.options.endpoints, (endpoint, url) => { 51 | const route = new Route(url, endpoint, endpoint.dataPath); 52 | self.add(route); 53 | }); 54 | }, 55 | 56 | routes(routePaths, dataPath) { 57 | // support multiple directories for routes 58 | routePaths = isArray(routePaths) ? routePaths : [routePaths]; 59 | 60 | forEach(routePaths, (routePath) => { 61 | const contents = fs.readFileSync(routePath, 'utf8'); 62 | const routeConfig = JSON.parse(contents); 63 | 64 | each(routeConfig, (route) => { 65 | route.dataPath = dataPath; 66 | }); 67 | merge(config.options.endpoints, routeConfig); 68 | }); 69 | }, 70 | 71 | plugins() { 72 | const plugins = config.options.plugins || []; 73 | 74 | forEach(plugins, (plugin) => { 75 | let pluginConfig = null; 76 | 77 | pluginConfig = require(plugin); 78 | logger.info(`Loading plugin ${chalk.blue(plugin)}`); 79 | this.routes(pluginConfig.routes, pluginConfig.data); 80 | }); 81 | }, 82 | 83 | /** 84 | * Create a route in Express and cache the route in the config object cache. Express routes 85 | * forward request to the response module. The route configuration is attached to the res.locals 86 | * object for later use. 87 | * 88 | * @param {Object} route Object representation route in the configuration file. 89 | */ 90 | add(route) { 91 | // cache the route configuration 92 | config.cache[route.id] = route; 93 | 94 | const methods = keys(route.methods); 95 | const { router } = config; 96 | 97 | each(methods, (method) => { 98 | // builds get|post|put|delete routes like /v1/payers 99 | router[method](route.url, (req, res, next) => { 100 | // get from cache and attach to request local 101 | res.locals.route = config.cache[route.id]; 102 | response.send(req, res, next); 103 | }); 104 | }); 105 | } 106 | }; 107 | 108 | module.exports = routes; 109 | -------------------------------------------------------------------------------- /packages/mock-server/routes/tests/routes.spec.js: -------------------------------------------------------------------------------- 1 | const request = require('superagent'); 2 | const intersection = require('lodash/intersection'); 3 | const isEqual = require('lodash/isEqual'); 4 | 5 | const helper = require('../../tests/helpers'); 6 | 7 | const getConfiguredVerbs = (ekko, path) => { 8 | const routeConfigs = ekko.config().router.stack; 9 | 10 | const verbs = routeConfigs 11 | .filter((routeConfig) => routeConfig.route !== undefined) 12 | .map((routeConfig) => { 13 | if (routeConfig.route.path === path) { 14 | return Object.keys(routeConfig.route.methods)[0]; 15 | } 16 | return null; 17 | }) 18 | .filter(Boolean); 19 | 20 | return verbs; 21 | }; 22 | 23 | describe('Routes', () => { 24 | helper.serverSpecHelper(); 25 | 26 | it('route 1 should be defined with GET, PUT, POST and DELETE', () => { 27 | // since no verbs were defined the the mock server 28 | // will configure all verbs for this route 29 | const verbs = ['get', 'put', 'post', 'delete']; 30 | 31 | const configuredVerbs = getConfiguredVerbs(helper.ekko, '/v1/route1.:format?'); 32 | 33 | const count = intersection(verbs, configuredVerbs).length; 34 | expect(count).toBe(4); 35 | }); 36 | 37 | it('route 1 should respond with dummy-response1.json', async () => { 38 | const res = await request.get(helper.getUrl('/v1/route1')); 39 | 40 | expect(res.status).toBe(200); 41 | expect(isEqual(res.body, { a: 1 })).toBeTruthy(); 42 | }); 43 | 44 | it('route 2 should respond with dummy-response2.json for GET', async () => { 45 | const res = await request.get(helper.getUrl('/internal/v2/route2')); 46 | expect(res.status).toBe(200); 47 | expect(isEqual(res.body, { b: 2 })).toBeTruthy(); 48 | }); 49 | 50 | it('route 2 should respond with dummy-response3.json for POST', async () => { 51 | const res = await request.post(helper.getUrl('/internal/v2/route2')).send({ bar: 'baz' }); 52 | expect(res.status).toBe(200); 53 | expect(isEqual(res.body, { c: 3 })).toBeTruthy(); 54 | }); 55 | 56 | it('route 4 should respond with dummy-response-2.json for POST with parameters', async () => { 57 | const res = await request.post(helper.getUrl('/v1/route4')).send({ a: { b: 'b' } }); 58 | expect(res.status).toBe(200); 59 | expect(isEqual(res.body, { b: 2 })).toBeTruthy(); 60 | }); 61 | 62 | it('route 4 should response with dummy-response1.json [default file] for POST with no parameters', async () => { 63 | const res = await request.post(helper.getUrl('/v1/route4')); 64 | expect(res.status).toBe(200); 65 | expect(isEqual(res.body, { a: 1 })).toBeTruthy(); 66 | }); 67 | 68 | it('route 9 should response with dummy-response1.json and status 201 for GET', async () => { 69 | const res = await request.get(helper.getUrl('/v1/route9')); 70 | expect(res.status).toBe(201); 71 | expect(isEqual(res.body, { a: 1 })).toBeTruthy(); 72 | }); 73 | 74 | it('route 9 should response with dummy-response2.json and status 422 for POST', async () => { 75 | const res = await request.post(helper.getUrl('/v1/route9')); 76 | expect(res.status).toBe(203); 77 | expect(isEqual(res.body, { b: 2 })).toBeTruthy(); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /packages/mock-server/tests/data/dummy-response-1.json: -------------------------------------------------------------------------------- 1 | {"a": 1} 2 | -------------------------------------------------------------------------------- /packages/mock-server/tests/data/dummy-response-2.json: -------------------------------------------------------------------------------- 1 | {"b":2} 2 | -------------------------------------------------------------------------------- /packages/mock-server/tests/data/dummy-response-3.json: -------------------------------------------------------------------------------- 1 | {"c": 3} 2 | -------------------------------------------------------------------------------- /packages/mock-server/tests/data/dummy-response-4.json: -------------------------------------------------------------------------------- 1 | {"d": 4} 2 | -------------------------------------------------------------------------------- /packages/mock-server/tests/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const config = require('../config'); 3 | const Ekko = require('../index'); 4 | 5 | const test = { 6 | ekko: null, 7 | 8 | serverSpecHelper() { 9 | beforeEach(async () => { 10 | test.ekko = new Ekko(path.join(__dirname, 'test-config.js')); 11 | await test.ekko.start(); 12 | }); 13 | 14 | afterEach(async () => { 15 | await test.ekko.stop(); 16 | }); 17 | }, 18 | 19 | getUrl(endpoint) { 20 | const url = ['http://localhost:', config.server.address().port, endpoint].join(''); 21 | return url; 22 | }, 23 | 24 | getFile(name) { 25 | const filePath = path.join(__dirname, 'data', name); 26 | return filePath; 27 | } 28 | }; 29 | 30 | module.exports = test; 31 | -------------------------------------------------------------------------------- /packages/mock-server/tests/server.spec.js: -------------------------------------------------------------------------------- 1 | const Ekko = require('../index'); 2 | 3 | describe('Ekko', () => { 4 | it('should be defined', () => { 5 | expect(Ekko).toBeDefined(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/mock-server/tests/test-config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const config = { 4 | latency: 0, 5 | user: null, 6 | cache: 0, 7 | limit: '50mb', 8 | port: 0, 9 | host: '127.0.0.1', // 0.0.0.0 or localhost causes windows tests to fail? 10 | 11 | data: path.join(__dirname, '/data'), 12 | routes: path.join(__dirname, '/dummy.routes.config.json') 13 | }; 14 | 15 | module.exports = config; 16 | -------------------------------------------------------------------------------- /packages/workflow-logger/README.md: -------------------------------------------------------------------------------- 1 | # @availity/workflow-logger 2 | 3 | > Simple colorized logger for availity-workflow 4 | 5 | ## Stack 6 | - [chalk](https://www.npmjs.com/package/chalk) for color palette. 7 | - [figures](https://www.npmjs.com/package/figures) for symbols 8 | - [boxen](https://www.npmjs.com/package/boxen) for terminal boxes 9 | 10 | ## Usage 11 | 12 | ```js 13 | const Logger = require('@availity/workflow-logger); 14 | Logger.warn('Caution: Here be dragons.'); 15 | ``` 16 | 17 | ## Static Methods 18 | 19 | ### `warn` 20 | `console.log` message in `yellow`. 21 | 22 | ### `error` 23 | `console.log` message in `red`. 24 | 25 | ### `info` 26 | `console.log` message in `gray`. 27 | 28 | ### `debug` 29 | Same as `info`. 30 | 31 | ### `log` 32 | Same as `info`. 33 | 34 | ### `simple` 35 | Alias for `console.log` and does not use colors. 36 | 37 | ### `empty` 38 | Prints an empty line in the terminal. 39 | 40 | ### `failed` 41 | Prints an error message in `red` with a cross symbol and error label 42 | 43 | ```bash 44 | ✖ [ ERROR ] Failed linting 45 | ``` 46 | 47 | ### `message` 48 | Prints info message with custom label or **INFO** if not label is provided 49 | 50 | ```bash 51 | ℹ [ Dry Run ] Skipping version bump 52 | ``` 53 | 54 | ### `success` 55 | Prints a success message in `green` with a checkbox symbol. 56 | 57 | ```bash 58 | ✔︎ Finished linting 59 | ``` 60 | 61 | ### `box` 62 | Terminal message wrapped in a box. 63 | 64 | ```bash 65 | +--------------------------------------------------+ 66 | | | 67 | | The app is running at http://localhost:3000/ | 68 | | | 69 | +--------------------------------------------------+ 70 | ``` 71 | -------------------------------------------------------------------------------- /packages/workflow-logger/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | const chalk = require('chalk'); 3 | const figures = require('figures'); 4 | const boxen = require('boxen'); 5 | 6 | class Logger { 7 | constructor(options) { 8 | this.options = options; 9 | } 10 | 11 | static warn(entry) { 12 | this.record(entry, 'yellow'); 13 | } 14 | 15 | static error(entry) { 16 | this.record(entry, 'red'); 17 | } 18 | 19 | static info(entry) { 20 | this.record(entry); 21 | } 22 | 23 | static debug(entry) { 24 | this.record(entry); 25 | } 26 | 27 | static log(entry) { 28 | this.record(entry); 29 | } 30 | 31 | // › Started dev server 32 | static record(entry = '', color = '') { 33 | const defaultColor = entry instanceof Error ? 'red' : 'gray'; 34 | const crayoloa = color || defaultColor; 35 | 36 | // Determine color of prefix log 37 | let delimeter = chalk.bold[crayoloa](figures.pointerSmall); 38 | delimeter = `${delimeter}`; 39 | 40 | console.log(`${delimeter} ${chalk[crayoloa](entry)}`); 41 | } 42 | 43 | static simple(entry) { 44 | console.log(entry); 45 | } 46 | 47 | static empty() { 48 | console.log(''); 49 | } 50 | 51 | // ✖ [ ERROR] Failed linting 52 | static failed(entry) { 53 | const prefix = chalk.red(figures.cross); 54 | const label = chalk.white.bold(' ERROR '); 55 | console.log(`${prefix} ${chalk.bgRed(label)} ${chalk.red(entry)}`); 56 | } 57 | 58 | // ★ [ WARNING] Compiled with warnings 59 | static alert(entry) { 60 | const prefix = chalk.yellow(figures.star); 61 | const label = chalk.black.bold(' WARNING '); 62 | console.log(`${prefix} ${chalk.bgYellow(label)} ${chalk.yellow(entry)}`); 63 | } 64 | 65 | static message(entry, entryLabel) { 66 | const prefix = chalk.blue(figures.info); 67 | entryLabel = entryLabel || 'INFO'; 68 | const label = chalk.white.bold(` ${entryLabel} `); 69 | console.log(`${prefix} ${chalk.bgBlue(label)} ${chalk.blue(entry)}`); 70 | } 71 | 72 | // ✔︎ Finished linting 73 | static success(entry) { 74 | const prefix = chalk.green.bold(figures.tick); 75 | console.log(`${prefix} ${chalk.gray(entry)}`); 76 | } 77 | 78 | static box(entry) { 79 | console.log( 80 | boxen(`${chalk.gray(entry)}`, { 81 | padding: 1, 82 | borderColor: 'yellow', 83 | borderStyle: 'classic', 84 | dimBorder: true 85 | }) 86 | ); 87 | } 88 | } 89 | 90 | module.exports = Logger; 91 | -------------------------------------------------------------------------------- /packages/workflow-logger/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@availity/workflow-logger", 3 | "version": "7.1.0", 4 | "description": "Simple colorized logger for @availity/workflow", 5 | "main": "index.js", 6 | "scripts": { 7 | "generateChangelog": "yarn changelog:generate --tag-prefix $npm_package_name --lerna-package $npm_package_name", 8 | "generateTag": "yarn exec git tag --message \"${npm_package_name}@${npm_package_version}\" --annotate \"${npm_package_name}@${npm_package_version}\"", 9 | "test": "echo \"No test specified\" && exit 0", 10 | "test:integration": "echo \"No test specified\" && exit 0", 11 | "publish": "yarn npm publish --tolerate-republish --access public", 12 | "publish:canary": "yarn npm publish --access public --tag canary" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/availity/availity-workflow.git" 17 | }, 18 | "keywords": [ 19 | "logger", 20 | "availity" 21 | ], 22 | "author": "Robert McGuinness", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/availity/availity-workflow/issues" 26 | }, 27 | "homepage": "https://github.com/availity/availity-workflow#readme", 28 | "dependencies": { 29 | "boxen": "^5.1.2", 30 | "chalk": "^4.1.2", 31 | "figures": "^3.2.0" 32 | }, 33 | "engines": { 34 | "node": "^18.0.0 || ^20.0.0 || ^22.0.0", 35 | "npm": "^10.5.0", 36 | "yarn": ">=1.19.1" 37 | }, 38 | "publishConfig": { 39 | "access": "public" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/workflow-logger/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workflow-logger", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "projectType": "library", 5 | "targets": { 6 | "version": { 7 | "executor": "@jscutlery/semver:version", 8 | "options": { 9 | "preset": "angular", 10 | "commitMessageFormat": "chore({projectName}): release version ${version}", 11 | "tagPrefix": "@availity/{projectName}@", 12 | "trackDeps": true 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/workflow-upgrade/README.md: -------------------------------------------------------------------------------- 1 | ## @availity/workflow-upgrade 2 | 3 | Simple package that will upgrade the `@availity/workflow` to the latest version. This was inspired by the upgrade process followed by [Husky](https://github.com/typicode/husky/tree/master/src/upgrader). 4 | 5 | #### Usage 6 | Change to the directory you want to upgrade the workflow for and run the below command. 7 | ```bash 8 | npx @availity/workflow-upgrade 9 | ``` 10 | -------------------------------------------------------------------------------- /packages/workflow-upgrade/bin.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const index = require('./index'); 3 | 4 | index(process.cwd()); 5 | -------------------------------------------------------------------------------- /packages/workflow-upgrade/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@availity/workflow-upgrade", 3 | "version": "7.2.0", 4 | "bin": { 5 | "upgrade-workflow": "./bin.js" 6 | }, 7 | "description": "Package to upgrade availity-workflow projects", 8 | "main": "bin.js", 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/availity/availity-workflow.git" 12 | }, 13 | "scripts": { 14 | "generateChangelog": "yarn changelog:generate --tag-prefix $npm_package_name --lerna-package $npm_package_name", 15 | "generateTag": "yarn exec git tag --message \"${npm_package_name}@${npm_package_version}\" --annotate \"${npm_package_name}@${npm_package_version}\"", 16 | "start": "node bin.js", 17 | "publish": "yarn npm publish --tolerate-republish --access public", 18 | "publish:canary": "yarn npm publish --access public --tag canary" 19 | }, 20 | "keywords": [ 21 | "upgrade", 22 | "workflow", 23 | "availity", 24 | "react" 25 | ], 26 | "author": "Robert McGuinness", 27 | "license": "MIT", 28 | "bugs": { 29 | "url": "https://github.com/availity/availity-workflow/issues" 30 | }, 31 | "homepage": "https://github.com/availity/availity-workflow#readme", 32 | "engines": { 33 | "node": "^18.0.0 || ^20.0.0 || ^22.0.0", 34 | "npm": "^10.5.0", 35 | "yarn": "^1.19.1 || ^2.0.0 || ^3.0.0" 36 | }, 37 | "publishConfig": { 38 | "access": "public" 39 | }, 40 | "dependencies": { 41 | "@availity/workflow-logger": "workspace:*", 42 | "inquirer": "^8.2.6", 43 | "read-pkg": "^5.2.0", 44 | "rimraf": "^5.0.10" 45 | }, 46 | "devDependencies": { 47 | "@types/inquirer": "^8.2.10" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/workflow-upgrade/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workflow-upgrade", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "projectType": "library", 5 | "targets": { 6 | "version": { 7 | "executor": "@jscutlery/semver:version", 8 | "options": { 9 | "preset": "angular", 10 | "commitMessageFormat": "chore({projectName}): release version ${version} [skip ci]", 11 | "tagPrefix": "@availity/{projectName}@", 12 | "trackDeps": true 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/workflow/README.md: -------------------------------------------------------------------------------- 1 | # CLI 2 | 3 | ## Table of Contents 4 | 5 | - [NPM](#npm) 6 | - [Commands](#commands) 7 | - [help](#help) 8 | - [start](#start) 9 | - [lint](#lint) 10 | - [build](#build) 11 | - [release](#release) 12 | - [profile](#profile) 13 | - [test](#test) 14 | 15 | ## NPM 16 | 17 | > NPM requires `--` between the command and options 18 | 19 | ```bash 20 | npm run -- 21 | ``` 22 | 23 | ## Commands 24 | 25 | ### `help` 26 | 27 | Show help menu for all CLI options. 28 | 29 | ### `init ` 30 | 31 | #### Positionals 32 | 33 | `projectName` Required. The name of the project you want to create. 34 | 35 | #### Options 36 | 37 | - `--version`, `-v`: Specify which version of the plugin project you want. [default: "latest"] 38 | - `--current-dir`, `-c`: If you want the project to be created in the current directory 39 | - `--template`, `-t`: The template you want to initialize the projec with 40 | 41 | #### Examples 42 | 43 | - `npx @availity/workflow init my-package-name` 44 | - `npx @availity/workflow init my-package-name --template https://github.com/Availity/availity-starter-typescript` 45 | - `npx @availity/workflow init my-package-name --version 4.0.0-alpha.4` 46 | 47 | ### `start` 48 | 49 | Start the development server and watches for file changes. Hot-reloading is enabled for React projects. Angular projects hot reload CSS only. 50 | 51 | #### options 52 | 53 | ##### `--dry-run` 54 | 55 | Start the development server using production settings. **Example:** 56 | 57 | `npm start -- --dry-run` 58 | 59 | ### `lint` 60 | 61 | Lint project files using EsLint. 62 | 63 | #### options 64 | 65 | ##### `--include` 66 | 67 | Include additional glob patterns for linting. 68 | 69 | ##### `--ignore-git-untracked` 70 | 71 | Ignore files that are not indexed by git. 72 | 73 | ##### `--disable-linter` 74 | 75 | Disable linter when creating bundles for production or staging. 76 | 77 | ### `build` 78 | 79 | - Cleans up `/dist` folder 80 | - Bundles project assets into the `/dist` folder 81 | 82 | ### `release` 83 | 84 | - Prompts the user for a version 85 | - Cleans up `/dist` folder 86 | - Bundles project assets into the `/dist` folder 87 | - Tags the version in Git 88 | 89 | #### `--no-optimize` 90 | 91 | -Bundles javascript assets without minifying 92 | 93 | `npm run production --no-optimize` 94 | 95 | #### NODE_ENV 96 | 97 | ##### `production` 98 | 99 | Minifies the javascript assets. 100 | 101 | ##### `staging` 102 | 103 | No minification is done on javascript assets 104 | 105 | #### options 106 | 107 | ##### `--dry-run` 108 | 109 | - Disables committing files to git 110 | - Disables creating a git tag 111 | 112 | Skipped tasks will print a message in the console 113 | 114 | ```bash 115 | ℹ [ Dry Run ] Skipping version bump 116 | ``` 117 | 118 | ### `profile` 119 | 120 | Analyze Webpack bundles and find what is contributing their sizes. This command generates an html graphical chart that automatically opens in the browser as well as a text summary report in the console. 121 | 122 | ``` 123 | › Webpack profile: 124 | 125 | react-dom: 533.24 KB (39.1%) 126 | availity-uikit: 242.86 KB (17.8%) 127 | reactstrap: 152.56 KB (11.2%) 128 | react: 130.38 KB (9.56%) 129 | react-select: 89.44 KB (6.56%) 130 | tether: 55.04 KB (4.03%) 131 | buffer: 47.47 KB (3.48%) 132 | lodash.omit: 36.87 KB (2.70%) 133 | fbjs: 30.45 KB (2.23%) 134 | style-loader: 11.25 KB (0.824%) 135 | process: 5.17 KB (0.379%) 136 | react-input-autosize: 4.54 KB (0.333%) 137 | react-hot-loader: 3.95 KB (0.289%) 138 | lodash.tonumber: 3.91 KB (0.286%) 139 | base64-js: 3.4 KB (0.249%) 140 | css-loader: 2.14 KB (0.157%) 141 | : 11.71 KB (0.859%) 142 | ``` 143 | 144 | ### `test` 145 | 146 | Run the tests for your project. The behavior of the test are determined by the plugin used in the workflow engine. The engine supports `@availity/workflow-plugin-react` (Jest) and `@availity/workflow-plugin-angular` (Karma and Chrome|Firefox|IE) plugins. 147 | 148 | #### options 149 | 150 | ##### `--coverage` 151 | 152 | Indicates that test coverage information should be collected and reported in the output. React project leverage the `--coverage` option from Jest. Angular projects use Instanbul to collect coverage metrics. Angular projects output coverage statistics to the console as well as html files in the `{workspaces}/coverage` folder. 153 | 154 | ```bash 155 | › Started testing 156 | Chrome 59.0.3071 (Mac OS X 10.12.5): Executed 3 of 3 SUCCESS (0.706 secs / 0.082 secs) 157 | 158 | =============================== Coverage summary =============================== 159 | Statements : 27.69% ( 8620/31134 ) 160 | Branches : 9.81% ( 1980/20189 ) 161 | Functions : 22.15% ( 1381/6235 ) 162 | Lines : 27.23% ( 7955/29217 ) 163 | ================================================================================ 164 | ✔ Finished testing 165 | ``` 166 | 167 | ### `about` 168 | 169 | WIP 170 | 171 | ## Disclaimer 172 | 173 | Open source software components distributed or made available in the Availity Materials are licensed to Company under the terms of the applicable open source license agreements, which may be found in text files included in the Availity Materials. 174 | 175 | ## License 176 | 177 | [MIT](../../LICENSE) 178 | -------------------------------------------------------------------------------- /packages/workflow/helpers/paths.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | // https://github.com/facebook/create-react-app/blob/134cd3c59cdac8db9383432519f5a612b4dcfb79/packages/react-scripts/config/paths.js#L15-L18 5 | const appDirectory = fs.realpathSync(process.cwd()); 6 | const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath); 7 | 8 | module.exports = { 9 | // TODO: add other common paths 10 | project: resolveApp(''), 11 | app: resolveApp('project/app'), 12 | appNodeModules: resolveApp('node_modules'), 13 | appStatic: resolveApp('project/app/static'), 14 | tsconfig: resolveApp('tsconfig.json') 15 | }; 16 | -------------------------------------------------------------------------------- /packages/workflow/helpers/resolve-module.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | const moduleFileExtensions = [ 4 | 'web.mjs', 5 | 'mjs', 6 | 'web.js', 7 | 'js', 8 | 'web.ts', 9 | 'ts', 10 | 'web.tsx', 11 | 'tsx', 12 | 'json', 13 | 'web.jsx', 14 | 'jsx' 15 | ]; 16 | 17 | // Resolve file paths in the same order as webpack 18 | const resolveModule = (resolveFn, filePath) => { 19 | const extension = moduleFileExtensions.find((extension) => fs.existsSync(resolveFn(`${filePath}.${extension}`))); 20 | 21 | if (extension) { 22 | return resolveFn(`${filePath}.${extension}`); 23 | } 24 | 25 | return resolveFn(`${filePath}.js`); 26 | }; 27 | 28 | module.exports = resolveModule; 29 | -------------------------------------------------------------------------------- /packages/workflow/html.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const html = (settings) => { 4 | const workflowTemplate = path.join(__dirname, './public/index.html'); 5 | const projectTemplate = path.join(settings.app(), 'index.html'); 6 | 7 | const workflowFavicon = path.join(__dirname, './public/favicon.ico'); 8 | const projectFavicon = path.join(settings.app(), 'favicon.ico'); 9 | 10 | const config = { 11 | template: settings.asset(workflowTemplate, projectTemplate), 12 | favicon: settings.asset(workflowFavicon, projectFavicon), 13 | title: settings.title() 14 | }; 15 | 16 | return config; 17 | }; 18 | 19 | module.exports = html; 20 | -------------------------------------------------------------------------------- /packages/workflow/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | if (process.env.NODE_ENV === 'staging') { 3 | process.argv.push('--no-optimize'); 4 | process.env.NODE_ENV = 'production'; 5 | } 6 | const shouldMimicStaging = process.argv.includes('--no-optimize'); 7 | 8 | const yargs = require('yargs'); 9 | const chalk = require('chalk'); 10 | const start = require('./scripts/start'); 11 | const test = require('./scripts/test'); 12 | const lint = require('./scripts/lint'); 13 | const about = require('./scripts/about'); 14 | const build = require('./scripts/build'); 15 | const release = require('./scripts/release'); 16 | const profile = require('./scripts/profile'); 17 | const settings = require('./settings'); 18 | require('./scripts/init'); 19 | 20 | yargs.command( 21 | 'release', 22 | `${chalk.dim('Bundle project for distribution (production or staging) and create a git tag')}`, 23 | (yyargs) => { 24 | yyargs 25 | .version(false) 26 | .option('version', { 27 | alias: 'v', 28 | describe: 'Specify which version you want to use when tagging the project and creating the release.' 29 | }) 30 | .usage(`\nUsage: ${chalk.yellow('av release')} ${chalk.magenta('[options]')}`) 31 | .example(chalk.yellow(`${chalk.yellow('av release')} ${chalk.magenta('-v 2.0.0')}`)); 32 | }, 33 | async () => { 34 | await settings.init({ shouldMimicStaging }); 35 | await release({ settings }); 36 | } 37 | ); 38 | 39 | // eslint-disable-next-line no-unused-expressions 40 | yargs 41 | 42 | .usage(`\nUsage: ${chalk.yellow('av')} ${chalk.green('')} ${chalk.magenta('[options]')}`) 43 | 44 | .command('start', `${chalk.dim('Start the development server')}`, async () => { 45 | try { 46 | await settings.init(); 47 | await start(); 48 | } catch { 49 | /* noop */ 50 | } 51 | }) 52 | 53 | .command( 54 | 'lint', 55 | `${chalk.dim('Lint source files using ESLint')}`, 56 | (yyargs) => { 57 | yyargs 58 | .option('include', { 59 | alias: 'i', 60 | describe: 'Glob patterns to INCLUDE for ESLint scanning' 61 | }) 62 | .option('fail', { 63 | alias: 'f', 64 | describe: 'Force linter to fail and exit' 65 | }) 66 | .option('ignore-git-untracked', { 67 | alias: 'u', 68 | describe: 'Ignore files that are not indexed by git' 69 | }) 70 | .option('disable-linter', { 71 | describe: 'Disable linter when creating bundles for production or staging' 72 | }); 73 | }, 74 | async () => { 75 | try { 76 | await settings.init(); 77 | await lint(); 78 | } catch { 79 | /* noop */ 80 | } 81 | } 82 | ) 83 | 84 | .command( 85 | 'test', 86 | `${chalk.dim(test.description)}`, 87 | (yyargs) => 88 | yyargs.option('watch', { 89 | alias: 'w', 90 | describe: 'Watch files for changes and rerun tests related to changed files.' 91 | }), 92 | async () => { 93 | try { 94 | await settings.init(); 95 | await test.run({ settings }); 96 | } catch { 97 | /* noop */ 98 | } 99 | } 100 | ) 101 | 102 | .command('profile', `${chalk.dim('Analyze Webpack bundles and find what is contributing their sizes')}`, async () => { 103 | try { 104 | await settings.init(); 105 | await profile(settings); 106 | } catch { 107 | /* noop */ 108 | } 109 | }) 110 | 111 | .command('build', `${chalk.dim('Bundle project for distribution (production or staging)')}`, async () => { 112 | try { 113 | await settings.init({ shouldMimicStaging }); 114 | await build({ settings }); 115 | } catch { 116 | /* noop */ 117 | } 118 | }) 119 | 120 | .command('about', `${chalk.dim('About @availity/workflow')}`, async () => { 121 | try { 122 | await settings.init(); 123 | about({ settings }); 124 | } catch { 125 | /* noop */ 126 | } 127 | }) 128 | 129 | .demand(1, chalk.red('Must provide a valid cli command')) 130 | 131 | .showHelpOnFail(false, 'Specify --help for available options') 132 | 133 | .help('help') 134 | .alias('help', 'h') 135 | 136 | .version() 137 | .alias('version', 'v') 138 | 139 | .example(chalk.yellow('av start')) 140 | 141 | .example(chalk.yellow('av lint')) 142 | 143 | .epilog(`View documentation at ${chalk.blue('https://github.com/availity/availity-workflow')}`).argv; 144 | -------------------------------------------------------------------------------- /packages/workflow/jest.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-dynamic-require */ 2 | // https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/scripts/utils/createJestConfig.js 3 | const path = require('path'); 4 | const merge = require('lodash/merge'); 5 | const jest = require('jest'); 6 | const { existsSync } = require('fs'); 7 | 8 | function create(settings) { 9 | settings.init(); 10 | const rootDir = settings.project(); 11 | const jestInitExists = existsSync(`${path.join(settings.app(), 'jest.init.js')}`); 12 | 13 | const setupFilesPath = path.join(settings.project(), 'jest.setup.js'); 14 | const setupFilesExist = existsSync(setupFilesPath); 15 | 16 | const setupFiles = setupFilesExist ? [`${require.resolve(setupFilesPath)}`] : []; 17 | 18 | // Allow developers to add their own node_modules include path 19 | const userInclude = settings.configuration.development.babelInclude; 20 | const includes = ['@av', 'axios', '@tanstack', 'is-what', 'copy-anything', ...userInclude].join('|'); 21 | 22 | const userJestOverrides = settings.configuration.development.jestOverrides; 23 | 24 | const config = { 25 | cache: false, 26 | clearMocks: true, 27 | collectCoverageFrom: ['project/app/**/*.{js,jsx,ts,tsx}'], 28 | coveragePathIgnorePatterns: ['/node_modules/', '/coverage/', '/dist/', '/build/'], 29 | coverageDirectory: './reports', 30 | coverageProvider: 'v8', 31 | coverageReporters: ['text', 'text-summary', 'cobertura'], 32 | reporters: [ 33 | 'default', 34 | [ 35 | 'jest-junit', 36 | { 37 | outputDirectory: 'reports/junit-test-results', 38 | outputName: 'junit-report.xml' 39 | } 40 | ] 41 | ], 42 | testEnvironment: 'node', 43 | testURL: 'http://localhost', 44 | transform: { 45 | // Jest and Babel don't allow functions in the options so we just return their values here 46 | '^.+\\.(js|jsx|ts|tsx)$': `${require.resolve('./jest/babel.js')}`, 47 | '^.+\\.css$': `${require.resolve('./jest/css.js')}`, 48 | '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': `${require.resolve('./jest/file.js')}` 49 | }, 50 | setupFiles: [require.resolve('raf/polyfill'), ...setupFiles], 51 | setupFilesAfterEnv: jestInitExists 52 | ? require(path.join(settings.app(), 'jest.init.js')) 53 | : ['@testing-library/jest-dom/extend-expect'], 54 | transformIgnorePatterns: [`[/\\\\]node_modules[/\\\\](?!(${includes})).+\\.(js|jsx|ts|tsx)$`], 55 | testMatch: [ 56 | // Ignore the following directories: 57 | // build 58 | // - the build output directory 59 | // .cache 60 | // - the yarn module cache on Ubuntu if $HOME === rootDir 61 | // docs 62 | // - often used to publish to Github Pages 63 | // node_modules 64 | // - ignore tests in dependencies 65 | // dist 66 | // - the dist output directory 67 | '/!(build|docs|dist|node_modules|scripts)/**/__tests__/**/*.(js|ts|tsx)?(x)', 68 | '/!(build|docs|dist|node_modules|scripts)/**/*(*.)(spec|test).(js|ts|tsx)?(x)' 69 | ], 70 | testPathIgnorePatterns: [ 71 | '/.vscode/', 72 | '/.yarn/', 73 | '/automated-tests/', 74 | '/dist/', 75 | '/infra/', 76 | '/node_modules/', 77 | '/observability/', 78 | '/scripts/', 79 | '/static/', 80 | '/wiremock/' 81 | ], 82 | globals: settings.globals() 83 | }; 84 | 85 | if (rootDir) { 86 | config.rootDir = rootDir; 87 | } 88 | 89 | return merge({}, config, userJestOverrides); 90 | } 91 | 92 | function unit(settings) { 93 | const argv = process.argv.slice(2); 94 | const jestConfig = JSON.stringify(create(settings)); 95 | argv.push(`--config=${jestConfig}`, '--env=jsdom'); 96 | 97 | jest.run(argv); 98 | 99 | return Promise.resolve(); 100 | } 101 | 102 | module.exports = (settings) => ({ 103 | run: () => unit(settings), 104 | description: 'Run your tests using Jest' 105 | }); 106 | -------------------------------------------------------------------------------- /packages/workflow/jest/babel.js: -------------------------------------------------------------------------------- 1 | // https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/config/jest/babelTransform.js 2 | const babelJest = require('babel-jest').default; 3 | 4 | const createTransformer = () => 5 | babelJest.createTransformer({ 6 | presets: [ 7 | [ 8 | '@babel/preset-react', 9 | { 10 | runtime: 'automatic' 11 | } 12 | ], 13 | [ 14 | '@babel/preset-env', 15 | { 16 | include: ['@babel/plugin-transform-class-properties'] 17 | } 18 | ], 19 | '@babel/preset-typescript' 20 | ], 21 | plugins: [ 22 | [require.resolve('@babel/plugin-proposal-decorators'), { legacy: true }], 23 | [ 24 | 'babel-plugin-root-import', 25 | { 26 | rootPathSuffix: 'project/app', 27 | rootPathPrefix: '@/' 28 | } 29 | ] 30 | ], 31 | babelrc: false 32 | }); 33 | 34 | module.exports = { 35 | createTransformer 36 | }; 37 | -------------------------------------------------------------------------------- /packages/workflow/jest/css.js: -------------------------------------------------------------------------------- 1 | // https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/config/jest/cssTransform.js 2 | 3 | module.exports = { 4 | process() { 5 | return 'module.exports = {};'; 6 | }, 7 | getCacheKey() { 8 | // The output is always the same. 9 | return 'cssTransform'; 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /packages/workflow/jest/file.js: -------------------------------------------------------------------------------- 1 | // https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/config/jest/fileTransform.js 2 | 3 | const path = require('path'); 4 | 5 | module.exports = { 6 | process(src, filename) { 7 | return `module.exports = ${JSON.stringify(path.basename(filename))};`; 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /packages/workflow/loaders/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | css: require('./loader-css'), 3 | less: require('./loader-less'), 4 | scss: require('./loader-scss'), 5 | postcss: require('./loader-postcss'), 6 | images: require('./rule-images'), 7 | MiniCssExtractPlugin: require('mini-css-extract-plugin') 8 | }; 9 | -------------------------------------------------------------------------------- /packages/workflow/loaders/loader-css.js: -------------------------------------------------------------------------------- 1 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 2 | const loaderPostcss = require('./loader-postcss'); 3 | 4 | module.exports = { 5 | development: { 6 | test: /\.css$/, 7 | use: [ 8 | 'style-loader', 9 | { 10 | loader: 'css-loader', 11 | options: { 12 | sourceMap: true, 13 | importLoaders: 1 14 | } 15 | }, 16 | loaderPostcss 17 | ] 18 | }, 19 | production: { 20 | test: /\.css$/, 21 | use: [ 22 | { 23 | loader: MiniCssExtractPlugin.loader, 24 | options: { 25 | publicPath: 'auto' 26 | } 27 | }, 28 | { 29 | loader: 'css-loader', 30 | options: { 31 | sourceMap: true, 32 | importLoaders: 1 33 | } 34 | }, 35 | loaderPostcss 36 | ] 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /packages/workflow/loaders/loader-less.js: -------------------------------------------------------------------------------- 1 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 2 | const loaderPostcss = require('./loader-postcss'); 3 | 4 | module.exports = { 5 | development: { 6 | test: /\.less$/, 7 | use: [ 8 | 'style-loader', 9 | { 10 | loader: 'css-loader', 11 | options: { 12 | sourceMap: true, 13 | importLoaders: 1 14 | } 15 | }, 16 | { 17 | loader: 'less-loader', 18 | options: { 19 | sourceMap: false 20 | } 21 | } 22 | ] 23 | }, 24 | production: { 25 | test: /\.less$/, 26 | use: [ 27 | { 28 | loader: MiniCssExtractPlugin.loader, 29 | options: { 30 | publicPath: 'auto' 31 | } 32 | }, 33 | { 34 | loader: 'css-loader', 35 | options: { sourceMap: true, importLoaders: 1 } 36 | }, 37 | loaderPostcss, 38 | { 39 | loader: 'less-loader', 40 | options: { sourceMap: false } 41 | } 42 | ] 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /packages/workflow/loaders/loader-postcss.js: -------------------------------------------------------------------------------- 1 | const postcssFocus = require('postcss-focus'); 2 | const postcssReporter = require('postcss-reporter'); 3 | const postcssFlexBugs = require('postcss-flexbugs-fixes'); 4 | const postCssEnv = require('postcss-preset-env'); 5 | 6 | module.exports = { 7 | loader: 'postcss-loader', 8 | options: { 9 | sourceMap: true, 10 | postcssOptions: { 11 | plugins: [ 12 | postcssFocus(), 13 | postcssFlexBugs(), 14 | postCssEnv({ 15 | autoprefixer: { 16 | flexbox: 'no-2009' 17 | }, 18 | stage: 3 19 | }), 20 | postcssReporter({ 21 | clearMessages: true 22 | }) 23 | ] 24 | } 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /packages/workflow/loaders/loader-scss.js: -------------------------------------------------------------------------------- 1 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 2 | const loaderPostcss = require('./loader-postcss'); 3 | 4 | module.exports = { 5 | development: { 6 | test: /\.(scss|sass)$/, 7 | use: [ 8 | 'style-loader', 9 | { 10 | loader: 'css-loader', 11 | options: { 12 | sourceMap: true 13 | } 14 | }, 15 | loaderPostcss, 16 | { 17 | loader: 'sass-loader', 18 | options: { 19 | sourceMap: true 20 | } 21 | } 22 | ] 23 | }, 24 | production: { 25 | test: /\.(scss|sass)$/, 26 | use: [ 27 | { 28 | loader: MiniCssExtractPlugin.loader, 29 | options: { 30 | publicPath: 'auto' 31 | } 32 | }, 33 | 'css-loader', 34 | loaderPostcss, 35 | { loader: 'sass-loader', options: { sourceMap: true } } 36 | ] 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /packages/workflow/loaders/rule-images.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | test: /\.(jpe?g|png|gif|svg)$/i, 3 | type: 'asset/resource', 4 | generator: { 5 | filename: 'images/[name].[ext]' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /packages/workflow/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@availity/workflow", 3 | "version": "12.2.0", 4 | "description": "Upgradable workflow for Availity boilerplate projects", 5 | "main": "index.js", 6 | "bin": { 7 | "av": "./index.js", 8 | "workflow": "./index.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/availity/availity-workflow.git" 13 | }, 14 | "keywords": [ 15 | "boilerplate", 16 | "workflow", 17 | "availity", 18 | "react" 19 | ], 20 | "author": "Robert McGuinness", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/availity/availity-workflow/issues" 24 | }, 25 | "homepage": "https://github.com/availity/availity-workflow#readme", 26 | "scripts": { 27 | "generateChangelog": "yarn changelog:generate --tag-prefix $npm_package_name --lerna-package $npm_package_name", 28 | "generateTag": "yarn exec git tag --message \"${npm_package_name}@${npm_package_version}\" --annotate \"${npm_package_name}@${npm_package_version}\"", 29 | "publish": "yarn npm publish --tolerate-republish --access public", 30 | "publish:canary": "yarn npm publish --access public --tag canary" 31 | }, 32 | "dependencies": { 33 | "@availity/mock-server": "workspace:*", 34 | "@availity/workflow-logger": "workspace:*", 35 | "@babel/core": "^7.27.1", 36 | "@babel/plugin-proposal-decorators": "^7.27.1", 37 | "@babel/preset-env": "^7.27.2", 38 | "@babel/preset-react": "^7.27.1", 39 | "@babel/preset-typescript": "^7.27.1", 40 | "@babel/runtime": "^7.27.1", 41 | "@pmmmwh/react-refresh-webpack-plugin": "^0.5.16", 42 | "@testing-library/jest-dom": "^5.17.0", 43 | "babel-jest": "^27.5.1", 44 | "babel-loader": "^9.2.1", 45 | "babel-plugin-jsx-remove-data-test-id": "3.0.0", 46 | "babel-plugin-root-import": "^6.6.0", 47 | "case-sensitive-paths-webpack-plugin": "^2.4.0", 48 | "chalk": "^4.1.2", 49 | "copy-webpack-plugin": "^11.0.0", 50 | "cross-spawn": "^7.0.6", 51 | "css-loader": "^6.11.0", 52 | "css-minimizer-webpack-plugin": "^5.0.1", 53 | "debug": "^4.4.0", 54 | "del": "^6.1.1", 55 | "duplicate-package-checker-webpack-plugin": "^3.0.0", 56 | "envinfo": "^7.14.0", 57 | "esbuild-loader": "^3.2.0", 58 | "escape-string-regexp": "^4.0.0", 59 | "eslint-config-availity": "^10.0.7", 60 | "eslint-webpack-plugin": "^4.2.0", 61 | "execa": "^5.1.1", 62 | "figures": "^3.2.0", 63 | "file-loader": "^6.2.0", 64 | "fs-extra": "^11.3.0", 65 | "get-port": "^5.1.1", 66 | "globby": "^11.1.0", 67 | "hosted-git-info": "^6.1.3", 68 | "html-webpack-plugin": "5.6.3", 69 | "imports-loader": "^4.0.1", 70 | "inquirer": "^8.2.6", 71 | "is-valid-path": "^0.1.1", 72 | "jest": "^27.5.1", 73 | "joi": "^17.13.3", 74 | "lodash": "^4.17.21", 75 | "mini-css-extract-plugin": "^2.9.2", 76 | "moment": "^2.30.1", 77 | "navigator.sendbeacon": "^0.0.20", 78 | "open": "^7.4.2", 79 | "ora": "^5.4.1", 80 | "path-browserify": "^1.0.1", 81 | "postcss": "^8.5.3", 82 | "postcss-flexbugs-fixes": "^5.0.2", 83 | "postcss-focus": "^6.0.0", 84 | "postcss-loader": "^7.3.4", 85 | "postcss-preset-env": "^9.6.0", 86 | "postcss-reporter": "^7.1.0", 87 | "pretty-ms": "^7.0.1", 88 | "process": "^0.11.10", 89 | "raf": "^3.4.1", 90 | "react-app-polyfill": "^3.0.0", 91 | "react-refresh": "^0.14.2", 92 | "regenerator-runtime": "^0.14.1", 93 | "require-relative": "^0.8.7", 94 | "sass": "^1.87.0", 95 | "sass-loader": "^13.3.3", 96 | "semver": "^7.7.1", 97 | "shelljs": "^0.8.5", 98 | "style-loader": "^3.3.4", 99 | "terser-webpack-plugin": "^5.3.14", 100 | "tsconfig-paths-webpack-plugin": "^4.2.0", 101 | "type-is": "^1.6.18", 102 | "url-join": "^4.0.1", 103 | "validate-npm-package-name": "^5.0.1", 104 | "webpack": "^5.99.8", 105 | "webpack-bundle-analyzer": "^4.10.2", 106 | "webpack-bundle-size-analyzer": "^3.1.0", 107 | "webpack-dev-server": "^4.15.2", 108 | "webpack-notifier": "^1.15.0", 109 | "webpack-sources": "^3.2.3", 110 | "yargs": "^17.7.2" 111 | }, 112 | "devDependencies": { 113 | "@testing-library/react": "^14.3.1", 114 | "@types/babel__preset-env": "^7.10.0", 115 | "@types/sass": "^1.45.0" 116 | }, 117 | "engines": { 118 | "node": "^18.0.0 || ^20.0.0 || ^22.0.0", 119 | "npm": "^10.5.0", 120 | "yarn": "^1.19.1 || ^2.0.0 || ^3.0.0" 121 | }, 122 | "publishConfig": { 123 | "access": "public" 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /packages/workflow/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workflow", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "projectType": "library", 5 | "targets": { 6 | "version": { 7 | "executor": "@jscutlery/semver:version", 8 | "options": { 9 | "preset": "angular", 10 | "commitMessageFormat": "chore({projectName}): release version ${version}", 11 | "tagPrefix": "@availity/{projectName}@", 12 | "trackDeps": true 13 | } 14 | } 15 | }, 16 | "implicitDependencies": ["!example"] 17 | } 18 | -------------------------------------------------------------------------------- /packages/workflow/public/availity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Availity/availity-workflow/5be3c71ebf853865359b3a4458ee059be80edcd1/packages/workflow/public/availity.png -------------------------------------------------------------------------------- /packages/workflow/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Availity/availity-workflow/5be3c71ebf853865359b3a4458ee059be80edcd1/packages/workflow/public/favicon.ico -------------------------------------------------------------------------------- /packages/workflow/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <%= htmlWebpackPlugin.options.title %> 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/workflow/scripts/about.js: -------------------------------------------------------------------------------- 1 | const Logger = require('@availity/workflow-logger'); 2 | const chalk = require('chalk'); 3 | const envinfo = require('envinfo'); 4 | const pkg = require('../package.json'); 5 | 6 | const version = chalk.bold.yellow(`v${pkg.version}`); 7 | 8 | const logo = ` 9 | LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL 10 | LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL 11 | LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL 12 | LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL 13 | LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL 14 | LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL 15 | LLLLLLLLLLLLLLLLLLLCG000CLLLLLLLLLLLLLLLLLLLLLLLLLLL 16 | LLLLLLLLLLLLLLLLLLC88GG08GLLLLLLLLLLLLLLLLLLLLLLLLLL 17 | LLLLLLLLLLLLLLLLLG@0CLLL0@0LLLLLLLLLLLLLLLLLLLLLLLLL 18 | LLLLLLLLLLLLLLLL0@0GGGGGG0@8CLLLLLLLLLLLLLLLLLLLLLLL 19 | LLLLLLLLLLLLLLC0@GG@@008@0G88CLLC00000GCLLLLLLLLLLLL 20 | LLLLLLLLLLLLLC88GLLG80080CLC88G080GGGG88CCCCLLLLLLLL 21 | LLLLLLLLLLLLC88CLLLL0880CLLLC088GLLLLC88CLLLLLLLLLLL 22 | LLLLLLLLLLLLC88GGGG080G88CLC08008GLLG88CLLLLLLLLLLLL 23 | LLLLLLLLLLLLLCG00000CLLC88G0@808@@G0@0CLLLLLLLLLLLLL 24 | LLLLLLLLLLLLLLLLLLLLLLLLC8@0GGGGCG8@0LLLLLLLLLLLLLLL 25 | LLLLLLLLLLLLLLLLLLLLLLLLLC0@0LLLC88GLLLLLLLLLLLLLLLL 26 | LLLLLLLLLLLLLLLLLLLLLLLLLLLG88GG88GLLLLLLLLLLLLLLLLL 27 | LLLLLLLLLLLLLLLLLLLLLLLLLLLLC000GCLLLLLLLLLLLLLLLLLL 28 | LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL 29 | LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL 30 | LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL 31 | LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL 32 | LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL 33 | LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL 34 | LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL 35 | `; 36 | 37 | const message = ` 38 | ${chalk.yellow(logo)} 39 | ${chalk.bold('@availity/workflow')} ${version} 40 | `; 41 | 42 | function about() { 43 | // TODO: Implement an update-notifier 44 | // notifier(); 45 | Logger.simple(message); 46 | envinfo.run( 47 | { 48 | System: ['OS', 'CPU'], 49 | Binaries: ['Node', 'Yarn', 'npm'], 50 | Browsers: ['Chrome', 'Firefox', 'Safari'], 51 | npmPackages: ['@availity/workflow'] 52 | }, 53 | { console: true, showNotFound: true } 54 | ); 55 | } 56 | 57 | module.exports = about; 58 | -------------------------------------------------------------------------------- /packages/workflow/scripts/build.js: -------------------------------------------------------------------------------- 1 | const del = require('del'); 2 | const webpack = require('webpack'); 3 | const ProgressPlugin = require('webpack/lib/ProgressPlugin'); 4 | const ora = require('ora'); 5 | const chalk = require('chalk'); 6 | const Logger = require('@availity/workflow-logger'); 7 | const sizeTree = require('webpack-bundle-size-analyzer/build/src/size_tree'); 8 | 9 | const webpackConfigProfile = require('../webpack.config.profile'); 10 | const webpackConfigProduction = require('../webpack.config.production'); 11 | const customStats = require('./stats'); 12 | 13 | function bundle({ profile, settings }) { 14 | return new Promise((resolve, reject) => { 15 | if (!settings.isDryRun()) { 16 | Logger.success(`Cleaning directories ${settings.output()}`); 17 | del.sync([settings.output()]); 18 | } 19 | 20 | // Lazy load this else yargs loads too early https://github.com/Availity/@availity/workflow/issues/133 21 | const { argv } = require('yargs'); 22 | 23 | // Check argument or CLI arg or default to false 24 | const shouldProfile = profile || argv.profile || false; 25 | 26 | let webpackConfig; 27 | try { 28 | webpackConfig = shouldProfile ? webpackConfigProfile(settings) : webpackConfigProduction(settings); 29 | } catch (error) { 30 | Logger.error(`There was an error creating the Webpack config: ${error.message}`); 31 | reject(error.message); 32 | return; 33 | } 34 | 35 | Logger.info('Started compiling'); 36 | const spinner = ora('Running webpack'); 37 | spinner.color = 'yellow'; 38 | spinner.start(); 39 | 40 | let previousPercent; 41 | 42 | webpackConfig.plugins.push( 43 | new ProgressPlugin((percentage, msg) => { 44 | const percent = Math.round(percentage * 100); 45 | 46 | if (previousPercent === percent) { 47 | return; 48 | } 49 | previousPercent = percent; 50 | 51 | if (percent % 10 === 0 && msg !== null && msg !== undefined && msg.trim() !== '') { 52 | spinner.text = `Webpack ${percent}%`; 53 | } 54 | }) 55 | ); 56 | 57 | const { modifyWebpackConfig } = settings.config(); 58 | 59 | if (typeof modifyWebpackConfig === 'function') { 60 | webpackConfig = modifyWebpackConfig(webpackConfig, settings) || webpackConfig; 61 | } 62 | 63 | // https://webpack.js.org/api/node/#webpack 64 | webpack(webpackConfig).run((err, stats) => { 65 | spinner.stop(); 66 | 67 | if (err) { 68 | Logger.failed('Failed to run, possible webpack configuration error'); 69 | reject(err); 70 | return; 71 | } 72 | 73 | // https://webpack.js.org/api/node/#error-handling 74 | const statistics = customStats(stats, { 75 | errorDetails: stats.hasErrors(), 76 | warnings: stats.hasWarnings() 77 | }); 78 | 79 | if (shouldProfile) { 80 | Logger.info(`${chalk.dim('Webpack profile:')} 81 | `); 82 | const statz = JSON.stringify(stats.toJson()); 83 | const parsedStats = JSON.parse(statz); 84 | const trees = sizeTree.dependencySizeTree(parsedStats); 85 | for (const tree of trees) { 86 | sizeTree.printDependencySizeTree(tree, true, 2, (output) => { 87 | Logger.simple(output); 88 | }); 89 | } 90 | Logger.empty(); 91 | } 92 | 93 | Logger.info(`${chalk.dim('Webpack stats:')} 94 | 95 | ${statistics} 96 | `); 97 | Logger.success('Finished compiling'); 98 | resolve(); 99 | }); 100 | }); 101 | } 102 | 103 | module.exports = bundle; 104 | -------------------------------------------------------------------------------- /packages/workflow/scripts/clone-starter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Motivation 3 | * https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-cli/src/init-starter.js 4 | */ 5 | const execa = require('execa'); 6 | const hostedGitInfo = require('hosted-git-info'); 7 | const fs = require('fs-extra'); 8 | const sysPath = require('path'); 9 | const Logger = require('@availity/workflow-logger'); 10 | const url = require('url'); 11 | const isValid = require('is-valid-path'); 12 | 13 | const spawn = (cmd, options) => { 14 | const [file, ...args] = cmd.split(/\s+/); 15 | return execa(file, args, { stdio: `inherit`, ...options }); 16 | }; 17 | 18 | // Clones starter from URI. 19 | const clone = async (hostInfo, appPath, branchOverride) => { 20 | let url; 21 | // Let people use private repos accessed over SSH. 22 | // eslint-disable-next-line unicorn/prefer-ternary 23 | if (hostInfo.getDefaultRepresentation() === `sshurl`) { 24 | url = hostInfo.ssh({ noCommittish: true }); 25 | // Otherwise default to normal git syntax. 26 | } else { 27 | url = hostInfo.https({ noCommittish: true, noGitPlus: true }); 28 | } 29 | 30 | let branch = ``; 31 | 32 | if (branchOverride) { 33 | branch = `-b ${branchOverride}`; 34 | } else if (hostInfo.committish) { 35 | branch = `-b ${hostInfo.committish}`; 36 | } 37 | 38 | Logger.info(`Creating new site from git: ${url}`); 39 | 40 | await spawn(`git clone ${branch} ${url} ${appPath} --single-branch`); 41 | 42 | Logger.success(`Created starter directory layout`); 43 | 44 | await fs.remove(sysPath.join(appPath, `.git`)); 45 | }; 46 | 47 | /** 48 | * Main function that clones or copies the starter. 49 | */ 50 | module.exports = async ({ template: templateUrl, appPath, branchOverride }) => { 51 | const urlObject = url.parse(appPath); 52 | if (urlObject.protocol && urlObject.host) { 53 | Logger.failed( 54 | `It looks like you forgot to add a name for your new project. Try running instead "npx @availity/workflow init new-project"` 55 | ); 56 | return; 57 | } 58 | 59 | if (!isValid(appPath)) { 60 | Logger.failed(`Could not create a project in "${sysPath.resolve(appPath)}" because it's not a valid path`); 61 | return; 62 | } 63 | 64 | const hostedInfo = hostedGitInfo.fromUrl(templateUrl); 65 | 66 | if (hostedInfo) { 67 | await clone(hostedInfo, appPath, branchOverride); 68 | } else { 69 | Logger.failed('Could not find Hosted Git Info for the Project.', hostedInfo); 70 | } 71 | }; 72 | -------------------------------------------------------------------------------- /packages/workflow/scripts/format.js: -------------------------------------------------------------------------------- 1 | const friendlySyntaxErrorLabel = 'Syntax error:'; 2 | 3 | function isLikelyASyntaxError(message) { 4 | return message.indexOf(friendlySyntaxErrorLabel) !== -1; 5 | } 6 | 7 | // Cleans up webpack error messages. 8 | function formatMessage(message) { 9 | let lines = message.split('\n'); 10 | 11 | // Remove webpack-specific loader notation from filename. 12 | // Before: 13 | // ./~/css-loader!./~/postcss-loader!./src/App.css 14 | // After: 15 | // ./src/App.css 16 | if (lines[0].lastIndexOf('!') !== -1) { 17 | lines[0] = lines[0].slice(lines[0].lastIndexOf('!') + 1); 18 | } 19 | 20 | // line #0 is filename 21 | // line #1 is the main error message 22 | if (!lines[0] || !lines[1]) { 23 | return lines.join('\n'); 24 | } 25 | 26 | // Cleans up verbose "module not found" messages for files and packages. 27 | if (lines[1].indexOf('Module not found: ') === 0) { 28 | lines = [ 29 | lines[0], 30 | // Clean up message because "Module not found: " is descriptive enough. 31 | lines[1] 32 | .replace("Cannot resolve 'file' or 'directory' ", '') 33 | .replace('Cannot resolve module ', '') 34 | .replace('Error: ', ''), 35 | // Skip all irrelevant lines. 36 | // (For some reason they only appear on the client in browser.) 37 | '', 38 | lines.at(-1) // error location is the last line 39 | ]; 40 | } 41 | 42 | // Cleans up syntax error messages. 43 | if (lines[1].indexOf('Module build failed: ') === 0) { 44 | const cleanedLines = []; 45 | for (const line of lines) { 46 | if (line !== '') { 47 | cleanedLines.push(line); 48 | } 49 | } 50 | // We are clean now! 51 | lines = cleanedLines; 52 | // Finally, brush up the error message a little. 53 | lines[1] = lines[1].replace('Module build failed: SyntaxError:', friendlySyntaxErrorLabel); 54 | } 55 | 56 | // Reassemble the message. 57 | message = lines.join('\n'); 58 | // Internal stacks are generally useless so we strip them... with the 59 | // exception of stacks containing `webpack:` because they're normally 60 | // from user code generated by WebPack. For more information see 61 | // https://github.com/facebookincubator/create-react-app/pull/1050 62 | message = message.replaceAll(/^\s*at\s((?!webpack:).)*:\d+:\d+[\s)]*(\n|$)/gm, ''); // at ... ...:x:y 63 | 64 | return message; 65 | } 66 | 67 | function formatWebpackMessages(json) { 68 | const formattedErrors = json.errors.map((message) => `Error in ${formatMessage(message.message)}`); 69 | const formattedWarnings = json.warnings.map((message) => `Warning in ${formatMessage(message.message)}`); 70 | const result = { 71 | errors: formattedErrors, 72 | warnings: formattedWarnings 73 | }; 74 | 75 | if (result.errors.some((msg) => isLikelyASyntaxError(msg))) { 76 | // If there are any syntax errors, show just them. 77 | // This prevents a confusing ESLint parsing error 78 | // preceding a much more useful Babel syntax error. 79 | result.errors = result.errors.filter((error) => isLikelyASyntaxError(error)); 80 | } 81 | 82 | // Only keep the first error. Others are often indicative 83 | // of the same problem, but confuse the reader with noise. 84 | if (result.errors.length > 1) { 85 | result.errors.length = 1; 86 | } 87 | 88 | return result; 89 | } 90 | 91 | module.exports = formatWebpackMessages; 92 | -------------------------------------------------------------------------------- /packages/workflow/scripts/lint.js: -------------------------------------------------------------------------------- 1 | const globby = require('globby'); 2 | const ora = require('ora'); 3 | const chalk = require('chalk'); 4 | const Logger = require('@availity/workflow-logger'); 5 | const requireRelative = require('require-relative'); 6 | const path = require('path'); 7 | const { spawnSync } = require('child_process'); 8 | const settings = require('../settings'); 9 | 10 | async function lint() { 11 | if (settings.isLinterDisabled()) { 12 | Logger.warn('Linting is disabled'); 13 | return true; 14 | } 15 | 16 | let eslint; 17 | try { 18 | eslint = requireRelative('eslint', settings.project()); 19 | } catch { 20 | // no op 21 | } 22 | 23 | if (!eslint) { 24 | try { 25 | eslint = require('eslint'); 26 | } catch { 27 | Logger.failed('Failed linting. Unable to load eslint.'); 28 | throw new Error('Unable to load eslint.'); 29 | } 30 | } 31 | 32 | let engine; 33 | try { 34 | engine = new eslint.ESLint({ 35 | useEslintrc: true 36 | }); 37 | } catch (error) { 38 | Logger.failed(`ESLint configuration error in @availity/workflow. "${error.message}"`); 39 | throw new Error(`ESLint configuration error in @availity/workflow. "${error.message}"`); 40 | } 41 | 42 | Logger.info('Started linting'); 43 | const spinner = ora('running linter rules'); 44 | spinner.color = 'yellow'; 45 | spinner.start(); 46 | 47 | // Files tracked by git 48 | let gitTrackedFiles; 49 | if (settings.isIgnoreUntracked()) { 50 | const gitRootCmd = spawnSync('git', ['rev-parse', '--show-toplevel']); 51 | if (gitRootCmd.status) { 52 | // Non-zero exit code; assume git repository absent 53 | gitTrackedFiles = null; 54 | } else { 55 | const gitLsFiles = spawnSync('git', ['ls-files']); 56 | gitTrackedFiles = gitLsFiles.stdout.toString().trim().split('\n'); 57 | 58 | const gitRoot = gitRootCmd.stdout.toString().trim(); 59 | gitTrackedFiles = gitTrackedFiles.map((file) => path.join(gitRoot, file)); 60 | } 61 | } 62 | 63 | // Uses globby. Defaults to process.cwd() and path.resolve(options.cwd, "/") 64 | const paths = await globby(settings.js().map((path) => path.replaceAll('\\', '/'))); 65 | 66 | // Git repository present 67 | const filesToLint = gitTrackedFiles ? paths.filter((file) => gitTrackedFiles.indexOf(file) > 0) : paths; 68 | 69 | const report = await engine.lintFiles(filesToLint); 70 | 71 | const status = { error: false, warning: false }; 72 | for (const result of report) { 73 | if (result.errorCount) status.error = true; 74 | if (result.warningCount) status.warning = true; 75 | 76 | // If we have error and warning already then no need to check more 77 | if (status.error && status.warning) { 78 | break; 79 | } 80 | } 81 | 82 | spinner.stop(); 83 | 84 | if (status.error || status.warning) { 85 | const formatter = await engine.loadFormatter(); 86 | 87 | Logger.simple(`${formatter.format(report)}`); 88 | Logger.failed('Failed linting'); 89 | 90 | // eslint-disable-next-line unicorn/no-process-exit 91 | if (settings.isFail()) process.exit(1); 92 | 93 | if (status.error) throw new Error('Failed linting'); 94 | } else { 95 | Logger.success(`Finished linting ${chalk.magenta(paths.length)} file(s)`); 96 | } 97 | 98 | return true; 99 | } 100 | 101 | module.exports = lint; 102 | -------------------------------------------------------------------------------- /packages/workflow/scripts/open.js: -------------------------------------------------------------------------------- 1 | const opn = require('open'); 2 | const chalk = require('chalk'); 3 | const urlJoin = require('url-join'); 4 | const Logger = require('@availity/workflow-logger'); 5 | const settings = require('../settings'); 6 | 7 | function open() { 8 | if (settings.open()) { 9 | try { 10 | const port = settings.port(); 11 | const url = settings.open() || ''; 12 | const host = settings.host(); 13 | 14 | const uri = urlJoin(`http://${host}:${port}/`, url); 15 | opn(uri); 16 | Logger.info(`Opening browser at ${chalk.green(uri)}`); 17 | } catch { 18 | // Ignore errors. 19 | } 20 | } 21 | 22 | return Promise.resolve(true); 23 | } 24 | 25 | module.exports = open; 26 | -------------------------------------------------------------------------------- /packages/workflow/scripts/profile.js: -------------------------------------------------------------------------------- 1 | const build = require('./build'); 2 | 3 | function profile(settings) { 4 | return build({ profile: true, settings }); 5 | } 6 | 7 | module.exports = profile; 8 | -------------------------------------------------------------------------------- /packages/workflow/scripts/release.js: -------------------------------------------------------------------------------- 1 | const Logger = require('@availity/workflow-logger'); 2 | 3 | const version = require('./version'); 4 | const lint = require('./lint'); 5 | const build = require('./build'); 6 | 7 | async function release({ settings }) { 8 | try { 9 | await version.prompt(); 10 | Logger.info('Started releasing'); 11 | 12 | await lint(); 13 | await version.bump(); 14 | await build({ settings }); 15 | await version.tag(); 16 | Logger.success('Finished releasing'); 17 | } catch (error) { 18 | Logger.failed(`Failed releasing ${error.message ? `${error.message} \n\n` : ''} ${error.stack || ''}`); 19 | throw error; 20 | } 21 | } 22 | 23 | module.exports = release; 24 | -------------------------------------------------------------------------------- /packages/workflow/scripts/stats.js: -------------------------------------------------------------------------------- 1 | module.exports = (stats, options = {}) => { 2 | const { errorDetails = true, warnings = true } = options; 3 | 4 | // https://webpack.js.org/configuration/stats/#stats-options 5 | return stats.toString({ 6 | colors: true, 7 | cached: true, 8 | reasons: false, 9 | source: false, 10 | chunks: false, 11 | modules: false, 12 | chunkModules: false, 13 | chunkOrigins: false, 14 | children: false, 15 | errorDetails, 16 | warnings 17 | }); 18 | }; 19 | -------------------------------------------------------------------------------- /packages/workflow/scripts/test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable unicorn/no-process-exit */ 2 | // eslint-disable-next-line import/extensions 3 | const webpackConfig = require('../jest.config.js'); 4 | 5 | module.exports = { 6 | description: 'Run your tests', 7 | run: ({ settings }) => { 8 | const tester = webpackConfig(settings); 9 | process.env.NODE_ENV = 'test'; 10 | return tester.run().then((exitCode) => { 11 | if (Number.isInteger(exitCode)) { 12 | process.exit(exitCode); 13 | } 14 | return exitCode; 15 | }); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /packages/workflow/scripts/version.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const shell = require('shelljs'); 4 | const semver = require('semver'); 5 | const inquirer = require('inquirer'); 6 | const merge = require('lodash/merge'); 7 | const moment = require('moment'); 8 | const yargs = require('yargs'); 9 | const Logger = require('@availity/workflow-logger'); 10 | const settings = require('../settings'); 11 | 12 | // Add a new line character to end of contents 13 | function newLine(contents) { 14 | const lastChar = contents && contents.slice(-1) === '\n' ? '' : '\n'; 15 | return contents + lastChar; 16 | } 17 | 18 | function tag() { 19 | if (settings.isDistribution() && !settings.isDryRun()) { 20 | const message = settings.commitMessage() 21 | ? `${settings.commitMessage()} v${settings.version}` 22 | : `v${settings.version}`; 23 | shell.exec('git add --all'); 24 | shell.exec(`git commit -m "${message}"`); 25 | shell.exec(`git tag -a "${message}" -m "${message}"`); 26 | } else { 27 | Logger.message('Skipping git commands', 'Dry Run'); 28 | } 29 | 30 | return Promise.resolve(true); 31 | } 32 | 33 | function bump() { 34 | Logger.info('Starting version bump'); 35 | 36 | if (!settings.isDistribution()) { 37 | settings.version = moment().format(); 38 | } 39 | 40 | if (!settings.version) { 41 | return Promise.reject(new Error('version is undefined')); 42 | } 43 | 44 | const pkg = settings.pkg(); 45 | merge(pkg, { version: settings.version }); 46 | 47 | let contents = JSON.stringify(pkg, null, 2); 48 | contents = newLine(contents); 49 | 50 | // update package.pkg 51 | if (settings.isDistribution() && !settings.isDryRun()) { 52 | fs.writeFileSync(path.join(process.cwd(), 'package.json'), contents, 'utf8'); 53 | Logger.success('Finished version bump'); 54 | } else { 55 | Logger.message('Skipping version bump', 'Dry Run'); 56 | } 57 | 58 | return Promise.resolve(true); 59 | } 60 | 61 | function prompt() { 62 | if (!settings.isDistribution()) { 63 | return Promise.resolve(true); 64 | } 65 | 66 | const { version } = settings.pkg(); 67 | const parsed = semver.parse(version); 68 | const versionArg = yargs.argv.version || yargs.argv._[1]; 69 | 70 | if (versionArg) { 71 | let error; 72 | if (semver.valid(versionArg)) { 73 | if (semver.gt(versionArg, version)) { 74 | settings.version = versionArg; 75 | return Promise.resolve(true); 76 | } 77 | error = `must be greater than the current version [${version}].`; 78 | } else { 79 | error = `is not a valid semver version.`; 80 | } 81 | error = `Specified version [${versionArg}] ${error}`; 82 | Logger.error(error); 83 | throw new Error(error); 84 | } 85 | 86 | // regular release 87 | const simpleVersion = `${parsed.major}.${parsed.minor}.${parsed.patch}`; 88 | 89 | let choices = [ 90 | { 91 | name: `patch ( ${version} => ${semver.inc(simpleVersion, 'patch')} )`, 92 | value: semver.inc(simpleVersion, 'patch') 93 | }, 94 | { 95 | name: `minor ( ${version} => ${semver.inc(simpleVersion, 'minor')} )`, 96 | value: semver.inc(simpleVersion, 'minor') 97 | }, 98 | { 99 | name: `major ( ${version} => ${semver.inc(simpleVersion, 'major')} )`, 100 | value: semver.inc(simpleVersion, 'major') 101 | }, 102 | new inquirer.Separator(), 103 | { name: 'other', value: 'other' } 104 | ]; 105 | 106 | // pre-release 107 | if (parsed.prerelease && parsed.prerelease.length > 0) { 108 | choices = [ 109 | { 110 | name: `prerelease ( ${version} => ${semver.inc(version, 'prerelease', parsed[0])} )`, 111 | value: semver.inc(version, 'prerelease', parsed[0]) 112 | }, 113 | { 114 | name: `release ( ${version} => ${simpleVersion} )`, 115 | value: simpleVersion 116 | }, 117 | new inquirer.Separator(), 118 | { name: 'other', value: 'other' } 119 | ]; 120 | } 121 | 122 | const questions = [ 123 | { 124 | type: 'rawlist', 125 | name: 'bump', 126 | message: 'What type of version bump would you like to do?', 127 | choices 128 | }, 129 | { 130 | type: 'input', 131 | name: 'version', 132 | message: `version (current version is ${settings.pkg().version})`, 133 | when(answer) { 134 | return answer.bump === 'other'; 135 | }, 136 | filter(value) { 137 | return semver.clean(value); 138 | }, 139 | validate(value) { 140 | const valid = semver.valid(value); 141 | 142 | if (valid) { 143 | return true; 144 | } 145 | 146 | return 'Enter valid semver version. See https://docs.npmjs.com/misc/semver for more details.'; 147 | } 148 | } 149 | ]; 150 | 151 | return inquirer.prompt(questions).then((answers) => { 152 | settings.version = answers.bump !== 'other' ? answers.bump : answers.version; 153 | return settings.version; 154 | }); 155 | } 156 | 157 | module.exports = { 158 | tag, 159 | prompt, 160 | bump 161 | }; 162 | -------------------------------------------------------------------------------- /packages/workflow/tests/workflowConfigs.spec.js: -------------------------------------------------------------------------------- 1 | const settings = require('../settings'); 2 | const baseConfig = require('../webpack.config'); 3 | const prodConfig = require('../webpack.config.production'); 4 | const profileConfig = require('../webpack.config.profile'); 5 | 6 | const NOW = '2023-03-28T16:07:07.909Z'; 7 | 8 | describe('webpack configs', () => { 9 | beforeAll(async () => { 10 | jest.spyOn(global.Date.prototype, 'toJSON').mockImplementation(() => NOW); 11 | await settings.init(); 12 | }); 13 | 14 | it('sets up the default config with default settings', () => { 15 | expect(new Date().toJSON()).toBe(NOW); 16 | const config = baseConfig(settings); 17 | expect(config).toMatchSnapshot(); 18 | }); 19 | 20 | it('sets up the production config with default settings', () => { 21 | const config = prodConfig(settings); 22 | expect(config).toMatchSnapshot(); 23 | }); 24 | 25 | it('sets up the profile config with default settings', () => { 26 | const config = profileConfig(settings); 27 | expect(config).toMatchSnapshot(); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /packages/workflow/webpack.config.profile.js: -------------------------------------------------------------------------------- 1 | const DuplicatePackageCheckerPlugin = require('duplicate-package-checker-webpack-plugin'); 2 | const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); 3 | const merge = require('lodash/merge'); 4 | 5 | const buildProdConfig = require('./webpack.config.production'); 6 | 7 | process.noDeprecation = true; 8 | 9 | // Override user's potential browserslist config to ensure portal support here 10 | // This is needed in addition to config.target below so that browserslist queries inside 11 | // react-app-polyfill/stable and core-js provide everything needed 12 | process.env.BROWSERSLIST = 'defaults'; 13 | 14 | const plugin = (settings) => { 15 | // const baseConfig = buildBaseConfig(settings) 16 | const baseProdConfig = buildProdConfig(settings); 17 | 18 | const overrides = { 19 | plugins: [ 20 | ...baseProdConfig.plugins, 21 | 22 | new BundleAnalyzerPlugin({ 23 | analyzerMode: 'static', 24 | reportFilename: 'profile.html' 25 | }), 26 | 27 | new DuplicatePackageCheckerPlugin({ 28 | exclude(instance) { 29 | return instance.name === 'regenerator-runtime'; 30 | } 31 | }) 32 | ] 33 | }; 34 | 35 | return merge({}, baseProdConfig, overrides); 36 | }; 37 | 38 | module.exports = plugin; 39 | -------------------------------------------------------------------------------- /scripts/artifactory-check.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Instructions: 4 | # 5 | # - In package.json add: 6 | # "scripts": { "check:packages": "sh ./scripts/artifactory-check.sh" } 7 | # 8 | # - Run: 9 | # npm run check:packages 10 | # 11 | 12 | # error out if something fails 13 | set -e 14 | 15 | if grep -R --exclude='*.sh' --exclude-dir='node_modules' --include='yarn.lock' -e 'artifactory.availity' -e 'packages.availity' ./ 16 | then 17 | printf "\n" 18 | printf " (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) \n" 19 | printf " / ._. \ / ._. \ / ._. \ / ._. \ / ._. \ \n" 20 | printf " __\( Y )/__ __\( Y )/__ __\( Y )/__ __\( Y )/__ __\( Y )/__\n" 21 | printf " (_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)\n" 22 | printf " || E || || R || || R || || O || || R ||\n" 23 | printf " _.' \`-' '._ _.' \`-' '._ _.' \`-' '._ _.' \`-' '._ _.' \`-' '._\n" 24 | printf " (.-./\`-'\.-.)(.-./\`-\`\.-.)(.-./\`-\`\.-.)(.-./\`-'\.-.)(.-./\`-\`\.-.)\n" 25 | printf " \`-' \`-' \`-' \`-' \`-' \`-' \`-' \`-' \`-' \`-' \n" 26 | printf "\n\n" 27 | printf "\nOne of your packages contains a dependency from registry: artifactory.availity.com.\n" 28 | printf "Please correct this by running 'yarn nuke' and then 'yarn'.\n\n" 29 | exit 1 30 | else 31 | echo "Artifactory Check Passed" 32 | fi 33 | -------------------------------------------------------------------------------- /scripts/check-missing-deps.js: -------------------------------------------------------------------------------- 1 | const check = require('dependency-check'); 2 | const globby = require('globby'); 3 | 4 | const defaultPaths = ['packages/**/package.json', '!packages/**/node_modules/**/package.json']; 5 | 6 | const dependencyAnalyzer = async (args = {}) => { 7 | let { ignorePaths = [] } = args; 8 | let exitCode = 0; 9 | ignorePaths = Array.isArray(ignorePaths) ? ignorePaths : [ignorePaths]; 10 | const globs = [...defaultPaths, ...ignorePaths]; 11 | const files = await globby(globs, { 12 | absolute: true 13 | }); 14 | 15 | await Promise.all( 16 | files.map(async (file) => { 17 | const data = await check({ 18 | path: file, 19 | entries: [], 20 | noDefaultEntries: false, 21 | extensions: ['.js'] 22 | }); 23 | 24 | const pkg = data.package; 25 | const deps = data.used; 26 | 27 | const missing = check.missing(pkg, deps, { 28 | excludeDev: false, 29 | excludePeer: false, 30 | ignore: [ 31 | `${pkg.name}` // Can ignore packages that require their own compiled modules 32 | ] 33 | }); 34 | 35 | if (missing.length > 0) { 36 | // eslint-disable-next-line no-console 37 | console.log( 38 | `Run the following command to add missing packages to ${pkg.name}: 39 | yarn workspace ${pkg.name} add ${missing.map((m) => `${m}`).join(' ')} 40 | ` 41 | ); 42 | exitCode = 1; 43 | } 44 | }) 45 | ); 46 | // eslint-disable-next-line unicorn/no-process-exit 47 | process.exit(exitCode); 48 | }; 49 | 50 | dependencyAnalyzer(); 51 | -------------------------------------------------------------------------------- /scripts/check-version-strategy.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | const { accessSync, constants } = require('fs'); 3 | 4 | // Checks for existence of .yarn/versions directory 5 | // If not present then there is no release strategy and no need to run release scripts 6 | const checkVersionStrategy = () => { 7 | let exitCode = 1; 8 | const dir = '.yarn/versions'; 9 | 10 | console.log(process.cwd()); 11 | try { 12 | accessSync(dir, constants.F_OK); 13 | console.log(`${dir} exists`); 14 | exitCode = 0; 15 | } catch { 16 | console.log(`${dir} does not exist`); 17 | exitCode = 1; 18 | } 19 | 20 | // eslint-disable-next-line unicorn/no-process-exit 21 | process.exit(exitCode); 22 | }; 23 | 24 | checkVersionStrategy(); 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true, 4 | "allowJs": true, 5 | "baseUrl": "./example/", 6 | "paths": { 7 | "@/*": ["./project/app/*"] 8 | }, 9 | "target": "es5", 10 | "lib": ["dom", "dom.iterable", "esnext"], 11 | "skipLibCheck": true, 12 | "esModuleInterop": true, 13 | "allowSyntheticDefaultImports": true, 14 | "strict": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "module": "esnext", 17 | "jsx": "react", 18 | "moduleResolution": "node", 19 | "resolveJsonModule": true, 20 | "isolatedModules": true, 21 | "noEmit": true 22 | } 23 | } 24 | --------------------------------------------------------------------------------