Compare commits

...

4 Commits

Author SHA1 Message Date
aaf3c9cb1c v11.1.0
Some checks failed
Docker (tags) / security (push) Failing after 0s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2026-03-06 07:52:10 +00:00
abde872ab2 feat(apiclient): add TypeScript API client (ts_apiclient) with resource managers and package exports 2026-03-06 07:52:10 +00:00
ca2d2b09ad v11.0.51
Some checks failed
Docker (tags) / security (push) Failing after 0s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2026-03-05 19:06:53 +00:00
fb7d4d988b fix(build): include HTML files in tsbundle output and bump tsbuild/tsbundle devDependencies 2026-03-05 19:06:53 +00:00
19 changed files with 1867 additions and 246 deletions

View File

@@ -1,5 +1,20 @@
# Changelog # Changelog
## 2026-03-06 - 11.1.0 - feat(apiclient)
add TypeScript API client (ts_apiclient) with resource managers and package exports
- Add new ts_apiclient module providing DcRouterApiClient and resource managers: routes, certificates, api tokens, remote ingress, emails, stats, config, logs, and radius (with sub-managers).
- Add resource classes and builders (Route, RemoteIngress, ApiToken, Certificate, Email) and convenience manager APIs for common operations.
- Export apiclient in package.json (exports and files) and add ts_apiclient index and plugins wrapper for @api.global/typedrequest.
- Add comprehensive tests for the API client (test/test.apiclient.ts).
- Bump devDependencies: @git.zone/tsbuild -> ^4.3.0 and @types/node -> ^25.3.5
## 2026-03-05 - 11.0.51 - fix(build)
include HTML files in tsbundle output and bump tsbuild/tsbundle devDependencies
- Add includeFiles: ["./html/**/*.html"] to bundler config in npmextra.json so HTML assets are included in the bundle
- Bump devDependencies: @git.zone/tsbuild ^4.2.4 -> ^4.2.6, @git.zone/tsbundle ^2.9.0 -> ^2.9.1 (non-breaking tooling updates)
## 2026-03-05 - 11.0.50 - fix(devDependencies) ## 2026-03-05 - 11.0.50 - fix(devDependencies)
bump @git.zone/tsbuild to ^4.2.4 bump @git.zone/tsbuild to ^4.2.4

View File

@@ -22,7 +22,8 @@
"to": "./dist_serve/bundle.js", "to": "./dist_serve/bundle.js",
"outputMode": "bundle", "outputMode": "bundle",
"bundler": "esbuild", "bundler": "esbuild",
"production": true "production": true,
"includeFiles": ["./html/**/*.html"]
} }
] ]
}, },

View File

@@ -1,12 +1,13 @@
{ {
"name": "@serve.zone/dcrouter", "name": "@serve.zone/dcrouter",
"private": false, "private": false,
"version": "11.0.50", "version": "11.1.0",
"description": "A multifaceted routing service handling mail and SMS delivery functions.", "description": "A multifaceted routing service handling mail and SMS delivery functions.",
"type": "module", "type": "module",
"exports": { "exports": {
".": "./dist_ts/index.js", ".": "./dist_ts/index.js",
"./interfaces": "./dist_ts_interfaces/index.js" "./interfaces": "./dist_ts_interfaces/index.js",
"./apiclient": "./dist_ts_apiclient/index.js"
}, },
"author": "Task Venture Capital GmbH", "author": "Task Venture Capital GmbH",
"license": "MIT", "license": "MIT",
@@ -19,12 +20,12 @@
"watch": "tswatch" "watch": "tswatch"
}, },
"devDependencies": { "devDependencies": {
"@git.zone/tsbuild": "^4.2.4", "@git.zone/tsbuild": "^4.3.0",
"@git.zone/tsbundle": "^2.9.0", "@git.zone/tsbundle": "^2.9.1",
"@git.zone/tsrun": "^2.0.1", "@git.zone/tsrun": "^2.0.1",
"@git.zone/tstest": "^3.2.0", "@git.zone/tstest": "^3.2.0",
"@git.zone/tswatch": "^3.2.5", "@git.zone/tswatch": "^3.2.5",
"@types/node": "^25.3.3" "@types/node": "^25.3.5"
}, },
"dependencies": { "dependencies": {
"@api.global/typedrequest": "^3.3.0", "@api.global/typedrequest": "^3.3.0",
@@ -100,10 +101,12 @@
"files": [ "files": [
"ts/**/*", "ts/**/*",
"ts_web/**/*", "ts_web/**/*",
"ts_apiclient/**/*",
"dist/**/*", "dist/**/*",
"dist_*/**/*", "dist_*/**/*",
"dist_ts/**/*", "dist_ts/**/*",
"dist_ts_web/**/*", "dist_ts_web/**/*",
"dist_ts_apiclient/**/*",
"assets/**/*", "assets/**/*",
"cli.js", "cli.js",
"npmextra.json", "npmextra.json",

477
pnpm-lock.yaml generated
View File

@@ -115,11 +115,11 @@ importers:
version: 13.0.0 version: 13.0.0
devDependencies: devDependencies:
'@git.zone/tsbuild': '@git.zone/tsbuild':
specifier: ^4.2.4 specifier: ^4.3.0
version: 4.2.4 version: 4.3.0
'@git.zone/tsbundle': '@git.zone/tsbundle':
specifier: ^2.9.0 specifier: ^2.9.1
version: 2.9.0 version: 2.9.1
'@git.zone/tsrun': '@git.zone/tsrun':
specifier: ^2.0.1 specifier: ^2.0.1
version: 2.0.1 version: 2.0.1
@@ -130,8 +130,8 @@ importers:
specifier: ^3.2.5 specifier: ^3.2.5
version: 3.2.5(@tiptap/pm@2.27.2) version: 3.2.5(@tiptap/pm@2.27.2)
'@types/node': '@types/node':
specifier: ^25.3.3 specifier: ^25.3.5
version: 25.3.3 version: 25.3.5
packages: packages:
@@ -178,127 +178,127 @@ packages:
'@aws-crypto/util@5.2.0': '@aws-crypto/util@5.2.0':
resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==}
'@aws-sdk/client-s3@3.1002.0': '@aws-sdk/client-s3@3.1003.0':
resolution: {integrity: sha512-tc+vZgvjcm+1Ot+YhQjXZxVELKGGGO3D5cuR4p5xaeitXYX2+RRiz4/WdSak9slumIClnlXsdqhJ0OHognUT+w==} resolution: {integrity: sha512-on8GvIWeH1pD0l53NuKbPO84bEC1mk/9zskgU+dVKcVoGxOZI94fVddCJb+IwIUN6rfBHCfXPCVbgVyzsHTAVg==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/core@3.973.17': '@aws-sdk/core@3.973.18':
resolution: {integrity: sha512-VtgGP0TjbCeyp6DQpiBqJKbemTSIaN2bZc3UbeTDCani3lBCyxn75ouJYD6koSSp0bh7rKLEbUpiFsNCI7tr0w==} resolution: {integrity: sha512-GUIlegfcK2LO1J2Y98sCJy63rQSiLiDOgVw7HiHPRqfI2vb3XozTVqemwO0VSGXp54ngCnAQz0Lf0YPCBINNxA==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/crc64-nvme@3.972.3': '@aws-sdk/crc64-nvme@3.972.4':
resolution: {integrity: sha512-UExeK+EFiq5LAcbHm96CQLSia+5pvpUVSAsVApscBzayb7/6dJBJKwV4/onsk4VbWSmqxDMcfuTD+pC4RxgZHg==} resolution: {integrity: sha512-HKZIZLbRyvzo/bXZU7Zmk6XqU+1C9DjI56xd02vwuDIxedxBEqP17t9ExhbP9QFeNq/a3l9GOcyirFXxmbDhmw==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/credential-provider-env@3.972.15': '@aws-sdk/credential-provider-env@3.972.16':
resolution: {integrity: sha512-RhHQG1lhkWHL4tK1C/KDjaOeis+9U0tAMnWDiwiSVQZMC7CsST9Xin+sK89XywJ5g/tyABtb7TvFePJ4Te5XSQ==} resolution: {integrity: sha512-HrdtnadvTGAQUr18sPzGlE5El3ICphnH6SU7UQOMOWFgRKbTRNN8msTxM4emzguUso9CzaHU2xy5ctSrmK5YNA==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/credential-provider-http@3.972.17': '@aws-sdk/credential-provider-http@3.972.18':
resolution: {integrity: sha512-b/bDL76p51+yQ+0O9ZDH5nw/ioE0sRYkjwjOwFWAWZXo6it2kQZUOXhVpjohx3ldKyUxt/SwAivjUu1Nr/PWlQ==} resolution: {integrity: sha512-NyB6smuZAixND5jZumkpkunQ0voc4Mwgkd+SZ6cvAzIB7gK8HV8Zd4rS8Kn5MmoGgusyNfVGG+RLoYc4yFiw+A==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/credential-provider-ini@3.972.15': '@aws-sdk/credential-provider-ini@3.972.16':
resolution: {integrity: sha512-qWnM+wB8MmU2kKY7f4KowKjOjkwRosaFxrtseEEIefwoXn1SjN+CbHzXBVdTAQxxkbBiqhPgJ/WHiPtES4grRQ==} resolution: {integrity: sha512-hzAnzNXKV0A4knFRWGu2NCt72P4WWxpEGnOc6H3DptUjC4oX3hGw846oN76M1rTHAOwDdbhjU0GAOWR4OUfTZg==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/credential-provider-login@3.972.15': '@aws-sdk/credential-provider-login@3.972.16':
resolution: {integrity: sha512-x92FJy34/95wgu+qOGD8SHcgh1hZ9Qx2uFtQEGn4m9Ljou8ICIv3Ybq5yxdB7A60S8ZGCQB0mIopmjJwiLbh5g==} resolution: {integrity: sha512-VI0kXTlr0o1FTay+Jvx6AKqx5ECBgp7X4VevGBEbuXdCXnNp7SPU0KvjsOLVhIz3OoPK4/lTXphk43t0IVk65w==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/credential-provider-node@3.972.16': '@aws-sdk/credential-provider-node@3.972.17':
resolution: {integrity: sha512-7mlt14Ee4rPFAFUVgpWE7+0CBhetJJyzVFqfIsMp7sgyOSm9Y/+qHZOWAuK5I4JNc+Y5PltvJ9kssTzRo92iXQ==} resolution: {integrity: sha512-98MAcQ2Dk7zkvgwZ5f6fLX2lTyptC3gTSDx4EpvTdJWET8qs9lBPYggoYx7GmKp/5uk0OwVl0hxIDZsDNS/Y9g==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/credential-provider-process@3.972.15': '@aws-sdk/credential-provider-process@3.972.16':
resolution: {integrity: sha512-PrH3iTeD18y/8uJvQD2s/T87BTGhsdS/1KZU7ReWHXsplBwvCqi7AbnnNbML1pFlQwRWCE2RdSZFWDVId3CvkA==} resolution: {integrity: sha512-n89ibATwnLEg0ZdZmUds5bq8AfBAdoYEDpqP3uzPLaRuGelsKlIvCYSNNvfgGLi8NaHPNNhs1HjJZYbqkW9b+g==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/credential-provider-sso@3.972.15': '@aws-sdk/credential-provider-sso@3.972.16':
resolution: {integrity: sha512-M/+LBHTPKZxxXckM6m4dnJeR+jlm9NynH9b2YDswN4Zj2St05SK/crdL3Wy3WfJTZootnnhm3oTh87Usl7PS7w==} resolution: {integrity: sha512-b9of7tQgERxgcEcwAFWvRe84ivw+Kw6b3jVuz/6LQzonkomiY5UoWfprkbjc8FSCQ2VjDqKTvIRA9F0KSQ025w==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/credential-provider-web-identity@3.972.15': '@aws-sdk/credential-provider-web-identity@3.972.16':
resolution: {integrity: sha512-QTH6k93v+UOfFam/ado8zc71tH+enTVyuvLy9uEWXX1x894dN5ovtf/MdBDgFwq3g6c9mbtgVJ4B+yBqDtXvdA==} resolution: {integrity: sha512-PaOH5jFoPQX4WkqpKzKh9cM7rieKtbgEGqrZ+ybGmotJhcvhI/xl69yCwMbHGnpQJJmHZIX9q2zaPB7HTBn/4w==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-bucket-endpoint@3.972.6': '@aws-sdk/middleware-bucket-endpoint@3.972.7':
resolution: {integrity: sha512-3H2bhvb7Cb/S6WFsBy/Dy9q2aegC9JmGH1inO8Lb2sWirSqpLJlZmvQHPE29h2tIxzv6el/14X/tLCQ8BQU6ZQ==} resolution: {integrity: sha512-goX+axlJ6PQlRnzE2bQisZ8wVrlm6dXJfBzMJhd8LhAIBan/w1Kl73fJnalM/S+18VnpzIHumyV6DtgmvqG5IA==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-expect-continue@3.972.6': '@aws-sdk/middleware-expect-continue@3.972.7':
resolution: {integrity: sha512-QMdffpU+GkSGC+bz6WdqlclqIeCsOfgX8JFZ5xvwDtX+UTj4mIXm3uXu7Ko6dBseRcJz1FA6T9OmlAAY6JgJUg==} resolution: {integrity: sha512-mvWqvm61bmZUKmmrtl2uWbokqpenY3Mc3Jf4nXB/Hse6gWxLPaCQThmhPBDzsPSV8/Odn8V6ovWt3pZ7vy4BFQ==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-flexible-checksums@3.973.3': '@aws-sdk/middleware-flexible-checksums@3.973.4':
resolution: {integrity: sha512-C9Mu9pXMpQh7jBydx0MrfQxNIKwJvKbVbJJ0GZthM+cQ+KTChXA01MwttRsMq0ZRb4pBJZQtIKDUxXusDr5OKg==} resolution: {integrity: sha512-7CH2jcGmkvkHc5Buz9IGbdjq1729AAlgYJiAvGq7qhCHqYleCsriWdSnmsqWTwdAfXHMT+pkxX3w6v5tJNcSug==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-host-header@3.972.6': '@aws-sdk/middleware-host-header@3.972.7':
resolution: {integrity: sha512-5XHwjPH1lHB+1q4bfC7T8Z5zZrZXfaLcjSMwTd1HPSPrCmPFMbg3UQ5vgNWcVj0xoX4HWqTGkSf2byrjlnRg5w==} resolution: {integrity: sha512-aHQZgztBFEpDU1BB00VWCIIm85JjGjQW1OG9+98BdmaOpguJvzmXBGbnAiYcciCd+IS4e9BEq664lhzGnWJHgQ==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-location-constraint@3.972.6': '@aws-sdk/middleware-location-constraint@3.972.7':
resolution: {integrity: sha512-XdZ2TLwyj3Am6kvUc67vquQvs6+D8npXvXgyEUJAdkUDx5oMFJKOqpK+UpJhVDsEL068WAJl2NEGzbSik7dGJQ==} resolution: {integrity: sha512-vdK1LJfffBp87Lj0Bw3WdK1rJk9OLDYdQpqoKgmpIZPe+4+HawZ6THTbvjhJt4C4MNnRrHTKHQjkwBiIpDBoig==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-logger@3.972.6': '@aws-sdk/middleware-logger@3.972.7':
resolution: {integrity: sha512-iFnaMFMQdljAPrvsCVKYltPt2j40LQqukAbXvW7v0aL5I+1GO7bZ/W8m12WxW3gwyK5p5u1WlHg8TSAizC5cZw==} resolution: {integrity: sha512-LXhiWlWb26txCU1vcI9PneESSeRp/RYY/McuM4SpdrimQR5NgwaPb4VJCadVeuGWgh6QmqZ6rAKSoL1ob16W6w==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-recursion-detection@3.972.6': '@aws-sdk/middleware-recursion-detection@3.972.7':
resolution: {integrity: sha512-dY4v3of5EEMvik6+UDwQ96KfUFDk8m1oZDdkSc5lwi4o7rFrjnv0A+yTV+gu230iybQZnKgDLg/rt2P3H+Vscw==} resolution: {integrity: sha512-l2VQdcBcYLzIzykCHtXlbpiVCZ94/xniLIkAj0jpnpjY4xlgZx7f56Ypn+uV1y3gG0tNVytJqo3K9bfMFee7SQ==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-sdk-s3@3.972.17': '@aws-sdk/middleware-sdk-s3@3.972.18':
resolution: {integrity: sha512-uSyOGoVFMP44pTt29MIMfsOjegqE/7lT0K3HG0GWPiH2lD4rqZC/TRi/kH4zrGiOQdsaLc+dkfd7Sb2q8vh+gA==} resolution: {integrity: sha512-5E3XxaElrdyk6ZJ0TjH7Qm6ios4b/qQCiLr6oQ8NK7e4Kn6JBTJCaYioQCQ65BpZ1+l1mK5wTAac2+pEz0Smpw==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-ssec@3.972.6': '@aws-sdk/middleware-ssec@3.972.7':
resolution: {integrity: sha512-acvMUX9jF4I2Ew+Z/EA6gfaFaz9ehci5wxBmXCZeulLuv8m+iGf6pY9uKz8TPjg39bdAz3hxoE0eLP8Qz+IYlA==} resolution: {integrity: sha512-G9clGVuAml7d8DYzY6DnRi7TIIDRvZ3YpqJPz/8wnWS5fYx/FNWNmkO6iJVlVkQg9BfeMzd+bVPtPJOvC4B+nQ==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-user-agent@3.972.17': '@aws-sdk/middleware-user-agent@3.972.18':
resolution: {integrity: sha512-HHArkgWzomuwufXwheQqkddu763PWCpoNTq1dGjqXzJT/lojX3VlOqjNSR2Xvb6/T9ISfwYcMOcbFgUp4EWxXA==} resolution: {integrity: sha512-KcqQDs/7WtoEnp52+879f8/i1XAJkgka5i4arOtOCPR10o4wWo3VRecDI9Gxoh6oghmLCnIiOSKyRcXI/50E+w==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/nested-clients@3.996.5': '@aws-sdk/nested-clients@3.996.6':
resolution: {integrity: sha512-zn0WApcULn7Rtl6T+KP2CQTZo/7wOa2YV1yHQnbijTQoi4YXQHM8s21JcJzt33/mqPh8AdvWX1f+83KvKuxlZw==} resolution: {integrity: sha512-blNJ3ugn4gCQ9ZSZi/firzKCvVl5LvPFVxv24LprENeWI4R8UApG006UQkF4SkmLygKq2BQXRad2/anQ13Te4Q==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/region-config-resolver@3.972.6': '@aws-sdk/region-config-resolver@3.972.7':
resolution: {integrity: sha512-Aa5PusHLXAqLTX1UKDvI3pHQJtIsF7Q+3turCHqfz/1F61/zDMWfbTC8evjhrrYVAtz9Vsv3SJ/waSUeu7B6gw==} resolution: {integrity: sha512-/Ev/6AI8bvt4HAAptzSjThGUMjcWaX3GX8oERkB0F0F9x2dLSBdgFDiyrRz3i0u0ZFZFQ1b28is4QhyqXTUsVA==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/signature-v4-multi-region@3.996.5': '@aws-sdk/signature-v4-multi-region@3.996.6':
resolution: {integrity: sha512-AVIhf74wRMzU1WBPVzcGPjlADF5VxZ8m8Ctm1v7eO4/reWMhZnEBn4tlR4vM4pOYFkdrYp3MTzYVZIikCO+53Q==} resolution: {integrity: sha512-NnsOQsVmJXy4+IdPFUjRCWPn9qNH1TzS/f7MiWgXeoHs903tJpAWQWQtoFvLccyPoBgomKP9L89RRr2YsT/L0g==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/token-providers@3.1002.0': '@aws-sdk/token-providers@3.1003.0':
resolution: {integrity: sha512-x972uKOydFn4Rb0PZJzLdNW59rH0KWC78Q2JbQzZpGlGt0DxjYdDRwBG6F42B1MyaEwHGqO/tkGc4r3/PRFfMw==} resolution: {integrity: sha512-SOyyWNdT7njKRwtZ1JhwHlH1csv6Pkgf305X96/OIfnhq1pU/EjmT6W6por57rVrjrKuHBuEIXgpWv8OgoMHpg==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/types@3.973.4': '@aws-sdk/types@3.973.5':
resolution: {integrity: sha512-RW60aH26Bsc016Y9B98hC0Plx6fK5P2v/iQYwMzrSjiDh1qRMUCP6KrXHYEHe3uFvKiOC93Z9zk4BJsUi6Tj1Q==} resolution: {integrity: sha512-hl7BGwDCWsjH8NkZfx+HgS7H2LyM2lTMAI7ba9c8O0KqdBLTdNJivsHpqjg9rNlAlPyREb6DeDRXUl0s8uFdmQ==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/util-arn-parser@3.972.2': '@aws-sdk/util-arn-parser@3.972.3':
resolution: {integrity: sha512-VkykWbqMjlSgBFDyrY3nOSqupMc6ivXuGmvci6Q3NnLq5kC+mKQe2QBZ4nrWRE/jqOxeFP2uYzLtwncYYcvQDg==} resolution: {integrity: sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/util-endpoints@3.996.3': '@aws-sdk/util-endpoints@3.996.4':
resolution: {integrity: sha512-yWIQSNiCjykLL+ezN5A+DfBb1gfXTytBxm57e64lYmwxDHNmInYHRJYYRAGWG1o77vKEiWaw4ui28e3yb1k5aQ==} resolution: {integrity: sha512-Hek90FBmd4joCFj+Vc98KLJh73Zqj3s2W56gjAcTkrNLMDI5nIFkG9YpfcJiVI1YlE2Ne1uOQNe+IgQ/Vz2XRA==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/util-locate-window@3.965.4': '@aws-sdk/util-locate-window@3.965.5':
resolution: {integrity: sha512-H1onv5SkgPBK2P6JR2MjGgbOnttoNzSPIRoeZTNPZYyaplwGg50zS3amXvXqF0/qfXpWEC9rLWU564QTB9bSog==} resolution: {integrity: sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws-sdk/util-user-agent-browser@3.972.6': '@aws-sdk/util-user-agent-browser@3.972.7':
resolution: {integrity: sha512-Fwr/llD6GOrFgQnKaI2glhohdGuBDfHfora6iG9qsBBBR8xv1SdCSwbtf5CWlUdCw5X7g76G/9Hf0Inh0EmoxA==} resolution: {integrity: sha512-7SJVuvhKhMF/BkNS1n0QAJYgvEwYbK2QLKBrzDiwQGiTRU6Yf1f3nehTzm/l21xdAOtWSfp2uWSddPnP2ZtsVw==}
'@aws-sdk/util-user-agent-node@3.973.2': '@aws-sdk/util-user-agent-node@3.973.3':
resolution: {integrity: sha512-lpaIuekdkpw7VRiik0IZmd6TyvEUcuLgKZ5fKRGpCA3I4PjrD/XH15sSwW+OptxQjNU4DEzSxag70spC9SluvA==} resolution: {integrity: sha512-8s2cQmTUOwcBlIJyI9PAZNnnnF+cGtdhHc1yzMMsSD/GR/Hxj7m0IGUE92CslXXb8/p5Q76iqOCjN1GFwyf+1A==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
peerDependencies: peerDependencies:
aws-crt: '>=1.0.0' aws-crt: '>=1.0.0'
@@ -306,8 +306,8 @@ packages:
aws-crt: aws-crt:
optional: true optional: true
'@aws-sdk/xml-builder@3.972.9': '@aws-sdk/xml-builder@3.972.10':
resolution: {integrity: sha512-ItnlMgSqkPrUfJs7EsvU/01zw5UeIb2tNPhD09LBLHbg+g+HDiKibSLwpkuz/ZIlz4F2IMn+5XgE4AK/pfPuog==} resolution: {integrity: sha512-OnejAIVD+CxzyAUrVic7lG+3QRltyja9LoNqCE/1YVs8ichoTbJlVSaZ9iSMcnHLyzrSNtvaOGjSDRP+d/ouFA==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@aws/lambda-invoke-store@0.2.3': '@aws/lambda-invoke-store@0.2.3':
@@ -538,12 +538,12 @@ packages:
resolution: {integrity: sha512-YTVITFGN0/24PxzXrwqCgnyd7njDuzp5ZvaCx5nq/jg55kUYd94Nj8UTchBdBofi/L0nwRfjGOg0E41d2u9T1w==} resolution: {integrity: sha512-YTVITFGN0/24PxzXrwqCgnyd7njDuzp5ZvaCx5nq/jg55kUYd94Nj8UTchBdBofi/L0nwRfjGOg0E41d2u9T1w==}
engines: {node: '>=6'} engines: {node: '>=6'}
'@git.zone/tsbuild@4.2.4': '@git.zone/tsbuild@4.3.0':
resolution: {integrity: sha512-jZePRGp1N2p99iz5t01sCgeZ+mAqCiO4E6avuDUX8OSfpwEXvGvzGH235TKtVTh7Roru/BB3yv4S0AS6AgPklA==} resolution: {integrity: sha512-lb6eMQ8RQPaJqAB4kC++GIElOiTAH1pClmoND/q7XHuiMZxv6cXz2/U/sZt339mon2c40dXRG2tkLF2jRsP0pQ==}
hasBin: true hasBin: true
'@git.zone/tsbundle@2.9.0': '@git.zone/tsbundle@2.9.1':
resolution: {integrity: sha512-itXX/oiJjrRHUlIGTHUEqSwPuGwsG4Cq8kh7aqFOm8mYzJwtXYE1gBqLJTWZma6gI5n+xAk5qTxTyfikuPgWQA==} resolution: {integrity: sha512-JW1xjSv7UjAm2lwAQPxhCWs14wqs+UIq5FqIGUPuI6rrDBWIMT2d0gpP6iP6TqXqgm6XpBlfU4rHcHheUXzXbQ==}
hasBin: true hasBin: true
'@git.zone/tspublish@1.11.2': '@git.zone/tspublish@1.11.2':
@@ -905,8 +905,8 @@ packages:
'@push.rocks/smartfs@1.3.1': '@push.rocks/smartfs@1.3.1':
resolution: {integrity: sha512-ZSduVS8tM+/erbyCTvRRvc9gLWwbpqN5xdIIkMr+gub7fowSeJb7tR2rnGwySa63DyimU0q2KTp79VV9YqGLeg==} resolution: {integrity: sha512-ZSduVS8tM+/erbyCTvRRvc9gLWwbpqN5xdIIkMr+gub7fowSeJb7tR2rnGwySa63DyimU0q2KTp79VV9YqGLeg==}
'@push.rocks/smartfs@1.3.3': '@push.rocks/smartfs@1.4.0':
resolution: {integrity: sha512-IF16dgeDFDv65j+lhyhqjhm6gFhBrWTrnayVOCwbBg4yJ/6tNpd7sL8YsvBJRBRCBWrMFBCSL7thG0Thvq0VZA==} resolution: {integrity: sha512-4PgteGOyMBiUWKLfTXOjxrZz+sXPLnvcmHeAEHY2gwZJflfp5/YDVPhodctOydersXzkynO359dIGLSCyQnnAQ==}
'@push.rocks/smartguard@3.1.0': '@push.rocks/smartguard@3.1.0':
resolution: {integrity: sha512-J23q84f1O+TwFGmd4lrO9XLHUh2DaLXo9PN/9VmTWYzTkQDv5JehmifXVI0esophXcCIfbdIu6hbt7/aHlDF4A==} resolution: {integrity: sha512-J23q84f1O+TwFGmd4lrO9XLHUh2DaLXo9PN/9VmTWYzTkQDv5JehmifXVI0esophXcCIfbdIu6hbt7/aHlDF4A==}
@@ -1789,11 +1789,11 @@ packages:
'@types/node@18.19.130': '@types/node@18.19.130':
resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==}
'@types/node@22.19.13': '@types/node@22.19.15':
resolution: {integrity: sha512-akNQMv0wW5uyRpD2v2IEyRSZiR+BeGuoB6L310EgGObO44HSMNT8z1xzio28V8qOrgYaopIDNA18YgdXd+qTiw==} resolution: {integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==}
'@types/node@25.3.3': '@types/node@25.3.5':
resolution: {integrity: sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==} resolution: {integrity: sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA==}
'@types/ping@0.4.4': '@types/ping@0.4.4':
resolution: {integrity: sha512-ifvo6w2f5eJYlXm+HiVx67iJe8WZp87sfa683nlqED5Vnt9Z93onkokNoWqOG21EaE8fMxyKPobE+mkPEyxsdw==} resolution: {integrity: sha512-ifvo6w2f5eJYlXm+HiVx67iJe8WZp87sfa683nlqED5Vnt9Z93onkokNoWqOG21EaE8fMxyKPobE+mkPEyxsdw==}
@@ -4197,21 +4197,21 @@ snapshots:
'@aws-crypto/crc32@5.2.0': '@aws-crypto/crc32@5.2.0':
dependencies: dependencies:
'@aws-crypto/util': 5.2.0 '@aws-crypto/util': 5.2.0
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
tslib: 2.8.1 tslib: 2.8.1
'@aws-crypto/crc32c@5.2.0': '@aws-crypto/crc32c@5.2.0':
dependencies: dependencies:
'@aws-crypto/util': 5.2.0 '@aws-crypto/util': 5.2.0
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
tslib: 2.8.1 tslib: 2.8.1
'@aws-crypto/sha1-browser@5.2.0': '@aws-crypto/sha1-browser@5.2.0':
dependencies: dependencies:
'@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0
'@aws-crypto/util': 5.2.0 '@aws-crypto/util': 5.2.0
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@aws-sdk/util-locate-window': 3.965.4 '@aws-sdk/util-locate-window': 3.965.5
'@smithy/util-utf8': 2.3.0 '@smithy/util-utf8': 2.3.0
tslib: 2.8.1 tslib: 2.8.1
@@ -4220,15 +4220,15 @@ snapshots:
'@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/sha256-js': 5.2.0
'@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0
'@aws-crypto/util': 5.2.0 '@aws-crypto/util': 5.2.0
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@aws-sdk/util-locate-window': 3.965.4 '@aws-sdk/util-locate-window': 3.965.5
'@smithy/util-utf8': 2.3.0 '@smithy/util-utf8': 2.3.0
tslib: 2.8.1 tslib: 2.8.1
'@aws-crypto/sha256-js@5.2.0': '@aws-crypto/sha256-js@5.2.0':
dependencies: dependencies:
'@aws-crypto/util': 5.2.0 '@aws-crypto/util': 5.2.0
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
tslib: 2.8.1 tslib: 2.8.1
'@aws-crypto/supports-web-crypto@5.2.0': '@aws-crypto/supports-web-crypto@5.2.0':
@@ -4237,33 +4237,33 @@ snapshots:
'@aws-crypto/util@5.2.0': '@aws-crypto/util@5.2.0':
dependencies: dependencies:
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/util-utf8': 2.3.0 '@smithy/util-utf8': 2.3.0
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/client-s3@3.1002.0': '@aws-sdk/client-s3@3.1003.0':
dependencies: dependencies:
'@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha1-browser': 5.2.0
'@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0
'@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/sha256-js': 5.2.0
'@aws-sdk/core': 3.973.17 '@aws-sdk/core': 3.973.18
'@aws-sdk/credential-provider-node': 3.972.16 '@aws-sdk/credential-provider-node': 3.972.17
'@aws-sdk/middleware-bucket-endpoint': 3.972.6 '@aws-sdk/middleware-bucket-endpoint': 3.972.7
'@aws-sdk/middleware-expect-continue': 3.972.6 '@aws-sdk/middleware-expect-continue': 3.972.7
'@aws-sdk/middleware-flexible-checksums': 3.973.3 '@aws-sdk/middleware-flexible-checksums': 3.973.4
'@aws-sdk/middleware-host-header': 3.972.6 '@aws-sdk/middleware-host-header': 3.972.7
'@aws-sdk/middleware-location-constraint': 3.972.6 '@aws-sdk/middleware-location-constraint': 3.972.7
'@aws-sdk/middleware-logger': 3.972.6 '@aws-sdk/middleware-logger': 3.972.7
'@aws-sdk/middleware-recursion-detection': 3.972.6 '@aws-sdk/middleware-recursion-detection': 3.972.7
'@aws-sdk/middleware-sdk-s3': 3.972.17 '@aws-sdk/middleware-sdk-s3': 3.972.18
'@aws-sdk/middleware-ssec': 3.972.6 '@aws-sdk/middleware-ssec': 3.972.7
'@aws-sdk/middleware-user-agent': 3.972.17 '@aws-sdk/middleware-user-agent': 3.972.18
'@aws-sdk/region-config-resolver': 3.972.6 '@aws-sdk/region-config-resolver': 3.972.7
'@aws-sdk/signature-v4-multi-region': 3.996.5 '@aws-sdk/signature-v4-multi-region': 3.996.6
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@aws-sdk/util-endpoints': 3.996.3 '@aws-sdk/util-endpoints': 3.996.4
'@aws-sdk/util-user-agent-browser': 3.972.6 '@aws-sdk/util-user-agent-browser': 3.972.7
'@aws-sdk/util-user-agent-node': 3.973.2 '@aws-sdk/util-user-agent-node': 3.973.3
'@smithy/config-resolver': 4.4.10 '@smithy/config-resolver': 4.4.10
'@smithy/core': 3.23.8 '@smithy/core': 3.23.8
'@smithy/eventstream-serde-browser': 4.2.11 '@smithy/eventstream-serde-browser': 4.2.11
@@ -4301,10 +4301,10 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- aws-crt - aws-crt
'@aws-sdk/core@3.973.17': '@aws-sdk/core@3.973.18':
dependencies: dependencies:
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@aws-sdk/xml-builder': 3.972.9 '@aws-sdk/xml-builder': 3.972.10
'@smithy/core': 3.23.8 '@smithy/core': 3.23.8
'@smithy/node-config-provider': 4.3.11 '@smithy/node-config-provider': 4.3.11
'@smithy/property-provider': 4.2.11 '@smithy/property-provider': 4.2.11
@@ -4317,23 +4317,23 @@ snapshots:
'@smithy/util-utf8': 4.2.2 '@smithy/util-utf8': 4.2.2
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/crc64-nvme@3.972.3': '@aws-sdk/crc64-nvme@3.972.4':
dependencies: dependencies:
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/credential-provider-env@3.972.15': '@aws-sdk/credential-provider-env@3.972.16':
dependencies: dependencies:
'@aws-sdk/core': 3.973.17 '@aws-sdk/core': 3.973.18
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/property-provider': 4.2.11 '@smithy/property-provider': 4.2.11
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/credential-provider-http@3.972.17': '@aws-sdk/credential-provider-http@3.972.18':
dependencies: dependencies:
'@aws-sdk/core': 3.973.17 '@aws-sdk/core': 3.973.18
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/fetch-http-handler': 5.3.13 '@smithy/fetch-http-handler': 5.3.13
'@smithy/node-http-handler': 4.4.14 '@smithy/node-http-handler': 4.4.14
'@smithy/property-provider': 4.2.11 '@smithy/property-provider': 4.2.11
@@ -4343,17 +4343,17 @@ snapshots:
'@smithy/util-stream': 4.5.17 '@smithy/util-stream': 4.5.17
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/credential-provider-ini@3.972.15': '@aws-sdk/credential-provider-ini@3.972.16':
dependencies: dependencies:
'@aws-sdk/core': 3.973.17 '@aws-sdk/core': 3.973.18
'@aws-sdk/credential-provider-env': 3.972.15 '@aws-sdk/credential-provider-env': 3.972.16
'@aws-sdk/credential-provider-http': 3.972.17 '@aws-sdk/credential-provider-http': 3.972.18
'@aws-sdk/credential-provider-login': 3.972.15 '@aws-sdk/credential-provider-login': 3.972.16
'@aws-sdk/credential-provider-process': 3.972.15 '@aws-sdk/credential-provider-process': 3.972.16
'@aws-sdk/credential-provider-sso': 3.972.15 '@aws-sdk/credential-provider-sso': 3.972.16
'@aws-sdk/credential-provider-web-identity': 3.972.15 '@aws-sdk/credential-provider-web-identity': 3.972.16
'@aws-sdk/nested-clients': 3.996.5 '@aws-sdk/nested-clients': 3.996.6
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/credential-provider-imds': 4.2.11 '@smithy/credential-provider-imds': 4.2.11
'@smithy/property-provider': 4.2.11 '@smithy/property-provider': 4.2.11
'@smithy/shared-ini-file-loader': 4.4.6 '@smithy/shared-ini-file-loader': 4.4.6
@@ -4362,11 +4362,11 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- aws-crt - aws-crt
'@aws-sdk/credential-provider-login@3.972.15': '@aws-sdk/credential-provider-login@3.972.16':
dependencies: dependencies:
'@aws-sdk/core': 3.973.17 '@aws-sdk/core': 3.973.18
'@aws-sdk/nested-clients': 3.996.5 '@aws-sdk/nested-clients': 3.996.6
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/property-provider': 4.2.11 '@smithy/property-provider': 4.2.11
'@smithy/protocol-http': 5.3.11 '@smithy/protocol-http': 5.3.11
'@smithy/shared-ini-file-loader': 4.4.6 '@smithy/shared-ini-file-loader': 4.4.6
@@ -4375,15 +4375,15 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- aws-crt - aws-crt
'@aws-sdk/credential-provider-node@3.972.16': '@aws-sdk/credential-provider-node@3.972.17':
dependencies: dependencies:
'@aws-sdk/credential-provider-env': 3.972.15 '@aws-sdk/credential-provider-env': 3.972.16
'@aws-sdk/credential-provider-http': 3.972.17 '@aws-sdk/credential-provider-http': 3.972.18
'@aws-sdk/credential-provider-ini': 3.972.15 '@aws-sdk/credential-provider-ini': 3.972.16
'@aws-sdk/credential-provider-process': 3.972.15 '@aws-sdk/credential-provider-process': 3.972.16
'@aws-sdk/credential-provider-sso': 3.972.15 '@aws-sdk/credential-provider-sso': 3.972.16
'@aws-sdk/credential-provider-web-identity': 3.972.15 '@aws-sdk/credential-provider-web-identity': 3.972.16
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/credential-provider-imds': 4.2.11 '@smithy/credential-provider-imds': 4.2.11
'@smithy/property-provider': 4.2.11 '@smithy/property-provider': 4.2.11
'@smithy/shared-ini-file-loader': 4.4.6 '@smithy/shared-ini-file-loader': 4.4.6
@@ -4392,21 +4392,21 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- aws-crt - aws-crt
'@aws-sdk/credential-provider-process@3.972.15': '@aws-sdk/credential-provider-process@3.972.16':
dependencies: dependencies:
'@aws-sdk/core': 3.973.17 '@aws-sdk/core': 3.973.18
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/property-provider': 4.2.11 '@smithy/property-provider': 4.2.11
'@smithy/shared-ini-file-loader': 4.4.6 '@smithy/shared-ini-file-loader': 4.4.6
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/credential-provider-sso@3.972.15': '@aws-sdk/credential-provider-sso@3.972.16':
dependencies: dependencies:
'@aws-sdk/core': 3.973.17 '@aws-sdk/core': 3.973.18
'@aws-sdk/nested-clients': 3.996.5 '@aws-sdk/nested-clients': 3.996.6
'@aws-sdk/token-providers': 3.1002.0 '@aws-sdk/token-providers': 3.1003.0
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/property-provider': 4.2.11 '@smithy/property-provider': 4.2.11
'@smithy/shared-ini-file-loader': 4.4.6 '@smithy/shared-ini-file-loader': 4.4.6
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
@@ -4414,11 +4414,11 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- aws-crt - aws-crt
'@aws-sdk/credential-provider-web-identity@3.972.15': '@aws-sdk/credential-provider-web-identity@3.972.16':
dependencies: dependencies:
'@aws-sdk/core': 3.973.17 '@aws-sdk/core': 3.973.18
'@aws-sdk/nested-clients': 3.996.5 '@aws-sdk/nested-clients': 3.996.6
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/property-provider': 4.2.11 '@smithy/property-provider': 4.2.11
'@smithy/shared-ini-file-loader': 4.4.6 '@smithy/shared-ini-file-loader': 4.4.6
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
@@ -4426,31 +4426,31 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- aws-crt - aws-crt
'@aws-sdk/middleware-bucket-endpoint@3.972.6': '@aws-sdk/middleware-bucket-endpoint@3.972.7':
dependencies: dependencies:
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@aws-sdk/util-arn-parser': 3.972.2 '@aws-sdk/util-arn-parser': 3.972.3
'@smithy/node-config-provider': 4.3.11 '@smithy/node-config-provider': 4.3.11
'@smithy/protocol-http': 5.3.11 '@smithy/protocol-http': 5.3.11
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
'@smithy/util-config-provider': 4.2.2 '@smithy/util-config-provider': 4.2.2
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/middleware-expect-continue@3.972.6': '@aws-sdk/middleware-expect-continue@3.972.7':
dependencies: dependencies:
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/protocol-http': 5.3.11 '@smithy/protocol-http': 5.3.11
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/middleware-flexible-checksums@3.973.3': '@aws-sdk/middleware-flexible-checksums@3.973.4':
dependencies: dependencies:
'@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32': 5.2.0
'@aws-crypto/crc32c': 5.2.0 '@aws-crypto/crc32c': 5.2.0
'@aws-crypto/util': 5.2.0 '@aws-crypto/util': 5.2.0
'@aws-sdk/core': 3.973.17 '@aws-sdk/core': 3.973.18
'@aws-sdk/crc64-nvme': 3.972.3 '@aws-sdk/crc64-nvme': 3.972.4
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/is-array-buffer': 4.2.2 '@smithy/is-array-buffer': 4.2.2
'@smithy/node-config-provider': 4.3.11 '@smithy/node-config-provider': 4.3.11
'@smithy/protocol-http': 5.3.11 '@smithy/protocol-http': 5.3.11
@@ -4460,38 +4460,38 @@ snapshots:
'@smithy/util-utf8': 4.2.2 '@smithy/util-utf8': 4.2.2
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/middleware-host-header@3.972.6': '@aws-sdk/middleware-host-header@3.972.7':
dependencies: dependencies:
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/protocol-http': 5.3.11 '@smithy/protocol-http': 5.3.11
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/middleware-location-constraint@3.972.6': '@aws-sdk/middleware-location-constraint@3.972.7':
dependencies: dependencies:
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/middleware-logger@3.972.6': '@aws-sdk/middleware-logger@3.972.7':
dependencies: dependencies:
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/middleware-recursion-detection@3.972.6': '@aws-sdk/middleware-recursion-detection@3.972.7':
dependencies: dependencies:
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@aws/lambda-invoke-store': 0.2.3 '@aws/lambda-invoke-store': 0.2.3
'@smithy/protocol-http': 5.3.11 '@smithy/protocol-http': 5.3.11
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/middleware-sdk-s3@3.972.17': '@aws-sdk/middleware-sdk-s3@3.972.18':
dependencies: dependencies:
'@aws-sdk/core': 3.973.17 '@aws-sdk/core': 3.973.18
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@aws-sdk/util-arn-parser': 3.972.2 '@aws-sdk/util-arn-parser': 3.972.3
'@smithy/core': 3.23.8 '@smithy/core': 3.23.8
'@smithy/node-config-provider': 4.3.11 '@smithy/node-config-provider': 4.3.11
'@smithy/protocol-http': 5.3.11 '@smithy/protocol-http': 5.3.11
@@ -4504,36 +4504,36 @@ snapshots:
'@smithy/util-utf8': 4.2.2 '@smithy/util-utf8': 4.2.2
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/middleware-ssec@3.972.6': '@aws-sdk/middleware-ssec@3.972.7':
dependencies: dependencies:
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/middleware-user-agent@3.972.17': '@aws-sdk/middleware-user-agent@3.972.18':
dependencies: dependencies:
'@aws-sdk/core': 3.973.17 '@aws-sdk/core': 3.973.18
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@aws-sdk/util-endpoints': 3.996.3 '@aws-sdk/util-endpoints': 3.996.4
'@smithy/core': 3.23.8 '@smithy/core': 3.23.8
'@smithy/protocol-http': 5.3.11 '@smithy/protocol-http': 5.3.11
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/nested-clients@3.996.5': '@aws-sdk/nested-clients@3.996.6':
dependencies: dependencies:
'@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0
'@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/sha256-js': 5.2.0
'@aws-sdk/core': 3.973.17 '@aws-sdk/core': 3.973.18
'@aws-sdk/middleware-host-header': 3.972.6 '@aws-sdk/middleware-host-header': 3.972.7
'@aws-sdk/middleware-logger': 3.972.6 '@aws-sdk/middleware-logger': 3.972.7
'@aws-sdk/middleware-recursion-detection': 3.972.6 '@aws-sdk/middleware-recursion-detection': 3.972.7
'@aws-sdk/middleware-user-agent': 3.972.17 '@aws-sdk/middleware-user-agent': 3.972.18
'@aws-sdk/region-config-resolver': 3.972.6 '@aws-sdk/region-config-resolver': 3.972.7
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@aws-sdk/util-endpoints': 3.996.3 '@aws-sdk/util-endpoints': 3.996.4
'@aws-sdk/util-user-agent-browser': 3.972.6 '@aws-sdk/util-user-agent-browser': 3.972.7
'@aws-sdk/util-user-agent-node': 3.973.2 '@aws-sdk/util-user-agent-node': 3.973.3
'@smithy/config-resolver': 4.4.10 '@smithy/config-resolver': 4.4.10
'@smithy/core': 3.23.8 '@smithy/core': 3.23.8
'@smithy/fetch-http-handler': 5.3.13 '@smithy/fetch-http-handler': 5.3.13
@@ -4563,28 +4563,28 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- aws-crt - aws-crt
'@aws-sdk/region-config-resolver@3.972.6': '@aws-sdk/region-config-resolver@3.972.7':
dependencies: dependencies:
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/config-resolver': 4.4.10 '@smithy/config-resolver': 4.4.10
'@smithy/node-config-provider': 4.3.11 '@smithy/node-config-provider': 4.3.11
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/signature-v4-multi-region@3.996.5': '@aws-sdk/signature-v4-multi-region@3.996.6':
dependencies: dependencies:
'@aws-sdk/middleware-sdk-s3': 3.972.17 '@aws-sdk/middleware-sdk-s3': 3.972.18
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/protocol-http': 5.3.11 '@smithy/protocol-http': 5.3.11
'@smithy/signature-v4': 5.3.11 '@smithy/signature-v4': 5.3.11
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/token-providers@3.1002.0': '@aws-sdk/token-providers@3.1003.0':
dependencies: dependencies:
'@aws-sdk/core': 3.973.17 '@aws-sdk/core': 3.973.18
'@aws-sdk/nested-clients': 3.996.5 '@aws-sdk/nested-clients': 3.996.6
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/property-provider': 4.2.11 '@smithy/property-provider': 4.2.11
'@smithy/shared-ini-file-loader': 4.4.6 '@smithy/shared-ini-file-loader': 4.4.6
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
@@ -4592,43 +4592,43 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- aws-crt - aws-crt
'@aws-sdk/types@3.973.4': '@aws-sdk/types@3.973.5':
dependencies: dependencies:
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/util-arn-parser@3.972.2': '@aws-sdk/util-arn-parser@3.972.3':
dependencies: dependencies:
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/util-endpoints@3.996.3': '@aws-sdk/util-endpoints@3.996.4':
dependencies: dependencies:
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
'@smithy/url-parser': 4.2.11 '@smithy/url-parser': 4.2.11
'@smithy/util-endpoints': 3.3.2 '@smithy/util-endpoints': 3.3.2
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/util-locate-window@3.965.4': '@aws-sdk/util-locate-window@3.965.5':
dependencies: dependencies:
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/util-user-agent-browser@3.972.6': '@aws-sdk/util-user-agent-browser@3.972.7':
dependencies: dependencies:
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
bowser: 2.14.1 bowser: 2.14.1
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/util-user-agent-node@3.973.2': '@aws-sdk/util-user-agent-node@3.973.3':
dependencies: dependencies:
'@aws-sdk/middleware-user-agent': 3.972.17 '@aws-sdk/middleware-user-agent': 3.972.18
'@aws-sdk/types': 3.973.4 '@aws-sdk/types': 3.973.5
'@smithy/node-config-provider': 4.3.11 '@smithy/node-config-provider': 4.3.11
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
tslib: 2.8.1 tslib: 2.8.1
'@aws-sdk/xml-builder@3.972.9': '@aws-sdk/xml-builder@3.972.10':
dependencies: dependencies:
'@smithy/types': 4.13.0 '@smithy/types': 4.13.0
fast-xml-parser: 5.4.1 fast-xml-parser: 5.4.1
@@ -4860,14 +4860,14 @@ snapshots:
dependencies: dependencies:
'@fortawesome/fontawesome-common-types': 7.2.0 '@fortawesome/fontawesome-common-types': 7.2.0
'@git.zone/tsbuild@4.2.4': '@git.zone/tsbuild@4.3.0':
dependencies: dependencies:
'@git.zone/tspublish': 1.11.2 '@git.zone/tspublish': 1.11.2
'@push.rocks/early': 4.0.4 '@push.rocks/early': 4.0.4
'@push.rocks/smartcli': 4.0.20 '@push.rocks/smartcli': 4.0.20
'@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartfile': 13.1.2 '@push.rocks/smartfile': 13.1.2
'@push.rocks/smartfs': 1.3.3 '@push.rocks/smartfs': 1.4.0
'@push.rocks/smartlog': 3.2.1 '@push.rocks/smartlog': 3.2.1
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartpromise': 4.2.3
@@ -4882,7 +4882,7 @@ snapshots:
- supports-color - supports-color
- vue - vue
'@git.zone/tsbundle@2.9.0': '@git.zone/tsbundle@2.9.1':
dependencies: dependencies:
'@push.rocks/early': 4.0.4 '@push.rocks/early': 4.0.4
'@push.rocks/npmextra': 5.3.3 '@push.rocks/npmextra': 5.3.3
@@ -4915,7 +4915,7 @@ snapshots:
'@push.rocks/smartcli': 4.0.20 '@push.rocks/smartcli': 4.0.20
'@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartfile': 13.1.2 '@push.rocks/smartfile': 13.1.2
'@push.rocks/smartfs': 1.3.3 '@push.rocks/smartfs': 1.4.0
'@push.rocks/smartlog': 3.2.1 '@push.rocks/smartlog': 3.2.1
'@push.rocks/smartnpm': 2.0.6 '@push.rocks/smartnpm': 2.0.6
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0
@@ -4940,7 +4940,7 @@ snapshots:
'@git.zone/tstest@3.2.0(@tiptap/pm@2.27.2)(socks@2.8.7)(typescript@5.9.3)': '@git.zone/tstest@3.2.0(@tiptap/pm@2.27.2)(socks@2.8.7)(typescript@5.9.3)':
dependencies: dependencies:
'@api.global/typedserver': 8.4.2(@tiptap/pm@2.27.2) '@api.global/typedserver': 8.4.2(@tiptap/pm@2.27.2)
'@git.zone/tsbundle': 2.9.0 '@git.zone/tsbundle': 2.9.1
'@git.zone/tsrun': 2.0.1 '@git.zone/tsrun': 2.0.1
'@push.rocks/consolecolor': 2.0.3 '@push.rocks/consolecolor': 2.0.3
'@push.rocks/qenv': 6.1.3 '@push.rocks/qenv': 6.1.3
@@ -4989,7 +4989,7 @@ snapshots:
'@git.zone/tswatch@3.2.5(@tiptap/pm@2.27.2)': '@git.zone/tswatch@3.2.5(@tiptap/pm@2.27.2)':
dependencies: dependencies:
'@api.global/typedserver': 8.4.2(@tiptap/pm@2.27.2) '@api.global/typedserver': 8.4.2(@tiptap/pm@2.27.2)
'@git.zone/tsbundle': 2.9.0 '@git.zone/tsbundle': 2.9.1
'@git.zone/tsrun': 2.0.1 '@git.zone/tsrun': 2.0.1
'@push.rocks/early': 4.0.4 '@push.rocks/early': 4.0.4
'@push.rocks/lik': 6.3.1 '@push.rocks/lik': 6.3.1
@@ -5036,7 +5036,7 @@ snapshots:
'@inquirer/figures': 1.0.15 '@inquirer/figures': 1.0.15
'@inquirer/type': 2.0.0 '@inquirer/type': 2.0.0
'@types/mute-stream': 0.0.4 '@types/mute-stream': 0.0.4
'@types/node': 22.19.13 '@types/node': 22.19.15
'@types/wrap-ansi': 3.0.0 '@types/wrap-ansi': 3.0.0
ansi-escapes: 4.3.2 ansi-escapes: 4.3.2
cli-width: 4.1.0 cli-width: 4.1.0
@@ -5523,7 +5523,7 @@ snapshots:
'@push.rocks/smartbucket@3.3.10': '@push.rocks/smartbucket@3.3.10':
dependencies: dependencies:
'@aws-sdk/client-s3': 3.1002.0 '@aws-sdk/client-s3': 3.1003.0
'@push.rocks/smartmime': 2.0.4 '@push.rocks/smartmime': 2.0.4
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartpromise': 4.2.3
@@ -5743,9 +5743,10 @@ snapshots:
dependencies: dependencies:
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0
'@push.rocks/smartfs@1.3.3': '@push.rocks/smartfs@1.4.0':
dependencies: dependencies:
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0
'@push.rocks/smartrust': 1.3.1
'@push.rocks/smartguard@3.1.0': '@push.rocks/smartguard@3.1.0':
dependencies: dependencies:
@@ -6992,18 +6993,18 @@ snapshots:
'@types/body-parser@1.19.6': '@types/body-parser@1.19.6':
dependencies: dependencies:
'@types/connect': 3.4.38 '@types/connect': 3.4.38
'@types/node': 25.3.3 '@types/node': 25.3.5
'@types/buffer-json@2.0.3': {} '@types/buffer-json@2.0.3': {}
'@types/clean-css@4.2.11': '@types/clean-css@4.2.11':
dependencies: dependencies:
'@types/node': 25.3.3 '@types/node': 25.3.5
source-map: 0.6.1 source-map: 0.6.1
'@types/connect@3.4.38': '@types/connect@3.4.38':
dependencies: dependencies:
'@types/node': 25.3.3 '@types/node': 25.3.5
'@types/debug@4.1.12': '@types/debug@4.1.12':
dependencies: dependencies:
@@ -7011,7 +7012,7 @@ snapshots:
'@types/express-serve-static-core@5.1.1': '@types/express-serve-static-core@5.1.1':
dependencies: dependencies:
'@types/node': 25.3.3 '@types/node': 25.3.5
'@types/qs': 6.14.0 '@types/qs': 6.14.0
'@types/range-parser': 1.2.7 '@types/range-parser': 1.2.7
'@types/send': 1.2.1 '@types/send': 1.2.1
@@ -7024,17 +7025,17 @@ snapshots:
'@types/from2@2.3.6': '@types/from2@2.3.6':
dependencies: dependencies:
'@types/node': 25.3.3 '@types/node': 25.3.5
'@types/fs-extra@11.0.4': '@types/fs-extra@11.0.4':
dependencies: dependencies:
'@types/jsonfile': 6.1.4 '@types/jsonfile': 6.1.4
'@types/node': 25.3.3 '@types/node': 25.3.5
'@types/glob@8.1.0': '@types/glob@8.1.0':
dependencies: dependencies:
'@types/minimatch': 5.1.2 '@types/minimatch': 5.1.2
'@types/node': 25.3.3 '@types/node': 25.3.5
'@types/hast@3.0.4': '@types/hast@3.0.4':
dependencies: dependencies:
@@ -7056,12 +7057,12 @@ snapshots:
'@types/jsonfile@6.1.4': '@types/jsonfile@6.1.4':
dependencies: dependencies:
'@types/node': 25.3.3 '@types/node': 25.3.5
'@types/jsonwebtoken@9.0.10': '@types/jsonwebtoken@9.0.10':
dependencies: dependencies:
'@types/ms': 2.1.0 '@types/ms': 2.1.0
'@types/node': 25.3.3 '@types/node': 25.3.5
'@types/linkify-it@5.0.0': {} '@types/linkify-it@5.0.0': {}
@@ -7084,26 +7085,26 @@ snapshots:
'@types/mute-stream@0.0.4': '@types/mute-stream@0.0.4':
dependencies: dependencies:
'@types/node': 25.3.3 '@types/node': 25.3.5
'@types/node-fetch@2.6.13': '@types/node-fetch@2.6.13':
dependencies: dependencies:
'@types/node': 25.3.3 '@types/node': 25.3.5
form-data: 4.0.5 form-data: 4.0.5
'@types/node-forge@1.3.14': '@types/node-forge@1.3.14':
dependencies: dependencies:
'@types/node': 25.3.3 '@types/node': 25.3.5
'@types/node@18.19.130': '@types/node@18.19.130':
dependencies: dependencies:
undici-types: 5.26.5 undici-types: 5.26.5
'@types/node@22.19.13': '@types/node@22.19.15':
dependencies: dependencies:
undici-types: 6.21.0 undici-types: 6.21.0
'@types/node@25.3.3': '@types/node@25.3.5':
dependencies: dependencies:
undici-types: 7.18.2 undici-types: 7.18.2
@@ -7121,22 +7122,22 @@ snapshots:
'@types/send@1.2.1': '@types/send@1.2.1':
dependencies: dependencies:
'@types/node': 25.3.3 '@types/node': 25.3.5
'@types/serve-static@2.2.0': '@types/serve-static@2.2.0':
dependencies: dependencies:
'@types/http-errors': 2.0.5 '@types/http-errors': 2.0.5
'@types/node': 25.3.3 '@types/node': 25.3.5
'@types/symbol-tree@3.2.5': {} '@types/symbol-tree@3.2.5': {}
'@types/tar-stream@3.1.4': '@types/tar-stream@3.1.4':
dependencies: dependencies:
'@types/node': 25.3.3 '@types/node': 25.3.5
'@types/through2@2.0.41': '@types/through2@2.0.41':
dependencies: dependencies:
'@types/node': 25.3.3 '@types/node': 25.3.5
'@types/trusted-types@2.0.7': {} '@types/trusted-types@2.0.7': {}
@@ -7166,11 +7167,11 @@ snapshots:
'@types/ws@8.18.1': '@types/ws@8.18.1':
dependencies: dependencies:
'@types/node': 25.3.3 '@types/node': 25.3.5
'@types/yauzl@2.10.3': '@types/yauzl@2.10.3':
dependencies: dependencies:
'@types/node': 25.3.3 '@types/node': 25.3.5
optional: true optional: true
'@ungap/structured-clone@1.3.0': {} '@ungap/structured-clone@1.3.0': {}

376
test/test.apiclient.ts Normal file
View File

@@ -0,0 +1,376 @@
import { tap, expect } from '@git.zone/tstest/tapbundle';
import {
DcRouterApiClient,
Route,
RouteBuilder,
RouteManager,
Certificate,
CertificateManager,
ApiToken,
ApiTokenBuilder,
ApiTokenManager,
RemoteIngress,
RemoteIngressBuilder,
RemoteIngressManager,
Email,
EmailManager,
StatsManager,
ConfigManager,
LogManager,
RadiusManager,
RadiusClientManager,
RadiusVlanManager,
RadiusSessionManager,
} from '../ts_apiclient/index.js';
// =============================================================================
// Instantiation & Structure
// =============================================================================
tap.test('DcRouterApiClient - should instantiate with baseUrl', async () => {
const client = new DcRouterApiClient({ baseUrl: 'https://localhost:3000' });
expect(client).toBeTruthy();
expect(client.baseUrl).toEqual('https://localhost:3000');
expect(client.identity).toBeUndefined();
});
tap.test('DcRouterApiClient - should strip trailing slashes from baseUrl', async () => {
const client = new DcRouterApiClient({ baseUrl: 'https://localhost:3000///' });
expect(client.baseUrl).toEqual('https://localhost:3000');
});
tap.test('DcRouterApiClient - should accept optional apiToken', async () => {
const client = new DcRouterApiClient({
baseUrl: 'https://localhost:3000',
apiToken: 'dcr_test_token',
});
expect(client.apiToken).toEqual('dcr_test_token');
});
tap.test('DcRouterApiClient - should have all resource managers', async () => {
const client = new DcRouterApiClient({ baseUrl: 'https://localhost:3000' });
expect(client.routes).toBeInstanceOf(RouteManager);
expect(client.certificates).toBeInstanceOf(CertificateManager);
expect(client.apiTokens).toBeInstanceOf(ApiTokenManager);
expect(client.remoteIngress).toBeInstanceOf(RemoteIngressManager);
expect(client.stats).toBeInstanceOf(StatsManager);
expect(client.config).toBeInstanceOf(ConfigManager);
expect(client.logs).toBeInstanceOf(LogManager);
expect(client.emails).toBeInstanceOf(EmailManager);
expect(client.radius).toBeInstanceOf(RadiusManager);
});
// =============================================================================
// buildRequestPayload
// =============================================================================
tap.test('DcRouterApiClient - buildRequestPayload includes identity when set', async () => {
const client = new DcRouterApiClient({ baseUrl: 'https://localhost:3000' });
const identity = {
jwt: 'test-jwt',
userId: 'user1',
name: 'Admin',
expiresAt: Date.now() + 3600000,
};
client.identity = identity;
const payload = client.buildRequestPayload({ extra: 'data' });
expect(payload.identity).toEqual(identity);
expect(payload.extra).toEqual('data');
});
tap.test('DcRouterApiClient - buildRequestPayload includes apiToken when set', async () => {
const client = new DcRouterApiClient({
baseUrl: 'https://localhost:3000',
apiToken: 'dcr_abc123',
});
const payload = client.buildRequestPayload();
expect(payload.apiToken).toEqual('dcr_abc123');
});
tap.test('DcRouterApiClient - buildRequestPayload with both identity and apiToken', async () => {
const client = new DcRouterApiClient({
baseUrl: 'https://localhost:3000',
apiToken: 'dcr_abc123',
});
client.identity = {
jwt: 'test-jwt',
userId: 'user1',
name: 'Admin',
expiresAt: Date.now() + 3600000,
};
const payload = client.buildRequestPayload({ foo: 'bar' });
expect(payload.identity).toBeTruthy();
expect(payload.apiToken).toEqual('dcr_abc123');
expect(payload.foo).toEqual('bar');
});
// =============================================================================
// Route Builder
// =============================================================================
tap.test('RouteBuilder - should support fluent builder pattern', async () => {
const client = new DcRouterApiClient({ baseUrl: 'https://localhost:3000' });
const builder = client.routes.build();
expect(builder).toBeInstanceOf(RouteBuilder);
// Fluent methods return `this` (same reference)
const result = builder
.setName('test-route')
.setMatch({ ports: 443, domains: 'example.com' })
.setAction({ type: 'forward', targets: [{ host: 'backend', port: 8080 }] })
.setEnabled(true);
expect(result === builder).toBeTrue();
});
// =============================================================================
// ApiToken Builder
// =============================================================================
tap.test('ApiTokenBuilder - should support fluent builder pattern', async () => {
const client = new DcRouterApiClient({ baseUrl: 'https://localhost:3000' });
const builder = client.apiTokens.build();
expect(builder).toBeInstanceOf(ApiTokenBuilder);
const result = builder
.setName('ci-token')
.setScopes(['routes:read', 'routes:write'])
.addScope('config:read')
.setExpiresInDays(30);
expect(result === builder).toBeTrue();
});
// =============================================================================
// RemoteIngress Builder
// =============================================================================
tap.test('RemoteIngressBuilder - should support fluent builder pattern', async () => {
const client = new DcRouterApiClient({ baseUrl: 'https://localhost:3000' });
const builder = client.remoteIngress.build();
expect(builder).toBeInstanceOf(RemoteIngressBuilder);
const result = builder
.setName('edge-1')
.setListenPorts([80, 443])
.setAutoDerivePorts(true)
.setTags(['production']);
expect(result === builder).toBeTrue();
});
// =============================================================================
// Route resource class
// =============================================================================
tap.test('Route - should hydrate from IMergedRoute data', async () => {
const client = new DcRouterApiClient({ baseUrl: 'https://localhost:3000' });
const route = new Route(client, {
route: {
name: 'test-route',
match: { ports: 443, domains: 'example.com' },
action: { type: 'forward', targets: [{ host: 'backend', port: 8080 }] },
},
source: 'programmatic',
enabled: true,
overridden: false,
storedRouteId: 'route-123',
createdAt: 1000,
updatedAt: 2000,
});
expect(route.name).toEqual('test-route');
expect(route.source).toEqual('programmatic');
expect(route.enabled).toEqual(true);
expect(route.overridden).toEqual(false);
expect(route.storedRouteId).toEqual('route-123');
expect(route.routeConfig.match.ports).toEqual(443);
});
tap.test('Route - should throw on update/delete/toggle for hardcoded routes', async () => {
const client = new DcRouterApiClient({ baseUrl: 'https://localhost:3000' });
const route = new Route(client, {
route: {
name: 'hardcoded-route',
match: { ports: 80 },
action: { type: 'forward', targets: [{ host: 'localhost', port: 8080 }] },
},
source: 'hardcoded',
enabled: true,
overridden: false,
// No storedRouteId for hardcoded routes
});
let updateError: Error | undefined;
try {
await route.update({ name: 'new-name' });
} catch (e) {
updateError = e as Error;
}
expect(updateError).toBeTruthy();
expect(updateError!.message).toInclude('hardcoded');
let deleteError: Error | undefined;
try {
await route.delete();
} catch (e) {
deleteError = e as Error;
}
expect(deleteError).toBeTruthy();
let toggleError: Error | undefined;
try {
await route.toggle(false);
} catch (e) {
toggleError = e as Error;
}
expect(toggleError).toBeTruthy();
});
// =============================================================================
// Certificate resource class
// =============================================================================
tap.test('Certificate - should hydrate from ICertificateInfo data', async () => {
const client = new DcRouterApiClient({ baseUrl: 'https://localhost:3000' });
const cert = new Certificate(client, {
domain: 'example.com',
routeNames: ['main-route'],
status: 'valid',
source: 'acme',
tlsMode: 'terminate',
expiryDate: '2027-01-01T00:00:00Z',
issuer: "Let's Encrypt",
canReprovision: true,
});
expect(cert.domain).toEqual('example.com');
expect(cert.status).toEqual('valid');
expect(cert.source).toEqual('acme');
expect(cert.canReprovision).toEqual(true);
expect(cert.routeNames.length).toEqual(1);
});
// =============================================================================
// ApiToken resource class
// =============================================================================
tap.test('ApiToken - should hydrate from IApiTokenInfo data', async () => {
const client = new DcRouterApiClient({ baseUrl: 'https://localhost:3000' });
const token = new ApiToken(
client,
{
id: 'token-1',
name: 'ci-token',
scopes: ['routes:read', 'routes:write'],
createdAt: Date.now(),
expiresAt: null,
lastUsedAt: null,
enabled: true,
},
'dcr_secret_value',
);
expect(token.id).toEqual('token-1');
expect(token.name).toEqual('ci-token');
expect(token.scopes.length).toEqual(2);
expect(token.enabled).toEqual(true);
expect(token.tokenValue).toEqual('dcr_secret_value');
});
// =============================================================================
// RemoteIngress resource class
// =============================================================================
tap.test('RemoteIngress - should hydrate from IRemoteIngress data', async () => {
const client = new DcRouterApiClient({ baseUrl: 'https://localhost:3000' });
const edge = new RemoteIngress(client, {
id: 'edge-1',
name: 'test-edge',
secret: 'secret123',
listenPorts: [80, 443],
enabled: true,
autoDerivePorts: true,
tags: ['prod'],
createdAt: 1000,
updatedAt: 2000,
effectiveListenPorts: [80, 443, 8080],
manualPorts: [80, 443],
derivedPorts: [8080],
});
expect(edge.id).toEqual('edge-1');
expect(edge.name).toEqual('test-edge');
expect(edge.listenPorts.length).toEqual(2);
expect(edge.effectiveListenPorts!.length).toEqual(3);
expect(edge.autoDerivePorts).toEqual(true);
});
// =============================================================================
// Email resource class
// =============================================================================
tap.test('Email - should hydrate from IEmail data', async () => {
const client = new DcRouterApiClient({ baseUrl: 'https://localhost:3000' });
const email = new Email(client, {
id: 'email-1',
direction: 'inbound',
status: 'delivered',
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Test email',
timestamp: '2026-03-06T00:00:00Z',
messageId: '<msg-1@example.com>',
size: '1234',
});
expect(email.id).toEqual('email-1');
expect(email.direction).toEqual('inbound');
expect(email.status).toEqual('delivered');
expect(email.from).toEqual('sender@example.com');
expect(email.subject).toEqual('Test email');
});
// =============================================================================
// RadiusManager structure
// =============================================================================
tap.test('RadiusManager - should have sub-managers', async () => {
const client = new DcRouterApiClient({ baseUrl: 'https://localhost:3000' });
expect(client.radius.clients).toBeInstanceOf(RadiusClientManager);
expect(client.radius.vlans).toBeInstanceOf(RadiusVlanManager);
expect(client.radius.sessions).toBeInstanceOf(RadiusSessionManager);
});
// =============================================================================
// Exports verification
// =============================================================================
tap.test('Exports - all expected classes should be importable', async () => {
expect(DcRouterApiClient).toBeTruthy();
expect(Route).toBeTruthy();
expect(RouteBuilder).toBeTruthy();
expect(RouteManager).toBeTruthy();
expect(Certificate).toBeTruthy();
expect(CertificateManager).toBeTruthy();
expect(ApiToken).toBeTruthy();
expect(ApiTokenBuilder).toBeTruthy();
expect(ApiTokenManager).toBeTruthy();
expect(RemoteIngress).toBeTruthy();
expect(RemoteIngressBuilder).toBeTruthy();
expect(RemoteIngressManager).toBeTruthy();
expect(Email).toBeTruthy();
expect(EmailManager).toBeTruthy();
expect(StatsManager).toBeTruthy();
expect(ConfigManager).toBeTruthy();
expect(LogManager).toBeTruthy();
expect(RadiusManager).toBeTruthy();
expect(RadiusClientManager).toBeTruthy();
expect(RadiusVlanManager).toBeTruthy();
expect(RadiusSessionManager).toBeTruthy();
});
export default tap.start();

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/dcrouter', name: '@serve.zone/dcrouter',
version: '11.0.50', version: '11.1.0',
description: 'A multifaceted routing service handling mail and SMS delivery functions.' description: 'A multifaceted routing service handling mail and SMS delivery functions.'
} }

View File

@@ -0,0 +1,157 @@
import * as interfaces from '../ts_interfaces/index.js';
import type { DcRouterApiClient } from './classes.dcrouterapiclient.js';
export class ApiToken {
private clientRef: DcRouterApiClient;
// Data from IApiTokenInfo
public id: string;
public name: string;
public scopes: interfaces.data.TApiTokenScope[];
public createdAt: number;
public expiresAt: number | null;
public lastUsedAt: number | null;
public enabled: boolean;
/** Only set on creation or roll. Not persisted on server side. */
public tokenValue?: string;
constructor(clientRef: DcRouterApiClient, data: interfaces.data.IApiTokenInfo, tokenValue?: string) {
this.clientRef = clientRef;
this.id = data.id;
this.name = data.name;
this.scopes = data.scopes;
this.createdAt = data.createdAt;
this.expiresAt = data.expiresAt;
this.lastUsedAt = data.lastUsedAt;
this.enabled = data.enabled;
this.tokenValue = tokenValue;
}
public async revoke(): Promise<void> {
const response = await this.clientRef.request<interfaces.requests.IReq_RevokeApiToken>(
'revokeApiToken',
this.clientRef.buildRequestPayload({ id: this.id }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to revoke token');
}
}
public async roll(): Promise<string> {
const response = await this.clientRef.request<interfaces.requests.IReq_RollApiToken>(
'rollApiToken',
this.clientRef.buildRequestPayload({ id: this.id }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to roll token');
}
this.tokenValue = response.tokenValue;
return response.tokenValue!;
}
public async toggle(enabled: boolean): Promise<void> {
const response = await this.clientRef.request<interfaces.requests.IReq_ToggleApiToken>(
'toggleApiToken',
this.clientRef.buildRequestPayload({ id: this.id, enabled }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to toggle token');
}
this.enabled = enabled;
}
}
export class ApiTokenBuilder {
private clientRef: DcRouterApiClient;
private tokenName: string = '';
private tokenScopes: interfaces.data.TApiTokenScope[] = [];
private tokenExpiresInDays?: number | null;
constructor(clientRef: DcRouterApiClient) {
this.clientRef = clientRef;
}
public setName(name: string): this {
this.tokenName = name;
return this;
}
public setScopes(scopes: interfaces.data.TApiTokenScope[]): this {
this.tokenScopes = scopes;
return this;
}
public addScope(scope: interfaces.data.TApiTokenScope): this {
if (!this.tokenScopes.includes(scope)) {
this.tokenScopes.push(scope);
}
return this;
}
public setExpiresInDays(days: number | null): this {
this.tokenExpiresInDays = days;
return this;
}
public async save(): Promise<ApiToken> {
const response = await this.clientRef.request<interfaces.requests.IReq_CreateApiToken>(
'createApiToken',
this.clientRef.buildRequestPayload({
name: this.tokenName,
scopes: this.tokenScopes,
expiresInDays: this.tokenExpiresInDays,
}) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to create API token');
}
return new ApiToken(
this.clientRef,
{
id: response.tokenId!,
name: this.tokenName,
scopes: this.tokenScopes,
createdAt: Date.now(),
expiresAt: this.tokenExpiresInDays
? Date.now() + this.tokenExpiresInDays * 24 * 60 * 60 * 1000
: null,
lastUsedAt: null,
enabled: true,
},
response.tokenValue,
);
}
}
export class ApiTokenManager {
private clientRef: DcRouterApiClient;
constructor(clientRef: DcRouterApiClient) {
this.clientRef = clientRef;
}
public async list(): Promise<ApiToken[]> {
const response = await this.clientRef.request<interfaces.requests.IReq_ListApiTokens>(
'listApiTokens',
this.clientRef.buildRequestPayload() as any,
);
return response.tokens.map((t) => new ApiToken(this.clientRef, t));
}
public async create(options: {
name: string;
scopes: interfaces.data.TApiTokenScope[];
expiresInDays?: number | null;
}): Promise<ApiToken> {
return this.build()
.setName(options.name)
.setScopes(options.scopes)
.setExpiresInDays(options.expiresInDays ?? null)
.save();
}
public build(): ApiTokenBuilder {
return new ApiTokenBuilder(this.clientRef);
}
}

View File

@@ -0,0 +1,123 @@
import * as interfaces from '../ts_interfaces/index.js';
import type { DcRouterApiClient } from './classes.dcrouterapiclient.js';
export class Certificate {
private clientRef: DcRouterApiClient;
// Data from ICertificateInfo
public domain: string;
public routeNames: string[];
public status: interfaces.requests.TCertificateStatus;
public source: interfaces.requests.TCertificateSource;
public tlsMode: 'terminate' | 'terminate-and-reencrypt' | 'passthrough';
public expiryDate?: string;
public issuer?: string;
public issuedAt?: string;
public error?: string;
public canReprovision: boolean;
public backoffInfo?: {
failures: number;
retryAfter?: string;
lastError?: string;
};
constructor(clientRef: DcRouterApiClient, data: interfaces.requests.ICertificateInfo) {
this.clientRef = clientRef;
this.domain = data.domain;
this.routeNames = data.routeNames;
this.status = data.status;
this.source = data.source;
this.tlsMode = data.tlsMode;
this.expiryDate = data.expiryDate;
this.issuer = data.issuer;
this.issuedAt = data.issuedAt;
this.error = data.error;
this.canReprovision = data.canReprovision;
this.backoffInfo = data.backoffInfo;
}
public async reprovision(): Promise<void> {
const response = await this.clientRef.request<interfaces.requests.IReq_ReprovisionCertificateDomain>(
'reprovisionCertificateDomain',
this.clientRef.buildRequestPayload({ domain: this.domain }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to reprovision certificate');
}
}
public async delete(): Promise<void> {
const response = await this.clientRef.request<interfaces.requests.IReq_DeleteCertificate>(
'deleteCertificate',
this.clientRef.buildRequestPayload({ domain: this.domain }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to delete certificate');
}
}
public async export(): Promise<{
id: string;
domainName: string;
created: number;
validUntil: number;
privateKey: string;
publicKey: string;
csr: string;
} | undefined> {
const response = await this.clientRef.request<interfaces.requests.IReq_ExportCertificate>(
'exportCertificate',
this.clientRef.buildRequestPayload({ domain: this.domain }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to export certificate');
}
return response.cert;
}
}
export interface ICertificateSummary {
total: number;
valid: number;
expiring: number;
expired: number;
failed: number;
unknown: number;
}
export class CertificateManager {
private clientRef: DcRouterApiClient;
constructor(clientRef: DcRouterApiClient) {
this.clientRef = clientRef;
}
public async list(): Promise<{ certificates: Certificate[]; summary: ICertificateSummary }> {
const response = await this.clientRef.request<interfaces.requests.IReq_GetCertificateOverview>(
'getCertificateOverview',
this.clientRef.buildRequestPayload() as any,
);
return {
certificates: response.certificates.map((c) => new Certificate(this.clientRef, c)),
summary: response.summary,
};
}
public async import(cert: {
id: string;
domainName: string;
created: number;
validUntil: number;
privateKey: string;
publicKey: string;
csr: string;
}): Promise<void> {
const response = await this.clientRef.request<interfaces.requests.IReq_ImportCertificate>(
'importCertificate',
this.clientRef.buildRequestPayload({ cert }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to import certificate');
}
}
}

View File

@@ -0,0 +1,17 @@
import * as interfaces from '../ts_interfaces/index.js';
import type { DcRouterApiClient } from './classes.dcrouterapiclient.js';
export class ConfigManager {
private clientRef: DcRouterApiClient;
constructor(clientRef: DcRouterApiClient) {
this.clientRef = clientRef;
}
public async get(section?: string): Promise<interfaces.requests.IReq_GetConfiguration['response']> {
return this.clientRef.request<interfaces.requests.IReq_GetConfiguration>(
'getConfiguration',
this.clientRef.buildRequestPayload({ section }) as any,
);
}
}

View File

@@ -0,0 +1,112 @@
import * as plugins from './plugins.js';
import * as interfaces from '../ts_interfaces/index.js';
import { RouteManager } from './classes.route.js';
import { CertificateManager } from './classes.certificate.js';
import { ApiTokenManager } from './classes.apitoken.js';
import { RemoteIngressManager } from './classes.remoteingress.js';
import { StatsManager } from './classes.stats.js';
import { ConfigManager } from './classes.config.js';
import { LogManager } from './classes.logs.js';
import { EmailManager } from './classes.email.js';
import { RadiusManager } from './classes.radius.js';
export interface IDcRouterApiClientOptions {
baseUrl: string;
apiToken?: string;
}
export class DcRouterApiClient {
public baseUrl: string;
public apiToken?: string;
public identity?: interfaces.data.IIdentity;
// Resource managers
public routes: RouteManager;
public certificates: CertificateManager;
public apiTokens: ApiTokenManager;
public remoteIngress: RemoteIngressManager;
public stats: StatsManager;
public config: ConfigManager;
public logs: LogManager;
public emails: EmailManager;
public radius: RadiusManager;
constructor(options: IDcRouterApiClientOptions) {
this.baseUrl = options.baseUrl.replace(/\/+$/, '');
this.apiToken = options.apiToken;
this.routes = new RouteManager(this);
this.certificates = new CertificateManager(this);
this.apiTokens = new ApiTokenManager(this);
this.remoteIngress = new RemoteIngressManager(this);
this.stats = new StatsManager(this);
this.config = new ConfigManager(this);
this.logs = new LogManager(this);
this.emails = new EmailManager(this);
this.radius = new RadiusManager(this);
}
// =====================
// Auth
// =====================
public async login(username: string, password: string): Promise<interfaces.data.IIdentity> {
const response = await this.request<interfaces.requests.IReq_AdminLoginWithUsernameAndPassword>(
'adminLoginWithUsernameAndPassword',
{ username, password },
);
if (response.identity) {
this.identity = response.identity;
}
return response.identity!;
}
public async logout(): Promise<void> {
await this.request<interfaces.requests.IReq_AdminLogout>(
'adminLogout',
{ identity: this.identity! },
);
this.identity = undefined;
}
public async verifyIdentity(): Promise<{ valid: boolean; identity?: interfaces.data.IIdentity }> {
const response = await this.request<interfaces.requests.IReq_VerifyIdentity>(
'verifyIdentity',
{ identity: this.identity! },
);
if (response.identity) {
this.identity = response.identity;
}
return response;
}
// =====================
// Internal request helper
// =====================
public async request<T extends plugins.typedrequestInterfaces.ITypedRequest>(
method: string,
requestData: T['request'],
): Promise<T['response']> {
const typedRequest = new plugins.typedrequest.TypedRequest<T>(
`${this.baseUrl}/typedrequest`,
method,
);
return typedRequest.fire(requestData);
}
/**
* Build a request payload with identity and optional API token auto-injected.
*/
public buildRequestPayload(extra: Record<string, any> = {}): Record<string, any> {
const payload: Record<string, any> = { ...extra };
if (this.identity) {
payload.identity = this.identity;
}
if (this.apiToken) {
payload.apiToken = this.apiToken;
}
return payload;
}
}

View File

@@ -0,0 +1,77 @@
import * as interfaces from '../ts_interfaces/index.js';
import type { DcRouterApiClient } from './classes.dcrouterapiclient.js';
export class Email {
private clientRef: DcRouterApiClient;
// Data from IEmail
public id: string;
public direction: interfaces.requests.TEmailDirection;
public status: interfaces.requests.TEmailStatus;
public from: string;
public to: string;
public subject: string;
public timestamp: string;
public messageId: string;
public size: string;
constructor(clientRef: DcRouterApiClient, data: interfaces.requests.IEmail) {
this.clientRef = clientRef;
this.id = data.id;
this.direction = data.direction;
this.status = data.status;
this.from = data.from;
this.to = data.to;
this.subject = data.subject;
this.timestamp = data.timestamp;
this.messageId = data.messageId;
this.size = data.size;
}
public async getDetail(): Promise<interfaces.requests.IEmailDetail | null> {
const response = await this.clientRef.request<interfaces.requests.IReq_GetEmailDetail>(
'getEmailDetail',
this.clientRef.buildRequestPayload({ emailId: this.id }) as any,
);
return response.email;
}
public async resend(): Promise<{ success: boolean; newQueueId?: string }> {
const response = await this.clientRef.request<interfaces.requests.IReq_ResendEmail>(
'resendEmail',
this.clientRef.buildRequestPayload({ emailId: this.id }) as any,
);
return response;
}
}
export class EmailManager {
private clientRef: DcRouterApiClient;
constructor(clientRef: DcRouterApiClient) {
this.clientRef = clientRef;
}
public async list(): Promise<Email[]> {
const response = await this.clientRef.request<interfaces.requests.IReq_GetAllEmails>(
'getAllEmails',
this.clientRef.buildRequestPayload() as any,
);
return response.emails.map((e) => new Email(this.clientRef, e));
}
public async getDetail(emailId: string): Promise<interfaces.requests.IEmailDetail | null> {
const response = await this.clientRef.request<interfaces.requests.IReq_GetEmailDetail>(
'getEmailDetail',
this.clientRef.buildRequestPayload({ emailId }) as any,
);
return response.email;
}
public async resend(emailId: string): Promise<{ success: boolean; newQueueId?: string }> {
return this.clientRef.request<interfaces.requests.IReq_ResendEmail>(
'resendEmail',
this.clientRef.buildRequestPayload({ emailId }) as any,
);
}
}

View File

@@ -0,0 +1,37 @@
import * as interfaces from '../ts_interfaces/index.js';
import type { DcRouterApiClient } from './classes.dcrouterapiclient.js';
export class LogManager {
private clientRef: DcRouterApiClient;
constructor(clientRef: DcRouterApiClient) {
this.clientRef = clientRef;
}
public async getRecent(options?: {
level?: 'debug' | 'info' | 'warn' | 'error';
category?: 'smtp' | 'dns' | 'security' | 'system' | 'email';
limit?: number;
offset?: number;
search?: string;
timeRange?: string;
}): Promise<interfaces.requests.IReq_GetRecentLogs['response']> {
return this.clientRef.request<interfaces.requests.IReq_GetRecentLogs>(
'getRecentLogs',
this.clientRef.buildRequestPayload(options || {}) as any,
);
}
public async getStream(options?: {
follow?: boolean;
filters?: {
level?: string[];
category?: string[];
};
}): Promise<interfaces.requests.IReq_GetLogStream['response']> {
return this.clientRef.request<interfaces.requests.IReq_GetLogStream>(
'getLogStream',
this.clientRef.buildRequestPayload(options || {}) as any,
);
}
}

View File

@@ -0,0 +1,180 @@
import * as interfaces from '../ts_interfaces/index.js';
import type { DcRouterApiClient } from './classes.dcrouterapiclient.js';
// =====================
// Sub-managers
// =====================
export class RadiusClientManager {
private clientRef: DcRouterApiClient;
constructor(clientRef: DcRouterApiClient) {
this.clientRef = clientRef;
}
public async list(): Promise<Array<{
name: string;
ipRange: string;
description?: string;
enabled: boolean;
}>> {
const response = await this.clientRef.request<interfaces.requests.IReq_GetRadiusClients>(
'getRadiusClients',
this.clientRef.buildRequestPayload() as any,
);
return response.clients;
}
public async set(client: {
name: string;
ipRange: string;
secret: string;
description?: string;
enabled: boolean;
}): Promise<void> {
const response = await this.clientRef.request<interfaces.requests.IReq_SetRadiusClient>(
'setRadiusClient',
this.clientRef.buildRequestPayload({ client }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to set RADIUS client');
}
}
public async remove(name: string): Promise<void> {
const response = await this.clientRef.request<interfaces.requests.IReq_RemoveRadiusClient>(
'removeRadiusClient',
this.clientRef.buildRequestPayload({ name }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to remove RADIUS client');
}
}
}
export class RadiusVlanManager {
private clientRef: DcRouterApiClient;
constructor(clientRef: DcRouterApiClient) {
this.clientRef = clientRef;
}
public async list(): Promise<interfaces.requests.IReq_GetVlanMappings['response']> {
return this.clientRef.request<interfaces.requests.IReq_GetVlanMappings>(
'getVlanMappings',
this.clientRef.buildRequestPayload() as any,
);
}
public async set(mapping: {
mac: string;
vlan: number;
description?: string;
enabled: boolean;
}): Promise<void> {
const response = await this.clientRef.request<interfaces.requests.IReq_SetVlanMapping>(
'setVlanMapping',
this.clientRef.buildRequestPayload({ mapping }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to set VLAN mapping');
}
}
public async remove(mac: string): Promise<void> {
const response = await this.clientRef.request<interfaces.requests.IReq_RemoveVlanMapping>(
'removeVlanMapping',
this.clientRef.buildRequestPayload({ mac }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to remove VLAN mapping');
}
}
public async updateConfig(options: {
defaultVlan?: number;
allowUnknownMacs?: boolean;
}): Promise<{ defaultVlan: number; allowUnknownMacs: boolean }> {
const response = await this.clientRef.request<interfaces.requests.IReq_UpdateVlanConfig>(
'updateVlanConfig',
this.clientRef.buildRequestPayload(options) as any,
);
if (!response.success) {
throw new Error('Failed to update VLAN config');
}
return response.config;
}
public async testAssignment(mac: string): Promise<interfaces.requests.IReq_TestVlanAssignment['response']> {
return this.clientRef.request<interfaces.requests.IReq_TestVlanAssignment>(
'testVlanAssignment',
this.clientRef.buildRequestPayload({ mac }) as any,
);
}
}
export class RadiusSessionManager {
private clientRef: DcRouterApiClient;
constructor(clientRef: DcRouterApiClient) {
this.clientRef = clientRef;
}
public async list(filter?: {
username?: string;
nasIpAddress?: string;
vlanId?: number;
}): Promise<interfaces.requests.IReq_GetRadiusSessions['response']> {
return this.clientRef.request<interfaces.requests.IReq_GetRadiusSessions>(
'getRadiusSessions',
this.clientRef.buildRequestPayload({ filter }) as any,
);
}
public async disconnect(sessionId: string, reason?: string): Promise<void> {
const response = await this.clientRef.request<interfaces.requests.IReq_DisconnectRadiusSession>(
'disconnectRadiusSession',
this.clientRef.buildRequestPayload({ sessionId, reason }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to disconnect session');
}
}
}
// =====================
// Main RADIUS Manager
// =====================
export class RadiusManager {
private clientRef: DcRouterApiClient;
public clients: RadiusClientManager;
public vlans: RadiusVlanManager;
public sessions: RadiusSessionManager;
constructor(clientRef: DcRouterApiClient) {
this.clientRef = clientRef;
this.clients = new RadiusClientManager(clientRef);
this.vlans = new RadiusVlanManager(clientRef);
this.sessions = new RadiusSessionManager(clientRef);
}
public async getAccountingSummary(
startTime: number,
endTime: number,
): Promise<interfaces.requests.IReq_GetRadiusAccountingSummary['response']['summary']> {
const response = await this.clientRef.request<interfaces.requests.IReq_GetRadiusAccountingSummary>(
'getRadiusAccountingSummary',
this.clientRef.buildRequestPayload({ startTime, endTime }) as any,
);
return response.summary;
}
public async getStatistics(): Promise<interfaces.requests.IReq_GetRadiusStatistics['response']> {
return this.clientRef.request<interfaces.requests.IReq_GetRadiusStatistics>(
'getRadiusStatistics',
this.clientRef.buildRequestPayload() as any,
);
}
}

View File

@@ -0,0 +1,185 @@
import * as interfaces from '../ts_interfaces/index.js';
import type { DcRouterApiClient } from './classes.dcrouterapiclient.js';
export class RemoteIngress {
private clientRef: DcRouterApiClient;
// Data from IRemoteIngress
public id: string;
public name: string;
public secret: string;
public listenPorts: number[];
public enabled: boolean;
public autoDerivePorts: boolean;
public tags?: string[];
public createdAt: number;
public updatedAt: number;
public effectiveListenPorts?: number[];
public manualPorts?: number[];
public derivedPorts?: number[];
constructor(clientRef: DcRouterApiClient, data: interfaces.data.IRemoteIngress) {
this.clientRef = clientRef;
this.id = data.id;
this.name = data.name;
this.secret = data.secret;
this.listenPorts = data.listenPorts;
this.enabled = data.enabled;
this.autoDerivePorts = data.autoDerivePorts;
this.tags = data.tags;
this.createdAt = data.createdAt;
this.updatedAt = data.updatedAt;
this.effectiveListenPorts = data.effectiveListenPorts;
this.manualPorts = data.manualPorts;
this.derivedPorts = data.derivedPorts;
}
public async update(changes: {
name?: string;
listenPorts?: number[];
autoDerivePorts?: boolean;
enabled?: boolean;
tags?: string[];
}): Promise<void> {
const response = await this.clientRef.request<interfaces.requests.IReq_UpdateRemoteIngress>(
'updateRemoteIngress',
this.clientRef.buildRequestPayload({ id: this.id, ...changes }) as any,
);
if (!response.success) {
throw new Error('Failed to update remote ingress');
}
// Update local state from response
const edge = response.edge;
this.name = edge.name;
this.listenPorts = edge.listenPorts;
this.enabled = edge.enabled;
this.autoDerivePorts = edge.autoDerivePorts;
this.tags = edge.tags;
this.updatedAt = edge.updatedAt;
this.effectiveListenPorts = edge.effectiveListenPorts;
this.manualPorts = edge.manualPorts;
this.derivedPorts = edge.derivedPorts;
}
public async delete(): Promise<void> {
const response = await this.clientRef.request<interfaces.requests.IReq_DeleteRemoteIngress>(
'deleteRemoteIngress',
this.clientRef.buildRequestPayload({ id: this.id }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to delete remote ingress');
}
}
public async regenerateSecret(): Promise<string> {
const response = await this.clientRef.request<interfaces.requests.IReq_RegenerateRemoteIngressSecret>(
'regenerateRemoteIngressSecret',
this.clientRef.buildRequestPayload({ id: this.id }) as any,
);
if (!response.success) {
throw new Error('Failed to regenerate secret');
}
this.secret = response.secret;
return response.secret;
}
public async getConnectionToken(hubHost?: string): Promise<string | undefined> {
const response = await this.clientRef.request<interfaces.requests.IReq_GetRemoteIngressConnectionToken>(
'getRemoteIngressConnectionToken',
this.clientRef.buildRequestPayload({ edgeId: this.id, hubHost }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to get connection token');
}
return response.token;
}
}
export class RemoteIngressBuilder {
private clientRef: DcRouterApiClient;
private edgeName: string = '';
private edgeListenPorts?: number[];
private edgeAutoDerivePorts?: boolean;
private edgeTags?: string[];
constructor(clientRef: DcRouterApiClient) {
this.clientRef = clientRef;
}
public setName(name: string): this {
this.edgeName = name;
return this;
}
public setListenPorts(ports: number[]): this {
this.edgeListenPorts = ports;
return this;
}
public setAutoDerivePorts(auto: boolean): this {
this.edgeAutoDerivePorts = auto;
return this;
}
public setTags(tags: string[]): this {
this.edgeTags = tags;
return this;
}
public async save(): Promise<RemoteIngress> {
const response = await this.clientRef.request<interfaces.requests.IReq_CreateRemoteIngress>(
'createRemoteIngress',
this.clientRef.buildRequestPayload({
name: this.edgeName,
listenPorts: this.edgeListenPorts,
autoDerivePorts: this.edgeAutoDerivePorts,
tags: this.edgeTags,
}) as any,
);
if (!response.success) {
throw new Error('Failed to create remote ingress');
}
return new RemoteIngress(this.clientRef, response.edge);
}
}
export class RemoteIngressManager {
private clientRef: DcRouterApiClient;
constructor(clientRef: DcRouterApiClient) {
this.clientRef = clientRef;
}
public async list(): Promise<RemoteIngress[]> {
const response = await this.clientRef.request<interfaces.requests.IReq_GetRemoteIngresses>(
'getRemoteIngresses',
this.clientRef.buildRequestPayload() as any,
);
return response.edges.map((e) => new RemoteIngress(this.clientRef, e));
}
public async getStatuses(): Promise<interfaces.data.IRemoteIngressStatus[]> {
const response = await this.clientRef.request<interfaces.requests.IReq_GetRemoteIngressStatus>(
'getRemoteIngressStatus',
this.clientRef.buildRequestPayload() as any,
);
return response.statuses;
}
public async create(options: {
name: string;
listenPorts?: number[];
autoDerivePorts?: boolean;
tags?: string[];
}): Promise<RemoteIngress> {
const builder = this.build().setName(options.name);
if (options.listenPorts) builder.setListenPorts(options.listenPorts);
if (options.autoDerivePorts !== undefined) builder.setAutoDerivePorts(options.autoDerivePorts);
if (options.tags) builder.setTags(options.tags);
return builder.save();
}
public build(): RemoteIngressBuilder {
return new RemoteIngressBuilder(this.clientRef);
}
}

View File

@@ -0,0 +1,203 @@
import * as interfaces from '../ts_interfaces/index.js';
import type { IRouteConfig } from '@push.rocks/smartproxy';
import type { DcRouterApiClient } from './classes.dcrouterapiclient.js';
export class Route {
private clientRef: DcRouterApiClient;
// Data from IMergedRoute
public routeConfig: IRouteConfig;
public source: 'hardcoded' | 'programmatic';
public enabled: boolean;
public overridden: boolean;
public storedRouteId?: string;
public createdAt?: number;
public updatedAt?: number;
// Convenience accessors
public get name(): string {
return this.routeConfig.name || '';
}
constructor(clientRef: DcRouterApiClient, data: interfaces.data.IMergedRoute) {
this.clientRef = clientRef;
this.routeConfig = data.route;
this.source = data.source;
this.enabled = data.enabled;
this.overridden = data.overridden;
this.storedRouteId = data.storedRouteId;
this.createdAt = data.createdAt;
this.updatedAt = data.updatedAt;
}
public async update(changes: Partial<IRouteConfig>): Promise<void> {
if (!this.storedRouteId) {
throw new Error('Cannot update a hardcoded route. Use setOverride() instead.');
}
const response = await this.clientRef.request<interfaces.requests.IReq_UpdateRoute>(
'updateRoute',
this.clientRef.buildRequestPayload({ id: this.storedRouteId, route: changes }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to update route');
}
}
public async delete(): Promise<void> {
if (!this.storedRouteId) {
throw new Error('Cannot delete a hardcoded route. Use setOverride() instead.');
}
const response = await this.clientRef.request<interfaces.requests.IReq_DeleteRoute>(
'deleteRoute',
this.clientRef.buildRequestPayload({ id: this.storedRouteId }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to delete route');
}
}
public async toggle(enabled: boolean): Promise<void> {
if (!this.storedRouteId) {
throw new Error('Cannot toggle a hardcoded route. Use setOverride() instead.');
}
const response = await this.clientRef.request<interfaces.requests.IReq_ToggleRoute>(
'toggleRoute',
this.clientRef.buildRequestPayload({ id: this.storedRouteId, enabled }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to toggle route');
}
this.enabled = enabled;
}
public async setOverride(enabled: boolean): Promise<void> {
const response = await this.clientRef.request<interfaces.requests.IReq_SetRouteOverride>(
'setRouteOverride',
this.clientRef.buildRequestPayload({ routeName: this.name, enabled }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to set route override');
}
this.overridden = true;
this.enabled = enabled;
}
public async removeOverride(): Promise<void> {
const response = await this.clientRef.request<interfaces.requests.IReq_RemoveRouteOverride>(
'removeRouteOverride',
this.clientRef.buildRequestPayload({ routeName: this.name }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to remove route override');
}
this.overridden = false;
}
}
export class RouteBuilder {
private clientRef: DcRouterApiClient;
private routeConfig: Partial<IRouteConfig> = {};
private isEnabled: boolean = true;
constructor(clientRef: DcRouterApiClient) {
this.clientRef = clientRef;
}
public setName(name: string): this {
this.routeConfig.name = name;
return this;
}
public setMatch(match: IRouteConfig['match']): this {
this.routeConfig.match = match;
return this;
}
public setAction(action: IRouteConfig['action']): this {
this.routeConfig.action = action;
return this;
}
public setTls(tls: IRouteConfig['action']['tls']): this {
if (!this.routeConfig.action) {
this.routeConfig.action = { type: 'forward' } as IRouteConfig['action'];
}
this.routeConfig.action!.tls = tls;
return this;
}
public setEnabled(enabled: boolean): this {
this.isEnabled = enabled;
return this;
}
public async save(): Promise<Route> {
const response = await this.clientRef.request<interfaces.requests.IReq_CreateRoute>(
'createRoute',
this.clientRef.buildRequestPayload({
route: this.routeConfig as IRouteConfig,
enabled: this.isEnabled,
}) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to create route');
}
// Return a Route instance by re-fetching the list
// The created route is programmatic, so we find it by storedRouteId
const { routes } = await new RouteManager(this.clientRef).list();
const created = routes.find((r) => r.storedRouteId === response.storedRouteId);
if (created) {
return created;
}
// Fallback: construct from known data
return new Route(this.clientRef, {
route: this.routeConfig as IRouteConfig,
source: 'programmatic',
enabled: this.isEnabled,
overridden: false,
storedRouteId: response.storedRouteId,
});
}
}
export class RouteManager {
private clientRef: DcRouterApiClient;
constructor(clientRef: DcRouterApiClient) {
this.clientRef = clientRef;
}
public async list(): Promise<{ routes: Route[]; warnings: interfaces.data.IRouteWarning[] }> {
const response = await this.clientRef.request<interfaces.requests.IReq_GetMergedRoutes>(
'getMergedRoutes',
this.clientRef.buildRequestPayload() as any,
);
return {
routes: response.routes.map((r) => new Route(this.clientRef, r)),
warnings: response.warnings,
};
}
public async create(routeConfig: IRouteConfig, enabled?: boolean): Promise<Route> {
const response = await this.clientRef.request<interfaces.requests.IReq_CreateRoute>(
'createRoute',
this.clientRef.buildRequestPayload({ route: routeConfig, enabled: enabled ?? true }) as any,
);
if (!response.success) {
throw new Error(response.message || 'Failed to create route');
}
return new Route(this.clientRef, {
route: routeConfig,
source: 'programmatic',
enabled: enabled ?? true,
overridden: false,
storedRouteId: response.storedRouteId,
});
}
public build(): RouteBuilder {
return new RouteBuilder(this.clientRef);
}
}

View File

@@ -0,0 +1,111 @@
import * as interfaces from '../ts_interfaces/index.js';
import type { DcRouterApiClient } from './classes.dcrouterapiclient.js';
type TTimeRange = '1h' | '6h' | '24h' | '7d' | '30d';
export class StatsManager {
private clientRef: DcRouterApiClient;
constructor(clientRef: DcRouterApiClient) {
this.clientRef = clientRef;
}
public async getServer(options?: {
timeRange?: TTimeRange;
includeHistory?: boolean;
}): Promise<interfaces.requests.IReq_GetServerStatistics['response']> {
return this.clientRef.request<interfaces.requests.IReq_GetServerStatistics>(
'getServerStatistics',
this.clientRef.buildRequestPayload(options || {}) as any,
);
}
public async getEmail(options?: {
timeRange?: TTimeRange;
domain?: string;
includeDetails?: boolean;
}): Promise<interfaces.requests.IReq_GetEmailStatistics['response']> {
return this.clientRef.request<interfaces.requests.IReq_GetEmailStatistics>(
'getEmailStatistics',
this.clientRef.buildRequestPayload(options || {}) as any,
);
}
public async getDns(options?: {
timeRange?: TTimeRange;
domain?: string;
includeQueryTypes?: boolean;
}): Promise<interfaces.requests.IReq_GetDnsStatistics['response']> {
return this.clientRef.request<interfaces.requests.IReq_GetDnsStatistics>(
'getDnsStatistics',
this.clientRef.buildRequestPayload(options || {}) as any,
);
}
public async getRateLimits(options?: {
domain?: string;
ip?: string;
includeBlocked?: boolean;
}): Promise<interfaces.requests.IReq_GetRateLimitStatus['response']> {
return this.clientRef.request<interfaces.requests.IReq_GetRateLimitStatus>(
'getRateLimitStatus',
this.clientRef.buildRequestPayload(options || {}) as any,
);
}
public async getSecurity(options?: {
timeRange?: TTimeRange;
includeDetails?: boolean;
}): Promise<interfaces.requests.IReq_GetSecurityMetrics['response']> {
return this.clientRef.request<interfaces.requests.IReq_GetSecurityMetrics>(
'getSecurityMetrics',
this.clientRef.buildRequestPayload(options || {}) as any,
);
}
public async getConnections(options?: {
protocol?: 'smtp' | 'smtps' | 'http' | 'https';
state?: string;
}): Promise<interfaces.requests.IReq_GetActiveConnections['response']> {
return this.clientRef.request<interfaces.requests.IReq_GetActiveConnections>(
'getActiveConnections',
this.clientRef.buildRequestPayload(options || {}) as any,
);
}
public async getQueues(options?: {
queueName?: string;
}): Promise<interfaces.requests.IReq_GetQueueStatus['response']> {
return this.clientRef.request<interfaces.requests.IReq_GetQueueStatus>(
'getQueueStatus',
this.clientRef.buildRequestPayload(options || {}) as any,
);
}
public async getHealth(detailed?: boolean): Promise<interfaces.requests.IReq_GetHealthStatus['response']> {
return this.clientRef.request<interfaces.requests.IReq_GetHealthStatus>(
'getHealthStatus',
this.clientRef.buildRequestPayload({ detailed }) as any,
);
}
public async getNetwork(): Promise<interfaces.requests.IReq_GetNetworkStats['response']> {
return this.clientRef.request<interfaces.requests.IReq_GetNetworkStats>(
'getNetworkStats',
this.clientRef.buildRequestPayload() as any,
);
}
public async getCombined(sections?: {
server?: boolean;
email?: boolean;
dns?: boolean;
security?: boolean;
network?: boolean;
}): Promise<interfaces.requests.IReq_GetCombinedMetrics['response']> {
return this.clientRef.request<interfaces.requests.IReq_GetCombinedMetrics>(
'getCombinedMetrics',
this.clientRef.buildRequestPayload({ sections }) as any,
);
}
}

15
ts_apiclient/index.ts Normal file
View File

@@ -0,0 +1,15 @@
// Main client
export { DcRouterApiClient, type IDcRouterApiClientOptions } from './classes.dcrouterapiclient.js';
// Resource classes
export { Route, RouteBuilder, RouteManager } from './classes.route.js';
export { Certificate, CertificateManager, type ICertificateSummary } from './classes.certificate.js';
export { ApiToken, ApiTokenBuilder, ApiTokenManager } from './classes.apitoken.js';
export { RemoteIngress, RemoteIngressBuilder, RemoteIngressManager } from './classes.remoteingress.js';
export { Email, EmailManager } from './classes.email.js';
// Read-only managers
export { StatsManager } from './classes.stats.js';
export { ConfigManager } from './classes.config.js';
export { LogManager } from './classes.logs.js';
export { RadiusManager, RadiusClientManager, RadiusVlanManager, RadiusSessionManager } from './classes.radius.js';

8
ts_apiclient/plugins.ts Normal file
View File

@@ -0,0 +1,8 @@
// @api.global scope
import * as typedrequest from '@api.global/typedrequest';
import * as typedrequestInterfaces from '@api.global/typedrequest-interfaces';
export {
typedrequest,
typedrequestInterfaces,
};

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/dcrouter', name: '@serve.zone/dcrouter',
version: '11.0.50', version: '11.1.0',
description: 'A multifaceted routing service handling mail and SMS delivery functions.' description: 'A multifaceted routing service handling mail and SMS delivery functions.'
} }