emilsoftware-utilities 2.0.0-dev.66 → 2.0.0-dev.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/dist/EmilsoftwareModule.d.ts +11 -3
  2. package/dist/EmilsoftwareModule.js +1 -1
  3. package/dist/EmilsoftwareModule.js.map +1 -1
  4. package/dist/accessi/client/index.d.ts +26 -0
  5. package/dist/accessi/client/index.js +49 -0
  6. package/dist/accessi/client/index.js.map +1 -0
  7. package/dist/accessi/console/index.d.ts +15 -0
  8. package/dist/accessi/console/index.js +25 -0
  9. package/dist/accessi/console/index.js.map +1 -0
  10. package/dist/accessi/contracts/dtos.d.ts +8 -0
  11. package/dist/accessi/contracts/dtos.js +25 -0
  12. package/dist/accessi/contracts/dtos.js.map +1 -0
  13. package/dist/accessi/contracts/index.d.ts +25 -0
  14. package/dist/accessi/contracts/index.js +28 -0
  15. package/dist/accessi/contracts/index.js.map +1 -0
  16. package/dist/accessi/openapi/index.d.ts +2 -0
  17. package/dist/accessi/openapi/index.js +19 -0
  18. package/dist/accessi/openapi/index.js.map +1 -0
  19. package/dist/accessi/resource-server/core.d.ts +6 -0
  20. package/dist/accessi/resource-server/core.js +23 -0
  21. package/dist/accessi/resource-server/core.js.map +1 -0
  22. package/dist/accessi/resource-server/errors.d.ts +10 -0
  23. package/dist/accessi/resource-server/errors.js +19 -0
  24. package/dist/accessi/resource-server/errors.js.map +1 -0
  25. package/dist/accessi/resource-server/express.d.ts +17 -0
  26. package/dist/accessi/resource-server/express.js +66 -0
  27. package/dist/accessi/resource-server/express.js.map +1 -0
  28. package/dist/accessi/resource-server/index.d.ts +3 -0
  29. package/dist/accessi/resource-server/index.js +20 -0
  30. package/dist/accessi/resource-server/index.js.map +1 -0
  31. package/dist/accessi/resource-server/introspection.d.ts +16 -0
  32. package/dist/accessi/resource-server/introspection.js +115 -0
  33. package/dist/accessi/resource-server/introspection.js.map +1 -0
  34. package/dist/accessi/resource-server/keys.d.ts +11 -0
  35. package/dist/accessi/resource-server/keys.js +22 -0
  36. package/dist/accessi/resource-server/keys.js.map +1 -0
  37. package/dist/accessi/resource-server/nest.d.ts +9 -0
  38. package/dist/accessi/resource-server/nest.js +62 -0
  39. package/dist/accessi/resource-server/nest.js.map +1 -0
  40. package/dist/accessi/resource-server/runtime.d.ts +27 -0
  41. package/dist/accessi/resource-server/runtime.js +128 -0
  42. package/dist/accessi/resource-server/runtime.js.map +1 -0
  43. package/dist/accessi/resource-server/token.d.ts +23 -0
  44. package/dist/accessi/resource-server/token.js +230 -0
  45. package/dist/accessi/resource-server/token.js.map +1 -0
  46. package/dist/accessi/resource-server/types.d.ts +78 -0
  47. package/dist/accessi/resource-server/types.js +3 -0
  48. package/dist/accessi/resource-server/types.js.map +1 -0
  49. package/dist/accessi/server/index.d.ts +10 -0
  50. package/dist/accessi/server/index.js +29 -0
  51. package/dist/accessi/server/index.js.map +1 -0
  52. package/dist/accessi/ui/index.d.ts +13 -0
  53. package/dist/accessi/ui/index.js +19 -0
  54. package/dist/accessi/ui/index.js.map +1 -0
  55. package/dist/accessi-module/AccessiModule.d.ts +25 -1
  56. package/dist/accessi-module/AccessiModule.js +2 -0
  57. package/dist/accessi-module/AccessiModule.js.map +1 -1
  58. package/dist/accessi-module/Console/accessiFetch.d.ts +4 -0
  59. package/dist/accessi-module/Console/accessiFetch.js +18 -4
  60. package/dist/accessi-module/Console/accessiFetch.js.map +1 -1
  61. package/dist/accessi-module/Console/console.js +114 -9
  62. package/dist/accessi-module/Console/console.js.map +1 -1
  63. package/dist/accessi-module/Console/generated/{accessiApi.d.ts → emilsoftware-utilities.d.ts} +11 -1
  64. package/dist/accessi-module/Console/generated/{accessiApi.js → emilsoftware-utilities.js} +10 -2
  65. package/dist/accessi-module/Console/generated/emilsoftware-utilities.js.map +1 -0
  66. package/dist/accessi-module/Console/generated/model/accessiIntrospectionResultDto.d.ts +16 -0
  67. package/dist/accessi-module/Console/generated/model/accessiIntrospectionResultDto.js +3 -0
  68. package/dist/accessi-module/Console/generated/model/accessiIntrospectionResultDto.js.map +1 -0
  69. package/dist/accessi-module/Console/generated/model/index.d.ts +3 -0
  70. package/dist/accessi-module/Console/generated/model/index.js +3 -0
  71. package/dist/accessi-module/Console/generated/model/index.js.map +1 -1
  72. package/dist/accessi-module/Console/generated/model/introspectAccessiTokenRequest.d.ts +10 -0
  73. package/dist/accessi-module/Console/generated/model/introspectAccessiTokenRequest.js +9 -0
  74. package/dist/accessi-module/Console/generated/model/introspectAccessiTokenRequest.js.map +1 -0
  75. package/dist/accessi-module/Console/generated/model/introspectAccessiTokenResponse.d.ts +14 -0
  76. package/dist/accessi-module/Console/generated/model/introspectAccessiTokenResponse.js +3 -0
  77. package/dist/accessi-module/Console/generated/model/introspectAccessiTokenResponse.js.map +1 -0
  78. package/dist/accessi-module/Console/wiki.js +98 -3
  79. package/dist/accessi-module/Console/wiki.js.map +1 -1
  80. package/dist/accessi-module/Controllers/AccessiIntrospectionController.d.ts +17 -0
  81. package/dist/accessi-module/Controllers/AccessiIntrospectionController.js +93 -0
  82. package/dist/accessi-module/Controllers/AccessiIntrospectionController.js.map +1 -0
  83. package/dist/accessi-module/Dtos/IntrospectionDtos.d.ts +16 -0
  84. package/dist/accessi-module/Dtos/IntrospectionDtos.js +59 -0
  85. package/dist/accessi-module/Dtos/IntrospectionDtos.js.map +1 -0
  86. package/dist/accessi-module/Dtos/index.d.ts +1 -0
  87. package/dist/accessi-module/Dtos/index.js +1 -0
  88. package/dist/accessi-module/Dtos/index.js.map +1 -1
  89. package/dist/accessi-module/Services/AuthService/AuthService.d.ts +13 -0
  90. package/dist/accessi-module/Services/AuthService/AuthService.js +42 -10
  91. package/dist/accessi-module/Services/AuthService/AuthService.js.map +1 -1
  92. package/dist/accessi-module/database-updates/accessiOptionsFromEnv.js +11 -4
  93. package/dist/accessi-module/database-updates/accessiOptionsFromEnv.js.map +1 -1
  94. package/dist/accessi-module/database-updates/menuScopeSeed.d.ts +2 -2
  95. package/dist/accessi-module/database-updates/menuScopeSeed.js +101 -53
  96. package/dist/accessi-module/database-updates/menuScopeSeed.js.map +1 -1
  97. package/dist/accessi-module/database-updates/menuScopeSeeder.js +1 -1
  98. package/dist/accessi-module/database-updates/menuScopeSeeder.js.map +1 -1
  99. package/dist/accessi-module/index.d.ts +16 -3
  100. package/dist/accessi-module/index.js +15 -19
  101. package/dist/accessi-module/index.js.map +1 -1
  102. package/dist/accessi-module/jwt/jwt.strategy.js +5 -5
  103. package/dist/accessi-module/jwt/jwt.strategy.js.map +1 -1
  104. package/dist/accessi-module/middleware/authenticateGen.js +5 -5
  105. package/dist/accessi-module/middleware/authenticateGen.js.map +1 -1
  106. package/dist/accessi-module/security/jwtTokenOptions.d.ts +15 -0
  107. package/dist/accessi-module/security/jwtTokenOptions.js +44 -0
  108. package/dist/accessi-module/security/jwtTokenOptions.js.map +1 -0
  109. package/dist/accessi-module/startupLog.d.ts +3 -2
  110. package/dist/accessi-module/startupLog.js +8 -4
  111. package/dist/accessi-module/startupLog.js.map +1 -1
  112. package/dist/allegati-module/index.js +2 -2
  113. package/dist/allegati-module/index.js.map +1 -1
  114. package/dist/angular-components/aree-disponibili/demo.html +4 -4
  115. package/dist/emilsoftware-utilities-2.0.0-dev.67.tgz +0 -0
  116. package/dist/index.d.ts +2 -1
  117. package/dist/index.js +2 -1
  118. package/dist/index.js.map +1 -1
  119. package/dist/initEmilsoftwareModule.d.ts +11 -1
  120. package/dist/initEmilsoftwareModule.js +11 -23
  121. package/dist/initEmilsoftwareModule.js.map +1 -1
  122. package/dist/openapi/OpenApiConfig.d.ts +68 -0
  123. package/dist/openapi/OpenApiConfig.js +157 -0
  124. package/dist/openapi/OpenApiConfig.js.map +1 -0
  125. package/dist/openapi/OpenApiMerge.d.ts +15 -0
  126. package/dist/openapi/OpenApiMerge.js +119 -0
  127. package/dist/openapi/OpenApiMerge.js.map +1 -0
  128. package/integration-prompt.md +481 -0
  129. package/package.json +88 -5
  130. package/dist/accessi-module/Console/generated/accessiApi.js.map +0 -1
  131. package/dist/emilsoftware-utilities-2.0.0-dev.66.tgz +0 -0
  132. package/dist/swagger/SwaggerConfig.d.ts +0 -8
  133. package/dist/swagger/SwaggerConfig.js +0 -41
  134. package/dist/swagger/SwaggerConfig.js.map +0 -1
@@ -16,7 +16,7 @@ exports.initEmilsoftwareModule = initEmilsoftwareModule;
16
16
  const express_1 = __importDefault(require("express"));
17
17
  const core_1 = require("@nestjs/core");
18
18
  const platform_express_1 = require("@nestjs/platform-express");
19
- const SwaggerConfig_1 = require("./swagger/SwaggerConfig");
19
+ const OpenApiConfig_1 = require("./openapi/OpenApiConfig");
20
20
  const Logger_1 = require("./Logger");
21
21
  const EmilsoftwareModule_1 = require("./EmilsoftwareModule");
22
22
  const authenticateGen_1 = require("./accessi-module/middleware/authenticateGen");
@@ -28,10 +28,11 @@ const AccessiHttpExceptionFilter_1 = require("./accessi-module/security/AccessiH
28
28
  * Initializes the unified module that combines Accessi and Allegati functionality
29
29
  * @param app Express application instance
30
30
  * @param options Configuration options for both modules
31
+ * @returns Il sotto-app Nest e il documento OpenAPI generato.
31
32
  */
32
33
  function initEmilsoftwareModule(app, options) {
33
34
  return __awaiter(this, void 0, void 0, function* () {
34
- var _a, _b, _c, _d, _e, _f;
35
+ var _a;
35
36
  const logger = new Logger_1.Logger(initEmilsoftwareModule.name);
36
37
  if (options === null || options === void 0 ? void 0 : options.accessiOptions) {
37
38
  (0, authenticateGen_1.beginAccessiAuthInitialization)();
@@ -42,19 +43,12 @@ function initEmilsoftwareModule(app, options) {
42
43
  // Prefisso OpenAPI configurabile (default `accessi/openapi`), cosi' piu'
43
44
  // documentazioni possono convivere (backend su `/openapi`, moduli sotto
44
45
  // `/<modulo>/openapi`).
45
- const swaggerPath = ((_b = (_a = options === null || options === void 0 ? void 0 : options.swagger) === null || _a === void 0 ? void 0 : _a.swaggerPath) !== null && _b !== void 0 ? _b : "accessi/openapi").replace(/^\/+|\/+$/g, "");
46
- const swaggerJsonPath = ((_d = (_c = options === null || options === void 0 ? void 0 : options.swagger) === null || _c === void 0 ? void 0 : _c.swaggerJsonPath) !== null && _d !== void 0 ? _d : `${swaggerPath}.json`).replace(/^\/+/, "");
47
- const legacySwaggerJsonPath = `${swaggerPath}-json`;
48
- const swaggerRoutePaths = [
49
- `/${swaggerPath}`,
50
- `/${swaggerPath}/`,
51
- `/${swaggerJsonPath}`,
52
- `/${legacySwaggerJsonPath}`,
53
- ];
46
+ // Precedenza: EmilsoftwareOptions.openapi > AccessiOptions.openapi > default.
47
+ const resolvedOpenApi = (0, OpenApiConfig_1.resolveAccessiOpenApiOptions)((_a = options === null || options === void 0 ? void 0 : options.accessiOptions) === null || _a === void 0 ? void 0 : _a.openapi, options === null || options === void 0 ? void 0 : options.openapi);
54
48
  const nestHostApp = (0, express_1.default)();
55
49
  const isNestRoute = (path) => path === "/api" ||
56
50
  path.startsWith("/api/") ||
57
- swaggerRoutePaths.some((routePath) => path === routePath || path.startsWith(`${routePath.replace(/\/$/, "")}/`));
51
+ (0, OpenApiConfig_1.isOpenApiRequestPath)(path, resolvedOpenApi);
58
52
  app.use((req, res, next) => {
59
53
  if (!isNestRoute(req.path)) {
60
54
  return next();
@@ -62,9 +56,7 @@ function initEmilsoftwareModule(app, options) {
62
56
  return nestHostApp(req, res, next);
63
57
  });
64
58
  const nestExpressInstance = new platform_express_1.ExpressAdapter(nestHostApp);
65
- const nestApp = yield core_1.NestFactory.create(EmilsoftwareModule_1.EmilsoftwareModule.forRoot(options), nestExpressInstance, {
66
- bufferLogs: true
67
- });
59
+ const nestApp = yield core_1.NestFactory.create(EmilsoftwareModule_1.EmilsoftwareModule.forRoot(options), nestExpressInstance, Object.assign({ bufferLogs: (options === null || options === void 0 ? void 0 : options.bufferLogs) !== false }, ((options === null || options === void 0 ? void 0 : options.logger) ? { logger: options.logger } : {})));
68
60
  nestApp.enableCors();
69
61
  if (options === null || options === void 0 ? void 0 : options.accessiOptions) {
70
62
  // Keep the Accessi API contract identical for standalone and unified bootstraps.
@@ -72,23 +64,19 @@ function initEmilsoftwareModule(app, options) {
72
64
  nestApp.useGlobalFilters(new AccessiHttpExceptionFilter_1.AccessiHttpExceptionFilter());
73
65
  }
74
66
  nestApp.setGlobalPrefix('api', {
75
- exclude: [`/${swaggerPath}`, `/${swaggerPath}/(.*)`]
67
+ exclude: (0, OpenApiConfig_1.buildOpenApiExcludePaths)(resolvedOpenApi)
76
68
  });
77
69
  // Documentazione OpenAPI del modulo unificato (Accessi + Allegati).
78
- (0, SwaggerConfig_1.setupSwagger)(nestApp, {
79
- swaggerPath,
80
- swaggerJsonPath,
81
- title: (_e = options === null || options === void 0 ? void 0 : options.swagger) === null || _e === void 0 ? void 0 : _e.title,
82
- description: (_f = options === null || options === void 0 ? void 0 : options.swagger) === null || _f === void 0 ? void 0 : _f.description,
83
- });
70
+ const document = (0, OpenApiConfig_1.setupOpenApi)(nestApp, resolvedOpenApi);
84
71
  yield nestApp.init();
85
72
  if (options === null || options === void 0 ? void 0 : options.accessiOptions) {
86
73
  const accessiServices = (0, accessiRuntime_1.registerAccessiModuleServices)(app, nestApp);
87
74
  app.locals.accessiAuthService = accessiServices.authenticateGenService;
88
75
  (0, authenticateGen_1.setAccessiAuthService)(accessiServices.authenticateGenService);
89
- (0, startupLog_1.logAccessiStartup)(options.accessiOptions);
76
+ (0, startupLog_1.logAccessiStartup)(options.accessiOptions, resolvedOpenApi);
90
77
  }
91
78
  logger.info("Emilsoftware module initialized successfully");
79
+ return { nestApp, document };
92
80
  }
93
81
  catch (error) {
94
82
  if (options === null || options === void 0 ? void 0 : options.accessiOptions) {
@@ -1 +1 @@
1
- {"version":3,"file":"initEmilsoftwareModule.js","sourceRoot":"","sources":["../src/initEmilsoftwareModule.ts"],"names":[],"mappings":"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","sourcesContent":["import express, { Application, Request } from \"express\";\nimport { NestFactory } from \"@nestjs/core\";\nimport { ExpressAdapter } from \"@nestjs/platform-express\";\nimport { setupSwagger } from \"./swagger/SwaggerConfig\";\nimport { Logger } from \"./Logger\";\nimport { EmilsoftwareModule, EmilsoftwareOptions } from \"./EmilsoftwareModule\";\nimport {\n beginAccessiAuthInitialization,\n failAccessiAuthInitialization,\n setAccessiAuthService\n} from \"./accessi-module/middleware/authenticateGen\";\nimport { registerAccessiModuleServices } from \"./accessi-module/accessiRuntime\";\nimport { logAccessiStartup } from \"./accessi-module/startupLog\";\nimport { createAccessiValidationPipe } from './accessi-module/security/accessiValidation';\nimport { AccessiHttpExceptionFilter } from './accessi-module/security/AccessiHttpExceptionFilter';\n\n/**\n * Initializes the unified module that combines Accessi and Allegati functionality\n * @param app Express application instance\n * @param options Configuration options for both modules\n */\nexport async function initEmilsoftwareModule(app: Application, options: EmilsoftwareOptions) {\n const logger: Logger = new Logger(initEmilsoftwareModule.name);\n if (options?.accessiOptions) {\n beginAccessiAuthInitialization();\n }\n\n try {\n // Isolate Nest on a dedicated Express instance and dispatch only module routes.\n // This keeps pre-existing routes/middlewares on the host app untouched.\n // Prefisso OpenAPI configurabile (default `accessi/openapi`), cosi' piu'\n // documentazioni possono convivere (backend su `/openapi`, moduli sotto\n // `/<modulo>/openapi`).\n const swaggerPath = (options?.swagger?.swaggerPath ?? \"accessi/openapi\").replace(/^\\/+|\\/+$/g, \"\");\n const swaggerJsonPath = (options?.swagger?.swaggerJsonPath ?? `${swaggerPath}.json`).replace(/^\\/+/, \"\");\n const legacySwaggerJsonPath = `${swaggerPath}-json`;\n const swaggerRoutePaths = [\n `/${swaggerPath}`,\n `/${swaggerPath}/`,\n `/${swaggerJsonPath}`,\n `/${legacySwaggerJsonPath}`,\n ];\n\n const nestHostApp = express();\n const isNestRoute = (path: string) =>\n path === \"/api\" ||\n path.startsWith(\"/api/\") ||\n swaggerRoutePaths.some(\n (routePath) => path === routePath || path.startsWith(`${routePath.replace(/\\/$/, \"\")}/`)\n );\n\n app.use((req: Request, res, next) => {\n if (!isNestRoute(req.path)) {\n return next();\n }\n return nestHostApp(req, res, next);\n });\n\n const nestExpressInstance = new ExpressAdapter(nestHostApp);\n const nestApp = await NestFactory.create(EmilsoftwareModule.forRoot(options), nestExpressInstance, {\n bufferLogs: true\n });\n\n nestApp.enableCors();\n if (options?.accessiOptions) {\n // Keep the Accessi API contract identical for standalone and unified bootstraps.\n nestApp.useGlobalPipes(createAccessiValidationPipe());\n nestApp.useGlobalFilters(new AccessiHttpExceptionFilter());\n }\n\n nestApp.setGlobalPrefix('api', {\n exclude: [`/${swaggerPath}`, `/${swaggerPath}/(.*)`]\n });\n\n // Documentazione OpenAPI del modulo unificato (Accessi + Allegati).\n setupSwagger(nestApp, {\n swaggerPath,\n swaggerJsonPath,\n title: options?.swagger?.title,\n description: options?.swagger?.description,\n });\n\n await nestApp.init();\n if (options?.accessiOptions) {\n const accessiServices = registerAccessiModuleServices(app, nestApp);\n app.locals.accessiAuthService = accessiServices.authenticateGenService;\n setAccessiAuthService(accessiServices.authenticateGenService);\n logAccessiStartup(options.accessiOptions);\n }\n\n logger.info(\"Emilsoftware module initialized successfully\");\n\n } catch (error) {\n if (options?.accessiOptions) {\n failAccessiAuthInitialization(error);\n }\n logger.error(\"Error initializing Emilsoftware Module:\", error);\n throw error;\n }\n}\n"]}
1
+ {"version":3,"file":"initEmilsoftwareModule.js","sourceRoot":"","sources":["../src/initEmilsoftwareModule.ts"],"names":[],"mappings":"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","sourcesContent":["import express, { Application, Request } from \"express\";\nimport { NestFactory } from \"@nestjs/core\";\nimport { ExpressAdapter } from \"@nestjs/platform-express\";\nimport type { INestApplication } from \"@nestjs/common\";\nimport type { OpenAPIObject } from \"@nestjs/swagger\";\nimport {\n buildOpenApiExcludePaths,\n isOpenApiRequestPath,\n resolveAccessiOpenApiOptions,\n setupOpenApi\n} from \"./openapi/OpenApiConfig\";\nimport { Logger } from \"./Logger\";\nimport { EmilsoftwareModule, EmilsoftwareOptions } from \"./EmilsoftwareModule\";\nimport {\n beginAccessiAuthInitialization,\n failAccessiAuthInitialization,\n setAccessiAuthService\n} from \"./accessi-module/middleware/authenticateGen\";\nimport { registerAccessiModuleServices } from \"./accessi-module/accessiRuntime\";\nimport { logAccessiStartup } from \"./accessi-module/startupLog\";\nimport { createAccessiValidationPipe } from './accessi-module/security/accessiValidation';\nimport { AccessiHttpExceptionFilter } from './accessi-module/security/AccessiHttpExceptionFilter';\n\n/** Esito del bootstrap del modulo unificato. */\nexport interface EmilsoftwareModuleInitializationResult {\n /** Sotto-app Nest che serve Accessi, Allegati e la documentazione. */\n nestApp: INestApplication;\n /** Documento OpenAPI generato, mergiabile nel documento unico dell'host. */\n document: OpenAPIObject;\n}\n\n/**\n * Initializes the unified module that combines Accessi and Allegati functionality\n * @param app Express application instance\n * @param options Configuration options for both modules\n * @returns Il sotto-app Nest e il documento OpenAPI generato.\n */\nexport async function initEmilsoftwareModule(\n app: Application,\n options: EmilsoftwareOptions\n): Promise<EmilsoftwareModuleInitializationResult> {\n const logger: Logger = new Logger(initEmilsoftwareModule.name);\n if (options?.accessiOptions) {\n beginAccessiAuthInitialization();\n }\n\n try {\n // Isolate Nest on a dedicated Express instance and dispatch only module routes.\n // This keeps pre-existing routes/middlewares on the host app untouched.\n // Prefisso OpenAPI configurabile (default `accessi/openapi`), cosi' piu'\n // documentazioni possono convivere (backend su `/openapi`, moduli sotto\n // `/<modulo>/openapi`).\n // Precedenza: EmilsoftwareOptions.openapi > AccessiOptions.openapi > default.\n const resolvedOpenApi = resolveAccessiOpenApiOptions(\n options?.accessiOptions?.openapi,\n options?.openapi\n );\n\n const nestHostApp = express();\n const isNestRoute = (path: string) =>\n path === \"/api\" ||\n path.startsWith(\"/api/\") ||\n isOpenApiRequestPath(path, resolvedOpenApi);\n\n app.use((req: Request, res, next) => {\n if (!isNestRoute(req.path)) {\n return next();\n }\n return nestHostApp(req, res, next);\n });\n\n const nestExpressInstance = new ExpressAdapter(nestHostApp);\n const nestApp = await NestFactory.create(EmilsoftwareModule.forRoot(options), nestExpressInstance, {\n bufferLogs: options?.bufferLogs !== false,\n ...(options?.logger ? { logger: options.logger } : {})\n });\n\n nestApp.enableCors();\n if (options?.accessiOptions) {\n // Keep the Accessi API contract identical for standalone and unified bootstraps.\n nestApp.useGlobalPipes(createAccessiValidationPipe());\n nestApp.useGlobalFilters(new AccessiHttpExceptionFilter());\n }\n\n nestApp.setGlobalPrefix('api', {\n exclude: buildOpenApiExcludePaths(resolvedOpenApi)\n });\n\n // Documentazione OpenAPI del modulo unificato (Accessi + Allegati).\n const document = setupOpenApi(nestApp, resolvedOpenApi);\n\n await nestApp.init();\n if (options?.accessiOptions) {\n const accessiServices = registerAccessiModuleServices(app, nestApp);\n app.locals.accessiAuthService = accessiServices.authenticateGenService;\n setAccessiAuthService(accessiServices.authenticateGenService);\n logAccessiStartup(options.accessiOptions, resolvedOpenApi);\n }\n\n logger.info(\"Emilsoftware module initialized successfully\");\n\n return { nestApp, document };\n } catch (error) {\n if (options?.accessiOptions) {\n failAccessiAuthInitialization(error);\n }\n logger.error(\"Error initializing Emilsoftware Module:\", error);\n throw error;\n }\n}\n"]}
@@ -0,0 +1,68 @@
1
+ import { INestApplication } from "@nestjs/common";
2
+ import type { OpenAPIObject } from "@nestjs/swagger";
3
+ /**
4
+ * Opzioni di setup della documentazione OpenAPI.
5
+ *
6
+ * `enabled: false` disabilita UI e JSON ma non il documento generato, che resta
7
+ * disponibile al chiamante per essere mergiato nel documento OpenAPI dell'host.
8
+ */
9
+ export type OpenApiSetupOptions = {
10
+ enabled?: boolean;
11
+ openApiPath?: string;
12
+ openApiJsonPath?: string;
13
+ title?: string;
14
+ description?: string;
15
+ /** Callback invocata subito dopo la generazione del documento OpenAPI. */
16
+ onDocument?: (document: OpenAPIObject) => void;
17
+ };
18
+ /** Opzioni OpenAPI risolte: nessun campo opzionale salvo il callback. */
19
+ export interface ResolvedOpenApiSetupOptions {
20
+ enabled: boolean;
21
+ openApiPath: string;
22
+ openApiJsonPath: string;
23
+ title: string;
24
+ description: string;
25
+ onDocument?: (document: OpenAPIObject) => void;
26
+ }
27
+ /**
28
+ * Default storici della documentazione Accessi.
29
+ *
30
+ * I consumer esistenti non devono cambiare comportamento: senza override la UI
31
+ * resta su `accessi/openapi` e la spec su `accessi/openapi.json`.
32
+ */
33
+ export declare const DEFAULT_ACCESSI_OPENAPI: {
34
+ readonly enabled: true;
35
+ readonly openApiPath: "accessi/openapi";
36
+ readonly openApiJsonPath: "accessi/openapi.json";
37
+ readonly title: "Accessi API Documentation";
38
+ readonly description: "API del modulo accessi";
39
+ };
40
+ /**
41
+ * Risolve i default e i vari livelli di override in un'unica configurazione.
42
+ *
43
+ * L'ordine dei livelli e esplicito: i parametri successivi vincono sui precedenti.
44
+ */
45
+ export declare function resolveOpenApiSetupOptions(defaults?: OpenApiSetupOptions, ...overrides: Array<OpenApiSetupOptions | undefined>): ResolvedOpenApiSetupOptions;
46
+ /**
47
+ * Risolve la configurazione OpenAPI di Accessi.
48
+ *
49
+ * I livelli successivi vincono sui precedenti, quindi la variante unificata puo
50
+ * usare `resolveAccessiOpenApiOptions(accessiOptions.openapi, emilsoftwareOptions.openapi)`.
51
+ */
52
+ export declare function resolveAccessiOpenApiOptions(...overrides: Array<OpenApiSetupOptions | undefined>): ResolvedOpenApiSetupOptions;
53
+ /**
54
+ * Path da passare a `setGlobalPrefix` per escludere la documentazione.
55
+ *
56
+ * Usa la sintassi wildcard di path-to-regexp v8 (`{*path}`) per evitare il
57
+ * LegacyRouteConverter warning di Nest.
58
+ */
59
+ export declare function buildOpenApiExcludePaths(options: ResolvedOpenApiSetupOptions): string[];
60
+ /** True se il path Express appartiene alla documentazione OpenAPI configurata. */
61
+ export declare function isOpenApiRequestPath(path: string, options: ResolvedOpenApiSetupOptions): boolean;
62
+ /**
63
+ * Genera il documento OpenAPI e, se `enabled` non e false, monta UI e JSON.
64
+ *
65
+ * Il documento viene sempre restituito: questo permette all'host di mergiarlo
66
+ * nel proprio documento unico anche quando la UI del modulo e disabilitata.
67
+ */
68
+ export declare function setupOpenApi(app: INestApplication, options?: OpenApiSetupOptions): OpenAPIObject;
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_ACCESSI_OPENAPI = void 0;
4
+ exports.resolveOpenApiSetupOptions = resolveOpenApiSetupOptions;
5
+ exports.resolveAccessiOpenApiOptions = resolveAccessiOpenApiOptions;
6
+ exports.buildOpenApiExcludePaths = buildOpenApiExcludePaths;
7
+ exports.isOpenApiRequestPath = isOpenApiRequestPath;
8
+ exports.setupOpenApi = setupOpenApi;
9
+ const swagger_1 = require("@nestjs/swagger");
10
+ const Logger_1 = require("../Logger");
11
+ /**
12
+ * Default storici della documentazione Accessi.
13
+ *
14
+ * I consumer esistenti non devono cambiare comportamento: senza override la UI
15
+ * resta su `accessi/openapi` e la spec su `accessi/openapi.json`.
16
+ */
17
+ exports.DEFAULT_ACCESSI_OPENAPI = {
18
+ enabled: true,
19
+ openApiPath: "accessi/openapi",
20
+ openApiJsonPath: "accessi/openapi.json",
21
+ title: "Accessi API Documentation",
22
+ description: "API del modulo accessi",
23
+ };
24
+ /** Default del setup OpenAPI generico (usato direttamente da `setupOpenApi`). */
25
+ const DEFAULT_OPENAPI_SETUP = {
26
+ enabled: true,
27
+ openApiPath: "openapi",
28
+ openApiJsonPath: "openapi.json",
29
+ title: "API Documentation",
30
+ description: "API per la gestione di accessi utenti e allegati",
31
+ };
32
+ function normalizeOpenApiPath(value) {
33
+ return value.replace(/^\/+|\/+$/g, "");
34
+ }
35
+ function normalizeOpenApiJsonPath(value) {
36
+ return value.replace(/^\/+|\/+$/g, "");
37
+ }
38
+ /**
39
+ * Unisce i livelli di configurazione ignorando le proprieta `undefined`.
40
+ *
41
+ * A differenza dello spread semplice, un `{ openApiPath: undefined }` esplicito
42
+ * non cancella il default del livello precedente.
43
+ */
44
+ function mergeOpenApiSetupOptions(defaults, ...overrides) {
45
+ const merged = Object.assign({}, defaults);
46
+ for (const override of overrides) {
47
+ if (!override)
48
+ continue;
49
+ if (override.enabled !== undefined)
50
+ merged.enabled = override.enabled;
51
+ if (override.openApiPath !== undefined)
52
+ merged.openApiPath = override.openApiPath;
53
+ if (override.openApiJsonPath !== undefined)
54
+ merged.openApiJsonPath = override.openApiJsonPath;
55
+ if (override.title !== undefined)
56
+ merged.title = override.title;
57
+ if (override.description !== undefined)
58
+ merged.description = override.description;
59
+ if (override.onDocument !== undefined)
60
+ merged.onDocument = override.onDocument;
61
+ }
62
+ return merged;
63
+ }
64
+ /**
65
+ * Risolve i default e i vari livelli di override in un'unica configurazione.
66
+ *
67
+ * L'ordine dei livelli e esplicito: i parametri successivi vincono sui precedenti.
68
+ */
69
+ function resolveOpenApiSetupOptions(defaults = DEFAULT_OPENAPI_SETUP, ...overrides) {
70
+ var _a, _b, _c, _d, _e, _f;
71
+ const merged = mergeOpenApiSetupOptions(defaults, ...overrides);
72
+ const defaultOpenApiPath = normalizeOpenApiPath((_a = defaults.openApiPath) !== null && _a !== void 0 ? _a : "openapi") || "openapi";
73
+ const openApiPath = normalizeOpenApiPath((_b = merged.openApiPath) !== null && _b !== void 0 ? _b : defaultOpenApiPath) || defaultOpenApiPath;
74
+ // Storico: se si cambia solo `openApiPath`, il JSON segue il nuovo path
75
+ // (`<path>.json`). Un `openApiJsonPath` esplicito in un override vince sempre.
76
+ const explicitJsonPath = (_c = [...overrides]
77
+ .reverse()
78
+ .find((override) => (override === null || override === void 0 ? void 0 : override.openApiJsonPath) !== undefined)) === null || _c === void 0 ? void 0 : _c.openApiJsonPath;
79
+ const hasOverrides = overrides.some((override) => override !== undefined);
80
+ const jsonPathCandidate = explicitJsonPath !== null && explicitJsonPath !== void 0 ? explicitJsonPath : (hasOverrides ? `${openApiPath}.json` : ((_d = merged.openApiJsonPath) !== null && _d !== void 0 ? _d : `${openApiPath}.json`));
81
+ const openApiJsonPath = normalizeOpenApiJsonPath(jsonPathCandidate) || `${openApiPath}.json`;
82
+ return Object.assign({ enabled: merged.enabled !== false, openApiPath,
83
+ openApiJsonPath, title: (_e = merged.title) !== null && _e !== void 0 ? _e : "API Documentation", description: (_f = merged.description) !== null && _f !== void 0 ? _f : "API per la gestione di accessi utenti e allegati" }, (merged.onDocument ? { onDocument: merged.onDocument } : {}));
84
+ }
85
+ /**
86
+ * Risolve la configurazione OpenAPI di Accessi.
87
+ *
88
+ * I livelli successivi vincono sui precedenti, quindi la variante unificata puo
89
+ * usare `resolveAccessiOpenApiOptions(accessiOptions.openapi, emilsoftwareOptions.openapi)`.
90
+ */
91
+ function resolveAccessiOpenApiOptions(...overrides) {
92
+ return resolveOpenApiSetupOptions(exports.DEFAULT_ACCESSI_OPENAPI, ...overrides);
93
+ }
94
+ /**
95
+ * Path da passare a `setGlobalPrefix` per escludere la documentazione.
96
+ *
97
+ * Usa la sintassi wildcard di path-to-regexp v8 (`{*path}`) per evitare il
98
+ * LegacyRouteConverter warning di Nest.
99
+ */
100
+ function buildOpenApiExcludePaths(options) {
101
+ if (!options.enabled)
102
+ return [];
103
+ return [`/${options.openApiPath}`, `/${options.openApiPath}/{*path}`];
104
+ }
105
+ /** True se il path Express appartiene alla documentazione OpenAPI configurata. */
106
+ function isOpenApiRequestPath(path, options) {
107
+ if (!options.enabled)
108
+ return false;
109
+ const uiPath = `/${options.openApiPath}`;
110
+ if (path === uiPath || path.startsWith(`${uiPath}/`))
111
+ return true;
112
+ return path === `/${options.openApiJsonPath}` || path === `/${options.openApiPath}-json`;
113
+ }
114
+ /**
115
+ * Genera il documento OpenAPI e, se `enabled` non e false, monta UI e JSON.
116
+ *
117
+ * Il documento viene sempre restituito: questo permette all'host di mergiarlo
118
+ * nel proprio documento unico anche quando la UI del modulo e disabilitata.
119
+ */
120
+ function setupOpenApi(app, options) {
121
+ var _a, _b, _c;
122
+ const logger = new Logger_1.Logger("OpenApiConfig");
123
+ const resolved = resolveOpenApiSetupOptions(DEFAULT_OPENAPI_SETUP, options);
124
+ const config = new swagger_1.DocumentBuilder()
125
+ .setTitle(resolved.title)
126
+ .setDescription(resolved.description)
127
+ .setVersion("1.0")
128
+ .addBearerAuth() // Per abilitare l'autenticazione JWT
129
+ .build();
130
+ const document = swagger_1.SwaggerModule.createDocument(app, config);
131
+ (_a = resolved.onDocument) === null || _a === void 0 ? void 0 : _a.call(resolved, document);
132
+ if (!resolved.enabled) {
133
+ logger.info("OpenAPI disabilitato: UI e JSON non montati.");
134
+ return document;
135
+ }
136
+ swagger_1.SwaggerModule.setup(resolved.openApiPath, app, document);
137
+ app.use(`/${resolved.openApiJsonPath}`, (_req, res) => {
138
+ res.setHeader("Content-Type", "application/json");
139
+ res.send(document);
140
+ });
141
+ // Manteniamo l'alias storico `<path>-json` per non rompere i consumer esistenti.
142
+ const legacyOpenApiJsonPath = `${resolved.openApiPath}-json`;
143
+ if (legacyOpenApiJsonPath !== resolved.openApiJsonPath) {
144
+ app.use(`/${legacyOpenApiJsonPath}`, (_req, res) => {
145
+ res.setHeader("Content-Type", "application/json");
146
+ res.send(document);
147
+ });
148
+ }
149
+ const serverAddress = (_c = (_b = app.getHttpServer()) === null || _b === void 0 ? void 0 : _b.address) === null || _c === void 0 ? void 0 : _c.call(_b);
150
+ const port = serverAddress && typeof serverAddress === "object"
151
+ ? serverAddress.port
152
+ : 3000;
153
+ logger.info(`OpenAPI documentation available at: http://localhost:${port}/${resolved.openApiPath}`);
154
+ logger.info(`OpenAPI JSON available at: http://localhost:${port}/${resolved.openApiJsonPath}`);
155
+ return document;
156
+ }
157
+ //# sourceMappingURL=OpenApiConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpenApiConfig.js","sourceRoot":"","sources":["../../src/openapi/OpenApiConfig.ts"],"names":[],"mappings":"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","sourcesContent":["import { INestApplication } from \"@nestjs/common\";\nimport { DocumentBuilder, SwaggerModule } from \"@nestjs/swagger\";\nimport type { OpenAPIObject } from \"@nestjs/swagger\";\nimport { Request, Response } from \"express\";\nimport { AddressInfo } from \"net\";\nimport { Logger } from \"../Logger\";\n\n/**\n * Opzioni di setup della documentazione OpenAPI.\n *\n * `enabled: false` disabilita UI e JSON ma non il documento generato, che resta\n * disponibile al chiamante per essere mergiato nel documento OpenAPI dell'host.\n */\nexport type OpenApiSetupOptions = {\n enabled?: boolean;\n openApiPath?: string;\n openApiJsonPath?: string;\n title?: string;\n description?: string;\n /** Callback invocata subito dopo la generazione del documento OpenAPI. */\n onDocument?: (document: OpenAPIObject) => void;\n};\n\n/** Opzioni OpenAPI risolte: nessun campo opzionale salvo il callback. */\nexport interface ResolvedOpenApiSetupOptions {\n enabled: boolean;\n openApiPath: string;\n openApiJsonPath: string;\n title: string;\n description: string;\n onDocument?: (document: OpenAPIObject) => void;\n}\n\n/**\n * Default storici della documentazione Accessi.\n *\n * I consumer esistenti non devono cambiare comportamento: senza override la UI\n * resta su `accessi/openapi` e la spec su `accessi/openapi.json`.\n */\nexport const DEFAULT_ACCESSI_OPENAPI = {\n enabled: true,\n openApiPath: \"accessi/openapi\",\n openApiJsonPath: \"accessi/openapi.json\",\n title: \"Accessi API Documentation\",\n description: \"API del modulo accessi\",\n} as const satisfies ResolvedOpenApiSetupOptions;\n\n/** Default del setup OpenAPI generico (usato direttamente da `setupOpenApi`). */\nconst DEFAULT_OPENAPI_SETUP = {\n enabled: true,\n openApiPath: \"openapi\",\n openApiJsonPath: \"openapi.json\",\n title: \"API Documentation\",\n description: \"API per la gestione di accessi utenti e allegati\",\n} as const satisfies ResolvedOpenApiSetupOptions;\n\nfunction normalizeOpenApiPath(value: string): string {\n return value.replace(/^\\/+|\\/+$/g, \"\");\n}\n\nfunction normalizeOpenApiJsonPath(value: string): string {\n return value.replace(/^\\/+|\\/+$/g, \"\");\n}\n\n/**\n * Unisce i livelli di configurazione ignorando le proprieta `undefined`.\n *\n * A differenza dello spread semplice, un `{ openApiPath: undefined }` esplicito\n * non cancella il default del livello precedente.\n */\nfunction mergeOpenApiSetupOptions(\n defaults: OpenApiSetupOptions,\n ...overrides: Array<OpenApiSetupOptions | undefined>\n): OpenApiSetupOptions {\n const merged: OpenApiSetupOptions = { ...defaults };\n for (const override of overrides) {\n if (!override) continue;\n if (override.enabled !== undefined) merged.enabled = override.enabled;\n if (override.openApiPath !== undefined) merged.openApiPath = override.openApiPath;\n if (override.openApiJsonPath !== undefined) merged.openApiJsonPath = override.openApiJsonPath;\n if (override.title !== undefined) merged.title = override.title;\n if (override.description !== undefined) merged.description = override.description;\n if (override.onDocument !== undefined) merged.onDocument = override.onDocument;\n }\n return merged;\n}\n\n/**\n * Risolve i default e i vari livelli di override in un'unica configurazione.\n *\n * L'ordine dei livelli e esplicito: i parametri successivi vincono sui precedenti.\n */\nexport function resolveOpenApiSetupOptions(\n defaults: OpenApiSetupOptions = DEFAULT_OPENAPI_SETUP,\n ...overrides: Array<OpenApiSetupOptions | undefined>\n): ResolvedOpenApiSetupOptions {\n const merged = mergeOpenApiSetupOptions(defaults, ...overrides);\n const defaultOpenApiPath = normalizeOpenApiPath(defaults.openApiPath ?? \"openapi\") || \"openapi\";\n const openApiPath = normalizeOpenApiPath(merged.openApiPath ?? defaultOpenApiPath) || defaultOpenApiPath;\n\n // Storico: se si cambia solo `openApiPath`, il JSON segue il nuovo path\n // (`<path>.json`). Un `openApiJsonPath` esplicito in un override vince sempre.\n const explicitJsonPath = [...overrides]\n .reverse()\n .find((override) => override?.openApiJsonPath !== undefined)\n ?.openApiJsonPath;\n const hasOverrides = overrides.some((override) => override !== undefined);\n const jsonPathCandidate = explicitJsonPath\n ?? (hasOverrides ? `${openApiPath}.json` : (merged.openApiJsonPath ?? `${openApiPath}.json`));\n const openApiJsonPath = normalizeOpenApiJsonPath(jsonPathCandidate) || `${openApiPath}.json`;\n\n return {\n enabled: merged.enabled !== false,\n openApiPath,\n openApiJsonPath,\n title: merged.title ?? \"API Documentation\",\n description: merged.description ?? \"API per la gestione di accessi utenti e allegati\",\n ...(merged.onDocument ? { onDocument: merged.onDocument } : {}),\n };\n}\n\n/**\n * Risolve la configurazione OpenAPI di Accessi.\n *\n * I livelli successivi vincono sui precedenti, quindi la variante unificata puo\n * usare `resolveAccessiOpenApiOptions(accessiOptions.openapi, emilsoftwareOptions.openapi)`.\n */\nexport function resolveAccessiOpenApiOptions(\n ...overrides: Array<OpenApiSetupOptions | undefined>\n): ResolvedOpenApiSetupOptions {\n return resolveOpenApiSetupOptions(DEFAULT_ACCESSI_OPENAPI, ...overrides);\n}\n\n/**\n * Path da passare a `setGlobalPrefix` per escludere la documentazione.\n *\n * Usa la sintassi wildcard di path-to-regexp v8 (`{*path}`) per evitare il\n * LegacyRouteConverter warning di Nest.\n */\nexport function buildOpenApiExcludePaths(options: ResolvedOpenApiSetupOptions): string[] {\n if (!options.enabled) return [];\n return [`/${options.openApiPath}`, `/${options.openApiPath}/{*path}`];\n}\n\n/** True se il path Express appartiene alla documentazione OpenAPI configurata. */\nexport function isOpenApiRequestPath(path: string, options: ResolvedOpenApiSetupOptions): boolean {\n if (!options.enabled) return false;\n\n const uiPath = `/${options.openApiPath}`;\n if (path === uiPath || path.startsWith(`${uiPath}/`)) return true;\n\n return path === `/${options.openApiJsonPath}` || path === `/${options.openApiPath}-json`;\n}\n\n/**\n * Genera il documento OpenAPI e, se `enabled` non e false, monta UI e JSON.\n *\n * Il documento viene sempre restituito: questo permette all'host di mergiarlo\n * nel proprio documento unico anche quando la UI del modulo e disabilitata.\n */\nexport function setupOpenApi(app: INestApplication, options?: OpenApiSetupOptions): OpenAPIObject {\n const logger: Logger = new Logger(\"OpenApiConfig\");\n const resolved = resolveOpenApiSetupOptions(DEFAULT_OPENAPI_SETUP, options);\n\n const config = new DocumentBuilder()\n .setTitle(resolved.title)\n .setDescription(resolved.description)\n .setVersion(\"1.0\")\n .addBearerAuth() // Per abilitare l'autenticazione JWT\n .build();\n\n const document = SwaggerModule.createDocument(app, config);\n resolved.onDocument?.(document);\n\n if (!resolved.enabled) {\n logger.info(\"OpenAPI disabilitato: UI e JSON non montati.\");\n return document;\n }\n\n SwaggerModule.setup(resolved.openApiPath, app, document);\n\n app.use(`/${resolved.openApiJsonPath}`, (_req: Request, res: Response) => {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.send(document);\n });\n\n // Manteniamo l'alias storico `<path>-json` per non rompere i consumer esistenti.\n const legacyOpenApiJsonPath = `${resolved.openApiPath}-json`;\n if (legacyOpenApiJsonPath !== resolved.openApiJsonPath) {\n app.use(`/${legacyOpenApiJsonPath}`, (_req: Request, res: Response) => {\n res.setHeader(\"Content-Type\", \"application/json\");\n res.send(document);\n });\n }\n\n const serverAddress = app.getHttpServer()?.address?.();\n const port = serverAddress && typeof serverAddress === \"object\"\n ? (serverAddress as AddressInfo).port\n : 3000;\n\n logger.info(\n `OpenAPI documentation available at: http://localhost:${port}/${resolved.openApiPath}`\n );\n logger.info(\n `OpenAPI JSON available at: http://localhost:${port}/${resolved.openApiJsonPath}`\n );\n\n return document;\n}\n"]}
@@ -0,0 +1,15 @@
1
+ import type { OpenAPIObject } from "@nestjs/swagger";
2
+ /** Politica di risoluzione quando due documenti definiscono la stessa chiave. */
3
+ export type OpenApiMergeConflictPolicy = "error" | "keep" | "overwrite";
4
+ export interface MergeOpenApiOptions {
5
+ /** Default: `error`. */
6
+ conflict?: OpenApiMergeConflictPolicy;
7
+ }
8
+ /**
9
+ * Unisce il documento Accessi nel documento OpenAPI dell'host.
10
+ *
11
+ * Per default un conflitto su path, schema o security scheme produce errore:
12
+ * e meglio accorgersi subito di due contratti incompatibili. Usare
13
+ * `conflict: "overwrite"` solo quando il documento host deve vincere.
14
+ */
15
+ export declare function mergeOpenApiDocuments(target: OpenAPIObject, source: OpenAPIObject, options?: MergeOpenApiOptions): OpenAPIObject;
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mergeOpenApiDocuments = mergeOpenApiDocuments;
4
+ function isRecord(value) {
5
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6
+ }
7
+ function cloneDocument(document) {
8
+ return JSON.parse(JSON.stringify(document));
9
+ }
10
+ function sameValue(left, right) {
11
+ return JSON.stringify(left) === JSON.stringify(right);
12
+ }
13
+ function mergeRecord(target, source, policy, section) {
14
+ if (!source)
15
+ return target;
16
+ const merged = Object.assign({}, (target !== null && target !== void 0 ? target : {}));
17
+ for (const [key, value] of Object.entries(source)) {
18
+ if (!(key in merged)) {
19
+ merged[key] = value;
20
+ continue;
21
+ }
22
+ if (sameValue(merged[key], value))
23
+ continue;
24
+ if (policy === "keep")
25
+ continue;
26
+ if (policy === "overwrite") {
27
+ merged[key] = value;
28
+ continue;
29
+ }
30
+ throw new Error(`Conflitto OpenAPI in ${section}.${key}.`);
31
+ }
32
+ return merged;
33
+ }
34
+ function mergeTags(target, source, policy) {
35
+ if (!Array.isArray(target) || !Array.isArray(source))
36
+ return target !== null && target !== void 0 ? target : source;
37
+ const merged = [...target];
38
+ for (const tag of source) {
39
+ if (!isRecord(tag) || typeof tag.name !== "string") {
40
+ merged.push(tag);
41
+ continue;
42
+ }
43
+ const existingIndex = merged.findIndex((candidate) => isRecord(candidate) && candidate.name === tag.name);
44
+ if (existingIndex < 0) {
45
+ merged.push(tag);
46
+ continue;
47
+ }
48
+ const existing = merged[existingIndex];
49
+ if (sameValue(existing, tag))
50
+ continue;
51
+ if (policy === "keep")
52
+ continue;
53
+ if (policy === "overwrite") {
54
+ merged[existingIndex] = tag;
55
+ continue;
56
+ }
57
+ throw new Error(`Conflitto OpenAPI nel tag "${tag.name}".`);
58
+ }
59
+ return merged;
60
+ }
61
+ function mergeServers(target, source, policy) {
62
+ if (!Array.isArray(target) || !Array.isArray(source))
63
+ return target !== null && target !== void 0 ? target : source;
64
+ const merged = [...target];
65
+ for (const server of source) {
66
+ if (!isRecord(server) || typeof server.url !== "string") {
67
+ merged.push(server);
68
+ continue;
69
+ }
70
+ const existingIndex = merged.findIndex((candidate) => isRecord(candidate) && candidate.url === server.url);
71
+ if (existingIndex < 0) {
72
+ merged.push(server);
73
+ continue;
74
+ }
75
+ const existing = merged[existingIndex];
76
+ if (sameValue(existing, server))
77
+ continue;
78
+ if (policy === "keep")
79
+ continue;
80
+ if (policy === "overwrite") {
81
+ merged[existingIndex] = server;
82
+ continue;
83
+ }
84
+ throw new Error(`Conflitto OpenAPI nel server "${server.url}".`);
85
+ }
86
+ return merged;
87
+ }
88
+ /**
89
+ * Unisce il documento Accessi nel documento OpenAPI dell'host.
90
+ *
91
+ * Per default un conflitto su path, schema o security scheme produce errore:
92
+ * e meglio accorgersi subito di due contratti incompatibili. Usare
93
+ * `conflict: "overwrite"` solo quando il documento host deve vincere.
94
+ */
95
+ function mergeOpenApiDocuments(target, source, options = {}) {
96
+ var _a;
97
+ if (!target || !source) {
98
+ throw new Error("mergeOpenApiDocuments richiede due documenti OpenAPI validi.");
99
+ }
100
+ const policy = (_a = options.conflict) !== null && _a !== void 0 ? _a : "error";
101
+ const result = cloneDocument(target);
102
+ const sourceDocument = source;
103
+ result.paths = mergeRecord(isRecord(result.paths) ? result.paths : undefined, isRecord(sourceDocument.paths) ? sourceDocument.paths : undefined, policy, "paths");
104
+ const targetComponents = isRecord(result.components) ? result.components : {};
105
+ const sourceComponents = isRecord(sourceDocument.components) ? sourceDocument.components : {};
106
+ const componentSections = new Set([
107
+ ...Object.keys(targetComponents),
108
+ ...Object.keys(sourceComponents),
109
+ ]);
110
+ const mergedComponents = Object.assign({}, targetComponents);
111
+ for (const section of componentSections) {
112
+ mergedComponents[section] = mergeRecord(isRecord(targetComponents[section]) ? targetComponents[section] : undefined, isRecord(sourceComponents[section]) ? sourceComponents[section] : undefined, policy, `components.${section}`);
113
+ }
114
+ result.components = mergedComponents;
115
+ result.tags = mergeTags(result.tags, sourceDocument.tags, policy);
116
+ result.servers = mergeServers(result.servers, sourceDocument.servers, policy);
117
+ return result;
118
+ }
119
+ //# sourceMappingURL=OpenApiMerge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpenApiMerge.js","sourceRoot":"","sources":["../../src/openapi/OpenApiMerge.ts"],"names":[],"mappings":"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","sourcesContent":["import type { OpenAPIObject } from \"@nestjs/swagger\";\n\n/** Politica di risoluzione quando due documenti definiscono la stessa chiave. */\nexport type OpenApiMergeConflictPolicy = \"error\" | \"keep\" | \"overwrite\";\n\nexport interface MergeOpenApiOptions {\n /** Default: `error`. */\n conflict?: OpenApiMergeConflictPolicy;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction cloneDocument(document: OpenAPIObject): Record<string, unknown> {\n return JSON.parse(JSON.stringify(document)) as Record<string, unknown>;\n}\n\nfunction sameValue(left: unknown, right: unknown): boolean {\n return JSON.stringify(left) === JSON.stringify(right);\n}\n\nfunction mergeRecord(\n target: Record<string, unknown> | undefined,\n source: Record<string, unknown> | undefined,\n policy: OpenApiMergeConflictPolicy,\n section: string,\n): Record<string, unknown> | undefined {\n if (!source) return target;\n const merged: Record<string, unknown> = { ...(target ?? {}) };\n\n for (const [key, value] of Object.entries(source)) {\n if (!(key in merged)) {\n merged[key] = value;\n continue;\n }\n if (sameValue(merged[key], value)) continue;\n if (policy === \"keep\") continue;\n if (policy === \"overwrite\") {\n merged[key] = value;\n continue;\n }\n throw new Error(`Conflitto OpenAPI in ${section}.${key}.`);\n }\n\n return merged;\n}\n\nfunction mergeTags(\n target: unknown,\n source: unknown,\n policy: OpenApiMergeConflictPolicy,\n): unknown {\n if (!Array.isArray(target) || !Array.isArray(source)) return target ?? source;\n const merged = [...target];\n for (const tag of source) {\n if (!isRecord(tag) || typeof tag.name !== \"string\") {\n merged.push(tag);\n continue;\n }\n const existingIndex = merged.findIndex(\n (candidate) => isRecord(candidate) && candidate.name === tag.name,\n );\n if (existingIndex < 0) {\n merged.push(tag);\n continue;\n }\n const existing = merged[existingIndex];\n if (sameValue(existing, tag)) continue;\n if (policy === \"keep\") continue;\n if (policy === \"overwrite\") {\n merged[existingIndex] = tag;\n continue;\n }\n throw new Error(`Conflitto OpenAPI nel tag \"${tag.name}\".`);\n }\n return merged;\n}\n\nfunction mergeServers(\n target: unknown,\n source: unknown,\n policy: OpenApiMergeConflictPolicy,\n): unknown {\n if (!Array.isArray(target) || !Array.isArray(source)) return target ?? source;\n const merged = [...target];\n for (const server of source) {\n if (!isRecord(server) || typeof server.url !== \"string\") {\n merged.push(server);\n continue;\n }\n const existingIndex = merged.findIndex(\n (candidate) => isRecord(candidate) && candidate.url === server.url,\n );\n if (existingIndex < 0) {\n merged.push(server);\n continue;\n }\n const existing = merged[existingIndex];\n if (sameValue(existing, server)) continue;\n if (policy === \"keep\") continue;\n if (policy === \"overwrite\") {\n merged[existingIndex] = server;\n continue;\n }\n throw new Error(`Conflitto OpenAPI nel server \"${server.url}\".`);\n }\n return merged;\n}\n\n/**\n * Unisce il documento Accessi nel documento OpenAPI dell'host.\n *\n * Per default un conflitto su path, schema o security scheme produce errore:\n * e meglio accorgersi subito di due contratti incompatibili. Usare\n * `conflict: \"overwrite\"` solo quando il documento host deve vincere.\n */\nexport function mergeOpenApiDocuments(\n target: OpenAPIObject,\n source: OpenAPIObject,\n options: MergeOpenApiOptions = {},\n): OpenAPIObject {\n if (!target || !source) {\n throw new Error(\"mergeOpenApiDocuments richiede due documenti OpenAPI validi.\");\n }\n\n const policy = options.conflict ?? \"error\";\n const result = cloneDocument(target);\n const sourceDocument = source as unknown as Record<string, unknown>;\n\n result.paths = mergeRecord(\n isRecord(result.paths) ? result.paths : undefined,\n isRecord(sourceDocument.paths) ? sourceDocument.paths : undefined,\n policy,\n \"paths\",\n );\n\n const targetComponents = isRecord(result.components) ? result.components : {};\n const sourceComponents = isRecord(sourceDocument.components) ? sourceDocument.components : {};\n const componentSections = new Set([\n ...Object.keys(targetComponents),\n ...Object.keys(sourceComponents),\n ]);\n const mergedComponents: Record<string, unknown> = { ...targetComponents };\n for (const section of componentSections) {\n mergedComponents[section] = mergeRecord(\n isRecord(targetComponents[section]) ? targetComponents[section] : undefined,\n isRecord(sourceComponents[section]) ? sourceComponents[section] : undefined,\n policy,\n `components.${section}`,\n );\n }\n result.components = mergedComponents;\n\n result.tags = mergeTags(result.tags, sourceDocument.tags, policy);\n result.servers = mergeServers(result.servers, sourceDocument.servers, policy);\n\n return result as unknown as OpenAPIObject;\n}\n"]}