mirror of
https://github.com/keycloak/keycloak.git
synced 2026-02-03 20:39:33 -05:00
now uses openapitools to generate and moved it into the existing module for better adoption
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
This commit is contained in:
parent
a7a45e61fd
commit
79e6ed85ce
18 changed files with 441 additions and 1335 deletions
|
|
@ -60,7 +60,8 @@ async function startServer() {
|
|||
path.join(SERVER_DIR, `bin/kc${SCRIPT_EXTENSION}`),
|
||||
[
|
||||
"start-dev",
|
||||
`--features="transient-users,oid4vc-vci,declarative-ui,quick-theme,spiffe,kubernetes-service-accounts,workflows,client-auth-federated,jwt-authorization-grant"`,
|
||||
`--features="transient-users,oid4vc-vci,declarative-ui,quick-theme,spiffe,kubernetes-service-accounts,workflows,client-auth-federated,openapi,client-admin-api:v2"`,
|
||||
"--openapi-enabled=true",
|
||||
...keycloakArgs,
|
||||
],
|
||||
{
|
||||
|
|
|
|||
24
js/libs/keycloak-admin-client-v2/.gitignore
vendored
24
js/libs/keycloak-admin-client-v2/.gitignore
vendored
|
|
@ -1,24 +0,0 @@
|
|||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Build output
|
||||
dist/
|
||||
|
||||
# Kiota workspace files (regenerated on each run)
|
||||
.kiota/
|
||||
.openapi-temp.yaml
|
||||
src/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
src/.kiota.log
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
754
js/libs/keycloak-admin-client-v2/package-lock.json
generated
754
js/libs/keycloak-admin-client-v2/package-lock.json
generated
|
|
@ -1,754 +0,0 @@
|
|||
{
|
||||
"name": "keycloak-admin-client-v2",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "keycloak-admin-client-v2",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@microsoft/kiota-bundle": "^1.0.0-preview.99",
|
||||
"@microsoft/kiota-http-fetchlibrary": "^1.0.0-preview.99"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/kiota": "^1.29.0",
|
||||
"@types/node": "^25.0.8",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
|
||||
"integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
|
||||
"integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
|
||||
"integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
|
||||
"integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
|
||||
"integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
|
||||
"integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
|
||||
"integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
|
||||
"integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
|
||||
"integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
|
||||
"integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
|
||||
"integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
|
||||
"integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
|
||||
"integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
|
||||
"integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
|
||||
"integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
|
||||
"integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
|
||||
"integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
|
||||
"integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
|
||||
"integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
|
||||
"integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
|
||||
"integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openharmony-arm64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
|
||||
"integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
|
||||
"integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
|
||||
"integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
|
||||
"integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
|
||||
"integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@microsoft/kiota": {
|
||||
"version": "1.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/kiota/-/kiota-1.29.0.tgz",
|
||||
"integrity": "sha512-qqIlTz48OJ5ZMRoTA/uQA70B7ltS4lPSs9atG5PUn+dKZcgXny3LzQPe12B1LsKoBJYbwhaU3fD8/C1DsLW6Cw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"adm-zip": "^0.5.16",
|
||||
"original-fs": "^1.2.0",
|
||||
"uuid": "^13.0.0",
|
||||
"vscode-jsonrpc": "^8.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@microsoft/kiota-abstractions": {
|
||||
"version": "1.0.0-preview.99",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/kiota-abstractions/-/kiota-abstractions-1.0.0-preview.99.tgz",
|
||||
"integrity": "sha512-6qrlwGCO3DbvpA7tszqk7JNQPFPDg8gv5NGMTziwdtHqaQrUALKusm3vdJGPVGrbNiZL6/lBaY5NSUvLtlhRCg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^1.7.0",
|
||||
"@std-uritemplate/std-uritemplate": "^2.0.0",
|
||||
"tinyduration": "^3.3.0",
|
||||
"tslib": "^2.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@microsoft/kiota-bundle": {
|
||||
"version": "1.0.0-preview.99",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/kiota-bundle/-/kiota-bundle-1.0.0-preview.99.tgz",
|
||||
"integrity": "sha512-AxvO+z6UgWMAT2NfXN36CMhAUm/39tUQt8o32axeEJDS/EvZINDAPstbQV+zK7bJRC8kCqUHhCE/fuHX2dXA+g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@microsoft/kiota-abstractions": "^1.0.0-preview.99",
|
||||
"@microsoft/kiota-http-fetchlibrary": "^1.0.0-preview.99",
|
||||
"@microsoft/kiota-serialization-form": "^1.0.0-preview.99",
|
||||
"@microsoft/kiota-serialization-json": "^1.0.0-preview.99",
|
||||
"@microsoft/kiota-serialization-multipart": "^1.0.0-preview.99",
|
||||
"@microsoft/kiota-serialization-text": "^1.0.0-preview.99"
|
||||
}
|
||||
},
|
||||
"node_modules/@microsoft/kiota-http-fetchlibrary": {
|
||||
"version": "1.0.0-preview.99",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/kiota-http-fetchlibrary/-/kiota-http-fetchlibrary-1.0.0-preview.99.tgz",
|
||||
"integrity": "sha512-lIruiYf8L7DPG0Fm92QN5YK4zx4sh76EtTONUAqBCxt5AtEJ7KQceBajaXQsjfcndUAp9LmDVdqR44o8sc9/mA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@microsoft/kiota-abstractions": "^1.0.0-preview.99",
|
||||
"@opentelemetry/api": "^1.7.0",
|
||||
"tslib": "^2.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@microsoft/kiota-serialization-form": {
|
||||
"version": "1.0.0-preview.99",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/kiota-serialization-form/-/kiota-serialization-form-1.0.0-preview.99.tgz",
|
||||
"integrity": "sha512-BdXxqNfy+5yWTyxpBguqJYy6E9RRotrDClRcUO89okFcR5N6s6xenjsvGw++q9KWNi7qcHrqaG8xlRz1TLk81Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@microsoft/kiota-abstractions": "^1.0.0-preview.99",
|
||||
"tslib": "^2.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@microsoft/kiota-serialization-json": {
|
||||
"version": "1.0.0-preview.99",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/kiota-serialization-json/-/kiota-serialization-json-1.0.0-preview.99.tgz",
|
||||
"integrity": "sha512-kDmMYmB7XkRprlLviEynRPDkE45JDxqiuUopfBRMvNK4qhzFiJTXGLihZ2FG6fdVu9LbV3/W0QxbeGP35kDK6A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@microsoft/kiota-abstractions": "^1.0.0-preview.99",
|
||||
"tslib": "^2.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@microsoft/kiota-serialization-multipart": {
|
||||
"version": "1.0.0-preview.99",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/kiota-serialization-multipart/-/kiota-serialization-multipart-1.0.0-preview.99.tgz",
|
||||
"integrity": "sha512-cfviCAVTlZPD+MUgdTIgsX/IfHND+gKQhem3vJeo7l5Qqz2rvvVFXOHwECI19umO9Un1QFKe1V5wg+M+aj90+g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@microsoft/kiota-abstractions": "^1.0.0-preview.99",
|
||||
"tslib": "^2.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@microsoft/kiota-serialization-text": {
|
||||
"version": "1.0.0-preview.99",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/kiota-serialization-text/-/kiota-serialization-text-1.0.0-preview.99.tgz",
|
||||
"integrity": "sha512-gcBffQRI1soHVAiS4YjfMr3SQ9fC8xVUGMfarTTszU4PjpxyFOknaWoFdt/0rvMeavI9jOtbyvDKAf77cZyYIQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@microsoft/kiota-abstractions": "^1.0.0-preview.99",
|
||||
"tslib": "^2.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/api": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
|
||||
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@std-uritemplate/std-uritemplate": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@std-uritemplate/std-uritemplate/-/std-uritemplate-2.0.8.tgz",
|
||||
"integrity": "sha512-8oj7jKksoTRxjdPkWKX9FyOKDS8ORBm+ZfTSHm0f3eYha8cI0O1d57gyduOIAX7Y2uUukNDNlxlvGb+FFkE7yQ==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.8.tgz",
|
||||
"integrity": "sha512-powIePYMmC3ibL0UJ2i2s0WIbq6cg6UyVFQxSCpaPxxzAaziRfimGivjdF943sSGV6RADVbk0Nvlm5P/FB44Zg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/adm-zip": {
|
||||
"version": "0.5.16",
|
||||
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz",
|
||||
"integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
|
||||
"integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.27.2",
|
||||
"@esbuild/android-arm": "0.27.2",
|
||||
"@esbuild/android-arm64": "0.27.2",
|
||||
"@esbuild/android-x64": "0.27.2",
|
||||
"@esbuild/darwin-arm64": "0.27.2",
|
||||
"@esbuild/darwin-x64": "0.27.2",
|
||||
"@esbuild/freebsd-arm64": "0.27.2",
|
||||
"@esbuild/freebsd-x64": "0.27.2",
|
||||
"@esbuild/linux-arm": "0.27.2",
|
||||
"@esbuild/linux-arm64": "0.27.2",
|
||||
"@esbuild/linux-ia32": "0.27.2",
|
||||
"@esbuild/linux-loong64": "0.27.2",
|
||||
"@esbuild/linux-mips64el": "0.27.2",
|
||||
"@esbuild/linux-ppc64": "0.27.2",
|
||||
"@esbuild/linux-riscv64": "0.27.2",
|
||||
"@esbuild/linux-s390x": "0.27.2",
|
||||
"@esbuild/linux-x64": "0.27.2",
|
||||
"@esbuild/netbsd-arm64": "0.27.2",
|
||||
"@esbuild/netbsd-x64": "0.27.2",
|
||||
"@esbuild/openbsd-arm64": "0.27.2",
|
||||
"@esbuild/openbsd-x64": "0.27.2",
|
||||
"@esbuild/openharmony-arm64": "0.27.2",
|
||||
"@esbuild/sunos-x64": "0.27.2",
|
||||
"@esbuild/win32-arm64": "0.27.2",
|
||||
"@esbuild/win32-ia32": "0.27.2",
|
||||
"@esbuild/win32-x64": "0.27.2"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-tsconfig": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
|
||||
"integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"resolve-pkg-maps": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/original-fs": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/original-fs/-/original-fs-1.2.0.tgz",
|
||||
"integrity": "sha512-IGo+qFumpIV65oDchJrqL0BOk9kr82fObnTesNJt8t3YgP6vfqcmRs0ofPzg3D9PKMeBHt7lrg1k/6L+oFdS8g==",
|
||||
"dev": true,
|
||||
"license": "Unlicense"
|
||||
},
|
||||
"node_modules/resolve-pkg-maps": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyduration": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/tinyduration/-/tinyduration-3.4.1.tgz",
|
||||
"integrity": "sha512-NemFoamVYn7TmtwZKZ3OiliM9fZkr6EWiTM+wKknco6POSy2gS689xx/pXip0JYp40HXpUw6k65CUYHWYUXdaA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
|
||||
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "~0.27.0",
|
||||
"get-tsconfig": "^4.7.5"
|
||||
},
|
||||
"bin": {
|
||||
"tsx": "dist/cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "13.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
|
||||
"integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist-node/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-jsonrpc": {
|
||||
"version": "8.2.1",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1.tgz",
|
||||
"integrity": "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
{
|
||||
"name": "@keycloak/keycloak-admin-client-v2",
|
||||
"version": "999.0.0-SNAPSHOT",
|
||||
"description": "Keycloak Admin Client v2 - Generated with Microsoft Kiota",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"generate": "tsx scripts/generate.ts",
|
||||
"generate:file": "OPENAPI_FILE=openapi.yaml tsx scripts/generate.ts",
|
||||
"build": "tsc",
|
||||
"test": "tsx test/test-app.ts"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@microsoft/kiota": "^1.29.0",
|
||||
"@types/node": "^25.0.8",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/kiota-abstractions": "1.0.0-preview.99",
|
||||
"@microsoft/kiota-bundle": "^1.0.0-preview.99",
|
||||
"@microsoft/kiota-http-fetchlibrary": "^1.0.0-preview.99"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,181 +0,0 @@
|
|||
// Use require for CommonJS compatibility with @microsoft/kiota
|
||||
// The ESM build of @microsoft/kiota is broken (missing files), so we use require()
|
||||
import { createRequire } from "module";
|
||||
import { fileURLToPath } from "url";
|
||||
import { dirname, resolve } from "path";
|
||||
import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const {
|
||||
generateClient,
|
||||
KiotaGenerationLanguage,
|
||||
ConsumerOperation,
|
||||
} = require("@microsoft/kiota");
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const projectRoot = resolve(__dirname, "..");
|
||||
|
||||
// Configuration
|
||||
const OPENAPI_URL = process.env.OPENAPI_URL || "http://localhost:9000/openapi";
|
||||
const OPENAPI_FILE = process.env.OPENAPI_FILE; // Optional: use a local file instead
|
||||
const OUTPUT_PATH = resolve(projectRoot, "src");
|
||||
const CLIENT_CLASS_NAME = "AdminClient";
|
||||
const CLIENT_NAMESPACE = "ApiSdk";
|
||||
|
||||
async function downloadOpenApiSpec(url: string): Promise<string> {
|
||||
console.log(`📥 Downloading OpenAPI spec from ${url}...`);
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to download OpenAPI spec: ${response.status} ${response.statusText}`,
|
||||
);
|
||||
}
|
||||
const content = await response.text();
|
||||
|
||||
// Save to a temp file
|
||||
const tempFile = resolve(projectRoot, ".openapi-temp.yaml");
|
||||
writeFileSync(tempFile, content);
|
||||
console.log(`✅ Downloaded and saved to ${tempFile}`);
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log("🚀 Keycloak Admin Client v2 - Kiota Generator\n");
|
||||
|
||||
let openApiFilePath: string;
|
||||
|
||||
if (OPENAPI_FILE) {
|
||||
// Use local file
|
||||
openApiFilePath = resolve(projectRoot, OPENAPI_FILE);
|
||||
if (!existsSync(openApiFilePath)) {
|
||||
console.error(`❌ OpenAPI file not found: ${openApiFilePath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`📄 Using local OpenAPI file: ${openApiFilePath}`);
|
||||
} else {
|
||||
// Download from URL
|
||||
openApiFilePath = await downloadOpenApiSpec(OPENAPI_URL);
|
||||
}
|
||||
|
||||
// Ensure output directory exists
|
||||
if (!existsSync(OUTPUT_PATH)) {
|
||||
mkdirSync(OUTPUT_PATH, { recursive: true });
|
||||
}
|
||||
|
||||
console.log(`\n📦 Generating TypeScript client...`);
|
||||
console.log(` Output: ${OUTPUT_PATH}`);
|
||||
console.log(` Client class: ${CLIENT_CLASS_NAME}`);
|
||||
console.log(` Namespace: ${CLIENT_NAMESPACE}\n`);
|
||||
|
||||
try {
|
||||
const result = await generateClient({
|
||||
openAPIFilePath: openApiFilePath,
|
||||
clientClassName: CLIENT_CLASS_NAME,
|
||||
clientNamespaceName: CLIENT_NAMESPACE,
|
||||
language: KiotaGenerationLanguage.TypeScript,
|
||||
outputPath: OUTPUT_PATH,
|
||||
operation: ConsumerOperation.Generate,
|
||||
workingDirectory: projectRoot,
|
||||
cleanOutput: true,
|
||||
});
|
||||
|
||||
if (result) {
|
||||
console.log("\n✅ Client generated successfully!");
|
||||
|
||||
if (result.logs && result.logs.length > 0) {
|
||||
console.log("\n📋 Generation logs:");
|
||||
for (const log of result.logs) {
|
||||
const level = log.level === 1 ? "⚠️" : log.level === 2 ? "❌" : "ℹ️";
|
||||
console.log(` ${level} ${log.message}`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log("\n⚠️ Generation completed but returned no result");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("\n❌ Generation failed:", error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Post-process: Fix empty if blocks in adminClient.ts that cause TS2774 errors
|
||||
postProcessGeneratedCode();
|
||||
|
||||
console.log("\n🎉 Done! You can now build the project with: npm run build");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix known issues in Kiota-generated code
|
||||
*/
|
||||
function postProcessGeneratedCode() {
|
||||
const adminClientPath = resolve(OUTPUT_PATH, "adminClient.ts");
|
||||
|
||||
if (!existsSync(adminClientPath)) {
|
||||
console.log("⚠️ adminClient.ts not found, skipping post-processing");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("\n🔧 Post-processing generated code...");
|
||||
|
||||
let content = readFileSync(adminClientPath, "utf-8");
|
||||
let modified = false;
|
||||
|
||||
// The Kiota npm package generates empty if-blocks for serializer registration
|
||||
// We need to fill them in with the actual registration calls
|
||||
|
||||
// Check if the if-blocks are empty (missing the registration calls)
|
||||
if (
|
||||
content.includes(
|
||||
"if (parseNodeFactoryRegistry.registerDefaultDeserializer) {\n }",
|
||||
)
|
||||
) {
|
||||
// Add required imports for serializers/deserializers
|
||||
const serializerImports = `// @ts-ignore
|
||||
import { FormParseNodeFactory, FormSerializationWriterFactory } from '@microsoft/kiota-serialization-form';
|
||||
// @ts-ignore
|
||||
import { JsonParseNodeFactory, JsonSerializationWriterFactory } from '@microsoft/kiota-serialization-json';
|
||||
// @ts-ignore
|
||||
import { MultipartSerializationWriterFactory } from '@microsoft/kiota-serialization-multipart';
|
||||
// @ts-ignore
|
||||
import { TextParseNodeFactory, TextSerializationWriterFactory } from '@microsoft/kiota-serialization-text';
|
||||
`;
|
||||
|
||||
// Add imports after the existing imports
|
||||
content = content.replace(
|
||||
/import \{ apiClientProxifier,/,
|
||||
serializerImports + "\nimport { apiClientProxifier,",
|
||||
);
|
||||
|
||||
// Fill in the deserializer registration
|
||||
content = content.replace(
|
||||
/if \(parseNodeFactoryRegistry\.registerDefaultDeserializer\) \{\n {4}\}/,
|
||||
`if (parseNodeFactoryRegistry.registerDefaultDeserializer) {
|
||||
parseNodeFactoryRegistry.registerDefaultDeserializer(JsonParseNodeFactory, backingStoreFactory);
|
||||
parseNodeFactoryRegistry.registerDefaultDeserializer(TextParseNodeFactory, backingStoreFactory);
|
||||
parseNodeFactoryRegistry.registerDefaultDeserializer(FormParseNodeFactory, backingStoreFactory);
|
||||
}`,
|
||||
);
|
||||
|
||||
// Fill in the serializer registration
|
||||
content = content.replace(
|
||||
/if \(serializationWriterFactory\.registerDefaultSerializer\) \{\n {4}\}/,
|
||||
`if (serializationWriterFactory.registerDefaultSerializer) {
|
||||
serializationWriterFactory.registerDefaultSerializer(JsonSerializationWriterFactory);
|
||||
serializationWriterFactory.registerDefaultSerializer(TextSerializationWriterFactory);
|
||||
serializationWriterFactory.registerDefaultSerializer(FormSerializationWriterFactory);
|
||||
serializationWriterFactory.registerDefaultSerializer(MultipartSerializationWriterFactory);
|
||||
}`,
|
||||
);
|
||||
|
||||
modified = true;
|
||||
console.log(" ✅ Added serializer/deserializer registration code");
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
writeFileSync(adminClientPath, content);
|
||||
} else {
|
||||
console.log(" ℹ️ No fixes needed");
|
||||
}
|
||||
}
|
||||
|
||||
void main();
|
||||
|
|
@ -1,177 +0,0 @@
|
|||
import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary";
|
||||
import {
|
||||
type AccessTokenProvider,
|
||||
AllowedHostsValidator,
|
||||
BaseBearerTokenAuthenticationProvider,
|
||||
} from "@microsoft/kiota-abstractions";
|
||||
import { createAdminClient } from "../src/adminClient.js";
|
||||
import type { OIDCClientRepresentation } from "../src/models/index.js";
|
||||
|
||||
// Configuration
|
||||
const KEYCLOAK_URL = process.env.KEYCLOAK_URL || "http://localhost:8080";
|
||||
const REALM = process.env.KEYCLOAK_REALM || "master";
|
||||
const CLIENT_ID = process.env.KEYCLOAK_CLIENT_ID || "admin-cli";
|
||||
const USERNAME = process.env.KEYCLOAK_USERNAME || "admin";
|
||||
const PASSWORD = process.env.KEYCLOAK_PASSWORD || "admin";
|
||||
|
||||
interface TokenResponse {
|
||||
access_token: string;
|
||||
expires_in: number;
|
||||
refresh_token?: string;
|
||||
token_type: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple access token provider that fetches tokens from Keycloak
|
||||
*/
|
||||
class KeycloakAccessTokenProvider implements AccessTokenProvider {
|
||||
#accessToken: string | null = null;
|
||||
#tokenExpiry: number = 0;
|
||||
#allowedHostsValidator: AllowedHostsValidator;
|
||||
#keycloakUrl: string;
|
||||
#realm: string;
|
||||
#clientId: string;
|
||||
#username: string;
|
||||
#password: string;
|
||||
|
||||
constructor(
|
||||
keycloakUrl: string,
|
||||
realm: string,
|
||||
clientId: string,
|
||||
username: string,
|
||||
password: string,
|
||||
) {
|
||||
this.#keycloakUrl = keycloakUrl;
|
||||
this.#realm = realm;
|
||||
this.#clientId = clientId;
|
||||
this.#username = username;
|
||||
this.#password = password;
|
||||
this.#allowedHostsValidator = new AllowedHostsValidator(
|
||||
new Set([new URL(keycloakUrl).host]),
|
||||
);
|
||||
}
|
||||
|
||||
async getAuthorizationToken(): Promise<string> {
|
||||
// Check if we have a valid cached token
|
||||
if (this.#accessToken && Date.now() < this.#tokenExpiry - 30000) {
|
||||
return this.#accessToken;
|
||||
}
|
||||
|
||||
// Fetch a new token
|
||||
const tokenUrl = `${this.#keycloakUrl}/realms/${this.#realm}/protocol/openid-connect/token`;
|
||||
const response = await fetch(tokenUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
grant_type: "password",
|
||||
client_id: this.#clientId,
|
||||
username: this.#username,
|
||||
password: this.#password,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.text();
|
||||
throw new Error(
|
||||
`Failed to get access token: ${response.status} - ${error}`,
|
||||
);
|
||||
}
|
||||
|
||||
const tokenData: TokenResponse = (await response.json()) as TokenResponse;
|
||||
this.#accessToken = tokenData.access_token;
|
||||
this.#tokenExpiry = Date.now() + tokenData.expires_in * 1000;
|
||||
|
||||
return this.#accessToken;
|
||||
}
|
||||
|
||||
getAllowedHostsValidator(): AllowedHostsValidator {
|
||||
return this.#allowedHostsValidator;
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log("🔐 Keycloak Admin Client v2 - Test Application\n");
|
||||
console.log(`Keycloak URL: ${KEYCLOAK_URL}`);
|
||||
console.log(`Realm: ${REALM}`);
|
||||
|
||||
// Create the authentication provider
|
||||
const tokenProvider = new KeycloakAccessTokenProvider(
|
||||
KEYCLOAK_URL,
|
||||
REALM,
|
||||
CLIENT_ID,
|
||||
USERNAME,
|
||||
PASSWORD,
|
||||
);
|
||||
const authProvider = new BaseBearerTokenAuthenticationProvider(tokenProvider);
|
||||
|
||||
// Create the request adapter
|
||||
const adapter = new FetchRequestAdapter(authProvider);
|
||||
adapter.baseUrl = KEYCLOAK_URL;
|
||||
|
||||
// Create the admin client
|
||||
const client = createAdminClient(adapter);
|
||||
|
||||
try {
|
||||
// List all clients in the realm
|
||||
console.log(`📋 Listing clients in realm '${REALM}'...\n`);
|
||||
const clients = await client.admin.api.v2.realms
|
||||
.byName(REALM)
|
||||
.clients.get();
|
||||
|
||||
if (clients && clients.length > 0) {
|
||||
console.log(`Found ${clients.length} client(s):\n`);
|
||||
for (const c of clients) {
|
||||
console.log(` - ${c.clientId} (${c.protocol || "unknown protocol"})`);
|
||||
if (c.displayName) {
|
||||
console.log(` Display Name: ${c.displayName}`);
|
||||
}
|
||||
if (c.description) {
|
||||
console.log(` Description: ${c.description}`);
|
||||
}
|
||||
console.log(` Enabled: ${c.enabled ?? "unknown"}`);
|
||||
console.log();
|
||||
}
|
||||
} else {
|
||||
console.log("No clients found.");
|
||||
}
|
||||
|
||||
// Create a new test client
|
||||
console.log("\n🆕 Creating a new OIDC test client...\n");
|
||||
const newClient: OIDCClientRepresentation = {
|
||||
clientId: `test-client-${Date.now()}`,
|
||||
displayName: "Test Client",
|
||||
description: "A test client created by the admin client v2",
|
||||
enabled: true,
|
||||
protocol: "openid-connect",
|
||||
redirectUris: ["http://localhost:3000/callback"],
|
||||
webOrigins: ["http://localhost:3000"],
|
||||
};
|
||||
|
||||
await client.admin.api.v2.realms.byName(REALM).clients.post(newClient);
|
||||
console.log(`✅ Created client: ${newClient.clientId}`);
|
||||
|
||||
// List clients again to verify
|
||||
console.log("\n📋 Listing clients again to verify...\n");
|
||||
const updatedClients = await client.admin.api.v2.realms
|
||||
.byName(REALM)
|
||||
.clients.get();
|
||||
const createdClient = updatedClients?.find(
|
||||
(c) => c.clientId === newClient.clientId,
|
||||
);
|
||||
|
||||
if (createdClient) {
|
||||
console.log(`✅ Verified: Client '${createdClient.clientId}' exists!`);
|
||||
} else {
|
||||
console.log(`❌ Could not find the created client`);
|
||||
}
|
||||
|
||||
console.log("\n🎉 Test completed successfully!");
|
||||
} catch (error) {
|
||||
console.error("❌ Error:", error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void main();
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
{
|
||||
// Visit https://aka.ms/tsconfig to read more about this file
|
||||
"compilerOptions": {
|
||||
// File Layout
|
||||
"rootDir": ".",
|
||||
"outDir": "./dist",
|
||||
|
||||
// Environment Settings
|
||||
// See also https://aka.ms/tsconfig/module
|
||||
"module": "nodenext",
|
||||
"target": "esnext",
|
||||
"lib": ["esnext"],
|
||||
"types": ["node"],
|
||||
|
||||
// Other Outputs
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
|
||||
// Stricter Typechecking Options
|
||||
// Relaxed for Kiota generated code compatibility
|
||||
"noUncheckedIndexedAccess": false,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictBindCallApply": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"alwaysStrict": true,
|
||||
|
||||
// Recommended Options
|
||||
"verbatimModuleSyntax": true,
|
||||
"isolatedModules": true,
|
||||
"noUncheckedSideEffectImports": true,
|
||||
"moduleDetection": "force",
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["src/**/*", "test/**/*", "scripts/**/*"]
|
||||
}
|
||||
1
js/libs/keycloak-admin-client/.gitignore
vendored
1
js/libs/keycloak-admin-client/.gitignore
vendored
|
|
@ -1 +1,2 @@
|
|||
lib/
|
||||
generated/
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ tags:
|
|||
- name: Clients (v2)
|
||||
x-smallrye-profile-admin: ""
|
||||
paths:
|
||||
/admin/api/v2/realms/{name}/clients:
|
||||
/admin/api/{realmName}/clients/{version}:
|
||||
get:
|
||||
summary: Get all clients
|
||||
description: Returns a list of all clients in the realm
|
||||
|
|
@ -154,12 +154,18 @@ paths:
|
|||
application/json:
|
||||
schema: {}
|
||||
parameters:
|
||||
- name: name
|
||||
- name: realmName
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
/admin/api/v2/realms/{name}/clients/{id}:
|
||||
- name: version
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: v\d+
|
||||
/admin/api/{realmName}/clients/{version}/{id}:
|
||||
get:
|
||||
tags:
|
||||
- Clients (v2)
|
||||
|
|
@ -204,7 +210,10 @@ paths:
|
|||
- Clients (v2)
|
||||
requestBody:
|
||||
content:
|
||||
application/merge-patch+json: {}
|
||||
application/merge-patch+json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
|
|
@ -227,11 +236,17 @@ paths:
|
|||
"204":
|
||||
description: No Content
|
||||
parameters:
|
||||
- name: name
|
||||
- name: realmName
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: version
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: v\d+
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
7
js/libs/keycloak-admin-client/openapitools.json
Normal file
7
js/libs/keycloak-admin-client/openapitools.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
|
||||
"spaces": 2,
|
||||
"generator-cli": {
|
||||
"version": "7.18.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -18,11 +18,15 @@
|
|||
"build": "wireit",
|
||||
"lint": "wireit",
|
||||
"test": "wireit",
|
||||
"generate:openapi": "wireit",
|
||||
"prepublishOnly": "pnpm build"
|
||||
},
|
||||
"wireit": {
|
||||
"build": {
|
||||
"command": "tsc --pretty",
|
||||
"dependencies": [
|
||||
"generate:openapi"
|
||||
],
|
||||
"files": [
|
||||
"src",
|
||||
"tsconfig.json"
|
||||
|
|
@ -31,6 +35,16 @@
|
|||
"lib"
|
||||
]
|
||||
},
|
||||
"generate:openapi": {
|
||||
"command": "pnpm dlx @openapitools/openapi-generator-cli generate -i api.yml -g typescript-fetch -o src/generated --additional-properties=supportsES6=true,typescriptThreePlus=true,importFileExtension=.js && node scripts/fix-generated-runtime.mjs",
|
||||
"files": [
|
||||
"api.yml",
|
||||
"scripts/fix-generated-runtime.mjs"
|
||||
],
|
||||
"output": [
|
||||
"src/generated"
|
||||
]
|
||||
},
|
||||
"lint": {
|
||||
"command": "eslint ."
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
/**
|
||||
* Post-processing script to fix OpenAPI Generator TypeScript output for Node.js compatibility.
|
||||
*
|
||||
* Fixes:
|
||||
* 1. Adds missing type definitions for Node.js (RequestCredentials)
|
||||
* 2. Fixes type compatibility issues in runtime.ts
|
||||
*
|
||||
* Note: Import file extensions are handled by the generator's importFileExtension option.
|
||||
*/
|
||||
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const generatedDir = join(__dirname, '..', 'src', 'generated');
|
||||
|
||||
/**
|
||||
* Fix runtime.ts for Node.js compatibility
|
||||
*/
|
||||
function fixRuntime(content) {
|
||||
// Add type definitions at the top of the file (after the eslint-disable comments)
|
||||
const typeDefinitions = `
|
||||
// Node.js compatibility types
|
||||
type RequestCredentials = 'include' | 'omit' | 'same-origin';
|
||||
`;
|
||||
|
||||
// Replace WindowOrWorkerGlobalScope['fetch'] with typeof fetch
|
||||
let fixed = content.replace(
|
||||
/export type FetchAPI = WindowOrWorkerGlobalScope\['fetch'\];/g,
|
||||
'export type FetchAPI = typeof fetch;'
|
||||
);
|
||||
|
||||
// Add type definitions after the eslint-disable comment block
|
||||
if (!fixed.includes('// Node.js compatibility types')) {
|
||||
fixed = fixed.replace(
|
||||
/(\* Do not edit the class manually\.\s*\*\/)\s*\n/,
|
||||
'$1\n' + typeDefinitions + '\n'
|
||||
);
|
||||
}
|
||||
|
||||
// Fix 'response' possibly undefined errors
|
||||
fixed = fixed.replace(
|
||||
/throw new ResponseError\(response, 'Response returned an error code'\);/g,
|
||||
"throw new ResponseError(response!, 'Response returned an error code');"
|
||||
);
|
||||
|
||||
fixed = fixed.replace(
|
||||
/response: response\.clone\(\),/g,
|
||||
'response: response!.clone(),'
|
||||
);
|
||||
|
||||
// Fix fetchApi type compatibility in middleware contexts
|
||||
fixed = fixed.replace(
|
||||
/fetch: this\.fetchApi,/g,
|
||||
'fetch: this.fetchApi as FetchAPI,'
|
||||
);
|
||||
|
||||
return fixed;
|
||||
}
|
||||
|
||||
function main() {
|
||||
console.log('Fixing OpenAPI Generator output for Node.js compatibility...\n');
|
||||
|
||||
const runtimePath = join(generatedDir, 'runtime.ts');
|
||||
|
||||
try {
|
||||
const content = readFileSync(runtimePath, 'utf-8');
|
||||
const fixed = fixRuntime(content);
|
||||
|
||||
if (content !== fixed) {
|
||||
writeFileSync(runtimePath, fixed);
|
||||
console.log(` Fixed: ${runtimePath}`);
|
||||
console.log('\n✅ Applied runtime fixes');
|
||||
} else {
|
||||
console.log('✅ No fixes needed');
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
console.log('⚠️ runtime.ts not found, skipping');
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
@ -8,3 +8,20 @@ export type { NetworkErrorOptions } from "./utils/fetchWithError.js";
|
|||
|
||||
export type { default as OrganizationInvitationRepresentation } from "./defs/organizationInvitationRepresentation.js";
|
||||
export { OrganizationInvitationStatus } from "./defs/organizationInvitationRepresentation.js";
|
||||
|
||||
// V2 API types and classes
|
||||
export {
|
||||
ClientsV2Api,
|
||||
createClientsV2Api,
|
||||
} from "./resources/clientsV2.js";
|
||||
export type {
|
||||
OIDCClientRepresentation,
|
||||
SAMLClientRepresentation,
|
||||
ClientRepresentationV2,
|
||||
AdminApiRealmNameClientsVersionGetRequest,
|
||||
AdminApiRealmNameClientsVersionIdDeleteRequest,
|
||||
AdminApiRealmNameClientsVersionIdGetRequest,
|
||||
AdminApiRealmNameClientsVersionIdPatchRequest,
|
||||
AdminApiRealmNameClientsVersionIdPutRequest,
|
||||
AdminApiRealmNameClientsVersionPostRequest,
|
||||
} from "./resources/clientsV2.js";
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import type ScopeRepresentation from "../defs/scopeRepresentation.js";
|
|||
import type UserRepresentation from "../defs/userRepresentation.js";
|
||||
import type UserSessionRepresentation from "../defs/userSessionRepresentation.js";
|
||||
import Resource from "./resource.js";
|
||||
import { ClientsV2 } from "./clientsV2.js";
|
||||
|
||||
export interface PaginatedQuery {
|
||||
first?: number;
|
||||
|
|
@ -53,6 +54,11 @@ export interface PolicyQuery extends PaginatedQuery {
|
|||
}
|
||||
|
||||
export class Clients extends Resource<{ realm?: string }> {
|
||||
/**
|
||||
* Clients v2 API - New versioned API with OpenAPI-generated client.
|
||||
*/
|
||||
public v2: ClientsV2;
|
||||
|
||||
public find = this.makeRequest<ClientQuery, ClientRepresentation[]>({
|
||||
method: "GET",
|
||||
});
|
||||
|
|
@ -1055,6 +1061,9 @@ export class Clients extends Resource<{ realm?: string }> {
|
|||
}),
|
||||
getBaseUrl: () => client.baseUrl,
|
||||
});
|
||||
|
||||
// Initialize v2 API
|
||||
this.v2 = new ClientsV2(client);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
66
js/libs/keycloak-admin-client/src/resources/clientsV2.ts
Normal file
66
js/libs/keycloak-admin-client/src/resources/clientsV2.ts
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import type { KeycloakAdminClient } from "../client.js";
|
||||
import { Configuration, ClientsV2Api } from "../generated/index.js";
|
||||
|
||||
// Re-export types for convenience
|
||||
export { ClientsV2Api } from "../generated/index.js";
|
||||
export type {
|
||||
AdminApiRealmNameClientsVersionGetRequest,
|
||||
AdminApiRealmNameClientsVersionIdDeleteRequest,
|
||||
AdminApiRealmNameClientsVersionIdGetRequest,
|
||||
AdminApiRealmNameClientsVersionIdPatchRequest,
|
||||
AdminApiRealmNameClientsVersionIdPutRequest,
|
||||
AdminApiRealmNameClientsVersionPostRequest,
|
||||
} from "../generated/apis/ClientsV2Api.js";
|
||||
export type {
|
||||
OIDCClientRepresentation,
|
||||
SAMLClientRepresentation,
|
||||
AdminApiRealmNameClientsVersionGet200ResponseInner as ClientRepresentationV2,
|
||||
} from "../generated/models/index.js";
|
||||
|
||||
/**
|
||||
* Creates a ClientsV2Api instance configured with the KeycloakAdminClient's
|
||||
* base URL and access token.
|
||||
*/
|
||||
export async function createClientsV2Api(
|
||||
client: KeycloakAdminClient,
|
||||
): Promise<ClientsV2Api> {
|
||||
const accessToken = await client.getAccessToken();
|
||||
|
||||
const config = new Configuration({
|
||||
basePath: client.baseUrl,
|
||||
headers: {
|
||||
...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),
|
||||
},
|
||||
});
|
||||
|
||||
return new ClientsV2Api(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clients v2 API resource.
|
||||
* Provides access to the OpenAPI-generated ClientsV2Api.
|
||||
*/
|
||||
export class ClientsV2 {
|
||||
#client: KeycloakAdminClient;
|
||||
|
||||
constructor(client: KeycloakAdminClient) {
|
||||
this.#client = client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ClientsV2Api instance configured with the current access token.
|
||||
* Call this method to get an API instance, then use its methods directly.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const api = await client.clients.v2.api();
|
||||
* const clients = await api.adminApiRealmNameClientsVersionGet({
|
||||
* realmName: "master",
|
||||
* version: "v2",
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
async api(): Promise<ClientsV2Api> {
|
||||
return createClientsV2Api(this.#client);
|
||||
}
|
||||
}
|
||||
211
js/libs/keycloak-admin-client/test/clientsV2.spec.ts
Normal file
211
js/libs/keycloak-admin-client/test/clientsV2.spec.ts
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
import { faker } from "@faker-js/faker";
|
||||
import * as chai from "chai";
|
||||
import { KeycloakAdminClient } from "../src/client.js";
|
||||
import type { ClientsV2Api } from "../src/resources/clientsV2.js";
|
||||
import type { OIDCClientRepresentation } from "../src/generated/models/index.js";
|
||||
import { credentials } from "./constants.js";
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
describe("Clients V2 API", () => {
|
||||
let kcAdminClient: KeycloakAdminClient;
|
||||
let clientsV2Api: ClientsV2Api;
|
||||
let currentClientId: string;
|
||||
|
||||
before(async () => {
|
||||
kcAdminClient = new KeycloakAdminClient();
|
||||
await kcAdminClient.auth(credentials);
|
||||
|
||||
// Get the v2 API instance
|
||||
clientsV2Api = await kcAdminClient.clients.v2.api();
|
||||
|
||||
// Create a client for testing using v2 API
|
||||
currentClientId = faker.internet.username();
|
||||
await clientsV2Api.adminApiRealmNameClientsVersionPost({
|
||||
realmName: kcAdminClient.realmName,
|
||||
version: "v2",
|
||||
adminApiRealmNameClientsVersionGet200ResponseInner: {
|
||||
clientId: currentClientId,
|
||||
protocol: "openid-connect",
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
// Delete the test client
|
||||
if (currentClientId) {
|
||||
await clientsV2Api.adminApiRealmNameClientsVersionIdDelete({
|
||||
realmName: kcAdminClient.realmName,
|
||||
version: "v2",
|
||||
id: currentClientId,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("should list clients", async () => {
|
||||
const clients = await clientsV2Api.adminApiRealmNameClientsVersionGet({
|
||||
realmName: kcAdminClient.realmName,
|
||||
version: "v2",
|
||||
});
|
||||
|
||||
expect(clients).to.be.ok;
|
||||
expect(clients).to.be.an("array");
|
||||
expect(clients.length).to.be.greaterThan(0);
|
||||
|
||||
// Verify our test client is in the list
|
||||
const testClient = clients.find(
|
||||
(c) => (c as OIDCClientRepresentation).clientId === currentClientId,
|
||||
);
|
||||
expect(testClient).to.be.ok;
|
||||
});
|
||||
|
||||
it("should get a single client by clientId", async () => {
|
||||
const client = await clientsV2Api.adminApiRealmNameClientsVersionIdGet({
|
||||
realmName: kcAdminClient.realmName,
|
||||
version: "v2",
|
||||
id: currentClientId,
|
||||
});
|
||||
|
||||
expect(client).to.be.ok;
|
||||
expect((client as OIDCClientRepresentation).clientId).to.equal(
|
||||
currentClientId,
|
||||
);
|
||||
});
|
||||
|
||||
it("should update a client with PUT", async () => {
|
||||
const updatedDescription = "Updated via V2 API test";
|
||||
|
||||
await clientsV2Api.adminApiRealmNameClientsVersionIdPut({
|
||||
realmName: kcAdminClient.realmName,
|
||||
version: "v2",
|
||||
id: currentClientId,
|
||||
adminApiRealmNameClientsVersionGet200ResponseInner: {
|
||||
clientId: currentClientId,
|
||||
protocol: "openid-connect",
|
||||
description: updatedDescription,
|
||||
},
|
||||
});
|
||||
|
||||
const client = await clientsV2Api.adminApiRealmNameClientsVersionIdGet({
|
||||
realmName: kcAdminClient.realmName,
|
||||
version: "v2",
|
||||
id: currentClientId,
|
||||
});
|
||||
|
||||
expect((client as OIDCClientRepresentation).description).to.equal(
|
||||
updatedDescription,
|
||||
);
|
||||
});
|
||||
|
||||
it("should patch a client", async () => {
|
||||
const patchedDisplayName = "Patched Display Name";
|
||||
|
||||
const patchedClient =
|
||||
await clientsV2Api.adminApiRealmNameClientsVersionIdPatch({
|
||||
realmName: kcAdminClient.realmName,
|
||||
version: "v2",
|
||||
id: currentClientId,
|
||||
requestBody: {
|
||||
displayName: patchedDisplayName,
|
||||
},
|
||||
});
|
||||
|
||||
expect((patchedClient as OIDCClientRepresentation).displayName).to.equal(
|
||||
patchedDisplayName,
|
||||
);
|
||||
|
||||
// Verify the change persisted
|
||||
const client = await clientsV2Api.adminApiRealmNameClientsVersionIdGet({
|
||||
realmName: kcAdminClient.realmName,
|
||||
version: "v2",
|
||||
id: currentClientId,
|
||||
});
|
||||
|
||||
expect((client as OIDCClientRepresentation).displayName).to.equal(
|
||||
patchedDisplayName,
|
||||
);
|
||||
});
|
||||
|
||||
it("should create and delete a client", async () => {
|
||||
const clientId = faker.internet.username();
|
||||
|
||||
// Create a new client using v2 API
|
||||
await clientsV2Api.adminApiRealmNameClientsVersionPost({
|
||||
realmName: kcAdminClient.realmName,
|
||||
version: "v2",
|
||||
adminApiRealmNameClientsVersionGet200ResponseInner: {
|
||||
clientId,
|
||||
protocol: "openid-connect",
|
||||
enabled: true,
|
||||
description: "Test client for deletion",
|
||||
},
|
||||
});
|
||||
|
||||
// Verify we can get it via v2 API
|
||||
const client = await clientsV2Api.adminApiRealmNameClientsVersionIdGet({
|
||||
realmName: kcAdminClient.realmName,
|
||||
version: "v2",
|
||||
id: clientId,
|
||||
});
|
||||
expect((client as OIDCClientRepresentation).clientId).to.equal(clientId);
|
||||
|
||||
// Delete the client using v2 API
|
||||
await clientsV2Api.adminApiRealmNameClientsVersionIdDelete({
|
||||
realmName: kcAdminClient.realmName,
|
||||
version: "v2",
|
||||
id: clientId,
|
||||
});
|
||||
|
||||
// Verify it's deleted by checking it's no longer in the list
|
||||
const clients = await clientsV2Api.adminApiRealmNameClientsVersionGet({
|
||||
realmName: kcAdminClient.realmName,
|
||||
version: "v2",
|
||||
});
|
||||
|
||||
const deletedClient = clients.find(
|
||||
(c) => (c as OIDCClientRepresentation).clientId === clientId,
|
||||
);
|
||||
expect(deletedClient).to.be.undefined;
|
||||
});
|
||||
|
||||
it("should create an OIDC client with full configuration", async () => {
|
||||
const clientId = `full-config-${faker.internet.username()}`;
|
||||
|
||||
await clientsV2Api.adminApiRealmNameClientsVersionPost({
|
||||
realmName: kcAdminClient.realmName,
|
||||
version: "v2",
|
||||
adminApiRealmNameClientsVersionGet200ResponseInner: {
|
||||
clientId,
|
||||
protocol: "openid-connect",
|
||||
enabled: true,
|
||||
displayName: "Full Config Test Client",
|
||||
description: "A client with full OIDC configuration",
|
||||
redirectUris: new Set(["http://localhost:3000/callback"]),
|
||||
webOrigins: new Set(["http://localhost:3000"]),
|
||||
},
|
||||
});
|
||||
|
||||
// Get via v2 API and verify
|
||||
const client = await clientsV2Api.adminApiRealmNameClientsVersionIdGet({
|
||||
realmName: kcAdminClient.realmName,
|
||||
version: "v2",
|
||||
id: clientId,
|
||||
});
|
||||
|
||||
expect(client).to.be.ok;
|
||||
expect((client as OIDCClientRepresentation).displayName).to.equal(
|
||||
"Full Config Test Client",
|
||||
);
|
||||
expect((client as OIDCClientRepresentation).protocol).to.equal(
|
||||
"openid-connect",
|
||||
);
|
||||
|
||||
// Cleanup
|
||||
await clientsV2Api.adminApiRealmNameClientsVersionIdDelete({
|
||||
realmName: kcAdminClient.realmName,
|
||||
version: "v2",
|
||||
id: clientId,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -13,7 +13,6 @@
|
|||
"./apps/account-ui:build",
|
||||
"./apps/admin-ui:build",
|
||||
"./libs/keycloak-admin-client:build",
|
||||
"./libs/keycloak-admin-client-v2:generate:file",
|
||||
"./libs/ui-shared:build",
|
||||
"./themes-vendor:build"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -363,31 +363,6 @@ importers:
|
|||
specifier: ^10.9.2
|
||||
version: 10.9.2(@swc/core@1.15.8)(@types/node@25.0.3)(typescript@5.9.3)
|
||||
|
||||
libs/keycloak-admin-client-v2:
|
||||
dependencies:
|
||||
'@microsoft/kiota-abstractions':
|
||||
specifier: 1.0.0-preview.99
|
||||
version: 1.0.0-preview.99
|
||||
'@microsoft/kiota-bundle':
|
||||
specifier: ^1.0.0-preview.99
|
||||
version: 1.0.0-preview.99
|
||||
'@microsoft/kiota-http-fetchlibrary':
|
||||
specifier: ^1.0.0-preview.99
|
||||
version: 1.0.0-preview.99
|
||||
devDependencies:
|
||||
'@microsoft/kiota':
|
||||
specifier: ^1.29.0
|
||||
version: 1.29.0
|
||||
'@types/node':
|
||||
specifier: ^25.0.8
|
||||
version: 25.0.8
|
||||
tsx:
|
||||
specifier: ^4.21.0
|
||||
version: 4.21.0
|
||||
typescript:
|
||||
specifier: ^5.9.3
|
||||
version: 5.9.3
|
||||
|
||||
libs/ui-shared:
|
||||
dependencies:
|
||||
'@keycloak/keycloak-admin-client':
|
||||
|
|
@ -1071,30 +1046,6 @@ packages:
|
|||
resolution: {integrity: sha512-LhKytJM5ZJkbHQVfW/3o747rZUNs/MGg6j/wt/9qwwqEOfvUDTYXXxIBuMgrRXhJ528p41iyz4zjBVHZU74Odg==}
|
||||
hasBin: true
|
||||
|
||||
'@microsoft/kiota-abstractions@1.0.0-preview.99':
|
||||
resolution: {integrity: sha512-6qrlwGCO3DbvpA7tszqk7JNQPFPDg8gv5NGMTziwdtHqaQrUALKusm3vdJGPVGrbNiZL6/lBaY5NSUvLtlhRCg==}
|
||||
|
||||
'@microsoft/kiota-bundle@1.0.0-preview.99':
|
||||
resolution: {integrity: sha512-AxvO+z6UgWMAT2NfXN36CMhAUm/39tUQt8o32axeEJDS/EvZINDAPstbQV+zK7bJRC8kCqUHhCE/fuHX2dXA+g==}
|
||||
|
||||
'@microsoft/kiota-http-fetchlibrary@1.0.0-preview.99':
|
||||
resolution: {integrity: sha512-lIruiYf8L7DPG0Fm92QN5YK4zx4sh76EtTONUAqBCxt5AtEJ7KQceBajaXQsjfcndUAp9LmDVdqR44o8sc9/mA==}
|
||||
|
||||
'@microsoft/kiota-serialization-form@1.0.0-preview.99':
|
||||
resolution: {integrity: sha512-BdXxqNfy+5yWTyxpBguqJYy6E9RRotrDClRcUO89okFcR5N6s6xenjsvGw++q9KWNi7qcHrqaG8xlRz1TLk81Q==}
|
||||
|
||||
'@microsoft/kiota-serialization-json@1.0.0-preview.99':
|
||||
resolution: {integrity: sha512-kDmMYmB7XkRprlLviEynRPDkE45JDxqiuUopfBRMvNK4qhzFiJTXGLihZ2FG6fdVu9LbV3/W0QxbeGP35kDK6A==}
|
||||
|
||||
'@microsoft/kiota-serialization-multipart@1.0.0-preview.99':
|
||||
resolution: {integrity: sha512-cfviCAVTlZPD+MUgdTIgsX/IfHND+gKQhem3vJeo7l5Qqz2rvvVFXOHwECI19umO9Un1QFKe1V5wg+M+aj90+g==}
|
||||
|
||||
'@microsoft/kiota-serialization-text@1.0.0-preview.99':
|
||||
resolution: {integrity: sha512-gcBffQRI1soHVAiS4YjfMr3SQ9fC8xVUGMfarTTszU4PjpxyFOknaWoFdt/0rvMeavI9jOtbyvDKAf77cZyYIQ==}
|
||||
|
||||
'@microsoft/kiota@1.29.0':
|
||||
resolution: {integrity: sha512-qqIlTz48OJ5ZMRoTA/uQA70B7ltS4lPSs9atG5PUn+dKZcgXny3LzQPe12B1LsKoBJYbwhaU3fD8/C1DsLW6Cw==}
|
||||
|
||||
'@microsoft/tsdoc-config@0.17.1':
|
||||
resolution: {integrity: sha512-UtjIFe0C6oYgTnad4q1QP4qXwLhe6tIpNTRStJ2RZEPIkqQPREAwE5spzVxsdn9UaEMUqhh0AqSx3X4nWAKXWw==}
|
||||
|
||||
|
|
@ -1463,9 +1414,6 @@ packages:
|
|||
'@standard-schema/spec@1.1.0':
|
||||
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
|
||||
|
||||
'@std-uritemplate/std-uritemplate@2.0.8':
|
||||
resolution: {integrity: sha512-8oj7jKksoTRxjdPkWKX9FyOKDS8ORBm+ZfTSHm0f3eYha8cI0O1d57gyduOIAX7Y2uUukNDNlxlvGb+FFkE7yQ==}
|
||||
|
||||
'@swc/core-darwin-arm64@1.15.8':
|
||||
resolution: {integrity: sha512-M9cK5GwyWWRkRGwwCbREuj6r8jKdES/haCZ3Xckgkl8MUQJZA3XB7IXXK1IXRNeLjg6m7cnoMICpXv1v1hlJOg==}
|
||||
engines: {node: '>=10'}
|
||||
|
|
@ -1908,10 +1856,6 @@ packages:
|
|||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
adm-zip@0.5.16:
|
||||
resolution: {integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==}
|
||||
engines: {node: '>=12.0'}
|
||||
|
||||
agent-base@7.1.4:
|
||||
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
|
||||
engines: {node: '>= 14'}
|
||||
|
|
@ -3429,9 +3373,6 @@ packages:
|
|||
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
original-fs@1.2.0:
|
||||
resolution: {integrity: sha512-IGo+qFumpIV65oDchJrqL0BOk9kr82fObnTesNJt8t3YgP6vfqcmRs0ofPzg3D9PKMeBHt7lrg1k/6L+oFdS8g==}
|
||||
|
||||
own-keys@1.0.1:
|
||||
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
|
@ -3996,9 +3937,6 @@ packages:
|
|||
tinybench@2.9.0:
|
||||
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
||||
|
||||
tinyduration@3.4.1:
|
||||
resolution: {integrity: sha512-NemFoamVYn7TmtwZKZ3OiliM9fZkr6EWiTM+wKknco6POSy2gS689xx/pXip0JYp40HXpUw6k65CUYHWYUXdaA==}
|
||||
|
||||
tinyexec@1.0.2:
|
||||
resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==}
|
||||
engines: {node: '>=18'}
|
||||
|
|
@ -4318,10 +4256,6 @@ packages:
|
|||
resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
vscode-jsonrpc@8.2.1:
|
||||
resolution: {integrity: sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
vscode-uri@3.1.0:
|
||||
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
|
||||
|
||||
|
|
@ -5137,55 +5071,6 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
|
||||
'@microsoft/kiota-abstractions@1.0.0-preview.99':
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@std-uritemplate/std-uritemplate': 2.0.8
|
||||
tinyduration: 3.4.1
|
||||
tslib: 2.8.1
|
||||
|
||||
'@microsoft/kiota-bundle@1.0.0-preview.99':
|
||||
dependencies:
|
||||
'@microsoft/kiota-abstractions': 1.0.0-preview.99
|
||||
'@microsoft/kiota-http-fetchlibrary': 1.0.0-preview.99
|
||||
'@microsoft/kiota-serialization-form': 1.0.0-preview.99
|
||||
'@microsoft/kiota-serialization-json': 1.0.0-preview.99
|
||||
'@microsoft/kiota-serialization-multipart': 1.0.0-preview.99
|
||||
'@microsoft/kiota-serialization-text': 1.0.0-preview.99
|
||||
|
||||
'@microsoft/kiota-http-fetchlibrary@1.0.0-preview.99':
|
||||
dependencies:
|
||||
'@microsoft/kiota-abstractions': 1.0.0-preview.99
|
||||
'@opentelemetry/api': 1.9.0
|
||||
tslib: 2.8.1
|
||||
|
||||
'@microsoft/kiota-serialization-form@1.0.0-preview.99':
|
||||
dependencies:
|
||||
'@microsoft/kiota-abstractions': 1.0.0-preview.99
|
||||
tslib: 2.8.1
|
||||
|
||||
'@microsoft/kiota-serialization-json@1.0.0-preview.99':
|
||||
dependencies:
|
||||
'@microsoft/kiota-abstractions': 1.0.0-preview.99
|
||||
tslib: 2.8.1
|
||||
|
||||
'@microsoft/kiota-serialization-multipart@1.0.0-preview.99':
|
||||
dependencies:
|
||||
'@microsoft/kiota-abstractions': 1.0.0-preview.99
|
||||
tslib: 2.8.1
|
||||
|
||||
'@microsoft/kiota-serialization-text@1.0.0-preview.99':
|
||||
dependencies:
|
||||
'@microsoft/kiota-abstractions': 1.0.0-preview.99
|
||||
tslib: 2.8.1
|
||||
|
||||
'@microsoft/kiota@1.29.0':
|
||||
dependencies:
|
||||
adm-zip: 0.5.16
|
||||
original-fs: 1.2.0
|
||||
uuid: 13.0.0
|
||||
vscode-jsonrpc: 8.2.1
|
||||
|
||||
'@microsoft/tsdoc-config@0.17.1':
|
||||
dependencies:
|
||||
'@microsoft/tsdoc': 0.15.1
|
||||
|
|
@ -5271,7 +5156,8 @@ snapshots:
|
|||
dependencies:
|
||||
'@octokit/openapi-types': 27.0.0
|
||||
|
||||
'@opentelemetry/api@1.9.0': {}
|
||||
'@opentelemetry/api@1.9.0':
|
||||
optional: true
|
||||
|
||||
'@patternfly/patternfly@4.224.5': {}
|
||||
|
||||
|
|
@ -5563,8 +5449,6 @@ snapshots:
|
|||
|
||||
'@standard-schema/spec@1.1.0': {}
|
||||
|
||||
'@std-uritemplate/std-uritemplate@2.0.8': {}
|
||||
|
||||
'@swc/core-darwin-arm64@1.15.8':
|
||||
optional: true
|
||||
|
||||
|
|
@ -6070,8 +5954,6 @@ snapshots:
|
|||
|
||||
acorn@8.15.0: {}
|
||||
|
||||
adm-zip@0.5.16: {}
|
||||
|
||||
agent-base@7.1.4: {}
|
||||
|
||||
ajv-draft-04@1.0.0(ajv@8.13.0):
|
||||
|
|
@ -6996,6 +6878,7 @@ snapshots:
|
|||
get-tsconfig@4.13.0:
|
||||
dependencies:
|
||||
resolve-pkg-maps: 1.0.0
|
||||
optional: true
|
||||
|
||||
glob-parent@5.1.2:
|
||||
dependencies:
|
||||
|
|
@ -7743,8 +7626,6 @@ snapshots:
|
|||
type-check: 0.4.0
|
||||
word-wrap: 1.2.5
|
||||
|
||||
original-fs@1.2.0: {}
|
||||
|
||||
own-keys@1.0.1:
|
||||
dependencies:
|
||||
get-intrinsic: 1.3.0
|
||||
|
|
@ -8060,7 +7941,8 @@ snapshots:
|
|||
|
||||
resolve-from@4.0.0: {}
|
||||
|
||||
resolve-pkg-maps@1.0.0: {}
|
||||
resolve-pkg-maps@1.0.0:
|
||||
optional: true
|
||||
|
||||
resolve@1.22.10:
|
||||
dependencies:
|
||||
|
|
@ -8432,8 +8314,6 @@ snapshots:
|
|||
|
||||
tinybench@2.9.0: {}
|
||||
|
||||
tinyduration@3.4.1: {}
|
||||
|
||||
tinyexec@1.0.2: {}
|
||||
|
||||
tinyglobby@0.2.15:
|
||||
|
|
@ -8517,6 +8397,7 @@ snapshots:
|
|||
get-tsconfig: 4.13.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
optional: true
|
||||
|
||||
type-check@0.4.0:
|
||||
dependencies:
|
||||
|
|
@ -8769,8 +8650,6 @@ snapshots:
|
|||
|
||||
void-elements@3.1.0: {}
|
||||
|
||||
vscode-jsonrpc@8.2.1: {}
|
||||
|
||||
vscode-uri@3.1.0: {}
|
||||
|
||||
w3c-xmlserializer@5.0.0:
|
||||
|
|
|
|||
Loading…
Reference in a new issue