feat: implement service provider
This commit is contained in:
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
build/
|
dist/
|
||||||
node_modules/
|
node_modules/
|
||||||
coverage/
|
coverage/
|
||||||
|
|||||||
Generated
+313
@@ -8,7 +8,11 @@
|
|||||||
"name": "@stevanfreeborn/netdi",
|
"name": "@stevanfreeborn/netdi",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"reflect-metadata": "^0.2.2"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@swc/core": "^1.11.24",
|
||||||
"@types/node": "^22.15.17",
|
"@types/node": "^22.15.17",
|
||||||
"@vitest/coverage-istanbul": "^3.1.3",
|
"@vitest/coverage-istanbul": "^3.1.3",
|
||||||
"@vitest/ui": "^3.1.3",
|
"@vitest/ui": "^3.1.3",
|
||||||
@@ -17,6 +21,7 @@
|
|||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"typescript-eslint": "^8.32.0",
|
"typescript-eslint": "^8.32.0",
|
||||||
|
"unplugin-swc": "^1.5.2",
|
||||||
"vitest": "^3.1.3"
|
"vitest": "^3.1.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1095,6 +1100,36 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@rollup/pluginutils": {
|
||||||
|
"version": "5.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz",
|
||||||
|
"integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/estree": "^1.0.0",
|
||||||
|
"estree-walker": "^2.0.2",
|
||||||
|
"picomatch": "^4.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"rollup": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/pluginutils/node_modules/estree-walker": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.40.2",
|
"version": "4.40.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.2.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.2.tgz",
|
||||||
@@ -1375,6 +1410,232 @@
|
|||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@swc/core": {
|
||||||
|
"version": "1.11.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.11.24.tgz",
|
||||||
|
"integrity": "sha512-MaQEIpfcEMzx3VWWopbofKJvaraqmL6HbLlw2bFZ7qYqYw3rkhM0cQVEgyzbHtTWwCwPMFZSC2DUbhlZgrMfLg==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@swc/counter": "^0.1.3",
|
||||||
|
"@swc/types": "^0.1.21"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/swc"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@swc/core-darwin-arm64": "1.11.24",
|
||||||
|
"@swc/core-darwin-x64": "1.11.24",
|
||||||
|
"@swc/core-linux-arm-gnueabihf": "1.11.24",
|
||||||
|
"@swc/core-linux-arm64-gnu": "1.11.24",
|
||||||
|
"@swc/core-linux-arm64-musl": "1.11.24",
|
||||||
|
"@swc/core-linux-x64-gnu": "1.11.24",
|
||||||
|
"@swc/core-linux-x64-musl": "1.11.24",
|
||||||
|
"@swc/core-win32-arm64-msvc": "1.11.24",
|
||||||
|
"@swc/core-win32-ia32-msvc": "1.11.24",
|
||||||
|
"@swc/core-win32-x64-msvc": "1.11.24"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@swc/helpers": ">=0.5.17"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@swc/helpers": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-darwin-arm64": {
|
||||||
|
"version": "1.11.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.11.24.tgz",
|
||||||
|
"integrity": "sha512-dhtVj0PC1APOF4fl5qT2neGjRLgHAAYfiVP8poJelhzhB/318bO+QCFWAiimcDoyMgpCXOhTp757gnoJJrheWA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0 AND MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-darwin-x64": {
|
||||||
|
"version": "1.11.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.11.24.tgz",
|
||||||
|
"integrity": "sha512-H/3cPs8uxcj2Fe3SoLlofN5JG6Ny5bl8DuZ6Yc2wr7gQFBmyBkbZEz+sPVgsID7IXuz7vTP95kMm1VL74SO5AQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0 AND MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-linux-arm-gnueabihf": {
|
||||||
|
"version": "1.11.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.24.tgz",
|
||||||
|
"integrity": "sha512-PHJgWEpCsLo/NGj+A2lXZ2mgGjsr96ULNW3+T3Bj2KTc8XtMUkE8tmY2Da20ItZOvPNC/69KroU7edyo1Flfbw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-linux-arm64-gnu": {
|
||||||
|
"version": "1.11.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.24.tgz",
|
||||||
|
"integrity": "sha512-C2FJb08+n5SD4CYWCTZx1uR88BN41ZieoHvI8A55hfVf2woT8+6ZiBzt74qW2g+ntZ535Jts5VwXAKdu41HpBg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0 AND MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-linux-arm64-musl": {
|
||||||
|
"version": "1.11.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.24.tgz",
|
||||||
|
"integrity": "sha512-ypXLIdszRo0re7PNNaXN0+2lD454G8l9LPK/rbfRXnhLWDBPURxzKlLlU/YGd2zP98wPcVooMmegRSNOKfvErw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0 AND MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-linux-x64-gnu": {
|
||||||
|
"version": "1.11.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.24.tgz",
|
||||||
|
"integrity": "sha512-IM7d+STVZD48zxcgo69L0yYptfhaaE9cMZ+9OoMxirNafhKKXwoZuufol1+alEFKc+Wbwp+aUPe/DeWC/Lh3dg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0 AND MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-linux-x64-musl": {
|
||||||
|
"version": "1.11.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.11.24.tgz",
|
||||||
|
"integrity": "sha512-DZByJaMVzSfjQKKQn3cqSeqwy6lpMaQDQQ4HPlch9FWtDx/dLcpdIhxssqZXcR2rhaQVIaRQsCqwV6orSDGAGw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0 AND MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-win32-arm64-msvc": {
|
||||||
|
"version": "1.11.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.24.tgz",
|
||||||
|
"integrity": "sha512-Q64Ytn23y9aVDKN5iryFi8mRgyHw3/kyjTjT4qFCa8AEb5sGUuSj//AUZ6c0J7hQKMHlg9do5Etvoe61V98/JQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0 AND MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-win32-ia32-msvc": {
|
||||||
|
"version": "1.11.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.24.tgz",
|
||||||
|
"integrity": "sha512-9pKLIisE/Hh2vJhGIPvSoTK4uBSPxNVyXHmOrtdDot4E1FUUI74Vi8tFdlwNbaj8/vusVnb8xPXsxF1uB0VgiQ==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0 AND MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-win32-x64-msvc": {
|
||||||
|
"version": "1.11.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.24.tgz",
|
||||||
|
"integrity": "sha512-sybnXtOsdB+XvzVFlBVGgRHLqp3yRpHK7CrmpuDKszhj/QhmsaZzY/GHSeALlMtLup13M0gqbcQvsTNlAHTg3w==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0 AND MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/counter": {
|
||||||
|
"version": "0.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
|
||||||
|
"integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
|
"node_modules/@swc/types": {
|
||||||
|
"version": "0.1.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.21.tgz",
|
||||||
|
"integrity": "sha512-2YEtj5HJVbKivud9N4bpPBAyZhj4S2Ipe5LkUG94alTpr7in/GU/EARgPAd3BwU+YOmFVJC2+kjqhGRi3r0ZpQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@swc/counter": "^0.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/estree": {
|
"node_modules/@types/estree": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
|
||||||
@@ -3391,6 +3652,16 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/load-tsconfig": {
|
||||||
|
"version": "0.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz",
|
||||||
|
"integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/locate-path": {
|
"node_modules/locate-path": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||||
@@ -4014,6 +4285,12 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/reflect-metadata": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/resolve-from": {
|
"node_modules/resolve-from": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||||
@@ -4731,6 +5008,35 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/unplugin": {
|
||||||
|
"version": "1.16.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.16.1.tgz",
|
||||||
|
"integrity": "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"acorn": "^8.14.0",
|
||||||
|
"webpack-virtual-modules": "^0.6.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/unplugin-swc": {
|
||||||
|
"version": "1.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/unplugin-swc/-/unplugin-swc-1.5.2.tgz",
|
||||||
|
"integrity": "sha512-bf8DJO8lD1wpnwFglQpVH2XEaFfVsSU5C7yFyLwGT1gxskPtejlDeuttKxjtmHTSqrDsQrK0FCFdhw3Ny+K7hA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@rollup/pluginutils": "^5.1.0",
|
||||||
|
"load-tsconfig": "^0.2.5",
|
||||||
|
"unplugin": "^1.11.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@swc/core": "^1.2.108"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/update-browserslist-db": {
|
"node_modules/update-browserslist-db": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
|
||||||
@@ -4951,6 +5257,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/webpack-virtual-modules": {
|
||||||
|
"version": "0.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
||||||
|
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@swc/core": "^1.11.24",
|
||||||
"@types/node": "^22.15.17",
|
"@types/node": "^22.15.17",
|
||||||
"@vitest/coverage-istanbul": "^3.1.3",
|
"@vitest/coverage-istanbul": "^3.1.3",
|
||||||
"@vitest/ui": "^3.1.3",
|
"@vitest/ui": "^3.1.3",
|
||||||
@@ -55,6 +56,10 @@
|
|||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"typescript-eslint": "^8.32.0",
|
"typescript-eslint": "^8.32.0",
|
||||||
|
"unplugin-swc": "^1.5.2",
|
||||||
"vitest": "^3.1.3"
|
"vitest": "^3.1.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"reflect-metadata": "^0.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import type { ServiceIdentifier } from './types.js';
|
||||||
|
|
||||||
|
export function inject<T>(serviceType: ServiceIdentifier<T>): ParameterDecorator {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-wrapper-object-types
|
||||||
|
return (target: Object, _: string | symbol | undefined, parameterIndex: number) => {
|
||||||
|
Reflect.defineMetadata('di:paramtypes', serviceType, target, parameterIndex.toString());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function injectable(): ClassDecorator {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
|
return (target: Function) => {
|
||||||
|
Reflect.defineMetadata('di:injectable', true, target);
|
||||||
|
};
|
||||||
|
}
|
||||||
+5
-1
@@ -1 +1,5 @@
|
|||||||
console.log('netdi');
|
import "reflect-metadata";
|
||||||
|
|
||||||
|
export * from './serviceProvider.js';
|
||||||
|
export * from './types.js';
|
||||||
|
export * from './decorators.js';
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
import type {
|
||||||
|
IServiceProvider,
|
||||||
|
IServiceScope,
|
||||||
|
ServiceDescriptor,
|
||||||
|
ServiceIdentifier,
|
||||||
|
Constructor,
|
||||||
|
} from './types.js';
|
||||||
|
|
||||||
|
export class ServiceScope implements IServiceScope {
|
||||||
|
public readonly serviceProvider: IServiceProvider;
|
||||||
|
|
||||||
|
constructor(serviceProvider: IServiceProvider) {
|
||||||
|
this.serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose(): void {
|
||||||
|
this.serviceProvider.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ServiceProvider implements IServiceProvider {
|
||||||
|
private descriptors: Map<ServiceIdentifier<unknown>, ServiceDescriptor<unknown>>;
|
||||||
|
private singletonInstances: Map<ServiceIdentifier<unknown>, unknown> = new Map();
|
||||||
|
private scopedInstances: Map<ServiceIdentifier<unknown>, unknown> = new Map();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
descriptors: Map<ServiceIdentifier<unknown>, ServiceDescriptor<unknown>>,
|
||||||
|
parent?: ServiceProvider,
|
||||||
|
) {
|
||||||
|
this.descriptors = descriptors;
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
parent.singletonInstances.forEach((value, key) => {
|
||||||
|
this.singletonInstances.set(key, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const descriptor of descriptors.values()) {
|
||||||
|
if (descriptor.lifetime === 'singleton') {
|
||||||
|
this.resolveService(descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getService<T>(serviceType: ServiceIdentifier<T>): T {
|
||||||
|
const descriptor = this.descriptors.get(serviceType);
|
||||||
|
|
||||||
|
if (!descriptor) {
|
||||||
|
throw new Error(`Service of type ${serviceType.toString()} is not registered.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.resolveService(descriptor as ServiceDescriptor<T>);
|
||||||
|
}
|
||||||
|
|
||||||
|
createScope(): IServiceScope {
|
||||||
|
const scopedProvider = new ServiceProvider(this.descriptors, this);
|
||||||
|
return new ServiceScope(scopedProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose(): void {
|
||||||
|
this.scopedInstances.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private resolveService<T>(descriptor: ServiceDescriptor<T>): T {
|
||||||
|
const { serviceType, implementationType, lifetime, factory } = descriptor;
|
||||||
|
|
||||||
|
switch (lifetime) {
|
||||||
|
case 'singleton': {
|
||||||
|
if (this.singletonInstances.has(serviceType)) {
|
||||||
|
return this.singletonInstances.get(serviceType) as T;
|
||||||
|
}
|
||||||
|
const singletonInstance = factory ? factory(this) : this.createInstance(implementationType);
|
||||||
|
this.singletonInstances.set(serviceType, singletonInstance);
|
||||||
|
return singletonInstance;
|
||||||
|
}
|
||||||
|
case 'scoped': {
|
||||||
|
if (this.scopedInstances.has(serviceType)) {
|
||||||
|
return this.scopedInstances.get(serviceType) as T;
|
||||||
|
}
|
||||||
|
const scopedInstance = factory ? factory(this) : this.createInstance(implementationType);
|
||||||
|
this.scopedInstances.set(serviceType, scopedInstance);
|
||||||
|
return scopedInstance;
|
||||||
|
}
|
||||||
|
case 'transient':
|
||||||
|
return factory ? factory(this) : this.createInstance(implementationType);
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown service lifetime: ${lifetime}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private createInstance<T>(ctor: Constructor<T>): T {
|
||||||
|
const paramTypes = Reflect.getMetadata('design:paramtypes', ctor) ?? [];
|
||||||
|
|
||||||
|
const params = paramTypes.map((_: unknown, index: number) => {
|
||||||
|
const serviceType = Reflect.getMetadata(
|
||||||
|
'di:paramtypes',
|
||||||
|
ctor,
|
||||||
|
index.toString(),
|
||||||
|
) as ServiceIdentifier<unknown>;
|
||||||
|
|
||||||
|
return this.getService(serviceType);
|
||||||
|
});
|
||||||
|
|
||||||
|
return new ctor(...params);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
export type ServiceLifetime = 'singleton' | 'scoped' | 'transient';
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export type Constructor<T> = new (...args: any[]) => T;
|
||||||
|
|
||||||
|
export type ServiceIdentifier<T = unknown> = symbol & { __brand: T };
|
||||||
|
|
||||||
|
export type ServiceFactory<T> = (provider: IServiceProvider) => T;
|
||||||
|
|
||||||
|
export type ServiceDescriptor<T = unknown> = {
|
||||||
|
serviceType: ServiceIdentifier<T>;
|
||||||
|
implementationType: Constructor<T>;
|
||||||
|
lifetime: ServiceLifetime;
|
||||||
|
factory?: ServiceFactory<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createServiceIdentifier<T>(): ServiceIdentifier<T> {
|
||||||
|
return Symbol() as ServiceIdentifier<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IServiceCollection {
|
||||||
|
addSingleton<T>(serviceType: ServiceIdentifier<T>, implementationType: Constructor<T>): IServiceCollection;
|
||||||
|
addSingleton<T>(serviceType: ServiceIdentifier<T>, factory: ServiceFactory<T>): IServiceCollection;
|
||||||
|
addScoped<T>(serviceType: ServiceIdentifier<T>, implementationType: Constructor<T>): IServiceCollection;
|
||||||
|
addScoped<T>(serviceType: ServiceIdentifier<T>, factory: ServiceFactory<T>): IServiceCollection;
|
||||||
|
addTransient<T>(serviceType: ServiceIdentifier<T>, implementationType: Constructor<T>): IServiceCollection;
|
||||||
|
addTransient<T>(serviceType: ServiceIdentifier<T>, factory: ServiceFactory<T>): IServiceCollection;
|
||||||
|
buildServiceProvider(): IServiceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IServiceProvider {
|
||||||
|
getService<T>(serviceType: ServiceIdentifier<T>): T;
|
||||||
|
createScope(): IServiceScope;
|
||||||
|
dispose(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IServiceScope {
|
||||||
|
serviceProvider: IServiceProvider;
|
||||||
|
dispose(): void;
|
||||||
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
console.log('netdi tests');
|
|
||||||
@@ -0,0 +1,509 @@
|
|||||||
|
import { describe, expect, it, test } from 'vitest';
|
||||||
|
import { injectable, ServiceProvider } from '../src';
|
||||||
|
import { createServiceIdentifier, ServiceLifetime } from '../src/types';
|
||||||
|
import { randomUUID } from 'crypto';
|
||||||
|
import { inject } from '../src';
|
||||||
|
|
||||||
|
describe('ServiceProvider', () => {
|
||||||
|
it('it should throw an error when service is not registered', () => {
|
||||||
|
type IService = object;
|
||||||
|
const serviceIdentifier = createServiceIdentifier<IService>();
|
||||||
|
const serviceProvider = new ServiceProvider(new Map());
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
serviceProvider.getService(serviceIdentifier);
|
||||||
|
}).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it should resolve singleton service', () => {
|
||||||
|
type IService = object;
|
||||||
|
const serviceIdentifier = createServiceIdentifier<IService>();
|
||||||
|
const serviceProvider = new ServiceProvider(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
serviceIdentifier,
|
||||||
|
{
|
||||||
|
serviceType: serviceIdentifier,
|
||||||
|
implementationType: Object,
|
||||||
|
lifetime: 'singleton',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const service1 = serviceProvider.getService(serviceIdentifier);
|
||||||
|
const service2 = serviceProvider.getService(serviceIdentifier);
|
||||||
|
|
||||||
|
expect(service1).toBe(service2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it should resolve singleton service using factory', () => {
|
||||||
|
type IService = object;
|
||||||
|
const serviceIdentifier = createServiceIdentifier<IService>();
|
||||||
|
const serviceProvider = new ServiceProvider(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
serviceIdentifier,
|
||||||
|
{
|
||||||
|
serviceType: serviceIdentifier,
|
||||||
|
implementationType: Object,
|
||||||
|
lifetime: 'singleton',
|
||||||
|
factory: () => new Object(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const service1 = serviceProvider.getService(serviceIdentifier);
|
||||||
|
const service2 = serviceProvider.getService(serviceIdentifier);
|
||||||
|
|
||||||
|
expect(service1).toBe(service2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it should resolve scoped service', () => {
|
||||||
|
type IService = object;
|
||||||
|
const serviceIdentifier = createServiceIdentifier<IService>();
|
||||||
|
const serviceProvider = new ServiceProvider(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
serviceIdentifier,
|
||||||
|
{
|
||||||
|
serviceType: serviceIdentifier,
|
||||||
|
implementationType: Object,
|
||||||
|
lifetime: 'scoped',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const scope1 = serviceProvider.createScope();
|
||||||
|
const scope2 = serviceProvider.createScope();
|
||||||
|
|
||||||
|
const service1 = scope1.serviceProvider.getService(serviceIdentifier);
|
||||||
|
const service2 = scope2.serviceProvider.getService(serviceIdentifier);
|
||||||
|
|
||||||
|
expect(service1).not.toBe(service2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it should resolve scoped service using factory', () => {
|
||||||
|
type IService = object;
|
||||||
|
const serviceIdentifier = createServiceIdentifier<IService>();
|
||||||
|
const serviceProvider = new ServiceProvider(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
serviceIdentifier,
|
||||||
|
{
|
||||||
|
serviceType: serviceIdentifier,
|
||||||
|
implementationType: Object,
|
||||||
|
lifetime: 'scoped',
|
||||||
|
factory: () => new Object(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const scope1 = serviceProvider.createScope();
|
||||||
|
const scope2 = serviceProvider.createScope();
|
||||||
|
|
||||||
|
const service1 = scope1.serviceProvider.getService(serviceIdentifier);
|
||||||
|
const service2 = scope2.serviceProvider.getService(serviceIdentifier);
|
||||||
|
|
||||||
|
expect(service1).not.toBe(service2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it should resolve transient service', () => {
|
||||||
|
type IService = object;
|
||||||
|
const serviceIdentifier = createServiceIdentifier<IService>();
|
||||||
|
const serviceProvider = new ServiceProvider(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
serviceIdentifier,
|
||||||
|
{
|
||||||
|
serviceType: serviceIdentifier,
|
||||||
|
implementationType: Object,
|
||||||
|
lifetime: 'transient',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const service1 = serviceProvider.getService(serviceIdentifier);
|
||||||
|
const service2 = serviceProvider.getService(serviceIdentifier);
|
||||||
|
|
||||||
|
expect(service1).not.toBe(service2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it should resolve transient service using factory', () => {
|
||||||
|
type IService = object;
|
||||||
|
const serviceIdentifier = createServiceIdentifier<IService>();
|
||||||
|
const serviceProvider = new ServiceProvider(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
serviceIdentifier,
|
||||||
|
{
|
||||||
|
serviceType: serviceIdentifier,
|
||||||
|
implementationType: Object,
|
||||||
|
lifetime: 'transient',
|
||||||
|
factory: () => new Object(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const service1 = serviceProvider.getService(serviceIdentifier);
|
||||||
|
const service2 = serviceProvider.getService(serviceIdentifier);
|
||||||
|
|
||||||
|
expect(service1).not.toBe(service2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it should resolve same singleton instance across scopes', () => {
|
||||||
|
type IService = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Service implements IService {
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.id = randomUUID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const serviceIdentifier = createServiceIdentifier<IService>();
|
||||||
|
|
||||||
|
const serviceProvider = new ServiceProvider(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
serviceIdentifier,
|
||||||
|
{
|
||||||
|
serviceType: serviceIdentifier,
|
||||||
|
implementationType: Service,
|
||||||
|
lifetime: 'singleton',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const scope1 = serviceProvider.createScope();
|
||||||
|
const scope2 = serviceProvider.createScope();
|
||||||
|
|
||||||
|
const service1 = scope1.serviceProvider.getService(serviceIdentifier);
|
||||||
|
const service2 = scope2.serviceProvider.getService(serviceIdentifier);
|
||||||
|
|
||||||
|
expect(service1.id).toBe(service2.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it should resolve solve injected dependencies', () => {
|
||||||
|
interface IUserRepository {
|
||||||
|
getUser(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
class UserRepository implements IUserRepository {
|
||||||
|
getUser(): string {
|
||||||
|
return 'John Doe';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const userRepositoryIdentifier = createServiceIdentifier<IUserRepository>();
|
||||||
|
|
||||||
|
interface IUserService {
|
||||||
|
getUser(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserService implements IUserService {
|
||||||
|
private userRepository: IUserRepository;
|
||||||
|
|
||||||
|
constructor(@inject(userRepositoryIdentifier) userRepository: IUserRepository) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
getUser(): string {
|
||||||
|
return this.userRepository.getUser();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const userServiceIdentifier = createServiceIdentifier<IUserService>();
|
||||||
|
|
||||||
|
const serviceProvider = new ServiceProvider(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
userRepositoryIdentifier,
|
||||||
|
{
|
||||||
|
serviceType: userRepositoryIdentifier,
|
||||||
|
implementationType: UserRepository,
|
||||||
|
lifetime: 'singleton',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
userServiceIdentifier,
|
||||||
|
{
|
||||||
|
serviceType: userServiceIdentifier,
|
||||||
|
implementationType: UserService,
|
||||||
|
lifetime: 'singleton',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const userService = serviceProvider.getService(userServiceIdentifier);
|
||||||
|
|
||||||
|
const user = userService.getUser();
|
||||||
|
|
||||||
|
expect(user).toBe('John Doe');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it should throw an error when service has invalid lifetime', () => {
|
||||||
|
type IService = object;
|
||||||
|
|
||||||
|
const serviceIdentifier = createServiceIdentifier<IService>();
|
||||||
|
|
||||||
|
const serviceProvider = new ServiceProvider(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
serviceIdentifier,
|
||||||
|
{
|
||||||
|
serviceType: serviceIdentifier,
|
||||||
|
implementationType: Object,
|
||||||
|
lifetime: 'invalid' as ServiceLifetime,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
serviceProvider.getService(serviceIdentifier);
|
||||||
|
}).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it should resolve same scoped instance in the same scope', () => {
|
||||||
|
type IService = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Service implements IService {
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.id = randomUUID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const serviceIdentifier = createServiceIdentifier<IService>();
|
||||||
|
|
||||||
|
const serviceProvider = new ServiceProvider(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
serviceIdentifier,
|
||||||
|
{
|
||||||
|
serviceType: serviceIdentifier,
|
||||||
|
implementationType: Service,
|
||||||
|
lifetime: 'scoped',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const scope = serviceProvider.createScope();
|
||||||
|
|
||||||
|
const service1 = scope.serviceProvider.getService(serviceIdentifier);
|
||||||
|
const service2 = scope.serviceProvider.getService(serviceIdentifier);
|
||||||
|
|
||||||
|
expect(service1).toBe(service2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it should clear the scoped instances on dispose from provider', () => {
|
||||||
|
type IService = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Service implements IService {
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.id = randomUUID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const serviceIdentifier = createServiceIdentifier<IService>();
|
||||||
|
|
||||||
|
const serviceProvider = new ServiceProvider(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
serviceIdentifier,
|
||||||
|
{
|
||||||
|
serviceType: serviceIdentifier,
|
||||||
|
implementationType: Service,
|
||||||
|
lifetime: 'scoped',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const initialService = serviceProvider.getService(serviceIdentifier);
|
||||||
|
|
||||||
|
serviceProvider.dispose();
|
||||||
|
|
||||||
|
const newService = serviceProvider.getService(serviceIdentifier);
|
||||||
|
|
||||||
|
expect(initialService).not.toBe(newService);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it should clear the scoped instances on dispose from a scope', () => {
|
||||||
|
type IService = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Service implements IService {
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.id = randomUUID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const serviceIdentifier = createServiceIdentifier<IService>();
|
||||||
|
|
||||||
|
const serviceProvider = new ServiceProvider(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
serviceIdentifier,
|
||||||
|
{
|
||||||
|
serviceType: serviceIdentifier,
|
||||||
|
implementationType: Service,
|
||||||
|
lifetime: 'scoped',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const scope = serviceProvider.createScope();
|
||||||
|
|
||||||
|
const initialService = scope.serviceProvider.getService(serviceIdentifier);
|
||||||
|
|
||||||
|
scope.dispose();
|
||||||
|
|
||||||
|
const newService = scope.serviceProvider.getService(serviceIdentifier);
|
||||||
|
|
||||||
|
expect(initialService).not.toBe(newService);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it should not clear the scoped instances on dispose from a scope when the provider is disposed', () => {
|
||||||
|
type IService = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Service implements IService {
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.id = randomUUID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const serviceIdentifier = createServiceIdentifier<IService>();
|
||||||
|
|
||||||
|
const serviceProvider = new ServiceProvider(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
serviceIdentifier,
|
||||||
|
{
|
||||||
|
serviceType: serviceIdentifier,
|
||||||
|
implementationType: Service,
|
||||||
|
lifetime: 'scoped',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const scope = serviceProvider.createScope();
|
||||||
|
|
||||||
|
const initialService = scope.serviceProvider.getService(serviceIdentifier);
|
||||||
|
|
||||||
|
serviceProvider.dispose();
|
||||||
|
|
||||||
|
const newService = scope.serviceProvider.getService(serviceIdentifier);
|
||||||
|
|
||||||
|
expect(initialService).toBe(newService);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("it should not clear the scoped instances on dispose from another scope when one scope is disposed", () => {
|
||||||
|
type IService = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Service implements IService {
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.id = randomUUID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const serviceIdentifier = createServiceIdentifier<IService>();
|
||||||
|
|
||||||
|
const serviceProvider = new ServiceProvider(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
serviceIdentifier,
|
||||||
|
{
|
||||||
|
serviceType: serviceIdentifier,
|
||||||
|
implementationType: Service,
|
||||||
|
lifetime: 'scoped',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const scope1 = serviceProvider.createScope();
|
||||||
|
const scope2 = serviceProvider.createScope();
|
||||||
|
|
||||||
|
const initialService1 = scope1.serviceProvider.getService(serviceIdentifier);
|
||||||
|
const initialService2 = scope2.serviceProvider.getService(serviceIdentifier);
|
||||||
|
|
||||||
|
scope1.dispose();
|
||||||
|
|
||||||
|
const newService1 = scope1.serviceProvider.getService(serviceIdentifier);
|
||||||
|
const newService2 = scope2.serviceProvider.getService(serviceIdentifier);
|
||||||
|
|
||||||
|
expect(initialService1).not.toBe(newService1);
|
||||||
|
expect(initialService2).toBe(newService2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it should throw error when dependency is not registered', () => {
|
||||||
|
interface IUserRepository {
|
||||||
|
getUser(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserService {
|
||||||
|
private userRepository: IUserRepository;
|
||||||
|
|
||||||
|
constructor(@inject('IUserRepository') userRepository: IUserRepository) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
getUser(): string {
|
||||||
|
return this.userRepository.getUser();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const userServiceIdentifier = createServiceIdentifier<UserService>();
|
||||||
|
|
||||||
|
const serviceProvider = new ServiceProvider(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
userServiceIdentifier,
|
||||||
|
{
|
||||||
|
serviceType: userServiceIdentifier,
|
||||||
|
implementationType: UserService,
|
||||||
|
lifetime: 'scoped',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
serviceProvider.getService(userServiceIdentifier);
|
||||||
|
}).toThrowError();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { defineConfig } from 'vitest/config';
|
import { defineConfig } from 'vitest/config';
|
||||||
|
import swc from 'unplugin-swc';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
test: {
|
test: {
|
||||||
@@ -10,4 +11,5 @@ export default defineConfig({
|
|||||||
exclude: ['**/src/index.ts', '**/src/server.ts'],
|
exclude: ['**/src/index.ts', '**/src/server.ts'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
plugins: [swc.vite()],
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user