From 2191294fa22237a09572b3893fd0e1603a0bd275 Mon Sep 17 00:00:00 2001 From: bhurtyalkritan Date: Tue, 2 Sep 2025 01:20:53 -0400 Subject: [PATCH 1/3] add github workflow for api to prevent prod fails --- .eslintrc.json | 33 ++++++++++ .github/workflows/ci.yml | 120 ++++++++++++++++++++++++++++++++++ .github/workflows/quality.yml | 103 +++++++++++++++++++++++++++++ .github/workflows/release.yml | 98 +++++++++++++++++++++++++++ README.md | 9 ++- package.json | 5 +- 6 files changed, 364 insertions(+), 4 deletions(-) create mode 100644 .eslintrc.json create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/quality.yml create mode 100644 .github/workflows/release.yml diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..6894000 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "env": { + "browser": false, + "commonjs": true, + "es6": true, + "node": true, + "mocha": true + }, + "extends": [ + "eslint:recommended" + ], + "parserOptions": { + "ecmaVersion": 2020, + "sourceType": "module" + }, + "rules": { + "no-console": "warn", + "no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], + "no-var": "error", + "prefer-const": "error", + "eqeqeq": "error", + "no-trailing-spaces": "error", + "semi": ["error", "always"], + "quotes": ["error", "single", { "allowTemplateLiterals": true }], + "indent": ["error", 4], + "no-multiple-empty-lines": ["error", { "max": 2 }] + }, + "ignorePatterns": [ + "node_modules/", + "dist/", + "build/" + ] +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c227b58 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,120 @@ +name: CI/CD Pipeline + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main, develop ] + +jobs: + test: + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:13 + env: + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + POSTGRES_DB: test_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + strategy: + matrix: + node-version: [16.x, 18.x, 20.x] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Create test environment file + run: | + cat > config.env << EOF + POSTGRES_URI=postgresql://postgres:postgres@localhost:5432/test_db + ATLAS_URI_LOGS=mongodb://localhost:27017/test_logs + EOF + + - name: Run linting (if eslint config exists) + run: | + if [ -f ".eslintrc.js" ] || [ -f ".eslintrc.json" ] || [ -f "package.json" ] && grep -q "eslint" package.json; then + npm run lint + else + echo "No ESLint configuration found, skipping linting" + fi + continue-on-error: true + + - name: Run tests + run: npm run mocha + env: + NODE_ENV: test + POSTGRES_URI: postgresql://postgres:postgres@localhost:5432/test_db + + - name: Run basic server test + run: npm test + env: + NODE_ENV: test + POSTGRES_URI: postgresql://postgres:postgres@localhost:5432/test_db + + security: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Use Node.js 18.x + uses: actions/setup-node@v4 + with: + node-version: 18.x + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run security audit + run: npm audit --audit-level=moderate + + - name: Check for outdated packages + run: npm outdated + continue-on-error: true + + build: + runs-on: ubuntu-latest + needs: [test, security] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Use Node.js 18.x + uses: actions/setup-node@v4 + with: + node-version: 18.x + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Check if server starts successfully + run: | + timeout 30s node server.js & + sleep 10 + curl -f http://localhost:3000/app/ || exit 1 + env: + NODE_ENV: production + POSTGRES_URI: postgresql://dummy:dummy@localhost:5432/dummy diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml new file mode 100644 index 0000000..1db0f80 --- /dev/null +++ b/.github/workflows/quality.yml @@ -0,0 +1,103 @@ +name: Code Quality & Dependencies + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main, develop ] + workflow_dispatch: # Allow manual triggering + +jobs: + dependency-review: + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Dependency Review + uses: actions/dependency-review-action@v4 + + update-dependencies: + runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Use Node.js 18.x + uses: actions/setup-node@v4 + with: + node-version: 18.x + cache: 'npm' + + - name: Update dependencies + run: | + npm update + npm audit fix --force + continue-on-error: true + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: 'chore: update dependencies' + title: 'chore: automated dependency updates' + body: | + Automated dependency updates created by GitHub Actions. + + Please review the changes before merging. + branch: chore/dependency-updates + delete-branch: true + + code-quality: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Use Node.js 18.x + uses: actions/setup-node@v4 + with: + node-version: 18.x + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Check for console.log statements + run: | + if grep -r "console\.log" --include="*.js" . --exclude-dir=node_modules --exclude-dir=.git; then + echo "Warning: Found console.log statements in code" + exit 1 + else + echo "No console.log statements found" + fi + continue-on-error: true + + - name: Check for TODO comments + run: | + echo "TODO items found in codebase:" + grep -r "TODO\|FIXME\|HACK" --include="*.js" . --exclude-dir=node_modules --exclude-dir=.git || echo "No TODO items found" + continue-on-error: true + + - name: Check file structure + run: | + echo "Checking for proper file structure..." + if [ ! -f "package.json" ]; then + echo "Error: package.json not found" + exit 1 + fi + if [ ! -f "server.js" ]; then + echo "Error: server.js not found" + exit 1 + fi + if [ ! -d "routes" ]; then + echo "Warning: routes directory not found" + fi + echo "File structure check completed" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..815414b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,98 @@ +name: Release & Deploy + +on: + push: + tags: + - 'v*.*.*' + release: + types: [published] + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Use Node.js 18.x + uses: actions/setup-node@v4 + with: + node-version: 18.x + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run tests + run: npm run mocha + + - name: Create release notes + run: | + echo "# Release Notes" > release-notes.md + echo "" >> release-notes.md + echo "## Changes in this release:" >> release-notes.md + git log --pretty=format:"- %s" $(git describe --tags --abbrev=0 HEAD^)..HEAD >> release-notes.md + + - name: Upload release notes + uses: actions/upload-artifact@v4 + with: + name: release-notes + path: release-notes.md + + docker-build: + runs-on: ubuntu-latest + needs: release + if: github.event_name == 'release' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Create Dockerfile + run: | + cat > Dockerfile << 'EOF' + FROM node:18-alpine + + WORKDIR /app + + # Copy package files + COPY package*.json ./ + + # Install dependencies + RUN npm ci --only=production + + # Copy source code + COPY . . + + # Expose port + EXPOSE 3000 + + # Create non-root user + RUN addgroup -g 1001 -S nodejs + RUN adduser -S nodejs -u 1001 + + # Change ownership of the app directory + RUN chown -R nodejs:nodejs /app + USER nodejs + + # Start the application + CMD ["node", "server.js"] + EOF + + - name: Build Docker image + run: | + docker build -t polaris-api:${{ github.ref_name }} . + docker build -t polaris-api:latest . + + # Uncomment and configure these steps if you want to push to a registry + # - name: Log in to Docker Hub + # uses: docker/login-action@v3 + # with: + # username: ${{ secrets.DOCKER_USERNAME }} + # password: ${{ secrets.DOCKER_PASSWORD }} + + # - name: Push to Docker Hub + # run: | + # docker push polaris-api:${{ github.ref_name }} + # docker push polaris-api:latest diff --git a/README.md b/README.md index 190da94..3050d61 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ -# API +# Polaris API -The API backend, implemented with Node.js. +[![CI/CD Pipeline](https://github.com/polaris-maps/polaris-api/actions/workflows/ci.yml/badge.svg)](https://github.com/polaris-maps/polaris-api/actions/workflows/ci.yml) +[![Code Quality](https://github.com/polaris-maps/polaris-api/actions/workflows/quality.yml/badge.svg)](https://github.com/polaris-maps/polaris-api/actions/workflows/quality.yml) -Exposes the data in the database(s). \ No newline at end of file +The API backend for Polaris Maps, implemented with Node.js and Express. + +Exposes the data in the database(s) and provides accessibility-focused navigation services. \ No newline at end of file diff --git a/package.json b/package.json index d8f057c..b2c1de4 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,9 @@ "test": "(timeout --signal=SIGINT 15 node server.js --port=5001; exit 0) & sleep 5s && curl -s http://localhost:5001/app/ && sleep 5s", "run": "nodemon server.js", "start": "nodemon server.js", - "mocha": "mocha test/*.test.js --exit" + "mocha": "mocha test/*.test.js --exit", + "lint": "eslint . --ext .js", + "lint:fix": "eslint . --ext .js --fix" }, "author": "Team Polaris", "license": "GPL-3.0-or-later", @@ -30,6 +32,7 @@ "devDependencies": { "chai": "^4.3.10", "chai-http": "^4.4.0", + "eslint": "^8.57.0", "mocha": "^10.2.0" } } From 4d4e4e3d33c6f80c440968d605ba8ebc0572643d Mon Sep 17 00:00:00 2001 From: bhurtyalkritan Date: Tue, 2 Sep 2025 01:22:42 -0400 Subject: [PATCH 2/3] npm install --- package-lock.json | 998 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 993 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index b5c4870..7d5fa82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "devDependencies": { "chai": "^4.3.10", "chai-http": "^4.4.0", + "eslint": "^8.57.0", "mocha": "^10.2.0" } }, @@ -725,6 +726,157 @@ "kuler": "^2.0.0" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/@mongodb-js/saslprep": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.5.tgz", @@ -734,6 +886,44 @@ "sparse-bitfield": "^3.0.3" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@smithy/abort-controller": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.2.0.tgz", @@ -1343,6 +1533,13 @@ "@types/webidl-conversions": "*" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -1360,6 +1557,46 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -1618,6 +1855,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -1939,6 +2186,21 @@ "node": ">= 0.10" } }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1971,6 +2233,13 @@ "node": ">=6" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2022,6 +2291,19 @@ "node": ">=0.3.1" } }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/dotenv": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", @@ -2068,12 +2350,290 @@ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", "engines": { - "node": ">=0.8.0" + "node": ">=0.10.0" } }, "node_modules/etag": { @@ -2155,6 +2715,27 @@ "winston": ">=3.x <4" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", @@ -2182,11 +2763,34 @@ "fxparser": "src/cli/cli.js" } }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fecha": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2240,6 +2844,28 @@ "flat": "cli.js" } }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, "node_modules/fn.name": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", @@ -2374,6 +3000,29 @@ "node": ">= 6" } }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -2475,11 +3124,48 @@ } ] }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -2581,6 +3267,16 @@ "node": ">=0.12.0" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -2618,6 +3314,13 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -2630,6 +3333,27 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/kareem": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", @@ -2638,11 +3362,35 @@ "node": ">=12.0.0" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2663,6 +3411,13 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -3090,6 +3845,13 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -3223,6 +3985,24 @@ "node": ">=4" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -3253,6 +4033,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -3279,6 +4072,16 @@ "node": ">=0.10.0" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -3420,6 +4223,16 @@ "node": ">=0.10.0" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -3464,6 +4277,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -3536,6 +4370,68 @@ "node": ">=0.10.0" } }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -3644,6 +4540,29 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -3880,6 +4799,13 @@ "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3932,6 +4858,19 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "optional": true }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -3941,6 +4880,19 @@ "node": ">=4" } }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -3966,6 +4918,16 @@ "node": ">= 0.8" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -4016,6 +4978,22 @@ "node": ">=12" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/winston": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/winston/-/winston-3.8.2.tgz", @@ -4111,6 +5089,16 @@ "node": ">= 6.4.0" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", From 930db8e04c7b81df3023b942dcbc71d3b72b881f Mon Sep 17 00:00:00 2001 From: bhurtyalkritan Date: Mon, 15 Sep 2025 16:24:36 -0500 Subject: [PATCH 3/3] add documentation for backend --- docs/README.md | 28 +++ docs/api/accessibility.md | 144 +++++++++++++ docs/api/buildings.md | 156 ++++++++++++++ docs/api/navigation.md | 147 +++++++++++++ docs/development/contributing.md | 226 +++++++++++++++++++ docs/development/database.md | 270 +++++++++++++++++++++++ docs/development/testing.md | 276 ++++++++++++++++++++++++ docs/getting-started/core-concepts.md | 92 ++++++++ docs/getting-started/first-api-call.md | 73 +++++++ docs/getting-started/installation.md | 91 ++++++++ docs/navigation/building-to-building.md | 200 +++++++++++++++++ docs/navigation/obstacles.md | 212 ++++++++++++++++++ docs/navigation/path-to-path.md | 153 +++++++++++++ 13 files changed, 2068 insertions(+) create mode 100644 docs/README.md create mode 100644 docs/api/accessibility.md create mode 100644 docs/api/buildings.md create mode 100644 docs/api/navigation.md create mode 100644 docs/development/contributing.md create mode 100644 docs/development/database.md create mode 100644 docs/development/testing.md create mode 100644 docs/getting-started/core-concepts.md create mode 100644 docs/getting-started/first-api-call.md create mode 100644 docs/getting-started/installation.md create mode 100644 docs/navigation/building-to-building.md create mode 100644 docs/navigation/obstacles.md create mode 100644 docs/navigation/path-to-path.md diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..7edb172 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,28 @@ +# Polaris API Documentation + +**Accessibility-focused navigation API for wheelchair and mobility device routing.** + +## Quick Start +1. [Installation](getting-started/installation.md) - Get up and running +2. [First API Call](getting-started/first-api-call.md) - Test the system +3. [Core Concepts](getting-started/core-concepts.md) - Understand the basics + +## API Reference +- [Navigation Endpoints](api/navigation.md) - Core routing functionality +- [Building Management](api/buildings.md) - Location and door data +- [Accessibility Data](api/accessibility.md) - Issues, ramps, barriers + +## Navigation System +- [Path-to-Path Routing](navigation/path-to-path.md) - Point A to Point B navigation +- [Building-to-Building](navigation/building-to-building.md) - Optimized door selection +- [Obstacle Avoidance](navigation/obstacles.md) - Real-time barrier handling + +## Development +- [Testing Guide](development/testing.md) - Run and write tests +- [Database Schema](development/database.md) - Data models and relationships +- [Contributing](development/contributing.md) - Code standards and workflow + +## System Overview +**Tech Stack:** Node.js + Express + PostgreSQL + MongoDB + OpenRouteService +**Mission:** Enable independent navigation for users with mobility challenges +**Key Features:** Wheelchair routing, real-time obstacles, community-driven data diff --git a/docs/api/accessibility.md b/docs/api/accessibility.md new file mode 100644 index 0000000..68df7e2 --- /dev/null +++ b/docs/api/accessibility.md @@ -0,0 +1,144 @@ +# Accessibility Data Management + +## Indoor Issues (Accessibility Problems) + +### GET /app/indoorIssue/all +List all reported accessibility issues. + +**Response:** +```json +[{ + "issue_id": 1, + "location": "Library Main Entrance", + "latitude": 35.910000, + "longitude": -79.050000, + "description": "Automatic door button not working", + "status": "open", + "categories": ["door", "automatic"], + "votes": 5, + "datetimeOpen": "2024-09-01T10:30:00Z" +}] +``` + +### POST /app/indoorIssue/filtered +Filter issues by categories. + +**Request:** +```json +{ + "category": ["elevator", "ramp", "door"] +} +``` + +### POST /app/indoorIssue/add +Report new accessibility issue. + +**Request:** +```json +{ + "location": "Student Union South Entrance", + "latitude": 35.909500, + "longitude": -79.051000, + "description": "Ramp is blocked by construction materials", + "categories": ["ramp", "construction"], + "avoidPolygon": { + "type": "Polygon", + "coordinates": [[ + [-79.051100, 35.909400], + [-79.050900, 35.909400], + [-79.050900, 35.909600], + [-79.051100, 35.909600], + [-79.051100, 35.909400] + ]] + } +} +``` + +### PATCH /app/indoorIssue/update/:id +Update issue status or details. + +**Request:** +```json +{ + "status": "resolved", + "datetimeClosed": "2024-09-15T14:20:00Z" +} +``` + +## User Management + +### GET /app/user/all +List user profiles. + +### POST /app/user/add +Create user profile. + +**Request:** +```json +{ + "favoriteLocations": ["Library", "Student Union"], + "indoorIssueInteractions": [1, 3, 7], + "indoorIssuesCreated": [5, 12] +} +``` + +## Logging Endpoints + +### POST /app/log/add +Submit client-side error logs. + +**Request:** +```json +{ + "log_timestamp": "2024-09-11T15:30:00Z", + "log_level": "error", + "log_message": "Navigation request failed", + "file_name": "navigation.js", + "line_number": 45, + "column_number": 12, + "additional": "User agent: Mozilla/5.0..." +} +``` + +### GET /app/clientlog/all +Retrieve client error logs (admin only). + +## Issue Categories + +### Standard Categories +- **door** - Door accessibility problems +- **elevator** - Elevator issues +- **ramp** - Wheelchair ramp problems +- **narrow** - Passages too narrow +- **construction** - Construction barriers +- **automatic** - Automatic door failures +- **stairs** - Stair-related barriers + +### Issue Status Values +- **open** - Newly reported, needs attention +- **in-progress** - Being addressed +- **resolved** - Fixed/completed +- **permanent** - Long-term or design limitation + +## Obstacle Avoidance Integration + +Issues with `avoidPolygon` data automatically become routing obstacles: + +```json +{ + "avoidPolygon": { + "type": "Polygon", + "coordinates": [[ + [-79.051000, 35.909000], + [-79.050000, 35.909000], + [-79.050000, 35.910000], + [-79.051000, 35.910000], + [-79.051000, 35.909000] + ]] + } +} +``` + +Navigation system queries issues by category and applies polygons as routing constraints: +- `avoid_obstacles: ["construction"]` → Avoids all construction-related polygons +- Real-time updates as issues are added/resolved diff --git a/docs/api/buildings.md b/docs/api/buildings.md new file mode 100644 index 0000000..75421a2 --- /dev/null +++ b/docs/api/buildings.md @@ -0,0 +1,156 @@ +# Building & Location Management + +## Building Endpoints + +### GET /app/building/all +List all buildings/locations in the system. + +**Response:** +```json +[{ + "location_id": 1, + "full_name": "Davis Library", + "abbreviation": "DLIB", + "defaultLatitude": 35.910000, + "defaultLongitude": -79.050000, + "campus_id": 1, + "geo_address": "208 Raleigh St, Chapel Hill, NC" +}] +``` + +### GET /app/building/:id +Get specific building details. + +### POST /app/building/add +Create new building entry. + +**Request:** +```json +{ + "full_name": "New Academic Building", + "abbreviation": "NAB", + "defaultLatitude": 35.910500, + "defaultLongitude": -79.049500, + "campus_id": 1, + "geo_address": "123 Campus Drive" +} +``` + +## Door Endpoints + +### GET /app/door/all +List all building doors with accessibility info. + +**Response:** +```json +[{ + "door_id": 1, + "node_id": "osm_123456", + "latitude": 35.910100, + "longitude": -79.050100, + "building_id": 1, + "is_indoor": false, + "is_emergency": false, + "is_service": false, + "automatic": true, + "stairs": false +}] +``` + +### GET /app/door/filtered/:buildingId +Get doors for specific building. + +### POST /app/door/add +Add single door entry. + +**Request:** +```json +{ + "node_id": "osm_789012", + "latitude": 35.910200, + "longitude": -79.050200, + "building_id": 1, + "is_indoor": false, + "is_emergency": false, + "is_service": false, + "automatic": true, + "stairs": false +} +``` + +### POST /app/door/add/multiple +Batch door creation with transaction safety. + +**Request:** +```json +[{ + "node_id": "osm_111", + "latitude": 35.910300, + "longitude": -79.050300, + "building_id": 1, + "automatic": false, + "stairs": true +}, { + "node_id": "osm_222", + "latitude": 35.910400, + "longitude": -79.050400, + "building_id": 1, + "automatic": true, + "stairs": false +}] +``` + +## Ramp Endpoints + +### GET /app/ramp/all +List all wheelchair ramps. + +**Response:** +```json +[{ + "ramp_id": 1, + "latitude": 35.910000, + "longitude": -79.050000, + "building": "Library" +}] +``` + +### POST /app/ramp/add +Add single ramp location. + +**Request:** +```json +{ + "latitude": 35.910050, + "longitude": -79.050050, + "building": "Student Union" +} +``` + +### POST /app/ramp/add/multiple +Batch ramp creation. + +## Door Accessibility Attributes + +### Key Fields +- **automatic**: `true` for push-button or motion-sensor doors +- **stairs**: `true` if stairs required to reach door +- **is_emergency**: `true` for emergency exits (may be locked) +- **is_service**: `true` for service entrances +- **is_indoor**: `true` for interior doors + +### Filtering Logic +Navigation system automatically applies filters: +- `exclude_stairs: true` → Only doors with `stairs: false` +- `require_automatic: true` → Only doors with `automatic: true` +- Emergency doors excluded from routing by default + +## Database Relationships +``` +Location (buildings) + ↓ (1:many) +Door (entrances/exits) + +Ramp (accessibility features) + → building (string reference) +``` diff --git a/docs/api/navigation.md b/docs/api/navigation.md new file mode 100644 index 0000000..0dc9242 --- /dev/null +++ b/docs/api/navigation.md @@ -0,0 +1,147 @@ +# Navigation Endpoints + +## POST /app/route +**Primary routing endpoint with full accessibility support** + +### Request +```json +{ + "coordinates": [[-79.045848, 35.904798], [-79.053061, 35.909575]], + "avoid_features": ["steps"], + "avoid_obstacles": ["construction", "narrow"], + "restrictions": { + "surface_type": "cobblestone:flattened", + "track_type": "grade1", + "smoothness_type": "good", + "maximum_incline": 6 + }, + "source_building": "Library", // Optional: auto door selection + "destination_building": "Student Union", // Optional: auto door selection + "exclude_stairs": true, // Optional: filter door selection + "require_automatic": false // Optional: require automatic doors +} +``` + +### Response +```json +{ + "type": "FeatureCollection", + "features": [{ + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [[-79.045848, 35.904798], ...] + }, + "properties": { + "segments": [{ + "distance": 245.7, + "duration": 180.3, + "steps": [{ + "distance": 125.5, + "duration": 90.2, + "type": 0, + "instruction": "Head southwest", + "name": "Campus Drive", + "way_points": [0, 15] + }] + }] + } + }] +} +``` + +## POST /app/route/minimize-door-distance +**Optimized building-to-building routing with door selection** + +### Request +```json +{ + "source": "Library", + "destination": "Student Union", + "maximum_number_results": 1, + "exclude_stairs": true, + "require_automatic": false +} +``` + +### Response +```json +{ + "SourceDoorCoordinates": [-79.045900, 35.904850], + "DestinationDoorCoordinates": [-79.053100, 35.909600], + "distance": 450.2 +} +``` + +## POST /app/route/to-door +**Find best building entrance from current location** + +### Request +```json +{ + "longitude": -79.045848, + "latitude": 35.904798, + "destination": "Library", + "exclude_stairs": true, + "require_automatic": false +} +``` + +### Response +```json +{ + "start": { + "longitude": -79.045848, + "latitude": 35.904798 + }, + "end": { + "longitude": -79.045900, + "latitude": 35.904850, + "door_id": 123, + "automatic": true, + "stairs": false + }, + "duration": 120.5 +} +``` + +## GET /app/route/hardcodedtest +**Test endpoint with predefined route** + +Returns sample route data for testing integration. + +## Error Responses + +### No Accessible Route +```json +{ + "message": "Route impossible. No accessible path found.", + "status": 404 +} +``` + +### Missing Building Doors +```json +{ + "message": "Route impossible. (No destination doors exist with those attributes.)", + "status": 500 +} +``` + +### Invalid Parameters +```json +{ + "message": "Missing required coordinates", + "status": 400 +} +``` + +## Route Calculation Process +1. **Coordinate validation** - Ensure valid lat/lng pairs +2. **Building detection** - Check if coordinates near buildings +3. **Door filtering** - Apply accessibility constraints +4. **Matrix calculation** - Compute door-to-door distances +5. **Optimization** - Select best door pair +6. **Obstacle integration** - Apply real-time barriers +7. **External routing** - Use OpenRouteService for path +8. **Response formatting** - Return GeoJSON result diff --git a/docs/development/contributing.md b/docs/development/contributing.md new file mode 100644 index 0000000..d353748 --- /dev/null +++ b/docs/development/contributing.md @@ -0,0 +1,226 @@ +# Contributing to Polaris API + +## Quick Start + +### 1. Setup +```bash +git clone https://github.com/polaris-maps/polaris-api.git +cd polaris-api +npm install +cp config.env.example config.env +# Add your database credentials and API keys +``` + +### 2. Make Your Changes +```bash +git checkout -b feature/cool-new-thing +# Write some code, add tests +npm run lint && npm test +git commit -m "add cool new thing" +git push origin feature/cool-new-thing +``` + +### 3. Create Pull Request +- Make sure CI/CD passes ✅ +- That's it! We'll review and merge + +## Coding Standards + +### ESLint Configuration +We use ESLint for code quality enforcement: +```javascript +{ + "rules": { + "no-console": "warn", // Avoid console.log in production + "no-unused-vars": "error", // Remove unused variables + "prefer-const": "error", // Use const when possible + "semi": ["error", "always"], // Require semicolons + "quotes": ["error", "single"], // Use single quotes + "indent": ["error", 4] // 4-space indentation + } +} +``` + +### Code Style Guidelines + +#### Function Naming +```javascript +// Use descriptive, camelCase names +function calculateOptimalDoorPair(sourceDoors, destinationDoors) { + // Implementation +} + +// Async functions should be clear +async function queryAccessibleDoors(buildingId, filters) { + // Implementation +} +``` + +#### Error Handling +```javascript +// Consistent error handling pattern +router.post('/app/endpoint', async (req, res, next) => { + try { + const result = await someAsyncOperation(); + res.status(200).json({ + message: "Success message", + data: result + }); + } catch (error) { + next(error); // Pass to global error handler + } +}); +``` + +#### Database Queries +```javascript +// Use parameterized queries (never string concatenation) +const { rows } = await pool.query( + 'SELECT * FROM Door WHERE building_id = $1 AND stairs = $2', + [buildingId, false] +); + +// Consistent response format +res.status(200).json({ + message: "Successfully retrieved doors", + data: rows +}); +``` + +#### Comments and Documentation +```javascript +/** + * Calculates optimal door pair for building-to-building navigation + * @param {Array} sourceDoors - Available source building doors + * @param {Array} destDoors - Available destination building doors + * @param {Object} filters - Accessibility filters (stairs, automatic) + * @returns {Promise} Optimal door coordinates and distance + */ +async function selectOptimalDoors(sourceDoors, destDoors, filters) { + // Implementation with clear inline comments +} +``` + +## Testing Requirements + +### Test Coverage +All new features must include tests: +```javascript +describe('New Feature', () => { + it('should handle valid input', async () => { + const response = await chai.request(app) + .post('/app/new-endpoint') + .send(validTestData); + + response.should.have.status(200); + response.body.should.have.property('data'); + }); + + it('should reject invalid input', async () => { + const response = await chai.request(app) + .post('/app/new-endpoint') + .send(invalidTestData); + + response.should.have.status(400); + }); +}); +``` + +### Database Tests +```javascript +describe('Database Operations', () => { + beforeEach(async () => { + // Set up test data + await pool.query('INSERT INTO test_table...'); + }); + + afterEach(async () => { + // Clean up test data + await pool.query('DELETE FROM test_table WHERE...'); + }); +}); +``` + +## Documentation Updates + +### API Documentation +When adding/modifying endpoints, update: +- `docs/api/` - Endpoint documentation +- `docs/navigation/` - Navigation-specific features +- Code comments with JSDoc format + +### README Updates +Update relevant README files when changing: +- Installation procedures +- Environment variables +- Dependencies + +## Pull Requests + +Keep it simple: +- Descriptive title (what you did) +- Brief description in the PR +- Make sure CI/CD passes +- Reference any related issues + +We'll handle the rest during review! + +## Issue Reporting + +### Bug Reports +```markdown +## Bug Description +Clear description of the issue. + +## Steps to Reproduce +1. Make request to `/app/route` +2. Use coordinates `[...]` +3. Observe error response + +## Expected Behavior +Should return valid route. + +## Actual Behavior +Returns 500 error. + +## Environment +- Node.js version: 18.x +- OS: macOS/Linux/Windows +- Database: PostgreSQL 13 +``` + +### Feature Requests +```markdown +## Feature Description +Describe the new accessibility feature needed. + +## Use Case +Explain why this feature would benefit wheelchair users. + +## Proposed Solution +Suggest implementation approach if known. + +## Acceptance Criteria +- [ ] Feature works with existing routing +- [ ] Includes proper error handling +- [ ] Has test coverage +- [ ] Documentation is updated +``` + +## What We Look For + +- Does it help wheelchair users? 🎯 +- Does it break anything? 🔧 +- Are there tests? ✅ +- Does CI/CD pass? 🚀 + +That's about it! We're pretty chill. + +## Release Schedule + +We do monthly releases using weekly branches: +- `develop/2025.40` (week 40 of 2025) +- `develop/2025.41` (week 41 of 2025) +- etc. + +Features merge to the current develop branch, then we cut releases monthly. diff --git a/docs/development/database.md b/docs/development/database.md new file mode 100644 index 0000000..dd76b96 --- /dev/null +++ b/docs/development/database.md @@ -0,0 +1,270 @@ +# Database Schema & Models + +## PostgreSQL Tables + +### Location (Buildings) +```sql +CREATE TABLE Location ( + location_id SERIAL PRIMARY KEY, + full_name VARCHAR(255) NOT NULL, + abbreviation VARCHAR(50), + defaultLatitude DECIMAL(10,8), + defaultLongitude DECIMAL(11,8), + campus_id INTEGER, + geo_address TEXT +); +``` + +**Purpose:** Central registry of all campus buildings and locations. + +### Door (Building Entrances) +```sql +CREATE TABLE Door ( + door_id SERIAL PRIMARY KEY, + node_id VARCHAR(50), -- OpenStreetMap node reference + latitude DECIMAL(10,8) NOT NULL, + longitude DECIMAL(11,8) NOT NULL, + building_id INTEGER REFERENCES Location(location_id), + is_indoor BOOLEAN DEFAULT FALSE, + is_emergency BOOLEAN DEFAULT FALSE, + is_service BOOLEAN DEFAULT FALSE, + automatic BOOLEAN DEFAULT FALSE, -- Push-button or motion sensor + stairs BOOLEAN DEFAULT FALSE -- Requires stairs to access +); +``` + +**Purpose:** Accessibility data for all building entrances/exits. + +### Ramp (Wheelchair Access) +```sql +CREATE TABLE Ramp ( + ramp_id SERIAL PRIMARY KEY, + latitude DECIMAL(10,8) NOT NULL, + longitude DECIMAL(11,8) NOT NULL, + building VARCHAR(255) -- Building association +); +``` + +**Purpose:** Wheelchair ramp locations for accessible routing. + +### Issue (Accessibility Problems) +```sql +CREATE TABLE Issue ( + issue_id SERIAL PRIMARY KEY, + location VARCHAR(255), + latitude DECIMAL(10,8), + longitude DECIMAL(11,8), + description TEXT, + status VARCHAR(50) DEFAULT 'open', + datetimeOpen TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + datetimeClosed TIMESTAMP, + datetimePermanent TIMESTAMP, + votes INTEGER DEFAULT 0, + image TEXT, -- Image URL + categories TEXT[], -- Array of issue types + qna JSONB, -- Questions and answers + avoidPolygon JSONB -- GeoJSON polygon for routing +); +``` + +**Purpose:** Community-reported accessibility barriers and obstacles. + +### Profile (User Data) +```sql +CREATE TABLE Profile ( + profile_id SERIAL PRIMARY KEY, + favoriteLocations TEXT[], -- Array of preferred buildings + indoorIssueInteractions INTEGER[], -- Issues user has voted on + indoorIssuesCreated INTEGER[] -- Issues reported by user +); +``` + +**Purpose:** User preferences and interaction history. + +### ClientLog (Error Tracking) +```sql +CREATE TABLE ClientLog ( + client_log_id SERIAL PRIMARY KEY, + log_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + log_level VARCHAR(20), -- error, warning, info + log_message TEXT, + file_name VARCHAR(255), + line_number INTEGER, + column_number INTEGER, + additional TEXT -- Extra debugging info +); +``` + +**Purpose:** Client-side error reporting and debugging. + +### ApiLog (Request Logging) +```sql +CREATE TABLE ApiLog ( + api_log_id SERIAL PRIMARY KEY, + timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + method VARCHAR(10), + url TEXT, + status_code INTEGER, + response_time INTEGER, + user_agent TEXT, + ip_address INET +); +``` + +**Purpose:** Server-side request logging (if not using MongoDB). + +## MongoDB Collections + +### api (Request Logs) +```javascript +{ + _id: ObjectId, + timestamp: ISODate, + level: "info", + message: "HTTP POST /app/route", + meta: { + req: { + method: "POST", + url: "/app/route", + headers: {...}, + body: {...} + }, + res: { + statusCode: 200, + responseTime: 450 + } + } +} +``` + +**Purpose:** Structured HTTP request logging via Winston. + +## Entity Relationships + +### Core Relationships +``` +Location (1) ←→ (many) Door + ↓ +Building references in: +- Ramp.building (string) +- Issue.location (string) +- Profile.favoriteLocations (array) +``` + +### Data Flow +``` +Client Request + ↓ +Route Calculation + ↓ +Query Doors by building_id + ↓ +Filter by accessibility attributes + ↓ +Query Issues by categories + ↓ +Apply obstacle avoidance polygons + ↓ +Return optimized route +``` + +## Data Types & Constraints + +### Coordinate Precision +```sql +-- High precision for GPS coordinates +latitude DECIMAL(10,8) -- 99.99999999 (8 decimal places) +longitude DECIMAL(11,8) -- -999.99999999 (8 decimal places) +``` + +### Boolean Accessibility Flags +```sql +-- Door accessibility attributes +automatic BOOLEAN DEFAULT FALSE -- Push-button activation +stairs BOOLEAN DEFAULT FALSE -- Ground-level access +is_emergency BOOLEAN DEFAULT FALSE -- Regular vs emergency exit +is_service BOOLEAN DEFAULT FALSE -- Public vs service entrance +``` + +### Array Storage +```sql +-- PostgreSQL array types +categories TEXT[] -- ['door', 'elevator', 'ramp'] +favoriteLocations TEXT[] -- ['Library', 'Student Union'] +indoorIssueInteractions INTEGER[] -- [1, 5, 12, 23] +``` + +### JSON Storage +```sql +-- Complex structured data +qna JSONB -- Questions and answers about issues +avoidPolygon JSONB -- GeoJSON polygon for obstacle avoidance +``` + +## Indexing Strategy + +### Geospatial Indexes +```sql +-- Speed up coordinate-based queries +CREATE INDEX idx_door_coordinates ON Door (latitude, longitude); +CREATE INDEX idx_issue_coordinates ON Issue (latitude, longitude); +CREATE INDEX idx_ramp_coordinates ON Ramp (latitude, longitude); +``` + +### Foreign Key Indexes +```sql +-- Optimize building-door relationships +CREATE INDEX idx_door_building ON Door (building_id); +``` + +### Array Indexes +```sql +-- Speed up category-based issue filtering +CREATE INDEX idx_issue_categories ON Issue USING GIN (categories); +``` + +## Sample Data + +### Location Example +```sql +INSERT INTO Location (full_name, abbreviation, defaultLatitude, defaultLongitude, campus_id) +VALUES ('Davis Library', 'DLIB', 35.910000, -79.050000, 1); +``` + +### Door Example +```sql +INSERT INTO Door (latitude, longitude, building_id, automatic, stairs, is_emergency) +VALUES (35.910100, -79.050100, 1, true, false, false); +``` + +### Issue Example +```sql +INSERT INTO Issue (location, latitude, longitude, description, categories, avoidPolygon) +VALUES ( + 'Library Main Entrance', + 35.910000, -79.050000, + 'Automatic door button not working', + ARRAY['door', 'automatic'], + '{"type":"Polygon","coordinates":[[[-79.050100,35.909900],[-79.049900,35.909900],[-79.049900,35.910100],[-79.050100,35.910100],[-79.050100,35.909900]]]}'::jsonb +); +``` + +## Migration Strategy + +### Version Control +```sql +-- Track schema versions +CREATE TABLE schema_migrations ( + version VARCHAR(50) PRIMARY KEY, + applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +``` + +### Safe Migrations +```sql +-- Add columns safely +ALTER TABLE Door ADD COLUMN IF NOT EXISTS width_cm INTEGER; + +-- Add indexes concurrently +CREATE INDEX CONCURRENTLY idx_issue_status ON Issue (status); +``` diff --git a/docs/development/testing.md b/docs/development/testing.md new file mode 100644 index 0000000..f30c540 --- /dev/null +++ b/docs/development/testing.md @@ -0,0 +1,276 @@ +# Testing Guide + +## Running Tests + +### Full Test Suite +```bash +npm run mocha +``` + +### Server Health Check +```bash +npm test +# Starts server on port 5001, tests /app/ endpoint, then stops +``` + +### Individual Test Files +```bash +npx mocha test/adaptiveNav.test.js --exit +``` + +## Test Structure + +### Framework Setup +```javascript +const chai = require('chai'); +const chaiHttp = require('chai-http'); +chai.use(chaiHttp); +chai.should(); +``` + +### Test Categories + +#### 1. Basic Route Testing +```javascript +describe('/app/route', function() { + this.timeout(5000); // Allow time for external API + + it('should return valid route', (done) => { + const requestBody = { + "coordinates": [[-79.045848, 35.904798], [-79.053061, 35.909575]], + "avoid_features": ["steps"] + }; + + chai.request('http://localhost:5001') + .post('/app/route') + .send(requestBody) + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.a('object'); + res.body.should.have.property('type').that.equals('FeatureCollection'); + done(); + }); + }); +}); +``` + +#### 2. Response Validation +```javascript +// Validate GeoJSON structure +res.body.should.have.property('features').that.is.an('array'); +res.body.features[0].should.have.property('geometry'); +res.body.features[0].geometry.should.have.property('type').that.equals('LineString'); + +// Validate route properties +const route = res.body.features[0].properties; +route.should.have.property('segments').that.is.an('array'); + +// Validate steps +const steps = route.segments[0].steps; +steps.should.be.an('array').with.length.greaterThan(0); +steps.forEach((step) => { + step.should.have.property('distance').that.is.a('number'); + step.should.have.property('duration').that.is.a('number'); + step.should.have.property('instruction').that.is.a('string'); +}); +``` + +## Writing New Tests + +### Test Template +```javascript +describe('New Feature Tests', () => { + it('should handle valid input', (done) => { + const testData = { + // Your test data + }; + + chai.request('http://localhost:5001') + .post('/app/your-endpoint') + .send(testData) + .end((err, res) => { + if (err) return done(err); + + res.should.have.status(200); + res.body.should.have.property('expectedField'); + done(); + }); + }); + + it('should handle invalid input', (done) => { + chai.request('http://localhost:5001') + .post('/app/your-endpoint') + .send({}) // Invalid/empty data + .end((err, res) => { + res.should.have.status(400); + done(); + }); + }); +}); +``` + +### Async/Await Pattern +```javascript +it('should return building data', async () => { + const res = await chai.request('http://localhost:5001') + .get('/app/building/all'); + + res.should.have.status(200); + res.body.should.be.an('array'); +}); +``` + +## Database Testing + +### Test Database Setup +```javascript +before(async () => { + // Set up test database + await pool.query('CREATE TABLE IF NOT EXISTS test_table...'); +}); + +after(async () => { + // Clean up test data + await pool.query('DROP TABLE IF EXISTS test_table'); +}); +``` + +### Mock External APIs +```javascript +const sinon = require('sinon'); + +beforeEach(() => { + // Mock OpenRouteService + sinon.stub(orsDirections, 'calculate').resolves({ + type: 'FeatureCollection', + features: [/* mock route data */] + }); +}); + +afterEach(() => { + sinon.restore(); +}); +``` + +## Common Test Scenarios + +### Navigation Tests +```javascript +// Test valid coordinates +{ + "coordinates": [[-79.045848, 35.904798], [-79.053061, 35.909575]] +} + +// Test with accessibility constraints +{ + "coordinates": [[-79.046155, 35.910851], [-79.052946, 35.909613]], + "avoid_features": ["steps"], + "restrictions": { + "surface_type": "cobblestone:flattened", + "maximum_incline": 6 + } +} + +// Test with obstacle avoidance +{ + "coordinates": [[-79.046155, 35.910851], [-79.052946, 35.909613]], + "avoid_obstacles": ["construction"], + "avoid_polygons": { + "type": "Polygon", + "coordinates": [[...]] + } +} +``` + +### Error Handling Tests +```javascript +// Invalid coordinates +it('should reject invalid coordinates', (done) => { + chai.request('http://localhost:5001') + .post('/app/route') + .send({ coordinates: "invalid" }) + .end((err, res) => { + res.should.have.status(400); + done(); + }); +}); + +// Missing required fields +it('should require coordinates', (done) => { + chai.request('http://localhost:5001') + .post('/app/route') + .send({}) + .end((err, res) => { + res.should.have.status(400); + done(); + }); +}); +``` + +## Performance Testing + +### Response Time Testing +```javascript +it('should respond within reasonable time', function(done) { + this.timeout(10000); // 10 second max + + const startTime = Date.now(); + + chai.request('http://localhost:5001') + .post('/app/route') + .send(validRouteRequest) + .end((err, res) => { + const responseTime = Date.now() - startTime; + responseTime.should.be.below(5000); // Under 5 seconds + done(); + }); +}); +``` + +## Test Configuration + +### Environment Variables +```bash +# Test-specific environment +NODE_ENV=test +POSTGRES_URI=postgresql://user:pass@localhost:5432/polaris_test +PORT=5001 +``` + +### Package.json Scripts +```json +{ + "scripts": { + "test": "(timeout --signal=SIGINT 15 node server.js --port=5001; exit 0) & sleep 5s && curl -s http://localhost:5001/app/ && sleep 5s", + "mocha": "mocha test/*.test.js --exit", + "test:watch": "mocha test/*.test.js --watch", + "test:coverage": "nyc mocha test/*.test.js" + } +} +``` + +## Debugging Tests + +### Console Output +```javascript +// Add debugging output +console.log('Request body:', requestBody); +console.log('Response:', res.body); +``` + +### Test Isolation +```javascript +// Run single test +npx mocha test/adaptiveNav.test.js --grep "should return valid route" +``` + +### Error Investigation +```javascript +.end((err, res) => { + if (err) { + console.error('Test error:', err); + console.error('Response body:', res ? res.body : 'No response'); + } + // Continue with assertions +}); +``` diff --git a/docs/getting-started/core-concepts.md b/docs/getting-started/core-concepts.md new file mode 100644 index 0000000..96cba07 --- /dev/null +++ b/docs/getting-started/core-concepts.md @@ -0,0 +1,92 @@ +# Core Concepts + +## Accessibility-First Design +Every route calculation prioritizes wheelchair and mobility device access: +- **Avoid stairs** by default +- **Surface quality** analysis (smooth vs rough) +- **Incline limitations** for wheelchair safety +- **Door accessibility** (automatic, width, emergency exits) + +## Key Data Models + +### Coordinates +All locations use `[longitude, latitude]` format (GeoJSON standard) +```json +[-79.045848, 35.904798] // [lng, lat] NOT [lat, lng] +``` + +### Buildings & Doors +- **Buildings** have multiple **doors** (entrances/exits) +- Each door has accessibility attributes (automatic, stairs, emergency) +- Routing automatically selects optimal doors for wheelchair access + +### Indoor Issues +Community-reported accessibility problems: +- **Temporary**: Broken elevator, blocked ramp +- **Permanent**: Construction, design barriers +- **Categories**: door, elevator, ramp, narrow, construction + +## Navigation Types + +### 1. Point-to-Point +Direct routing between two coordinates +```json +{ + "coordinates": [[lng1, lat1], [lng2, lat2]] +} +``` + +### 2. Building-to-Building +Intelligent door selection for optimal accessibility +```json +{ + "source_building": "Library", + "destination_building": "Student Union" +} +``` + +### 3. Location-to-Building +Find best entrance from current position +```json +{ + "longitude": -79.045848, + "latitude": 35.904798, + "destination": "Library" +} +``` + +## Routing Constraints + +### Surface Types +- `paved`, `asphalt`, `concrete` - Wheelchair friendly +- `cobblestone`, `gravel`, `dirt` - May be difficult +- `grass`, `sand`, `mud` - Usually avoided + +### Restrictions +```json +{ + "surface_type": "cobblestone:flattened", + "track_type": "grade1", // Best quality + "smoothness_type": "good", + "maximum_incline": 6 // Max slope in degrees +} +``` + +### Obstacle Avoidance +Real-time barriers from community reports: +- `construction` - Construction zones +- `narrow` - Passages too narrow for wheelchairs + +## API Response Flow +1. **Request validation** - Check required parameters +2. **Building detection** - Identify nearby buildings +3. **Door optimization** - Select accessible entrances +4. **Obstacle integration** - Apply current barriers +5. **Route calculation** - Generate wheelchair path +6. **GeoJSON response** - Return navigation data + +## Error Handling +- `404` - No accessible route found +- `400` - Invalid request parameters +- `500` - Server or external API error +- `429` - Rate limit exceeded diff --git a/docs/getting-started/first-api-call.md b/docs/getting-started/first-api-call.md new file mode 100644 index 0000000..b31c373 --- /dev/null +++ b/docs/getting-started/first-api-call.md @@ -0,0 +1,73 @@ +# First API Call + +## Test Server Connection +```bash +curl http://localhost:5000/app/ +``` +**Response:** `{"message":"Your API works! (200)"}` + +## Basic Navigation Request +```bash +curl -X POST http://localhost:5000/app/route \ + -H "Content-Type: application/json" \ + -d '{ + "coordinates": [[-79.045848, 35.904798], [-79.053061, 35.909575]], + "avoid_features": ["steps"] + }' +``` + +**Response:** GeoJSON route with wheelchair-accessible path + +## Get All Buildings +```bash +curl http://localhost:5000/app/building/all +``` + +## Report Accessibility Issue +```bash +curl -X POST http://localhost:5000/app/indoorIssue/add \ + -H "Content-Type: application/json" \ + -d '{ + "location": "Main Library Entrance", + "latitude": 35.910000, + "longitude": -79.050000, + "description": "Automatic door not working", + "categories": ["door", "automatic"] + }' +``` + +## Common Response Formats + +### Navigation Response +```json +{ + "type": "FeatureCollection", + "features": [{ + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [[-79.045848, 35.904798], ...] + }, + "properties": { + "segments": [{ + "distance": 125.5, + "duration": 90.2, + "steps": [...] + }] + } + }] +} +``` + +### Error Response +```json +{ + "message": "Route impossible. No accessible path found.", + "status": 404 +} +``` + +## Next Steps +- Read [Core Concepts](core-concepts.md) +- Explore [Navigation Endpoints](../api/navigation.md) +- Try [Building-to-Building Routing](../navigation/building-to-building.md) diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md new file mode 100644 index 0000000..dc28173 --- /dev/null +++ b/docs/getting-started/installation.md @@ -0,0 +1,91 @@ +# Installation + +## Prerequisites +- Node.js 16+ +- PostgreSQL 13+ +- MongoDB (for logging) + +## Setup +```bash +# Clone and install +git clone https://github.com/polaris-maps/polaris-api.git +cd polaris-api +npm install + +# Environment configuration +cp config.env.example config.env +# Edit config.env with your database credentials and API keys +``` + +## Environment Variables +```bash +# PostgreSQL +POSTGRES_URI=postgresql://user:pass@localhost:5432/polaris + +# MongoDB (logging) +ATLAS_URI_LOGS=mongodb://localhost:27017/polaris_logs + +# OpenRouteService API +ORS_API_KEY=your_openrouteservice_api_key + +# Database connection details +DB_HOST=localhost +DB_PORT=5432 +DB_USER=your_username +DB_PASSWORD=your_password +DB_NAME=polaris +``` + +## Database Setup +```sql +-- Create PostgreSQL database +CREATE DATABASE polaris; + +-- Required tables (basic structure) +CREATE TABLE Location ( + location_id SERIAL PRIMARY KEY, + full_name VARCHAR(255), + abbreviation VARCHAR(50), + defaultLatitude DECIMAL, + defaultLongitude DECIMAL +); + +CREATE TABLE Door ( + door_id SERIAL PRIMARY KEY, + latitude DECIMAL, + longitude DECIMAL, + building_id INTEGER REFERENCES Location(location_id), + is_emergency BOOLEAN DEFAULT FALSE, + automatic BOOLEAN DEFAULT FALSE, + stairs BOOLEAN DEFAULT FALSE +); + +CREATE TABLE Issue ( + issue_id SERIAL PRIMARY KEY, + location VARCHAR(255), + latitude DECIMAL, + longitude DECIMAL, + description TEXT, + categories TEXT[], + status VARCHAR(50) DEFAULT 'open' +); +``` + +## Start Server +```bash +# Development +npm start + +# Test connection +curl http://localhost:5000/app/ +# Expected: {"message":"Your API works! (200)"} +``` + +## Verify Setup +```bash +# Run tests +npm run mocha + +# Check database connection +curl http://localhost:5000/app/test-db +``` diff --git a/docs/navigation/building-to-building.md b/docs/navigation/building-to-building.md new file mode 100644 index 0000000..677c4bf --- /dev/null +++ b/docs/navigation/building-to-building.md @@ -0,0 +1,200 @@ +# Building-to-Building Navigation + +## Overview +Intelligent routing between buildings with automatic door selection for optimal wheelchair accessibility. + +## Basic Building Routing +```bash +curl -X POST http://localhost:5000/app/route \ + -H "Content-Type: application/json" \ + -d '{ + "source_building": "Library", + "destination_building": "Student Union", + "exclude_stairs": true, + "require_automatic": false + }' +``` + +## Door Optimization Endpoint +```bash +curl -X POST http://localhost:5000/app/route/minimize-door-distance \ + -H "Content-Type: application/json" \ + -d '{ + "source": "Library", + "destination": "Student Union", + "maximum_number_results": 1, + "exclude_stairs": true, + "require_automatic": true + }' +``` + +**Response:** +```json +{ + "SourceDoorCoordinates": [-79.045900, 35.904850], + "DestinationDoorCoordinates": [-79.053100, 35.909600], + "distance": 450.2 +} +``` + +## Door Selection Algorithm + +### 1. Filter Available Doors +```javascript +// Automatic filtering based on parameters +let doorAttributes = { + "emergency": false // Always exclude emergency exits +}; + +if (exclude_stairs) { + doorAttributes.stairs = false; +} + +if (require_automatic) { + doorAttributes.automatic = true; +} +``` + +### 2. Matrix Distance Calculation +```javascript +// Calculate distances between all source/destination door pairs +orsMatrix.calculate({ + locations: [...sourceDoorsLocations, ...destinationDoorsLocations], + profile: "foot-walking", + sources: Array.from({length: sourceDoors.length}, (_, i) => i), + destinations: Array.from({length: destDoors.length}, (_, i) => i + sourceDoors.length) +}) +``` + +### 3. Optimal Pair Selection +```javascript +// Find shortest door-to-door distance +let minDistance = Infinity; +let closestPair = { sourceIndex: -1, destinationIndex: -1 }; + +for (let i = 0; i < sourceLocations.length; i++) { + for (let j = 0; j < destLocations.length; j++) { + let distance = json.durations[i][j]; + if (distance < minDistance) { + minDistance = distance; + closestPair = { sourceIndex: i, destinationIndex: j }; + } + } +} +``` + +## Location-to-Building Routing +Find the best building entrance from current position: + +```bash +curl -X POST http://localhost:5000/app/route/to-door \ + -H "Content-Type: application/json" \ + -d '{ + "longitude": -79.045848, + "latitude": 35.904798, + "destination": "Library", + "exclude_stairs": true + }' +``` + +**Response:** +```json +{ + "start": { + "longitude": -79.045848, + "latitude": 35.904798 + }, + "end": { + "longitude": -79.045900, + "latitude": 35.904850, + "door_id": 123, + "automatic": true, + "stairs": false, + "building_id": 1 + }, + "duration": 120.5 +} +``` + +## Door Accessibility Attributes + +### Primary Filters +- **stairs**: `true` if stairs required to reach door +- **automatic**: `true` for push-button or motion-sensor doors +- **emergency**: `true` for emergency exits (usually locked) +- **is_service**: `true` for service/delivery entrances + +### Building Door Types +```json +{ + "door_id": 45, + "building_id": 1, + "latitude": 35.910100, + "longitude": -79.050100, + "automatic": true, // Push-button activation + "stairs": false, // Ground-level access + "is_emergency": false, // Regular entrance + "is_service": false, // Public entrance + "is_indoor": false // Exterior door +} +``` + +## Fallback Behavior +When no suitable doors are found: +1. **Log warning** - "No doors meet accessibility criteria" +2. **Use original coordinates** - Fall back to basic point-to-point routing +3. **Return route** - Still attempt navigation with user-provided coordinates + +```javascript +if (sourceDoors.length < 1 || destinationDoors.length < 1) { + resolve({ useOriginal: true }); +} +``` + +## Integration with Main Routing +Building-to-building routing automatically integrates with the main `/app/route` endpoint: + +```json +{ + "coordinates": [[-79.045000, 35.904000], [-79.053000, 35.909000]], + "source_building": "Library", + "destination_building": "Student Union", + "exclude_stairs": true, + "avoid_features": ["steps"], + "restrictions": { + "maximum_incline": 6 + } +} +``` + +**Process:** +1. Override coordinates with optimal door locations +2. Apply standard accessibility constraints +3. Integrate obstacle avoidance +4. Generate complete wheelchair-accessible route + +## Error Scenarios + +### No Accessible Doors +```json +{ + "message": "Route impossible. (No destination doors exist with those attributes.)", + "status": 500 +} +``` + +### Building Not Found +```json +{ + "message": "Building 'Unknown Building' not found in database", + "status": 404 +} +``` + +### Database Error +```json +{ + "message": "Database connection failed during door lookup", + "status": 500 +} +``` diff --git a/docs/navigation/obstacles.md b/docs/navigation/obstacles.md new file mode 100644 index 0000000..5339049 --- /dev/null +++ b/docs/navigation/obstacles.md @@ -0,0 +1,212 @@ +# Obstacle Avoidance System + +## Overview +Real-time integration of community-reported accessibility barriers into navigation routing. + +## Obstacle Categories +- **construction** - Construction zones, blocked areas +- **narrow** - Passages too narrow for wheelchairs +- **temporary** - Temporary barriers (events, maintenance) +- **permanent** - Design limitations, permanent obstacles + +## Dynamic Obstacle Integration + +### Request with Obstacle Avoidance +```json +{ + "coordinates": [[-79.045848, 35.904798], [-79.053061, 35.909575]], + "avoid_obstacles": ["construction", "narrow"], + "avoid_features": ["steps"] +} +``` + +### Database Query Process +```javascript +// Query indoor issues by obstacle categories +indoorIssueRoutes.find({ + "category": { $in: ["construction", "narrow"] } +}, "avoidPolygon").exec() +``` + +### Polygon Generation +Issues with geospatial data become routing obstacles: +```json +{ + "issue_id": 15, + "categories": ["construction"], + "avoidPolygon": { + "type": "Polygon", + "coordinates": [[ + [-79.051000, 35.909000], + [-79.050000, 35.909000], + [-79.050000, 35.910000], + [-79.051000, 35.910000], + [-79.051000, 35.909000] + ]] + } +} +``` + +## OpenRouteService Integration + +### MultiPolygon Avoidance +```javascript +const avoidPolygons = obstacleData.map(obj => obj.avoidPolygon); + +let adaptiveNavOptions = { + avoid_features: ["steps"], + profile_params: { + "restrictions": { + "surface_type": "cobblestone:flattened", + "maximum_incline": 6 + } + }, + avoid_polygons: { + "type": "MultiPolygon", + "coordinates": avoidPolygons + } +} +``` + +### Route Calculation +```javascript +orsDirections.calculate({ + coordinates: [[lng1, lat1], [lng2, lat2]], + profile: 'wheelchair', + options: adaptiveNavOptions, + format: 'geojson' +}) +``` + +## Hardcoded Campus Obstacles + +### Predefined Avoidance Areas +```javascript +"avoid_polygons": { + "type": "MultiPolygon", + "coordinates": [ + // Gardner Hall construction area + [[ + ["-79.051700", "35.910300"], + ["-79.050900", "35.910600"], + ["-79.050600", "35.910200"], + ["-79.051400", "35.90990"], + ["-79.051700", "35.910300"] + ]], + // Connor Hall bus stop area + [[ + ["-79.050574", "35.910172"], + ["-79.04985", "35.910457"], + ["-79.049587", "35.909994"], + ["-79.050312", "35.909731"], + ["-79.050574", "35.910172"] + ]] + ] +} +``` + +## Issue-Based Obstacle Creation + +### Reporting Obstacle Issues +```bash +curl -X POST http://localhost:5000/app/indoorIssue/add \ + -H "Content-Type: application/json" \ + -d '{ + "location": "Library South Entrance", + "description": "Construction blocking wheelchair ramp", + "categories": ["construction", "ramp"], + "latitude": 35.910000, + "longitude": -79.050000, + "avoidPolygon": { + "type": "Polygon", + "coordinates": [[ + [-79.050100, 35.909900], + [-79.049900, 35.909900], + [-79.049900, 35.910100], + [-79.050100, 35.910100], + [-79.050100, 35.909900] + ]] + } + }' +``` + +### Automatic Route Integration +1. **Issue Created** - Community reports obstacle with polygon +2. **Database Storage** - Issue saved with geospatial data +3. **Route Query** - Navigation requests check for obstacles +4. **Dynamic Avoidance** - Polygon applied to routing constraints +5. **Alternative Path** - System finds accessible route around barrier + +## Real-Time Updates + +### Issue Status Changes +```bash +# Resolve construction issue +curl -X PATCH http://localhost:5000/app/indoorIssue/update/15 \ + -H "Content-Type: application/json" \ + -d '{ + "status": "resolved", + "datetimeClosed": "2024-09-15T10:00:00Z" + }' +``` + +**Impact:** Resolved issues no longer appear in obstacle queries, automatically removing routing constraints. + +### Category-Based Filtering +```javascript +// Only avoid active construction +{ + "avoid_obstacles": ["construction"], + "status": "open" // Only active issues +} +``` + +## Polygon Precision + +### Accurate Boundary Definition +```json +{ + "avoidPolygon": { + "type": "Polygon", + "coordinates": [[ + [-79.051020, 35.909980], // Southwest corner + [-79.050980, 35.909980], // Southeast corner + [-79.050980, 35.910020], // Northeast corner + [-79.051020, 35.910020], // Northwest corner + [-79.051020, 35.909980] // Close polygon + ]] + } +} +``` + +**Guidelines:** +- **Precise coordinates** - Use GPS measurements when possible +- **Closed polygons** - First and last coordinates must match +- **Reasonable buffer** - Include safety margin around actual obstacle +- **Regular updates** - Update boundaries as obstacles change + +## Error Handling + +### Invalid Polygon Data +```json +{ + "message": "Invalid avoidPolygon format. Must be valid GeoJSON Polygon", + "status": 400 +} +``` + +### No Alternative Route +```json +{ + "message": "Route impossible. All accessible paths blocked by obstacles.", + "status": 404 +} +``` + +### External Service Error +```json +{ + "message": "Routing service failed to process obstacle avoidance", + "status": 503 +} +``` diff --git a/docs/navigation/path-to-path.md b/docs/navigation/path-to-path.md new file mode 100644 index 0000000..38411a7 --- /dev/null +++ b/docs/navigation/path-to-path.md @@ -0,0 +1,153 @@ +# Path-to-Path Routing + +## Overview +Direct navigation between any two coordinate points with wheelchair accessibility optimization. + +## Basic Usage +```bash +curl -X POST http://localhost:5000/app/route \ + -H "Content-Type: application/json" \ + -d '{ + "coordinates": [[-79.045848, 35.904798], [-79.053061, 35.909575]] + }' +``` + +## Advanced Parameters + +### Accessibility Constraints +```json +{ + "coordinates": [[-79.045848, 35.904798], [-79.053061, 35.909575]], + "avoid_features": ["steps"], + "restrictions": { + "surface_type": "cobblestone:flattened", + "track_type": "grade1", + "smoothness_type": "good", + "maximum_incline": 6 + } +} +``` + +### Obstacle Avoidance +```json +{ + "coordinates": [[-79.045848, 35.904798], [-79.053061, 35.909575]], + "avoid_obstacles": ["construction", "narrow"], + "avoid_polygons": { + "type": "Polygon", + "coordinates": [[ + [-79.051000, 35.909000], + [-79.050000, 35.909000], + [-79.050000, 35.910000], + [-79.051000, 35.910000], + [-79.051000, 35.909000] + ]] + } +} +``` + +## Algorithm Flow +1. **Input validation** - Verify coordinate format `[lng, lat]` +2. **Building detection** - Check if coordinates near building entrances +3. **Accessibility filtering** - Apply surface, incline, feature restrictions +4. **Obstacle integration** - Query database for current barriers +5. **Route calculation** - External OpenRouteService API call +6. **Response formatting** - Return GeoJSON with accessibility metadata + +## Surface Type Analysis + +### Wheelchair-Friendly Surfaces +- `paved`, `asphalt`, `concrete` - Smooth, predictable +- `cobblestone:flattened` - Acceptable if well-maintained +- `paving_stones` - Usually accessible + +### Challenging Surfaces +- `cobblestone`, `gravel`, `dirt` - Bumpy, unstable +- `grass`, `sand`, `mud` - Soft, difficult navigation +- `snow`, `ice` - Weather-dependent hazards + +## Incline Restrictions +```json +{ + "maximum_incline": 6 // degrees, ADA recommends ≤5% +} +``` + +**Slope Guidelines:** +- 0-3°: Comfortable for most wheelchairs +- 3-5°: Manageable, may require assistance +- 5-8°: Difficult, power wheelchair recommended +- >8°: Usually avoided by routing system + +## Track Quality Levels +```json +{ + "track_type": "grade1" // OpenStreetMap standard +} +``` + +**Quality Scale:** +- `grade1`: Solid, best quality (paved roads) +- `grade2`: Mostly solid (gravel roads) +- `grade3`: Mixed solid/soft (dirt roads) +- `grade4`: Mostly soft (rough tracks) +- `grade5`: Soft (grass, sand paths) + +## Response Structure +```json +{ + "type": "FeatureCollection", + "features": [{ + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [-79.045848, 35.904798], + [-79.046000, 35.905000], + [-79.053061, 35.909575] + ] + }, + "properties": { + "segments": [{ + "distance": 1245.7, // meters + "duration": 900.3, // seconds + "steps": [{ + "distance": 125.5, + "duration": 90.2, + "type": 0, // turn type + "instruction": "Head southwest on Campus Drive", + "name": "Campus Drive", + "way_points": [0, 15] // coordinate indices + }] + }] + } + }] +} +``` + +## Error Handling + +### No Accessible Route +When no wheelchair-accessible path exists between points: +```json +{ + "message": "Route impossible. No accessible path found.", + "status": 404 +} +``` + +### Invalid Coordinates +```json +{ + "message": "Invalid coordinate format. Use [longitude, latitude]", + "status": 400 +} +``` + +### External API Failure +```json +{ + "message": "Routing service temporarily unavailable", + "status": 503 +} +```