Initial Agrarmonitor connector

This commit is contained in:
2026-05-21 21:15:25 +02:00
commit b47cbc00a8
13 changed files with 1860 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
node_modules/
dist/
data/
*.log
.DS_Store
.env
+65
View File
@@ -0,0 +1,65 @@
# Agrarmonitor Connector
TypeScript MVP connector for Agrarmonitor with shared cookie persistence, optional AES-GCM cookie encryption, automatic login, retry after expired sessions, device registration checks, and customer detail extraction.
## Installation
```bash
npm install
```
## Build
```bash
npm run build
```
## Example
```ts
import {
AesGcmCookieEncryptor,
FileCookieStore,
createAgrarmonitorClient,
} from 'agrarmonitor-connector';
const agrarmonitor = await createAgrarmonitorClient({
baseUrl: 'https://admin7.agrarmonitor.de',
apiToken: process.env.AGRARMONITOR_API_TOKEN,
username: process.env.AGRARMONITOR_USERNAME ?? '',
password: process.env.AGRARMONITOR_PASSWORD ?? '',
cookieStore: new FileCookieStore(
process.env.AGRARMONITOR_COOKIE_PATH ?? './data/agrarmonitor-cookies.json',
{
encryptor: process.env.AGRARMONITOR_ENCRYPTION_KEY
? new AesGcmCookieEncryptor(process.env.AGRARMONITOR_ENCRYPTION_KEY)
: undefined,
logger: console,
}
),
logger: console,
});
const freischaltung = await agrarmonitor.checkFreigeschaltet();
console.log(freischaltung.freigeschaltet);
const registrierung = await agrarmonitor.checkRegistriert();
console.log(registrierung.registriert);
const response = await agrarmonitor.http.get('/kunden/detail/123');
console.log(response.status);
```
## Cookie Persistence
`FileCookieStore` keeps one shared `CookieJar` per file path inside the Node process. Multiple connector instances that use the same cookie file therefore reuse the same session and every successful request saves the latest cookies back to disk.
The store can read both the connector format and the older Telefonbuch cookie-array format.
## Useful Methods
- `checkFreigeschaltet()` checks whether Agrarmonitor redirects to `/freischaltung/`.
- `checkRegistriert()` checks whether the page still contains `Neues Gerät registrieren`.
- `registerDevice({ agrarmonitorId, pcName })` loads `/freischaltung/`, extracts the nonce, and posts the registration request.
- `fetchCustomers()` loads customers from `https://api.agrarmonitor.de/v1/kunden`.
- `saveSession()` explicitly persists the current cookie jar.
+35
View File
@@ -0,0 +1,35 @@
import {
AesGcmCookieEncryptor,
FileCookieStore,
createAgrarmonitorClient,
} from '../src';
async function main(): Promise<void> {
const agrarmonitor = await createAgrarmonitorClient({
baseUrl: 'https://admin7.agrarmonitor.de',
apiToken: process.env.AGRARMONITOR_API_TOKEN,
username: process.env.AGRARMONITOR_USERNAME ?? '',
password: process.env.AGRARMONITOR_PASSWORD ?? '',
cookieStore: new FileCookieStore(
process.env.AGRARMONITOR_COOKIE_PATH ?? './data/agrarmonitor-cookies.json',
{
encryptor: process.env.AGRARMONITOR_ENCRYPTION_KEY
? new AesGcmCookieEncryptor(process.env.AGRARMONITOR_ENCRYPTION_KEY)
: undefined,
logger: console,
}
),
logger: console,
});
const freischaltung = await agrarmonitor.checkFreigeschaltet();
console.log('Freigeschaltet:', freischaltung.freigeschaltet);
const registrierung = await agrarmonitor.checkRegistriert();
console.log('Registriert:', registrierung.registriert);
const response = await agrarmonitor.http.get('/kunden/detail/123');
console.log(response.status);
}
void main();
+989
View File
@@ -0,0 +1,989 @@
{
"name": "agrarmonitor-connector",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "agrarmonitor-connector",
"version": "0.1.0",
"license": "MIT",
"dependencies": {
"axios": "^1.7.9",
"axios-cookiejar-support": "^5.0.5",
"jsdom": "^29.1.1",
"tough-cookie": "^4.1.4"
},
"devDependencies": {
"@types/jsdom": "^28.0.3",
"@types/node": "^22.10.2",
"@types/tough-cookie": "^4.0.5",
"typescript": "^5.7.2"
}
},
"node_modules/@asamuzakjp/css-color": {
"version": "5.1.11",
"resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz",
"integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==",
"license": "MIT",
"dependencies": {
"@asamuzakjp/generational-cache": "^1.0.1",
"@csstools/css-calc": "^3.2.0",
"@csstools/css-color-parser": "^4.1.0",
"@csstools/css-parser-algorithms": "^4.0.0",
"@csstools/css-tokenizer": "^4.0.0"
},
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}
},
"node_modules/@asamuzakjp/dom-selector": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz",
"integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==",
"license": "MIT",
"dependencies": {
"@asamuzakjp/generational-cache": "^1.0.1",
"@asamuzakjp/nwsapi": "^2.3.9",
"bidi-js": "^1.0.3",
"css-tree": "^3.2.1",
"is-potential-custom-element-name": "^1.0.1"
},
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}
},
"node_modules/@asamuzakjp/generational-cache": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz",
"integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==",
"license": "MIT",
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}
},
"node_modules/@asamuzakjp/nwsapi": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz",
"integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==",
"license": "MIT"
},
"node_modules/@bramus/specificity": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz",
"integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==",
"license": "MIT",
"dependencies": {
"css-tree": "^3.0.0"
},
"bin": {
"specificity": "bin/cli.js"
}
},
"node_modules/@csstools/color-helpers": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz",
"integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT-0",
"engines": {
"node": ">=20.19.0"
}
},
"node_modules/@csstools/css-calc": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz",
"integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT",
"engines": {
"node": ">=20.19.0"
},
"peerDependencies": {
"@csstools/css-parser-algorithms": "^4.0.0",
"@csstools/css-tokenizer": "^4.0.0"
}
},
"node_modules/@csstools/css-color-parser": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.1.tgz",
"integrity": "sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT",
"dependencies": {
"@csstools/color-helpers": "^6.0.2",
"@csstools/css-calc": "^3.2.1"
},
"engines": {
"node": ">=20.19.0"
},
"peerDependencies": {
"@csstools/css-parser-algorithms": "^4.0.0",
"@csstools/css-tokenizer": "^4.0.0"
}
},
"node_modules/@csstools/css-parser-algorithms": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz",
"integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT",
"engines": {
"node": ">=20.19.0"
},
"peerDependencies": {
"@csstools/css-tokenizer": "^4.0.0"
}
},
"node_modules/@csstools/css-syntax-patches-for-csstree": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.4.tgz",
"integrity": "sha512-wgsqt92b7C7tQhIdPNxj0n9zuUbQlvAuI1exyzeNrOKOi62SD7ren8zqszmpVREjAOqg8cD2FqYhQfAuKjk4sw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT-0",
"peerDependencies": {
"css-tree": "^3.2.1"
},
"peerDependenciesMeta": {
"css-tree": {
"optional": true
}
}
},
"node_modules/@csstools/css-tokenizer": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz",
"integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT",
"engines": {
"node": ">=20.19.0"
}
},
"node_modules/@exodus/bytes": {
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz",
"integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==",
"license": "MIT",
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
},
"peerDependencies": {
"@noble/hashes": "^1.8.0 || ^2.0.0"
},
"peerDependenciesMeta": {
"@noble/hashes": {
"optional": true
}
}
},
"node_modules/@types/jsdom": {
"version": "28.0.3",
"resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-28.0.3.tgz",
"integrity": "sha512-/HQ2uFoetFTXuye8vzIcHw2z6Fwi7Hi/qcgC+RoS9NCyewiqxhVGqlG+ViGB6lkax481R6dmhf1I7lIGlzJStQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*",
"@types/tough-cookie": "*",
"parse5": "^8.0.0",
"undici-types": "^7.21.0"
}
},
"node_modules/@types/jsdom/node_modules/undici-types": {
"version": "7.25.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.25.0.tgz",
"integrity": "sha512-AXNgS1Byr27fTI+2bsPEkV9CxkT8H6xNyRI68b3TatlZo3RkzlqQBLL+w7SmGPVpokjHbcuNVQUWE7FRTg+LRA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "22.19.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.18.tgz",
"integrity": "sha512-9v00a+dn2yWVsYDEunWC4g/TcRKVq3r8N5FuZp7u0SGrPvdN9c2yXI9bBuf5Fl0hNCb+QTIePTn5pJs2pwBOQQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/@types/tough-cookie": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
"integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
"dev": true,
"license": "MIT"
},
"node_modules/agent-base": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/axios": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz",
"integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.16.0",
"form-data": "^4.0.5",
"proxy-from-env": "^2.1.0"
}
},
"node_modules/axios-cookiejar-support": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/axios-cookiejar-support/-/axios-cookiejar-support-5.0.5.tgz",
"integrity": "sha512-jJG+p7JnOYxkVrYkCDKBrLqUmcpwHZTNQrEcIEKr5qe7YVTyPAD9nCsi1cO5LDmQpQApfS430czO+oceI3g/3g==",
"license": "MIT",
"dependencies": {
"http-cookie-agent": "^6.0.8"
},
"engines": {
"node": ">=18.0.0"
},
"funding": {
"url": "https://github.com/sponsors/3846masa"
},
"peerDependencies": {
"axios": ">=0.20.0",
"tough-cookie": ">=4.0.0"
}
},
"node_modules/bidi-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
"integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
"license": "MIT",
"dependencies": {
"require-from-string": "^2.0.2"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/css-tree": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz",
"integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==",
"license": "MIT",
"dependencies": {
"mdn-data": "2.27.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
}
},
"node_modules/data-urls": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz",
"integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==",
"license": "MIT",
"dependencies": {
"whatwg-mimetype": "^5.0.0",
"whatwg-url": "^16.0.0"
},
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}
},
"node_modules/decimal.js": {
"version": "10.6.0",
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
"integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
"license": "MIT"
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/entities": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz",
"integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=20.19.0"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/follow-redirects": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
"integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/html-encoding-sniffer": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz",
"integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==",
"license": "MIT",
"dependencies": {
"@exodus/bytes": "^1.6.0"
},
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}
},
"node_modules/http-cookie-agent": {
"version": "6.0.8",
"resolved": "https://registry.npmjs.org/http-cookie-agent/-/http-cookie-agent-6.0.8.tgz",
"integrity": "sha512-qnYh3yLSr2jBsTYkw11elq+T361uKAJaZ2dR4cfYZChw1dt9uL5t3zSUwehoqqVb4oldk1BpkXKm2oat8zV+oA==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.3"
},
"engines": {
"node": ">=18.0.0"
},
"funding": {
"url": "https://github.com/sponsors/3846masa"
},
"peerDependencies": {
"tough-cookie": "^4.0.0 || ^5.0.0",
"undici": "^5.11.0 || ^6.0.0"
},
"peerDependenciesMeta": {
"undici": {
"optional": true
}
}
},
"node_modules/is-potential-custom-element-name": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
"integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
"license": "MIT"
},
"node_modules/jsdom": {
"version": "29.1.1",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz",
"integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==",
"license": "MIT",
"dependencies": {
"@asamuzakjp/css-color": "^5.1.11",
"@asamuzakjp/dom-selector": "^7.1.1",
"@bramus/specificity": "^2.4.2",
"@csstools/css-syntax-patches-for-csstree": "^1.1.3",
"@exodus/bytes": "^1.15.0",
"css-tree": "^3.2.1",
"data-urls": "^7.0.0",
"decimal.js": "^10.6.0",
"html-encoding-sniffer": "^6.0.0",
"is-potential-custom-element-name": "^1.0.1",
"lru-cache": "^11.3.5",
"parse5": "^8.0.1",
"saxes": "^6.0.0",
"symbol-tree": "^3.2.4",
"tough-cookie": "^6.0.1",
"undici": "^7.25.0",
"w3c-xmlserializer": "^5.0.0",
"webidl-conversions": "^8.0.1",
"whatwg-mimetype": "^5.0.0",
"whatwg-url": "^16.0.1",
"xml-name-validator": "^5.0.0"
},
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24.0.0"
},
"peerDependencies": {
"canvas": "^3.0.0"
},
"peerDependenciesMeta": {
"canvas": {
"optional": true
}
}
},
"node_modules/jsdom/node_modules/tough-cookie": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz",
"integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==",
"license": "BSD-3-Clause",
"dependencies": {
"tldts": "^7.0.5"
},
"engines": {
"node": ">=16"
}
},
"node_modules/jsdom/node_modules/undici": {
"version": "7.25.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz",
"integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==",
"license": "MIT",
"engines": {
"node": ">=20.18.1"
}
},
"node_modules/lru-cache": {
"version": "11.5.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz",
"integrity": "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==",
"license": "BlueOak-1.0.0",
"engines": {
"node": "20 || >=22"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/mdn-data": {
"version": "2.27.1",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz",
"integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==",
"license": "CC0-1.0"
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/parse5": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz",
"integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==",
"license": "MIT",
"dependencies": {
"entities": "^8.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/proxy-from-env": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
"integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/psl": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
"integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==",
"license": "MIT",
"dependencies": {
"punycode": "^2.3.1"
},
"funding": {
"url": "https://github.com/sponsors/lupomontero"
}
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/querystringify": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
"license": "MIT"
},
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
"license": "MIT"
},
"node_modules/saxes": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
"integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
"license": "ISC",
"dependencies": {
"xmlchars": "^2.2.0"
},
"engines": {
"node": ">=v12.22.7"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/symbol-tree": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
"license": "MIT"
},
"node_modules/tldts": {
"version": "7.0.30",
"resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.30.tgz",
"integrity": "sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==",
"license": "MIT",
"dependencies": {
"tldts-core": "^7.0.30"
},
"bin": {
"tldts": "bin/cli.js"
}
},
"node_modules/tldts-core": {
"version": "7.0.30",
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.30.tgz",
"integrity": "sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==",
"license": "MIT"
},
"node_modules/tough-cookie": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
"integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
"license": "BSD-3-Clause",
"dependencies": {
"psl": "^1.1.33",
"punycode": "^2.1.1",
"universalify": "^0.2.0",
"url-parse": "^1.5.3"
},
"engines": {
"node": ">=6"
}
},
"node_modules/tr46": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz",
"integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==",
"license": "MIT",
"dependencies": {
"punycode": "^2.3.1"
},
"engines": {
"node": ">=20"
}
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici": {
"version": "6.25.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.25.0.tgz",
"integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==",
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=18.17"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},
"node_modules/universalify": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
"license": "MIT",
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/url-parse": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
"license": "MIT",
"dependencies": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
}
},
"node_modules/w3c-xmlserializer": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
"integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
"license": "MIT",
"dependencies": {
"xml-name-validator": "^5.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/webidl-conversions": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz",
"integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=20"
}
},
"node_modules/whatwg-mimetype": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz",
"integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==",
"license": "MIT",
"engines": {
"node": ">=20"
}
},
"node_modules/whatwg-url": {
"version": "16.0.1",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz",
"integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==",
"license": "MIT",
"dependencies": {
"@exodus/bytes": "^1.11.0",
"tr46": "^6.0.0",
"webidl-conversions": "^8.0.1"
},
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}
},
"node_modules/xml-name-validator": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
"integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
"license": "Apache-2.0",
"engines": {
"node": ">=18"
}
},
"node_modules/xmlchars": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
"license": "MIT"
}
}
}
+33
View File
@@ -0,0 +1,33 @@
{
"name": "agrarmonitor-connector",
"version": "0.1.0",
"description": "TypeScript connector MVP for Agrarmonitor with cookie persistence.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "tsc -p tsconfig.json",
"clean": "rm -rf dist",
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"keywords": [
"agrarmonitor",
"connector",
"typescript"
],
"license": "MIT",
"dependencies": {
"axios": "^1.7.9",
"axios-cookiejar-support": "^5.0.5",
"jsdom": "^29.1.1",
"tough-cookie": "^4.1.4"
},
"devDependencies": {
"@types/jsdom": "^28.0.3",
"@types/node": "^22.10.2",
"@types/tough-cookie": "^4.0.5",
"typescript": "^5.7.2"
}
}
+392
View File
@@ -0,0 +1,392 @@
import axios, { type AxiosInstance, type AxiosRequestConfig, type AxiosResponse } from 'axios';
import { wrapper } from 'axios-cookiejar-support';
import { JSDOM } from 'jsdom';
import { CookieJar } from 'tough-cookie';
import type {
AgrarmonitorApiCustomer,
AgrarmonitorConnectorOptions,
AgrarmonitorConnectorResult,
AgrarmonitorDeviceRegistrationOptions,
AgrarmonitorDeviceRegistrationResult,
AgrarmonitorFetchCustomersOptions,
AgrarmonitorFreischaltungStatus,
AgrarmonitorLoginStrategy,
AgrarmonitorRegistrierungStatus,
Logger,
} from './types';
type RetryableAxiosRequestConfig = AxiosRequestConfig & {
_agrarmonitorRetry?: boolean;
};
export class AgrarmonitorConnector implements AgrarmonitorConnectorResult {
public http!: AxiosInstance;
private readonly baseUrl: string;
private readonly apiBaseUrl: string;
private readonly timeoutMs: number;
private readonly autoLogin: boolean;
private readonly autoRetry: boolean;
private readonly loginStrategy: AgrarmonitorLoginStrategy;
private readonly logger?: Logger;
private cookieJar!: CookieJar;
private loginInProgress: Promise<void> | null = null;
constructor(private readonly options: AgrarmonitorConnectorOptions) {
this.baseUrl = options.baseUrl ?? 'https://admin7.agrarmonitor.de';
this.apiBaseUrl = options.apiBaseUrl ?? 'https://api.agrarmonitor.de';
this.timeoutMs = options.timeoutMs ?? 15000;
this.autoLogin = options.autoLogin ?? true;
this.autoRetry = options.autoRetry ?? true;
this.loginStrategy = options.loginStrategy ?? 'auto';
this.logger = options.logger;
}
async init(): Promise<this> {
this.cookieJar = await this.options.cookieStore.load();
this.http = this.createHttpClient();
if (this.autoLogin) {
const valid = await this.isSessionValid();
if (!valid) {
await this.login();
}
}
return this;
}
async login(): Promise<void> {
if (this.loginInProgress) {
return this.loginInProgress;
}
this.loginInProgress = this.performLogin().finally(() => {
this.loginInProgress = null;
});
return this.loginInProgress;
}
async clearSession(): Promise<void> {
this.cookieJar = new CookieJar();
await this.options.cookieStore.clear();
this.http = this.createHttpClient();
}
async saveSession(): Promise<void> {
await this.options.cookieStore.save(this.cookieJar);
}
async getCookieCount(url = this.baseUrl): Promise<number> {
return this.cookieJar.getCookiesSync(url).length;
}
async checkFreigeschaltet(): Promise<AgrarmonitorFreischaltungStatus> {
const response = await this.http.get('/', {
maxRedirects: 0,
validateStatus: status => status >= 200 && status < 400,
});
await this.saveSession();
const redirectLocation = this.getHeader(response, 'location');
const redirected = response.status >= 300 && response.status < 400 && this.isFreischaltungUrl(redirectLocation);
return {
freigeschaltet: !redirected,
status: response.status,
redirected,
redirectLocation,
timestamp: new Date().toISOString(),
cookies: await this.getCookieCount(),
};
}
async checkRegistriert(): Promise<AgrarmonitorRegistrierungStatus> {
const response = await this.http.get('/', {
maxRedirects: 5,
validateStatus: status => status >= 200 && status < 500,
});
await this.saveSession();
const pageContent = typeof response.data === 'string' ? response.data : '';
const hasRegistrationText = pageContent.includes('Neues Gerät registrieren');
return {
registriert: !hasRegistrationText,
status: response.status,
hasRegistrationText,
timestamp: new Date().toISOString(),
cookies: await this.getCookieCount(),
};
}
async registerDevice(
registration: AgrarmonitorDeviceRegistrationOptions
): Promise<AgrarmonitorDeviceRegistrationResult> {
const agrarmonitorId = registration.agrarmonitorId.trim();
const pcName = registration.pcName.trim();
if (!agrarmonitorId || !pcName) {
throw new Error('AgrarmonitorID und PC-Name sind erforderlich');
}
const freischaltungResponse = await this.http.get('/freischaltung/');
const responseContent = typeof freischaltungResponse.data === 'string' ? freischaltungResponse.data : '';
const nonce = this.extractNonce(responseContent, '#nonce, input[name="nonce"]');
const registerResponse = await this.http.post(
'/freischaltung/api/register.php',
{
firma: agrarmonitorId,
name: pcName,
nonce,
},
{
headers: {
'Content-Type': 'application/json',
},
validateStatus: status => status >= 200 && status < 500,
}
);
await this.saveSession();
const success = registerResponse.status >= 200 && registerResponse.status < 300;
return {
success,
status: registerResponse.status,
message: success ? 'Registrierung erfolgreich' : 'Registrierung fehlgeschlagen',
data: {
agrarmonitorId,
pcName,
nonce: this.maskNonce(nonce),
},
timestamp: new Date().toISOString(),
cookies: await this.getCookieCount(),
};
}
async fetchCustomers(options: AgrarmonitorFetchCustomersOptions = {}): Promise<AgrarmonitorApiCustomer[]> {
const apiToken = options.apiToken ?? this.options.apiToken;
if (!apiToken) {
throw new Error('Agrarmonitor API-Token nicht konfiguriert');
}
const response = await this.http.get(`${this.apiBaseUrl}/v1/kunden`, {
params: {
per_page: options.perPage ?? 99999,
api_token: apiToken,
},
});
await this.saveSession();
const responseData = response.data as { data?: unknown };
if (!responseData || !Array.isArray(responseData.data)) {
throw new Error('Ungueltige Agrarmonitor API-Antwort');
}
return responseData.data as AgrarmonitorApiCustomer[];
}
private createHttpClient(): AxiosInstance {
const client = wrapper(
axios.create({
baseURL: this.baseUrl,
jar: this.cookieJar,
withCredentials: true,
timeout: this.timeoutMs,
headers: {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
},
maxRedirects: 5,
validateStatus: status => status >= 200 && status < 400,
})
);
client.interceptors.response.use(
async response => {
await this.options.cookieStore.save(this.cookieJar);
if (this.autoRetry && this.isLoginRequiredResponse(response)) {
return this.retryAfterLogin(response.config);
}
return response;
},
async error => {
const response = error.response as AxiosResponse | undefined;
if (this.autoRetry && response && this.isLoginRequiredResponse(response)) {
return this.retryAfterLogin(error.config);
}
throw error;
}
);
return client;
}
private async performLogin(): Promise<void> {
if (!this.options.username || !this.options.password) {
throw new Error('Agrarmonitor-Credentials nicht konfiguriert');
}
this.logger?.info?.('Fuehre Agrarmonitor-Login durch');
if (this.loginStrategy === 'auth') {
await this.performAuthLogin();
} else if (this.loginStrategy === 'legacy') {
await this.performLegacyLogin();
} else {
await this.performAutoLogin();
}
await this.options.cookieStore.save(this.cookieJar);
this.logger?.info?.('Agrarmonitor-Login erfolgreich');
}
private async performAutoLogin(): Promise<void> {
try {
await this.performAuthLogin();
} catch (authError) {
this.logger?.warn?.('Agrarmonitor-Login via /auth/login fehlgeschlagen, versuche Legacy-Login', authError);
await this.performLegacyLogin();
}
}
private async performAuthLogin(): Promise<void> {
await this.http.get('/auth/login');
const loginData = new URLSearchParams({
email: this.options.username,
password: this.options.password,
remember: 'on',
});
const response = await this.http.post('/auth/login', loginData, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
const responseText = typeof response.data === 'string' ? response.data : '';
if (responseText.includes('Anmeldung fehlgeschlagen')) {
throw new Error('Agrarmonitor-Login fehlgeschlagen');
}
}
private async performLegacyLogin(): Promise<void> {
const loginPageResponse = await this.http.get('/');
const loginPageText = typeof loginPageResponse.data === 'string' ? loginPageResponse.data : '';
if (!this.isLoginPageText(loginPageText)) {
return;
}
const nonce = this.extractNonce(loginPageText, 'input[name="nonce"]');
const loginData = new URLSearchParams({
username: this.options.username,
passwort: this.options.password,
nonce,
});
const response = await this.http.post('/redirect.php?id=benutzerverwaltung&action=login', loginData, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
const responseText = typeof response.data === 'string' ? response.data : '';
if (this.isLoginPageText(responseText)) {
throw new Error('Agrarmonitor-Legacy-Login fehlgeschlagen');
}
}
private async isSessionValid(): Promise<boolean> {
try {
const response = await this.http.get('/');
return !this.isLoginRequiredResponse(response);
} catch {
return false;
}
}
private isLoginRequiredResponse(response: AxiosResponse): boolean {
const responseUrl = this.getResponseUrl(response);
const responseText = typeof response.data === 'string' ? response.data : '';
return (
response.status === 401 ||
response.status === 403 ||
responseUrl.includes('/auth/login') ||
responseText.includes('/auth/login') ||
this.isLoginPageText(responseText) ||
responseText.includes('Anmeldung') ||
responseText.includes('Einloggen')
);
}
private async retryAfterLogin(config: RetryableAxiosRequestConfig): Promise<AxiosResponse> {
if (config._agrarmonitorRetry) {
throw new Error('Agrarmonitor-Request nach erneutem Login weiterhin nicht autorisiert');
}
config._agrarmonitorRetry = true;
this.logger?.info?.('Agrarmonitor-Session abgelaufen, erneuter Login wird ausgefuehrt');
await this.login();
return this.http.request(config);
}
private getResponseUrl(response: AxiosResponse): string {
const request = response.request as { res?: { responseUrl?: string } } | undefined;
return request?.res?.responseUrl ?? '';
}
private getHeader(response: AxiosResponse, header: string): string | null {
const value = response.headers[header.toLowerCase()];
if (Array.isArray(value)) {
return value[0] ?? null;
}
return typeof value === 'string' ? value : null;
}
private isFreischaltungUrl(value: string | null): boolean {
return Boolean(value?.includes('freischaltung'));
}
private isLoginPageText(responseText: string): boolean {
return responseText.includes('Anmeldung - AGRARMONITOR');
}
private extractNonce(html: string, selector: string): string {
const dom = new JSDOM(html);
const element = dom.window.document.querySelector<HTMLInputElement>(selector);
const nonce = element?.getAttribute('value') ?? element?.value ?? '';
if (!nonce) {
throw new Error('Nonce-Element nicht gefunden oder leer');
}
return nonce;
}
private maskNonce(nonce: string): string {
return nonce.length <= 10 ? nonce : `${nonce.slice(0, 10)}...`;
}
}
+130
View File
@@ -0,0 +1,130 @@
import * as fs from 'fs';
import * as path from 'path';
import { Cookie, CookieJar } from 'tough-cookie';
import type { CookieEncryptor, CookieStore, Logger } from '../types';
interface FileCookieStoreOptions {
encryptor?: CookieEncryptor;
logger?: Logger;
}
type PlainCookieFile = ReturnType<CookieJar['toJSON']>;
type EncryptedCookieFile = {
encrypted: true;
algorithm: 'aes-256-gcm';
data: string;
updatedAt: string;
};
export class FileCookieStore implements CookieStore {
private static readonly sharedJars = new Map<string, CookieJar>();
constructor(
private readonly filePath: string,
private readonly options: FileCookieStoreOptions = {}
) {}
async load(): Promise<CookieJar> {
const sharedJar = FileCookieStore.sharedJars.get(this.filePath);
if (sharedJar) {
return sharedJar;
}
try {
if (!fs.existsSync(this.filePath)) {
this.options.logger?.info?.('Neuer Cookie-Store wird erstellt');
return this.remember(new CookieJar());
}
const raw = fs.readFileSync(this.filePath, 'utf8');
const parsed = JSON.parse(raw) as PlainCookieFile | EncryptedCookieFile;
if (this.isEncryptedCookieFile(parsed)) {
if (!this.options.encryptor) {
throw new Error('Cookie-Datei ist verschluesselt, aber kein Encryptor wurde konfiguriert');
}
const decrypted = this.options.encryptor.decrypt(parsed.data);
const cookieJson = JSON.parse(decrypted);
return this.remember(this.cookieJarFromJson(cookieJson));
}
return this.remember(this.cookieJarFromJson(parsed));
} catch (error) {
this.options.logger?.warn?.('Cookies konnten nicht geladen werden, neuer Cookie-Store wird erstellt', error);
return this.remember(new CookieJar());
}
}
async save(cookieJar: CookieJar): Promise<void> {
FileCookieStore.sharedJars.set(this.filePath, cookieJar);
fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
const cookieJson = cookieJar.toJSON();
let fileContent: string;
if (this.options.encryptor) {
fileContent = JSON.stringify(
{
encrypted: true,
algorithm: 'aes-256-gcm',
data: this.options.encryptor.encrypt(JSON.stringify(cookieJson)),
updatedAt: new Date().toISOString(),
} satisfies EncryptedCookieFile,
null,
2
);
} else {
fileContent = JSON.stringify(cookieJson, null, 2);
}
fs.writeFileSync(this.filePath, fileContent, {
mode: 0o600,
});
}
async clear(): Promise<void> {
FileCookieStore.sharedJars.set(this.filePath, new CookieJar());
if (fs.existsSync(this.filePath)) {
fs.unlinkSync(this.filePath);
}
}
private isEncryptedCookieFile(value: unknown): value is EncryptedCookieFile {
return (
typeof value === 'object' &&
value !== null &&
(value as EncryptedCookieFile).encrypted === true &&
(value as EncryptedCookieFile).algorithm === 'aes-256-gcm' &&
typeof (value as EncryptedCookieFile).data === 'string'
);
}
private cookieJarFromJson(value: unknown): CookieJar {
if (Array.isArray(value)) {
const cookieJar = new CookieJar();
for (const cookieData of value) {
const cookie = Cookie.fromJSON(cookieData);
if (cookie) {
const domain = cookie.domain ?? 'admin7.agrarmonitor.de';
const url = domain.startsWith('http') ? domain : `https://${domain}`;
cookieJar.setCookieSync(cookie, url);
}
}
return cookieJar;
}
return CookieJar.fromJSON(JSON.stringify(value));
}
private remember(cookieJar: CookieJar): CookieJar {
FileCookieStore.sharedJars.set(this.filePath, cookieJar);
return cookieJar;
}
}
+18
View File
@@ -0,0 +1,18 @@
import { CookieJar } from 'tough-cookie';
import type { CookieStore } from '../types';
export class MemoryCookieStore implements CookieStore {
private cookieJar = new CookieJar();
async load(): Promise<CookieJar> {
return this.cookieJar;
}
async save(cookieJar: CookieJar): Promise<void> {
this.cookieJar = cookieJar;
}
async clear(): Promise<void> {
this.cookieJar = new CookieJar();
}
}
+10
View File
@@ -0,0 +1,10 @@
import { AgrarmonitorConnector } from './AgrarmonitorConnector';
import type { AgrarmonitorConnectorOptions, AgrarmonitorConnectorResult } from './types';
export async function createAgrarmonitorClient(
options: AgrarmonitorConnectorOptions
): Promise<AgrarmonitorConnectorResult> {
const connector = new AgrarmonitorConnector(options);
await connector.init();
return connector;
}
+45
View File
@@ -0,0 +1,45 @@
import * as crypto from 'crypto';
import type { CookieEncryptor } from '../types';
export class AesGcmCookieEncryptor implements CookieEncryptor {
private readonly key: Buffer;
constructor(secret: string) {
if (!secret || secret.trim().length < 16) {
throw new Error('Cookie encryption secret muss mindestens 16 Zeichen lang sein');
}
this.key = crypto.createHash('sha256').update(secret).digest();
}
encrypt(text: string): string {
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv('aes-256-gcm', this.key, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`;
}
decrypt(encryptedText: string): string {
const [ivHex, authTagHex, encrypted] = encryptedText.split(':');
if (!ivHex || !authTagHex || !encrypted) {
throw new Error('Ungueltiges verschluesseltes Cookie-Format');
}
const iv = Buffer.from(ivHex, 'hex');
const authTag = Buffer.from(authTagHex, 'hex');
const decipher = crypto.createDecipheriv('aes-256-gcm', this.key, iv);
decipher.setAuthTag(authTag);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
}
+12
View File
@@ -0,0 +1,12 @@
export { AesGcmCookieEncryptor } from './crypto/AesGcmCookieEncryptor';
export { FileCookieStore } from './cookie-store/FileCookieStore';
export { MemoryCookieStore } from './cookie-store/MemoryCookieStore';
export { AgrarmonitorConnector } from './AgrarmonitorConnector';
export { createAgrarmonitorClient } from './createAgrarmonitorClient';
export type {
AgrarmonitorConnectorOptions,
AgrarmonitorConnectorResult,
CookieEncryptor,
CookieStore,
Logger,
} from './types';
+98
View File
@@ -0,0 +1,98 @@
import type { AxiosInstance } from 'axios';
import type { CookieJar } from 'tough-cookie';
export interface Logger {
debug?(message: string, meta?: unknown): void;
info?(message: string, meta?: unknown): void;
warn?(message: string, meta?: unknown): void;
error?(message: string, meta?: unknown): void;
}
export interface CookieEncryptor {
encrypt(text: string): string;
decrypt(encryptedText: string): string;
}
export interface CookieStore {
load(): Promise<CookieJar>;
save(cookieJar: CookieJar): Promise<void>;
clear(): Promise<void>;
}
export type AgrarmonitorLoginStrategy = 'auto' | 'auth' | 'legacy';
export interface AgrarmonitorConnectorOptions {
baseUrl?: string;
apiBaseUrl?: string;
apiToken?: string;
username: string;
password: string;
cookieStore: CookieStore;
timeoutMs?: number;
autoLogin?: boolean;
autoRetry?: boolean;
loginStrategy?: AgrarmonitorLoginStrategy;
logger?: Logger;
}
export interface AgrarmonitorConnectorResult {
http: AxiosInstance;
login(): Promise<void>;
clearSession(): Promise<void>;
saveSession(): Promise<void>;
getCookieCount(url?: string): Promise<number>;
checkFreigeschaltet(): Promise<AgrarmonitorFreischaltungStatus>;
checkRegistriert(): Promise<AgrarmonitorRegistrierungStatus>;
registerDevice(options: AgrarmonitorDeviceRegistrationOptions): Promise<AgrarmonitorDeviceRegistrationResult>;
fetchCustomers(options?: AgrarmonitorFetchCustomersOptions): Promise<AgrarmonitorApiCustomer[]>;
}
export interface AgrarmonitorFreischaltungStatus {
freigeschaltet: boolean;
status: number;
redirected: boolean;
redirectLocation: string | null;
timestamp: string;
cookies: number;
}
export interface AgrarmonitorRegistrierungStatus {
registriert: boolean;
status: number;
hasRegistrationText: boolean;
timestamp: string;
cookies: number;
}
export interface AgrarmonitorDeviceRegistrationOptions {
agrarmonitorId: string;
pcName: string;
}
export interface AgrarmonitorDeviceRegistrationResult {
success: boolean;
status: number;
message: string;
data: {
agrarmonitorId: string;
pcName: string;
nonce: string;
};
timestamp: string;
cookies: number;
}
export interface AgrarmonitorFetchCustomersOptions {
perPage?: number;
apiToken?: string;
}
export interface AgrarmonitorApiCustomer {
id: string | number;
vorname?: string;
nachname?: string;
firma?: string;
ist_aktiv?: number | boolean;
bearbeitet_am?: string | number;
[key: string]: unknown;
}
+27
View File
@@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"moduleResolution": "Node",
"lib": [
"ES2022",
"DOM"
],
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "dist",
"rootDir": "src",
"strict": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
},
"include": [
"src/**/*.ts"
],
"exclude": [
"dist",
"node_modules"
]
}