Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 0 additions & 68 deletions .eslintrc.json

This file was deleted.

12 changes: 11 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
# Dependencies
node_modules/

# Local and config (secrets, environment overrides)
examples/config-local.js
examples/config.local.js
config/local.json
config/default.json
config/prod.json
config/dev.json

# Build output and caches
dist/
.dccache
coverage

# IDE and editor
.idea/
.DS_Store
.vscode/settings.json

# OS
.DS_Store
7 changes: 3 additions & 4 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

#!/usr/bin/env sh
npm run lint
npm run build
npm run build
npm run test:unit
Comment thread
cursor[bot] marked this conversation as resolved.
38 changes: 35 additions & 3 deletions .husky/pre-push
Original file line number Diff line number Diff line change
@@ -1,6 +1,38 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
#!/usr/bin/env sh
# Node versions to run tests on (aligned with engines ">= 20.20.0" and common LTS)
NODE_VERSIONS="20 22 24"

npm run lint
npm run build
npm test

# Load nvm when available so we can run tests on multiple Node versions in parallel
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
if [ -s "$NVM_DIR/nvm.sh" ]; then
. "$NVM_DIR/nvm.sh"
TMPD=$(mktemp -d)
trap 'rm -rf "$TMPD"' EXIT
PIDS=""
for ver in $NODE_VERSIONS; do
nvm exec "$ver" npm test > "$TMPD/$ver" 2>&1 &
PIDS="$PIDS $!"
done
FAIL=0
set -- $PIDS
for ver in $NODE_VERSIONS; do
r=0; wait "$1" || r=$?
if [ $r -ne 0 ]; then
FAIL=1
echo "Node $ver failed:"
cat "$TMPD/$ver" 2>/dev/null || true
fi
shift
done
if [ $FAIL -eq 0 ]; then
echo "Tests passed on Node $NODE_VERSIONS"
fi
[ $FAIL -eq 1 ] && exit 1
exit 0
else
echo "nvm not found; running tests with current Node only"
npm test
fi
26 changes: 24 additions & 2 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,32 @@
# IDE and tooling
.cursor
.github
.husky
.vscode

# Dev and config directories
config
examples
resources

# Source and tests (publish dist/ only; exclude compiled tests)
src
test
__tests__
.eslintrc.json
tsconfig.json
dist/__tests__

# Build and lint config
eslint.config.js
tsconfig.json

# Build artefacts and caches
coverage
.dccache
*.tsbuildinfo

# Internal documentation
ENDPOINT_COVERAGE.md

# Local environment
.env
.env.*
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v20
29 changes: 28 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
7.0.0 / 2026-02-23
==========

This major release updates the Node.js requirement and upgrades dependencies for security and compatibility. It also adds test infrastructure, documentation, and minor improvements.

**Breaking Changes**

* Node.js 12, 14, 16, and 18 are no longer supported; requires Node.js >= 20.20.0.

**Dependency upgrades**

* **jose** upgraded to v6 (from v5).
* **TypeScript** upgraded to v5.
* **ESLint** upgraded to v9 (flat config).
* **form-data** ^3.0.4 → ^4.0.5, **query-string** ^7.1.1 → ^9.3.1, **ramda** ^0.27.2 → ^0.32.0.
* Dev dependencies: **@mft/eslint-config-momentumft**, **@types/chai**, **@types/mocha**, and related tooling updated.

**Tooling and development**

* Added `.nvmrc` (Node 20).
* Test scripts split into `test:unit` and `test:integration`; `test` runs both. Added `test:coverage` and `test:integration:coverage` with c8.
* Husky pre-commit and pre-push scripts simplified; pre-push runs tests across multiple Node versions.
* Ramda imports refactored to use named imports instead of the full library.
* **ENDPOINT_COVERAGE.md** added to document API definitions and client method coverage.
* New and expanded unit tests (discovery, exchange-code-for-token, get-auth-urls, request, tokens, requests modules, and various endpoint tests).
* TypeScript config and ts-node options clarified.

6.95.0 / 2026-02-23
==========

Expand All @@ -22,7 +49,7 @@
* Update User Connection Types


6.0.0 / 2024-04-8
6.0.0 / 2024-04-08
==================

**Breaking Changes**
Expand Down
104 changes: 104 additions & 0 deletions ENDPOINT_COVERAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Endpoint coverage: Swagger / API definition vs client

This document maps API definitions (Swagger and router definitions) to the moneyhub-api-client methods.

## 1. Request routing

| Config | Backend | Definition source |
|--------|---------|-------------------|
| `resourceServerUrl` | API Gateway (Data API) | api-gateway `swagger.json` (v2), `swagger-v3.json` (v3) |
| `identityServiceUrl` | Identity | identity `swagger.json` |
| `caasResourceServerUrl` | CaaS (Enrichment) | api-gateway `docs-enrichment-engine/swagger-enrichment-engine.json` |
| `osipResourceServerUrl` | OSIP | api-gateway `create-router.js` + `handlers/osip.js` (no Swagger) |

## 2. API Gateway (Data API) – client mapping

**Swagger:** api-gateway `docs/swagger.json` (basePath `/v2.0`), `docs/swagger-v3.json` (basePath `/v3`).

| Path / area | Client method(s) | Module | Notes |
|-------------|------------------|--------|--------|
| GET /accounts | getAccounts, getAccountsWithDetails | accounts | |
| GET /accounts-list | getAccountsList, getAccountsListWithDetails | accounts | **v2 only** – not in v3 Swagger |
| GET/POST/PATCH/DELETE /accounts, /accounts/{id}, /balances | getAccount, getAccountWithDetails, getAccountBalances, createAccount, updateAccount, deleteAccount, addAccountBalance | accounts | |
| Holdings, counterparties, recurring-transactions, standing-orders, statements | getAccountHoldings, getAccountHoldingsWithMatches, getAccountHolding, getAccountCounterparties, getAccountRecurringTransactions, getAccountStandingOrders, getAccountStandingOrdersWithDetail, getAccountStatements, getAccountStatementsWithDetail | accounts | **v2 only:** /accounts-list, /accounts/{id}/counterparties, /accounts/{id}/recurring-transactions (no v3 path) |
| Affordability | (affordability module) | affordability | |
| Beneficiaries | getBeneficiary, getBeneficiaryWithDetail, getBeneficiaries, getBeneficiariesWithDetail | beneficiaries | |
| GET /global-counterparties | getGlobalCounterparties | unauthenticated | |
| Transactions, files, splits | getTransactions, getTransaction, getUnenrichedTransactions, getUnenrichedTransaction, addTransaction, addTransactions, getTransactionFiles, getTransactionFile, addFileToTransaction, deleteTransactionFile, getTransactionSplits, splitTransaction, patchTransactionSplit, deleteTransactionSplits | transactions, transaction-files, transaction-splits | |
| Categories, category-groups | getCategories, getCategory, getCategoryGroups, getStandardCategories, getStandardCategoryGroups, createCustomCategory, updateCategory, deleteCategory | categories | |
| Categorise, regular-transactions | categoriseTransactions, getRegularTransactions, addRegularTransaction, updateRegularTransaction, deleteRegularTransaction, detectRegularTransactions | categorise-transactions, regular-transactions | |
| Rental records | (rental-records module) | rental-records | |
| Spending analysis, spending/savings goals | getSpendingAnalysis, getSpendingGoals, getSpendingGoal, addSpendingGoal, updateSpendingGoal, deleteSpendingGoal, getSavingsGoals, getSavingsGoal, addSavingsGoal, updateSavingsGoal, deleteSavingsGoal | spending-analysis, spending-goals, savings-goals | |
| **GET /standard-financial-statements**, **GET /standard-financial-statements/{reportId}** | **getStandardFinancialStatements**, **getStandardFinancialStatement** | **standard-financial-statements** | **Added in this plan** |
| Sync, projects, tax | syncUserConnection, getProjects, getProject, addProject, updateProject, deleteProject, getTaxReturn | sync, projects, tax | |
| Notification thresholds | getNotificationThresholds, addNotificationThreshold, getNotificationThreshold, updateNotificationThreshold, deleteNotificationThreshold | notification-thresholds | |

## 3. Identity – client mapping

**Swagger:** identity `docs/swagger.json`.

| Path / area | Client method(s) | Module | Notes |
|-------------|------------------|--------|--------|
| GET .well-known/all-connections | listConnections | unauthenticated | |
| GET .well-known/api-connections | listAPIConnections | unauthenticated | |
| GET .well-known/legacy-connections | listLegacyConnections | unauthenticated | **Added in this plan** |
| GET .well-known/test-connections | listTestConnections | unauthenticated | |
| GET .well-known/payments-connections | listPaymentsConnections | unauthenticated | |
| GET .well-known/beta-connections | listBetaConnections | unauthenticated | |
| GET .well-known/openid-configuration | getOpenIdConfig | unauthenticated | |
| Auth-requests, OIDC | createAuthRequest, getAuthRequest, getAllAuthRequests, completeAuthRequest, getAuthUrls, token helpers | auth-requests, get-auth-urls, tokens | |
| Users, connections, syncs, SCIM | getUser, getUsers, registerUser, deleteUser, getUserConnections, deleteUserConnection, updateUserConnection, getConnectionSyncs, getUserSyncs, getSync, getSCIMUser, registerSCIMUser, searchSCIMUsers | users-and-connections | |
| Payees, pay-links, payments | addPayee, getPayees, getPayee, getPayment, getPayments, getPaymentFromIDToken, addPayLink, getPayLink, getPayLinks | payees, pay-links, payments | |
| Standing orders, recurring payments | getStandingOrder, getStandingOrders, getRecurringPayments, getRecurringPayment, makeRecurringPayment, revokeRecurringPayment, confirmFundsForRecurringPayment | standing-orders, recurring-payments | |
| Reseller-check, consent-history | createResellerCheckRequest, getConsentHistory | reseller-check, consent-history | |
| **Pay-file** | — | — | **Client missing** |
| **Pay-file-consent** | — | — | **Client missing** |
| **GET /bank-icons/{bankRef}** | — | — | **Client missing** |
| **GET /consents/{authRequestId}** | — | — | **Client missing** |

## 4. CaaS (Enrichment API) – client mapping

**Swagger:** api-gateway `docs/docs-enrichment-engine/swagger-enrichment-engine.json` (basePath `/caas/v1`).

| Path / area | Client method(s) | Module | Notes |
|-------------|------------------|--------|--------|
| DELETE /users/{userId} | caasDeleteUser | caas/users | |
| DELETE /accounts/{accountId} | caasDeleteAccount | caas/accounts | |
| GET /categories, /category-groups | caasGetCategories, caasGetCategoryGroups | caas/categories | |
| GET /counterparties | caasGetCounterparties | caas/counterparties | |
| GET /geotags | caasGetGeotags | caas/geotags | |
| GET/PATCH/DELETE /accounts/{accountId}/transactions/{transactionId} | caasGetTransactions, caasPatchTransaction, caasDeleteTransaction | caas/transactions | |
| POST /transactions/enrich | caasEnrichTransactions | caas/transactions | |
| **Custom categories** (users/{userId}/custom-categories) | — | — | **Client missing** |
| **Account regular-transactions** | — | — | **Client missing** |
| **Transaction enhanced, splits** | — | — | **Client missing** |

## 5. OSIP – client mapping

**Definition:** api-gateway `src/api/create-router.js` and `src/api/handlers/osip.js` (no Swagger).

| Method + path | Client method | Module |
|---------------|---------------|--------|
| GET /osip/v1.0/accounts | getOsipAccounts | osip |
| GET /osip/v1.0/accounts/:id | getOsipAccount | osip |
| GET /osip/v1.0/accounts/:id/holdings | getOsipAccountHoldings | osip |
| GET /osip/v1.0/accounts/:id/transactions | getOsipAccountTransactions | osip |

All require `osip:read` scope. Client fully covers these routes.

## 6. Integration test files

| Area | Test file(s) |
|------|--------------|
| Data API (accounts, transactions, etc.) | accounts.ts, accounts-with-extra-options.ts, transactions.ts, categories.ts, tax.ts, beneficiaries.ts, spending-analysis.ts, sync.ts, projects.ts, notification-thresholds.ts, regular-transactions.ts, rental-records.ts, savings-goals.ts, spending-goals.ts, transaction-files.ts, transaction-splits.ts, categorise-transactions.ts |
| Identity | auth-requests.ts, auth-urls.ts, payees.ts, pay-links.ts, payments.ts, standing-orders.ts, recurring-payments.ts, consent-history.ts, reseller-check.ts, users (via index) |
| Unauthenticated / connections | (listConnections etc. exercised via index or auth-urls) |
| CaaS | (optional; depends on env) |
| OSIP | osip.ts |
| Standard financial statements | standard-financial-statements.ts |

**Running tests and coverage**

- **Unit only** (no API config): `npm run test:coverage` — runs `*.unit.ts`, enforces 90% coverage.
- **Full suite** (unit + integration, requires config): `npm run test:integration:coverage` — runs all tests with hooks, enforces 90% line/function/branch/statement coverage.
- Integration tests live in `src/__tests__/*.ts` (non-`.unit.ts`); hooks in `test/hooks.js` provide `this.config` (e.g. `testUserId`, `testAccountId`).
9 changes: 9 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"use strict";
const {baseConfigWithFiles} = require("@mft/eslint-config-momentumft/typescript")
const {base, testsWithFiles} = require("@mft/eslint-config-momentumft")

module.exports = [
...base,
...baseConfigWithFiles,
testsWithFiles,
]
11 changes: 6 additions & 5 deletions examples/jwks/create-jwks.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const commandLineArgs = require("command-line-args")
const commandLineUsage = require("command-line-usage")
const jose = require("jose")


const optionDefinitions = [
{name: "key-alg", type: String},
Expand All @@ -20,19 +20,20 @@ console.log(usage)
// eslint-disable-next-line max-statements
const start = async () => {
try {
const {generateKeyPair, exportJWK, calculateJwkThumbprint} = await import("jose")
const keySize = options["key-size"] || 2048
const keyUse = options["key-use"] || "sig"
const alg = options.alg || "RS256"

const {publicKey, privateKey} = await jose.generateKeyPair(alg, {
const {publicKey, privateKey} = await generateKeyPair(alg, {
extractable: true,
modulusLength: keySize,
})

const exportedPrivateJWT = await jose.exportJWK(privateKey)
const exportedPublicJWT = await jose.exportJWK(publicKey)
const exportedPrivateJWT = await exportJWK(privateKey)
const exportedPublicJWT = await exportJWK(publicKey)

const kid = await jose.calculateJwkThumbprint(exportedPublicJWT)
const kid = await calculateJwkThumbprint(exportedPublicJWT)

const privateJWT = {...exportedPrivateJWT, kid, use: keyUse, alg}
const publicJWT = {...exportedPublicJWT, kid, use: keyUse, alg}
Expand Down
6 changes: 3 additions & 3 deletions examples/jwks/encrypt.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/* eslint-disable max-statements */
const {importJWK, CompactEncrypt} = require("jose")
const fs = require("fs")
const got = require("got")
const R = require("ramda")
const {last} = require("ramda")
Comment thread
cursor[bot] marked this conversation as resolved.
const config = require("../config")
const run = async () => {
const file = R.last(process.argv)
const {importJWK, CompactEncrypt} = await import("jose")
const file = last(process.argv)
if (!file || file.includes("encrypt.js")) {
console.log("Please add the file you wish to encrypt as the final argument")
process.exit(1)
Expand Down
Loading
Loading