@decaf-ts/for-nest 0.11.1 → 0.11.2-refactor
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.
- package/README.md +1 -1
- package/dist/for-nest.cjs +2636 -2
- package/dist/for-nest.js +2738 -2
- package/lib/cjs/auth/AuthInterceptor.cjs +88 -0
- package/lib/cjs/auth/DecafAuthHandler.cjs +37 -0
- package/lib/cjs/auth/DecafAuthModule.cjs +56 -0
- package/lib/cjs/auth/constants.cjs +9 -0
- package/lib/cjs/auth/decorators.cjs +28 -0
- package/lib/cjs/auth/index.cjs +23 -0
- package/lib/cjs/bin/cli.cjs +2 -2
- package/lib/cjs/cli-module.cjs +48 -6
- package/lib/cjs/constants.cjs +5 -5
- package/lib/cjs/controllers.cjs +10 -102
- package/lib/cjs/core-module.cjs +8 -8
- package/lib/cjs/decaf-model/DecafModelModule.cjs +19 -4
- package/lib/cjs/decaf-model/FromModelController.cjs +733 -788
- package/lib/cjs/decaf-model/decorators/ApiOperationFromModel.cjs +53 -16
- package/lib/cjs/decaf-model/decorators/ApiParamsFromModel.cjs +2 -2
- package/lib/cjs/decaf-model/decorators/DecafBody.cjs +2 -2
- package/lib/cjs/decaf-model/decorators/DecafParams.cjs +2 -2
- package/lib/cjs/decaf-model/decorators/controller-config.cjs +29 -0
- package/lib/cjs/decaf-model/decorators/decorators.cjs +5 -45
- package/lib/cjs/decaf-model/decorators/expose.cjs +13 -0
- package/lib/cjs/decaf-model/decorators/index.cjs +3 -2
- package/lib/cjs/decaf-model/decorators/types.cjs +2 -2
- package/lib/cjs/decaf-model/decorators/utils.cjs +2 -2
- package/lib/cjs/decaf-model/index.cjs +28 -19
- package/lib/cjs/decaf-model/types.cjs +2 -2
- package/lib/cjs/decaf-model/utils.cjs +2 -2
- package/lib/cjs/decoration.cjs +2 -2
- package/lib/cjs/decorators.cjs +85 -0
- package/lib/cjs/events-module/DecafStreamModule.cjs +2 -2
- package/lib/cjs/events-module/EventsController.cjs +2 -2
- package/lib/cjs/events-module/constant.cjs +2 -2
- package/lib/cjs/events-module/index.cjs +2 -2
- package/lib/cjs/events-module/utils.cjs +2 -2
- package/lib/cjs/factory/NestBootstraper.cjs +2 -2
- package/lib/cjs/factory/errors/cors.cjs +2 -2
- package/lib/cjs/factory/errors/index.cjs +2 -2
- package/lib/cjs/factory/errors/throttling.cjs +2 -2
- package/lib/cjs/factory/exceptions/DecafErrorFilter.cjs +2 -2
- package/lib/cjs/factory/exceptions/decorators.cjs +2 -2
- package/lib/cjs/factory/exceptions/index.cjs +2 -2
- package/lib/cjs/factory/index.cjs +2 -2
- package/lib/cjs/factory/openapi/DtoBuilder.cjs +37 -6
- package/lib/cjs/factory/openapi/SwaggerBuilder.cjs +2 -2
- package/lib/cjs/factory/openapi/SwaggerCustomUI.cjs +2 -2
- package/lib/cjs/factory/openapi/constants.cjs +2 -2
- package/lib/cjs/factory/openapi/index.cjs +2 -2
- package/lib/cjs/index.cjs +8 -7
- package/lib/cjs/interceptors/DecafRequestHandlerInterceptor.cjs +27 -21
- package/lib/cjs/interceptors/index.cjs +3 -4
- package/lib/cjs/module.cjs +4 -2
- package/lib/cjs/overrides/Adapter.cjs +2 -2
- package/lib/cjs/overrides/ModelBuilderExtensions.cjs +2 -2
- package/lib/cjs/overrides/constants.cjs +2 -2
- package/lib/cjs/overrides/decoration.cjs +2 -2
- package/lib/cjs/overrides/helpers.cjs +2 -2
- package/lib/cjs/overrides/index.cjs +3 -2
- package/lib/cjs/overrides/logging.cjs +5 -0
- package/lib/cjs/overrides/overrides.cjs +2 -2
- package/lib/cjs/request/DecafAuthHandler.cjs +5 -39
- package/lib/cjs/request/DecafHandlerExecutor.cjs +2 -2
- package/lib/cjs/request/DecafRequestContext.cjs +11 -20
- package/lib/cjs/request/DecafResponseInterceptor.cjs +3 -3
- package/lib/cjs/request/index.cjs +2 -2
- package/lib/cjs/swagger-types.cjs +2 -2
- package/lib/cjs/types.cjs +2 -2
- package/lib/cjs/utils.cjs +2 -2
- package/lib/cjs/webhooks/DecafWebhookModule.cjs +11 -12
- package/lib/cjs/webhooks/controllers.cjs +10 -10
- package/lib/cjs/webhooks/index.cjs +2 -15
- package/lib/cjs/webhooks/types.cjs +2 -2
- package/lib/esm/auth/AuthInterceptor.js +79 -0
- package/lib/esm/auth/DecafAuthHandler.js +31 -0
- package/lib/esm/auth/DecafAuthModule.js +48 -0
- package/lib/esm/auth/constants.js +5 -0
- package/lib/esm/auth/decorators.js +22 -0
- package/lib/esm/auth/index.js +6 -0
- package/lib/esm/bin/cli.js +1 -1
- package/lib/esm/cli-module.js +47 -5
- package/lib/esm/constants.js +3 -3
- package/lib/esm/controllers.js +10 -102
- package/lib/esm/core-module.js +7 -7
- package/lib/esm/decaf-model/DecafModelModule.js +18 -3
- package/lib/esm/decaf-model/FromModelController.js +734 -789
- package/lib/esm/decaf-model/decorators/ApiOperationFromModel.js +53 -15
- package/lib/esm/decaf-model/decorators/ApiParamsFromModel.js +1 -1
- package/lib/esm/decaf-model/decorators/DecafBody.js +1 -1
- package/lib/esm/decaf-model/decorators/DecafParams.js +1 -1
- package/lib/esm/decaf-model/decorators/controller-config.js +25 -0
- package/lib/esm/decaf-model/decorators/decorators.js +2 -43
- package/lib/esm/decaf-model/decorators/expose.js +9 -0
- package/lib/esm/decaf-model/decorators/index.js +2 -1
- package/lib/esm/decaf-model/decorators/types.js +1 -1
- package/lib/esm/decaf-model/decorators/utils.js +1 -1
- package/lib/esm/decaf-model/index.js +11 -4
- package/lib/esm/decaf-model/types.js +1 -1
- package/lib/esm/decaf-model/utils.js +1 -1
- package/lib/esm/decoration.js +1 -1
- package/lib/esm/decorators.js +79 -0
- package/lib/esm/events-module/DecafStreamModule.js +1 -1
- package/lib/esm/events-module/EventsController.js +1 -1
- package/lib/esm/events-module/constant.js +1 -1
- package/lib/esm/events-module/index.js +1 -1
- package/lib/esm/events-module/utils.js +1 -1
- package/lib/esm/factory/NestBootstraper.js +1 -1
- package/lib/esm/factory/errors/cors.js +1 -1
- package/lib/esm/factory/errors/index.js +1 -1
- package/lib/esm/factory/errors/throttling.js +1 -1
- package/lib/esm/factory/exceptions/DecafErrorFilter.js +1 -1
- package/lib/esm/factory/exceptions/decorators.js +1 -1
- package/lib/esm/factory/exceptions/index.js +1 -1
- package/lib/esm/factory/index.js +1 -1
- package/lib/esm/factory/openapi/DtoBuilder.js +36 -5
- package/lib/esm/factory/openapi/SwaggerBuilder.js +1 -1
- package/lib/esm/factory/openapi/SwaggerCustomUI.js +1 -1
- package/lib/esm/factory/openapi/constants.js +1 -1
- package/lib/esm/factory/openapi/index.js +1 -1
- package/lib/esm/index.js +7 -6
- package/lib/esm/interceptors/DecafRequestHandlerInterceptor.js +27 -21
- package/lib/esm/interceptors/index.js +2 -3
- package/lib/esm/module.js +3 -1
- package/lib/esm/overrides/Adapter.js +1 -1
- package/lib/esm/overrides/ModelBuilderExtensions.js +1 -1
- package/lib/esm/overrides/constants.js +1 -1
- package/lib/esm/overrides/decoration.js +1 -1
- package/lib/esm/overrides/helpers.js +1 -1
- package/lib/esm/overrides/index.js +2 -1
- package/lib/esm/overrides/logging.js +2 -0
- package/lib/esm/overrides/overrides.js +1 -1
- package/lib/esm/request/DecafAuthHandler.js +2 -36
- package/lib/esm/request/DecafHandlerExecutor.js +1 -1
- package/lib/esm/request/DecafRequestContext.js +10 -19
- package/lib/esm/request/DecafResponseInterceptor.js +2 -2
- package/lib/esm/request/index.js +1 -1
- package/lib/esm/swagger-types.js +1 -1
- package/lib/esm/types.js +1 -1
- package/lib/esm/utils.js +1 -1
- package/lib/esm/webhooks/DecafWebhookModule.js +5 -6
- package/lib/esm/webhooks/controllers.js +2 -2
- package/lib/esm/webhooks/index.js +1 -2
- package/lib/esm/webhooks/types.js +1 -1
- package/lib/types/{interceptors → auth}/AuthInterceptor.d.cts +4 -1
- package/lib/types/{interceptors → auth}/AuthInterceptor.d.mts +4 -1
- package/lib/types/auth/DecafAuthHandler.d.cts +22 -0
- package/lib/types/auth/DecafAuthHandler.d.mts +22 -0
- package/lib/types/auth/DecafAuthModule.d.cts +9 -0
- package/lib/types/auth/DecafAuthModule.d.mts +9 -0
- package/lib/types/auth/constants.d.cts +4 -0
- package/lib/types/auth/constants.d.mts +4 -0
- package/lib/types/auth/decorators.d.cts +4 -0
- package/lib/types/auth/decorators.d.mts +4 -0
- package/lib/types/auth/index.d.cts +6 -0
- package/lib/types/auth/index.d.mts +6 -0
- package/lib/types/cli-module.d.cts +1 -4
- package/lib/types/cli-module.d.mts +1 -4
- package/lib/types/constants.d.cts +2 -2
- package/lib/types/constants.d.mts +2 -2
- package/lib/types/controllers.d.cts +7 -12
- package/lib/types/controllers.d.mts +7 -12
- package/lib/types/decaf-model/DecafModelModule.d.cts +1 -0
- package/lib/types/decaf-model/DecafModelModule.d.mts +1 -0
- package/lib/types/decaf-model/FromModelController.d.cts +40 -52
- package/lib/types/decaf-model/FromModelController.d.mts +40 -52
- package/lib/types/decaf-model/decorators/ApiOperationFromModel.d.cts +0 -2
- package/lib/types/decaf-model/decorators/ApiOperationFromModel.d.mts +0 -2
- package/lib/types/decaf-model/decorators/controller-config.d.cts +18 -0
- package/lib/types/decaf-model/decorators/controller-config.d.mts +18 -0
- package/lib/types/decaf-model/decorators/decorators.d.cts +1 -29
- package/lib/types/decaf-model/decorators/decorators.d.mts +1 -29
- package/lib/types/decaf-model/decorators/expose.d.cts +1 -0
- package/lib/types/decaf-model/decorators/expose.d.mts +1 -0
- package/lib/types/decaf-model/decorators/index.d.cts +1 -0
- package/lib/types/decaf-model/decorators/index.d.mts +1 -0
- package/lib/types/decaf-model/decorators/types.d.cts +8 -21
- package/lib/types/decaf-model/decorators/types.d.mts +8 -21
- package/lib/types/decaf-model/index.d.cts +10 -3
- package/lib/types/decaf-model/index.d.mts +10 -3
- package/lib/types/decaf-model/utils.d.cts +2 -1
- package/lib/types/decaf-model/utils.d.mts +2 -1
- package/lib/types/decorators.d.cts +34 -0
- package/lib/types/decorators.d.mts +34 -0
- package/lib/types/factory/openapi/DtoBuilder.d.cts +7 -3
- package/lib/types/factory/openapi/DtoBuilder.d.mts +7 -3
- package/lib/types/index.d.cts +5 -5
- package/lib/types/index.d.mts +5 -5
- package/lib/types/interceptors/DecafRequestHandlerInterceptor.d.cts +1 -2
- package/lib/types/interceptors/DecafRequestHandlerInterceptor.d.mts +1 -2
- package/lib/types/interceptors/index.d.cts +1 -2
- package/lib/types/interceptors/index.d.mts +1 -2
- package/lib/types/overrides/Adapter.d.cts +1 -1
- package/lib/types/overrides/Adapter.d.mts +1 -1
- package/lib/types/overrides/index.d.cts +1 -0
- package/lib/types/overrides/index.d.mts +1 -0
- package/lib/types/overrides/logging.d.cts +1 -0
- package/lib/types/overrides/logging.d.mts +1 -0
- package/lib/types/request/DecafAuthHandler.d.cts +1 -10
- package/lib/types/request/DecafAuthHandler.d.mts +1 -10
- package/lib/types/request/DecafRequestContext.d.cts +4 -5
- package/lib/types/request/DecafRequestContext.d.mts +4 -5
- package/lib/types/types.d.cts +12 -24
- package/lib/types/types.d.mts +12 -24
- package/lib/types/webhooks/controllers.d.cts +1 -1
- package/lib/types/webhooks/controllers.d.mts +1 -1
- package/lib/types/webhooks/index.d.cts +0 -1
- package/lib/types/webhooks/index.d.mts +0 -1
- package/package.json +5 -13
- package/dist/for-nest.cjs.map +0 -1
- package/dist/for-nest.js.map +0 -1
- package/lib/cjs/bin/cli.cjs.map +0 -1
- package/lib/cjs/cli-module.cjs.map +0 -1
- package/lib/cjs/constants.cjs.map +0 -1
- package/lib/cjs/controllers.cjs.map +0 -1
- package/lib/cjs/core-module.cjs.map +0 -1
- package/lib/cjs/decaf-model/DecafModelModule.cjs.map +0 -1
- package/lib/cjs/decaf-model/FromModelController.cjs.map +0 -1
- package/lib/cjs/decaf-model/decorators/ApiOperationFromModel.cjs.map +0 -1
- package/lib/cjs/decaf-model/decorators/ApiParamsFromModel.cjs.map +0 -1
- package/lib/cjs/decaf-model/decorators/DecafBody.cjs.map +0 -1
- package/lib/cjs/decaf-model/decorators/DecafParams.cjs.map +0 -1
- package/lib/cjs/decaf-model/decorators/decorators.cjs.map +0 -1
- package/lib/cjs/decaf-model/decorators/index.cjs.map +0 -1
- package/lib/cjs/decaf-model/decorators/types.cjs.map +0 -1
- package/lib/cjs/decaf-model/decorators/utils.cjs.map +0 -1
- package/lib/cjs/decaf-model/index.cjs.map +0 -1
- package/lib/cjs/decaf-model/types.cjs.map +0 -1
- package/lib/cjs/decaf-model/utils.cjs.map +0 -1
- package/lib/cjs/decoration.cjs.map +0 -1
- package/lib/cjs/events-module/DecafStreamModule.cjs.map +0 -1
- package/lib/cjs/events-module/EventsController.cjs.map +0 -1
- package/lib/cjs/events-module/constant.cjs.map +0 -1
- package/lib/cjs/events-module/index.cjs.map +0 -1
- package/lib/cjs/events-module/utils.cjs.map +0 -1
- package/lib/cjs/factory/NestBootstraper.cjs.map +0 -1
- package/lib/cjs/factory/errors/cors.cjs.map +0 -1
- package/lib/cjs/factory/errors/index.cjs.map +0 -1
- package/lib/cjs/factory/errors/throttling.cjs.map +0 -1
- package/lib/cjs/factory/exceptions/DecafErrorFilter.cjs.map +0 -1
- package/lib/cjs/factory/exceptions/decorators.cjs.map +0 -1
- package/lib/cjs/factory/exceptions/index.cjs.map +0 -1
- package/lib/cjs/factory/index.cjs.map +0 -1
- package/lib/cjs/factory/openapi/DtoBuilder.cjs.map +0 -1
- package/lib/cjs/factory/openapi/SwaggerBuilder.cjs.map +0 -1
- package/lib/cjs/factory/openapi/SwaggerCustomUI.cjs.map +0 -1
- package/lib/cjs/factory/openapi/constants.cjs.map +0 -1
- package/lib/cjs/factory/openapi/index.cjs.map +0 -1
- package/lib/cjs/index.cjs.map +0 -1
- package/lib/cjs/interceptors/AuthInterceptor.cjs +0 -52
- package/lib/cjs/interceptors/AuthInterceptor.cjs.map +0 -1
- package/lib/cjs/interceptors/DecafRequestHandlerInterceptor.cjs.map +0 -1
- package/lib/cjs/interceptors/context.cjs +0 -18
- package/lib/cjs/interceptors/context.cjs.map +0 -1
- package/lib/cjs/interceptors/index.cjs.map +0 -1
- package/lib/cjs/migrations/index.cjs +0 -35
- package/lib/cjs/migrations/index.cjs.map +0 -1
- package/lib/cjs/migrations/migration-module.cjs +0 -60
- package/lib/cjs/migrations/migration-module.cjs.map +0 -1
- package/lib/cjs/migrations/migration-options.cjs +0 -4
- package/lib/cjs/migrations/migration-options.cjs.map +0 -1
- package/lib/cjs/module.cjs.map +0 -1
- package/lib/cjs/overrides/Adapter.cjs.map +0 -1
- package/lib/cjs/overrides/ModelBuilderExtensions.cjs.map +0 -1
- package/lib/cjs/overrides/constants.cjs.map +0 -1
- package/lib/cjs/overrides/decoration.cjs.map +0 -1
- package/lib/cjs/overrides/helpers.cjs.map +0 -1
- package/lib/cjs/overrides/index.cjs.map +0 -1
- package/lib/cjs/overrides/overrides.cjs.map +0 -1
- package/lib/cjs/ram/RamRequestTransformer.cjs +0 -26
- package/lib/cjs/ram/RamRequestTransformer.cjs.map +0 -1
- package/lib/cjs/ram/index.cjs +0 -19
- package/lib/cjs/ram/index.cjs.map +0 -1
- package/lib/cjs/request/DecafAuthHandler.cjs.map +0 -1
- package/lib/cjs/request/DecafHandlerExecutor.cjs.map +0 -1
- package/lib/cjs/request/DecafRequestContext.cjs.map +0 -1
- package/lib/cjs/request/DecafResponseInterceptor.cjs.map +0 -1
- package/lib/cjs/request/index.cjs.map +0 -1
- package/lib/cjs/swagger-types.cjs.map +0 -1
- package/lib/cjs/types.cjs.map +0 -1
- package/lib/cjs/utils.cjs.map +0 -1
- package/lib/cjs/webhooks/DecafWebhookModule.cjs.map +0 -1
- package/lib/cjs/webhooks/controllers.cjs.map +0 -1
- package/lib/cjs/webhooks/index.cjs.map +0 -1
- package/lib/cjs/webhooks/types.cjs.map +0 -1
- package/lib/esm/bin/cli.js.map +0 -1
- package/lib/esm/cli-module.js.map +0 -1
- package/lib/esm/constants.js.map +0 -1
- package/lib/esm/controllers.js.map +0 -1
- package/lib/esm/core-module.js.map +0 -1
- package/lib/esm/decaf-model/DecafModelModule.js.map +0 -1
- package/lib/esm/decaf-model/FromModelController.js.map +0 -1
- package/lib/esm/decaf-model/decorators/ApiOperationFromModel.js.map +0 -1
- package/lib/esm/decaf-model/decorators/ApiParamsFromModel.js.map +0 -1
- package/lib/esm/decaf-model/decorators/DecafBody.js.map +0 -1
- package/lib/esm/decaf-model/decorators/DecafParams.js.map +0 -1
- package/lib/esm/decaf-model/decorators/decorators.js.map +0 -1
- package/lib/esm/decaf-model/decorators/index.js.map +0 -1
- package/lib/esm/decaf-model/decorators/types.js.map +0 -1
- package/lib/esm/decaf-model/decorators/utils.js.map +0 -1
- package/lib/esm/decaf-model/index.js.map +0 -1
- package/lib/esm/decaf-model/types.js.map +0 -1
- package/lib/esm/decaf-model/utils.js.map +0 -1
- package/lib/esm/decoration.js.map +0 -1
- package/lib/esm/events-module/DecafStreamModule.js.map +0 -1
- package/lib/esm/events-module/EventsController.js.map +0 -1
- package/lib/esm/events-module/constant.js.map +0 -1
- package/lib/esm/events-module/index.js.map +0 -1
- package/lib/esm/events-module/utils.js.map +0 -1
- package/lib/esm/factory/NestBootstraper.js.map +0 -1
- package/lib/esm/factory/errors/cors.js.map +0 -1
- package/lib/esm/factory/errors/index.js.map +0 -1
- package/lib/esm/factory/errors/throttling.js.map +0 -1
- package/lib/esm/factory/exceptions/DecafErrorFilter.js.map +0 -1
- package/lib/esm/factory/exceptions/decorators.js.map +0 -1
- package/lib/esm/factory/exceptions/index.js.map +0 -1
- package/lib/esm/factory/index.js.map +0 -1
- package/lib/esm/factory/openapi/DtoBuilder.js.map +0 -1
- package/lib/esm/factory/openapi/SwaggerBuilder.js.map +0 -1
- package/lib/esm/factory/openapi/SwaggerCustomUI.js.map +0 -1
- package/lib/esm/factory/openapi/constants.js.map +0 -1
- package/lib/esm/factory/openapi/index.js.map +0 -1
- package/lib/esm/index.js.map +0 -1
- package/lib/esm/interceptors/AuthInterceptor.js +0 -43
- package/lib/esm/interceptors/AuthInterceptor.js.map +0 -1
- package/lib/esm/interceptors/DecafRequestHandlerInterceptor.js.map +0 -1
- package/lib/esm/interceptors/context.js +0 -12
- package/lib/esm/interceptors/context.js.map +0 -1
- package/lib/esm/interceptors/index.js.map +0 -1
- package/lib/esm/migrations/index.js +0 -18
- package/lib/esm/migrations/index.js.map +0 -1
- package/lib/esm/migrations/migration-module.js +0 -52
- package/lib/esm/migrations/migration-module.js.map +0 -1
- package/lib/esm/migrations/migration-options.js +0 -7
- package/lib/esm/migrations/migration-options.js.map +0 -1
- package/lib/esm/module.js.map +0 -1
- package/lib/esm/overrides/Adapter.js.map +0 -1
- package/lib/esm/overrides/ModelBuilderExtensions.js.map +0 -1
- package/lib/esm/overrides/constants.js.map +0 -1
- package/lib/esm/overrides/decoration.js.map +0 -1
- package/lib/esm/overrides/helpers.js.map +0 -1
- package/lib/esm/overrides/index.js.map +0 -1
- package/lib/esm/overrides/overrides.js.map +0 -1
- package/lib/esm/ram/RamRequestTransformer.js +0 -21
- package/lib/esm/ram/RamRequestTransformer.js.map +0 -1
- package/lib/esm/ram/index.js +0 -2
- package/lib/esm/ram/index.js.map +0 -1
- package/lib/esm/request/DecafAuthHandler.js.map +0 -1
- package/lib/esm/request/DecafHandlerExecutor.js.map +0 -1
- package/lib/esm/request/DecafRequestContext.js.map +0 -1
- package/lib/esm/request/DecafResponseInterceptor.js.map +0 -1
- package/lib/esm/request/index.js.map +0 -1
- package/lib/esm/swagger-types.js.map +0 -1
- package/lib/esm/types.js.map +0 -1
- package/lib/esm/utils.js.map +0 -1
- package/lib/esm/webhooks/DecafWebhookModule.js.map +0 -1
- package/lib/esm/webhooks/controllers.js.map +0 -1
- package/lib/esm/webhooks/index.js.map +0 -1
- package/lib/esm/webhooks/types.js.map +0 -1
- package/lib/types/interceptors/context.d.cts +0 -6
- package/lib/types/interceptors/context.d.mts +0 -6
- package/lib/types/migrations/index.d.cts +0 -17
- package/lib/types/migrations/index.d.mts +0 -17
- package/lib/types/migrations/migration-module.d.cts +0 -15
- package/lib/types/migrations/migration-module.d.mts +0 -15
- package/lib/types/migrations/migration-options.d.cts +0 -11
- package/lib/types/migrations/migration-options.d.mts +0 -11
- package/lib/types/ram/RamRequestTransformer.d.cts +0 -5
- package/lib/types/ram/RamRequestTransformer.d.mts +0 -5
- package/lib/types/ram/index.d.cts +0 -1
- package/lib/types/ram/index.d.mts +0 -1
|
@@ -7,73 +7,23 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
|
-
var
|
|
11
|
-
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
-
};
|
|
10
|
+
var DynamicModelController_1;
|
|
13
11
|
import { Controller, Param, Query, Response } from "@nestjs/common";
|
|
14
|
-
import { ApiBadRequestResponse, ApiBody, ApiCreatedResponse, ApiExtraModels, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiParam, ApiQuery, ApiTags, ApiUnprocessableEntityResponse, getSchemaPath, } from "@nestjs/swagger";
|
|
12
|
+
import { ApiBadRequestResponse, ApiBody, ApiCreatedResponse, ApiExtraModels, ApiNoContentResponse, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiParam, ApiQuery, ApiTags, ApiUnprocessableEntityResponse, getSchemaPath, } from "@nestjs/swagger";
|
|
15
13
|
import { ModelService, OrderDirection, PersistenceKeys, PreparedStatementKeys, Repository, Service, } from "@decaf-ts/core";
|
|
16
14
|
import { Model } from "@decaf-ts/decorator-validation";
|
|
17
15
|
import { Logging, toKebabCase } from "@decaf-ts/logging";
|
|
18
16
|
import { BulkCrudOperationKeys, DBKeys, OperationKeys, ValidationError, } from "@decaf-ts/db-decorators";
|
|
19
17
|
import { Metadata } from "@decaf-ts/decoration";
|
|
20
18
|
import { ApiOperationFromModel, ApiParamsFromModel, DecafBody, DecafParams, DecafQuery, } from "./decorators/index.js";
|
|
19
|
+
import { HttpVerbToDecorator } from "./decorators/utils.js";
|
|
21
20
|
import { DecafRequestContext } from "./../request/index.js";
|
|
22
|
-
import { DECAF_ROUTE } from "./../constants.js";
|
|
23
|
-
import { applyApiDecorators, createRouteHandler, defineRouteMethod, getApiDecorators, resolvePersistenceMethod, } from "./utils.js";
|
|
21
|
+
import { DECAF_CONTROLLER_CONFIG, DECAF_ROUTE } from "./../constants.js";
|
|
24
22
|
import { Auth } from "./decorators/decorators.js";
|
|
25
23
|
import { DecafModelController } from "./../controllers.js";
|
|
26
24
|
import { DtoFor } from "./../factory/openapi/DtoBuilder.js";
|
|
27
25
|
import "./../overrides/index.js";
|
|
28
|
-
|
|
29
|
-
* @description
|
|
30
|
-
* Factory and utilities for generating dynamic NestJS controllers from Decaf {@link Model} definitions.
|
|
31
|
-
*
|
|
32
|
-
* @summary
|
|
33
|
-
* The `FromModelController` class provides the infrastructure necessary to automatically generate
|
|
34
|
-
* strongly-typed CRUD controllers based on a given {@link ModelConstructor}. It inspects metadata from
|
|
35
|
-
* the model, derives route paths, parameters, and generates a dynamic controller class at runtime with
|
|
36
|
-
* full support for querying, creation, update, and deletion of model entities through a {@link Repo}.
|
|
37
|
-
*
|
|
38
|
-
* @template T The {@link Model} type associated with the generated controller.
|
|
39
|
-
*
|
|
40
|
-
* @param ModelClazz The model class to generate the controller from.
|
|
41
|
-
*
|
|
42
|
-
* @class FromModelController
|
|
43
|
-
*
|
|
44
|
-
* @example
|
|
45
|
-
* ```ts
|
|
46
|
-
* // Given a Decaf Model:
|
|
47
|
-
* class User extends Model<User> {
|
|
48
|
-
* id!: string;
|
|
49
|
-
* name!: string;
|
|
50
|
-
* }
|
|
51
|
-
*
|
|
52
|
-
* // Register controller:
|
|
53
|
-
* const UserController = FromModelController.create(User);
|
|
54
|
-
*
|
|
55
|
-
* // NestJS will expose:
|
|
56
|
-
* // POST /user
|
|
57
|
-
* // GET /user/:id
|
|
58
|
-
* // GET /user/query/:method
|
|
59
|
-
* // PUT /user/:id
|
|
60
|
-
* // DELETE /user/:id
|
|
61
|
-
* ```
|
|
62
|
-
*
|
|
63
|
-
* @mermaid
|
|
64
|
-
* sequenceDiagram
|
|
65
|
-
* participant Client
|
|
66
|
-
* participant Controller
|
|
67
|
-
* participant Repo
|
|
68
|
-
* participant DB
|
|
69
|
-
*
|
|
70
|
-
* Client->>Controller: HTTP Request
|
|
71
|
-
* Controller->>Repo: Resolve repository for Model
|
|
72
|
-
* Repo->>DB: Execute DB operation
|
|
73
|
-
* DB-->>Repo: DB Result
|
|
74
|
-
* Repo-->>Controller: Model Instance(s)
|
|
75
|
-
* Controller-->>Client: JSON Response
|
|
76
|
-
*/
|
|
26
|
+
import { ModelControllerFactory, } from "@decaf-ts/for-http/server";
|
|
77
27
|
export class FromModelController {
|
|
78
28
|
static { this.log = Logging.for(FromModelController.name); }
|
|
79
29
|
static getPersistence(ModelClazz) {
|
|
@@ -83,20 +33,17 @@ export class FromModelController {
|
|
|
83
33
|
catch (e) {
|
|
84
34
|
try {
|
|
85
35
|
return ModelService.getService(ModelClazz);
|
|
86
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
87
36
|
}
|
|
88
|
-
catch (
|
|
37
|
+
catch (e2) {
|
|
89
38
|
return Repository.forModel(ModelClazz);
|
|
90
39
|
}
|
|
91
40
|
}
|
|
92
41
|
}
|
|
93
42
|
static createQueryRoutesFromRepository(persistence, prefix = PersistenceKeys.QUERY) {
|
|
94
|
-
const log = FromModelController.log.for(FromModelController.createQueryRoutesFromRepository);
|
|
95
43
|
const repo = persistence instanceof ModelService ? persistence.repo : persistence;
|
|
96
44
|
const ModelConstr = repo.class;
|
|
97
45
|
const queryMethods = Metadata.get(repo.constructor, Metadata.key(PersistenceKeys.QUERY)) ?? {};
|
|
98
46
|
const routeMethods = Metadata.get(persistence.constructor, Metadata.key(DECAF_ROUTE)) ?? {};
|
|
99
|
-
// create base class
|
|
100
47
|
class QueryController extends DecafModelController {
|
|
101
48
|
get class() {
|
|
102
49
|
throw new Error("Method not implemented.");
|
|
@@ -106,733 +53,684 @@ export class FromModelController {
|
|
|
106
53
|
}
|
|
107
54
|
}
|
|
108
55
|
for (const [methodName, params] of Object.entries(routeMethods)) {
|
|
109
|
-
// regex to trim slashes from start and end
|
|
110
56
|
const routePath = [params.path.replace(/^\/+|\/+$/g, "")]
|
|
111
57
|
.filter((segment) => segment && segment.trim())
|
|
112
58
|
.join("/");
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
throw new Error(message);
|
|
119
|
-
}
|
|
120
|
-
const descriptor = defineRouteMethod(QueryController, methodName, handler);
|
|
121
|
-
if (descriptor) {
|
|
122
|
-
const decorators = getApiDecorators(methodName, routePath, params.httpMethod);
|
|
123
|
-
applyApiDecorators(QueryController, methodName, descriptor, decorators);
|
|
124
|
-
}
|
|
59
|
+
const handler = FromModelController.createComplexQueryHandler(methodName);
|
|
60
|
+
FromModelController.defineMethod(QueryController, methodName, handler);
|
|
61
|
+
const httpDecorator = HttpVerbToDecorator(params.httpMethod)(routePath || undefined);
|
|
62
|
+
const decorators = FromModelController.getQueryDecorators(methodName, routePath, params.httpMethod);
|
|
63
|
+
FromModelController.applyDecorators(QueryController, methodName, [httpDecorator, ...decorators]);
|
|
125
64
|
}
|
|
126
65
|
for (const [methodName, objValues] of Object.entries(queryMethods)) {
|
|
127
66
|
const fields = objValues.fields ?? [];
|
|
128
67
|
const routePath = [prefix, methodName, ...fields.map((f) => `:${f}`)]
|
|
129
68
|
.filter((segment) => segment && segment.trim())
|
|
130
69
|
.join("/");
|
|
131
|
-
const handler =
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
70
|
+
const handler = FromModelController.createComplexQueryHandler(methodName);
|
|
71
|
+
FromModelController.defineMethod(QueryController, methodName, handler);
|
|
72
|
+
const httpDecorator = HttpVerbToDecorator("GET")(routePath || undefined);
|
|
73
|
+
const decorators = FromModelController.getQueryDecorators(methodName, routePath, "GET", true);
|
|
74
|
+
FromModelController.applyDecorators(QueryController, methodName, [httpDecorator, ...decorators]);
|
|
137
75
|
}
|
|
138
76
|
return QueryController;
|
|
139
77
|
}
|
|
140
|
-
static create(ModelConstr) {
|
|
78
|
+
static create(ModelConstr, moduleConfigOverrides) {
|
|
141
79
|
const log = FromModelController.log.for(FromModelController.create);
|
|
142
80
|
const tableName = Model.tableName(ModelConstr);
|
|
143
81
|
const routePath = toKebabCase(tableName);
|
|
144
82
|
const modelClazzName = ModelConstr.name;
|
|
145
83
|
const persistence = FromModelController.getPersistence(ModelConstr);
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
);
|
|
151
|
-
|
|
84
|
+
// When persistence is a ModelService, the @query/@route metadata lives on
|
|
85
|
+
// the underlying repository class (custom repo), not on ModelService itself.
|
|
86
|
+
// Pass the repo to the factory so addComplexQueries() can discover them.
|
|
87
|
+
const factoryPersistence = persistence instanceof ModelService ? persistence.repo : persistence;
|
|
88
|
+
const decoratorConfig = Metadata.get(ModelConstr, Metadata.key(DECAF_CONTROLLER_CONFIG));
|
|
89
|
+
const moduleOverride = moduleConfigOverrides?.[ModelConstr.name];
|
|
90
|
+
const mergedConfig = {
|
|
91
|
+
...(decoratorConfig || {}),
|
|
92
|
+
...(moduleOverride || {}),
|
|
93
|
+
};
|
|
94
|
+
const FactoryController = ModelControllerFactory.create(ModelConstr, factoryPersistence, mergedConfig);
|
|
95
|
+
const factoryRoutes = FactoryController.__routes__;
|
|
96
|
+
const { getPK, apiProperties, path: pkPath } = FromModelController.getRouteParametersFromModel(ModelConstr);
|
|
97
|
+
log.debug(`Creating controller for model: ${modelClazzName} with ${factoryRoutes?.length ?? 0} factory routes`);
|
|
98
|
+
let DynamicModelController = DynamicModelController_1 = class DynamicModelController extends DecafModelController {
|
|
152
99
|
static get class() {
|
|
153
100
|
return ModelConstr;
|
|
154
101
|
}
|
|
155
102
|
get class() {
|
|
156
103
|
return ModelConstr;
|
|
157
|
-
// return DynamicModelController.class;
|
|
158
104
|
}
|
|
159
105
|
constructor(clientContext) {
|
|
160
|
-
super(clientContext);
|
|
106
|
+
super(clientContext, DynamicModelController_1.name);
|
|
161
107
|
this.pk = Model.pk(ModelConstr);
|
|
162
108
|
log.info(`Registering dynamic controller for model: ${this.class.name} route: /${routePath}`);
|
|
163
109
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
return
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
async page(value, details) {
|
|
197
|
-
const { ctx } = (await this.logCtx([], PreparedStatementKeys.PAGE, true)).for(this.page);
|
|
198
|
-
const { direction = OrderDirection.ASC, limit, offset, bookmark, } = details;
|
|
199
|
-
const ref = {
|
|
200
|
-
offset: offset ?? 1,
|
|
201
|
-
limit: limit ?? 10,
|
|
202
|
-
bookmark,
|
|
203
|
-
};
|
|
204
|
-
return resolvePersistenceMethod(this.persistence(ctx), this.page.name, value, direction, ref, ctx);
|
|
205
|
-
}
|
|
206
|
-
async findOneBy(key, value) {
|
|
207
|
-
const { ctx } = (await this.logCtx([], PreparedStatementKeys.FIND_ONE_BY, true)).for(this.findOneBy);
|
|
208
|
-
return this.persistence(ctx).findOneBy(key, value, ctx);
|
|
209
|
-
}
|
|
210
|
-
async findBy(key, value, details) {
|
|
211
|
-
const { ctx } = (await this.logCtx([], PreparedStatementKeys.FIND_BY, true)).for(this.findBy);
|
|
212
|
-
return this.persistence(ctx)
|
|
213
|
-
.for(ctx.toOverrides())
|
|
214
|
-
.findBy(key, value, ctx);
|
|
215
|
-
}
|
|
216
|
-
async statement(name, args, details) {
|
|
217
|
-
const { ctx } = (await this.logCtx([], PersistenceKeys.STATEMENT, true)).for(this.statement);
|
|
218
|
-
const { direction, offset, limit, bookmark } = details;
|
|
219
|
-
args = args.map((a) => (typeof a === "string" ? parseInt(a) : a) || a);
|
|
220
|
-
const pathDirection = args.length > 1 ? args[1] : undefined;
|
|
221
|
-
const resolvedDirection = (direction ?? pathDirection);
|
|
222
|
-
if (resolvedDirection && args.length > 1)
|
|
223
|
-
args[1] = resolvedDirection;
|
|
224
|
-
switch (name) {
|
|
225
|
-
case PreparedStatementKeys.FIND:
|
|
226
|
-
case PreparedStatementKeys.FIND_BY:
|
|
227
|
-
break;
|
|
228
|
-
case PreparedStatementKeys.LIST_BY:
|
|
229
|
-
args.push(direction);
|
|
230
|
-
break;
|
|
231
|
-
case PreparedStatementKeys.PAGE:
|
|
232
|
-
case PreparedStatementKeys.PAGE_BY:
|
|
233
|
-
args = [
|
|
234
|
-
args[0],
|
|
235
|
-
resolvedDirection,
|
|
236
|
-
{
|
|
237
|
-
limit: limit,
|
|
238
|
-
offset: offset,
|
|
239
|
-
bookmark: bookmark,
|
|
240
|
-
},
|
|
241
|
-
];
|
|
242
|
-
break;
|
|
243
|
-
case PreparedStatementKeys.FIND_ONE_BY:
|
|
244
|
-
break;
|
|
245
|
-
case PreparedStatementKeys.COUNT_OF:
|
|
246
|
-
case PreparedStatementKeys.MAX_OF:
|
|
247
|
-
case PreparedStatementKeys.MIN_OF:
|
|
248
|
-
case PreparedStatementKeys.AVG_OF:
|
|
249
|
-
case PreparedStatementKeys.SUM_OF:
|
|
250
|
-
case PreparedStatementKeys.DISTINCT_OF:
|
|
251
|
-
case PreparedStatementKeys.GROUP_OF:
|
|
252
|
-
// Aggregation methods - args[0] is the field name (if provided)
|
|
253
|
-
break;
|
|
254
|
-
}
|
|
255
|
-
return this.persistence(ctx).statement(name, ...args, ctx);
|
|
256
|
-
}
|
|
257
|
-
//
|
|
258
|
-
// @ApiOperationFromModel(ModelConstr, "GET", "countOf")
|
|
259
|
-
// @ApiOperation({ summary: `Count all ${modelClazzName} records.` })
|
|
260
|
-
// @ApiOkResponse({
|
|
261
|
-
// description: `Count of ${modelClazzName} records.`,
|
|
262
|
-
// type: Number,
|
|
263
|
-
// })
|
|
264
|
-
// async countOf() {
|
|
265
|
-
// const { ctx } = (
|
|
266
|
-
// await this.logCtx([], PreparedStatementKeys.COUNT_OF, true)
|
|
267
|
-
// ).for(this.countOf);
|
|
268
|
-
// return this.persistence(ctx).statement(
|
|
269
|
-
// PreparedStatementKeys.COUNT_OF,
|
|
270
|
-
// ctx
|
|
271
|
-
// );
|
|
272
|
-
// }
|
|
273
|
-
//
|
|
274
|
-
// @ApiOperationFromModel(ModelConstr, "GET", "countOf/:field")
|
|
275
|
-
// @ApiOperation({ summary: `Count ${modelClazzName} records by field.` })
|
|
276
|
-
// @ApiParam({ name: "field", description: "The field to count" })
|
|
277
|
-
// @ApiOkResponse({
|
|
278
|
-
// description: `Count of ${modelClazzName} records.`,
|
|
279
|
-
// type: Number,
|
|
280
|
-
// })
|
|
281
|
-
// async countOfField(@Param("field") field: string) {
|
|
282
|
-
// const { ctx } = (
|
|
283
|
-
// await this.logCtx([], PreparedStatementKeys.COUNT_OF, true)
|
|
284
|
-
// ).for(this.countOfField);
|
|
285
|
-
// return this.persistence(ctx).statement(
|
|
286
|
-
// PreparedStatementKeys.COUNT_OF,
|
|
287
|
-
// field,
|
|
288
|
-
// ctx
|
|
289
|
-
// );
|
|
290
|
-
// }
|
|
291
|
-
//
|
|
292
|
-
// @ApiOperationFromModel(ModelConstr, "GET", "maxOf/:field")
|
|
293
|
-
// @ApiOperation({
|
|
294
|
-
// summary: `Find maximum value of a field in ${modelClazzName}.`,
|
|
295
|
-
// })
|
|
296
|
-
// @ApiParam({ name: "field", description: "The field to find max of" })
|
|
297
|
-
// @ApiOkResponse({
|
|
298
|
-
// description: `Maximum value of the field in ${modelClazzName}.`,
|
|
299
|
-
// })
|
|
300
|
-
// async maxOf(@Param("field") field: string) {
|
|
301
|
-
// const { ctx } = (
|
|
302
|
-
// await this.logCtx([], PreparedStatementKeys.MAX_OF, true)
|
|
303
|
-
// ).for(this.maxOf);
|
|
304
|
-
// return this.persistence(ctx).statement(
|
|
305
|
-
// PreparedStatementKeys.MAX_OF,
|
|
306
|
-
// field,
|
|
307
|
-
// ctx
|
|
308
|
-
// );
|
|
309
|
-
// }
|
|
310
|
-
//
|
|
311
|
-
// @ApiOperationFromModel(ModelConstr, "GET", "minOf/:field")
|
|
312
|
-
// @ApiOperation({
|
|
313
|
-
// summary: `Find minimum value of a field in ${modelClazzName}.`,
|
|
314
|
-
// })
|
|
315
|
-
// @ApiParam({ name: "field", description: "The field to find min of" })
|
|
316
|
-
// @ApiOkResponse({
|
|
317
|
-
// description: `Minimum value of the field in ${modelClazzName}.`,
|
|
318
|
-
// })
|
|
319
|
-
// async minOf(@Param("field") field: string) {
|
|
320
|
-
// const { ctx } = (
|
|
321
|
-
// await this.logCtx([], PreparedStatementKeys.MIN_OF, true)
|
|
322
|
-
// ).for(this.minOf);
|
|
323
|
-
// return this.persistence(ctx).statement(
|
|
324
|
-
// PreparedStatementKeys.MIN_OF,
|
|
325
|
-
// field,
|
|
326
|
-
// ctx
|
|
327
|
-
// );
|
|
328
|
-
// }
|
|
329
|
-
//
|
|
330
|
-
// @ApiOperationFromModel(ModelConstr, "GET", "avgOf/:field")
|
|
331
|
-
// @ApiOperation({
|
|
332
|
-
// summary: `Calculate average of a field in ${modelClazzName}.`,
|
|
333
|
-
// })
|
|
334
|
-
// @ApiParam({
|
|
335
|
-
// name: "field",
|
|
336
|
-
// description: "The field to calculate average of",
|
|
337
|
-
// })
|
|
338
|
-
// @ApiOkResponse({
|
|
339
|
-
// description: `Average value of the field in ${modelClazzName}.`,
|
|
340
|
-
// type: Number,
|
|
341
|
-
// })
|
|
342
|
-
// async avgOf(@Param("field") field: string) {
|
|
343
|
-
// const { ctx } = (
|
|
344
|
-
// await this.logCtx([], PreparedStatementKeys.AVG_OF, true)
|
|
345
|
-
// ).for(this.avgOf);
|
|
346
|
-
// return this.persistence(ctx).statement(
|
|
347
|
-
// PreparedStatementKeys.AVG_OF,
|
|
348
|
-
// field,
|
|
349
|
-
// ctx
|
|
350
|
-
// );
|
|
351
|
-
// }
|
|
352
|
-
//
|
|
353
|
-
// @ApiOperationFromModel(ModelConstr, "GET", "sumOf/:field")
|
|
354
|
-
// @ApiOperation({
|
|
355
|
-
// summary: `Calculate sum of a field in ${modelClazzName}.`,
|
|
356
|
-
// })
|
|
357
|
-
// @ApiParam({ name: "field", description: "The field to calculate sum of" })
|
|
358
|
-
// @ApiOkResponse({
|
|
359
|
-
// description: `Sum of the field in ${modelClazzName}.`,
|
|
360
|
-
// type: Number,
|
|
361
|
-
// })
|
|
362
|
-
// async sumOf(@Param("field") field: string) {
|
|
363
|
-
// const { ctx } = (
|
|
364
|
-
// await this.logCtx([], PreparedStatementKeys.SUM_OF, true)
|
|
365
|
-
// ).for(this.sumOf);
|
|
366
|
-
// return this.persistence(ctx).statement(
|
|
367
|
-
// PreparedStatementKeys.SUM_OF,
|
|
368
|
-
// field,
|
|
369
|
-
// ctx
|
|
370
|
-
// );
|
|
371
|
-
// }
|
|
372
|
-
//
|
|
373
|
-
// @ApiOperationFromModel(ModelConstr, "GET", "distinctOf/:field")
|
|
374
|
-
// @ApiOperation({
|
|
375
|
-
// summary: `Find distinct values of a field in ${modelClazzName}.`,
|
|
376
|
-
// })
|
|
377
|
-
// @ApiParam({
|
|
378
|
-
// name: "field",
|
|
379
|
-
// description: "The field to find distinct values of",
|
|
380
|
-
// })
|
|
381
|
-
// @ApiOkResponse({
|
|
382
|
-
// description: `Distinct values of the field in ${modelClazzName}.`,
|
|
383
|
-
// type: [String],
|
|
384
|
-
// })
|
|
385
|
-
// async distinctOf(@Param("field") field: string) {
|
|
386
|
-
// const { ctx } = (
|
|
387
|
-
// await this.logCtx([], PreparedStatementKeys.DISTINCT_OF, true)
|
|
388
|
-
// ).for(this.distinctOf);
|
|
389
|
-
// return this.persistence(ctx).statement(
|
|
390
|
-
// PreparedStatementKeys.DISTINCT_OF,
|
|
391
|
-
// field,
|
|
392
|
-
// ctx
|
|
393
|
-
// );
|
|
394
|
-
// }
|
|
395
|
-
//
|
|
396
|
-
// @ApiOperationFromModel(ModelConstr, "GET", "groupOf/:field")
|
|
397
|
-
// @ApiOperation({
|
|
398
|
-
// summary: `Group ${modelClazzName} records by a field.`,
|
|
399
|
-
// })
|
|
400
|
-
// @ApiParam({ name: "field", description: "The field to group by" })
|
|
401
|
-
// @ApiOkResponse({
|
|
402
|
-
// description: `${modelClazzName} records grouped by the field.`,
|
|
403
|
-
// })
|
|
404
|
-
// async groupOf(@Param("field") field: string) {
|
|
405
|
-
// const { ctx } = (
|
|
406
|
-
// await this.logCtx([], PreparedStatementKeys.GROUP_OF, true)
|
|
407
|
-
// ).for(this.groupOf);
|
|
408
|
-
// return this.persistence(ctx).statement(
|
|
409
|
-
// PreparedStatementKeys.GROUP_OF,
|
|
410
|
-
// field,
|
|
411
|
-
// ctx
|
|
412
|
-
// );
|
|
413
|
-
// }
|
|
414
|
-
async createAll(data, resp) {
|
|
415
|
-
const { ctx, log } = (await this.logCtx([], BulkCrudOperationKeys.CREATE_ALL, true)).for(this.createAll);
|
|
416
|
-
log.verbose(`creating new ${modelClazzName}`);
|
|
417
|
-
let created;
|
|
418
|
-
try {
|
|
419
|
-
created = await this.persistence(ctx).createAll(data.map((d) => new ModelConstr(d)), ctx);
|
|
420
|
-
}
|
|
421
|
-
catch (e) {
|
|
422
|
-
log.error(`Failed to create new ${modelClazzName}`, e);
|
|
423
|
-
throw e;
|
|
110
|
+
};
|
|
111
|
+
DynamicModelController = DynamicModelController_1 = __decorate([
|
|
112
|
+
Controller(routePath),
|
|
113
|
+
ApiTags(modelClazzName),
|
|
114
|
+
ApiExtraModels(ModelConstr),
|
|
115
|
+
Auth(ModelConstr),
|
|
116
|
+
__metadata("design:paramtypes", [DecafRequestContext])
|
|
117
|
+
], DynamicModelController);
|
|
118
|
+
if (factoryRoutes) {
|
|
119
|
+
const sortedRoutes = [...factoryRoutes].sort((a, b) => {
|
|
120
|
+
const aSegments = a.path.split("/").filter(Boolean);
|
|
121
|
+
const bSegments = b.path.split("/").filter(Boolean);
|
|
122
|
+
const aParamCount = aSegments.filter((s) => s.startsWith(":")).length;
|
|
123
|
+
const bParamCount = bSegments.filter((s) => s.startsWith(":")).length;
|
|
124
|
+
const aLiteralCount = aSegments.length - aParamCount;
|
|
125
|
+
const bLiteralCount = bSegments.length - bParamCount;
|
|
126
|
+
if (aLiteralCount !== bLiteralCount)
|
|
127
|
+
return bLiteralCount - aLiteralCount;
|
|
128
|
+
if (aParamCount !== bParamCount)
|
|
129
|
+
return aParamCount - bParamCount;
|
|
130
|
+
return 0;
|
|
131
|
+
});
|
|
132
|
+
for (const route of sortedRoutes) {
|
|
133
|
+
const registration = FromModelController.matchRoute(route, pkPath, apiProperties, getPK, ModelConstr, modelClazzName, factoryPersistence);
|
|
134
|
+
if (!registration)
|
|
135
|
+
continue;
|
|
136
|
+
const { methodName, handler, decorators, paramDecorators } = registration;
|
|
137
|
+
const descriptor = FromModelController.defineMethod(DynamicModelController, methodName, handler);
|
|
138
|
+
if (descriptor) {
|
|
139
|
+
const httpDecorator = HttpVerbToDecorator(route.method)(route.path.replace(/^\/+|\/+$/g, "") || undefined);
|
|
140
|
+
FromModelController.applyDecorators(DynamicModelController, methodName, [httpDecorator, ...decorators], paramDecorators);
|
|
424
141
|
}
|
|
425
|
-
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return DynamicModelController;
|
|
145
|
+
}
|
|
146
|
+
static getRouteParametersFromModel(ModelClazz) {
|
|
147
|
+
const pk = Model.pk(ModelClazz);
|
|
148
|
+
const composed = Metadata.get(ModelClazz, Metadata.key(DBKeys.COMPOSED, pk));
|
|
149
|
+
const composedKeys = composed?.args ?? [];
|
|
150
|
+
const uniqueKeys = Array.isArray(composedKeys) && composedKeys.length > 0
|
|
151
|
+
? Array.from(new Set([...composedKeys]))
|
|
152
|
+
: Array.from(new Set([pk]));
|
|
153
|
+
const description = Metadata.description(ModelClazz) ?? "";
|
|
154
|
+
const path = `:${uniqueKeys.join("/:")}`;
|
|
155
|
+
const apiProperties = uniqueKeys.map((key) => {
|
|
156
|
+
return {
|
|
157
|
+
name: key,
|
|
158
|
+
description: Metadata.description(ModelClazz, key),
|
|
159
|
+
required: true,
|
|
160
|
+
type: String,
|
|
161
|
+
};
|
|
162
|
+
});
|
|
163
|
+
return {
|
|
164
|
+
path,
|
|
165
|
+
description,
|
|
166
|
+
apiProperties,
|
|
167
|
+
getPK: (...params) => composed?.separator ? params.join(composed.separator) : params.join(""),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
static defineMethod(target, methodName, handler) {
|
|
171
|
+
Object.defineProperty(target.prototype || target, methodName, {
|
|
172
|
+
value: handler,
|
|
173
|
+
writable: false,
|
|
174
|
+
configurable: true,
|
|
175
|
+
enumerable: false,
|
|
176
|
+
});
|
|
177
|
+
return Object.getOwnPropertyDescriptor(target.prototype || target, methodName);
|
|
178
|
+
}
|
|
179
|
+
static applyDecorators(target, methodName, methodDecorators, paramDecorators = []) {
|
|
180
|
+
const proto = target?.prototype ?? target;
|
|
181
|
+
const descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
|
|
182
|
+
methodDecorators.forEach((d) => d(proto, methodName, descriptor));
|
|
183
|
+
paramDecorators.forEach(({ decorator, index }) => decorator(proto, methodName, index));
|
|
184
|
+
}
|
|
185
|
+
static matchRoute(route, pkPath, apiProperties, getPK, ModelConstr, modelClazzName, persistence) {
|
|
186
|
+
const { method, path } = route;
|
|
187
|
+
const normalizedPath = path.replace(/^\/+|\/+$/g, "");
|
|
188
|
+
if (method === "POST" && normalizedPath === "") {
|
|
189
|
+
return FromModelController.createRegistration("create", FromModelController.createCreateHandler(ModelConstr, modelClazzName), FromModelController.createCreateDecorators(ModelConstr, modelClazzName), [{ decorator: DecafBody(), index: 0 }, { decorator: Response({ passthrough: true }), index: 1 }]);
|
|
190
|
+
}
|
|
191
|
+
if (method === "POST" && normalizedPath === "bulk") {
|
|
192
|
+
return FromModelController.createRegistration("createAll", FromModelController.createBulkCreateHandler(ModelConstr, modelClazzName), FromModelController.bulkCreateDecorators(ModelConstr, modelClazzName), [{ decorator: DecafBody(), index: 0 }, { decorator: Response({ passthrough: true }), index: 1 }]);
|
|
193
|
+
}
|
|
194
|
+
if (method === "GET" && normalizedPath === "bulk") {
|
|
195
|
+
return FromModelController.createRegistration("readAll", FromModelController.createBulkReadHandler(modelClazzName), FromModelController.bulkReadDecorators(ModelConstr, modelClazzName), [{ decorator: Query("ids"), index: 0 }]);
|
|
196
|
+
}
|
|
197
|
+
if (method === "PUT" && normalizedPath === "bulk") {
|
|
198
|
+
return FromModelController.createRegistration("updateAll", FromModelController.createBulkUpdateHandler(modelClazzName), FromModelController.bulkUpdateDecorators(ModelConstr, modelClazzName, apiProperties), [{ decorator: DecafBody(), index: 0 }, { decorator: Response({ passthrough: true }), index: 1 }]);
|
|
199
|
+
}
|
|
200
|
+
if (method === "DELETE" && normalizedPath === "bulk") {
|
|
201
|
+
return FromModelController.createRegistration("deleteAll", FromModelController.createBulkDeleteHandler(modelClazzName), FromModelController.bulkDeleteDecorators(ModelConstr, modelClazzName, apiProperties), [{ decorator: Query("ids"), index: 0 }, { decorator: Response({ passthrough: true }), index: 1 }]);
|
|
202
|
+
}
|
|
203
|
+
if (method === "GET" && normalizedPath === pkPath) {
|
|
204
|
+
return FromModelController.createRegistration("read", FromModelController.createReadHandler(getPK, modelClazzName), FromModelController.readDecorators(ModelConstr, modelClazzName, apiProperties, pkPath), [{ decorator: DecafParams(apiProperties), index: 0 }]);
|
|
205
|
+
}
|
|
206
|
+
if (method === "PUT" && normalizedPath === pkPath) {
|
|
207
|
+
return FromModelController.createRegistration("update", FromModelController.createUpdateHandler(getPK, ModelConstr, modelClazzName), FromModelController.updateDecorators(ModelConstr, modelClazzName, apiProperties, pkPath), [
|
|
208
|
+
{ decorator: DecafParams(apiProperties), index: 0 },
|
|
209
|
+
{ decorator: DecafBody(), index: 1 },
|
|
210
|
+
{ decorator: Response({ passthrough: true }), index: 2 },
|
|
211
|
+
]);
|
|
212
|
+
}
|
|
213
|
+
if (method === "DELETE" && normalizedPath === pkPath) {
|
|
214
|
+
return FromModelController.createRegistration("delete", FromModelController.createDeleteHandler(getPK, modelClazzName), FromModelController.deleteDecorators(ModelConstr, modelClazzName, apiProperties, pkPath), [
|
|
215
|
+
{ decorator: DecafParams(apiProperties), index: 0 },
|
|
216
|
+
{ decorator: Response({ passthrough: true }), index: 1 },
|
|
217
|
+
]);
|
|
218
|
+
}
|
|
219
|
+
// Composed PK fallback routes (filterEmpty) — path differs from pkPath
|
|
220
|
+
const fallbackSegments = normalizedPath.split("/").filter(Boolean);
|
|
221
|
+
const isAllParams = fallbackSegments.length > 0 && fallbackSegments.every((s) => s.startsWith(":"));
|
|
222
|
+
if (isAllParams && normalizedPath !== pkPath) {
|
|
223
|
+
const fallbackApiProps = fallbackSegments.map((s) => s.slice(1)).map((name) => ({
|
|
224
|
+
name,
|
|
225
|
+
description: `${name} parameter`,
|
|
226
|
+
required: true,
|
|
227
|
+
type: String,
|
|
228
|
+
}));
|
|
229
|
+
const suffix = fallbackSegments.map((s) => s.slice(1)).join("And");
|
|
230
|
+
if (method === "GET") {
|
|
231
|
+
return FromModelController.createRegistration(`readBy${suffix}`, FromModelController.createReadHandler(getPK, modelClazzName), FromModelController.readDecorators(ModelConstr, modelClazzName, fallbackApiProps, normalizedPath), [{ decorator: DecafParams(fallbackApiProps), index: 0 }]);
|
|
232
|
+
}
|
|
233
|
+
if (method === "PUT") {
|
|
234
|
+
return FromModelController.createRegistration(`updateBy${suffix}`, FromModelController.createUpdateHandler(getPK, ModelConstr, modelClazzName), FromModelController.updateDecorators(ModelConstr, modelClazzName, fallbackApiProps, normalizedPath), [
|
|
235
|
+
{ decorator: DecafParams(fallbackApiProps), index: 0 },
|
|
236
|
+
{ decorator: DecafBody(), index: 1 },
|
|
237
|
+
{ decorator: Response({ passthrough: true }), index: 2 },
|
|
238
|
+
]);
|
|
239
|
+
}
|
|
240
|
+
if (method === "DELETE") {
|
|
241
|
+
return FromModelController.createRegistration(`deleteBy${suffix}`, FromModelController.createDeleteHandler(getPK, modelClazzName), FromModelController.deleteDecorators(ModelConstr, modelClazzName, fallbackApiProps, normalizedPath), [
|
|
242
|
+
{ decorator: DecafParams(fallbackApiProps), index: 0 },
|
|
243
|
+
{ decorator: Response({ passthrough: true }), index: 1 },
|
|
244
|
+
]);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (method === "GET" && normalizedPath === "statement/:method/*args") {
|
|
248
|
+
return FromModelController.createRegistration("statement", FromModelController.createStatementHandler(modelClazzName), FromModelController.statementDecorators(ModelConstr, modelClazzName), [
|
|
249
|
+
{ decorator: Param("method"), index: 0 },
|
|
250
|
+
{ decorator: Param("args"), index: 1 },
|
|
251
|
+
{ decorator: DecafQuery(), index: 2 },
|
|
252
|
+
]);
|
|
253
|
+
}
|
|
254
|
+
const statementRoutes = {
|
|
255
|
+
"listBy/:key": PreparedStatementKeys.LIST_BY,
|
|
256
|
+
"paginateBy/:key/:page": PreparedStatementKeys.PAGE_BY,
|
|
257
|
+
"find/:value": PreparedStatementKeys.FIND,
|
|
258
|
+
"page/:value": PreparedStatementKeys.PAGE,
|
|
259
|
+
"findOneBy/:key/:value": PreparedStatementKeys.FIND_ONE_BY,
|
|
260
|
+
"findBy/:key/:value": PreparedStatementKeys.FIND_BY,
|
|
261
|
+
"countOf/:field": PreparedStatementKeys.COUNT_OF,
|
|
262
|
+
"maxOf/:field": PreparedStatementKeys.MAX_OF,
|
|
263
|
+
"minOf/:field": PreparedStatementKeys.MIN_OF,
|
|
264
|
+
"avgOf/:field": PreparedStatementKeys.AVG_OF,
|
|
265
|
+
"sumOf/:field": PreparedStatementKeys.SUM_OF,
|
|
266
|
+
"distinctOf/:field": PreparedStatementKeys.DISTINCT_OF,
|
|
267
|
+
"groupOf/:field": PreparedStatementKeys.GROUP_OF,
|
|
268
|
+
};
|
|
269
|
+
const statementKey = statementRoutes[normalizedPath];
|
|
270
|
+
if (statementKey && method === "GET") {
|
|
271
|
+
return FromModelController.createRegistration(FromModelController.statementMethodName(normalizedPath), FromModelController.createStatementShortcutHandler(statementKey, modelClazzName), FromModelController.statementShortcutDecorators(ModelConstr, modelClazzName, normalizedPath, statementKey), FromModelController.statementShortcutParams(normalizedPath));
|
|
272
|
+
}
|
|
273
|
+
if (method === "GET" && normalizedPath.startsWith("query/")) {
|
|
274
|
+
const queryMethod = normalizedPath.replace(/^query\//, "").split("/")[0];
|
|
275
|
+
return FromModelController.createRegistration(queryMethod, FromModelController.createComplexQueryHandler(queryMethod), FromModelController.getQueryDecorators(queryMethod, normalizedPath, "GET", true), FromModelController.complexQueryParams(normalizedPath));
|
|
276
|
+
}
|
|
277
|
+
// Fallback for custom @route() paths (e.g. "metadata/for-product/:productCode")
|
|
278
|
+
const pathSegments = normalizedPath.split("/").filter(Boolean);
|
|
279
|
+
const knownPrefixes = new Set([
|
|
280
|
+
"listBy", "findBy", "findByPaginate", "findOneBy", "paginateBy",
|
|
281
|
+
"find", "page", "countOf", "maxOf", "minOf", "avgOf", "sumOf",
|
|
282
|
+
"distinctOf", "groupOf", "statement", "bulk", "query",
|
|
283
|
+
]);
|
|
284
|
+
if (pathSegments.length > 0 &&
|
|
285
|
+
!normalizedPath.startsWith("query/") &&
|
|
286
|
+
!knownPrefixes.has(pathSegments[0])) {
|
|
287
|
+
// Look up the actual method name from @route metadata by matching the path.
|
|
288
|
+
// The @route decorator stores { path, httpMethod, handler } keyed by method name
|
|
289
|
+
// on the repository constructor.
|
|
290
|
+
const routeMetadata = Metadata.get(persistence?.constructor, Metadata.key(DECAF_ROUTE)) ?? {};
|
|
291
|
+
const matchedEntry = Object.entries(routeMetadata).find(([, info]) => info &&
|
|
292
|
+
typeof info === "object" &&
|
|
293
|
+
info.path?.replace(/^\/+|\/+$/g, "") === normalizedPath);
|
|
294
|
+
const actualMethodName = matchedEntry?.[0] || pathSegments[0];
|
|
295
|
+
const paramSegments = pathSegments.filter((s) => s.startsWith(":"));
|
|
296
|
+
const apiPathParams = paramSegments.map((s) => s.slice(1)).map((name) => ({
|
|
297
|
+
name,
|
|
298
|
+
description: `${name} parameter for the query`,
|
|
299
|
+
required: true,
|
|
300
|
+
type: String,
|
|
301
|
+
}));
|
|
302
|
+
return FromModelController.createRegistration(actualMethodName, FromModelController.createCustomRouteHandler(actualMethodName), [
|
|
303
|
+
...apiPathParams.map((p) => ApiParam(p)),
|
|
304
|
+
ApiOperation({ summary: `Retrieve records using "${actualMethodName}".` }),
|
|
305
|
+
ApiOkResponse({ description: "Result successfully retrieved." }),
|
|
306
|
+
ApiNoContentResponse({ description: "No content returned by the method." }),
|
|
307
|
+
], FromModelController.complexQueryParams(normalizedPath));
|
|
308
|
+
}
|
|
309
|
+
return undefined;
|
|
310
|
+
}
|
|
311
|
+
static createRegistration(methodName, handler, decorators, paramDecorators) {
|
|
312
|
+
return { methodName, handler, decorators, paramDecorators };
|
|
313
|
+
}
|
|
314
|
+
static statementMethodName(path) {
|
|
315
|
+
const firstSegment = path.split("/")[0];
|
|
316
|
+
return firstSegment;
|
|
317
|
+
}
|
|
318
|
+
static statementShortcutParams(path) {
|
|
319
|
+
const segments = path.split("/").filter((s) => s.startsWith(":"));
|
|
320
|
+
const params = [];
|
|
321
|
+
segments.forEach((seg, i) => {
|
|
322
|
+
const name = seg.replace(":", "");
|
|
323
|
+
params.push({ decorator: Param(name), index: i });
|
|
324
|
+
});
|
|
325
|
+
if (path.startsWith("listBy/") ||
|
|
326
|
+
path.startsWith("paginateBy/") ||
|
|
327
|
+
path.startsWith("find/") ||
|
|
328
|
+
path.startsWith("page/")) {
|
|
329
|
+
params.push({ decorator: DecafQuery(), index: segments.length });
|
|
330
|
+
}
|
|
331
|
+
return params;
|
|
332
|
+
}
|
|
333
|
+
static complexQueryParams(path) {
|
|
334
|
+
const segments = path.split("/").filter((s) => s.startsWith(":"));
|
|
335
|
+
const params = [];
|
|
336
|
+
segments.forEach((seg, i) => {
|
|
337
|
+
const name = seg.replace(":", "");
|
|
338
|
+
params.push({ decorator: Param(name), index: i });
|
|
339
|
+
});
|
|
340
|
+
if (path.startsWith("query/")) {
|
|
341
|
+
params.push({ decorator: DecafQuery(), index: segments.length });
|
|
342
|
+
}
|
|
343
|
+
return params;
|
|
344
|
+
}
|
|
345
|
+
static createCreateHandler(ModelConstr, modelClazzName) {
|
|
346
|
+
return async function create(data, resp) {
|
|
347
|
+
const { ctx, log } = (await this.logCtx([], OperationKeys.CREATE, true)).for(create);
|
|
348
|
+
log.verbose(`creating new ${modelClazzName}`);
|
|
349
|
+
let created;
|
|
350
|
+
try {
|
|
351
|
+
created = await this.persistence(ctx).create(data, ctx);
|
|
352
|
+
}
|
|
353
|
+
catch (e) {
|
|
354
|
+
log.error(`Failed to create new ${modelClazzName}`, e);
|
|
355
|
+
throw e;
|
|
356
|
+
}
|
|
357
|
+
log.info(`created new ${modelClazzName} with id ${created[this.pk]}`);
|
|
358
|
+
if (resp)
|
|
426
359
|
ctx.toResponse(resp);
|
|
427
|
-
|
|
360
|
+
return created;
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
static createBulkCreateHandler(ModelConstr, modelClazzName) {
|
|
364
|
+
return async function createAll(data, resp) {
|
|
365
|
+
const { ctx, log } = (await this.logCtx([], BulkCrudOperationKeys.CREATE_ALL, true)).for(createAll);
|
|
366
|
+
log.verbose(`creating new ${modelClazzName}`);
|
|
367
|
+
let created;
|
|
368
|
+
try {
|
|
369
|
+
created = await this.persistence(ctx).createAll(data.map((d) => new ModelConstr(d)), ctx);
|
|
428
370
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
created = await persistence.create(data, ctx);
|
|
436
|
-
}
|
|
437
|
-
catch (e) {
|
|
438
|
-
log.error(`Failed to create new ${modelClazzName}`, e);
|
|
439
|
-
throw e;
|
|
440
|
-
}
|
|
441
|
-
log.info(`created new ${modelClazzName} with id ${created[this.pk]}`);
|
|
371
|
+
catch (e) {
|
|
372
|
+
log.error(`Failed to create new ${modelClazzName}`, e);
|
|
373
|
+
throw e;
|
|
374
|
+
}
|
|
375
|
+
log.info(`created new ${modelClazzName} with id ${created[this.pk]}`);
|
|
376
|
+
if (resp)
|
|
442
377
|
ctx.toResponse(resp);
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
378
|
+
return created;
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
static createBulkReadHandler(modelClazzName) {
|
|
382
|
+
return async function readAll(ids) {
|
|
383
|
+
const { ctx, log } = (await this.logCtx([], BulkCrudOperationKeys.READ_ALL, true)).for(readAll);
|
|
384
|
+
const normalizedIds = Array.isArray(ids) ? ids : [ids];
|
|
385
|
+
let read;
|
|
386
|
+
try {
|
|
387
|
+
log.debug(`reading ${normalizedIds} ${modelClazzName}`);
|
|
388
|
+
read = await this.persistence(ctx).readAll(normalizedIds, ctx);
|
|
389
|
+
}
|
|
390
|
+
catch (e) {
|
|
391
|
+
log.error(`Failed to read ${modelClazzName}`, e);
|
|
392
|
+
throw e;
|
|
393
|
+
}
|
|
394
|
+
log.info(`read ${read.length} ${modelClazzName}`);
|
|
395
|
+
return read;
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
static createBulkUpdateHandler(modelClazzName) {
|
|
399
|
+
return async function updateAll(body, resp) {
|
|
400
|
+
const { ctx, log } = (await this.logCtx([], BulkCrudOperationKeys.UPDATE_ALL, true)).for(updateAll);
|
|
401
|
+
let updated;
|
|
402
|
+
try {
|
|
403
|
+
log.info(`updating ${body.length} ${modelClazzName}`);
|
|
404
|
+
updated = await this.persistence(ctx).updateAll(body, ctx);
|
|
405
|
+
}
|
|
406
|
+
catch (e) {
|
|
407
|
+
log.error(e);
|
|
408
|
+
throw e;
|
|
409
|
+
}
|
|
410
|
+
if (resp)
|
|
411
|
+
ctx.toResponse(resp);
|
|
412
|
+
return updated;
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
static createBulkDeleteHandler(modelClazzName) {
|
|
416
|
+
return async function deleteAll(ids, resp) {
|
|
417
|
+
const { ctx, log } = (await this.logCtx([], BulkCrudOperationKeys.DELETE_ALL, true)).for(deleteAll);
|
|
418
|
+
const normalizedIds = Array.isArray(ids) ? ids : [ids];
|
|
419
|
+
let read;
|
|
420
|
+
try {
|
|
421
|
+
log.debug(`deleting ${normalizedIds.length} ${modelClazzName}`);
|
|
422
|
+
read = await this.persistence(ctx).deleteAll(normalizedIds, ctx);
|
|
423
|
+
}
|
|
424
|
+
catch (e) {
|
|
425
|
+
log.error(`Failed to delete ${modelClazzName}`, e);
|
|
426
|
+
throw e;
|
|
427
|
+
}
|
|
428
|
+
log.info(`deleted ${read.length} ${modelClazzName}`);
|
|
429
|
+
if (resp)
|
|
430
|
+
ctx.toResponse(resp);
|
|
431
|
+
return read;
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
static createReadHandler(getPK, modelClazzName) {
|
|
435
|
+
return async function read(routeParams) {
|
|
436
|
+
const { ctx, log } = (await this.logCtx([], OperationKeys.READ, true)).for(read);
|
|
437
|
+
const id = getPK(...routeParams.valuesInOrder);
|
|
438
|
+
if (typeof id === "undefined")
|
|
439
|
+
throw new ValidationError(`No ${this.pk} provided`);
|
|
440
|
+
let readResult;
|
|
441
|
+
try {
|
|
442
|
+
log.debug(`reading ${modelClazzName} with ${this.pk} ${id}`);
|
|
443
|
+
readResult = await this.persistence(ctx).read(id, ctx);
|
|
444
|
+
}
|
|
445
|
+
catch (e) {
|
|
446
|
+
log.error(`Failed to read ${modelClazzName} with id ${id}`, e);
|
|
447
|
+
throw e;
|
|
448
|
+
}
|
|
449
|
+
log.info(`read ${modelClazzName} with id ${readResult[this.pk]}`);
|
|
450
|
+
return readResult;
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
static createUpdateHandler(getPK, ModelConstr, modelClazzName) {
|
|
454
|
+
return async function update(routeParams, body, resp) {
|
|
455
|
+
const { ctx, log } = (await this.logCtx([], OperationKeys.UPDATE, true)).for(update);
|
|
456
|
+
const id = getPK(...routeParams.valuesInOrder);
|
|
457
|
+
if (typeof id === "undefined")
|
|
458
|
+
throw new ValidationError(`No ${this.pk} provided`);
|
|
459
|
+
let updated;
|
|
460
|
+
try {
|
|
461
|
+
log.info(`updating ${modelClazzName} with ${this.pk} ${id}`);
|
|
462
|
+
const payload = JSON.parse(JSON.stringify(body));
|
|
463
|
+
updated = await this.persistence(ctx).update(new ModelConstr({ ...payload, [this.pk]: id }), ctx);
|
|
464
|
+
}
|
|
465
|
+
catch (e) {
|
|
466
|
+
log.error(e);
|
|
467
|
+
throw e;
|
|
468
|
+
}
|
|
469
|
+
if (resp)
|
|
470
|
+
ctx.toResponse(resp);
|
|
471
|
+
return updated;
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
static createDeleteHandler(getPK, modelClazzName) {
|
|
475
|
+
return async function remove(routeParams, resp) {
|
|
476
|
+
const { ctx, log } = (await this.logCtx([], OperationKeys.DELETE, true)).for(remove);
|
|
477
|
+
const id = getPK(...routeParams.valuesInOrder);
|
|
478
|
+
if (typeof id === "undefined")
|
|
479
|
+
throw new ValidationError(`No ${this.pk} provided`);
|
|
480
|
+
let del;
|
|
481
|
+
try {
|
|
482
|
+
log.debug(`deleting ${modelClazzName} with ${this.pk} ${id}`);
|
|
483
|
+
del = await this.persistence(ctx).delete(id, ctx);
|
|
484
|
+
}
|
|
485
|
+
catch (e) {
|
|
486
|
+
log.error(`Failed to delete ${modelClazzName} with id ${id}`, e);
|
|
487
|
+
throw e;
|
|
488
|
+
}
|
|
489
|
+
log.info(`deleted ${modelClazzName} with id ${id}`);
|
|
490
|
+
if (resp)
|
|
491
|
+
ctx.toResponse(resp);
|
|
492
|
+
return del;
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
static createStatementHandler(modelClazzName) {
|
|
496
|
+
return async function statement(name, args, details) {
|
|
497
|
+
const { ctx } = (await this.logCtx([], PersistenceKeys.STATEMENT, true)).for(statement);
|
|
498
|
+
const { direction, offset, limit, bookmark } = details;
|
|
499
|
+
args = args.map((a) => (typeof a === "string" ? parseInt(a) : a) || a);
|
|
500
|
+
const pathDirection = args.length > 1 ? args[1] : undefined;
|
|
501
|
+
const resolvedDirection = (direction ?? pathDirection);
|
|
502
|
+
if (resolvedDirection && args.length > 1)
|
|
503
|
+
args[1] = resolvedDirection;
|
|
504
|
+
switch (name) {
|
|
505
|
+
case PreparedStatementKeys.FIND:
|
|
506
|
+
case PreparedStatementKeys.FIND_BY:
|
|
507
|
+
break;
|
|
508
|
+
case PreparedStatementKeys.LIST_BY:
|
|
509
|
+
args.push(direction);
|
|
510
|
+
break;
|
|
511
|
+
case PreparedStatementKeys.PAGE:
|
|
512
|
+
case PreparedStatementKeys.PAGE_BY:
|
|
513
|
+
args = [
|
|
514
|
+
args[0],
|
|
515
|
+
resolvedDirection,
|
|
516
|
+
{ limit, offset, bookmark },
|
|
517
|
+
];
|
|
518
|
+
break;
|
|
519
|
+
case PreparedStatementKeys.FIND_ONE_BY:
|
|
520
|
+
break;
|
|
521
|
+
case PreparedStatementKeys.COUNT_OF:
|
|
522
|
+
case PreparedStatementKeys.MAX_OF:
|
|
523
|
+
case PreparedStatementKeys.MIN_OF:
|
|
524
|
+
case PreparedStatementKeys.AVG_OF:
|
|
525
|
+
case PreparedStatementKeys.SUM_OF:
|
|
526
|
+
case PreparedStatementKeys.DISTINCT_OF:
|
|
527
|
+
case PreparedStatementKeys.GROUP_OF:
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
530
|
+
return this.persistence(ctx).statement(name, ...args, ctx);
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
static createStatementShortcutHandler(statementKey, modelClazzName) {
|
|
534
|
+
return async function statementShortcut(...args) {
|
|
535
|
+
const { ctx } = (await this.logCtx([], statementKey, true)).for(statementShortcut);
|
|
536
|
+
switch (statementKey) {
|
|
537
|
+
case PreparedStatementKeys.LIST_BY: {
|
|
538
|
+
const [key, details] = args;
|
|
539
|
+
return this.persistence(ctx).listBy(key, details?.direction, ctx);
|
|
453
540
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
541
|
+
case PreparedStatementKeys.PAGE_BY: {
|
|
542
|
+
const [key, page, details] = args;
|
|
543
|
+
return this.persistence(ctx).paginateBy(key, details?.direction, {
|
|
544
|
+
limit: details?.limit,
|
|
545
|
+
offset: details?.offset,
|
|
546
|
+
page,
|
|
547
|
+
}, ctx);
|
|
457
548
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
async read(routeParams) {
|
|
462
|
-
const { ctx, log } = (await this.logCtx([], OperationKeys.READ, true)).for(this.read);
|
|
463
|
-
const id = getPK(...routeParams.valuesInOrder);
|
|
464
|
-
if (typeof id === "undefined")
|
|
465
|
-
throw new ValidationError(`No ${this.pk} provided`);
|
|
466
|
-
let read;
|
|
467
|
-
try {
|
|
468
|
-
log.debug(`reading ${modelClazzName} with ${this.pk} ${id}`);
|
|
549
|
+
case PreparedStatementKeys.FIND: {
|
|
550
|
+
const [value, details] = args;
|
|
551
|
+
const direction = details?.direction ?? OrderDirection.ASC;
|
|
469
552
|
const persistence = this.persistence(ctx);
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
log.error(`Failed to read ${modelClazzName} with id ${id}`, e);
|
|
474
|
-
throw e;
|
|
553
|
+
if (typeof persistence.find === "function")
|
|
554
|
+
return persistence.find(value, direction, ctx);
|
|
555
|
+
return persistence.statement(PreparedStatementKeys.FIND, value, direction, ctx);
|
|
475
556
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
log.info(`updating ${body.length} ${modelClazzName}`);
|
|
484
|
-
updated = await this.persistence(ctx).updateAll(body, ctx);
|
|
485
|
-
}
|
|
486
|
-
catch (e) {
|
|
487
|
-
log.error(e);
|
|
488
|
-
throw e;
|
|
489
|
-
}
|
|
490
|
-
ctx.toResponse(resp);
|
|
491
|
-
return updated;
|
|
492
|
-
}
|
|
493
|
-
async update(routeParams, body, resp) {
|
|
494
|
-
const { ctx, log } = (await this.logCtx([], OperationKeys.UPDATE, true)).for(this.update);
|
|
495
|
-
const id = getPK(...routeParams.valuesInOrder);
|
|
496
|
-
if (typeof id === "undefined")
|
|
497
|
-
throw new ValidationError(`No ${this.pk} provided`);
|
|
498
|
-
let updated;
|
|
499
|
-
try {
|
|
500
|
-
log.info(`updating ${modelClazzName} with ${this.pk} ${id}`);
|
|
501
|
-
const payload = JSON.parse(JSON.stringify(body));
|
|
557
|
+
case PreparedStatementKeys.PAGE: {
|
|
558
|
+
const [value, details] = args;
|
|
559
|
+
const ref = {
|
|
560
|
+
offset: details?.offset ?? 1,
|
|
561
|
+
limit: details?.limit ?? 10,
|
|
562
|
+
bookmark: details?.bookmark,
|
|
563
|
+
};
|
|
502
564
|
const persistence = this.persistence(ctx);
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
565
|
+
const direction = details?.direction ?? OrderDirection.ASC;
|
|
566
|
+
if (typeof persistence.page === "function")
|
|
567
|
+
return persistence.page(value, direction, ref, ctx);
|
|
568
|
+
return persistence.statement(PreparedStatementKeys.PAGE, value, direction, ref, ctx);
|
|
507
569
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
}
|
|
512
|
-
ctx.toResponse(resp);
|
|
513
|
-
return updated;
|
|
514
|
-
}
|
|
515
|
-
async deleteAll(ids, resp) {
|
|
516
|
-
const { ctx, log } = (await this.logCtx([], BulkCrudOperationKeys.DELETE_ALL, true)).for(this.deleteAll);
|
|
517
|
-
const normalizedIds = Array.isArray(ids) ? ids : [ids];
|
|
518
|
-
let read;
|
|
519
|
-
try {
|
|
520
|
-
log.debug(`deleting ${normalizedIds.length} ${modelClazzName}: ${normalizedIds}`);
|
|
521
|
-
read = await this.persistence(ctx).deleteAll(normalizedIds, ctx);
|
|
570
|
+
case PreparedStatementKeys.FIND_ONE_BY: {
|
|
571
|
+
const [key, value] = args;
|
|
572
|
+
return this.persistence(ctx).findOneBy(key, value, ctx);
|
|
522
573
|
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
574
|
+
case PreparedStatementKeys.FIND_BY: {
|
|
575
|
+
const [key, value] = args;
|
|
576
|
+
return this.persistence(ctx)
|
|
577
|
+
.for(ctx.toOverrides())
|
|
578
|
+
.findBy(key, value, ctx);
|
|
526
579
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
580
|
+
default:
|
|
581
|
+
if (statementKey === PreparedStatementKeys.COUNT_OF ||
|
|
582
|
+
statementKey === PreparedStatementKeys.MAX_OF ||
|
|
583
|
+
statementKey === PreparedStatementKeys.MIN_OF ||
|
|
584
|
+
statementKey === PreparedStatementKeys.AVG_OF ||
|
|
585
|
+
statementKey === PreparedStatementKeys.SUM_OF ||
|
|
586
|
+
statementKey === PreparedStatementKeys.DISTINCT_OF ||
|
|
587
|
+
statementKey === PreparedStatementKeys.GROUP_OF) {
|
|
588
|
+
const [field] = args;
|
|
589
|
+
return this.persistence(ctx).statement(statementKey, field, ctx);
|
|
590
|
+
}
|
|
591
|
+
throw new Error(`Unknown statement: ${statementKey}`);
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
static createComplexQueryHandler(methodName) {
|
|
596
|
+
return async function complexQuery(...args) {
|
|
597
|
+
const log = this.log?.for?.(complexQuery);
|
|
598
|
+
try {
|
|
599
|
+
if (log)
|
|
600
|
+
log.debug(`Invoking custom query "${methodName}"`);
|
|
601
|
+
const { ctx } = (await this.logCtx([], methodName, true)).for(complexQuery);
|
|
602
|
+
const persistence = this.persistence(ctx);
|
|
603
|
+
const spreadArgs = FromModelController.extractQueryArgs(args);
|
|
604
|
+
if (typeof persistence[methodName] === "function") {
|
|
605
|
+
return persistence[methodName](...spreadArgs, ctx);
|
|
540
606
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
throw e;
|
|
607
|
+
if (typeof persistence.statement === "function") {
|
|
608
|
+
return persistence.statement(methodName, ...spreadArgs, ctx);
|
|
544
609
|
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
610
|
+
throw new Error(`Persistence method "${methodName}" not found on ${persistence?.constructor?.name}`);
|
|
611
|
+
}
|
|
612
|
+
catch (e) {
|
|
613
|
+
if (log)
|
|
614
|
+
log.error(`Custom query "${methodName}" failed`, e);
|
|
615
|
+
throw e;
|
|
548
616
|
}
|
|
549
617
|
};
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
enum: OrderDirection,
|
|
602
|
-
description: "the sort order for the matching results",
|
|
603
|
-
}),
|
|
604
|
-
ApiOkResponse({
|
|
605
|
-
description: `${modelClazzName} records matching the provided prefix.`,
|
|
606
|
-
}),
|
|
607
|
-
__param(0, Param("value")),
|
|
608
|
-
__param(1, DecafQuery()),
|
|
609
|
-
__metadata("design:type", Function),
|
|
610
|
-
__metadata("design:paramtypes", [String, Object]),
|
|
611
|
-
__metadata("design:returntype", Promise)
|
|
612
|
-
], DynamicModelController.prototype, "find", null);
|
|
613
|
-
__decorate([
|
|
614
|
-
ApiOperationFromModel(ModelConstr, "GET", "page/:value"),
|
|
615
|
-
ApiOperation({
|
|
616
|
-
summary: `Page ${modelClazzName} records using the default query attributes.`,
|
|
617
|
-
}),
|
|
618
|
-
ApiParam({
|
|
619
|
-
name: "value",
|
|
620
|
-
description: "The string to match against the default query attributes",
|
|
621
|
-
}),
|
|
622
|
-
ApiQuery({
|
|
623
|
-
name: "direction",
|
|
624
|
-
required: false,
|
|
625
|
-
enum: OrderDirection,
|
|
626
|
-
description: "the sort order for the paged results",
|
|
627
|
-
}),
|
|
628
|
-
ApiQuery({
|
|
629
|
-
name: "limit",
|
|
630
|
-
required: false,
|
|
631
|
-
description: "page size",
|
|
632
|
-
}),
|
|
633
|
-
ApiQuery({
|
|
634
|
-
name: "offset",
|
|
635
|
-
required: false,
|
|
636
|
-
description: "page number",
|
|
637
|
-
}),
|
|
638
|
-
ApiQuery({
|
|
639
|
-
name: "bookmark",
|
|
640
|
-
required: false,
|
|
641
|
-
description: "bookmark for cursor pagination",
|
|
642
|
-
}),
|
|
643
|
-
ApiOkResponse({
|
|
644
|
-
description: `${modelClazzName} records paged by the provided prefix.`,
|
|
645
|
-
}),
|
|
646
|
-
__param(0, Param("value")),
|
|
647
|
-
__param(1, DecafQuery()),
|
|
648
|
-
__metadata("design:type", Function),
|
|
649
|
-
__metadata("design:paramtypes", [String, Object]),
|
|
650
|
-
__metadata("design:returntype", Promise)
|
|
651
|
-
], DynamicModelController.prototype, "page", null);
|
|
652
|
-
__decorate([
|
|
653
|
-
ApiOperationFromModel(ModelConstr, "GET", "findOneBy/:key"),
|
|
654
|
-
ApiOperation({ summary: `Retrieve ${modelClazzName} records by query.` }),
|
|
655
|
-
ApiParam({ name: "key", description: "the model key to sort by" }),
|
|
656
|
-
ApiOkResponse({
|
|
657
|
-
description: `${modelClazzName} listed found.`,
|
|
658
|
-
}),
|
|
659
|
-
ApiNotFoundResponse({
|
|
660
|
-
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
661
|
-
}),
|
|
662
|
-
__param(0, Param("key")),
|
|
663
|
-
__param(1, Param("value")),
|
|
664
|
-
__metadata("design:type", Function),
|
|
665
|
-
__metadata("design:paramtypes", [String, Object]),
|
|
666
|
-
__metadata("design:returntype", Promise)
|
|
667
|
-
], DynamicModelController.prototype, "findOneBy", null);
|
|
668
|
-
__decorate([
|
|
669
|
-
ApiOperationFromModel(ModelConstr, "GET", "findBy/:key/:value"),
|
|
670
|
-
ApiOperation({ summary: `Retrieve ${modelClazzName} records by query.` }),
|
|
671
|
-
ApiParam({ name: "key", description: "the model key to compare" }),
|
|
672
|
-
ApiParam({ name: "value", description: "the value to match" }),
|
|
673
|
-
ApiQuery({
|
|
674
|
-
name: "direction",
|
|
675
|
-
required: true,
|
|
676
|
-
enum: OrderDirection,
|
|
677
|
-
description: "the sort order when applicable",
|
|
678
|
-
}),
|
|
679
|
-
ApiOkResponse({
|
|
680
|
-
description: `${modelClazzName} listed found.`,
|
|
681
|
-
}),
|
|
682
|
-
ApiNotFoundResponse({
|
|
683
|
-
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
684
|
-
}),
|
|
685
|
-
__param(0, Param("key")),
|
|
686
|
-
__param(1, Param("value")),
|
|
687
|
-
__param(2, DecafQuery()),
|
|
688
|
-
__metadata("design:type", Function),
|
|
689
|
-
__metadata("design:paramtypes", [String, Object, Object]),
|
|
690
|
-
__metadata("design:returntype", Promise)
|
|
691
|
-
], DynamicModelController.prototype, "findBy", null);
|
|
692
|
-
__decorate([
|
|
693
|
-
ApiOperationFromModel(ModelConstr, "GET", "statement/:method/*args"),
|
|
694
|
-
ApiOperation({
|
|
695
|
-
summary: `Executes a prepared statement on ${modelClazzName}.`,
|
|
696
|
-
}),
|
|
697
|
-
ApiParam({
|
|
698
|
-
name: "method",
|
|
699
|
-
description: "the prepared statement to execute",
|
|
700
|
-
}),
|
|
701
|
-
ApiParam({
|
|
702
|
-
name: "args",
|
|
703
|
-
description: "concatenated list of arguments the prepared statement can accept",
|
|
704
|
-
}),
|
|
705
|
-
ApiQuery({
|
|
706
|
-
name: "direction",
|
|
707
|
-
required: true,
|
|
708
|
-
enum: OrderDirection,
|
|
709
|
-
description: "the sort order when applicable",
|
|
710
|
-
}),
|
|
711
|
-
ApiQuery({
|
|
712
|
-
name: "limit",
|
|
713
|
-
required: true,
|
|
714
|
-
description: "limit or page size when applicable",
|
|
715
|
-
}),
|
|
716
|
-
ApiQuery({
|
|
717
|
-
name: "offset",
|
|
718
|
-
required: true,
|
|
719
|
-
description: "offset or bookmark when applicable",
|
|
720
|
-
}),
|
|
721
|
-
ApiOkResponse({
|
|
722
|
-
description: `${modelClazzName} listed found.`,
|
|
723
|
-
}),
|
|
724
|
-
ApiNotFoundResponse({
|
|
725
|
-
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
726
|
-
}),
|
|
727
|
-
__param(0, Param("method")),
|
|
728
|
-
__param(1, Param("args")),
|
|
729
|
-
__param(2, DecafQuery()),
|
|
730
|
-
__metadata("design:type", Function),
|
|
731
|
-
__metadata("design:paramtypes", [String, Array, Object]),
|
|
732
|
-
__metadata("design:returntype", Promise)
|
|
733
|
-
], DynamicModelController.prototype, "statement", null);
|
|
734
|
-
__decorate([
|
|
735
|
-
ApiOperationFromModel(ModelConstr, "POST", "bulk"),
|
|
618
|
+
}
|
|
619
|
+
static createCustomRouteHandler(methodName) {
|
|
620
|
+
return async function customRoute(...args) {
|
|
621
|
+
const log = this.log?.for?.(customRoute);
|
|
622
|
+
const { ctx } = (await this.logCtx([], methodName, true)).for(customRoute);
|
|
623
|
+
const persistence = this.persistence(ctx);
|
|
624
|
+
const spreadArgs = FromModelController.extractQueryArgs(args);
|
|
625
|
+
// Try the persistence directly (works when it's a custom Repository)
|
|
626
|
+
if (typeof persistence[methodName] === "function") {
|
|
627
|
+
return persistence[methodName](...spreadArgs, ctx);
|
|
628
|
+
}
|
|
629
|
+
// When persistence is a ModelService, the method lives on the underlying repo
|
|
630
|
+
if (persistence?.repo && typeof persistence.repo[methodName] === "function") {
|
|
631
|
+
return persistence.repo[methodName](...spreadArgs, ctx);
|
|
632
|
+
}
|
|
633
|
+
// Fall back to statement gateway
|
|
634
|
+
if (typeof persistence.statement === "function") {
|
|
635
|
+
return persistence.statement(methodName, ...spreadArgs, ctx);
|
|
636
|
+
}
|
|
637
|
+
throw new Error(`Method "${methodName}" not found on ${persistence?.constructor?.name} or its repo`);
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
static extractQueryArgs(args) {
|
|
641
|
+
if (args.length === 0)
|
|
642
|
+
return args;
|
|
643
|
+
const last = args[args.length - 1];
|
|
644
|
+
if (last &&
|
|
645
|
+
typeof last === "object" &&
|
|
646
|
+
!Array.isArray(last)) {
|
|
647
|
+
const queryObj = args.pop();
|
|
648
|
+
const hasDirection = queryObj.direction !== undefined;
|
|
649
|
+
const hasLimit = queryObj.limit !== undefined;
|
|
650
|
+
const hasOffset = queryObj.offset !== undefined;
|
|
651
|
+
if (!hasDirection && !hasLimit && !hasOffset)
|
|
652
|
+
return args;
|
|
653
|
+
const extras = [];
|
|
654
|
+
if (hasDirection)
|
|
655
|
+
extras.push(queryObj.direction);
|
|
656
|
+
else if (hasLimit || hasOffset)
|
|
657
|
+
extras.push(undefined);
|
|
658
|
+
if (hasLimit)
|
|
659
|
+
extras.push(queryObj.limit);
|
|
660
|
+
if (hasOffset)
|
|
661
|
+
extras.push(queryObj.offset);
|
|
662
|
+
return [...args, ...extras];
|
|
663
|
+
}
|
|
664
|
+
return args;
|
|
665
|
+
}
|
|
666
|
+
static createCreateDecorators(ModelConstr, modelClazzName) {
|
|
667
|
+
return [
|
|
668
|
+
ApiOperationFromModel(ModelConstr, "POST"),
|
|
736
669
|
ApiOperation({ summary: `Create a new ${modelClazzName}.` }),
|
|
737
670
|
ApiBody({
|
|
738
671
|
description: `Payload for ${modelClazzName}`,
|
|
739
|
-
|
|
740
|
-
type: "array",
|
|
741
|
-
items: {
|
|
742
|
-
$ref: getSchemaPath(ModelConstr),
|
|
743
|
-
// $ref: getSchemaPath(DtoFor(OperationKeys.CREATE, ModelConstr)),
|
|
744
|
-
},
|
|
745
|
-
},
|
|
672
|
+
type: DtoFor(OperationKeys.CREATE, ModelConstr),
|
|
746
673
|
}),
|
|
747
674
|
ApiCreatedResponse({
|
|
748
675
|
description: `${modelClazzName} created successfully.`,
|
|
749
|
-
schema: {
|
|
750
|
-
type: "array",
|
|
751
|
-
items: {
|
|
752
|
-
$ref: getSchemaPath(ModelConstr),
|
|
753
|
-
},
|
|
754
|
-
},
|
|
676
|
+
schema: { $ref: getSchemaPath(ModelConstr) },
|
|
755
677
|
}),
|
|
756
678
|
ApiBadRequestResponse({ description: "Payload validation failed." }),
|
|
757
679
|
ApiUnprocessableEntityResponse({
|
|
758
680
|
description: "Repository rejected the provided payload.",
|
|
759
681
|
}),
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
], DynamicModelController.prototype, "createAll", null);
|
|
766
|
-
__decorate([
|
|
767
|
-
ApiOperationFromModel(ModelConstr, "POST"),
|
|
682
|
+
];
|
|
683
|
+
}
|
|
684
|
+
static bulkCreateDecorators(ModelConstr, modelClazzName) {
|
|
685
|
+
return [
|
|
686
|
+
ApiOperationFromModel(ModelConstr, "POST", "bulk"),
|
|
768
687
|
ApiOperation({ summary: `Create a new ${modelClazzName}.` }),
|
|
769
688
|
ApiBody({
|
|
770
689
|
description: `Payload for ${modelClazzName}`,
|
|
771
|
-
|
|
690
|
+
schema: {
|
|
691
|
+
type: "array",
|
|
692
|
+
items: { $ref: getSchemaPath(ModelConstr) },
|
|
693
|
+
},
|
|
772
694
|
}),
|
|
773
695
|
ApiCreatedResponse({
|
|
774
696
|
description: `${modelClazzName} created successfully.`,
|
|
775
697
|
schema: {
|
|
776
|
-
|
|
698
|
+
type: "array",
|
|
699
|
+
items: { $ref: getSchemaPath(ModelConstr) },
|
|
777
700
|
},
|
|
778
701
|
}),
|
|
779
702
|
ApiBadRequestResponse({ description: "Payload validation failed." }),
|
|
780
703
|
ApiUnprocessableEntityResponse({
|
|
781
704
|
description: "Repository rejected the provided payload.",
|
|
782
705
|
}),
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
__metadata("design:returntype", Promise)
|
|
788
|
-
], DynamicModelController.prototype, "create", null);
|
|
789
|
-
__decorate([
|
|
706
|
+
];
|
|
707
|
+
}
|
|
708
|
+
static bulkReadDecorators(ModelConstr, modelClazzName) {
|
|
709
|
+
return [
|
|
790
710
|
ApiOperationFromModel(ModelConstr, "GET", "bulk"),
|
|
791
|
-
ApiOperation({ summary: `Retrieve
|
|
711
|
+
ApiOperation({ summary: `Retrieve ${modelClazzName} records by ids.` }),
|
|
792
712
|
ApiQuery({ name: "ids", required: true, type: "array" }),
|
|
793
713
|
ApiOkResponse({
|
|
794
714
|
description: `${modelClazzName} retrieved successfully.`,
|
|
795
715
|
schema: {
|
|
796
716
|
type: "array",
|
|
797
|
-
items: {
|
|
798
|
-
$ref: getSchemaPath(ModelConstr),
|
|
799
|
-
},
|
|
717
|
+
items: { $ref: getSchemaPath(ModelConstr) },
|
|
800
718
|
},
|
|
801
719
|
}),
|
|
802
720
|
ApiNotFoundResponse({
|
|
803
721
|
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
804
722
|
}),
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
__decorate([
|
|
811
|
-
ApiOperationFromModel(ModelConstr, "GET", path),
|
|
812
|
-
ApiParamsFromModel(apiProperties),
|
|
813
|
-
ApiOperation({ summary: `Retrieve a ${modelClazzName} record by id.` }),
|
|
814
|
-
ApiOkResponse({
|
|
815
|
-
description: `${modelClazzName} retrieved successfully.`,
|
|
816
|
-
schema: {
|
|
817
|
-
$ref: getSchemaPath(ModelConstr),
|
|
818
|
-
},
|
|
819
|
-
}),
|
|
820
|
-
ApiNotFoundResponse({
|
|
821
|
-
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
822
|
-
}),
|
|
823
|
-
__param(0, DecafParams(apiProperties)),
|
|
824
|
-
__metadata("design:type", Function),
|
|
825
|
-
__metadata("design:paramtypes", [Object]),
|
|
826
|
-
__metadata("design:returntype", Promise)
|
|
827
|
-
], DynamicModelController.prototype, "read", null);
|
|
828
|
-
__decorate([
|
|
829
|
-
ApiOperationFromModel(ModelConstr, "PUT", `bulk`),
|
|
723
|
+
];
|
|
724
|
+
}
|
|
725
|
+
static bulkUpdateDecorators(ModelConstr, modelClazzName, apiProperties) {
|
|
726
|
+
return [
|
|
727
|
+
ApiOperationFromModel(ModelConstr, "PUT", "bulk"),
|
|
830
728
|
ApiParamsFromModel(apiProperties),
|
|
831
729
|
ApiOperation({
|
|
832
|
-
summary: `Replace
|
|
730
|
+
summary: `Replace existing ${modelClazzName} records with new payloads.`,
|
|
833
731
|
}),
|
|
834
732
|
ApiBody({
|
|
835
|
-
description: `Payload for
|
|
733
|
+
description: `Payload for replacing existing records of ${modelClazzName}`,
|
|
836
734
|
schema: {
|
|
837
735
|
type: "array",
|
|
838
736
|
$ref: getSchemaPath(DtoFor(OperationKeys.UPDATE, ModelConstr)),
|
|
@@ -842,123 +740,170 @@ export class FromModelController {
|
|
|
842
740
|
description: `${modelClazzName} updated successfully.`,
|
|
843
741
|
schema: {
|
|
844
742
|
type: "array",
|
|
845
|
-
items: {
|
|
846
|
-
$ref: getSchemaPath(ModelConstr),
|
|
847
|
-
},
|
|
743
|
+
items: { $ref: getSchemaPath(ModelConstr) },
|
|
848
744
|
},
|
|
849
745
|
}),
|
|
850
746
|
ApiNotFoundResponse({
|
|
851
747
|
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
852
748
|
}),
|
|
853
749
|
ApiBadRequestResponse({ description: "Payload validation failed." }),
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
750
|
+
];
|
|
751
|
+
}
|
|
752
|
+
static bulkDeleteDecorators(ModelConstr, modelClazzName, apiProperties) {
|
|
753
|
+
return [
|
|
754
|
+
ApiOperationFromModel(ModelConstr, "DELETE", "bulk"),
|
|
755
|
+
ApiParamsFromModel(apiProperties),
|
|
756
|
+
ApiOperation({ summary: `Delete ${modelClazzName} records by ids.` }),
|
|
757
|
+
ApiQuery({ name: "ids", required: true, type: "array" }),
|
|
758
|
+
ApiOkResponse({
|
|
759
|
+
description: `${modelClazzName} deleted successfully.`,
|
|
760
|
+
schema: {
|
|
761
|
+
type: "array",
|
|
762
|
+
items: { $ref: getSchemaPath(ModelConstr) },
|
|
763
|
+
},
|
|
764
|
+
}),
|
|
765
|
+
ApiNotFoundResponse({
|
|
766
|
+
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
767
|
+
}),
|
|
768
|
+
];
|
|
769
|
+
}
|
|
770
|
+
static readDecorators(ModelConstr, modelClazzName, apiProperties, pkPath) {
|
|
771
|
+
return [
|
|
772
|
+
ApiOperationFromModel(ModelConstr, "GET", pkPath),
|
|
773
|
+
ApiParamsFromModel(apiProperties),
|
|
774
|
+
ApiOperation({ summary: `Retrieve a ${modelClazzName} record by id.` }),
|
|
775
|
+
ApiOkResponse({
|
|
776
|
+
description: `${modelClazzName} retrieved successfully.`,
|
|
777
|
+
schema: { $ref: getSchemaPath(ModelConstr) },
|
|
778
|
+
}),
|
|
779
|
+
ApiNotFoundResponse({
|
|
780
|
+
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
781
|
+
}),
|
|
782
|
+
];
|
|
783
|
+
}
|
|
784
|
+
static updateDecorators(ModelConstr, modelClazzName, apiProperties, pkPath) {
|
|
785
|
+
return [
|
|
786
|
+
ApiOperationFromModel(ModelConstr, "PUT", pkPath),
|
|
862
787
|
ApiParamsFromModel(apiProperties),
|
|
863
788
|
ApiOperation({
|
|
864
789
|
summary: `Replace an existing ${modelClazzName} record with a new payload.`,
|
|
865
790
|
}),
|
|
866
791
|
ApiBody({
|
|
867
|
-
description: `Payload for
|
|
792
|
+
description: `Payload for replacing an existing record of ${modelClazzName}`,
|
|
868
793
|
type: DtoFor(OperationKeys.UPDATE, ModelConstr),
|
|
869
794
|
}),
|
|
870
795
|
ApiOkResponse({
|
|
871
796
|
description: `${modelClazzName} updated successfully.`,
|
|
872
|
-
schema: {
|
|
873
|
-
$ref: getSchemaPath(ModelConstr),
|
|
874
|
-
},
|
|
797
|
+
schema: { $ref: getSchemaPath(ModelConstr) },
|
|
875
798
|
}),
|
|
876
799
|
ApiNotFoundResponse({
|
|
877
800
|
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
878
801
|
}),
|
|
879
802
|
ApiBadRequestResponse({ description: "Payload validation failed." }),
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
__metadata("design:returntype", Promise)
|
|
886
|
-
], DynamicModelController.prototype, "update", null);
|
|
887
|
-
__decorate([
|
|
888
|
-
ApiOperationFromModel(ModelConstr, "DELETE", "bulk"),
|
|
803
|
+
];
|
|
804
|
+
}
|
|
805
|
+
static deleteDecorators(ModelConstr, modelClazzName, apiProperties, pkPath) {
|
|
806
|
+
return [
|
|
807
|
+
ApiOperationFromModel(ModelConstr, "DELETE", pkPath),
|
|
889
808
|
ApiParamsFromModel(apiProperties),
|
|
890
|
-
ApiOperation({ summary: `
|
|
891
|
-
ApiQuery({ name: "ids", required: true, type: "array" }),
|
|
809
|
+
ApiOperation({ summary: `Delete a ${modelClazzName} record by id.` }),
|
|
892
810
|
ApiOkResponse({
|
|
893
811
|
description: `${modelClazzName} deleted successfully.`,
|
|
894
|
-
schema: {
|
|
895
|
-
type: "array",
|
|
896
|
-
items: {
|
|
897
|
-
$ref: getSchemaPath(ModelConstr),
|
|
898
|
-
},
|
|
899
|
-
},
|
|
812
|
+
schema: { $ref: getSchemaPath(ModelConstr) },
|
|
900
813
|
}),
|
|
901
814
|
ApiNotFoundResponse({
|
|
902
815
|
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
903
816
|
}),
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
ApiOperationFromModel(ModelConstr, "DELETE", path),
|
|
912
|
-
ApiParamsFromModel(apiProperties),
|
|
913
|
-
ApiOperation({ summary: `Delete a ${modelClazzName} record by id.` }),
|
|
914
|
-
ApiOkResponse({
|
|
915
|
-
description: `${modelClazzName} deleted successfully.`,
|
|
916
|
-
schema: {
|
|
917
|
-
$ref: getSchemaPath(ModelConstr),
|
|
918
|
-
},
|
|
817
|
+
];
|
|
818
|
+
}
|
|
819
|
+
static statementDecorators(ModelConstr, modelClazzName) {
|
|
820
|
+
return [
|
|
821
|
+
ApiOperationFromModel(ModelConstr, "GET", "statement/:method/*args"),
|
|
822
|
+
ApiOperation({
|
|
823
|
+
summary: `Executes a prepared statement on ${modelClazzName}.`,
|
|
919
824
|
}),
|
|
825
|
+
ApiParam({ name: "method", description: "the prepared statement to execute" }),
|
|
826
|
+
ApiParam({
|
|
827
|
+
name: "args",
|
|
828
|
+
description: "concatenated list of arguments the prepared statement can accept",
|
|
829
|
+
}),
|
|
830
|
+
ApiQuery({
|
|
831
|
+
name: "direction",
|
|
832
|
+
required: true,
|
|
833
|
+
enum: OrderDirection,
|
|
834
|
+
description: "the sort order when applicable",
|
|
835
|
+
}),
|
|
836
|
+
ApiQuery({ name: "limit", required: true, description: "limit or page size when applicable" }),
|
|
837
|
+
ApiQuery({ name: "offset", required: true, description: "offset or bookmark when applicable" }),
|
|
838
|
+
ApiOkResponse({ description: `${modelClazzName} listed found.` }),
|
|
920
839
|
ApiNotFoundResponse({
|
|
921
840
|
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
922
841
|
}),
|
|
923
|
-
|
|
924
|
-
__param(1, Response({ passthrough: true })),
|
|
925
|
-
__metadata("design:type", Function),
|
|
926
|
-
__metadata("design:paramtypes", [Object, Object]),
|
|
927
|
-
__metadata("design:returntype", Promise)
|
|
928
|
-
], DynamicModelController.prototype, "delete", null);
|
|
929
|
-
DynamicModelController = __decorate([
|
|
930
|
-
Controller(routePath),
|
|
931
|
-
ApiTags(modelClazzName),
|
|
932
|
-
ApiExtraModels(ModelConstr),
|
|
933
|
-
Auth(ModelConstr),
|
|
934
|
-
__metadata("design:paramtypes", [DecafRequestContext])
|
|
935
|
-
], DynamicModelController);
|
|
936
|
-
return DynamicModelController;
|
|
842
|
+
];
|
|
937
843
|
}
|
|
938
|
-
static
|
|
939
|
-
const
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
const apiProperties = uniqueKeys.map((key) => {
|
|
949
|
-
return {
|
|
950
|
-
name: key,
|
|
951
|
-
description: Metadata.description(ModelClazz, key),
|
|
952
|
-
required: true,
|
|
953
|
-
type: String,
|
|
954
|
-
};
|
|
844
|
+
static statementShortcutDecorators(ModelConstr, modelClazzName, path, statementKey) {
|
|
845
|
+
const base = [
|
|
846
|
+
ApiOperationFromModel(ModelConstr, "GET", path),
|
|
847
|
+
ApiOperation({ summary: `Retrieve ${modelClazzName} records.` }),
|
|
848
|
+
ApiOkResponse({ description: `${modelClazzName} retrieved successfully.` }),
|
|
849
|
+
];
|
|
850
|
+
const segments = path.split("/").filter((s) => s.startsWith(":"));
|
|
851
|
+
segments.forEach((seg) => {
|
|
852
|
+
const name = seg.replace(":", "");
|
|
853
|
+
base.push(ApiParam({ name, description: `The ${name} parameter` }));
|
|
955
854
|
});
|
|
956
|
-
|
|
957
|
-
path
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
855
|
+
if (path.startsWith("listBy/") ||
|
|
856
|
+
path.startsWith("paginateBy/") ||
|
|
857
|
+
path.startsWith("find/") ||
|
|
858
|
+
path.startsWith("page/")) {
|
|
859
|
+
base.push(ApiQuery({
|
|
860
|
+
name: "direction",
|
|
861
|
+
required: true,
|
|
862
|
+
enum: OrderDirection,
|
|
863
|
+
description: "the sort order",
|
|
864
|
+
}));
|
|
865
|
+
}
|
|
866
|
+
if (path.startsWith("paginateBy/") || path.startsWith("page/")) {
|
|
867
|
+
base.push(ApiQuery({ name: "limit", required: false, description: "page size" }), ApiQuery({ name: "offset", required: false, description: "page number" }), ApiQuery({ name: "bookmark", required: false, description: "bookmark for cursor pagination" }));
|
|
868
|
+
}
|
|
869
|
+
if (path.startsWith("findOneBy/") || path.startsWith("findBy/")) {
|
|
870
|
+
base.push(ApiNotFoundResponse({
|
|
871
|
+
description: `No ${modelClazzName} record matches the provided identifier.`,
|
|
872
|
+
}));
|
|
873
|
+
}
|
|
874
|
+
if (statementKey === PreparedStatementKeys.COUNT_OF ||
|
|
875
|
+
statementKey === PreparedStatementKeys.AVG_OF ||
|
|
876
|
+
statementKey === PreparedStatementKeys.SUM_OF) {
|
|
877
|
+
base.push(ApiOkResponse({ description: `Result for ${modelClazzName}.`, type: Number }));
|
|
878
|
+
}
|
|
879
|
+
if (statementKey === PreparedStatementKeys.DISTINCT_OF) {
|
|
880
|
+
base.push(ApiOkResponse({ description: `Distinct values for ${modelClazzName}.`, type: [String] }));
|
|
881
|
+
}
|
|
882
|
+
return base;
|
|
883
|
+
}
|
|
884
|
+
static getQueryDecorators(methodName, routePath, httpVerb, includeQueryParams = false) {
|
|
885
|
+
const extractPathParams = (p) => p.split("/").filter((s) => s.startsWith(":")).map((s) => s.slice(1));
|
|
886
|
+
const apiPathParams = extractPathParams(routePath).map((name) => ({
|
|
887
|
+
name,
|
|
888
|
+
description: `${name} parameter for the query`,
|
|
889
|
+
required: true,
|
|
890
|
+
type: String,
|
|
891
|
+
}));
|
|
892
|
+
const decorators = [
|
|
893
|
+
...apiPathParams.map((p) => ApiParam(p)),
|
|
894
|
+
ApiOperation({ summary: `Retrieve records using "${methodName}".` }),
|
|
895
|
+
ApiOkResponse({ description: "Result successfully retrieved." }),
|
|
896
|
+
ApiNoContentResponse({ description: "No content returned by the method." }),
|
|
897
|
+
];
|
|
898
|
+
if (httpVerb === "GET" && includeQueryParams) {
|
|
899
|
+
decorators.push(ApiQuery({
|
|
900
|
+
name: "direction",
|
|
901
|
+
required: false,
|
|
902
|
+
enum: OrderDirection,
|
|
903
|
+
description: "the sort order when applicable",
|
|
904
|
+
}), ApiQuery({ name: "limit", required: false, description: "limit or page size" }), ApiQuery({ name: "offset", required: false, description: "offset or bookmark" }));
|
|
905
|
+
}
|
|
906
|
+
return decorators;
|
|
962
907
|
}
|
|
963
908
|
}
|
|
964
|
-
//# sourceMappingURL=FromModelController.js.map
|
|
909
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRnJvbU1vZGVsQ29udHJvbGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9kZWNhZi1tb2RlbC9Gcm9tTW9kZWxDb250cm9sbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7QUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDcEUsT0FBTyxFQUNMLHFCQUFxQixFQUNyQixPQUFPLEVBQ1Asa0JBQWtCLEVBQ2xCLGNBQWMsRUFDZCxvQkFBb0IsRUFDcEIsbUJBQW1CLEVBQ25CLGFBQWEsRUFDYixZQUFZLEVBQ1osUUFBUSxFQUNSLFFBQVEsRUFDUixPQUFPLEVBQ1AsOEJBQThCLEVBQzlCLGFBQWEsR0FDZCxNQUFNLGlCQUFpQixDQUFDO0FBQ3pCLE9BQU8sRUFFTCxZQUFZLEVBQ1osY0FBYyxFQUNkLGVBQWUsRUFDZixxQkFBcUIsRUFFckIsVUFBVSxFQUNWLE9BQU8sR0FDUixNQUFNLGdCQUFnQixDQUFDO0FBQ3hCLE9BQU8sRUFBRSxLQUFLLEVBQW9CLE1BQU0sZ0NBQWdDLENBQUM7QUFDekUsT0FBTyxFQUFFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUN6RCxPQUFPLEVBQ0wscUJBQXFCLEVBQ3JCLE1BQU0sRUFDTixhQUFhLEVBQ2IsZUFBZSxHQUNoQixNQUFNLHlCQUF5QixDQUFDO0FBQ2pDLE9BQU8sRUFBZSxRQUFRLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUM3RCxPQUFPLEVBQ0wscUJBQXFCLEVBQ3JCLGtCQUFrQixFQUVsQixTQUFTLEVBQ1QsV0FBVyxFQUNYLFVBQVUsR0FDWCw4QkFBcUI7QUFDdEIsT0FBTyxFQUFFLG1CQUFtQixFQUFFLDhCQUEyQjtBQUV6RCxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsOEJBQW1CO0FBQ2pELE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxXQUFX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QkFBb0IsQ0FBQyxFQUFFLFdBQVcsRUFBRSxvQ0FBb0MsRUFBRSxDQUFDO1NBQzVFLENBQUM7UUFFRixJQUFJLFFBQVEsS0FBSyxLQUFLLElBQUksa0JBQWtCLEVBQUUsQ0FBQztZQUM3QyxVQUFVLENBQUMsSUFBSSxDQUNiLFFBQVEsQ0FBQztnQkFDUCxJQUFJLEVBQUUsV0FBVztnQkFDakIsUUFBUSxFQUFFLEtBQUs7Z0JBQ2YsSUFBSSxFQUFFLGNBQWM7Z0JBQ3BCLFdBQVcsRUFBRSxnQ0FBZ0M7YUFDOUMsQ0FBQyxFQUNGLFFBQVEsQ0FBQyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxXQUFXLEVBQUUsb0JBQW9CLEVBQUUsQ0FBQyxFQUMvRSxRQUFRLENBQUMsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsV0FBVyxFQUFFLG9CQUFvQixFQUFFLENBQUMsQ0FDakYsQ0FBQztRQUNKLENBQUM7UUFFRCxPQUFPLFVBQVUsQ0FBQztJQUNwQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29udHJvbGxlciwgUGFyYW0sIFF1ZXJ5LCBSZXNwb25zZSB9IGZyb20gXCJAbmVzdGpzL2NvbW1vblwiO1xuaW1wb3J0IHtcbiAgQXBpQmFkUmVxdWVzdFJlc3BvbnNlLFxuICBBcGlCb2R5LFxuICBBcGlDcmVhdGVkUmVzcG9uc2UsXG4gIEFwaUV4dHJhTW9kZWxzLFxuICBBcGlOb0NvbnRlbnRSZXNwb25zZSxcbiAgQXBpTm90Rm91bmRSZXNwb25zZSxcbiAgQXBpT2tSZXNwb25zZSxcbiAgQXBpT3BlcmF0aW9uLFxuICBBcGlQYXJhbSxcbiAgQXBpUXVlcnksXG4gIEFwaVRhZ3MsXG4gIEFwaVVucHJvY2Vzc2FibGVFbnRpdHlSZXNwb25zZSxcbiAgZ2V0U2NoZW1hUGF0aCxcbn0gZnJvbSBcIkBuZXN0anMvc3dhZ2dlclwiO1xuaW1wb3J0IHtcbiAgdHlwZSBEaXJlY3Rpb25MaW1pdE9mZnNldCxcbiAgTW9kZWxTZXJ2aWNlLFxuICBPcmRlckRpcmVjdGlvbixcbiAgUGVyc2lzdGVuY2VLZXlzLFxuICBQcmVwYXJlZFN0YXRlbWVudEtleXMsXG4gIHR5cGUgUmVwbyxcbiAgUmVwb3NpdG9yeSxcbiAgU2VydmljZSxcbn0gZnJvbSBcIkBkZWNhZi10cy9jb3JlXCI7XG5pbXBvcnQgeyBNb2RlbCwgTW9kZWxDb25zdHJ1Y3RvciB9IGZyb20gXCJAZGVjYWYtdHMvZGVjb3JhdG9yLXZhbGlkYXRpb25cIjtcbmltcG9ydCB7IExvZ2dpbmcsIHRvS2ViYWJDYXNlIH0gZnJvbSBcIkBkZWNhZi10cy9sb2dnaW5nXCI7XG5pbXBvcnQge1xuICBCdWxrQ3J1ZE9wZXJhdGlvbktleXMsXG4gIERCS2V5cyxcbiAgT3BlcmF0aW9uS2V5cyxcbiAgVmFsaWRhdGlvbkVycm9yLFxufSBmcm9tIFwiQGRlY2FmLXRzL2RiLWRlY29yYXRvcnNcIjtcbmltcG9ydCB7IENvbnN0cnVjdG9yLCBNZXRhZGF0YSB9IGZyb20gXCJAZGVjYWYtdHMvZGVjb3JhdGlvblwiO1xuaW1wb3J0IHtcbiAgQXBpT3BlcmF0aW9uRnJvbU1vZGVsLFxuICBBcGlQYXJhbXNGcm9tTW9kZWwsXG4gIHR5cGUgRGVjYWZBcGlQcm9wZXJ0eSxcbiAgRGVjYWZCb2R5LFxuICBEZWNhZlBhcmFtcyxcbiAgRGVjYWZRdWVyeSxcbn0gZnJvbSBcIi4vZGVjb3JhdG9yc1wiO1xuaW1wb3J0IHsgSHR0cFZlcmJUb0RlY29yYXRvciB9IGZyb20gXCIuL2RlY29yYXRvcnMvdXRpbHNcIjtcbmltcG9ydCB0eXBlIHsgSHR0cFZlcmJzIH0gZnJvbSBcIi4vZGVjb3JhdG9ycy90eXBlc1wiO1xuaW1wb3J0IHsgRGVjYWZSZXF1ZXN0Q29udGV4dCB9IGZyb20gXCIuLi9yZXF1ZXN0XCI7XG5pbXBvcnQgeyBERUNBRl9DT05UUk9MTEVSX0NPTkZJRywgREVDQUZfUk9VVEUgfSBmcm9tIFwiLi4vY29uc3RhbnRzXCI7XG5pbXBvcnQgeyBBdXRoIH0gZnJvbSBcIi4vZGVjb3JhdG9ycy9kZWNvcmF0b3JzXCI7XG5pbXBvcnQgeyBDb250cm9sbGVyQ29uc3RydWN0b3IgfSBmcm9tIFwiLi90eXBlc1wiO1xuaW1wb3J0IHsgRGVjYWZNb2RlbENvbnRyb2xsZXIgfSBmcm9tIFwiLi4vY29udHJvbGxlcnNcIjtcbmltcG9ydCB7IER0b0ZvciB9IGZyb20gXCIuLi9mYWN0b3J5L29wZW5hcGkvRHRvQnVpbGRlclwiO1xuaW1wb3J0IFwiLi4vb3ZlcnJpZGVzXCI7XG5pbXBvcnQge1xuICBNb2RlbENvbnRyb2xsZXJGYWN0b3J5LFxuICB0eXBlIE1vZGVsQ29udHJvbGxlckZhY3RvcnlDb25maWcsXG4gIHR5cGUgU2VydmVyUm91dGUsXG59IGZyb20gXCJAZGVjYWYtdHMvZm9yLWh0dHAvc2VydmVyXCI7XG5cbmV4cG9ydCBjbGFzcyBGcm9tTW9kZWxDb250cm9sbGVyIHtcbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgbG9nID0gTG9nZ2luZy5mb3IoRnJvbU1vZGVsQ29udHJvbGxlci5uYW1lKTtcblxuICBzdGF0aWMgZ2V0UGVyc2lzdGVuY2U8VCBleHRlbmRzIE1vZGVsPGJvb2xlYW4+PihcbiAgICBNb2RlbENsYXp6OiBNb2RlbENvbnN0cnVjdG9yPFQ+XG4gICk6IFJlcG88VD4gfCBNb2RlbFNlcnZpY2U8VD4ge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gU2VydmljZS5nZXQ8TW9kZWxTZXJ2aWNlPFQ+PihNb2RlbENsYXp6KTtcbiAgICB9IGNhdGNoIChlOiB1bmtub3duKSB7XG4gICAgICB0cnkge1xuICAgICAgICByZXR1cm4gTW9kZWxTZXJ2aWNlLmdldFNlcnZpY2UoTW9kZWxDbGF6eikgYXMgTW9kZWxTZXJ2aWNlPFQ+O1xuICAgICAgfSBjYXRjaCAoZTI6IHVua25vd24pIHtcbiAgICAgICAgcmV0dXJuIFJlcG9zaXRvcnkuZm9yTW9kZWwoTW9kZWxDbGF6eikgYXMgUmVwbzxUPjtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBzdGF0aWMgY3JlYXRlUXVlcnlSb3V0ZXNGcm9tUmVwb3NpdG9yeTxUIGV4dGVuZHMgTW9kZWw8Ym9vbGVhbj4+KFxuICAgIHBlcnNpc3RlbmNlOiBSZXBvPFQ+IHwgTW9kZWxTZXJ2aWNlPFQ+LFxuICAgIHByZWZpeDogc3RyaW5nID0gUGVyc2lzdGVuY2VLZXlzLlFVRVJZXG4gICk6IENvbnRyb2xsZXJDb25zdHJ1Y3RvcjxUPiB7XG4gICAgY29uc3QgcmVwbzogUmVwbzxUPiA9XG4gICAgICBwZXJzaXN0ZW5jZSBpbnN0YW5jZW9mIE1vZGVsU2VydmljZSA/IHBlcnNpc3RlbmNlLnJlcG8gOiBwZXJzaXN0ZW5jZTtcbiAgICBjb25zdCBNb2RlbENvbnN0cjogQ29uc3RydWN0b3IgPSByZXBvLmNsYXNzO1xuICAgIGNvbnN0IHF1ZXJ5TWV0aG9kczogUmVjb3JkPHN0cmluZywgeyBmaWVsZHM/OiBzdHJpbmdbXSB8IHVuZGVmaW5lZCB9PiA9XG4gICAgICBNZXRhZGF0YS5nZXQoXG4gICAgICAgIHJlcG8uY29uc3RydWN0b3IgYXMgQ29uc3RydWN0b3IsXG4gICAgICAgIE1ldGFkYXRhLmtleShQZXJzaXN0ZW5jZUtleXMuUVVFUlkpXG4gICAgICApID8/IHt9O1xuXG4gICAgY29uc3Qgcm91dGVNZXRob2RzOiBSZWNvcmQ8c3RyaW5nLCBhbnk+ID1cbiAgICAgIE1ldGFkYXRhLmdldChcbiAgICAgICAgcGVyc2lzdGVuY2UuY29uc3RydWN0b3IgYXMgQ29uc3RydWN0b3IsXG4gICAgICAgIE1ldGFkYXRhLmtleShERUNBRl9ST1VURSlcbiAgICAgICkgPz8ge307XG5cbiAgICBjbGFzcyBRdWVyeUNvbnRyb2xsZXIgZXh0ZW5kcyBEZWNhZk1vZGVsQ29udHJvbGxlcjxUPiB7XG4gICAgICBvdmVycmlkZSBnZXQgY2xhc3MoKTogTW9kZWxDb25zdHJ1Y3RvcjxUPiB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIk1ldGhvZCBub3QgaW1wbGVtZW50ZWQuXCIpO1xuICAgICAgfVxuICAgICAgY29uc3RydWN0b3IoY2xpZW50Q29udGV4dDogRGVjYWZSZXF1ZXN0Q29udGV4dCwgbmFtZTogc3RyaW5nKSB7XG4gICAgICAgIHN1cGVyKGNsaWVudENvbnRleHQsIG5hbWUpO1xuICAgICAgfVxuICAgIH1cblxuICAgIGZvciAoY29uc3QgW21ldGhvZE5hbWUsIHBhcmFtc10gb2YgT2JqZWN0LmVudHJpZXMocm91dGVNZXRob2RzKSkge1xuICAgICAgY29uc3Qgcm91dGVQYXRoID0gW3BhcmFtcy5wYXRoLnJlcGxhY2UoL15cXC8rfFxcLyskL2csIFwiXCIpXVxuICAgICAgICAuZmlsdGVyKChzZWdtZW50OiBzdHJpbmcpID0+IHNlZ21lbnQgJiYgc2VnbWVudC50cmltKCkpXG4gICAgICAgIC5qb2luKFwiL1wiKTtcblxuICAgICAgY29uc3QgaGFuZGxlciA9IEZyb21Nb2RlbENvbnRyb2xsZXIuY3JlYXRlQ29tcGxleFF1ZXJ5SGFuZGxlcihcbiAgICAgICAgbWV0aG9kTmFtZVxuICAgICAgKSBhcyBhbnk7XG4gICAgICBGcm9tTW9kZWxDb250cm9sbGVyLmRlZmluZU1ldGhvZChcbiAgICAgICAgUXVlcnlDb250cm9sbGVyLFxuICAgICAgICBtZXRob2ROYW1lLFxuICAgICAgICBoYW5kbGVyXG4gICAgICApO1xuXG4gICAgICBjb25zdCBodHRwRGVjb3JhdG9yID0gSHR0cFZlcmJUb0RlY29yYXRvcihwYXJhbXMuaHR0cE1ldGhvZCBhcyBIdHRwVmVyYnMpKHJvdXRlUGF0aCB8fCB1bmRlZmluZWQpO1xuICAgICAgY29uc3QgZGVjb3JhdG9ycyA9IEZyb21Nb2RlbENvbnRyb2xsZXIuZ2V0UXVlcnlEZWNvcmF0b3JzKFxuICAgICAgICBtZXRob2ROYW1lLFxuICAgICAgICByb3V0ZVBhdGgsXG4gICAgICAgIHBhcmFtcy5odHRwTWV0aG9kXG4gICAgICApO1xuICAgICAgRnJvbU1vZGVsQ29udHJvbGxlci5hcHBseURlY29yYXRvcnMoXG4gICAgICAgIFF1ZXJ5Q29udHJvbGxlcixcbiAgICAgICAgbWV0aG9kTmFtZSxcbiAgICAgICAgW2h0dHBEZWNvcmF0b3IsIC4uLmRlY29yYXRvcnNdXG4gICAgICApO1xuICAgIH1cblxuICAgIGZvciAoY29uc3QgW21ldGhvZE5hbWUsIG9ialZhbHVlc10gb2YgT2JqZWN0LmVudHJpZXMocXVlcnlNZXRob2RzKSkge1xuICAgICAgY29uc3QgZmllbGRzID0gb2JqVmFsdWVzLmZpZWxkcyA/PyBbXTtcbiAgICAgIGNvbnN0IHJvdXRlUGF0aCA9IFtwcmVmaXgsIG1ldGhvZE5hbWUsIC4uLmZpZWxkcy5tYXAoKGYpID0+IGA6JHtmfWApXVxuICAgICAgICAuZmlsdGVyKChzZWdtZW50KSA9PiBzZWdtZW50ICYmIHNlZ21lbnQudHJpbSgpKVxuICAgICAgICAuam9pbihcIi9cIik7XG5cbiAgICAgIGNvbnN0IGhhbmRsZXIgPSBGcm9tTW9kZWxDb250cm9sbGVyLmNyZWF0ZUNvbXBsZXhRdWVyeUhhbmRsZXIoXG4gICAgICAgIG1ldGhvZE5hbWVcbiAgICAgICkgYXMgYW55O1xuICAgICAgRnJvbU1vZGVsQ29udHJvbGxlci5kZWZpbmVNZXRob2QoXG4gICAgICAgIFF1ZXJ5Q29udHJvbGxlcixcbiAgICAgICAgbWV0aG9kTmFtZSxcbiAgICAgICAgaGFuZGxlclxuICAgICAgKTtcblxuICAgICAgY29uc3QgaHR0cERlY29yYXRvciA9IEh0dHBWZXJiVG9EZWNvcmF0b3IoXCJHRVRcIiBhcyBIdHRwVmVyYnMpKHJvdXRlUGF0aCB8fCB1bmRlZmluZWQpO1xuICAgICAgY29uc3QgZGVjb3JhdG9ycyA9IEZyb21Nb2RlbENvbnRyb2xsZXIuZ2V0UXVlcnlEZWNvcmF0b3JzKFxuICAgICAgICBtZXRob2ROYW1lLFxuICAgICAgICByb3V0ZVBhdGgsXG4gICAgICAgIFwiR0VUXCIsXG4gICAgICAgIHRydWVcbiAgICAgICk7XG4gICAgICBGcm9tTW9kZWxDb250cm9sbGVyLmFwcGx5RGVjb3JhdG9ycyhcbiAgICAgICAgUXVlcnlDb250cm9sbGVyLFxuICAgICAgICBtZXRob2ROYW1lLFxuICAgICAgICBbaHR0cERlY29yYXRvciwgLi4uZGVjb3JhdG9yc11cbiAgICAgICk7XG4gICAgfVxuXG4gICAgcmV0dXJuIFF1ZXJ5Q29udHJvbGxlcjtcbiAgfVxuXG4gIHN0YXRpYyBjcmVhdGU8VCBleHRlbmRzIE1vZGVsPGFueT4+KFxuICAgIE1vZGVsQ29uc3RyOiBNb2RlbENvbnN0cnVjdG9yPFQ+LFxuICAgIG1vZHVsZUNvbmZpZ092ZXJyaWRlcz86IFJlY29yZDxzdHJpbmcsIE1vZGVsQ29udHJvbGxlckZhY3RvcnlDb25maWc+XG4gICk6IENvbnRyb2xsZXJDb25zdHJ1Y3RvcjxUPiB7XG4gICAgY29uc3QgbG9nID0gRnJvbU1vZGVsQ29udHJvbGxlci5sb2cuZm9yKEZyb21Nb2RlbENvbnRyb2xsZXIuY3JlYXRlKTtcbiAgICBjb25zdCB0YWJsZU5hbWUgPSBNb2RlbC50YWJsZU5hbWUoTW9kZWxDb25zdHIpO1xuICAgIGNvbnN0IHJvdXRlUGF0aCA9IHRvS2ViYWJDYXNlKHRhYmxlTmFtZSk7XG4gICAgY29uc3QgbW9kZWxDbGF6ek5hbWUgPSBNb2RlbENvbnN0ci5uYW1lO1xuICAgIGNvbnN0IHBlcnNpc3RlbmNlID0gRnJvbU1vZGVsQ29udHJvbGxlci5nZXRQZXJzaXN0ZW5jZShNb2RlbENvbnN0cik7XG5cbiAgICAvLyBXaGVuIHBlcnNpc3RlbmNlIGlzIGEgTW9kZWxTZXJ2aWNlLCB0aGUgQHF1ZXJ5L0Byb3V0ZSBtZXRhZGF0YSBsaXZlcyBvblxuICAgIC8vIHRoZSB1bmRlcmx5aW5nIHJlcG9zaXRvcnkgY2xhc3MgKGN1c3RvbSByZXBvKSwgbm90IG9uIE1vZGVsU2VydmljZSBpdHNlbGYuXG4gICAgLy8gUGFzcyB0aGUgcmVwbyB0byB0aGUgZmFjdG9yeSBzbyBhZGRDb21wbGV4UXVlcmllcygpIGNhbiBkaXNjb3ZlciB0aGVtLlxuICAgIGNvbnN0IGZhY3RvcnlQZXJzaXN0ZW5jZSA9XG4gICAgICBwZXJzaXN0ZW5jZSBpbnN0YW5jZW9mIE1vZGVsU2VydmljZSA/IHBlcnNpc3RlbmNlLnJlcG8gOiBwZXJzaXN0ZW5jZTtcblxuICAgIGNvbnN0IGRlY29yYXRvckNvbmZpZyA9IE1ldGFkYXRhLmdldChcbiAgICAgIE1vZGVsQ29uc3RyLFxuICAgICAgTWV0YWRhdGEua2V5KERFQ0FGX0NPTlRST0xMRVJfQ09ORklHKVxuICAgICkgYXMgTW9kZWxDb250cm9sbGVyRmFjdG9yeUNvbmZpZyB8IHVuZGVmaW5lZDtcbiAgICBjb25zdCBtb2R1bGVPdmVycmlkZSA9IG1vZHVsZUNvbmZpZ092ZXJyaWRlcz8uW01vZGVsQ29uc3RyLm5hbWVdO1xuICAgIGNvbnN0IG1lcmdlZENvbmZpZzogTW9kZWxDb250cm9sbGVyRmFjdG9yeUNvbmZpZyA9IHtcbiAgICAgIC4uLihkZWNvcmF0b3JDb25maWcgfHwge30pLFxuICAgICAgLi4uKG1vZHVsZU92ZXJyaWRlIHx8IHt9KSxcbiAgICB9O1xuXG4gICAgY29uc3QgRmFjdG9yeUNvbnRyb2xsZXIgPSBNb2RlbENvbnRyb2xsZXJGYWN0b3J5LmNyZWF0ZTxUPihcbiAgICAgIE1vZGVsQ29uc3RyLFxuICAgICAgZmFjdG9yeVBlcnNpc3RlbmNlLFxuICAgICAgbWVyZ2VkQ29uZmlnXG4gICAgKTtcbiAgICBjb25zdCBmYWN0b3J5Um91dGVzID0gKEZhY3RvcnlDb250cm9sbGVyIGFzIGFueSkuX19yb3V0ZXNfXyBhc1xuICAgICAgfCBTZXJ2ZXJSb3V0ZVtdXG4gICAgICB8IHVuZGVmaW5lZDtcblxuICAgIGNvbnN0IHsgZ2V0UEssIGFwaVByb3BlcnRpZXMsIHBhdGg6IHBrUGF0aCB9ID1cbiAgICAgIEZyb21Nb2RlbENvbnRyb2xsZXIuZ2V0Um91dGVQYXJhbWV0ZXJzRnJvbU1vZGVsKE1vZGVsQ29uc3RyKTtcblxuICAgIGxvZy5kZWJ1ZyhcbiAgICAgIGBDcmVhdGluZyBjb250cm9sbGVyIGZvciBtb2RlbDogJHttb2RlbENsYXp6TmFtZX0gd2l0aCAke2ZhY3RvcnlSb3V0ZXM/Lmxlbmd0aCA/PyAwfSBmYWN0b3J5IHJvdXRlc2BcbiAgICApO1xuXG4gICAgQENvbnRyb2xsZXIocm91dGVQYXRoKVxuICAgIEBBcGlUYWdzKG1vZGVsQ2xhenpOYW1lKVxuICAgIEBBcGlFeHRyYU1vZGVscyhNb2RlbENvbnN0cilcbiAgICBAQXV0aChNb2RlbENvbnN0cilcbiAgICBjbGFzcyBEeW5hbWljTW9kZWxDb250cm9sbGVyIGV4dGVuZHMgRGVjYWZNb2RlbENvbnRyb2xsZXI8VD4ge1xuICAgICAgcHJpdmF0ZSByZWFkb25seSBwazogc3RyaW5nID0gTW9kZWwucGsoTW9kZWxDb25zdHIpIGFzIHN0cmluZztcblxuICAgICAgcHJvdGVjdGVkIHN0YXRpYyBnZXQgY2xhc3MoKSB7XG4gICAgICAgIHJldHVybiBNb2RlbENvbnN0cjtcbiAgICAgIH1cblxuICAgICAgb3ZlcnJpZGUgZ2V0IGNsYXNzKCk6IE1vZGVsQ29uc3RydWN0b3I8VD4ge1xuICAgICAgICByZXR1cm4gTW9kZWxDb25zdHI7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0cnVjdG9yKGNsaWVudENvbnRleHQ6IERlY2FmUmVxdWVzdENvbnRleHQpIHtcbiAgICAgICAgc3VwZXIoY2xpZW50Q29udGV4dCwgRHluYW1pY01vZGVsQ29udHJvbGxlci5uYW1lKTtcbiAgICAgICAgbG9nLmluZm8oXG4gICAgICAgICAgYFJlZ2lzdGVyaW5nIGR5bmFtaWMgY29udHJvbGxlciBmb3IgbW9kZWw6ICR7dGhpcy5jbGFzcy5uYW1lfSByb3V0ZTogLyR7cm91dGVQYXRofWBcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoZmFjdG9yeVJvdXRlcykge1xuICAgICAgY29uc3Qgc29ydGVkUm91dGVzID0gWy4uLmZhY3RvcnlSb3V0ZXNdLnNvcnQoKGEsIGIpID0+IHtcbiAgICAgICAgY29uc3QgYVNlZ21lbnRzID0gYS5wYXRoLnNwbGl0KFwiL1wiKS5maWx0ZXIoQm9vbGVhbik7XG4gICAgICAgIGNvbnN0IGJTZWdtZW50cyA9IGIucGF0aC5zcGxpdChcIi9cIikuZmlsdGVyKEJvb2xlYW4pO1xuICAgICAgICBjb25zdCBhUGFyYW1Db3VudCA9IGFTZWdtZW50cy5maWx0ZXIoKHMpID0+IHMuc3RhcnRzV2l0aChcIjpcIikpLmxlbmd0aDtcbiAgICAgICAgY29uc3QgYlBhcmFtQ291bnQgPSBiU2VnbWVudHMuZmlsdGVyKChzKSA9PiBzLnN0YXJ0c1dpdGgoXCI6XCIpKS5sZW5ndGg7XG4gICAgICAgIGNvbnN0IGFMaXRlcmFsQ291bnQgPSBhU2VnbWVudHMubGVuZ3RoIC0gYVBhcmFtQ291bnQ7XG4gICAgICAgIGNvbnN0IGJMaXRlcmFsQ291bnQgPSBiU2VnbWVudHMubGVuZ3RoIC0gYlBhcmFtQ291bnQ7XG4gICAgICAgIGlmIChhTGl0ZXJhbENvdW50ICE9PSBiTGl0ZXJhbENvdW50KVxuICAgICAgICAgIHJldHVybiBiTGl0ZXJhbENvdW50IC0gYUxpdGVyYWxDb3VudDtcbiAgICAgICAgaWYgKGFQYXJhbUNvdW50ICE9PSBiUGFyYW1Db3VudClcbiAgICAgICAgICByZXR1cm4gYVBhcmFtQ291bnQgLSBiUGFyYW1Db3VudDtcbiAgICAgICAgcmV0dXJuIDA7XG4gICAgICB9KTtcbiAgICAgIGZvciAoY29uc3Qgcm91dGUgb2Ygc29ydGVkUm91dGVzKSB7XG4gICAgICAgIGNvbnN0IHJlZ2lzdHJhdGlvbiA9IEZyb21Nb2RlbENvbnRyb2xsZXIubWF0Y2hSb3V0ZShcbiAgICAgICAgICByb3V0ZSxcbiAgICAgICAgICBwa1BhdGgsXG4gICAgICAgICAgYXBpUHJvcGVydGllcyxcbiAgICAgICAgICBnZXRQSyxcbiAgICAgICAgICBNb2RlbENvbnN0cixcbiAgICAgICAgICBtb2RlbENsYXp6TmFtZSxcbiAgICAgICAgICBmYWN0b3J5UGVyc2lzdGVuY2VcbiAgICAgICAgKTtcbiAgICAgICAgaWYgKCFyZWdpc3RyYXRpb24pIGNvbnRpbnVlO1xuXG4gICAgICAgIGNvbnN0IHsgbWV0aG9kTmFtZSwgaGFuZGxlciwgZGVjb3JhdG9ycywgcGFyYW1EZWNvcmF0b3JzIH0gPVxuICAgICAgICAgIHJlZ2lzdHJhdGlvbjtcblxuICAgICAgICBjb25zdCBkZXNjcmlwdG9yID0gRnJvbU1vZGVsQ29udHJvbGxlci5kZWZpbmVNZXRob2QoXG4gICAgICAgICAgRHluYW1pY01vZGVsQ29udHJvbGxlcixcbiAgICAgICAgICBtZXRob2ROYW1lLFxuICAgICAgICAgIGhhbmRsZXJcbiAgICAgICAgKTtcblxuICAgICAgICBpZiAoZGVzY3JpcHRvcikge1xuICAgICAgICAgIGNvbnN0IGh0dHBEZWNvcmF0b3IgPSBIdHRwVmVyYlRvRGVjb3JhdG9yKHJvdXRlLm1ldGhvZCBhcyBIdHRwVmVyYnMpKFxuICAgICAgICAgICAgcm91dGUucGF0aC5yZXBsYWNlKC9eXFwvK3xcXC8rJC9nLCBcIlwiKSB8fCB1bmRlZmluZWRcbiAgICAgICAgICApO1xuICAgICAgICAgIEZyb21Nb2RlbENvbnRyb2xsZXIuYXBwbHlEZWNvcmF0b3JzKFxuICAgICAgICAgICAgRHluYW1pY01vZGVsQ29udHJvbGxlcixcbiAgICAgICAgICAgIG1ldGhvZE5hbWUsXG4gICAgICAgICAgICBbaHR0cERlY29yYXRvciwgLi4uZGVjb3JhdG9yc10sXG4gICAgICAgICAgICBwYXJhbURlY29yYXRvcnNcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIER5bmFtaWNNb2RlbENvbnRyb2xsZXIgYXMgYW55O1xuICB9XG5cbiAgc3RhdGljIGdldFJvdXRlUGFyYW1ldGVyc0Zyb21Nb2RlbDxUIGV4dGVuZHMgTW9kZWw8YW55Pj4oXG4gICAgTW9kZWxDbGF6ejogTW9kZWxDb25zdHJ1Y3RvcjxUPlxuICApOiB7XG4gICAgcGF0aDogc3RyaW5nO1xuICAgIGRlc2NyaXB0aW9uOiBzdHJpbmc7XG4gICAgYXBpUHJvcGVydGllczogRGVjYWZBcGlQcm9wZXJ0eVtdO1xuICAgIGdldFBLOiAoLi4ucGFyYW1zOiBBcnJheTxzdHJpbmcgfCBudW1iZXI+KSA9PiBzdHJpbmc7XG4gIH0ge1xuICAgIGNvbnN0IHBrID0gTW9kZWwucGsoTW9kZWxDbGF6eikgYXMga2V5b2YgTW9kZWw8YW55PjtcbiAgICBjb25zdCBjb21wb3NlZCA9IE1ldGFkYXRhLmdldChcbiAgICAgIE1vZGVsQ2xhenosXG4gICAgICBNZXRhZGF0YS5rZXkoREJLZXlzLkNPTVBPU0VELCBwaylcbiAgICApO1xuICAgIGNvbnN0IGNvbXBvc2VkS2V5cyA9IGNvbXBvc2VkPy5hcmdzID8/IFtdO1xuXG4gICAgY29uc3QgdW5pcXVlS2V5cyA9XG4gICAgICBBcnJheS5pc0FycmF5KGNvbXBvc2VkS2V5cykgJiYgY29tcG9zZWRLZXlzLmxlbmd0aCA+IDBcbiAgICAgICAgPyBBcnJheS5mcm9tKG5ldyBTZXQoWy4uLmNvbXBvc2VkS2V5c10pKVxuICAgICAgICA6IEFycmF5LmZyb20obmV3IFNldChbcGtdKSk7XG5cbiAgICBjb25zdCBkZXNjcmlwdGlvbiA9IE1ldGFkYXRhLmRlc2NyaXB0aW9uKE1vZGVsQ2xhenopID8/IFwiXCI7XG4gICAgY29uc3QgcGF0aCA9IGA6JHt1bmlxdWVLZXlzLmpvaW4oXCIvOlwiKX1gO1xuICAgIGNvbnN0IGFwaVByb3BlcnRpZXM6IERlY2FmQXBpUHJvcGVydHlbXSA9IHVuaXF1ZUtleXMubWFwKChrZXkpID0+IHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIG5hbWU6IGtleSxcbiAgICAgICAgZGVzY3JpcHRpb246IE1ldGFkYXRhLmRlc2NyaXB0aW9uKE1vZGVsQ2xhenosIGtleSksXG4gICAgICAgIHJlcXVpcmVkOiB0cnVlLFxuICAgICAgICB0eXBlOiBTdHJpbmcsXG4gICAgICB9O1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIHBhdGgsXG4gICAgICBkZXNjcmlwdGlvbixcbiAgICAgIGFwaVByb3BlcnRpZXMsXG4gICAgICBnZXRQSzogKC4uLnBhcmFtczogQXJyYXk8c3RyaW5nIHwgbnVtYmVyPikgPT5cbiAgICAgICAgY29tcG9zZWQ/LnNlcGFyYXRvciA/IHBhcmFtcy5qb2luKGNvbXBvc2VkLnNlcGFyYXRvcikgOiBwYXJhbXMuam9pbihcIlwiKSxcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgZGVmaW5lTWV0aG9kKFxuICAgIHRhcmdldDogYW55LFxuICAgIG1ldGhvZE5hbWU6IHN0cmluZyxcbiAgICBoYW5kbGVyOiAoLi4uYXJnczogYW55W10pID0+IGFueVxuICApOiBQcm9wZXJ0eURlc2NyaXB0b3IgfCB1bmRlZmluZWQge1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShcbiAgICAgIHRhcmdldC5wcm90b3R5cGUgfHwgdGFyZ2V0LFxuICAgICAgbWV0aG9kTmFtZSxcbiAgICAgIHtcbiAgICAgICAgdmFsdWU6IGhhbmRsZXIsXG4gICAgICAgIHdyaXRhYmxlOiBmYWxzZSxcbiAgICAgICAgY29uZmlndXJhYmxlOiB0cnVlLFxuICAgICAgICBlbnVtZXJhYmxlOiBmYWxzZSxcbiAgICAgIH1cbiAgICApO1xuXG4gICAgcmV0dXJuIE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IoXG4gICAgICB0YXJnZXQucHJvdG90eXBlIHx8IHRhcmdldCxcbiAgICAgIG1ldGhvZE5hbWVcbiAgICApO1xuICB9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgYXBwbHlEZWNvcmF0b3JzKFxuICAgIHRhcmdldDogYW55LFxuICAgIG1ldGhvZE5hbWU6IHN0cmluZyxcbiAgICBtZXRob2REZWNvcmF0b3JzOiBBcnJheTwodGFyZ2V0OiBhbnksIGtleTogc3RyaW5nLCBkZXNjOiBhbnkpID0+IHZvaWQ+LFxuICAgIHBhcmFtRGVjb3JhdG9yczogQXJyYXk8eyBkZWNvcmF0b3I6IFBhcmFtZXRlckRlY29yYXRvcjsgaW5kZXg6IG51bWJlciB9PiA9IFtdXG4gICkge1xuICAgIGNvbnN0IHByb3RvID0gdGFyZ2V0Py5wcm90b3R5cGUgPz8gdGFyZ2V0O1xuICAgIGNvbnN0IGRlc2NyaXB0b3IgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHByb3RvLCBtZXRob2ROYW1lKTtcbiAgICBtZXRob2REZWNvcmF0b3JzLmZvckVhY2goKGQpID0+IGQocHJvdG8sIG1ldGhvZE5hbWUsIGRlc2NyaXB0b3IpKTtcbiAgICBwYXJhbURlY29yYXRvcnMuZm9yRWFjaCgoeyBkZWNvcmF0b3IsIGluZGV4IH0pID0+XG4gICAgICBkZWNvcmF0b3IocHJvdG8sIG1ldGhvZE5hbWUsIGluZGV4KVxuICAgICk7XG4gIH1cblxuICBwcml2YXRlIHN0YXRpYyBtYXRjaFJvdXRlKFxuICAgIHJvdXRlOiBTZXJ2ZXJSb3V0ZSxcbiAgICBwa1BhdGg6IHN0cmluZyxcbiAgICBhcGlQcm9wZXJ0aWVzOiBEZWNhZkFwaVByb3BlcnR5W10sXG4gICAgZ2V0UEs6ICguLi5wOiBBcnJheTxzdHJpbmcgfCBudW1iZXI+KSA9PiBzdHJpbmcsXG4gICAgTW9kZWxDb25zdHI6IE1vZGVsQ29uc3RydWN0b3I8YW55PixcbiAgICBtb2RlbENsYXp6TmFtZTogc3RyaW5nLFxuICAgIHBlcnNpc3RlbmNlPzogYW55XG4gICk6IHtcbiAgICBtZXRob2ROYW1lOiBzdHJpbmc7XG4gICAgaGFuZGxlcjogKC4uLmFyZ3M6IGFueVtdKSA9PiBhbnk7XG4gICAgZGVjb3JhdG9yczogQXJyYXk8KHRhcmdldDogYW55LCBrZXk6IHN0cmluZywgZGVzYzogYW55KSA9PiB2b2lkPjtcbiAgICBwYXJhbURlY29yYXRvcnM6IEFycmF5PHsgZGVjb3JhdG9yOiBQYXJhbWV0ZXJEZWNvcmF0b3I7IGluZGV4OiBudW1iZXIgfT47XG4gIH0gfCB1bmRlZmluZWQge1xuICAgIGNvbnN0IHsgbWV0aG9kLCBwYXRoIH0gPSByb3V0ZTtcbiAgICBjb25zdCBub3JtYWxpemVkUGF0aCA9IHBhdGgucmVwbGFjZSgvXlxcLyt8XFwvKyQvZywgXCJcIik7XG5cbiAgICBpZiAobWV0aG9kID09PSBcIlBPU1RcIiAmJiBub3JtYWxpemVkUGF0aCA9PT0gXCJcIikge1xuICAgICAgcmV0dXJuIEZyb21Nb2RlbENvbnRyb2xsZXIuY3JlYXRlUmVnaXN0cmF0aW9uKFxuICAgICAgICBcImNyZWF0ZVwiLFxuICAgICAgICBGcm9tTW9kZWxDb250cm9sbGVyLmNyZWF0ZUNyZWF0ZUhhbmRsZXIoTW9kZWxDb25zdHIsIG1vZGVsQ2xhenpOYW1lKSxcbiAgICAgICAgRnJvbU1vZGVsQ29udHJvbGxlci5jcmVhdGVDcmVhdGVEZWNvcmF0b3JzKE1vZGVsQ29uc3RyLCBtb2RlbENsYXp6TmFtZSksXG4gICAgICAgIFt7IGRlY29yYXRvcjogRGVjYWZCb2R5KCkgYXMgYW55LCBpbmRleDogMCB9LCB7IGRlY29yYXRvcjogUmVzcG9uc2UoeyBwYXNzdGhyb3VnaDogdHJ1ZSB9KSBhcyBhbnksIGluZGV4OiAxIH1dXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmIChtZXRob2QgPT09IFwiUE9TVFwiICYmIG5vcm1hbGl6ZWRQYXRoID09PSBcImJ1bGtcIikge1xuICAgICAgcmV0dXJuIEZyb21Nb2RlbENvbnRyb2xsZXIuY3JlYXRlUmVnaXN0cmF0aW9uKFxuICAgICAgICBcImNyZWF0ZUFsbFwiLFxuICAgICAgICBGcm9tTW9kZWxDb250cm9sbGVyLmNyZWF0ZUJ1bGtDcmVhdGVIYW5kbGVyKE1vZGVsQ29uc3RyLCBtb2RlbENsYXp6TmFtZSksXG4gICAgICAgIEZyb21Nb2RlbENvbnRyb2xsZXIuYnVsa0NyZWF0ZURlY29yYXRvcnMoTW9kZWxDb25zdHIsIG1vZGVsQ2xhenpOYW1lKSxcbiAgICAgICAgW3sgZGVjb3JhdG9yOiBEZWNhZkJvZHkoKSBhcyBhbnksIGluZGV4OiAwIH0sIHsgZGVjb3JhdG9yOiBSZXNwb25zZSh7IHBhc3N0aHJvdWdoOiB0cnVlIH0pIGFzIGFueSwgaW5kZXg6IDEgfV1cbiAgICAgICk7XG4gICAgfVxuXG4gICAgaWYgKG1ldGhvZCA9PT0gXCJHRVRcIiAmJiBub3JtYWxpemVkUGF0aCA9PT0gXCJidWxrXCIpIHtcbiAgICAgIHJldHVybiBGcm9tTW9kZWxDb250cm9sbGVyLmNyZWF0ZVJlZ2lzdHJhdGlvbihcbiAgICAgICAgXCJyZWFkQWxsXCIsXG4gICAgICAgIEZyb21Nb2RlbENvbnRyb2xsZXIuY3JlYXRlQnVsa1JlYWRIYW5kbGVyKG1vZGVsQ2xhenpOYW1lKSxcbiAgICAgICAgRnJvbU1vZGVsQ29udHJvbGxlci5idWxrUmVhZERlY29yYXRvcnMoTW9kZWxDb25zdHIsIG1vZGVsQ2xhenpOYW1lKSxcbiAgICAgICAgW3sgZGVjb3JhdG9yOiBRdWVyeShcImlkc1wiKSBhcyBhbnksIGluZGV4OiAwIH1dXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmIChtZXRob2QgPT09IFwiUFVUXCIgJiYgbm9ybWFsaXplZFBhdGggPT09IFwiYnVsa1wiKSB7XG4gICAgICByZXR1cm4gRnJvbU1vZGVsQ29udHJvbGxlci5jcmVhdGVSZWdpc3RyYXRpb24oXG4gICAgICAgIFwidXBkYXRlQWxsXCIsXG4gICAgICAgIEZyb21Nb2RlbENvbnRyb2xsZXIuY3JlYXRlQnVsa1VwZGF0ZUhhbmRsZXIobW9kZWxDbGF6ek5hbWUpLFxuICAgICAgICBGcm9tTW9kZWxDb250cm9sbGVyLmJ1bGtVcGRhdGVEZWNvcmF0b3JzKE1vZGVsQ29uc3RyLCBtb2RlbENsYXp6TmFtZSwgYXBpUHJvcGVydGllcyksXG4gICAgICAgIFt7IGRlY29yYXRvcjogRGVjYWZCb2R5KCkgYXMgYW55LCBpbmRleDogMCB9LCB7IGRlY29yYXRvcjogUmVzcG9uc2UoeyBwYXNzdGhyb3VnaDogdHJ1ZSB9KSBhcyBhbnksIGluZGV4OiAxIH1dXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmIChtZXRob2QgPT09IFwiREVMRVRFXCIgJiYgbm9ybWFsaXplZFBhdGggPT09IFwiYnVsa1wiKSB7XG4gICAgICByZXR1cm4gRnJvbU1vZGVsQ29udHJvbGxlci5jcmVhdGVSZWdpc3RyYXRpb24oXG4gICAgICAgIFwiZGVsZXRlQWxsXCIsXG4gICAgICAgIEZyb21Nb2RlbENvbnRyb2xsZXIuY3JlYXRlQnVsa0RlbGV0ZUhhbmRsZXIobW9kZWxDbGF6ek5hbWUpLFxuICAgICAgICBGcm9tTW9kZWxDb250cm9sbGVyLmJ1bGtEZWxldGVEZWNvcmF0b3JzKE1vZGVsQ29uc3RyLCBtb2RlbENsYXp6TmFtZSwgYXBpUHJvcGVydGllcyksXG4gICAgICAgIFt7IGRlY29yYXRvcjogUXVlcnkoXCJpZHNcIikgYXMgYW55LCBpbmRleDogMCB9LCB7IGRlY29yYXRvcjogUmVzcG9uc2UoeyBwYXNzdGhyb3VnaDogdHJ1ZSB9KSBhcyBhbnksIGluZGV4OiAxIH1dXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmIChtZXRob2QgPT09IFwiR0VUXCIgJiYgbm9ybWFsaXplZFBhdGggPT09IHBrUGF0aCkge1xuICAgICAgcmV0dXJuIEZyb21Nb2RlbENvbnRyb2xsZXIuY3JlYXRlUmVnaXN0cmF0aW9uKFxuICAgICAgICBcInJlYWRcIixcbiAgICAgICAgRnJvbU1vZGVsQ29udHJvbGxlci5jcmVhdGVSZWFkSGFuZGxlcihnZXRQSywgbW9kZWxDbGF6ek5hbWUpLFxuICAgICAgICBGcm9tTW9kZWxDb250cm9sbGVyLnJlYWREZWNvcmF0b3JzKE1vZGVsQ29uc3RyLCBtb2RlbENsYXp6TmFtZSwgYXBpUHJvcGVydGllcywgcGtQYXRoKSxcbiAgICAgICAgW3sgZGVjb3JhdG9yOiBEZWNhZlBhcmFtcyhhcGlQcm9wZXJ0aWVzKSBhcyBhbnksIGluZGV4OiAwIH1dXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmIChtZXRob2QgPT09IFwiUFVUXCIgJiYgbm9ybWFsaXplZFBhdGggPT09IHBrUGF0aCkge1xuICAgICAgcmV0dXJuIEZyb21Nb2RlbENvbnRyb2xsZXIuY3JlYXRlUmVnaXN0cmF0aW9uKFxuICAgICAgICBcInVwZGF0ZVwiLFxuICAgICAgICBGcm9tTW9kZWxDb250cm9sbGVyLmNyZWF0ZVVwZGF0ZUhhbmRsZXIoZ2V0UEssIE1vZGVsQ29uc3RyLCBtb2RlbENsYXp6TmFtZSksXG4gICAgICAgIEZyb21Nb2RlbENvbnRyb2xsZXIudXBkYXRlRGVjb3JhdG9ycyhNb2RlbENvbnN0ciwgbW9kZWxDbGF6ek5hbWUsIGFwaVByb3BlcnRpZXMsIHBrUGF0aCksXG4gICAgICAgIFtcbiAgICAgICAgICB7IGRlY29yYXRvcjogRGVjYWZQYXJhbXMoYXBpUHJvcGVydGllcykgYXMgYW55LCBpbmRleDogMCB9LFxuICAgICAgICAgIHsgZGVjb3JhdG9yOiBEZWNhZkJvZHkoKSBhcyBhbnksIGluZGV4OiAxIH0sXG4gICAgICAgICAgeyBkZWNvcmF0b3I6IFJlc3BvbnNlKHsgcGFzc3Rocm91Z2g6IHRydWUgfSkgYXMgYW55LCBpbmRleDogMiB9LFxuICAgICAgICBdXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmIChtZXRob2QgPT09IFwiREVMRVRFXCIgJiYgbm9ybWFsaXplZFBhdGggPT09IHBrUGF0aCkge1xuICAgICAgcmV0dXJuIEZyb21Nb2RlbENvbnRyb2xsZXIuY3JlYXRlUmVnaXN0cmF0aW9uKFxuICAgICAgICBcImRlbGV0ZVwiLFxuICAgICAgICBGcm9tTW9kZWxDb250cm9sbGVyLmNyZWF0ZURlbGV0ZUhhbmRsZXIoZ2V0UEssIG1vZGVsQ2xhenpOYW1lKSxcbiAgICAgICAgRnJvbU1vZGVsQ29udHJvbGxlci5kZWxldGVEZWNvcmF0b3JzKE1vZGVsQ29uc3RyLCBtb2RlbENsYXp6TmFtZSwgYXBpUHJvcGVydGllcywgcGtQYXRoKSxcbiAgICAgICAgW1xuICAgICAgICAgIHsgZGVjb3JhdG9yOiBEZWNhZlBhcmFtcyhhcGlQcm9wZXJ0aWVzKSBhcyBhbnksIGluZGV4OiAwIH0sXG4gICAgICAgICAgeyBkZWNvcmF0b3I6IFJlc3BvbnNlKHsgcGFzc3Rocm91Z2g6IHRydWUgfSkgYXMgYW55LCBpbmRleDogMSB9LFxuICAgICAgICBdXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIENvbXBvc2VkIFBLIGZhbGxiYWNrIHJvdXRlcyAoZmlsdGVyRW1wdHkpIOKAlCBwYXRoIGRpZmZlcnMgZnJvbSBwa1BhdGhcbiAgICBjb25zdCBmYWxsYmFja1NlZ21lbnRzID0gbm9ybWFsaXplZFBhdGguc3BsaXQoXCIvXCIpLmZpbHRlcihCb29sZWFuKTtcbiAgICBjb25zdCBpc0FsbFBhcmFtcyA9IGZhbGxiYWNrU2VnbWVudHMubGVuZ3RoID4gMCAmJiBmYWxsYmFja1NlZ21lbnRzLmV2ZXJ5KChzKSA9PiBzLnN0YXJ0c1dpdGgoXCI6XCIpKTtcbiAgICBpZiAoaXNBbGxQYXJhbXMgJiYgbm9ybWFsaXplZFBhdGggIT09IHBrUGF0aCkge1xuICAgICAgY29uc3QgZmFsbGJhY2tBcGlQcm9wcyA9IGZhbGxiYWNrU2VnbWVudHMubWFwKChzKSA9PiBzLnNsaWNlKDEpKS5tYXAoKG5hbWUpID0+ICh7XG4gICAgICAgIG5hbWUsXG4gICAgICAgIGRlc2NyaXB0aW9uOiBgJHtuYW1lfSBwYXJhbWV0ZXJgLFxuICAgICAgICByZXF1aXJlZDogdHJ1ZSxcbiAgICAgICAgdHlwZTogU3RyaW5nLFxuICAgICAgfSkpO1xuICAgICAgY29uc3Qgc3VmZml4ID0gZmFsbGJhY2tTZWdtZW50cy5tYXAoKHMpID0+IHMuc2xpY2UoMSkpLmpvaW4oXCJBbmRcIik7XG4gICAgICBpZiAobWV0aG9kID09PSBcIkdFVFwiKSB7XG4gICAgICAgIHJldHVybiBGcm9tTW9kZWxDb250cm9sbGVyLmNyZWF0ZVJlZ2lzdHJhdGlvbihcbiAgICAgICAgICBgcmVhZEJ5JHtzdWZmaXh9YCxcbiAgICAgICAgICBGcm9tTW9kZWxDb250cm9sbGVyLmNyZWF0ZVJlYWRIYW5kbGVyKGdldFBLLCBtb2RlbENsYXp6TmFtZSksXG4gICAgICAgICAgRnJvbU1vZGVsQ29udHJvbGxlci5yZWFkRGVjb3JhdG9ycyhNb2RlbENvbnN0ciwgbW9kZWxDbGF6ek5hbWUsIGZhbGxiYWNrQXBpUHJvcHMsIG5vcm1hbGl6ZWRQYXRoKSxcbiAgICAgICAgICBbeyBkZWNvcmF0b3I6IERlY2FmUGFyYW1zKGZhbGxiYWNrQXBpUHJvcHMpIGFzIGFueSwgaW5kZXg6IDAgfV1cbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICAgIGlmIChtZXRob2QgPT09IFwiUFVUXCIpIHtcbiAgICAgICAgcmV0dXJuIEZyb21Nb2RlbENvbnRyb2xsZXIuY3JlYXRlUmVnaXN0cmF0aW9uKFxuICAgICAgICAgIGB1cGRhdGVCeSR7c3VmZml4fWAsXG4gICAgICAgICAgRnJvbU1vZGVsQ29udHJvbGxlci5jcmVhdGVVcGRhdGVIYW5kbGVyKGdldFBLLCBNb2RlbENvbnN0ciwgbW9kZWxDbGF6ek5hbWUpLFxuICAgICAgICAgIEZyb21Nb2RlbENvbnRyb2xsZXIudXBkYXRlRGVjb3JhdG9ycyhNb2RlbENvbnN0ciwgbW9kZWxDbGF6ek5hbWUsIGZhbGxiYWNrQXBpUHJvcHMsIG5vcm1hbGl6ZWRQYXRoKSxcbiAgICAgICAgICBbXG4gICAgICAgICAgICB7IGRlY29yYXRvcjogRGVjYWZQYXJhbXMoZmFsbGJhY2tBcGlQcm9wcykgYXMgYW55LCBpbmRleDogMCB9LFxuICAgICAgICAgICAgeyBkZWNvcmF0b3I6IERlY2FmQm9keSgpIGFzIGFueSwgaW5kZXg6IDEgfSxcbiAgICAgICAgICAgIHsgZGVjb3JhdG9yOiBSZXNwb25zZSh7IHBhc3N0aHJvdWdoOiB0cnVlIH0pIGFzIGFueSwgaW5kZXg6IDIgfSxcbiAgICAgICAgICBdXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgICBpZiAobWV0aG9kID09PSBcIkRFTEVURVwiKSB7XG4gICAgICAgIHJldHVybiBGcm9tTW9kZWxDb250cm9sbGVyLmNyZWF0ZVJlZ2lzdHJhdGlvbihcbiAgICAgICAgICBgZGVsZXRlQnkke3N1ZmZpeH1gLFxuICAgICAgICAgIEZyb21Nb2RlbENvbnRyb2xsZXIuY3JlYXRlRGVsZXRlSGFuZGxlcihnZXRQSywgbW9kZWxDbGF6ek5hbWUpLFxuICAgICAgICAgIEZyb21Nb2RlbENvbnRyb2xsZXIuZGVsZXRlRGVjb3JhdG9ycyhNb2RlbENvbnN0ciwgbW9kZWxDbGF6ek5hbWUsIGZhbGxiYWNrQXBpUHJvcHMsIG5vcm1hbGl6ZWRQYXRoKSxcbiAgICAgICAgICBbXG4gICAgICAgICAgICB7IGRlY29yYXRvcjogRGVjYWZQYXJhbXMoZmFsbGJhY2tBcGlQcm9wcykgYXMgYW55LCBpbmRleDogMCB9LFxuICAgICAgICAgICAgeyBkZWNvcmF0b3I6IFJlc3BvbnNlKHsgcGFzc3Rocm91Z2g6IHRydWUgfSkgYXMgYW55LCBpbmRleDogMSB9LFxuICAgICAgICAgIF1cbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAobWV0aG9kID09PSBcIkdFVFwiICYmIG5vcm1hbGl6ZWRQYXRoID09PSBcInN0YXRlbWVudC86bWV0aG9kLyphcmdzXCIpIHtcbiAgICAgIHJldHVybiBGcm9tTW9kZWxDb250cm9sbGVyLmNyZWF0ZVJlZ2lzdHJhdGlvbihcbiAgICAgICAgXCJzdGF0ZW1lbnRcIixcbiAgICAgICAgRnJvbU1vZGVsQ29udHJvbGxlci5jcmVhdGVTdGF0ZW1lbnRIYW5kbGVyKG1vZGVsQ2xhenpOYW1lKSxcbiAgICAgICAgRnJvbU1vZGVsQ29udHJvbGxlci5zdGF0ZW1lbnREZWNvcmF0b3JzKE1vZGVsQ29uc3RyLCBtb2RlbENsYXp6TmFtZSksXG4gICAgICAgIFtcbiAgICAgICAgICB7IGRlY29yYXRvcjogUGFyYW0oXCJtZXRob2RcIikgYXMgYW55LCBpbmRleDogMCB9LFxuICAgICAgICAgIHsgZGVjb3JhdG9yOiBQYXJhbShcImFyZ3NcIikgYXMgYW55LCBpbmRleDogMSB9LFxuICAgICAgICAgIHsgZGVjb3JhdG9yOiBEZWNhZlF1ZXJ5KCkgYXMgYW55LCBpbmRleDogMiB9LFxuICAgICAgICBdXG4gICAgICApO1xuICAgIH1cblxuICAgIGNvbnN0IHN0YXRlbWVudFJvdXRlczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHtcbiAgICAgIFwibGlzdEJ5LzprZXlcIjogUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLkxJU1RfQlksXG4gICAgICBcInBhZ2luYXRlQnkvOmtleS86cGFnZVwiOiBQcmVwYXJlZFN0YXRlbWVudEtleXMuUEFHRV9CWSxcbiAgICAgIFwiZmluZC86dmFsdWVcIjogUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLkZJTkQsXG4gICAgICBcInBhZ2UvOnZhbHVlXCI6IFByZXBhcmVkU3RhdGVtZW50S2V5cy5QQUdFLFxuICAgICAgXCJmaW5kT25lQnkvOmtleS86dmFsdWVcIjogUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLkZJTkRfT05FX0JZLFxuICAgICAgXCJmaW5kQnkvOmtleS86dmFsdWVcIjogUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLkZJTkRfQlksXG4gICAgICBcImNvdW50T2YvOmZpZWxkXCI6IFByZXBhcmVkU3RhdGVtZW50S2V5cy5DT1VOVF9PRixcbiAgICAgIFwibWF4T2YvOmZpZWxkXCI6IFByZXBhcmVkU3RhdGVtZW50S2V5cy5NQVhfT0YsXG4gICAgICBcIm1pbk9mLzpmaWVsZFwiOiBQcmVwYXJlZFN0YXRlbWVudEtleXMuTUlOX09GLFxuICAgICAgXCJhdmdPZi86ZmllbGRcIjogUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLkFWR19PRixcbiAgICAgIFwic3VtT2YvOmZpZWxkXCI6IFByZXBhcmVkU3RhdGVtZW50S2V5cy5TVU1fT0YsXG4gICAgICBcImRpc3RpbmN0T2YvOmZpZWxkXCI6IFByZXBhcmVkU3RhdGVtZW50S2V5cy5ESVNUSU5DVF9PRixcbiAgICAgIFwiZ3JvdXBPZi86ZmllbGRcIjogUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLkdST1VQX09GLFxuICAgIH07XG5cbiAgICBjb25zdCBzdGF0ZW1lbnRLZXkgPSBzdGF0ZW1lbnRSb3V0ZXNbbm9ybWFsaXplZFBhdGhdO1xuICAgIGlmIChzdGF0ZW1lbnRLZXkgJiYgbWV0aG9kID09PSBcIkdFVFwiKSB7XG4gICAgICByZXR1cm4gRnJvbU1vZGVsQ29udHJvbGxlci5jcmVhdGVSZWdpc3RyYXRpb24oXG4gICAgICAgIEZyb21Nb2RlbENvbnRyb2xsZXIuc3RhdGVtZW50TWV0aG9kTmFtZShub3JtYWxpemVkUGF0aCksXG4gICAgICAgIEZyb21Nb2RlbENvbnRyb2xsZXIuY3JlYXRlU3RhdGVtZW50U2hvcnRjdXRIYW5kbGVyKHN0YXRlbWVudEtleSwgbW9kZWxDbGF6ek5hbWUpLFxuICAgICAgICBGcm9tTW9kZWxDb250cm9sbGVyLnN0YXRlbWVudFNob3J0Y3V0RGVjb3JhdG9ycyhNb2RlbENvbnN0ciwgbW9kZWxDbGF6ek5hbWUsIG5vcm1hbGl6ZWRQYXRoLCBzdGF0ZW1lbnRLZXkpLFxuICAgICAgICBGcm9tTW9kZWxDb250cm9sbGVyLnN0YXRlbWVudFNob3J0Y3V0UGFyYW1zKG5vcm1hbGl6ZWRQYXRoKVxuICAgICAgKTtcbiAgICB9XG5cbiAgICBpZiAobWV0aG9kID09PSBcIkdFVFwiICYmIG5vcm1hbGl6ZWRQYXRoLnN0YXJ0c1dpdGgoXCJxdWVyeS9cIikpIHtcbiAgICAgIGNvbnN0IHF1ZXJ5TWV0aG9kID0gbm9ybWFsaXplZFBhdGgucmVwbGFjZSgvXnF1ZXJ5XFwvLywgXCJcIikuc3BsaXQoXCIvXCIpWzBdO1xuICAgICAgcmV0dXJuIEZyb21Nb2RlbENvbnRyb2xsZXIuY3JlYXRlUmVnaXN0cmF0aW9uKFxuICAgICAgICBxdWVyeU1ldGhvZCxcbiAgICAgICAgRnJvbU1vZGVsQ29udHJvbGxlci5jcmVhdGVDb21wbGV4UXVlcnlIYW5kbGVyKHF1ZXJ5TWV0aG9kKSxcbiAgICAgICAgRnJvbU1vZGVsQ29udHJvbGxlci5nZXRRdWVyeURlY29yYXRvcnMocXVlcnlNZXRob2QsIG5vcm1hbGl6ZWRQYXRoLCBcIkdFVFwiLCB0cnVlKSxcbiAgICAgICAgRnJvbU1vZGVsQ29udHJvbGxlci5jb21wbGV4UXVlcnlQYXJhbXMobm9ybWFsaXplZFBhdGgpXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIEZhbGxiYWNrIGZvciBjdXN0b20gQHJvdXRlKCkgcGF0aHMgKGUuZy4gXCJtZXRhZGF0YS9mb3ItcHJvZHVjdC86cHJvZHVjdENvZGVcIilcbiAgICBjb25zdCBwYXRoU2VnbWVudHMgPSBub3JtYWxpemVkUGF0aC5zcGxpdChcIi9cIikuZmlsdGVyKEJvb2xlYW4pO1xuICAgIGNvbnN0IGtub3duUHJlZml4ZXMgPSBuZXcgU2V0PHN0cmluZz4oW1xuICAgICAgXCJsaXN0QnlcIiwgXCJmaW5kQnlcIiwgXCJmaW5kQnlQYWdpbmF0ZVwiLCBcImZpbmRPbmVCeVwiLCBcInBhZ2luYXRlQnlcIixcbiAgICAgIFwiZmluZFwiLCBcInBhZ2VcIiwgXCJjb3VudE9mXCIsIFwibWF4T2ZcIiwgXCJtaW5PZlwiLCBcImF2Z09mXCIsIFwic3VtT2ZcIixcbiAgICAgIFwiZGlzdGluY3RPZlwiLCBcImdyb3VwT2ZcIiwgXCJzdGF0ZW1lbnRcIiwgXCJidWxrXCIsIFwicXVlcnlcIixcbiAgICBdKTtcbiAgICBpZiAoXG4gICAgICBwYXRoU2VnbWVudHMubGVuZ3RoID4gMCAmJlxuICAgICAgIW5vcm1hbGl6ZWRQYXRoLnN0YXJ0c1dpdGgoXCJxdWVyeS9cIikgJiZcbiAgICAgICFrbm93blByZWZpeGVzLmhhcyhwYXRoU2VnbWVudHNbMF0pXG4gICAgKSB7XG4gICAgICAvLyBMb29rIHVwIHRoZSBhY3R1YWwgbWV0aG9kIG5hbWUgZnJvbSBAcm91dGUgbWV0YWRhdGEgYnkgbWF0Y2hpbmcgdGhlIHBhdGguXG4gICAgICAvLyBUaGUgQHJvdXRlIGRlY29yYXRvciBzdG9yZXMgeyBwYXRoLCBodHRwTWV0aG9kLCBoYW5kbGVyIH0ga2V5ZWQgYnkgbWV0aG9kIG5hbWVcbiAgICAgIC8vIG9uIHRoZSByZXBvc2l0b3J5IGNvbnN0cnVjdG9yLlxuICAgICAgY29uc3Qgcm91dGVNZXRhZGF0YTogUmVjb3JkPHN0cmluZywgYW55PiA9XG4gICAgICAgIE1ldGFkYXRhLmdldChcbiAgICAgICAgICAocGVyc2lzdGVuY2UgYXMgYW55KT8uY29uc3RydWN0b3IsXG4gICAgICAgICAgTWV0YWRhdGEua2V5KERFQ0FGX1JPVVRFKVxuICAgICAgICApID8/IHt9O1xuICAgICAgY29uc3QgbWF0Y2hlZEVudHJ5ID0gT2JqZWN0LmVudHJpZXMocm91dGVNZXRhZGF0YSkuZmluZChcbiAgICAgICAgKFssIGluZm9dKSA9PlxuICAgICAgICAgIGluZm8gJiZcbiAgICAgICAgICB0eXBlb2YgaW5mbyA9PT0gXCJvYmplY3RcIiAmJlxuICAgICAgICAgIGluZm8ucGF0aD8ucmVwbGFjZSgvXlxcLyt8XFwvKyQvZywgXCJcIikgPT09IG5vcm1hbGl6ZWRQYXRoXG4gICAgICApO1xuICAgICAgY29uc3QgYWN0dWFsTWV0aG9kTmFtZSA9IG1hdGNoZWRFbnRyeT8uWzBdIHx8IHBhdGhTZWdtZW50c1swXTtcblxuICAgICAgY29uc3QgcGFyYW1TZWdtZW50cyA9IHBhdGhTZWdtZW50cy5maWx0ZXIoKHMpID0+IHMuc3RhcnRzV2l0aChcIjpcIikpO1xuICAgICAgY29uc3QgYXBpUGF0aFBhcmFtcyA9IHBhcmFtU2VnbWVudHMubWFwKChzKSA9PiBzLnNsaWNlKDEpKS5tYXAoKG5hbWUpID0+ICh7XG4gICAgICAgIG5hbWUsXG4gICAgICAgIGRlc2NyaXB0aW9uOiBgJHtuYW1lfSBwYXJhbWV0ZXIgZm9yIHRoZSBxdWVyeWAsXG4gICAgICAgIHJlcXVpcmVkOiB0cnVlLFxuICAgICAgICB0eXBlOiBTdHJpbmcsXG4gICAgICB9KSk7XG5cbiAgICAgIHJldHVybiBGcm9tTW9kZWxDb250cm9sbGVyLmNyZWF0ZVJlZ2lzdHJhdGlvbihcbiAgICAgICAgYWN0dWFsTWV0aG9kTmFtZSxcbiAgICAgICAgRnJvbU1vZGVsQ29udHJvbGxlci5jcmVhdGVDdXN0b21Sb3V0ZUhhbmRsZXIoYWN0dWFsTWV0aG9kTmFtZSksXG4gICAgICAgIFtcbiAgICAgICAgICAuLi5hcGlQYXRoUGFyYW1zLm1hcCgocCkgPT4gQXBpUGFyYW0ocCkpLFxuICAgICAgICAgIEFwaU9wZXJhdGlvbih7IHN1bW1hcnk6IGBSZXRyaWV2ZSByZWNvcmRzIHVzaW5nIFwiJHthY3R1YWxNZXRob2ROYW1lfVwiLmAgfSksXG4gICAgICAgICAgQXBpT2tSZXNwb25zZSh7IGRlc2NyaXB0aW9uOiBcIlJlc3VsdCBzdWNjZXNzZnVsbHkgcmV0cmlldmVkLlwiIH0pLFxuICAgICAgICAgIEFwaU5vQ29udGVudFJlc3BvbnNlKHsgZGVzY3JpcHRpb246IFwiTm8gY29udGVudCByZXR1cm5lZCBieSB0aGUgbWV0aG9kLlwiIH0pLFxuICAgICAgICBdLFxuICAgICAgICBGcm9tTW9kZWxDb250cm9sbGVyLmNvbXBsZXhRdWVyeVBhcmFtcyhub3JtYWxpemVkUGF0aClcbiAgICAgICk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIGNyZWF0ZVJlZ2lzdHJhdGlvbihcbiAgICBtZXRob2ROYW1lOiBzdHJpbmcsXG4gICAgaGFuZGxlcjogKC4uLmFyZ3M6IGFueVtdKSA9PiBhbnksXG4gICAgZGVjb3JhdG9yczogQXJyYXk8KHRhcmdldDogYW55LCBrZXk6IHN0cmluZywgZGVzYzogYW55KSA9PiB2b2lkPixcbiAgICBwYXJhbURlY29yYXRvcnM6IEFycmF5PHsgZGVjb3JhdG9yOiBQYXJhbWV0ZXJEZWNvcmF0b3I7IGluZGV4OiBudW1iZXIgfT5cbiAgKSB7XG4gICAgcmV0dXJuIHsgbWV0aG9kTmFtZSwgaGFuZGxlciwgZGVjb3JhdG9ycywgcGFyYW1EZWNvcmF0b3JzIH07XG4gIH1cblxuICBwcml2YXRlIHN0YXRpYyBzdGF0ZW1lbnRNZXRob2ROYW1lKHBhdGg6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgY29uc3QgZmlyc3RTZWdtZW50ID0gcGF0aC5zcGxpdChcIi9cIilbMF07XG4gICAgcmV0dXJuIGZpcnN0U2VnbWVudDtcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIHN0YXRlbWVudFNob3J0Y3V0UGFyYW1zKFxuICAgIHBhdGg6IHN0cmluZ1xuICApOiBBcnJheTx7IGRlY29yYXRvcjogUGFyYW1ldGVyRGVjb3JhdG9yOyBpbmRleDogbnVtYmVyIH0+IHtcbiAgICBjb25zdCBzZWdtZW50cyA9IHBhdGguc3BsaXQoXCIvXCIpLmZpbHRlcigocykgPT4gcy5zdGFydHNXaXRoKFwiOlwiKSk7XG4gICAgY29uc3QgcGFyYW1zOiBBcnJheTx7IGRlY29yYXRvcjogUGFyYW1ldGVyRGVjb3JhdG9yOyBpbmRleDogbnVtYmVyIH0+ID0gW107XG4gICAgc2VnbWVudHMuZm9yRWFjaCgoc2VnLCBpKSA9PiB7XG4gICAgICBjb25zdCBuYW1lID0gc2VnLnJlcGxhY2UoXCI6XCIsIFwiXCIpO1xuICAgICAgcGFyYW1zLnB1c2goeyBkZWNvcmF0b3I6IFBhcmFtKG5hbWUpIGFzIGFueSwgaW5kZXg6IGkgfSk7XG4gICAgfSk7XG4gICAgaWYgKFxuICAgICAgcGF0aC5zdGFydHNXaXRoKFwibGlzdEJ5L1wiKSB8fFxuICAgICAgcGF0aC5zdGFydHNXaXRoKFwicGFnaW5hdGVCeS9cIikgfHxcbiAgICAgIHBhdGguc3RhcnRzV2l0aChcImZpbmQvXCIpIHx8XG4gICAgICBwYXRoLnN0YXJ0c1dpdGgoXCJwYWdlL1wiKVxuICAgICkge1xuICAgICAgcGFyYW1zLnB1c2goeyBkZWNvcmF0b3I6IERlY2FmUXVlcnkoKSBhcyBhbnksIGluZGV4OiBzZWdtZW50cy5sZW5ndGggfSk7XG4gICAgfVxuICAgIHJldHVybiBwYXJhbXM7XG4gIH1cblxuICBwcml2YXRlIHN0YXRpYyBjb21wbGV4UXVlcnlQYXJhbXMoXG4gICAgcGF0aDogc3RyaW5nXG4gICk6IEFycmF5PHsgZGVjb3JhdG9yOiBQYXJhbWV0ZXJEZWNvcmF0b3I7IGluZGV4OiBudW1iZXIgfT4ge1xuICAgIGNvbnN0IHNlZ21lbnRzID0gcGF0aC5zcGxpdChcIi9cIikuZmlsdGVyKChzKSA9PiBzLnN0YXJ0c1dpdGgoXCI6XCIpKTtcbiAgICBjb25zdCBwYXJhbXM6IEFycmF5PHsgZGVjb3JhdG9yOiBQYXJhbWV0ZXJEZWNvcmF0b3I7IGluZGV4OiBudW1iZXIgfT4gPSBbXTtcbiAgICBzZWdtZW50cy5mb3JFYWNoKChzZWcsIGkpID0+IHtcbiAgICAgIGNvbnN0IG5hbWUgPSBzZWcucmVwbGFjZShcIjpcIiwgXCJcIik7XG4gICAgICBwYXJhbXMucHVzaCh7IGRlY29yYXRvcjogUGFyYW0obmFtZSkgYXMgYW55LCBpbmRleDogaSB9KTtcbiAgICB9KTtcbiAgICBpZiAocGF0aC5zdGFydHNXaXRoKFwicXVlcnkvXCIpKSB7XG4gICAgICBwYXJhbXMucHVzaCh7IGRlY29yYXRvcjogRGVjYWZRdWVyeSgpIGFzIGFueSwgaW5kZXg6IHNlZ21lbnRzLmxlbmd0aCB9KTtcbiAgICB9XG4gICAgcmV0dXJuIHBhcmFtcztcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIGNyZWF0ZUNyZWF0ZUhhbmRsZXIoXG4gICAgTW9kZWxDb25zdHI6IE1vZGVsQ29uc3RydWN0b3I8YW55PixcbiAgICBtb2RlbENsYXp6TmFtZTogc3RyaW5nXG4gICkge1xuICAgIHJldHVybiBhc3luYyBmdW5jdGlvbiBjcmVhdGUoXG4gICAgICB0aGlzOiBhbnksXG4gICAgICBkYXRhOiBhbnksXG4gICAgICByZXNwPzogYW55XG4gICAgKTogUHJvbWlzZTxNb2RlbDxhbnk+PiB7XG4gICAgICBjb25zdCB7IGN0eCwgbG9nIH0gPSAoXG4gICAgICAgIGF3YWl0IHRoaXMubG9nQ3R4KFtdLCBPcGVyYXRpb25LZXlzLkNSRUFURSwgdHJ1ZSlcbiAgICAgICkuZm9yKGNyZWF0ZSk7XG4gICAgICBsb2cudmVyYm9zZShgY3JlYXRpbmcgbmV3ICR7bW9kZWxDbGF6ek5hbWV9YCk7XG4gICAgICBsZXQgY3JlYXRlZDogTW9kZWw7XG4gICAgICB0cnkge1xuICAgICAgICBjcmVhdGVkID0gYXdhaXQgdGhpcy5wZXJzaXN0ZW5jZShjdHgpLmNyZWF0ZShkYXRhLCBjdHgpO1xuICAgICAgfSBjYXRjaCAoZTogdW5rbm93bikge1xuICAgICAgICBsb2cuZXJyb3IoYEZhaWxlZCB0byBjcmVhdGUgbmV3ICR7bW9kZWxDbGF6ek5hbWV9YCwgZSBhcyBFcnJvcik7XG4gICAgICAgIHRocm93IGU7XG4gICAgICB9XG4gICAgICBsb2cuaW5mbyhgY3JlYXRlZCBuZXcgJHttb2RlbENsYXp6TmFtZX0gd2l0aCBpZCAkeyhjcmVhdGVkIGFzIGFueSlbdGhpcy5wa119YCk7XG4gICAgICBpZiAocmVzcCkgY3R4LnRvUmVzcG9uc2UocmVzcCk7XG4gICAgICByZXR1cm4gY3JlYXRlZDtcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgY3JlYXRlQnVsa0NyZWF0ZUhhbmRsZXIoXG4gICAgTW9kZWxDb25zdHI6IE1vZGVsQ29uc3RydWN0b3I8YW55PixcbiAgICBtb2RlbENsYXp6TmFtZTogc3RyaW5nXG4gICkge1xuICAgIHJldHVybiBhc3luYyBmdW5jdGlvbiBjcmVhdGVBbGwoXG4gICAgICB0aGlzOiBhbnksXG4gICAgICBkYXRhOiBhbnlbXSxcbiAgICAgIHJlc3A/OiBhbnlcbiAgICApOiBQcm9taXNlPE1vZGVsW10+IHtcbiAgICAgIGNvbnN0IHsgY3R4LCBsb2cgfSA9IChcbiAgICAgICAgYXdhaXQgdGhpcy5sb2dDdHgoW10sIEJ1bGtDcnVkT3BlcmF0aW9uS2V5cy5DUkVBVEVfQUxMLCB0cnVlKVxuICAgICAgKS5mb3IoY3JlYXRlQWxsKTtcbiAgICAgIGxvZy52ZXJib3NlKGBjcmVhdGluZyBuZXcgJHttb2RlbENsYXp6TmFtZX1gKTtcbiAgICAgIGxldCBjcmVhdGVkOiBhbnlbXTtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNyZWF0ZWQgPSBhd2FpdCB0aGlzLnBlcnNpc3RlbmNlKGN0eCkuY3JlYXRlQWxsKFxuICAgICAgICAgIGRhdGEubWFwKChkKSA9PiBuZXcgTW9kZWxDb25zdHIoZCkpLFxuICAgICAgICAgIGN0eFxuICAgICAgICApO1xuICAgICAgfSBjYXRjaCAoZTogdW5rbm93bikge1xuICAgICAgICBsb2cuZXJyb3IoYEZhaWxlZCB0byBjcmVhdGUgbmV3ICR7bW9kZWxDbGF6ek5hbWV9YCwgZSBhcyBFcnJvcik7XG4gICAgICAgIHRocm93IGU7XG4gICAgICB9XG4gICAgICBsb2cuaW5mbyhgY3JlYXRlZCBuZXcgJHttb2RlbENsYXp6TmFtZX0gd2l0aCBpZCAkeyhjcmVhdGVkIGFzIGFueSlbdGhpcy5wa119YCk7XG4gICAgICBpZiAocmVzcCkgY3R4LnRvUmVzcG9uc2UocmVzcCk7XG4gICAgICByZXR1cm4gY3JlYXRlZDtcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgY3JlYXRlQnVsa1JlYWRIYW5kbGVyKG1vZGVsQ2xhenpOYW1lOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gYXN5bmMgZnVuY3Rpb24gcmVhZEFsbChcbiAgICAgIHRoaXM6IGFueSxcbiAgICAgIGlkczogc3RyaW5nW11cbiAgICApOiBQcm9taXNlPE1vZGVsW10+IHtcbiAgICAgIGNvbnN0IHsgY3R4LCBsb2cgfSA9IChcbiAgICAgICAgYXdhaXQgdGhpcy5sb2dDdHgoW10sIEJ1bGtDcnVkT3BlcmF0aW9uS2V5cy5SRUFEX0FMTCwgdHJ1ZSlcbiAgICAgICkuZm9yKHJlYWRBbGwpO1xuICAgICAgY29uc3Qgbm9ybWFsaXplZElkcyA9IEFycmF5LmlzQXJyYXkoaWRzKSA/IGlkcyA6IFtpZHNdO1xuICAgICAgbGV0IHJlYWQ6IE1vZGVsW107XG4gICAgICB0cnkge1xuICAgICAgICBsb2cuZGVidWcoYHJlYWRpbmcgJHtub3JtYWxpemVkSWRzfSAke21vZGVsQ2xhenpOYW1lfWApO1xuICAgICAgICByZWFkID0gYXdhaXQgdGhpcy5wZXJzaXN0ZW5jZShjdHgpLnJlYWRBbGwobm9ybWFsaXplZElkcyBhcyBhbnksIGN0eCk7XG4gICAgICB9IGNhdGNoIChlOiB1bmtub3duKSB7XG4gICAgICAgIGxvZy5lcnJvcihgRmFpbGVkIHRvIHJlYWQgJHttb2RlbENsYXp6TmFtZX1gLCBlIGFzIEVycm9yKTtcbiAgICAgICAgdGhyb3cgZTtcbiAgICAgIH1cbiAgICAgIGxvZy5pbmZvKGByZWFkICR7cmVhZC5sZW5ndGh9ICR7bW9kZWxDbGF6ek5hbWV9YCk7XG4gICAgICByZXR1cm4gcmVhZDtcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgY3JlYXRlQnVsa1VwZGF0ZUhhbmRsZXIobW9kZWxDbGF6ek5hbWU6IHN0cmluZykge1xuICAgIHJldHVybiBhc3luYyBmdW5jdGlvbiB1cGRhdGVBbGwoXG4gICAgICB0aGlzOiBhbnksXG4gICAgICBib2R5OiBhbnlbXSxcbiAgICAgIHJlc3A/OiBhbnlcbiAgICApOiBQcm9taXNlPGFueVtdPiB7XG4gICAgICBjb25zdCB7IGN0eCwgbG9nIH0gPSAoXG4gICAgICAgIGF3YWl0IHRoaXMubG9nQ3R4KFtdLCBCdWxrQ3J1ZE9wZXJhdGlvbktleXMuVVBEQVRFX0FMTCwgdHJ1ZSlcbiAgICAgICkuZm9yKHVwZGF0ZUFsbCk7XG4gICAgICBsZXQgdXBkYXRlZDogYW55W107XG4gICAgICB0cnkge1xuICAgICAgICBsb2cuaW5mbyhgdXBkYXRpbmcgJHtib2R5Lmxlbmd0aH0gJHttb2RlbENsYXp6TmFtZX1gKTtcbiAgICAgICAgdXBkYXRlZCA9IGF3YWl0IHRoaXMucGVyc2lzdGVuY2UoY3R4KS51cGRhdGVBbGwoYm9keSwgY3R4KTtcbiAgICAgIH0gY2F0Y2ggKGU6IHVua25vd24pIHtcbiAgICAgICAgbG9nLmVycm9yKGUgYXMgRXJyb3IpO1xuICAgICAgICB0aHJvdyBlO1xuICAgICAgfVxuICAgICAgaWYgKHJlc3ApIGN0eC50b1Jlc3BvbnNlKHJlc3ApO1xuICAgICAgcmV0dXJuIHVwZGF0ZWQ7XG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIGNyZWF0ZUJ1bGtEZWxldGVIYW5kbGVyKG1vZGVsQ2xhenpOYW1lOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gYXN5bmMgZnVuY3Rpb24gZGVsZXRlQWxsKFxuICAgICAgdGhpczogYW55LFxuICAgICAgaWRzOiBzdHJpbmdbXSxcbiAgICAgIHJlc3A/OiBhbnlcbiAgICApOiBQcm9taXNlPE1vZGVsW10+IHtcbiAgICAgIGNvbnN0IHsgY3R4LCBsb2cgfSA9IChcbiAgICAgICAgYXdhaXQgdGhpcy5sb2dDdHgoW10sIEJ1bGtDcnVkT3BlcmF0aW9uS2V5cy5ERUxFVEVfQUxMLCB0cnVlKVxuICAgICAgKS5mb3IoZGVsZXRlQWxsKTtcbiAgICAgIGNvbnN0IG5vcm1hbGl6ZWRJZHMgPSBBcnJheS5pc0FycmF5KGlkcykgPyBpZHMgOiBbaWRzXTtcbiAgICAgIGxldCByZWFkOiBNb2RlbFtdO1xuICAgICAgdHJ5IHtcbiAgICAgICAgbG9nLmRlYnVnKGBkZWxldGluZyAke25vcm1hbGl6ZWRJZHMubGVuZ3RofSAke21vZGVsQ2xhenpOYW1lfWApO1xuICAgICAgICByZWFkID0gYXdhaXQgdGhpcy5wZXJzaXN0ZW5jZShjdHgpLmRlbGV0ZUFsbChub3JtYWxpemVkSWRzLCBjdHgpO1xuICAgICAgfSBjYXRjaCAoZTogdW5rbm93bikge1xuICAgICAgICBsb2cuZXJyb3IoYEZhaWxlZCB0byBkZWxldGUgJHttb2RlbENsYXp6TmFtZX1gLCBlIGFzIEVycm9yKTtcbiAgICAgICAgdGhyb3cgZTtcbiAgICAgIH1cbiAgICAgIGxvZy5pbmZvKGBkZWxldGVkICR7cmVhZC5sZW5ndGh9ICR7bW9kZWxDbGF6ek5hbWV9YCk7XG4gICAgICBpZiAocmVzcCkgY3R4LnRvUmVzcG9uc2UocmVzcCk7XG4gICAgICByZXR1cm4gcmVhZDtcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgY3JlYXRlUmVhZEhhbmRsZXIoXG4gICAgZ2V0UEs6ICguLi5wOiBBcnJheTxzdHJpbmcgfCBudW1iZXI+KSA9PiBzdHJpbmcsXG4gICAgbW9kZWxDbGF6ek5hbWU6IHN0cmluZ1xuICApIHtcbiAgICByZXR1cm4gYXN5bmMgZnVuY3Rpb24gcmVhZChcbiAgICAgIHRoaXM6IGFueSxcbiAgICAgIHJvdXRlUGFyYW1zOiBhbnlcbiAgICApOiBQcm9taXNlPE1vZGVsPiB7XG4gICAgICBjb25zdCB7IGN0eCwgbG9nIH0gPSAoXG4gICAgICAgIGF3YWl0IHRoaXMubG9nQ3R4KFtdLCBPcGVyYXRpb25LZXlzLlJFQUQsIHRydWUpXG4gICAgICApLmZvcihyZWFkKTtcbiAgICAgIGNvbnN0IGlkID0gZ2V0UEsoLi4ucm91dGVQYXJhbXMudmFsdWVzSW5PcmRlcik7XG4gICAgICBpZiAodHlwZW9mIGlkID09PSBcInVuZGVmaW5lZFwiKVxuICAgICAgICB0aHJvdyBuZXcgVmFsaWRhdGlvbkVycm9yKGBObyAke3RoaXMucGt9IHByb3ZpZGVkYCk7XG4gICAgICBsZXQgcmVhZFJlc3VsdDogTW9kZWw7XG4gICAgICB0cnkge1xuICAgICAgICBsb2cuZGVidWcoYHJlYWRpbmcgJHttb2RlbENsYXp6TmFtZX0gd2l0aCAke3RoaXMucGt9ICR7aWR9YCk7XG4gICAgICAgIHJlYWRSZXN1bHQgPSBhd2FpdCB0aGlzLnBlcnNpc3RlbmNlKGN0eCkucmVhZChpZCwgY3R4KTtcbiAgICAgIH0gY2F0Y2ggKGU6IHVua25vd24pIHtcbiAgICAgICAgbG9nLmVycm9yKGBGYWlsZWQgdG8gcmVhZCAke21vZGVsQ2xhenpOYW1lfSB3aXRoIGlkICR7aWR9YCwgZSBhcyBFcnJvcik7XG4gICAgICAgIHRocm93IGU7XG4gICAgICB9XG4gICAgICBsb2cuaW5mbyhgcmVhZCAke21vZGVsQ2xhenpOYW1lfSB3aXRoIGlkICR7KHJlYWRSZXN1bHQgYXMgYW55KVt0aGlzLnBrXX1gKTtcbiAgICAgIHJldHVybiByZWFkUmVzdWx0O1xuICAgIH07XG4gIH1cblxuICBwcml2YXRlIHN0YXRpYyBjcmVhdGVVcGRhdGVIYW5kbGVyKFxuICAgIGdldFBLOiAoLi4ucDogQXJyYXk8c3RyaW5nIHwgbnVtYmVyPikgPT4gc3RyaW5nLFxuICAgIE1vZGVsQ29uc3RyOiBNb2RlbENvbnN0cnVjdG9yPGFueT4sXG4gICAgbW9kZWxDbGF6ek5hbWU6IHN0cmluZ1xuICApIHtcbiAgICByZXR1cm4gYXN5bmMgZnVuY3Rpb24gdXBkYXRlKFxuICAgICAgdGhpczogYW55LFxuICAgICAgcm91dGVQYXJhbXM6IGFueSxcbiAgICAgIGJvZHk6IGFueSxcbiAgICAgIHJlc3A/OiBhbnlcbiAgICApOiBQcm9taXNlPGFueT4ge1xuICAgICAgY29uc3QgeyBjdHgsIGxvZyB9ID0gKFxuICAgICAgICBhd2FpdCB0aGlzLmxvZ0N0eChbXSwgT3BlcmF0aW9uS2V5cy5VUERBVEUsIHRydWUpXG4gICAgICApLmZvcih1cGRhdGUpO1xuICAgICAgY29uc3QgaWQgPSBnZXRQSyguLi5yb3V0ZVBhcmFtcy52YWx1ZXNJbk9yZGVyKTtcbiAgICAgIGlmICh0eXBlb2YgaWQgPT09IFwidW5kZWZpbmVkXCIpXG4gICAgICAgIHRocm93IG5ldyBWYWxpZGF0aW9uRXJyb3IoYE5vICR7dGhpcy5wa30gcHJvdmlkZWRgKTtcbiAgICAgIGxldCB1cGRhdGVkOiBhbnk7XG4gICAgICB0cnkge1xuICAgICAgICBsb2cuaW5mbyhgdXBkYXRpbmcgJHttb2RlbENsYXp6TmFtZX0gd2l0aCAke3RoaXMucGt9ICR7aWR9YCk7XG4gICAgICAgIGNvbnN0IHBheWxvYWQgPSBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KGJvZHkpKTtcbiAgICAgICAgdXBkYXRlZCA9IGF3YWl0IHRoaXMucGVyc2lzdGVuY2UoY3R4KS51cGRhdGUoXG4gICAgICAgICAgbmV3IE1vZGVsQ29uc3RyKHsgLi4ucGF5bG9hZCwgW3RoaXMucGtdOiBpZCB9KSxcbiAgICAgICAgICBjdHhcbiAgICAgICAgKTtcbiAgICAgIH0gY2F0Y2ggKGU6IHVua25vd24pIHtcbiAgICAgICAgbG9nLmVycm9yKGUgYXMgRXJyb3IpO1xuICAgICAgICB0aHJvdyBlO1xuICAgICAgfVxuICAgICAgaWYgKHJlc3ApIGN0eC50b1Jlc3BvbnNlKHJlc3ApO1xuICAgICAgcmV0dXJuIHVwZGF0ZWQ7XG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIGNyZWF0ZURlbGV0ZUhhbmRsZXIoXG4gICAgZ2V0UEs6ICguLi5wOiBBcnJheTxzdHJpbmcgfCBudW1iZXI+KSA9PiBzdHJpbmcsXG4gICAgbW9kZWxDbGF6ek5hbWU6IHN0cmluZ1xuICApIHtcbiAgICByZXR1cm4gYXN5bmMgZnVuY3Rpb24gcmVtb3ZlKFxuICAgICAgdGhpczogYW55LFxuICAgICAgcm91dGVQYXJhbXM6IGFueSxcbiAgICAgIHJlc3A/OiBhbnlcbiAgICApOiBQcm9taXNlPE1vZGVsPiB7XG4gICAgICBjb25zdCB7IGN0eCwgbG9nIH0gPSAoXG4gICAgICAgIGF3YWl0IHRoaXMubG9nQ3R4KFtdLCBPcGVyYXRpb25LZXlzLkRFTEVURSwgdHJ1ZSlcbiAgICAgICkuZm9yKHJlbW92ZSk7XG4gICAgICBjb25zdCBpZCA9IGdldFBLKC4uLnJvdXRlUGFyYW1zLnZhbHVlc0luT3JkZXIpO1xuICAgICAgaWYgKHR5cGVvZiBpZCA9PT0gXCJ1bmRlZmluZWRcIilcbiAgICAgICAgdGhyb3cgbmV3IFZhbGlkYXRpb25FcnJvcihgTm8gJHt0aGlzLnBrfSBwcm92aWRlZGApO1xuICAgICAgbGV0IGRlbDogTW9kZWw7XG4gICAgICB0cnkge1xuICAgICAgICBsb2cuZGVidWcoYGRlbGV0aW5nICR7bW9kZWxDbGF6ek5hbWV9IHdpdGggJHt0aGlzLnBrfSAke2lkfWApO1xuICAgICAgICBkZWwgPSBhd2FpdCB0aGlzLnBlcnNpc3RlbmNlKGN0eCkuZGVsZXRlKGlkLCBjdHgpO1xuICAgICAgfSBjYXRjaCAoZTogdW5rbm93bikge1xuICAgICAgICBsb2cuZXJyb3IoYEZhaWxlZCB0byBkZWxldGUgJHttb2RlbENsYXp6TmFtZX0gd2l0aCBpZCAke2lkfWAsIGUgYXMgRXJyb3IpO1xuICAgICAgICB0aHJvdyBlO1xuICAgICAgfVxuICAgICAgbG9nLmluZm8oYGRlbGV0ZWQgJHttb2RlbENsYXp6TmFtZX0gd2l0aCBpZCAke2lkfWApO1xuICAgICAgaWYgKHJlc3ApIGN0eC50b1Jlc3BvbnNlKHJlc3ApO1xuICAgICAgcmV0dXJuIGRlbDtcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgY3JlYXRlU3RhdGVtZW50SGFuZGxlcihtb2RlbENsYXp6TmFtZTogc3RyaW5nKSB7XG4gICAgcmV0dXJuIGFzeW5jIGZ1bmN0aW9uIHN0YXRlbWVudChcbiAgICAgIHRoaXM6IGFueSxcbiAgICAgIG5hbWU6IHN0cmluZyxcbiAgICAgIGFyZ3M6IChzdHJpbmcgfCBudW1iZXIpW10sXG4gICAgICBkZXRhaWxzOiBEaXJlY3Rpb25MaW1pdE9mZnNldFxuICAgICk6IFByb21pc2U8YW55PiB7XG4gICAgICBjb25zdCB7IGN0eCB9ID0gKFxuICAgICAgICBhd2FpdCB0aGlzLmxvZ0N0eChbXSwgUGVyc2lzdGVuY2VLZXlzLlNUQVRFTUVOVCwgdHJ1ZSlcbiAgICAgICkuZm9yKHN0YXRlbWVudCk7XG4gICAgICBjb25zdCB7IGRpcmVjdGlvbiwgb2Zmc2V0LCBsaW1pdCwgYm9va21hcmsgfSA9IGRldGFpbHM7XG4gICAgICBhcmdzID0gYXJncy5tYXAoXG4gICAgICAgIChhKSA9PiAodHlwZW9mIGEgPT09IFwic3RyaW5nXCIgPyBwYXJzZUludChhIGFzIHN0cmluZykgOiBhKSB8fCBhXG4gICAgICApIGFzIGFueVtdO1xuICAgICAgY29uc3QgcGF0aERpcmVjdGlvbiA9IGFyZ3MubGVuZ3RoID4gMSA/IGFyZ3NbMV0gOiB1bmRlZmluZWQ7XG4gICAgICBjb25zdCByZXNvbHZlZERpcmVjdGlvbiA9IChkaXJlY3Rpb24gPz8gcGF0aERpcmVjdGlvbikgYXNcbiAgICAgICAgfCBzdHJpbmdcbiAgICAgICAgfCB1bmRlZmluZWQ7XG4gICAgICBpZiAocmVzb2x2ZWREaXJlY3Rpb24gJiYgYXJncy5sZW5ndGggPiAxKSBhcmdzWzFdID0gcmVzb2x2ZWREaXJlY3Rpb247XG4gICAgICBzd2l0Y2ggKG5hbWUpIHtcbiAgICAgICAgY2FzZSBQcmVwYXJlZFN0YXRlbWVudEtleXMuRklORDpcbiAgICAgICAgY2FzZSBQcmVwYXJlZFN0YXRlbWVudEtleXMuRklORF9CWTpcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBQcmVwYXJlZFN0YXRlbWVudEtleXMuTElTVF9CWTpcbiAgICAgICAgICBhcmdzLnB1c2goZGlyZWN0aW9uIGFzIHN0cmluZyk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLlBBR0U6XG4gICAgICAgIGNhc2UgUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLlBBR0VfQlk6XG4gICAgICAgICAgYXJncyA9IFtcbiAgICAgICAgICAgIGFyZ3NbMF0sXG4gICAgICAgICAgICByZXNvbHZlZERpcmVjdGlvbiBhcyBhbnksXG4gICAgICAgICAgICB7IGxpbWl0LCBvZmZzZXQsIGJvb2ttYXJrIH0sXG4gICAgICAgICAgXTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBQcmVwYXJlZFN0YXRlbWVudEtleXMuRklORF9PTkVfQlk6XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLkNPVU5UX09GOlxuICAgICAgICBjYXNlIFByZXBhcmVkU3RhdGVtZW50S2V5cy5NQVhfT0Y6XG4gICAgICAgIGNhc2UgUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLk1JTl9PRjpcbiAgICAgICAgY2FzZSBQcmVwYXJlZFN0YXRlbWVudEtleXMuQVZHX09GOlxuICAgICAgICBjYXNlIFByZXBhcmVkU3RhdGVtZW50S2V5cy5TVU1fT0Y6XG4gICAgICAgIGNhc2UgUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLkRJU1RJTkNUX09GOlxuICAgICAgICBjYXNlIFByZXBhcmVkU3RhdGVtZW50S2V5cy5HUk9VUF9PRjpcbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLnBlcnNpc3RlbmNlKGN0eCkuc3RhdGVtZW50KG5hbWUsIC4uLmFyZ3MsIGN0eCk7XG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIGNyZWF0ZVN0YXRlbWVudFNob3J0Y3V0SGFuZGxlcihcbiAgICBzdGF0ZW1lbnRLZXk6IHN0cmluZyxcbiAgICBtb2RlbENsYXp6TmFtZTogc3RyaW5nXG4gICkge1xuICAgIHJldHVybiBhc3luYyBmdW5jdGlvbiBzdGF0ZW1lbnRTaG9ydGN1dChcbiAgICAgIHRoaXM6IGFueSxcbiAgICAgIC4uLmFyZ3M6IGFueVtdXG4gICAgKTogUHJvbWlzZTxhbnk+IHtcbiAgICAgIGNvbnN0IHsgY3R4IH0gPSAoXG4gICAgICAgIGF3YWl0IHRoaXMubG9nQ3R4KFtdLCBzdGF0ZW1lbnRLZXksIHRydWUpXG4gICAgICApLmZvcihzdGF0ZW1lbnRTaG9ydGN1dCk7XG5cbiAgICAgIHN3aXRjaCAoc3RhdGVtZW50S2V5KSB7XG4gICAgICAgIGNhc2UgUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLkxJU1RfQlk6IHtcbiAgICAgICAgICBjb25zdCBba2V5LCBkZXRhaWxzXSA9IGFyZ3M7XG4gICAgICAgICAgcmV0dXJuIHRoaXMucGVyc2lzdGVuY2UoY3R4KS5saXN0QnkoXG4gICAgICAgICAgICBrZXksXG4gICAgICAgICAgICAoZGV0YWlscyBhcyBhbnkpPy5kaXJlY3Rpb24gYXMgT3JkZXJEaXJlY3Rpb24sXG4gICAgICAgICAgICBjdHhcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIGNhc2UgUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLlBBR0VfQlk6IHtcbiAgICAgICAgICBjb25zdCBba2V5LCBwYWdlLCBkZXRhaWxzXSA9IGFyZ3M7XG4gICAgICAgICAgcmV0dXJuIHRoaXMucGVyc2lzdGVuY2UoY3R4KS5wYWdpbmF0ZUJ5KFxuICAgICAgICAgICAga2V5LFxuICAgICAgICAgICAgKGRldGFpbHMgYXMgYW55KT8uZGlyZWN0aW9uIGFzIE9yZGVyRGlyZWN0aW9uLFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsaW1pdDogKGRldGFpbHMgYXMgYW55KT8ubGltaXQsXG4gICAgICAgICAgICAgIG9mZnNldDogKGRldGFpbHMgYXMgYW55KT8ub2Zmc2V0LFxuICAgICAgICAgICAgICBwYWdlLFxuICAgICAgICAgICAgfSBhcyBhbnksXG4gICAgICAgICAgICBjdHhcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIGNhc2UgUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLkZJTkQ6IHtcbiAgICAgICAgICBjb25zdCBbdmFsdWUsIGRldGFpbHNdID0gYXJncztcbiAgICAgICAgICBjb25zdCBkaXJlY3Rpb24gPVxuICAgICAgICAgICAgKGRldGFpbHMgYXMgYW55KT8uZGlyZWN0aW9uID8/IE9yZGVyRGlyZWN0aW9uLkFTQztcbiAgICAgICAgICBjb25zdCBwZXJzaXN0ZW5jZSA9IHRoaXMucGVyc2lzdGVuY2UoY3R4KTtcbiAgICAgICAgICBpZiAodHlwZW9mIHBlcnNpc3RlbmNlLmZpbmQgPT09IFwiZnVuY3Rpb25cIilcbiAgICAgICAgICAgIHJldHVybiBwZXJzaXN0ZW5jZS5maW5kKHZhbHVlLCBkaXJlY3Rpb24sIGN0eCk7XG4gICAgICAgICAgcmV0dXJuIHBlcnNpc3RlbmNlLnN0YXRlbWVudChQcmVwYXJlZFN0YXRlbWVudEtleXMuRklORCwgdmFsdWUsIGRpcmVjdGlvbiwgY3R4KTtcbiAgICAgICAgfVxuICAgICAgICBjYXNlIFByZXBhcmVkU3RhdGVtZW50S2V5cy5QQUdFOiB7XG4gICAgICAgICAgY29uc3QgW3ZhbHVlLCBkZXRhaWxzXSA9IGFyZ3M7XG4gICAgICAgICAgY29uc3QgcmVmID0ge1xuICAgICAgICAgICAgb2Zmc2V0OiAoZGV0YWlscyBhcyBhbnkpPy5vZmZzZXQgPz8gMSxcbiAgICAgICAgICAgIGxpbWl0OiAoZGV0YWlscyBhcyBhbnkpPy5saW1pdCA/PyAxMCxcbiAgICAgICAgICAgIGJvb2ttYXJrOiAoZGV0YWlscyBhcyBhbnkpPy5ib29rbWFyayxcbiAgICAgICAgICB9O1xuICAgICAgICAgIGNvbnN0IHBlcnNpc3RlbmNlID0gdGhpcy5wZXJzaXN0ZW5jZShjdHgpO1xuICAgICAgICAgIGNvbnN0IGRpcmVjdGlvbiA9IChkZXRhaWxzIGFzIGFueSk/LmRpcmVjdGlvbiA/PyBPcmRlckRpcmVjdGlvbi5BU0M7XG4gICAgICAgICAgaWYgKHR5cGVvZiBwZXJzaXN0ZW5jZS5wYWdlID09PSBcImZ1bmN0aW9uXCIpXG4gICAgICAgICAgICByZXR1cm4gcGVyc2lzdGVuY2UucGFnZSh2YWx1ZSwgZGlyZWN0aW9uLCByZWYsIGN0eCk7XG4gICAgICAgICAgcmV0dXJuIHBlcnNpc3RlbmNlLnN0YXRlbWVudChQcmVwYXJlZFN0YXRlbWVudEtleXMuUEFHRSwgdmFsdWUsIGRpcmVjdGlvbiwgcmVmLCBjdHgpO1xuICAgICAgICB9XG4gICAgICAgIGNhc2UgUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLkZJTkRfT05FX0JZOiB7XG4gICAgICAgICAgY29uc3QgW2tleSwgdmFsdWVdID0gYXJncztcbiAgICAgICAgICByZXR1cm4gdGhpcy5wZXJzaXN0ZW5jZShjdHgpLmZpbmRPbmVCeShrZXksIHZhbHVlLCBjdHgpO1xuICAgICAgICB9XG4gICAgICAgIGNhc2UgUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLkZJTkRfQlk6IHtcbiAgICAgICAgICBjb25zdCBba2V5LCB2YWx1ZV0gPSBhcmdzO1xuICAgICAgICAgIHJldHVybiB0aGlzLnBlcnNpc3RlbmNlKGN0eClcbiAgICAgICAgICAgIC5mb3IoY3R4LnRvT3ZlcnJpZGVzKCkpXG4gICAgICAgICAgICAuZmluZEJ5KGtleSwgdmFsdWUsIGN0eCk7XG4gICAgICAgIH1cbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICBpZiAoXG4gICAgICAgICAgICBzdGF0ZW1lbnRLZXkgPT09IFByZXBhcmVkU3RhdGVtZW50S2V5cy5DT1VOVF9PRiB8fFxuICAgICAgICAgICAgc3RhdGVtZW50S2V5ID09PSBQcmVwYXJlZFN0YXRlbWVudEtleXMuTUFYX09GIHx8XG4gICAgICAgICAgICBzdGF0ZW1lbnRLZXkgPT09IFByZXBhcmVkU3RhdGVtZW50S2V5cy5NSU5fT0YgfHxcbiAgICAgICAgICAgIHN0YXRlbWVudEtleSA9PT0gUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLkFWR19PRiB8fFxuICAgICAgICAgICAgc3RhdGVtZW50S2V5ID09PSBQcmVwYXJlZFN0YXRlbWVudEtleXMuU1VNX09GIHx8XG4gICAgICAgICAgICBzdGF0ZW1lbnRLZXkgPT09IFByZXBhcmVkU3RhdGVtZW50S2V5cy5ESVNUSU5DVF9PRiB8fFxuICAgICAgICAgICAgc3RhdGVtZW50S2V5ID09PSBQcmVwYXJlZFN0YXRlbWVudEtleXMuR1JPVVBfT0ZcbiAgICAgICAgICApIHtcbiAgICAgICAgICAgIGNvbnN0IFtmaWVsZF0gPSBhcmdzO1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMucGVyc2lzdGVuY2UoY3R4KS5zdGF0ZW1lbnQoc3RhdGVtZW50S2V5LCBmaWVsZCwgY3R4KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBVbmtub3duIHN0YXRlbWVudDogJHtzdGF0ZW1lbnRLZXl9YCk7XG4gICAgICB9XG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIGNyZWF0ZUNvbXBsZXhRdWVyeUhhbmRsZXIobWV0aG9kTmFtZTogc3RyaW5nKSB7XG4gICAgcmV0dXJuIGFzeW5jIGZ1bmN0aW9uIGNvbXBsZXhRdWVyeShcbiAgICAgIHRoaXM6IGFueSxcbiAgICAgIC4uLmFyZ3M6IGFueVtdXG4gICAgKTogUHJvbWlzZTxhbnk+IHtcbiAgICAgIGNvbnN0IGxvZzogYW55ID0gdGhpcy5sb2c/LmZvcj8uKGNvbXBsZXhRdWVyeSk7XG4gICAgICB0cnkge1xuICAgICAgICBpZiAobG9nKSBsb2cuZGVidWcoYEludm9raW5nIGN1c3RvbSBxdWVyeSBcIiR7bWV0aG9kTmFtZX1cImApO1xuICAgICAgICBjb25zdCB7IGN0eCB9ID0gKFxuICAgICAgICAgIGF3YWl0IHRoaXMubG9nQ3R4KFtdLCBtZXRob2ROYW1lLCB0cnVlKVxuICAgICAgICApLmZvcihjb21wbGV4UXVlcnkpO1xuICAgICAgICBjb25zdCBwZXJzaXN0ZW5jZSA9IHRoaXMucGVyc2lzdGVuY2UoY3R4KTtcbiAgICAgICAgY29uc3Qgc3ByZWFkQXJncyA9IEZyb21Nb2RlbENvbnRyb2xsZXIuZXh0cmFjdFF1ZXJ5QXJncyhhcmdzKTtcbiAgICAgICAgaWYgKHR5cGVvZiBwZXJzaXN0ZW5jZVttZXRob2ROYW1lXSA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgICAgcmV0dXJuIHBlcnNpc3RlbmNlW21ldGhvZE5hbWVdKC4uLnNwcmVhZEFyZ3MsIGN0eCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHR5cGVvZiBwZXJzaXN0ZW5jZS5zdGF0ZW1lbnQgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICAgIHJldHVybiBwZXJzaXN0ZW5jZS5zdGF0ZW1lbnQobWV0aG9kTmFtZSwgLi4uc3ByZWFkQXJncywgY3R4KTtcbiAgICAgICAgfVxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgYFBlcnNpc3RlbmNlIG1ldGhvZCBcIiR7bWV0aG9kTmFtZX1cIiBub3QgZm91bmQgb24gJHtwZXJzaXN0ZW5jZT8uY29uc3RydWN0b3I/Lm5hbWV9YFxuICAgICAgICApO1xuICAgICAgfSBjYXRjaCAoZTogYW55KSB7XG4gICAgICAgIGlmIChsb2cpIGxvZy5lcnJvcihgQ3VzdG9tIHF1ZXJ5IFwiJHttZXRob2ROYW1lfVwiIGZhaWxlZGAsIGUpO1xuICAgICAgICB0aHJvdyBlO1xuICAgICAgfVxuICAgIH07XG4gIH1cblxuICBwcml2YXRlIHN0YXRpYyBjcmVhdGVDdXN0b21Sb3V0ZUhhbmRsZXIobWV0aG9kTmFtZTogc3RyaW5nKSB7XG4gICAgcmV0dXJuIGFzeW5jIGZ1bmN0aW9uIGN1c3RvbVJvdXRlKFxuICAgICAgdGhpczogYW55LFxuICAgICAgLi4uYXJnczogYW55W11cbiAgICApOiBQcm9taXNlPGFueT4ge1xuICAgICAgY29uc3QgbG9nOiBhbnkgPSB0aGlzLmxvZz8uZm9yPy4oY3VzdG9tUm91dGUpO1xuICAgICAgY29uc3QgeyBjdHggfSA9IChcbiAgICAgICAgYXdhaXQgdGhpcy5sb2dDdHgoW10sIG1ldGhvZE5hbWUsIHRydWUpXG4gICAgICApLmZvcihjdXN0b21Sb3V0ZSk7XG4gICAgICBjb25zdCBwZXJzaXN0ZW5jZSA9IHRoaXMucGVyc2lzdGVuY2UoY3R4KTtcbiAgICAgIGNvbnN0IHNwcmVhZEFyZ3MgPSBGcm9tTW9kZWxDb250cm9sbGVyLmV4dHJhY3RRdWVyeUFyZ3MoYXJncyk7XG5cbiAgICAgIC8vIFRyeSB0aGUgcGVyc2lzdGVuY2UgZGlyZWN0bHkgKHdvcmtzIHdoZW4gaXQncyBhIGN1c3RvbSBSZXBvc2l0b3J5KVxuICAgICAgaWYgKHR5cGVvZiBwZXJzaXN0ZW5jZVttZXRob2ROYW1lXSA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgIHJldHVybiBwZXJzaXN0ZW5jZVttZXRob2ROYW1lXSguLi5zcHJlYWRBcmdzLCBjdHgpO1xuICAgICAgfVxuICAgICAgLy8gV2hlbiBwZXJzaXN0ZW5jZSBpcyBhIE1vZGVsU2VydmljZSwgdGhlIG1ldGhvZCBsaXZlcyBvbiB0aGUgdW5kZXJseWluZyByZXBvXG4gICAgICBpZiAocGVyc2lzdGVuY2U/LnJlcG8gJiYgdHlwZW9mIHBlcnNpc3RlbmNlLnJlcG9bbWV0aG9kTmFtZV0gPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICByZXR1cm4gcGVyc2lzdGVuY2UucmVwb1ttZXRob2ROYW1lXSguLi5zcHJlYWRBcmdzLCBjdHgpO1xuICAgICAgfVxuICAgICAgLy8gRmFsbCBiYWNrIHRvIHN0YXRlbWVudCBnYXRld2F5XG4gICAgICBpZiAodHlwZW9mIHBlcnNpc3RlbmNlLnN0YXRlbWVudCA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgIHJldHVybiBwZXJzaXN0ZW5jZS5zdGF0ZW1lbnQobWV0aG9kTmFtZSwgLi4uc3ByZWFkQXJncywgY3R4KTtcbiAgICAgIH1cbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYE1ldGhvZCBcIiR7bWV0aG9kTmFtZX1cIiBub3QgZm91bmQgb24gJHtwZXJzaXN0ZW5jZT8uY29uc3RydWN0b3I/Lm5hbWV9IG9yIGl0cyByZXBvYFxuICAgICAgKTtcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgZXh0cmFjdFF1ZXJ5QXJncyhhcmdzOiBhbnlbXSk6IGFueVtdIHtcbiAgICBpZiAoYXJncy5sZW5ndGggPT09IDApIHJldHVybiBhcmdzO1xuICAgIGNvbnN0IGxhc3QgPSBhcmdzW2FyZ3MubGVuZ3RoIC0gMV07XG4gICAgaWYgKFxuICAgICAgbGFzdCAmJlxuICAgICAgdHlwZW9mIGxhc3QgPT09IFwib2JqZWN0XCIgJiZcbiAgICAgICFBcnJheS5pc0FycmF5KGxhc3QpXG4gICAgKSB7XG4gICAgICBjb25zdCBxdWVyeU9iaiA9IGFyZ3MucG9wKCk7XG4gICAgICBjb25zdCBoYXNEaXJlY3Rpb24gPSBxdWVyeU9iai5kaXJlY3Rpb24gIT09IHVuZGVmaW5lZDtcbiAgICAgIGNvbnN0IGhhc0xpbWl0ID0gcXVlcnlPYmoubGltaXQgIT09IHVuZGVmaW5lZDtcbiAgICAgIGNvbnN0IGhhc09mZnNldCA9IHF1ZXJ5T2JqLm9mZnNldCAhPT0gdW5kZWZpbmVkO1xuICAgICAgaWYgKCFoYXNEaXJlY3Rpb24gJiYgIWhhc0xpbWl0ICYmICFoYXNPZmZzZXQpIHJldHVybiBhcmdzO1xuICAgICAgY29uc3QgZXh0cmFzOiBhbnlbXSA9IFtdO1xuICAgICAgaWYgKGhhc0RpcmVjdGlvbikgZXh0cmFzLnB1c2gocXVlcnlPYmouZGlyZWN0aW9uKTtcbiAgICAgIGVsc2UgaWYgKGhhc0xpbWl0IHx8IGhhc09mZnNldCkgZXh0cmFzLnB1c2godW5kZWZpbmVkKTtcbiAgICAgIGlmIChoYXNMaW1pdCkgZXh0cmFzLnB1c2gocXVlcnlPYmoubGltaXQpO1xuICAgICAgaWYgKGhhc09mZnNldCkgZXh0cmFzLnB1c2gocXVlcnlPYmoub2Zmc2V0KTtcbiAgICAgIHJldHVybiBbLi4uYXJncywgLi4uZXh0cmFzXTtcbiAgICB9XG4gICAgcmV0dXJuIGFyZ3M7XG4gIH1cblxuICBwcml2YXRlIHN0YXRpYyBjcmVhdGVDcmVhdGVEZWNvcmF0b3JzKFxuICAgIE1vZGVsQ29uc3RyOiBNb2RlbENvbnN0cnVjdG9yPGFueT4sXG4gICAgbW9kZWxDbGF6ek5hbWU6IHN0cmluZ1xuICApOiBBcnJheTwodGFyZ2V0OiBhbnksIGtleTogc3RyaW5nLCBkZXNjOiBhbnkpID0+IHZvaWQ+IHtcbiAgICByZXR1cm4gW1xuICAgICAgQXBpT3BlcmF0aW9uRnJvbU1vZGVsKE1vZGVsQ29uc3RyLCBcIlBPU1RcIiksXG4gICAgICBBcGlPcGVyYXRpb24oeyBzdW1tYXJ5OiBgQ3JlYXRlIGEgbmV3ICR7bW9kZWxDbGF6ek5hbWV9LmAgfSksXG4gICAgICBBcGlCb2R5KHtcbiAgICAgICAgZGVzY3JpcHRpb246IGBQYXlsb2FkIGZvciAke21vZGVsQ2xhenpOYW1lfWAsXG4gICAgICAgIHR5cGU6IER0b0ZvcihPcGVyYXRpb25LZXlzLkNSRUFURSwgTW9kZWxDb25zdHIpLFxuICAgICAgfSksXG4gICAgICBBcGlDcmVhdGVkUmVzcG9uc2Uoe1xuICAgICAgICBkZXNjcmlwdGlvbjogYCR7bW9kZWxDbGF6ek5hbWV9IGNyZWF0ZWQgc3VjY2Vzc2Z1bGx5LmAsXG4gICAgICAgIHNjaGVtYTogeyAkcmVmOiBnZXRTY2hlbWFQYXRoKE1vZGVsQ29uc3RyKSB9LFxuICAgICAgfSksXG4gICAgICBBcGlCYWRSZXF1ZXN0UmVzcG9uc2UoeyBkZXNjcmlwdGlvbjogXCJQYXlsb2FkIHZhbGlkYXRpb24gZmFpbGVkLlwiIH0pLFxuICAgICAgQXBpVW5wcm9jZXNzYWJsZUVudGl0eVJlc3BvbnNlKHtcbiAgICAgICAgZGVzY3JpcHRpb246IFwiUmVwb3NpdG9yeSByZWplY3RlZCB0aGUgcHJvdmlkZWQgcGF5bG9hZC5cIixcbiAgICAgIH0pLFxuICAgIF07XG4gIH1cblxuICBwcml2YXRlIHN0YXRpYyBidWxrQ3JlYXRlRGVjb3JhdG9ycyhcbiAgICBNb2RlbENvbnN0cjogTW9kZWxDb25zdHJ1Y3Rvcjxhbnk+LFxuICAgIG1vZGVsQ2xhenpOYW1lOiBzdHJpbmdcbiAgKTogQXJyYXk8KHRhcmdldDogYW55LCBrZXk6IHN0cmluZywgZGVzYzogYW55KSA9PiB2b2lkPiB7XG4gICAgcmV0dXJuIFtcbiAgICAgIEFwaU9wZXJhdGlvbkZyb21Nb2RlbChNb2RlbENvbnN0ciwgXCJQT1NUXCIsIFwiYnVsa1wiKSxcbiAgICAgIEFwaU9wZXJhdGlvbih7IHN1bW1hcnk6IGBDcmVhdGUgYSBuZXcgJHttb2RlbENsYXp6TmFtZX0uYCB9KSxcbiAgICAgIEFwaUJvZHkoe1xuICAgICAgICBkZXNjcmlwdGlvbjogYFBheWxvYWQgZm9yICR7bW9kZWxDbGF6ek5hbWV9YCxcbiAgICAgICAgc2NoZW1hOiB7XG4gICAgICAgICAgdHlwZTogXCJhcnJheVwiLFxuICAgICAgICAgIGl0ZW1zOiB7ICRyZWY6IGdldFNjaGVtYVBhdGgoTW9kZWxDb25zdHIpIH0sXG4gICAgICAgIH0sXG4gICAgICB9KSxcbiAgICAgIEFwaUNyZWF0ZWRSZXNwb25zZSh7XG4gICAgICAgIGRlc2NyaXB0aW9uOiBgJHttb2RlbENsYXp6TmFtZX0gY3JlYXRlZCBzdWNjZXNzZnVsbHkuYCxcbiAgICAgICAgc2NoZW1hOiB7XG4gICAgICAgICAgdHlwZTogXCJhcnJheVwiLFxuICAgICAgICAgIGl0ZW1zOiB7ICRyZWY6IGdldFNjaGVtYVBhdGgoTW9kZWxDb25zdHIpIH0sXG4gICAgICAgIH0sXG4gICAgICB9KSxcbiAgICAgIEFwaUJhZFJlcXVlc3RSZXNwb25zZSh7IGRlc2NyaXB0aW9uOiBcIlBheWxvYWQgdmFsaWRhdGlvbiBmYWlsZWQuXCIgfSksXG4gICAgICBBcGlVbnByb2Nlc3NhYmxlRW50aXR5UmVzcG9uc2Uoe1xuICAgICAgICBkZXNjcmlwdGlvbjogXCJSZXBvc2l0b3J5IHJlamVjdGVkIHRoZSBwcm92aWRlZCBwYXlsb2FkLlwiLFxuICAgICAgfSksXG4gICAgXTtcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIGJ1bGtSZWFkRGVjb3JhdG9ycyhcbiAgICBNb2RlbENvbnN0cjogTW9kZWxDb25zdHJ1Y3Rvcjxhbnk+LFxuICAgIG1vZGVsQ2xhenpOYW1lOiBzdHJpbmdcbiAgKTogQXJyYXk8KHRhcmdldDogYW55LCBrZXk6IHN0cmluZywgZGVzYzogYW55KSA9PiB2b2lkPiB7XG4gICAgcmV0dXJuIFtcbiAgICAgIEFwaU9wZXJhdGlvbkZyb21Nb2RlbChNb2RlbENvbnN0ciwgXCJHRVRcIiwgXCJidWxrXCIpLFxuICAgICAgQXBpT3BlcmF0aW9uKHsgc3VtbWFyeTogYFJldHJpZXZlICR7bW9kZWxDbGF6ek5hbWV9IHJlY29yZHMgYnkgaWRzLmAgfSksXG4gICAgICBBcGlRdWVyeSh7IG5hbWU6IFwiaWRzXCIsIHJlcXVpcmVkOiB0cnVlLCB0eXBlOiBcImFycmF5XCIgfSksXG4gICAgICBBcGlPa1Jlc3BvbnNlKHtcbiAgICAgICAgZGVzY3JpcHRpb246IGAke21vZGVsQ2xhenpOYW1lfSByZXRyaWV2ZWQgc3VjY2Vzc2Z1bGx5LmAsXG4gICAgICAgIHNjaGVtYToge1xuICAgICAgICAgIHR5cGU6IFwiYXJyYXlcIixcbiAgICAgICAgICBpdGVtczogeyAkcmVmOiBnZXRTY2hlbWFQYXRoKE1vZGVsQ29uc3RyKSB9LFxuICAgICAgICB9LFxuICAgICAgfSksXG4gICAgICBBcGlOb3RGb3VuZFJlc3BvbnNlKHtcbiAgICAgICAgZGVzY3JpcHRpb246IGBObyAke21vZGVsQ2xhenpOYW1lfSByZWNvcmQgbWF0Y2hlcyB0aGUgcHJvdmlkZWQgaWRlbnRpZmllci5gLFxuICAgICAgfSksXG4gICAgXTtcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIGJ1bGtVcGRhdGVEZWNvcmF0b3JzKFxuICAgIE1vZGVsQ29uc3RyOiBNb2RlbENvbnN0cnVjdG9yPGFueT4sXG4gICAgbW9kZWxDbGF6ek5hbWU6IHN0cmluZyxcbiAgICBhcGlQcm9wZXJ0aWVzOiBEZWNhZkFwaVByb3BlcnR5W11cbiAgKTogQXJyYXk8KHRhcmdldDogYW55LCBrZXk6IHN0cmluZywgZGVzYzogYW55KSA9PiB2b2lkPiB7XG4gICAgcmV0dXJuIFtcbiAgICAgIEFwaU9wZXJhdGlvbkZyb21Nb2RlbChNb2RlbENvbnN0ciwgXCJQVVRcIiwgXCJidWxrXCIpLFxuICAgICAgQXBpUGFyYW1zRnJvbU1vZGVsKGFwaVByb3BlcnRpZXMpLFxuICAgICAgQXBpT3BlcmF0aW9uKHtcbiAgICAgICAgc3VtbWFyeTogYFJlcGxhY2UgZXhpc3RpbmcgJHttb2RlbENsYXp6TmFtZX0gcmVjb3JkcyB3aXRoIG5ldyBwYXlsb2Fkcy5gLFxuICAgICAgfSksXG4gICAgICBBcGlCb2R5KHtcbiAgICAgICAgZGVzY3JpcHRpb246IGBQYXlsb2FkIGZvciByZXBsYWNpbmcgZXhpc3RpbmcgcmVjb3JkcyBvZiAke21vZGVsQ2xhenpOYW1lfWAsXG4gICAgICAgIHNjaGVtYToge1xuICAgICAgICAgIHR5cGU6IFwiYXJyYXlcIixcbiAgICAgICAgICAkcmVmOiBnZXRTY2hlbWFQYXRoKER0b0ZvcihPcGVyYXRpb25LZXlzLlVQREFURSwgTW9kZWxDb25zdHIpKSxcbiAgICAgICAgfSxcbiAgICAgIH0pLFxuICAgICAgQXBpT2tSZXNwb25zZSh7XG4gICAgICAgIGRlc2NyaXB0aW9uOiBgJHttb2RlbENsYXp6TmFtZX0gdXBkYXRlZCBzdWNjZXNzZnVsbHkuYCxcbiAgICAgICAgc2NoZW1hOiB7XG4gICAgICAgICAgdHlwZTogXCJhcnJheVwiLFxuICAgICAgICAgIGl0ZW1zOiB7ICRyZWY6IGdldFNjaGVtYVBhdGgoTW9kZWxDb25zdHIpIH0sXG4gICAgICAgIH0sXG4gICAgICB9KSxcbiAgICAgIEFwaU5vdEZvdW5kUmVzcG9uc2Uoe1xuICAgICAgICBkZXNjcmlwdGlvbjogYE5vICR7bW9kZWxDbGF6ek5hbWV9IHJlY29yZCBtYXRjaGVzIHRoZSBwcm92aWRlZCBpZGVudGlmaWVyLmAsXG4gICAgICB9KSxcbiAgICAgIEFwaUJhZFJlcXVlc3RSZXNwb25zZSh7IGRlc2NyaXB0aW9uOiBcIlBheWxvYWQgdmFsaWRhdGlvbiBmYWlsZWQuXCIgfSksXG4gICAgXTtcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIGJ1bGtEZWxldGVEZWNvcmF0b3JzKFxuICAgIE1vZGVsQ29uc3RyOiBNb2RlbENvbnN0cnVjdG9yPGFueT4sXG4gICAgbW9kZWxDbGF6ek5hbWU6IHN0cmluZyxcbiAgICBhcGlQcm9wZXJ0aWVzOiBEZWNhZkFwaVByb3BlcnR5W11cbiAgKTogQXJyYXk8KHRhcmdldDogYW55LCBrZXk6IHN0cmluZywgZGVzYzogYW55KSA9PiB2b2lkPiB7XG4gICAgcmV0dXJuIFtcbiAgICAgIEFwaU9wZXJhdGlvbkZyb21Nb2RlbChNb2RlbENvbnN0ciwgXCJERUxFVEVcIiwgXCJidWxrXCIpLFxuICAgICAgQXBpUGFyYW1zRnJvbU1vZGVsKGFwaVByb3BlcnRpZXMpLFxuICAgICAgQXBpT3BlcmF0aW9uKHsgc3VtbWFyeTogYERlbGV0ZSAke21vZGVsQ2xhenpOYW1lfSByZWNvcmRzIGJ5IGlkcy5gIH0pLFxuICAgICAgQXBpUXVlcnkoeyBuYW1lOiBcImlkc1wiLCByZXF1aXJlZDogdHJ1ZSwgdHlwZTogXCJhcnJheVwiIH0pLFxuICAgICAgQXBpT2tSZXNwb25zZSh7XG4gICAgICAgIGRlc2NyaXB0aW9uOiBgJHttb2RlbENsYXp6TmFtZX0gZGVsZXRlZCBzdWNjZXNzZnVsbHkuYCxcbiAgICAgICAgc2NoZW1hOiB7XG4gICAgICAgICAgdHlwZTogXCJhcnJheVwiLFxuICAgICAgICAgIGl0ZW1zOiB7ICRyZWY6IGdldFNjaGVtYVBhdGgoTW9kZWxDb25zdHIpIH0sXG4gICAgICAgIH0sXG4gICAgICB9KSxcbiAgICAgIEFwaU5vdEZvdW5kUmVzcG9uc2Uoe1xuICAgICAgICBkZXNjcmlwdGlvbjogYE5vICR7bW9kZWxDbGF6ek5hbWV9IHJlY29yZCBtYXRjaGVzIHRoZSBwcm92aWRlZCBpZGVudGlmaWVyLmAsXG4gICAgICB9KSxcbiAgICBdO1xuICB9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZERlY29yYXRvcnMoXG4gICAgTW9kZWxDb25zdHI6IE1vZGVsQ29uc3RydWN0b3I8YW55PixcbiAgICBtb2RlbENsYXp6TmFtZTogc3RyaW5nLFxuICAgIGFwaVByb3BlcnRpZXM6IERlY2FmQXBpUHJvcGVydHlbXSxcbiAgICBwa1BhdGg6IHN0cmluZ1xuICApOiBBcnJheTwodGFyZ2V0OiBhbnksIGtleTogc3RyaW5nLCBkZXNjOiBhbnkpID0+IHZvaWQ+IHtcbiAgICByZXR1cm4gW1xuICAgICAgQXBpT3BlcmF0aW9uRnJvbU1vZGVsKE1vZGVsQ29uc3RyLCBcIkdFVFwiLCBwa1BhdGgpLFxuICAgICAgQXBpUGFyYW1zRnJvbU1vZGVsKGFwaVByb3BlcnRpZXMpLFxuICAgICAgQXBpT3BlcmF0aW9uKHsgc3VtbWFyeTogYFJldHJpZXZlIGEgJHttb2RlbENsYXp6TmFtZX0gcmVjb3JkIGJ5IGlkLmAgfSksXG4gICAgICBBcGlPa1Jlc3BvbnNlKHtcbiAgICAgICAgZGVzY3JpcHRpb246IGAke21vZGVsQ2xhenpOYW1lfSByZXRyaWV2ZWQgc3VjY2Vzc2Z1bGx5LmAsXG4gICAgICAgIHNjaGVtYTogeyAkcmVmOiBnZXRTY2hlbWFQYXRoKE1vZGVsQ29uc3RyKSB9LFxuICAgICAgfSksXG4gICAgICBBcGlOb3RGb3VuZFJlc3BvbnNlKHtcbiAgICAgICAgZGVzY3JpcHRpb246IGBObyAke21vZGVsQ2xhenpOYW1lfSByZWNvcmQgbWF0Y2hlcyB0aGUgcHJvdmlkZWQgaWRlbnRpZmllci5gLFxuICAgICAgfSksXG4gICAgXTtcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIHVwZGF0ZURlY29yYXRvcnMoXG4gICAgTW9kZWxDb25zdHI6IE1vZGVsQ29uc3RydWN0b3I8YW55PixcbiAgICBtb2RlbENsYXp6TmFtZTogc3RyaW5nLFxuICAgIGFwaVByb3BlcnRpZXM6IERlY2FmQXBpUHJvcGVydHlbXSxcbiAgICBwa1BhdGg6IHN0cmluZ1xuICApOiBBcnJheTwodGFyZ2V0OiBhbnksIGtleTogc3RyaW5nLCBkZXNjOiBhbnkpID0+IHZvaWQ+IHtcbiAgICByZXR1cm4gW1xuICAgICAgQXBpT3BlcmF0aW9uRnJvbU1vZGVsKE1vZGVsQ29uc3RyLCBcIlBVVFwiLCBwa1BhdGgpLFxuICAgICAgQXBpUGFyYW1zRnJvbU1vZGVsKGFwaVByb3BlcnRpZXMpLFxuICAgICAgQXBpT3BlcmF0aW9uKHtcbiAgICAgICAgc3VtbWFyeTogYFJlcGxhY2UgYW4gZXhpc3RpbmcgJHttb2RlbENsYXp6TmFtZX0gcmVjb3JkIHdpdGggYSBuZXcgcGF5bG9hZC5gLFxuICAgICAgfSksXG4gICAgICBBcGlCb2R5KHtcbiAgICAgICAgZGVzY3JpcHRpb246IGBQYXlsb2FkIGZvciByZXBsYWNpbmcgYW4gZXhpc3RpbmcgcmVjb3JkIG9mICR7bW9kZWxDbGF6ek5hbWV9YCxcbiAgICAgICAgdHlwZTogRHRvRm9yKE9wZXJhdGlvbktleXMuVVBEQVRFLCBNb2RlbENvbnN0ciksXG4gICAgICB9KSxcbiAgICAgIEFwaU9rUmVzcG9uc2Uoe1xuICAgICAgICBkZXNjcmlwdGlvbjogYCR7bW9kZWxDbGF6ek5hbWV9IHVwZGF0ZWQgc3VjY2Vzc2Z1bGx5LmAsXG4gICAgICAgIHNjaGVtYTogeyAkcmVmOiBnZXRTY2hlbWFQYXRoKE1vZGVsQ29uc3RyKSB9LFxuICAgICAgfSksXG4gICAgICBBcGlOb3RGb3VuZFJlc3BvbnNlKHtcbiAgICAgICAgZGVzY3JpcHRpb246IGBObyAke21vZGVsQ2xhenpOYW1lfSByZWNvcmQgbWF0Y2hlcyB0aGUgcHJvdmlkZWQgaWRlbnRpZmllci5gLFxuICAgICAgfSksXG4gICAgICBBcGlCYWRSZXF1ZXN0UmVzcG9uc2UoeyBkZXNjcmlwdGlvbjogXCJQYXlsb2FkIHZhbGlkYXRpb24gZmFpbGVkLlwiIH0pLFxuICAgIF07XG4gIH1cblxuICBwcml2YXRlIHN0YXRpYyBkZWxldGVEZWNvcmF0b3JzKFxuICAgIE1vZGVsQ29uc3RyOiBNb2RlbENvbnN0cnVjdG9yPGFueT4sXG4gICAgbW9kZWxDbGF6ek5hbWU6IHN0cmluZyxcbiAgICBhcGlQcm9wZXJ0aWVzOiBEZWNhZkFwaVByb3BlcnR5W10sXG4gICAgcGtQYXRoOiBzdHJpbmdcbiAgKTogQXJyYXk8KHRhcmdldDogYW55LCBrZXk6IHN0cmluZywgZGVzYzogYW55KSA9PiB2b2lkPiB7XG4gICAgcmV0dXJuIFtcbiAgICAgIEFwaU9wZXJhdGlvbkZyb21Nb2RlbChNb2RlbENvbnN0ciwgXCJERUxFVEVcIiwgcGtQYXRoKSxcbiAgICAgIEFwaVBhcmFtc0Zyb21Nb2RlbChhcGlQcm9wZXJ0aWVzKSxcbiAgICAgIEFwaU9wZXJhdGlvbih7IHN1bW1hcnk6IGBEZWxldGUgYSAke21vZGVsQ2xhenpOYW1lfSByZWNvcmQgYnkgaWQuYCB9KSxcbiAgICAgIEFwaU9rUmVzcG9uc2Uoe1xuICAgICAgICBkZXNjcmlwdGlvbjogYCR7bW9kZWxDbGF6ek5hbWV9IGRlbGV0ZWQgc3VjY2Vzc2Z1bGx5LmAsXG4gICAgICAgIHNjaGVtYTogeyAkcmVmOiBnZXRTY2hlbWFQYXRoKE1vZGVsQ29uc3RyKSB9LFxuICAgICAgfSksXG4gICAgICBBcGlOb3RGb3VuZFJlc3BvbnNlKHtcbiAgICAgICAgZGVzY3JpcHRpb246IGBObyAke21vZGVsQ2xhenpOYW1lfSByZWNvcmQgbWF0Y2hlcyB0aGUgcHJvdmlkZWQgaWRlbnRpZmllci5gLFxuICAgICAgfSksXG4gICAgXTtcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIHN0YXRlbWVudERlY29yYXRvcnMoXG4gICAgTW9kZWxDb25zdHI6IE1vZGVsQ29uc3RydWN0b3I8YW55PixcbiAgICBtb2RlbENsYXp6TmFtZTogc3RyaW5nXG4gICk6IEFycmF5PCh0YXJnZXQ6IGFueSwga2V5OiBzdHJpbmcsIGRlc2M6IGFueSkgPT4gdm9pZD4ge1xuICAgIHJldHVybiBbXG4gICAgICBBcGlPcGVyYXRpb25Gcm9tTW9kZWwoTW9kZWxDb25zdHIsIFwiR0VUXCIsIFwic3RhdGVtZW50LzptZXRob2QvKmFyZ3NcIiksXG4gICAgICBBcGlPcGVyYXRpb24oe1xuICAgICAgICBzdW1tYXJ5OiBgRXhlY3V0ZXMgYSBwcmVwYXJlZCBzdGF0ZW1lbnQgb24gJHttb2RlbENsYXp6TmFtZX0uYCxcbiAgICAgIH0pLFxuICAgICAgQXBpUGFyYW0oeyBuYW1lOiBcIm1ldGhvZFwiLCBkZXNjcmlwdGlvbjogXCJ0aGUgcHJlcGFyZWQgc3RhdGVtZW50IHRvIGV4ZWN1dGVcIiB9KSxcbiAgICAgIEFwaVBhcmFtKHtcbiAgICAgICAgbmFtZTogXCJhcmdzXCIsXG4gICAgICAgIGRlc2NyaXB0aW9uOiBcImNvbmNhdGVuYXRlZCBsaXN0IG9mIGFyZ3VtZW50cyB0aGUgcHJlcGFyZWQgc3RhdGVtZW50IGNhbiBhY2NlcHRcIixcbiAgICAgIH0pLFxuICAgICAgQXBpUXVlcnkoe1xuICAgICAgICBuYW1lOiBcImRpcmVjdGlvblwiLFxuICAgICAgICByZXF1aXJlZDogdHJ1ZSxcbiAgICAgICAgZW51bTogT3JkZXJEaXJlY3Rpb24sXG4gICAgICAgIGRlc2NyaXB0aW9uOiBcInRoZSBzb3J0IG9yZGVyIHdoZW4gYXBwbGljYWJsZVwiLFxuICAgICAgfSksXG4gICAgICBBcGlRdWVyeSh7IG5hbWU6IFwibGltaXRcIiwgcmVxdWlyZWQ6IHRydWUsIGRlc2NyaXB0aW9uOiBcImxpbWl0IG9yIHBhZ2Ugc2l6ZSB3aGVuIGFwcGxpY2FibGVcIiB9KSxcbiAgICAgIEFwaVF1ZXJ5KHsgbmFtZTogXCJvZmZzZXRcIiwgcmVxdWlyZWQ6IHRydWUsIGRlc2NyaXB0aW9uOiBcIm9mZnNldCBvciBib29rbWFyayB3aGVuIGFwcGxpY2FibGVcIiB9KSxcbiAgICAgIEFwaU9rUmVzcG9uc2UoeyBkZXNjcmlwdGlvbjogYCR7bW9kZWxDbGF6ek5hbWV9IGxpc3RlZCBmb3VuZC5gIH0pLFxuICAgICAgQXBpTm90Rm91bmRSZXNwb25zZSh7XG4gICAgICAgIGRlc2NyaXB0aW9uOiBgTm8gJHttb2RlbENsYXp6TmFtZX0gcmVjb3JkIG1hdGNoZXMgdGhlIHByb3ZpZGVkIGlkZW50aWZpZXIuYCxcbiAgICAgIH0pLFxuICAgIF07XG4gIH1cblxuICBwcml2YXRlIHN0YXRpYyBzdGF0ZW1lbnRTaG9ydGN1dERlY29yYXRvcnMoXG4gICAgTW9kZWxDb25zdHI6IE1vZGVsQ29uc3RydWN0b3I8YW55PixcbiAgICBtb2RlbENsYXp6TmFtZTogc3RyaW5nLFxuICAgIHBhdGg6IHN0cmluZyxcbiAgICBzdGF0ZW1lbnRLZXk6IHN0cmluZ1xuICApOiBBcnJheTwodGFyZ2V0OiBhbnksIGtleTogc3RyaW5nLCBkZXNjOiBhbnkpID0+IHZvaWQ+IHtcbiAgICBjb25zdCBiYXNlOiBBcnJheTwodGFyZ2V0OiBhbnksIGtleTogc3RyaW5nLCBkZXNjOiBhbnkpID0+IHZvaWQ+ID0gW1xuICAgICAgQXBpT3BlcmF0aW9uRnJvbU1vZGVsKE1vZGVsQ29uc3RyLCBcIkdFVFwiLCBwYXRoKSxcbiAgICAgIEFwaU9wZXJhdGlvbih7IHN1bW1hcnk6IGBSZXRyaWV2ZSAke21vZGVsQ2xhenpOYW1lfSByZWNvcmRzLmAgfSksXG4gICAgICBBcGlPa1Jlc3BvbnNlKHsgZGVzY3JpcHRpb246IGAke21vZGVsQ2xhenpOYW1lfSByZXRyaWV2ZWQgc3VjY2Vzc2Z1bGx5LmAgfSksXG4gICAgXTtcblxuICAgIGNvbnN0IHNlZ21lbnRzID0gcGF0aC5zcGxpdChcIi9cIikuZmlsdGVyKChzKSA9PiBzLnN0YXJ0c1dpdGgoXCI6XCIpKTtcbiAgICBzZWdtZW50cy5mb3JFYWNoKChzZWcpID0+IHtcbiAgICAgIGNvbnN0IG5hbWUgPSBzZWcucmVwbGFjZShcIjpcIiwgXCJcIik7XG4gICAgICBiYXNlLnB1c2goQXBpUGFyYW0oeyBuYW1lLCBkZXNjcmlwdGlvbjogYFRoZSAke25hbWV9IHBhcmFtZXRlcmAgfSkpO1xuICAgIH0pO1xuXG4gICAgaWYgKFxuICAgICAgcGF0aC5zdGFydHNXaXRoKFwibGlzdEJ5L1wiKSB8fFxuICAgICAgcGF0aC5zdGFydHNXaXRoKFwicGFnaW5hdGVCeS9cIikgfHxcbiAgICAgIHBhdGguc3RhcnRzV2l0aChcImZpbmQvXCIpIHx8XG4gICAgICBwYXRoLnN0YXJ0c1dpdGgoXCJwYWdlL1wiKVxuICAgICkge1xuICAgICAgYmFzZS5wdXNoKFxuICAgICAgICBBcGlRdWVyeSh7XG4gICAgICAgICAgbmFtZTogXCJkaXJlY3Rpb25cIixcbiAgICAgICAgICByZXF1aXJlZDogdHJ1ZSxcbiAgICAgICAgICBlbnVtOiBPcmRlckRpcmVjdGlvbixcbiAgICAgICAgICBkZXNjcmlwdGlvbjogXCJ0aGUgc29ydCBvcmRlclwiLFxuICAgICAgICB9KVxuICAgICAgKTtcbiAgICB9XG4gICAgaWYgKHBhdGguc3RhcnRzV2l0aChcInBhZ2luYXRlQnkvXCIpIHx8IHBhdGguc3RhcnRzV2l0aChcInBhZ2UvXCIpKSB7XG4gICAgICBiYXNlLnB1c2goXG4gICAgICAgIEFwaVF1ZXJ5KHsgbmFtZTogXCJsaW1pdFwiLCByZXF1aXJlZDogZmFsc2UsIGRlc2NyaXB0aW9uOiBcInBhZ2Ugc2l6ZVwiIH0pLFxuICAgICAgICBBcGlRdWVyeSh7IG5hbWU6IFwib2Zmc2V0XCIsIHJlcXVpcmVkOiBmYWxzZSwgZGVzY3JpcHRpb246IFwicGFnZSBudW1iZXJcIiB9KSxcbiAgICAgICAgQXBpUXVlcnkoeyBuYW1lOiBcImJvb2ttYXJrXCIsIHJlcXVpcmVkOiBmYWxzZSwgZGVzY3JpcHRpb246IFwiYm9va21hcmsgZm9yIGN1cnNvciBwYWdpbmF0aW9uXCIgfSlcbiAgICAgICk7XG4gICAgfVxuXG4gICAgaWYgKHBhdGguc3RhcnRzV2l0aChcImZpbmRPbmVCeS9cIikgfHwgcGF0aC5zdGFydHNXaXRoKFwiZmluZEJ5L1wiKSkge1xuICAgICAgYmFzZS5wdXNoKFxuICAgICAgICBBcGlOb3RGb3VuZFJlc3BvbnNlKHtcbiAgICAgICAgICBkZXNjcmlwdGlvbjogYE5vICR7bW9kZWxDbGF6ek5hbWV9IHJlY29yZCBtYXRjaGVzIHRoZSBwcm92aWRlZCBpZGVudGlmaWVyLmAsXG4gICAgICAgIH0pXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmIChcbiAgICAgIHN0YXRlbWVudEtleSA9PT0gUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLkNPVU5UX09GIHx8XG4gICAgICBzdGF0ZW1lbnRLZXkgPT09IFByZXBhcmVkU3RhdGVtZW50S2V5cy5BVkdfT0YgfHxcbiAgICAgIHN0YXRlbWVudEtleSA9PT0gUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLlNVTV9PRlxuICAgICkge1xuICAgICAgYmFzZS5wdXNoKEFwaU9rUmVzcG9uc2UoeyBkZXNjcmlwdGlvbjogYFJlc3VsdCBmb3IgJHttb2RlbENsYXp6TmFtZX0uYCwgdHlwZTogTnVtYmVyIH0pKTtcbiAgICB9XG4gICAgaWYgKHN0YXRlbWVudEtleSA9PT0gUHJlcGFyZWRTdGF0ZW1lbnRLZXlzLkRJU1RJTkNUX09GKSB7XG4gICAgICBiYXNlLnB1c2goQXBpT2tSZXNwb25zZSh7IGRlc2NyaXB0aW9uOiBgRGlzdGluY3QgdmFsdWVzIGZvciAke21vZGVsQ2xhenpOYW1lfS5gLCB0eXBlOiBbU3RyaW5nXSB9KSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIGJhc2U7XG4gIH1cblxuICBwcml2YXRlIHN0YXRpYyBnZXRRdWVyeURlY29yYXRvcnMoXG4gICAgbWV0aG9kTmFtZTogc3RyaW5nLFxuICAgIHJvdXRlUGF0aDogc3RyaW5nLFxuICAgIGh0dHBWZXJiOiBzdHJpbmcsXG4gICAgaW5jbHVkZVF1ZXJ5UGFyYW1zOiBib29sZWFuID0gZmFsc2VcbiAgKTogQXJyYXk8KHRhcmdldDogYW55LCBrZXk6IHN0cmluZywgZGVzYzogYW55KSA9PiB2b2lkPiB7XG4gICAgY29uc3QgZXh0cmFjdFBhdGhQYXJhbXMgPSAocDogc3RyaW5nKTogc3RyaW5nW10gPT5cbiAgICAgIHAuc3BsaXQoXCIvXCIpLmZpbHRlcigocykgPT4gcy5zdGFydHNXaXRoKFwiOlwiKSkubWFwKChzKSA9PiBzLnNsaWNlKDEpKTtcblxuICAgIGNvbnN0IGFwaVBhdGhQYXJhbXMgPSBleHRyYWN0UGF0aFBhcmFtcyhyb3V0ZVBhdGgpLm1hcCgobmFtZSkgPT4gKHtcbiAgICAgIG5hbWUsXG4gICAgICBkZXNjcmlwdGlvbjogYCR7bmFtZX0gcGFyYW1ldGVyIGZvciB0aGUgcXVlcnlgLFxuICAgICAgcmVxdWlyZWQ6IHRydWUsXG4gICAgICB0eXBlOiBTdHJpbmcsXG4gICAgfSkpO1xuXG4gICAgY29uc3QgZGVjb3JhdG9yczogQXJyYXk8KHRhcmdldDogYW55LCBrZXk6IHN0cmluZywgZGVzYzogYW55KSA9PiB2b2lkPiA9IFtcbiAgICAgIC4uLmFwaVBhdGhQYXJhbXMubWFwKChwKSA9PiBBcGlQYXJhbShwKSksXG4gICAgICBBcGlPcGVyYXRpb24oeyBzdW1tYXJ5OiBgUmV0cmlldmUgcmVjb3JkcyB1c2luZyBcIiR7bWV0aG9kTmFtZX1cIi5gIH0pLFxuICAgICAgQXBpT2tSZXNwb25zZSh7IGRlc2NyaXB0aW9uOiBcIlJlc3VsdCBzdWNjZXNzZnVsbHkgcmV0cmlldmVkLlwiIH0pLFxuICAgICAgQXBpTm9Db250ZW50UmVzcG9uc2UoeyBkZXNjcmlwdGlvbjogXCJObyBjb250ZW50IHJldHVybmVkIGJ5IHRoZSBtZXRob2QuXCIgfSksXG4gICAgXTtcblxuICAgIGlmIChodHRwVmVyYiA9PT0gXCJHRVRcIiAmJiBpbmNsdWRlUXVlcnlQYXJhbXMpIHtcbiAgICAgIGRlY29yYXRvcnMucHVzaChcbiAgICAgICAgQXBpUXVlcnkoe1xuICAgICAgICAgIG5hbWU6IFwiZGlyZWN0aW9uXCIsXG4gICAgICAgICAgcmVxdWlyZWQ6IGZhbHNlLFxuICAgICAgICAgIGVudW06IE9yZGVyRGlyZWN0aW9uLFxuICAgICAgICAgIGRlc2NyaXB0aW9uOiBcInRoZSBzb3J0IG9yZGVyIHdoZW4gYXBwbGljYWJsZVwiLFxuICAgICAgICB9KSxcbiAgICAgICAgQXBpUXVlcnkoeyBuYW1lOiBcImxpbWl0XCIsIHJlcXVpcmVkOiBmYWxzZSwgZGVzY3JpcHRpb246IFwibGltaXQgb3IgcGFnZSBzaXplXCIgfSksXG4gICAgICAgIEFwaVF1ZXJ5KHsgbmFtZTogXCJvZmZzZXRcIiwgcmVxdWlyZWQ6IGZhbHNlLCBkZXNjcmlwdGlvbjogXCJvZmZzZXQgb3IgYm9va21hcmtcIiB9KVxuICAgICAgKTtcbiAgICB9XG5cbiAgICByZXR1cm4gZGVjb3JhdG9ycztcbiAgfVxufVxuIl19
|