diff --git a/.github/workflows/vercel-mongo-ci.yml b/.github/workflows/vercel-mongo-ci.yml new file mode 100644 index 0000000..851db9d --- /dev/null +++ b/.github/workflows/vercel-mongo-ci.yml @@ -0,0 +1,53 @@ +name: vercel-node-mongo CI + +on: + push: + branches: [master] + paths: + - examples/vercel-node-mongo/** + - .github/workflows/vercel-mongo-ci.yml + pull_request: + paths: + - examples/vercel-node-mongo/** + - .github/workflows/vercel-mongo-ci.yml + +concurrency: + group: vercel-mongo-${{ github.ref }} + cancel-in-progress: true + +jobs: + ci: + name: install / lint / test / build + runs-on: ubuntu-latest + defaults: + run: + working-directory: examples/vercel-node-mongo + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up pnpm + uses: pnpm/action-setup@v4 + with: + version: 11.1.1 + run_install: false + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + cache-dependency-path: examples/vercel-node-mongo/pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Lint + run: pnpm run lint + + - name: Test + run: pnpm run test + + - name: Build + run: pnpm run build diff --git a/examples/vercel-node-mongo/.gitignore b/examples/vercel-node-mongo/.gitignore index 1373f1d..d4e6398 100644 --- a/examples/vercel-node-mongo/.gitignore +++ b/examples/vercel-node-mongo/.gitignore @@ -1,4 +1,8 @@ .vercel .env -node_modules \ No newline at end of file +node_modules + +# Block npm/yarn lockfiles — this project is pnpm-only +package-lock.json +yarn.lock \ No newline at end of file diff --git a/examples/vercel-node-mongo/.npmrc b/examples/vercel-node-mongo/.npmrc deleted file mode 100644 index 26116ac..0000000 --- a/examples/vercel-node-mongo/.npmrc +++ /dev/null @@ -1,7 +0,0 @@ -save-exact=true -registry=https://registry.npmjs.org/ -engine-strict=true -audit-level=moderate -update-notifier=false -fund=false - diff --git a/examples/vercel-node-mongo/api/notifications.ts b/examples/vercel-node-mongo/api/notifications.ts index 25dd1e0..8f5fbd4 100644 --- a/examples/vercel-node-mongo/api/notifications.ts +++ b/examples/vercel-node-mongo/api/notifications.ts @@ -4,9 +4,14 @@ import { saveItem } from '../lib/items' const { PLUGGY_CLIENT_ID, PLUGGY_CLIENT_SECRET } = process.env -const pluggyClient = new PluggyClient({ clientId: PLUGGY_CLIENT_ID, clientSecret: PLUGGY_CLIENT_SECRET }) - export default async (req: VercelRequest, res: VercelResponse) => { + if (!PLUGGY_CLIENT_ID || !PLUGGY_CLIENT_SECRET) { + res.status(500).json({ message: 'Missing PLUGGY_CLIENT_ID or PLUGGY_CLIENT_SECRET' }) + return + } + + const pluggyClient = new PluggyClient({ clientId: PLUGGY_CLIENT_ID, clientSecret: PLUGGY_CLIENT_SECRET }) + const { id, event } = req.body if (!id) { res.status(400).json({ message: 'id parameter missing in body request.' }) diff --git a/examples/vercel-node-mongo/lib/db.ts b/examples/vercel-node-mongo/lib/db.ts index 88443c1..5ac5d44 100644 --- a/examples/vercel-node-mongo/lib/db.ts +++ b/examples/vercel-node-mongo/lib/db.ts @@ -3,6 +3,10 @@ import { MongoClient } from 'mongodb' const { MONGO_URI } = process.env export function getClient() { + if (!MONGO_URI) { + throw new Error('Missing MONGO_URI environment variable') + } + return new MongoClient(MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true diff --git a/examples/vercel-node-mongo/package.json b/examples/vercel-node-mongo/package.json index 3f27a6b..f854393 100644 --- a/examples/vercel-node-mongo/package.json +++ b/examples/vercel-node-mongo/package.json @@ -1,16 +1,33 @@ { "name": "pluggy-vercel-node-mongo-example", + "packageManager": "pnpm@11.1.1", "engines": { - "node": ">=22.11.0" + "node": ">=24.0.0", + "pnpm": ">=11.0.0" + }, + "devEngines": { + "runtime": { + "name": "node", + "version": ">=24.0.0", + "onFail": "error" + } + }, + "scripts": { + "preinstall": "pnpm audit && pnpm audit signatures", + "lint:lockfile": "pnpm install --frozen-lockfile", + "typecheck": "tsc --noEmit", + "lint": "tsc --noEmit", + "test": "tsc --noEmit", + "build": "tsc --noEmit" }, - "packageManager": "pnpm@11.1.1", "dependencies": { - "mongodb": "3.6.6", + "mongodb": "3.6.10", "pg": "8.6.0", "pluggy-sdk": "0.7.1" }, "devDependencies": { "@types/mongodb": "3.6.12", - "@vercel/node": "1.10.0" + "@vercel/node": "1.10.0", + "typescript": "5.9.3" } } diff --git a/examples/vercel-node-mongo/pnpm-lock.yaml b/examples/vercel-node-mongo/pnpm-lock.yaml index 0ebf9f4..2fad086 100644 --- a/examples/vercel-node-mongo/pnpm-lock.yaml +++ b/examples/vercel-node-mongo/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: mongodb: - specifier: 3.6.6 - version: 3.6.6 + specifier: 3.6.10 + version: 3.6.10 pg: specifier: 8.6.0 version: 8.6.0 @@ -24,6 +24,9 @@ importers: '@vercel/node': specifier: 1.10.0 version: 1.10.0 + typescript: + specifier: 5.9.3 + version: 5.9.3 packages: @@ -34,8 +37,8 @@ packages: '@types/mongodb@3.6.12': resolution: {integrity: sha512-49aEzQD5VdHPxyd5dRyQdqEveAg9LanwrH8RQipnMuulwzKmODXIZRp0umtxi1eBUfEusRkoy8AVOMr+kVuFog==} - '@types/node@25.7.0': - resolution: {integrity: sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg==} + '@types/node@25.6.0': + resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==} '@vercel/node@1.10.0': resolution: {integrity: sha512-11FIFoF4v9m/jAOSamwBDMsS2yIDa0fSnVhR70bbcUdBcu3BGGMCpg+jpA6rvkQSOa/WbTRvvu9hFh5bOsZxrw==} @@ -84,8 +87,8 @@ packages: memory-pager@1.5.0: resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} - mongodb@3.6.6: - resolution: {integrity: sha512-WlirMiuV1UPbej5JeCMqE93JRfZ/ZzqE7nJTwP85XzjAF4rRSeq2bGCb1cjfoHLOF06+HxADaPGqT0g3SbVT1w==} + mongodb@3.6.10: + resolution: {integrity: sha512-fvIBQBF7KwCJnDZUnFFy4WqEFP8ibdXeFANnylW19+vOwdjOAvqIzPdsNCEMT6VKTHnYu4K64AWRih0mkFms6Q==} engines: {node: '>=4'} peerDependencies: aws4: '*' @@ -226,8 +229,13 @@ packages: engines: {node: '>=4.2.0'} hasBin: true - undici-types@7.21.0: - resolution: {integrity: sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ==} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.19.2: + resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -255,15 +263,15 @@ snapshots: '@types/mongodb@3.6.12': dependencies: '@types/bson': 4.2.4 - '@types/node': 25.7.0 + '@types/node': 25.6.0 - '@types/node@25.7.0': + '@types/node@25.6.0': dependencies: - undici-types: 7.21.0 + undici-types: 7.19.2 '@vercel/node@1.10.0': dependencies: - '@types/node': 25.7.0 + '@types/node': 25.6.0 ts-node: 8.9.1(typescript@3.9.3) typescript: 3.9.3 @@ -297,7 +305,7 @@ snapshots: memory-pager@1.5.0: optional: true - mongodb@3.6.6: + mongodb@3.6.10: dependencies: bl: 2.2.1 bson: 1.1.6 @@ -419,7 +427,9 @@ snapshots: typescript@3.9.3: {} - undici-types@7.21.0: {} + typescript@5.9.3: {} + + undici-types@7.19.2: {} util-deprecate@1.0.2: {} diff --git a/examples/vercel-node-mongo/pnpm-workspace.yaml b/examples/vercel-node-mongo/pnpm-workspace.yaml index b417ab8..d663c66 100644 --- a/examples/vercel-node-mongo/pnpm-workspace.yaml +++ b/examples/vercel-node-mongo/pnpm-workspace.yaml @@ -1,3 +1,56 @@ -minimumReleaseAge: 10080 -autoInstallPeers: true +# Supply-chain hardening +# ---------------------- +# Reject any package version published less than 14 days ago when +# resolving. Most supply-chain attacks are detected within hours or +# days; the 14-day window absorbs that while keeping upgrades moving +# at a reasonable pace. For a critical CVE that demands a sub-14d +# upgrade, add a temporary `minimumReleaseAgeExclude` entry with a +# CVE link and remove it once the window closes. +# 14 days = 60 * 24 * 14 minutes +minimumReleaseAge: 20160 + +# When the registry metadata is missing the `time` field for a +# version, fall back to allowing it. +minimumReleaseAgeIgnoreMissingTime: true + +# Whitelist for the 14-day wait. `@pluggyai/*` ships from our own +# release pipeline. +minimumReleaseAgeExclude: + - '@pluggyai/*' + +# Pinned explicitly to survive any future default change. +resolutionMode: highest + +# Refuse to install if Node / pnpm don't satisfy the `engines` field. +engineStrict: true + +# Fail the install if a package version's trust level drops +# compared to earlier versions. +trustPolicy: no-downgrade + +# Auto-skip the trust check for packages older than 90 days. +trustPolicyIgnoreAfter: 129600 + +# Block transitive deps from being resolved from exotic sources. +blockExoticSubdeps: true + + +# Version pinning +# --------------- + +# Save exact resolved versions (no semver ranges) on `pnpm add`. +savePrefix: "" + + +# Per-package decisions +# --------------------- + +# pnpm 11 blocks postinstall / install / preinstall scripts by +# default; every package that ships one must be listed here. +# Default-deny: new entries start as `false` and only flip to `true` +# after a conscious review. +allowBuilds: {} + +# Force a specific version on transitive dependencies. +overrides: {} diff --git a/examples/vercel-node-mongo/tsconfig.json b/examples/vercel-node-mongo/tsconfig.json new file mode 100644 index 0000000..7026c4b --- /dev/null +++ b/examples/vercel-node-mongo/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "noEmit": true + }, + "include": ["api/**/*", "lib/**/*"] +}