sonamu 0.5.6 → 0.6.0
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/dist/api/base-frame.js +12 -2
- package/dist/api/caster.js +66 -2
- package/dist/api/code-converters.js +489 -2
- package/dist/api/config.d.ts +76 -0
- package/dist/api/config.d.ts.map +1 -0
- package/dist/api/config.js +32 -0
- package/dist/api/context.d.ts +1 -0
- package/dist/api/context.d.ts.map +1 -1
- package/dist/api/context.js +3 -2
- package/dist/api/decorators.d.ts +1 -0
- package/dist/api/decorators.d.ts.map +1 -1
- package/dist/api/decorators.js +142 -2
- package/dist/api/index.js +9 -2
- package/dist/api/sonamu.d.ts +8 -22
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +482 -2
- package/dist/bin/build-config.d.ts +2 -1
- package/dist/bin/build-config.d.ts.map +1 -1
- package/dist/bin/build-config.js +12 -2
- package/dist/bin/cli-wrapper.js +71 -2
- package/dist/bin/cli.js +418 -2
- package/dist/bin/hot-hook-register.d.ts +11 -0
- package/dist/bin/hot-hook-register.d.ts.map +1 -0
- package/dist/bin/hot-hook-register.js +21 -0
- package/dist/database/_batch_update.js +78 -2
- package/dist/database/base-model.js +247 -2
- package/dist/database/code-generator.js +53 -2
- package/dist/database/db.d.ts +5 -16
- package/dist/database/db.d.ts.map +1 -1
- package/dist/database/db.js +132 -2
- package/dist/database/knex-plugins/knex-on-duplicate-update.js +39 -2
- package/dist/database/puri-wrapper.d.ts +22 -10
- package/dist/database/puri-wrapper.d.ts.map +1 -1
- package/dist/database/puri-wrapper.js +109 -2
- package/dist/database/puri.d.ts +105 -73
- package/dist/database/puri.d.ts.map +1 -1
- package/dist/database/puri.js +539 -2
- package/dist/database/puri.types.d.ts +33 -42
- package/dist/database/puri.types.d.ts.map +1 -1
- package/dist/database/puri.types.js +3 -2
- package/dist/database/transaction-context.d.ts +3 -3
- package/dist/database/transaction-context.d.ts.map +1 -1
- package/dist/database/transaction-context.js +14 -2
- package/dist/database/upsert-builder.js +215 -2
- package/dist/entity/entity-manager.d.ts +3 -1
- package/dist/entity/entity-manager.d.ts.map +1 -1
- package/dist/entity/entity-manager.js +114 -2
- package/dist/entity/entity-utils.js +210 -2
- package/dist/entity/entity.d.ts.map +1 -1
- package/dist/entity/entity.js +651 -2
- package/dist/exceptions/error-handler.js +29 -2
- package/dist/exceptions/so-exceptions.js +85 -2
- package/dist/file-storage/driver.js +79 -2
- package/dist/file-storage/file-storage.js +75 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -2
- package/dist/migration/code-generation.js +558 -2
- package/dist/migration/migration-set.js +364 -2
- package/dist/migration/migrator.d.ts +0 -9
- package/dist/migration/migrator.d.ts.map +1 -1
- package/dist/migration/migrator.js +510 -2
- package/dist/migration/types.js +3 -2
- package/dist/naite/naite.d.ts +12 -0
- package/dist/naite/naite.d.ts.map +1 -0
- package/dist/naite/naite.js +72 -0
- package/dist/stream/index.js +3 -2
- package/dist/stream/sse.js +38 -2
- package/dist/syncer/api-parser.d.ts +20 -0
- package/dist/syncer/api-parser.d.ts.map +1 -0
- package/dist/syncer/api-parser.js +229 -0
- package/dist/syncer/checksum.d.ts +21 -0
- package/dist/syncer/checksum.d.ts.map +1 -0
- package/dist/syncer/checksum.js +98 -0
- package/dist/syncer/code-generator.d.ts +20 -0
- package/dist/syncer/code-generator.d.ts.map +1 -0
- package/dist/syncer/code-generator.js +141 -0
- package/dist/syncer/entity-operations.d.ts +17 -0
- package/dist/syncer/entity-operations.d.ts.map +1 -0
- package/dist/syncer/entity-operations.js +58 -0
- package/dist/syncer/file-patterns.d.ts +29 -0
- package/dist/syncer/file-patterns.d.ts.map +1 -0
- package/dist/syncer/file-patterns.js +38 -0
- package/dist/syncer/index.d.ts +6 -0
- package/dist/syncer/index.d.ts.map +1 -1
- package/dist/syncer/index.js +9 -2
- package/dist/syncer/module-loader.d.ts +35 -0
- package/dist/syncer/module-loader.d.ts.map +1 -0
- package/dist/syncer/module-loader.js +82 -0
- package/dist/syncer/syncer.d.ts +93 -108
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/syncer/syncer.js +375 -2
- package/dist/template/entity-converter.d.ts +14 -0
- package/dist/template/entity-converter.d.ts.map +1 -0
- package/dist/template/entity-converter.js +101 -0
- package/dist/template/helpers.d.ts +23 -0
- package/dist/template/helpers.d.ts.map +1 -0
- package/dist/template/helpers.js +64 -0
- package/dist/{templates → template/implementations}/entity.template.d.ts +3 -3
- package/dist/template/implementations/entity.template.d.ts.map +1 -0
- package/dist/template/implementations/entity.template.js +87 -0
- package/dist/{templates → template/implementations}/generated.template.d.ts +3 -3
- package/dist/template/implementations/generated.template.d.ts.map +1 -0
- package/dist/template/implementations/generated.template.js +232 -0
- package/dist/{templates → template/implementations}/generated_http.template.d.ts +3 -3
- package/dist/template/implementations/generated_http.template.d.ts.map +1 -0
- package/dist/template/implementations/generated_http.template.js +131 -0
- package/dist/{templates → template/implementations}/generated_sso.template.d.ts +3 -3
- package/dist/template/implementations/generated_sso.template.d.ts.map +1 -0
- package/dist/template/implementations/generated_sso.template.js +105 -0
- package/dist/{templates → template/implementations}/init_types.template.d.ts +3 -3
- package/dist/template/implementations/init_types.template.d.ts.map +1 -0
- package/dist/template/implementations/init_types.template.js +38 -0
- package/dist/template/implementations/model.template.d.ts +17 -0
- package/dist/template/implementations/model.template.d.ts.map +1 -0
- package/dist/template/implementations/model.template.js +171 -0
- package/dist/{templates → template/implementations}/model_test.template.d.ts +3 -3
- package/dist/template/implementations/model_test.template.d.ts.map +1 -0
- package/dist/template/implementations/model_test.template.js +35 -0
- package/dist/{templates → template/implementations}/service.template.d.ts +6 -6
- package/dist/template/implementations/service.template.d.ts.map +1 -0
- package/dist/template/implementations/service.template.js +193 -0
- package/dist/{templates → template/implementations}/view_enums_buttonset.template.d.ts +3 -3
- package/dist/template/implementations/view_enums_buttonset.template.d.ts.map +1 -0
- package/dist/template/implementations/view_enums_buttonset.template.js +31 -0
- package/dist/{templates → template/implementations}/view_enums_dropdown.template.d.ts +3 -4
- package/dist/template/implementations/view_enums_dropdown.template.d.ts.map +1 -0
- package/dist/template/implementations/view_enums_dropdown.template.js +50 -0
- package/dist/{templates → template/implementations}/view_enums_select.template.d.ts +3 -3
- package/dist/template/implementations/view_enums_select.template.d.ts.map +1 -0
- package/dist/template/implementations/view_enums_select.template.js +55 -0
- package/dist/{templates → template/implementations}/view_form.template.d.ts +5 -5
- package/dist/template/implementations/view_form.template.d.ts.map +1 -0
- package/dist/template/implementations/view_form.template.js +337 -0
- package/dist/{templates → template/implementations}/view_id_all_select.template.d.ts +3 -3
- package/dist/template/implementations/view_id_all_select.template.d.ts.map +1 -0
- package/dist/template/implementations/view_id_all_select.template.js +31 -0
- package/dist/{templates → template/implementations}/view_id_async_select.template.d.ts +3 -3
- package/dist/template/implementations/view_id_async_select.template.d.ts.map +1 -0
- package/dist/template/implementations/view_id_async_select.template.js +105 -0
- package/dist/{templates → template/implementations}/view_list.template.d.ts +5 -13
- package/dist/template/implementations/view_list.template.d.ts.map +1 -0
- package/dist/template/implementations/view_list.template.js +465 -0
- package/dist/{templates → template/implementations}/view_list_columns.template.d.ts +3 -3
- package/dist/template/implementations/view_list_columns.template.d.ts.map +1 -0
- package/dist/template/implementations/view_list_columns.template.js +49 -0
- package/dist/{templates → template/implementations}/view_search_input.template.d.ts +3 -3
- package/dist/template/implementations/view_search_input.template.d.ts.map +1 -0
- package/dist/template/implementations/view_search_input.template.js +64 -0
- package/dist/template/index.d.ts +5 -0
- package/dist/template/index.d.ts.map +1 -0
- package/dist/template/index.js +6 -0
- package/dist/template/template.d.ts +39 -0
- package/dist/template/template.d.ts.map +1 -0
- package/dist/template/template.js +47 -0
- package/dist/template/zod-converter.d.ts +18 -0
- package/dist/template/zod-converter.d.ts.map +1 -0
- package/dist/template/zod-converter.js +166 -0
- package/dist/testing/_relation-graph.js +80 -2
- package/dist/testing/fixture-manager.d.ts.map +1 -1
- package/dist/testing/fixture-manager.js +521 -2
- package/dist/types/types.d.ts +39 -40
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +289 -2
- package/dist/typings/knex.d.js +3 -2
- package/dist/utils/async-utils.d.ts +7 -0
- package/dist/utils/async-utils.d.ts.map +1 -1
- package/dist/utils/async-utils.js +57 -2
- package/dist/utils/console-util.d.ts +2 -0
- package/dist/utils/console-util.d.ts.map +1 -0
- package/dist/utils/console-util.js +6 -0
- package/dist/utils/controller.js +26 -2
- package/dist/utils/esm-utils.d.ts +45 -0
- package/dist/utils/esm-utils.d.ts.map +1 -0
- package/dist/utils/esm-utils.js +56 -0
- package/dist/utils/fs-utils.js +17 -2
- package/dist/utils/lodash-able.js +6 -2
- package/dist/utils/model.js +22 -2
- package/dist/utils/path-utils.d.ts +89 -0
- package/dist/utils/path-utils.d.ts.map +1 -0
- package/dist/utils/path-utils.js +60 -0
- package/dist/utils/process-utils.d.ts +13 -0
- package/dist/utils/process-utils.d.ts.map +1 -0
- package/dist/utils/process-utils.js +36 -0
- package/dist/utils/sql-parser.js +35 -2
- package/dist/utils/utils.d.ts +4 -7
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js +33 -2
- package/dist/utils/zod-error.d.ts.map +1 -1
- package/dist/utils/zod-error.js +19 -2
- package/package.json +21 -9
- package/src/api/code-converters.ts +2 -2
- package/src/api/config.ts +142 -0
- package/src/api/context.ts +1 -0
- package/src/api/decorators.ts +15 -5
- package/src/api/sonamu.ts +102 -87
- package/src/bin/build-config.ts +2 -1
- package/src/bin/cli-wrapper.ts +10 -3
- package/src/bin/cli.ts +108 -56
- package/src/bin/hot-hook-register.ts +22 -0
- package/src/database/base-model.ts +1 -1
- package/src/database/code-generator.ts +1 -1
- package/src/database/db.ts +53 -60
- package/src/database/puri-wrapper.ts +104 -26
- package/src/database/puri.ts +477 -580
- package/src/database/puri.types.ts +111 -201
- package/src/database/transaction-context.ts +4 -4
- package/src/database/upsert-builder.ts +1 -1
- package/src/entity/entity-manager.ts +19 -15
- package/src/entity/entity.ts +4 -3
- package/src/index.ts +2 -0
- package/src/migration/code-generation.ts +1 -1
- package/src/migration/migration-set.ts +1 -1
- package/src/migration/migrator.ts +23 -152
- package/src/naite/naite.ts +70 -0
- package/src/syncer/api-parser.ts +299 -0
- package/src/syncer/checksum.ts +152 -0
- package/src/syncer/code-generator.ts +202 -0
- package/src/syncer/entity-operations.ts +68 -0
- package/src/syncer/file-patterns.ts +56 -0
- package/src/syncer/index.ts +6 -0
- package/src/syncer/module-loader.ts +125 -0
- package/src/syncer/syncer.ts +363 -1420
- package/src/template/entity-converter.ts +123 -0
- package/src/template/helpers.ts +84 -0
- package/src/{templates → template/implementations}/entity.template.ts +4 -4
- package/src/{templates → template/implementations}/generated.template.ts +9 -9
- package/src/{templates → template/implementations}/generated_http.template.ts +9 -6
- package/src/{templates → template/implementations}/generated_sso.template.ts +7 -7
- package/src/{templates → template/implementations}/init_types.template.ts +4 -4
- package/src/{templates → template/implementations}/model.template.ts +9 -9
- package/src/{templates → template/implementations}/model_test.template.ts +5 -5
- package/src/{templates → template/implementations}/service.template.ts +29 -12
- package/src/{templates → template/implementations}/view_enums_buttonset.template.ts +3 -3
- package/src/{templates → template/implementations}/view_enums_dropdown.template.ts +5 -21
- package/src/{templates → template/implementations}/view_enums_select.template.ts +4 -4
- package/src/{templates → template/implementations}/view_form.template.ts +11 -13
- package/src/{templates → template/implementations}/view_id_all_select.template.ts +3 -3
- package/src/{templates → template/implementations}/view_id_async_select.template.ts +3 -3
- package/src/{templates → template/implementations}/view_list.template.ts +13 -64
- package/src/{templates → template/implementations}/view_list_columns.template.ts +3 -3
- package/src/{templates → template/implementations}/view_search_input.template.ts +3 -3
- package/src/template/index.ts +4 -0
- package/src/template/template.ts +86 -0
- package/src/template/zod-converter.ts +219 -0
- package/src/testing/fixture-manager.ts +8 -1
- package/src/types/types.ts +39 -62
- package/src/utils/async-utils.ts +17 -0
- package/src/utils/console-util.ts +4 -0
- package/src/utils/esm-utils.ts +69 -0
- package/src/utils/path-utils.ts +102 -0
- package/src/utils/process-utils.ts +46 -0
- package/src/utils/sql-parser.ts +1 -1
- package/src/utils/utils.ts +14 -40
- package/src/utils/zod-error.ts +0 -1
- package/dist/api/base-frame.js.map +0 -1
- package/dist/api/caster.js.map +0 -1
- package/dist/api/code-converters.js.map +0 -1
- package/dist/api/context.js.map +0 -1
- package/dist/api/decorators.js.map +0 -1
- package/dist/api/index.js.map +0 -1
- package/dist/api/sonamu.js.map +0 -1
- package/dist/bin/build-config.js.map +0 -1
- package/dist/bin/cli-wrapper.js.map +0 -1
- package/dist/bin/cli.js.map +0 -1
- package/dist/database/_batch_update.js.map +0 -1
- package/dist/database/base-model.js.map +0 -1
- package/dist/database/code-generator.js.map +0 -1
- package/dist/database/db.js.map +0 -1
- package/dist/database/knex-plugins/knex-on-duplicate-update.js.map +0 -1
- package/dist/database/puri-wrapper.js.map +0 -1
- package/dist/database/puri.js.map +0 -1
- package/dist/database/puri.types.js.map +0 -1
- package/dist/database/transaction-context.js.map +0 -1
- package/dist/database/upsert-builder.js.map +0 -1
- package/dist/entity/entity-manager.js.map +0 -1
- package/dist/entity/entity-utils.js.map +0 -1
- package/dist/entity/entity.js.map +0 -1
- package/dist/exceptions/error-handler.js.map +0 -1
- package/dist/exceptions/so-exceptions.js.map +0 -1
- package/dist/file-storage/driver.js.map +0 -1
- package/dist/file-storage/file-storage.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/migration/code-generation.js.map +0 -1
- package/dist/migration/migration-set.js.map +0 -1
- package/dist/migration/migrator.js.map +0 -1
- package/dist/migration/types.js.map +0 -1
- package/dist/stream/index.js.map +0 -1
- package/dist/stream/sse.js.map +0 -1
- package/dist/syncer/index.js.map +0 -1
- package/dist/syncer/syncer.js.map +0 -1
- package/dist/templates/base-template.d.ts +0 -13
- package/dist/templates/base-template.d.ts.map +0 -1
- package/dist/templates/base-template.js +0 -2
- package/dist/templates/base-template.js.map +0 -1
- package/dist/templates/entity.template.d.ts.map +0 -1
- package/dist/templates/entity.template.js +0 -2
- package/dist/templates/entity.template.js.map +0 -1
- package/dist/templates/generated.template.d.ts.map +0 -1
- package/dist/templates/generated.template.js +0 -2
- package/dist/templates/generated.template.js.map +0 -1
- package/dist/templates/generated_http.template.d.ts.map +0 -1
- package/dist/templates/generated_http.template.js +0 -2
- package/dist/templates/generated_http.template.js.map +0 -1
- package/dist/templates/generated_sso.template.d.ts.map +0 -1
- package/dist/templates/generated_sso.template.js +0 -2
- package/dist/templates/generated_sso.template.js.map +0 -1
- package/dist/templates/index.d.ts +0 -2
- package/dist/templates/index.d.ts.map +0 -1
- package/dist/templates/index.js +0 -2
- package/dist/templates/index.js.map +0 -1
- package/dist/templates/init_types.template.d.ts.map +0 -1
- package/dist/templates/init_types.template.js +0 -2
- package/dist/templates/init_types.template.js.map +0 -1
- package/dist/templates/model.template.d.ts +0 -17
- package/dist/templates/model.template.d.ts.map +0 -1
- package/dist/templates/model.template.js +0 -2
- package/dist/templates/model.template.js.map +0 -1
- package/dist/templates/model_test.template.d.ts.map +0 -1
- package/dist/templates/model_test.template.js +0 -2
- package/dist/templates/model_test.template.js.map +0 -1
- package/dist/templates/service.template.d.ts.map +0 -1
- package/dist/templates/service.template.js +0 -2
- package/dist/templates/service.template.js.map +0 -1
- package/dist/templates/view_enums_buttonset.template.d.ts.map +0 -1
- package/dist/templates/view_enums_buttonset.template.js +0 -2
- package/dist/templates/view_enums_buttonset.template.js.map +0 -1
- package/dist/templates/view_enums_dropdown.template.d.ts.map +0 -1
- package/dist/templates/view_enums_dropdown.template.js +0 -2
- package/dist/templates/view_enums_dropdown.template.js.map +0 -1
- package/dist/templates/view_enums_select.template.d.ts.map +0 -1
- package/dist/templates/view_enums_select.template.js +0 -2
- package/dist/templates/view_enums_select.template.js.map +0 -1
- package/dist/templates/view_form.template.d.ts.map +0 -1
- package/dist/templates/view_form.template.js +0 -2
- package/dist/templates/view_form.template.js.map +0 -1
- package/dist/templates/view_id_all_select.template.d.ts.map +0 -1
- package/dist/templates/view_id_all_select.template.js +0 -2
- package/dist/templates/view_id_all_select.template.js.map +0 -1
- package/dist/templates/view_id_async_select.template.d.ts.map +0 -1
- package/dist/templates/view_id_async_select.template.js +0 -2
- package/dist/templates/view_id_async_select.template.js.map +0 -1
- package/dist/templates/view_list.template.d.ts.map +0 -1
- package/dist/templates/view_list.template.js +0 -2
- package/dist/templates/view_list.template.js.map +0 -1
- package/dist/templates/view_list_columns.template.d.ts.map +0 -1
- package/dist/templates/view_list_columns.template.js +0 -2
- package/dist/templates/view_list_columns.template.js.map +0 -1
- package/dist/templates/view_search_input.template.d.ts.map +0 -1
- package/dist/templates/view_search_input.template.js +0 -2
- package/dist/templates/view_search_input.template.js.map +0 -1
- package/dist/testing/_relation-graph.js.map +0 -1
- package/dist/testing/fixture-manager.js.map +0 -1
- package/dist/types/types.js.map +0 -1
- package/dist/typings/knex.d.js.map +0 -1
- package/dist/utils/async-utils.js.map +0 -1
- package/dist/utils/controller.js.map +0 -1
- package/dist/utils/fs-utils.js.map +0 -1
- package/dist/utils/lodash-able.js.map +0 -1
- package/dist/utils/model.js.map +0 -1
- package/dist/utils/sql-parser.js.map +0 -1
- package/dist/utils/utils.js.map +0 -1
- package/dist/utils/zod-error.js.map +0 -1
- package/src/templates/base-template.ts +0 -19
- package/src/templates/index.ts +0 -1
|
@@ -1,2 +1,510 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"Migrator",{enumerable:true,get:function(){return Migrator}});var _lodash=/*#__PURE__*/_interop_require_default(require("lodash"));var _knex=/*#__PURE__*/_interop_require_default(require("knex"));var _chalk=/*#__PURE__*/_interop_require_default(require("chalk"));var _luxon=require("luxon");var _promises=require("fs/promises");var _fsutils=require("../utils/fs-utils");var _prompts=/*#__PURE__*/_interop_require_default(require("prompts"));var _child_process=require("child_process");var _path=/*#__PURE__*/_interop_require_default(require("path"));var _entitymanager=require("../entity/entity-manager");var _api=require("../api");var _soexceptions=require("../exceptions/so-exceptions");var _codegeneration=require("./code-generation");var _migrationset=require("./migration-set");function _array_like_to_array(arr,len){if(len==null||len>arr.length)len=arr.length;for(var i=0,arr2=new Array(len);i<len;i++)arr2[i]=arr[i];return arr2}function _array_with_holes(arr){if(Array.isArray(arr))return arr}function _array_without_holes(arr){if(Array.isArray(arr))return _array_like_to_array(arr)}function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg);var value=info.value}catch(error){reject(error);return}if(info.done){resolve(value)}else{Promise.resolve(value).then(_next,_throw)}}function _async_to_generator(fn){return function(){var self=this,args=arguments;return new Promise(function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err)}_next(undefined)})}}function _class_call_check(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function")}}function _defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||false;descriptor.configurable=true;if("value"in descriptor)descriptor.writable=true;Object.defineProperty(target,descriptor.key,descriptor)}}function _create_class(Constructor,protoProps,staticProps){if(protoProps)_defineProperties(Constructor.prototype,protoProps);if(staticProps)_defineProperties(Constructor,staticProps);return Constructor}function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}function _interop_require_default(obj){return obj&&obj.__esModule?obj:{default:obj}}function _iterable_to_array(iter){if(typeof Symbol!=="undefined"&&iter[Symbol.iterator]!=null||iter["@@iterator"]!=null)return Array.from(iter)}function _iterable_to_array_limit(arr,i){var _i=arr==null?null:typeof Symbol!=="undefined"&&arr[Symbol.iterator]||arr["@@iterator"];if(_i==null)return;var _arr=[];var _n=true;var _d=false;var _s,_e;try{for(_i=_i.call(arr);!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break}}catch(err){_d=true;_e=err}finally{try{if(!_n&&_i["return"]!=null)_i["return"]()}finally{if(_d)throw _e}}return _arr}function _non_iterable_rest(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _non_iterable_spread(){throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _object_spread(target){for(var i=1;i<arguments.length;i++){var source=arguments[i]!=null?arguments[i]:{};var ownKeys=Object.keys(source);if(typeof Object.getOwnPropertySymbols==="function"){ownKeys=ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym){return Object.getOwnPropertyDescriptor(source,sym).enumerable}))}ownKeys.forEach(function(key){_define_property(target,key,source[key])})}return target}function ownKeys(object,enumerableOnly){var keys=Object.keys(object);if(Object.getOwnPropertySymbols){var symbols=Object.getOwnPropertySymbols(object);if(enumerableOnly){symbols=symbols.filter(function(sym){return Object.getOwnPropertyDescriptor(object,sym).enumerable})}keys.push.apply(keys,symbols)}return keys}function _object_spread_props(target,source){source=source!=null?source:{};if(Object.getOwnPropertyDescriptors){Object.defineProperties(target,Object.getOwnPropertyDescriptors(source))}else{ownKeys(Object(source)).forEach(function(key){Object.defineProperty(target,key,Object.getOwnPropertyDescriptor(source,key))})}return target}function _sliced_to_array(arr,i){return _array_with_holes(arr)||_iterable_to_array_limit(arr,i)||_unsupported_iterable_to_array(arr,i)||_non_iterable_rest()}function _to_consumable_array(arr){return _array_without_holes(arr)||_iterable_to_array(arr)||_unsupported_iterable_to_array(arr)||_non_iterable_spread()}function _unsupported_iterable_to_array(o,minLen){if(!o)return;if(typeof o==="string")return _array_like_to_array(o,minLen);var n=Object.prototype.toString.call(o).slice(8,-1);if(n==="Object"&&o.constructor)n=o.constructor.name;if(n==="Map"||n==="Set")return Array.from(n);if(n==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return _array_like_to_array(o,minLen)}function _ts_generator(thisArg,body){var f,y,t,_={label:0,sent:function(){if(t[0]&1)throw t[1];return t[1]},trys:[],ops:[]},g=Object.create((typeof Iterator==="function"?Iterator:Object).prototype);return g.next=verb(0),g["throw"]=verb(1),g["return"]=verb(2),typeof Symbol==="function"&&(g[Symbol.iterator]=function(){return this}),g;function verb(n){return function(v){return step([n,v])}}function step(op){if(f)throw new TypeError("Generator is already executing.");while(g&&(g=0,op[0]&&(_=0)),_)try{if(f=1,y&&(t=op[0]&2?y["return"]:op[0]?y["throw"]||((t=y["return"])&&t.call(y),0):y.next)&&!(t=t.call(y,op[1])).done)return t;if(y=0,t)op=[op[0]&2,t.value];switch(op[0]){case 0:case 1:t=op;break;case 4:_.label++;return{value:op[1],done:false};case 5:_.label++;y=op[1];op=[0];continue;case 7:op=_.ops.pop();_.trys.pop();continue;default:if(!(t=_.trys,t=t.length>0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]<t[3])){_.label=op[1];break}if(op[0]===6&&_.label<t[1]){_.label=t[1];t=op;break}if(t&&_.label<t[2]){_.label=t[2];_.ops.push(op);break}if(t[2])_.ops.pop();_.trys.pop();continue}op=body.call(thisArg,_)}catch(e){op=[6,e];y=0}finally{f=t=0}if(op[0]&5)throw op[1];return{value:op[0]?op[1]:void 0,done:true}}}var Migrator=/*#__PURE__*/function(){"use strict";function Migrator(options){_class_call_check(this,Migrator);_define_property(this,"options",void 0);_define_property(this,"targets",void 0);this.options=options;var dbConfig=_api.Sonamu.dbConfig;if(this.options.mode==="dev"){var devDB=(0,_knex.default)(dbConfig.development_master);var testDB=(0,_knex.default)(dbConfig.test);var fixtureLocalDB=(0,_knex.default)(dbConfig.fixture_local);var applyDBs=[devDB,testDB,fixtureLocalDB];if(dbConfig.fixture_local.connection.host!==dbConfig.fixture_remote.connection.host||dbConfig.fixture_local.connection.database!==dbConfig.fixture_remote.connection.database){var fixtureRemoteDB=(0,_knex.default)(dbConfig.fixture_remote);applyDBs.push(fixtureRemoteDB)}this.targets={compare:devDB,pending:devDB,shadow:testDB,apply:applyDBs}}else if(this.options.mode==="deploy"){var productionDB=(0,_knex.default)(dbConfig.production_master);var testDB1=(0,_knex.default)(dbConfig.test);this.targets={pending:productionDB,shadow:testDB1,apply:[productionDB]}}else{throw new Error("잘못된 모드 ".concat(this.options.mode," 입력"))}}_create_class(Migrator,[{key:"getMigrationCodes",value:function getMigrationCodes(){return _async_to_generator(function(){var srcMigrationsDir,distMigrationsDir,srcMigrations,distMigrations,normal,onlyTs,onlyJs;return _ts_generator(this,function(_state){switch(_state.label){case 0:srcMigrationsDir="".concat(_api.Sonamu.apiRootPath,"/src/migrations");distMigrationsDir="".concat(_api.Sonamu.apiRootPath,"/dist/migrations");return[4,(0,_fsutils.exists)(srcMigrationsDir)];case 1:if(!!_state.sent())return[3,3];return[4,(0,_promises.mkdir)(srcMigrationsDir,{recursive:true})];case 2:_state.sent();_state.label=3;case 3:return[4,(0,_fsutils.exists)(distMigrationsDir)];case 4:if(!!_state.sent())return[3,6];return[4,(0,_promises.mkdir)(distMigrationsDir,{recursive:true})];case 5:_state.sent();_state.label=6;case 6:return[4,(0,_promises.readdir)(srcMigrationsDir)];case 7:srcMigrations=_state.sent().filter(function(f){return f.endsWith(".ts")}).map(function(f){return f.split(".")[0]});return[4,(0,_promises.readdir)(distMigrationsDir)];case 8:distMigrations=_state.sent().filter(function(f){return f.endsWith(".js")}).map(function(f){return f.split(".")[0]});normal=_lodash.default.intersection(srcMigrations,distMigrations).map(function(filename){return{name:filename,path:_path.default.join(srcMigrationsDir,filename)+".ts"}}).sort(function(a,b){return a>b?1:-1});onlyTs=_lodash.default.difference(srcMigrations,distMigrations).map(function(filename){return{name:filename,path:_path.default.join(srcMigrationsDir,filename)+".ts"}});onlyJs=_lodash.default.difference(distMigrations,srcMigrations).map(function(filename){return{name:filename,path:_path.default.join(distMigrationsDir,filename)+".js"}});return[2,{normal:normal,onlyTs:onlyTs,onlyJs:onlyJs}]}})})()}},{key:"getStatus",value:function getStatus(){return _async_to_generator(function(){var _this,_ref,normal,onlyTs,onlyJs,connKeys,statuses,preparedCodes;return _ts_generator(this,function(_state){switch(_state.label){case 0:_this=this;return[4,this.getMigrationCodes()];case 1:_ref=_state.sent(),normal=_ref.normal,onlyTs=_ref.onlyTs,onlyJs=_ref.onlyJs;if(onlyTs.length>0){console.debug({onlyTs:onlyTs});throw new _soexceptions.ServiceUnavailableException("There are un-compiled TS migration files.\nPlease compile them first. You might want to run a development server with HMR.\n\n".concat(onlyTs.map(function(f){return f.name}).join("\n")))}if(!(onlyJs.length>0))return[3,3];console.debug({onlyJs:onlyJs});return[4,Promise.all(onlyJs.map(function(f){return _async_to_generator(function(){return _ts_generator(this,function(_state){(0,_child_process.execSync)("rm -f ".concat(f.path.replace("/src/","/dist/").replace(".ts",".js")));return[2]})})()}))];case 2:_state.sent();_state.label=3;case 3:connKeys=Object.keys(_api.Sonamu.dbConfig).filter(function(key){return key.endsWith("_slave")===false});return[4,Promise.all(connKeys.map(function(connKey){return _async_to_generator(function(){var knexOptions,tConn,status,pending,currentVersion,connection,_connection_user;return _ts_generator(this,function(_state){switch(_state.label){case 0:knexOptions=_api.Sonamu.dbConfig[connKey];tConn=(0,_knex.default)(knexOptions);return[4,function(){return _async_to_generator(function(){var err;return _ts_generator(this,function(_state){switch(_state.label){case 0:_state.trys.push([0,2,,3]);return[4,tConn.migrate.status()];case 1:return[2,_state.sent()];case 2:err=_state.sent();console.warn(_chalk.default.yellow("".concat(connKey,"의 마이그레이션 상태를 가져오는 데에 실패하였습니다. 데이터베이스가 올바르게 구성되지 않은 것 같습니다. 확인하시고 다시 시도해주세요.\n시도한 연결 설정:\n").concat(JSON.stringify(knexOptions.connection,null,2),"\n발생한 에러:\n").concat(err,"\n")));return[2,"error"];case 3:return[2]}})})()}()];case 1:status=_state.sent();return[4,function(){return _async_to_generator(function(){var _ref,fdList,err;return _ts_generator(this,function(_state){switch(_state.label){case 0:_state.trys.push([0,2,,3]);return[4,tConn.migrate.list()];case 1:_ref=_sliced_to_array.apply(void 0,[_state.sent(),2]),fdList=_ref[1];return[2,fdList.map(function(fd){return fd.file.replace(".js","")})];case 2:err=_state.sent();return[2,[]];case 3:return[2]}})})()}()];case 2:pending=_state.sent();return[4,function(){return _async_to_generator(function(){var err;return _ts_generator(this,function(_state){switch(_state.label){case 0:_state.trys.push([0,2,,3]);return[4,tConn.migrate.currentVersion()];case 1:return[2,_state.sent()];case 2:err=_state.sent();return[2,"error"];case 3:return[2]}})})()}()];case 3:currentVersion=_state.sent();connection=knexOptions.connection;return[4,tConn.destroy()];case 4:_state.sent();return[2,{name:connKey.replace("_master",""),connKey:connKey,connString:"mysql2://".concat((_connection_user=connection.user)!==null&&_connection_user!==void 0?_connection_user:"","@").concat(connection.host,":").concat(connection.port,"/").concat(connection.database),currentVersion:currentVersion,status:status,pending:pending}]}})})()}))];case 4:statuses=_state.sent();return[4,function(){return _async_to_generator(function(){var status0conn,compareDBconn,genCodes;return _ts_generator(this,function(_state){switch(_state.label){case 0:status0conn=statuses.find(function(status){return status.status===0});if(status0conn===undefined){return[2,[]]}compareDBconn=(0,_knex.default)(_api.Sonamu.dbConfig[status0conn.connKey]);return[4,this.compareMigrations(compareDBconn)];case 1:genCodes=_state.sent();return[4,compareDBconn.destroy()];case 2:_state.sent();return[2,genCodes]}})}).call(_this)}()];case 5:preparedCodes=_state.sent();return[2,{conns:statuses,codes:normal,preparedCodes:preparedCodes}]}})}).call(this)}},{key:"runAction",value:function runAction(action,targets){return _async_to_generator(function(){var configs,conns,result;return _ts_generator(this,function(_state){switch(_state.label){case 0:configs=_lodash.default.uniqBy(targets.map(function(target){return{connKey:target,options:_api.Sonamu.dbConfig[target]}}).filter(function(c){return c.options!==undefined}),function(param){var options=param.options;var _options_connection_port;return"".concat(options.connection.host,":").concat((_options_connection_port=options.connection.port)!==null&&_options_connection_port!==void 0?_options_connection_port:3306,"/").concat(options.connection.database)});return[4,Promise.all(configs.map(function(config){return _async_to_generator(function(){return _ts_generator(this,function(_state){return[2,{connKey:config.connKey,knex:(0,_knex.default)(config.options)}]})})()}))];case 1:conns=_state.sent();return[4,function(){return _async_to_generator(function(){return _ts_generator(this,function(_state){switch(action){case"apply":return[2,Promise.all(conns.map(function(param){var connKey=param.connKey,knex=param.knex;return _async_to_generator(function(){var _ref,batchNo,applied;return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,knex.migrate.latest()];case 1:_ref=_sliced_to_array.apply(void 0,[_state.sent(),2]),batchNo=_ref[0],applied=_ref[1];return[2,{connKey:connKey,batchNo:batchNo,applied:applied}]}})})()}))];case"rollback":return[2,Promise.all(conns.map(function(param){var connKey=param.connKey,knex=param.knex;return _async_to_generator(function(){var _ref,batchNo,applied;return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,knex.migrate.rollback()];case 1:_ref=_sliced_to_array.apply(void 0,[_state.sent(),2]),batchNo=_ref[0],applied=_ref[1];return[2,{connKey:connKey,batchNo:batchNo,applied:applied}]}})})()}))]}return[2]})})()}()];case 2:result=_state.sent();return[4,Promise.all(conns.map(function(param){var knex=param.knex;return knex.destroy()}))];case 3:_state.sent();return[2,result]}})})()}},{key:"delCodes",value:function delCodes(codeNames){return _async_to_generator(function(){var conns,delFiles,res;return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,this.getStatus()];case 1:conns=_state.sent().conns;if(conns.some(function(conn){return codeNames.some(function(codeName){return conn.pending.includes(codeName)===false})})){throw new Error("You cannot delete a migration file if there is already applied.")}delFiles=codeNames.map(function(codeName){return["".concat(_api.Sonamu.apiRootPath,"/src/migrations/").concat(codeName,".ts"),"".concat(_api.Sonamu.apiRootPath,"/dist/migrations/").concat(codeName,".js")]}).flat();return[4,Promise.all(delFiles.map(function(delFile){return _async_to_generator(function(){return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,(0,_fsutils.exists)(delFile)];case 1:if(!_state.sent())return[3,3];console.log(_chalk.default.red("DELETE: ".concat(delFile)));return[4,(0,_promises.unlink)(delFile)];case 2:_state.sent();return[2,delFiles.includes(".ts")?1:0];case 3:return[2,0]}})})()}))];case 2:res=_state.sent();return[2,_lodash.default.sum(res)]}})}).call(this)}},{key:"generatePreparedCodes",value:function generatePreparedCodes(){return _async_to_generator(function(){var preparedCodes,migrationsDir,_iteratorNormalCompletion,_didIteratorError,_iteratorError,_iterator,_step,_step_value,index,pcode,dateTag,filePath,err;return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,this.getStatus()];case 1:preparedCodes=_state.sent().preparedCodes;if(preparedCodes.length===0){console.log(_chalk.default.green("\n현재 모두 싱크된 상태입니다."));return[2,0]}migrationsDir="".concat(_api.Sonamu.apiRootPath,"/src/migrations");_iteratorNormalCompletion=true,_didIteratorError=false,_iteratorError=undefined;_state.label=2;case 2:_state.trys.push([2,7,8,9]);_iterator=preparedCodes.entries()[Symbol.iterator]();_state.label=3;case 3:if(!!(_iteratorNormalCompletion=(_step=_iterator.next()).done))return[3,6];_step_value=_sliced_to_array(_step.value,2),index=_step_value[0],pcode=_step_value[1];if(!pcode.formatted)return[3,5];dateTag=_luxon.DateTime.local().plus({seconds:index}).toFormat("yyyyMMddHHmmss");filePath="".concat(migrationsDir,"/").concat(dateTag,"_").concat(pcode.title,".ts");return[4,(0,_promises.writeFile)(filePath,pcode.formatted)];case 4:_state.sent();console.log(_chalk.default.green("MIGRTAION CREATED ".concat(filePath)));_state.label=5;case 5:_iteratorNormalCompletion=true;return[3,3];case 6:return[3,9];case 7:err=_state.sent();_didIteratorError=true;_iteratorError=err;return[3,9];case 8:try{if(!_iteratorNormalCompletion&&_iterator.return!=null){_iterator.return()}}finally{if(_didIteratorError){throw _iteratorError}}return[7];case 9:return[2,preparedCodes.length]}})}).call(this)}},{key:"clearPendingList",value:function clearPendingList(){return _async_to_generator(function(){var _ref,pendingList,migrationsDir,delList,_iteratorNormalCompletion,_didIteratorError,_iteratorError,_iterator,_step,p,err;return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,this.targets.pending.migrate.list()];case 1:_ref=_sliced_to_array.apply(void 0,[_state.sent(),2]),pendingList=_ref[1];migrationsDir="".concat(_api.Sonamu.apiRootPath,"/src/migrations");delList=pendingList.map(function(df){return _path.default.join(migrationsDir,df.file).replace(".js",".ts")});_iteratorNormalCompletion=true,_didIteratorError=false,_iteratorError=undefined;_state.label=2;case 2:_state.trys.push([2,8,9,10]);_iterator=delList[Symbol.iterator]();_state.label=3;case 3:if(!!(_iteratorNormalCompletion=(_step=_iterator.next()).done))return[3,7];p=_step.value;return[4,(0,_fsutils.exists)(p)];case 4:if(!_state.sent())return[3,6];return[4,(0,_promises.unlink)(p)];case 5:_state.sent();_state.label=6;case 6:_iteratorNormalCompletion=true;return[3,3];case 7:return[3,10];case 8:err=_state.sent();_didIteratorError=true;_iteratorError=err;return[3,10];case 9:try{if(!_iteratorNormalCompletion&&_iterator.return!=null){_iterator.return()}}finally{if(_didIteratorError){throw _iteratorError}}return[7];case 10:return[4,this.cleanUpDist(true)];case 11:_state.sent();return[2]}})}).call(this)}},{key:"check",value:function check(){return _async_to_generator(function(){var codes;return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,this.compareMigrations(this.targets.compare)];case 1:codes=_state.sent();if(codes.length===0){console.log(_chalk.default.green("\n현재 모두 싱크된 상태입니다."));return[2]}console.table(codes,["type","title"]);console.log(codes[0]);return[2]}})}).call(this)}},{key:"run",value:function run(){return _async_to_generator(function(){var _ref,pendingList,answer,codes,answer1,migrationsDir,_iteratorNormalCompletion,_didIteratorError,_iteratorError,_iterator,_step,_step_value,index,code,dateTag,filePath,err;return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,this.targets.pending.migrate.list()];case 1:_ref=_sliced_to_array.apply(void 0,[_state.sent(),2]),pendingList=_ref[1];if(!(pendingList.length>0))return[3,5];console.log(_chalk.default.red("pending 된 마이그레이션이 존재합니다."),pendingList.map(function(pending){return pending.file}));return[4,(0,_prompts.default)({type:"confirm",name:"value",message:"Shadow DB 테스트를 진행하시겠습니까?",initial:true})];case 2:answer=_state.sent();if(answer.value===false){return[2]}console.time(_chalk.default.blue("Migrator - runShadowTest"));return[4,this.runShadowTest()];case 3:_state.sent();console.timeEnd(_chalk.default.blue("Migrator - runShadowTest"));return[4,Promise.all(this.targets.apply.map(function(applyDb){return _async_to_generator(function(){var label,_ref;return _ts_generator(this,function(_state){switch(_state.label){case 0:label=_chalk.default.green("APPLIED ".concat(applyDb.client.connectionSettings.host," ").concat(applyDb.client.database()));console.time(label);return[4,applyDb.migrate.latest()];case 1:_ref=_sliced_to_array.apply(void 0,[_state.sent(),1]);console.timeEnd(label);return[2]}})})()}))];case 4:_state.sent();_state.label=5;case 5:return[4,this.compareMigrations(this.targets.compare)];case 6:codes=_state.sent();if(codes.length===0){console.log(_chalk.default.green("\n현재 모두 싱크된 상태입니다."));return[2]}console.table(codes,["type","title"]);return[4,(0,_prompts.default)({type:"confirm",name:"value",message:"마이그레이션 코드를 생성하시겠습니까?",initial:false})];case 7:answer1=_state.sent();if(answer1.value===false){return[2]}migrationsDir="".concat(_api.Sonamu.apiRootPath,"/src/migrations");_iteratorNormalCompletion=true,_didIteratorError=false,_iteratorError=undefined;_state.label=8;case 8:_state.trys.push([8,13,14,15]);_iterator=codes.entries()[Symbol.iterator]();_state.label=9;case 9:if(!!(_iteratorNormalCompletion=(_step=_iterator.next()).done))return[3,12];_step_value=_sliced_to_array(_step.value,2),index=_step_value[0],code=_step_value[1];if(!code.formatted)return[3,11];dateTag=_luxon.DateTime.local().plus({seconds:index}).toFormat("yyyyMMddHHmmss");filePath="".concat(migrationsDir,"/").concat(dateTag,"_").concat(code.title,".ts");return[4,(0,_promises.writeFile)(filePath,code.formatted)];case 10:_state.sent();console.log(_chalk.default.green("MIGRTAION CREATED ".concat(filePath)));_state.label=11;case 11:_iteratorNormalCompletion=true;return[3,9];case 12:return[3,15];case 13:err=_state.sent();_didIteratorError=true;_iteratorError=err;return[3,15];case 14:try{if(!_iteratorNormalCompletion&&_iterator.return!=null){_iterator.return()}}finally{if(_didIteratorError){throw _iteratorError}}return[7];case 15:return[2]}})}).call(this)}},{key:"rollback",value:function rollback(){return _async_to_generator(function(){var rollbackAllResult;return _ts_generator(this,function(_state){switch(_state.label){case 0:console.time(_chalk.default.red("rollback:"));return[4,Promise.all(this.targets.apply.map(function(db){return _async_to_generator(function(){return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,db.migrate.forceFreeMigrationsLock()];case 1:_state.sent();return[2,db.migrate.rollback(undefined,false)]}})})()}))];case 1:rollbackAllResult=_state.sent();console.dir({rollbackAllResult:rollbackAllResult},{depth:null});console.timeEnd(_chalk.default.red("rollback:"));return[2]}})}).call(this)}},{key:"cleanUpDist",value:function cleanUpDist(){var force=arguments.length>0&&arguments[0]!==void 0?arguments[0]:false;return _async_to_generator(function(){var files,_tmp,diffOnSrc,diffOnDist,answer,filesToRm,_iteratorNormalCompletion,_didIteratorError,_iteratorError,_iterator,_step,filePath,err;function getFilesUnder(dir){return _async_to_generator(function(){var migrationPath;return _ts_generator(this,function(_state){switch(_state.label){case 0:migrationPath=_path.default.join(_api.Sonamu.apiRootPath,dir,"migrations");return[4,(0,_fsutils.exists)(migrationPath)];case 1:if(!!_state.sent())return[3,3];return[4,(0,_promises.mkdir)(migrationPath,{recursive:true})];case 2:_state.sent();_state.label=3;case 3:return[4,(0,_promises.readdir)(migrationPath)];case 4:return[2,_state.sent().filter(function(filename){return filename.startsWith(".")===false})]}})})()}return _ts_generator(this,function(_state){switch(_state.label){case 0:_tmp={};return[4,getFilesUnder("src")];case 1:_tmp.src=_state.sent();return[4,getFilesUnder("dist")];case 2:files=(_tmp.dist=_state.sent(),_tmp);diffOnSrc=_lodash.default.differenceBy(files.src,files.dist,function(filename){return filename.split(".")[0]});if(diffOnSrc.length>0){throw new Error("컴파일 되지 않은 파일이 있습니다.\n"+diffOnSrc.join("\n"))}diffOnDist=_lodash.default.differenceBy(files.dist,files.src,function(filename){return filename.split(".")[0]});if(!(diffOnDist.length>0))return[3,13];console.log(_chalk.default.red("원본 ts파일을 찾을 수 없는 js파일이 있습니다."));console.log(diffOnDist);if(!!force)return[3,4];return[4,(0,_prompts.default)({type:"confirm",name:"value",message:"삭제를 진행하시겠습니까?",initial:true})];case 3:answer=_state.sent();if(answer.value===false){return[2]}_state.label=4;case 4:filesToRm=diffOnDist.map(function(filename){return _path.default.join(_api.Sonamu.apiRootPath,"dist","migrations",filename)});_iteratorNormalCompletion=true,_didIteratorError=false,_iteratorError=undefined;_state.label=5;case 5:_state.trys.push([5,10,11,12]);_iterator=filesToRm[Symbol.iterator]();_state.label=6;case 6:if(!!(_iteratorNormalCompletion=(_step=_iterator.next()).done))return[3,9];filePath=_step.value;return[4,(0,_promises.unlink)(filePath)];case 7:_state.sent();_state.label=8;case 8:_iteratorNormalCompletion=true;return[3,6];case 9:return[3,12];case 10:err=_state.sent();_didIteratorError=true;_iteratorError=err;return[3,12];case 11:try{if(!_iteratorNormalCompletion&&_iterator.return!=null){_iterator.return()}}finally{if(_didIteratorError){throw _iteratorError}}return[7];case 12:console.log(_chalk.default.green("".concat(filesToRm.length,"건 삭제되었습니다!")));_state.label=13;case 13:return[2]}})})()}},{key:"runShadowTest",value:function runShadowTest(){return _async_to_generator(function(){var tdb,tdbConn,shadowDatabase,tmpSqlPath,_tdbConn_port,_tdbConn_port1,sdb,_ref,batchNo,applied,e;return _ts_generator(this,function(_state){switch(_state.label){case 0:tdb=(0,_knex.default)(_api.Sonamu.dbConfig.test);tdbConn=_api.Sonamu.dbConfig.test.connection;shadowDatabase=tdbConn.database+"__migration_shadow";tmpSqlPath="/tmp/".concat(shadowDatabase,".sql");console.log(_chalk.default.magenta("".concat(tdbConn.database,"의 데이터 ").concat(tmpSqlPath,"로 덤프")));(0,_child_process.execSync)("mysqldump -h".concat(tdbConn.host," -P").concat((_tdbConn_port=tdbConn.port)!==null&&_tdbConn_port!==void 0?_tdbConn_port:3306," -u").concat(tdbConn.user," -p'").concat(tdbConn.password,"' ").concat(tdbConn.database," --single-transaction --no-create-db --triggers > ").concat(tmpSqlPath,";"));(0,_child_process.execSync)("sed -i'' -e 's/`".concat(tdbConn.database,"`/`").concat(shadowDatabase,"`/g' ").concat(tmpSqlPath,";"));console.log(_chalk.default.magenta("".concat(shadowDatabase," 리셋")));return[4,tdb.raw("DROP DATABASE IF EXISTS `".concat(shadowDatabase,"`;"))];case 1:_state.sent();return[4,tdb.raw("CREATE DATABASE `".concat(shadowDatabase,"`;"))];case 2:_state.sent();console.log(_chalk.default.magenta("".concat(shadowDatabase," 데이터베이스 생성")));(0,_child_process.execSync)("mysql -h".concat(tdbConn.host," -P").concat((_tdbConn_port1=tdbConn.port)!==null&&_tdbConn_port1!==void 0?_tdbConn_port1:3306," -u").concat(tdbConn.user," -p'").concat(tdbConn.password,"' ").concat(shadowDatabase," < ").concat(tmpSqlPath,";"));sdb=(0,_knex.default)(_object_spread_props(_object_spread({},_api.Sonamu.dbConfig.test),{connection:_object_spread_props(_object_spread({},tdbConn),{database:shadowDatabase,password:tdbConn.password})}));_state.label=3;case 3:_state.trys.push([3,6,7,9]);return[4,sdb.migrate.latest()];case 4:_ref=_sliced_to_array.apply(void 0,[_state.sent(),2]),batchNo=_ref[0],applied=_ref[1];console.log(_chalk.default.green("Shadow DB 테스트에 성공했습니다!"),{batchNo:batchNo,applied:applied});console.log(_chalk.default.magenta("".concat(shadowDatabase," 삭제")));return[4,tdb.raw("DROP DATABASE IF EXISTS `".concat(shadowDatabase,"`;"))];case 5:_state.sent();return[2,[{connKey:"shadow",batchNo:batchNo,applied:applied}]];case 6:e=_state.sent();console.error(e);throw new _soexceptions.ServiceUnavailableException("Shadow DB 테스트 진행 중 에러");case 7:return[4,tdb.destroy()];case 8:_state.sent();return[7];case 9:return[2]}})})()}},{key:"resetAll",value:function resetAll(){return _async_to_generator(function(){var answer,rollbackAllResult,migrationsDir;return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,(0,_prompts.default)({type:"confirm",name:"value",message:"모든 DB를 롤백하고 전체 마이그레이션 파일을 삭제하시겠습니까?",initial:false})];case 1:answer=_state.sent();if(answer.value===false){return[2]}console.time(_chalk.default.red("rollback-all:"));return[4,Promise.all(this.targets.apply.map(function(db){return _async_to_generator(function(){return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,db.migrate.forceFreeMigrationsLock()];case 1:_state.sent();return[2,db.migrate.rollback(undefined,true)]}})})()}))];case 2:rollbackAllResult=_state.sent();console.log({rollbackAllResult:rollbackAllResult});console.timeEnd(_chalk.default.red("rollback-all:"));migrationsDir="".concat(_api.Sonamu.apiRootPath,"/src/migrations");console.time(_chalk.default.red("delete migration files"));(0,_child_process.execSync)("rm -f ".concat(migrationsDir,"/*"));(0,_child_process.execSync)("rm -f ".concat(migrationsDir.replace("/src/","/dist/"),"/*"));console.timeEnd(_chalk.default.red("delete migration files"));return[2]}})}).call(this)}},{key:"compareMigrations",value:function compareMigrations(compareDB){return _async_to_generator(function(){var entityIds,entitySetsWithJoinTable,joinTablesWithDup,joinTables,entitySets,codes;return _ts_generator(this,function(_state){switch(_state.label){case 0:entityIds=_entitymanager.EntityManager.getAllIds();entitySetsWithJoinTable=entityIds.filter(function(entityId){return _entitymanager.EntityManager.get(entityId).props.length>0}).map(function(entityId){return(0,_migrationset.getMigrationSetFromEntity)(_entitymanager.EntityManager.get(entityId))});joinTablesWithDup=entitySetsWithJoinTable.map(function(entitySet){return entitySet.joinTables}).flat();joinTables=Object.values(_lodash.default.groupBy(joinTablesWithDup,function(jt){return jt.table})).map(function(tables){if(tables.length===1){return tables[0]}return _object_spread_props(_object_spread({},tables[0]),{indexes:_lodash.default.uniqBy(tables.flatMap(function(t){return t.indexes}),function(index){return[index.type].concat(_to_consumable_array(index.columns.sort())).join("-")})})});entitySets=_to_consumable_array(entitySetsWithJoinTable).concat(_to_consumable_array(joinTables));return[4,Promise.all(entitySets.map(function(entitySet){return _async_to_generator(function(){var dbSet;return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,(0,_migrationset.getMigrationSetFromDB)(compareDB,entitySet.table)];case 1:dbSet=_state.sent();if(!(dbSet===null))return[3,3];return[4,(0,_codegeneration.generateCreateCode)(entitySet)];case 2:return[2,_state.sent()];case 3:return[4,(0,_codegeneration.generateAlterCode)(entitySet,dbSet)];case 4:return[2,_state.sent()];case 5:return[2]}})})()}))];case 1:codes=_state.sent().flat();codes.sort(function(codeA,codeB){if(codeA.type==="foreign"&&codeB.type=="normal"){return 1}else if(codeA.type==="normal"&&codeB.type==="foreign"){return-1}else{return 0}});return[2,codes]}})})()}},{key:"destroy",value:function destroy(){return _async_to_generator(function(){return _ts_generator(this,function(_state){switch(_state.label){case 0:return[4,Promise.all(this.targets.apply.map(function(db){return db.destroy()}))];case 1:_state.sent();return[2]}})}).call(this)}}]);return Migrator}();
|
|
2
|
-
|
|
1
|
+
import * as _ from "lodash-es";
|
|
2
|
+
import knex from "knex";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { DateTime } from "luxon";
|
|
5
|
+
import { mkdir, readdir, unlink, writeFile } from "node:fs/promises";
|
|
6
|
+
import { exists } from "../utils/fs-utils.js";
|
|
7
|
+
import prompts from "prompts";
|
|
8
|
+
import { execSync } from "child_process";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import { EntityManager } from "../entity/entity-manager.js";
|
|
11
|
+
import { Sonamu } from "../api/index.js";
|
|
12
|
+
import { ServiceUnavailableException } from "../exceptions/so-exceptions.js";
|
|
13
|
+
import { generateCreateCode, generateAlterCode } from "./code-generation.js";
|
|
14
|
+
import { getMigrationSetFromDB } from "./migration-set.js";
|
|
15
|
+
import { getMigrationSetFromEntity } from "./migration-set.js";
|
|
16
|
+
export class Migrator {
|
|
17
|
+
options;
|
|
18
|
+
targets;
|
|
19
|
+
constructor(options){
|
|
20
|
+
this.options = options;
|
|
21
|
+
const { dbConfig } = Sonamu;
|
|
22
|
+
if (this.options.mode === "dev") {
|
|
23
|
+
const devDB = knex(dbConfig.development_master);
|
|
24
|
+
const testDB = knex(dbConfig.test);
|
|
25
|
+
const fixtureLocalDB = knex(dbConfig.fixture_local);
|
|
26
|
+
const applyDBs = [
|
|
27
|
+
devDB,
|
|
28
|
+
testDB,
|
|
29
|
+
fixtureLocalDB
|
|
30
|
+
];
|
|
31
|
+
if (dbConfig.fixture_local.connection.host !== dbConfig.fixture_remote.connection.host || dbConfig.fixture_local.connection.database !== dbConfig.fixture_remote.connection.database) {
|
|
32
|
+
const fixtureRemoteDB = knex(dbConfig.fixture_remote);
|
|
33
|
+
applyDBs.push(fixtureRemoteDB);
|
|
34
|
+
}
|
|
35
|
+
this.targets = {
|
|
36
|
+
compare: devDB,
|
|
37
|
+
pending: devDB,
|
|
38
|
+
shadow: testDB,
|
|
39
|
+
apply: applyDBs
|
|
40
|
+
};
|
|
41
|
+
} else if (this.options.mode === "deploy") {
|
|
42
|
+
const productionDB = knex(dbConfig.production_master);
|
|
43
|
+
const testDB = knex(dbConfig.test);
|
|
44
|
+
this.targets = {
|
|
45
|
+
pending: productionDB,
|
|
46
|
+
shadow: testDB,
|
|
47
|
+
apply: [
|
|
48
|
+
productionDB
|
|
49
|
+
]
|
|
50
|
+
};
|
|
51
|
+
} else {
|
|
52
|
+
throw new Error(`잘못된 모드 ${this.options.mode} 입력`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async getMigrationCodes() {
|
|
56
|
+
const srcMigrationsDir = path.join(Sonamu.apiRootPath, "src", "migrations"); // 이건 환경에 관계없이 항상 src에서 찾아야 해요.
|
|
57
|
+
if (!await exists(srcMigrationsDir)) {
|
|
58
|
+
await mkdir(srcMigrationsDir, {
|
|
59
|
+
recursive: true
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
const codes = (await readdir(srcMigrationsDir)).filter((f)=>f.endsWith(".ts")).map((f)=>({
|
|
63
|
+
name: f.replace(".ts", ""),
|
|
64
|
+
path: path.join(srcMigrationsDir, f)
|
|
65
|
+
})).sort((a, b)=>a.name < b.name ? 1 : -1); // 이름 내림차순 정렬(최신순)
|
|
66
|
+
return codes;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 타겟별 마이그레이션 상태와 코드 생성/준비 상태를 구해옵니다.
|
|
70
|
+
* 실제로 DB에 접근도 하고 마이그레이션 코드 파일도 확인하고,
|
|
71
|
+
* 필요하다면 적용할 수 있는 코드를 생성까지 해옵니다.
|
|
72
|
+
*
|
|
73
|
+
* CLI와 Sonamu UI에서 사용됩니다.
|
|
74
|
+
*
|
|
75
|
+
* @returns
|
|
76
|
+
*/ async getStatus() {
|
|
77
|
+
const codes = await this.getMigrationCodes();
|
|
78
|
+
const connKeys = Object.keys(Sonamu.dbConfig).filter((key)=>key.endsWith("_slave") === false);
|
|
79
|
+
const statuses = await Promise.all(connKeys.map(async (connKey)=>{
|
|
80
|
+
const knexOptions = Sonamu.dbConfig[connKey];
|
|
81
|
+
const tConn = knex(knexOptions);
|
|
82
|
+
const status = await (async ()=>{
|
|
83
|
+
try {
|
|
84
|
+
return await tConn.migrate.status();
|
|
85
|
+
} catch (err) {
|
|
86
|
+
console.warn(chalk.yellow(`${connKey}의 마이그레이션 상태를 가져오는 데에 실패하였습니다. 데이터베이스가 올바르게 구성되지 않은 것 같습니다. 확인하시고 다시 시도해주세요.\n시도한 연결 설정:\n${JSON.stringify(knexOptions.connection, null, 2)}\n발생한 에러:\n${err}\n`));
|
|
87
|
+
return "error" /*클라이언트에서 에러 체크에 사용하는 리터럴입니다.*/ ;
|
|
88
|
+
}
|
|
89
|
+
})();
|
|
90
|
+
const pending = await (async ()=>{
|
|
91
|
+
try {
|
|
92
|
+
const [, fdList] = await tConn.migrate.list();
|
|
93
|
+
return fdList.map((fd)=>fd.file.replace(".ts", ""));
|
|
94
|
+
} catch (err) {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
})();
|
|
98
|
+
const currentVersion = await (async ()=>{
|
|
99
|
+
try {
|
|
100
|
+
return await tConn.migrate.currentVersion();
|
|
101
|
+
} catch (err) {
|
|
102
|
+
return "error";
|
|
103
|
+
}
|
|
104
|
+
})();
|
|
105
|
+
const connection = knexOptions.connection;
|
|
106
|
+
await tConn.destroy();
|
|
107
|
+
return {
|
|
108
|
+
name: connKey.replace("_master", ""),
|
|
109
|
+
connKey,
|
|
110
|
+
connString: `mysql2://${connection.user ?? ""}@${connection.host}:${connection.port}/${connection.database}`,
|
|
111
|
+
currentVersion,
|
|
112
|
+
status,
|
|
113
|
+
pending
|
|
114
|
+
};
|
|
115
|
+
}));
|
|
116
|
+
const preparedCodes = await (async ()=>{
|
|
117
|
+
const status0conn = statuses.find((status)=>status.status === 0);
|
|
118
|
+
if (status0conn === undefined) {
|
|
119
|
+
console.warn(chalk.yellow(`While trying to prepare migration codes, we found that there is no database to compare migrations. We need at least one database where every migration is applied(status === 0). You might want to apply your existing migrations to one of the databases.`));
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
const compareDBconn = knex(Sonamu.dbConfig[status0conn.connKey]);
|
|
123
|
+
const genCodes = await this.compareMigrations(compareDBconn);
|
|
124
|
+
await compareDBconn.destroy();
|
|
125
|
+
return genCodes;
|
|
126
|
+
})();
|
|
127
|
+
return {
|
|
128
|
+
conns: statuses,
|
|
129
|
+
codes,
|
|
130
|
+
preparedCodes
|
|
131
|
+
};
|
|
132
|
+
/*
|
|
133
|
+
DB 마이그레이션 상태 확인
|
|
134
|
+
1. 전체 DB설정에 대해서 현재 마이그레이션 상태 확인
|
|
135
|
+
- connKey: string
|
|
136
|
+
- status: number
|
|
137
|
+
- currentVersion: string
|
|
138
|
+
- list: { file: string; directory: string }[]
|
|
139
|
+
|
|
140
|
+
*/ }
|
|
141
|
+
/**
|
|
142
|
+
* 마이그레이션을 적용하거나 롤백합니다.
|
|
143
|
+
* Sonamu UI에서 마이그레이션 작업을 수행할 때 사용됩니다.
|
|
144
|
+
*
|
|
145
|
+
* CLI와 Sonamu UI에서 사용됩니다.
|
|
146
|
+
*
|
|
147
|
+
* @param action 작업 유형 (apply/rollback)
|
|
148
|
+
* @param targets 작업 대상 DB 설정 키 (keyof SonamuDBConfig)
|
|
149
|
+
* @returns 작업 결과
|
|
150
|
+
*/ async runAction(action, targets) {
|
|
151
|
+
// get uniq knex configs
|
|
152
|
+
const configs = _.uniqBy(targets.map((target)=>({
|
|
153
|
+
connKey: target,
|
|
154
|
+
options: Sonamu.dbConfig[target]
|
|
155
|
+
})).filter((c)=>c.options !== undefined), ({ options })=>`${options.connection.host}:${options.connection.port ?? 3306}/${options.connection.database}`);
|
|
156
|
+
// get connections
|
|
157
|
+
const conns = await Promise.all(configs.map(async (config)=>({
|
|
158
|
+
connKey: config.connKey,
|
|
159
|
+
knex: knex(config.options)
|
|
160
|
+
})));
|
|
161
|
+
// action
|
|
162
|
+
const result = await (async ()=>{
|
|
163
|
+
switch(action){
|
|
164
|
+
case "apply":
|
|
165
|
+
return Promise.all(conns.map(async ({ connKey, knex })=>{
|
|
166
|
+
const [batchNo, applied] = await knex.migrate.latest();
|
|
167
|
+
return {
|
|
168
|
+
connKey,
|
|
169
|
+
batchNo,
|
|
170
|
+
applied
|
|
171
|
+
};
|
|
172
|
+
}));
|
|
173
|
+
case "rollback":
|
|
174
|
+
return Promise.all(conns.map(async ({ connKey, knex })=>{
|
|
175
|
+
const [batchNo, applied] = await knex.migrate.rollback();
|
|
176
|
+
return {
|
|
177
|
+
connKey,
|
|
178
|
+
batchNo,
|
|
179
|
+
applied
|
|
180
|
+
};
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
})();
|
|
184
|
+
// destroy
|
|
185
|
+
await Promise.all(conns.map(({ knex })=>{
|
|
186
|
+
return knex.destroy();
|
|
187
|
+
}));
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* 마이그레이션 코드 파일을 삭제합니다.
|
|
192
|
+
*
|
|
193
|
+
* Sonamu UI에서 사용됩니다.
|
|
194
|
+
*
|
|
195
|
+
* @param codeNames 삭제할 마이그레이션 코드 파일 이름 배열
|
|
196
|
+
* @returns 삭제된 마이그레이션 코드 파일 개수
|
|
197
|
+
*/ async delCodes(codeNames) {
|
|
198
|
+
const { conns } = await this.getStatus();
|
|
199
|
+
if (conns.some((conn)=>{
|
|
200
|
+
return codeNames.some((codeName)=>conn.pending.includes(codeName) === false);
|
|
201
|
+
})) {
|
|
202
|
+
throw new Error("You cannot delete a migration file if there is already applied.");
|
|
203
|
+
}
|
|
204
|
+
const delFiles = codeNames.map((codeName)=>`${Sonamu.apiRootPath}/src/migrations/${codeName}.ts`);
|
|
205
|
+
const res = await Promise.all(delFiles.map(async (delFile)=>{
|
|
206
|
+
if (await exists(delFile)) {
|
|
207
|
+
console.log(chalk.red(`DELETE: ${delFile}`));
|
|
208
|
+
await unlink(delFile);
|
|
209
|
+
return delFiles.includes(".ts") ? 1 : 0;
|
|
210
|
+
}
|
|
211
|
+
return 0;
|
|
212
|
+
}));
|
|
213
|
+
return _.sum(res);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* 마이그레이션 코드 파일을 생성합니다.
|
|
217
|
+
*
|
|
218
|
+
* Sonamu UI에서 사용됩니다.
|
|
219
|
+
*
|
|
220
|
+
* @returns 생성된 마이그레이션 코드 파일 개수
|
|
221
|
+
*/ async generatePreparedCodes() {
|
|
222
|
+
const { preparedCodes } = await this.getStatus();
|
|
223
|
+
if (preparedCodes.length === 0) {
|
|
224
|
+
console.log(chalk.green("\n현재 모두 싱크된 상태입니다."));
|
|
225
|
+
return 0;
|
|
226
|
+
}
|
|
227
|
+
// 실제 코드 생성
|
|
228
|
+
const migrationsDir = `${Sonamu.apiRootPath}/src/migrations`;
|
|
229
|
+
for (const [index, pcode] of preparedCodes.entries()){
|
|
230
|
+
if (pcode.formatted) {
|
|
231
|
+
const dateTag = DateTime.local().plus({
|
|
232
|
+
seconds: index
|
|
233
|
+
}).toFormat("yyyyMMddHHmmss");
|
|
234
|
+
const filePath = `${migrationsDir}/${dateTag}_${pcode.title}.ts`;
|
|
235
|
+
await writeFile(filePath, pcode.formatted);
|
|
236
|
+
console.log(chalk.green(`MIGRTAION CREATED ${filePath}`));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return preparedCodes.length;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* pending 마이그레이션 목록을 삭제합니다.
|
|
243
|
+
*
|
|
244
|
+
* CLI에서 사용됩니다.
|
|
245
|
+
*/ async clearPendingList() {
|
|
246
|
+
const [, pendingList] = await this.targets.pending.migrate.list();
|
|
247
|
+
const migrationsDir = `${Sonamu.apiRootPath}/src/migrations`;
|
|
248
|
+
const delList = pendingList.map((df)=>{
|
|
249
|
+
return path.join(migrationsDir, df.file);
|
|
250
|
+
});
|
|
251
|
+
for (let p of delList){
|
|
252
|
+
if (await exists(p)) {
|
|
253
|
+
await unlink(p);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* 마이그레이션 코드 파일을 확인합니다.
|
|
259
|
+
*
|
|
260
|
+
* CLI에서 사용됩니다.
|
|
261
|
+
*/ async check() {
|
|
262
|
+
const codes = await this.compareMigrations(this.targets.compare);
|
|
263
|
+
if (codes.length === 0) {
|
|
264
|
+
console.log(chalk.green("\n현재 모두 싱크된 상태입니다."));
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
// 현재 생성된 코드 표기
|
|
268
|
+
console.table(codes, [
|
|
269
|
+
"type",
|
|
270
|
+
"title"
|
|
271
|
+
]);
|
|
272
|
+
console.log(codes[0]);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* 마이그레이션을 수행합니다.
|
|
276
|
+
*
|
|
277
|
+
* runAction이 인자로 들어온 타겟들에 대해 주어진 동작(apply/rollback)을 수행한다면,
|
|
278
|
+
* 이 함수는 생성자로 들어온 connection(knex)들에 대해 마이그레이션을 수행합니다.
|
|
279
|
+
*
|
|
280
|
+
* CLI에서 사용됩니다.
|
|
281
|
+
*/ async run() {
|
|
282
|
+
// pending 마이그레이션 확인
|
|
283
|
+
const [, pendingList] = await this.targets.pending.migrate.list();
|
|
284
|
+
if (pendingList.length > 0) {
|
|
285
|
+
console.log(chalk.red("pending 된 마이그레이션이 존재합니다."), pendingList.map((pending)=>pending.file));
|
|
286
|
+
// pending이 있는 경우 Shadow DB 테스트 진행 여부 컨펌
|
|
287
|
+
const answer = await prompts({
|
|
288
|
+
type: "confirm",
|
|
289
|
+
name: "value",
|
|
290
|
+
message: "Shadow DB 테스트를 진행하시겠습니까?",
|
|
291
|
+
initial: true
|
|
292
|
+
});
|
|
293
|
+
if (answer.value === false) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
console.time(chalk.blue("Migrator - runShadowTest"));
|
|
297
|
+
await this.runShadowTest();
|
|
298
|
+
console.timeEnd(chalk.blue("Migrator - runShadowTest"));
|
|
299
|
+
await Promise.all(this.targets.apply.map(async (applyDb)=>{
|
|
300
|
+
const label = chalk.green(`APPLIED ${applyDb.client.connectionSettings.host} ${applyDb.client.database()}`);
|
|
301
|
+
console.time(label);
|
|
302
|
+
const [, ] = await applyDb.migrate.latest();
|
|
303
|
+
console.timeEnd(label);
|
|
304
|
+
}));
|
|
305
|
+
}
|
|
306
|
+
// Entity-DB간 비교하여 코드 생성 리턴
|
|
307
|
+
const codes = await this.compareMigrations(this.targets.compare);
|
|
308
|
+
if (codes.length === 0) {
|
|
309
|
+
console.log(chalk.green("\n현재 모두 싱크된 상태입니다."));
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
// 현재 생성된 코드 표기
|
|
313
|
+
console.table(codes, [
|
|
314
|
+
"type",
|
|
315
|
+
"title"
|
|
316
|
+
]);
|
|
317
|
+
/* DEBUG: 디버깅용 코드
|
|
318
|
+
codes.map((code) => console.log(code.formatted));
|
|
319
|
+
process.exit();
|
|
320
|
+
*/ // 실제 파일 생성 프롬프트
|
|
321
|
+
const answer = await prompts({
|
|
322
|
+
type: "confirm",
|
|
323
|
+
name: "value",
|
|
324
|
+
message: "마이그레이션 코드를 생성하시겠습니까?",
|
|
325
|
+
initial: false
|
|
326
|
+
});
|
|
327
|
+
if (answer.value === false) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
// 실제 코드 생성
|
|
331
|
+
const migrationsDir = `${Sonamu.apiRootPath}/src/migrations`;
|
|
332
|
+
for (const [index, code] of codes.entries()){
|
|
333
|
+
if (code.formatted) {
|
|
334
|
+
const dateTag = DateTime.local().plus({
|
|
335
|
+
seconds: index
|
|
336
|
+
}).toFormat("yyyyMMddHHmmss");
|
|
337
|
+
const filePath = `${migrationsDir}/${dateTag}_${code.title}.ts`;
|
|
338
|
+
await writeFile(filePath, code.formatted);
|
|
339
|
+
console.log(chalk.green(`MIGRTAION CREATED ${filePath}`));
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* 타겟으로 지정된 DB를 롤백합니다.
|
|
345
|
+
*
|
|
346
|
+
* runAction이 인자로 들어온 타겟들에 대해 주어진 동작(apply/rollback)을 수행한다면,
|
|
347
|
+
* 이 함수는 생성자로 들어온 connection(knex)들에 대해 롤백을 수행합니다.
|
|
348
|
+
*
|
|
349
|
+
* CLI에서 사용됩니다.
|
|
350
|
+
*/ async rollback() {
|
|
351
|
+
console.time(chalk.red("rollback:"));
|
|
352
|
+
const rollbackAllResult = await Promise.all(this.targets.apply.map(async (db)=>{
|
|
353
|
+
await db.migrate.forceFreeMigrationsLock();
|
|
354
|
+
return db.migrate.rollback(undefined, false);
|
|
355
|
+
}));
|
|
356
|
+
console.dir({
|
|
357
|
+
rollbackAllResult
|
|
358
|
+
}, {
|
|
359
|
+
depth: null
|
|
360
|
+
});
|
|
361
|
+
console.timeEnd(chalk.red("rollback:"));
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Shadow DB 테스트를 진행합니다.
|
|
365
|
+
*
|
|
366
|
+
* Sonamu UI에서 사용됩니다.
|
|
367
|
+
*
|
|
368
|
+
* @returns Shadow DB 테스트 결과
|
|
369
|
+
*/ async runShadowTest() {
|
|
370
|
+
// ShadowDB 생성 후 테스트 진행
|
|
371
|
+
const tdb = knex(Sonamu.dbConfig.test);
|
|
372
|
+
const tdbConn = Sonamu.dbConfig.test.connection;
|
|
373
|
+
const shadowDatabase = tdbConn.database + "__migration_shadow";
|
|
374
|
+
const tmpSqlPath = `/tmp/${shadowDatabase}.sql`;
|
|
375
|
+
// 테스트DB 덤프 후 Database명 치환
|
|
376
|
+
console.log(chalk.magenta(`${tdbConn.database}의 데이터 ${tmpSqlPath}로 덤프`));
|
|
377
|
+
execSync(`mysqldump -h${tdbConn.host} -P${tdbConn.port ?? 3306} -u${tdbConn.user} -p'${tdbConn.password}' ${tdbConn.database} --single-transaction --no-create-db --triggers > ${tmpSqlPath};`);
|
|
378
|
+
execSync(`sed -i'' -e 's/\`${tdbConn.database}\`/\`${shadowDatabase}\`/g' ${tmpSqlPath};`);
|
|
379
|
+
// 기존 ShadowDB 리셋
|
|
380
|
+
console.log(chalk.magenta(`${shadowDatabase} 리셋`));
|
|
381
|
+
await tdb.raw(`DROP DATABASE IF EXISTS \`${shadowDatabase}\`;`);
|
|
382
|
+
await tdb.raw(`CREATE DATABASE \`${shadowDatabase}\`;`);
|
|
383
|
+
// ShadowDB 테이블 + 데이터 생성
|
|
384
|
+
console.log(chalk.magenta(`${shadowDatabase} 데이터베이스 생성`));
|
|
385
|
+
execSync(`mysql -h${tdbConn.host} -P${tdbConn.port ?? 3306} -u${tdbConn.user} -p'${tdbConn.password}' ${shadowDatabase} < ${tmpSqlPath};`);
|
|
386
|
+
// shadow db 테스트 진행
|
|
387
|
+
const sdb = knex({
|
|
388
|
+
...Sonamu.dbConfig.test,
|
|
389
|
+
connection: {
|
|
390
|
+
...tdbConn,
|
|
391
|
+
database: shadowDatabase,
|
|
392
|
+
password: tdbConn.password
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
// shadow db 테스트 진행
|
|
396
|
+
try {
|
|
397
|
+
const [batchNo, applied] = await sdb.migrate.latest();
|
|
398
|
+
console.log(chalk.green("Shadow DB 테스트에 성공했습니다!"), {
|
|
399
|
+
batchNo,
|
|
400
|
+
applied
|
|
401
|
+
});
|
|
402
|
+
// 생성한 Shadow DB 삭제
|
|
403
|
+
console.log(chalk.magenta(`${shadowDatabase} 삭제`));
|
|
404
|
+
await tdb.raw(`DROP DATABASE IF EXISTS \`${shadowDatabase}\`;`);
|
|
405
|
+
return [
|
|
406
|
+
{
|
|
407
|
+
connKey: "shadow",
|
|
408
|
+
batchNo,
|
|
409
|
+
applied
|
|
410
|
+
}
|
|
411
|
+
];
|
|
412
|
+
} catch (e) {
|
|
413
|
+
console.error(e);
|
|
414
|
+
throw new ServiceUnavailableException("Shadow DB 테스트 진행 중 에러");
|
|
415
|
+
} finally{
|
|
416
|
+
await tdb.destroy();
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* 모든 DB를 롤백하고 전체 마이그레이션 파일을 삭제합니다.
|
|
421
|
+
*
|
|
422
|
+
* CLI에서 사용됩니다.
|
|
423
|
+
*
|
|
424
|
+
* @returns
|
|
425
|
+
*/ async resetAll() {
|
|
426
|
+
const answer = await prompts({
|
|
427
|
+
type: "confirm",
|
|
428
|
+
name: "value",
|
|
429
|
+
message: "모든 DB를 롤백하고 전체 마이그레이션 파일을 삭제하시겠습니까?",
|
|
430
|
+
initial: false
|
|
431
|
+
});
|
|
432
|
+
if (answer.value === false) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
console.time(chalk.red("rollback-all:"));
|
|
436
|
+
const rollbackAllResult = await Promise.all(this.targets.apply.map(async (db)=>{
|
|
437
|
+
await db.migrate.forceFreeMigrationsLock();
|
|
438
|
+
return db.migrate.rollback(undefined, true);
|
|
439
|
+
}));
|
|
440
|
+
console.log({
|
|
441
|
+
rollbackAllResult
|
|
442
|
+
});
|
|
443
|
+
console.timeEnd(chalk.red("rollback-all:"));
|
|
444
|
+
const migrationsDir = `${Sonamu.apiRootPath}/src/migrations`;
|
|
445
|
+
console.time(chalk.red("delete migration files"));
|
|
446
|
+
execSync(`rm -f ${migrationsDir}/*`);
|
|
447
|
+
execSync(`rm -f ${migrationsDir.replace("/src/", "/dist/")}/*`);
|
|
448
|
+
console.timeEnd(chalk.red("delete migration files"));
|
|
449
|
+
}
|
|
450
|
+
async compareMigrations(compareDB) {
|
|
451
|
+
// Entity 순회하여 싱크
|
|
452
|
+
const entityIds = EntityManager.getAllIds();
|
|
453
|
+
// 조인테이블 포함하여 Entity에서 MigrationSet 추출
|
|
454
|
+
const entitySetsWithJoinTable = entityIds.filter((entityId)=>EntityManager.get(entityId).props.length > 0).map((entityId)=>getMigrationSetFromEntity(EntityManager.get(entityId)));
|
|
455
|
+
// 조인테이블만 추출
|
|
456
|
+
const joinTablesWithDup = entitySetsWithJoinTable.map((entitySet)=>entitySet.joinTables).flat();
|
|
457
|
+
// 중복 제거 (중복인 경우 indexes를 병합)
|
|
458
|
+
const joinTables = Object.values(_.groupBy(joinTablesWithDup, (jt)=>jt.table)).map((tables)=>{
|
|
459
|
+
if (tables.length === 1) {
|
|
460
|
+
return tables[0];
|
|
461
|
+
}
|
|
462
|
+
return {
|
|
463
|
+
...tables[0],
|
|
464
|
+
indexes: _.uniqBy(tables.flatMap((t)=>t.indexes), (index)=>[
|
|
465
|
+
index.type,
|
|
466
|
+
...index.columns.sort()
|
|
467
|
+
].join("-"))
|
|
468
|
+
};
|
|
469
|
+
});
|
|
470
|
+
// 조인테이블 포함하여 MigrationSet 배열
|
|
471
|
+
const entitySets = [
|
|
472
|
+
...entitySetsWithJoinTable,
|
|
473
|
+
...joinTables
|
|
474
|
+
];
|
|
475
|
+
const codes = (await Promise.all(entitySets.map(async (entitySet)=>{
|
|
476
|
+
const dbSet = await getMigrationSetFromDB(compareDB, entitySet.table);
|
|
477
|
+
if (dbSet === null) {
|
|
478
|
+
// 기존 테이블 없음, 새로 테이블 생성
|
|
479
|
+
return await generateCreateCode(entitySet);
|
|
480
|
+
} else {
|
|
481
|
+
// 기존 테이블 존재하는 케이스
|
|
482
|
+
return await generateAlterCode(entitySet, dbSet);
|
|
483
|
+
}
|
|
484
|
+
}))).flat();
|
|
485
|
+
// normal 타입이 앞으로, foreign이 뒤로
|
|
486
|
+
codes.sort((codeA, codeB)=>{
|
|
487
|
+
if (codeA.type === "foreign" && codeB.type == "normal") {
|
|
488
|
+
return 1;
|
|
489
|
+
} else if (codeA.type === "normal" && codeB.type === "foreign") {
|
|
490
|
+
return -1;
|
|
491
|
+
} else {
|
|
492
|
+
return 0;
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
return codes;
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* 마이그레이션 대상 커넥션을 종료합니다.
|
|
499
|
+
*
|
|
500
|
+
* CLI에서 사용됩니다.
|
|
501
|
+
*
|
|
502
|
+
* @returns {Promise<void>} 종료 결과
|
|
503
|
+
*/ async destroy() {
|
|
504
|
+
await Promise.all(this.targets.apply.map((db)=>{
|
|
505
|
+
return db.destroy();
|
|
506
|
+
}));
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9taWdyYXRpb24vbWlncmF0b3IudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgXyBmcm9tIFwibG9kYXNoLWVzXCI7XG5pbXBvcnQga25leCwgeyBLbmV4IH0gZnJvbSBcImtuZXhcIjtcbmltcG9ydCBjaGFsayBmcm9tIFwiY2hhbGtcIjtcbmltcG9ydCB7IERhdGVUaW1lIH0gZnJvbSBcImx1eG9uXCI7XG5pbXBvcnQgeyBta2RpciwgcmVhZGRpciwgdW5saW5rLCB3cml0ZUZpbGUgfSBmcm9tIFwiZnMvcHJvbWlzZXNcIjtcbmltcG9ydCB7IGV4aXN0cyB9IGZyb20gXCIuLi91dGlscy9mcy11dGlsc1wiO1xuaW1wb3J0IHByb21wdHMgZnJvbSBcInByb21wdHNcIjtcbmltcG9ydCB7IGV4ZWNTeW5jIH0gZnJvbSBcImNoaWxkX3Byb2Nlc3NcIjtcbmltcG9ydCBwYXRoIGZyb20gXCJwYXRoXCI7XG5pbXBvcnQgeyBHZW5NaWdyYXRpb25Db2RlLCBNaWdyYXRpb25TZXQgfSBmcm9tIFwiLi4vdHlwZXMvdHlwZXNcIjtcbmltcG9ydCB7IEVudGl0eU1hbmFnZXIgfSBmcm9tIFwiLi4vZW50aXR5L2VudGl0eS1tYW5hZ2VyXCI7XG5pbXBvcnQgeyBTb25hbXUgfSBmcm9tIFwiLi4vYXBpXCI7XG5pbXBvcnQgeyBTZXJ2aWNlVW5hdmFpbGFibGVFeGNlcHRpb24gfSBmcm9tIFwiLi4vZXhjZXB0aW9ucy9zby1leGNlcHRpb25zXCI7XG5pbXBvcnQgeyBTb25hbXVEQkNvbmZpZyB9IGZyb20gXCIuLi9kYXRhYmFzZS9kYlwiO1xuaW1wb3J0IHsgZ2VuZXJhdGVDcmVhdGVDb2RlLCBnZW5lcmF0ZUFsdGVyQ29kZSB9IGZyb20gXCIuL2NvZGUtZ2VuZXJhdGlvblwiO1xuaW1wb3J0IHsgTWlncmF0aW9uU3RhdHVzLCBNaWdyYXRpb25Db2RlLCBDb25uU3RyaW5nIH0gZnJvbSBcIi4vdHlwZXNcIjtcbmltcG9ydCB7IGdldE1pZ3JhdGlvblNldEZyb21EQiB9IGZyb20gXCIuL21pZ3JhdGlvbi1zZXRcIjtcbmltcG9ydCB7IGdldE1pZ3JhdGlvblNldEZyb21FbnRpdHkgfSBmcm9tIFwiLi9taWdyYXRpb24tc2V0XCI7XG5cbnR5cGUgTWlncmF0b3JNb2RlID0gXCJkZXZcIiB8IFwiZGVwbG95XCI7XG5leHBvcnQgdHlwZSBNaWdyYXRvck9wdGlvbnMgPSB7XG4gIHJlYWRvbmx5IG1vZGU6IE1pZ3JhdG9yTW9kZTtcbn07XG5cbmV4cG9ydCBjbGFzcyBNaWdyYXRvciB7XG4gIHRhcmdldHM6IHtcbiAgICBjb21wYXJlPzogS25leDtcbiAgICBwZW5kaW5nOiBLbmV4O1xuICAgIHNoYWRvdzogS25leDtcbiAgICBhcHBseTogS25leFtdO1xuICB9O1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgcmVhZG9ubHkgb3B0aW9uczogTWlncmF0b3JPcHRpb25zKSB7XG4gICAgY29uc3QgeyBkYkNvbmZpZyB9ID0gU29uYW11O1xuXG4gICAgaWYgKHRoaXMub3B0aW9ucy5tb2RlID09PSBcImRldlwiKSB7XG4gICAgICBjb25zdCBkZXZEQiA9IGtuZXgoZGJDb25maWcuZGV2ZWxvcG1lbnRfbWFzdGVyKTtcbiAgICAgIGNvbnN0IHRlc3REQiA9IGtuZXgoZGJDb25maWcudGVzdCk7XG4gICAgICBjb25zdCBmaXh0dXJlTG9jYWxEQiA9IGtuZXgoZGJDb25maWcuZml4dHVyZV9sb2NhbCk7XG5cbiAgICAgIGNvbnN0IGFwcGx5REJzID0gW2RldkRCLCB0ZXN0REIsIGZpeHR1cmVMb2NhbERCXTtcbiAgICAgIGlmIChcbiAgICAgICAgKGRiQ29uZmlnLmZpeHR1cmVfbG9jYWwuY29ubmVjdGlvbiBhcyBLbmV4Lk15U3FsMkNvbm5lY3Rpb25Db25maWcpXG4gICAgICAgICAgLmhvc3QgIT09XG4gICAgICAgICAgKGRiQ29uZmlnLmZpeHR1cmVfcmVtb3RlLmNvbm5lY3Rpb24gYXMgS25leC5NeVNxbDJDb25uZWN0aW9uQ29uZmlnKVxuICAgICAgICAgICAgLmhvc3QgfHxcbiAgICAgICAgKGRiQ29uZmlnLmZpeHR1cmVfbG9jYWwuY29ubmVjdGlvbiBhcyBLbmV4Lk15U3FsMkNvbm5lY3Rpb25Db25maWcpXG4gICAgICAgICAgLmRhdGFiYXNlICE9PVxuICAgICAgICAgIChkYkNvbmZpZy5maXh0dXJlX3JlbW90ZS5jb25uZWN0aW9uIGFzIEtuZXguTXlTcWwyQ29ubmVjdGlvbkNvbmZpZylcbiAgICAgICAgICAgIC5kYXRhYmFzZVxuICAgICAgKSB7XG4gICAgICAgIGNvbnN0IGZpeHR1cmVSZW1vdGVEQiA9IGtuZXgoZGJDb25maWcuZml4dHVyZV9yZW1vdGUpO1xuICAgICAgICBhcHBseURCcy5wdXNoKGZpeHR1cmVSZW1vdGVEQik7XG4gICAgICB9XG5cbiAgICAgIHRoaXMudGFyZ2V0cyA9IHtcbiAgICAgICAgY29tcGFyZTogZGV2REIsXG4gICAgICAgIHBlbmRpbmc6IGRldkRCLFxuICAgICAgICBzaGFkb3c6IHRlc3REQixcbiAgICAgICAgYXBwbHk6IGFwcGx5REJzLFxuICAgICAgfTtcbiAgICB9IGVsc2UgaWYgKHRoaXMub3B0aW9ucy5tb2RlID09PSBcImRlcGxveVwiKSB7XG4gICAgICBjb25zdCBwcm9kdWN0aW9uREIgPSBrbmV4KGRiQ29uZmlnLnByb2R1Y3Rpb25fbWFzdGVyKTtcbiAgICAgIGNvbnN0IHRlc3REQiA9IGtuZXgoZGJDb25maWcudGVzdCk7XG5cbiAgICAgIHRoaXMudGFyZ2V0cyA9IHtcbiAgICAgICAgcGVuZGluZzogcHJvZHVjdGlvbkRCLFxuICAgICAgICBzaGFkb3c6IHRlc3REQixcbiAgICAgICAgYXBwbHk6IFtwcm9kdWN0aW9uREJdLFxuICAgICAgfTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGDsnpjrqrvrkJwg66qo65OcICR7dGhpcy5vcHRpb25zLm1vZGV9IOyeheugpWApO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZ2V0TWlncmF0aW9uQ29kZXMoKTogUHJvbWlzZTxNaWdyYXRpb25Db2RlW10+IHtcbiAgICBjb25zdCBzcmNNaWdyYXRpb25zRGlyID0gcGF0aC5qb2luKFNvbmFtdS5hcGlSb290UGF0aCwgXCJzcmNcIiwgXCJtaWdyYXRpb25zXCIpOyAvLyDsnbTqsbQg7ZmY6rK97JeQIOq0gOqzhOyXhuydtCDtla3sg4Egc3Jj7JeQ7IScIOywvuyVhOyVvCDtlbTsmpQuXG5cbiAgICBpZiAoIShhd2FpdCBleGlzdHMoc3JjTWlncmF0aW9uc0RpcikpKSB7XG4gICAgICBhd2FpdCBta2RpcihzcmNNaWdyYXRpb25zRGlyLCB7XG4gICAgICAgIHJlY3Vyc2l2ZTogdHJ1ZSxcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIGNvbnN0IGNvZGVzID0gKGF3YWl0IHJlYWRkaXIoc3JjTWlncmF0aW9uc0RpcikpXG4gICAgICAuZmlsdGVyKChmKSA9PiBmLmVuZHNXaXRoKFwiLnRzXCIpKVxuICAgICAgLm1hcCgoZikgPT4gKHtcbiAgICAgICAgbmFtZTogZi5yZXBsYWNlKFwiLnRzXCIsIFwiXCIpLFxuICAgICAgICBwYXRoOiBwYXRoLmpvaW4oc3JjTWlncmF0aW9uc0RpciwgZiksXG4gICAgICB9KSlcbiAgICAgIC5zb3J0KChhLCBiKSA9PiAoYS5uYW1lIDwgYi5uYW1lID8gMSA6IC0xKSk7IC8vIOydtOumhCDrgrTrprzssKjsiJwg7KCV66CsKOy1nOyLoOyInClcblxuICAgIHJldHVybiBjb2RlcztcbiAgfVxuXG4gIC8qKlxuICAgKiDtg4Dqsp/rs4Qg66eI7J206re466CI7J207IWYIOyDge2DnOyZgCDsvZTrk5wg7IOd7ISxL+ykgOu5hCDsg4Htg5zrpbwg6rWs7ZW07Ji164uI64ukLlxuICAgKiDsi6TsoJzroZwgRELsl5Ag7KCR6re864+EIO2VmOqzoCDrp4jsnbTqt7jroIjsnbTshZgg7L2U65OcIO2MjOydvOuPhCDtmZXsnbjtlZjqs6AsXG4gICAqIO2VhOyalO2VmOuLpOuptCDsoIHsmqntlaAg7IiYIOyeiOuKlCDsvZTrk5zrpbwg7IOd7ISx6rmM7KeAIO2VtOyYteuLiOuLpC5cbiAgICpcbiAgICogQ0xJ7JmAIFNvbmFtdSBVSeyXkOyEnCDsgqzsmqnrkKnri4jri6QuXG4gICAqXG4gICAqIEByZXR1cm5zXG4gICAqL1xuICBhc3luYyBnZXRTdGF0dXMoKTogUHJvbWlzZTxNaWdyYXRpb25TdGF0dXM+IHtcbiAgICBjb25zdCBjb2RlcyA9IGF3YWl0IHRoaXMuZ2V0TWlncmF0aW9uQ29kZXMoKTtcblxuICAgIGNvbnN0IGNvbm5LZXlzID0gT2JqZWN0LmtleXMoU29uYW11LmRiQ29uZmlnKS5maWx0ZXIoXG4gICAgICAoa2V5KSA9PiBrZXkuZW5kc1dpdGgoXCJfc2xhdmVcIikgPT09IGZhbHNlXG4gICAgKSBhcyAoa2V5b2YgdHlwZW9mIFNvbmFtdS5kYkNvbmZpZylbXTtcblxuICAgIGNvbnN0IHN0YXR1c2VzID0gYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICBjb25uS2V5cy5tYXAoYXN5bmMgKGNvbm5LZXkpID0+IHtcbiAgICAgICAgY29uc3Qga25leE9wdGlvbnMgPSBTb25hbXUuZGJDb25maWdbY29ubktleV07XG4gICAgICAgIGNvbnN0IHRDb25uID0ga25leChrbmV4T3B0aW9ucyk7XG5cbiAgICAgICAgY29uc3Qgc3RhdHVzID0gYXdhaXQgKGFzeW5jICgpID0+IHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRDb25uLm1pZ3JhdGUuc3RhdHVzKCk7XG4gICAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgICAgIGNoYWxrLnllbGxvdyhcbiAgICAgICAgICAgICAgICBgJHtjb25uS2V5feydmCDrp4jsnbTqt7jroIjsnbTshZgg7IOB7YOc66W8IOqwgOyguOyYpOuKlCDrjbDsl5Ag7Iuk7Yyo7ZWY7JiA7Iq164uI64ukLiDrjbDsnbTthLDrsqDsnbTsiqTqsIAg7Jis67CU66W06rKMIOq1rOyEseuQmOyngCDslYrsnYAg6rKDIOqwmeyKteuLiOuLpC4g7ZmV7J247ZWY7Iuc6rOgIOuLpOyLnCDsi5zrj4TtlbTso7zshLjsmpQuXFxu7Iuc64+E7ZWcIOyXsOqysCDshKTsoJU6XFxuJHtKU09OLnN0cmluZ2lmeShrbmV4T3B0aW9ucy5jb25uZWN0aW9uLCBudWxsLCAyKX1cXG7rsJzsg53tlZwg7JeQ65+sOlxcbiR7ZXJyfVxcbmBcbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIHJldHVybiBcImVycm9yXCIgLyrtgbTrnbzsnbTslrjtirjsl5DshJwg7JeQ65+sIOyytO2BrOyXkCDsgqzsmqntlZjripQg66as7YSw65+07J6F64uI64ukLiovO1xuICAgICAgICAgIH1cbiAgICAgICAgfSkoKTtcbiAgICAgICAgY29uc3QgcGVuZGluZyA9IGF3YWl0IChhc3luYyAoKSA9PiB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IFssIGZkTGlzdF0gPSBhd2FpdCB0Q29ubi5taWdyYXRlLmxpc3QoKTtcbiAgICAgICAgICAgIHJldHVybiBmZExpc3QubWFwKChmZDogeyBmaWxlOiBzdHJpbmcgfSkgPT5cbiAgICAgICAgICAgICAgZmQuZmlsZS5yZXBsYWNlKFwiLnRzXCIsIFwiXCIpXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgcmV0dXJuIFtdO1xuICAgICAgICAgIH1cbiAgICAgICAgfSkoKTtcbiAgICAgICAgY29uc3QgY3VycmVudFZlcnNpb24gPSBhd2FpdCAoYXN5bmMgKCkgPT4ge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICByZXR1cm4gYXdhaXQgdENvbm4ubWlncmF0ZS5jdXJyZW50VmVyc2lvbigpO1xuICAgICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgcmV0dXJuIFwiZXJyb3JcIjtcbiAgICAgICAgICB9XG4gICAgICAgIH0pKCk7XG5cbiAgICAgICAgY29uc3QgY29ubmVjdGlvbiA9XG4gICAgICAgICAga25leE9wdGlvbnMuY29ubmVjdGlvbiBhcyBLbmV4Lk15U3FsMkNvbm5lY3Rpb25Db25maWc7XG5cbiAgICAgICAgYXdhaXQgdENvbm4uZGVzdHJveSgpO1xuXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgbmFtZTogY29ubktleS5yZXBsYWNlKFwiX21hc3RlclwiLCBcIlwiKSxcbiAgICAgICAgICBjb25uS2V5LFxuICAgICAgICAgIGNvbm5TdHJpbmc6IGBteXNxbDI6Ly8ke2Nvbm5lY3Rpb24udXNlciA/PyBcIlwifUAke2Nvbm5lY3Rpb24uaG9zdH06JHtcbiAgICAgICAgICAgIGNvbm5lY3Rpb24ucG9ydFxuICAgICAgICAgIH0vJHtjb25uZWN0aW9uLmRhdGFiYXNlfWAgYXMgQ29ublN0cmluZyxcbiAgICAgICAgICBjdXJyZW50VmVyc2lvbixcbiAgICAgICAgICBzdGF0dXMsXG4gICAgICAgICAgcGVuZGluZyxcbiAgICAgICAgfTtcbiAgICAgIH0pXG4gICAgKTtcblxuICAgIGNvbnN0IHByZXBhcmVkQ29kZXM6IEdlbk1pZ3JhdGlvbkNvZGVbXSA9IGF3YWl0IChhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBzdGF0dXMwY29ubiA9IHN0YXR1c2VzLmZpbmQoKHN0YXR1cykgPT4gc3RhdHVzLnN0YXR1cyA9PT0gMCk7XG4gICAgICBpZiAoc3RhdHVzMGNvbm4gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgY2hhbGsueWVsbG93KFxuICAgICAgICAgICAgYFdoaWxlIHRyeWluZyB0byBwcmVwYXJlIG1pZ3JhdGlvbiBjb2Rlcywgd2UgZm91bmQgdGhhdCB0aGVyZSBpcyBubyBkYXRhYmFzZSB0byBjb21wYXJlIG1pZ3JhdGlvbnMuIFdlIG5lZWQgYXQgbGVhc3Qgb25lIGRhdGFiYXNlIHdoZXJlIGV2ZXJ5IG1pZ3JhdGlvbiBpcyBhcHBsaWVkKHN0YXR1cyA9PT0gMCkuIFlvdSBtaWdodCB3YW50IHRvIGFwcGx5IHlvdXIgZXhpc3RpbmcgbWlncmF0aW9ucyB0byBvbmUgb2YgdGhlIGRhdGFiYXNlcy5gXG4gICAgICAgICAgKVxuICAgICAgICApO1xuICAgICAgICByZXR1cm4gW107XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGNvbXBhcmVEQmNvbm4gPSBrbmV4KFNvbmFtdS5kYkNvbmZpZ1tzdGF0dXMwY29ubi5jb25uS2V5XSk7XG4gICAgICBjb25zdCBnZW5Db2RlcyA9IGF3YWl0IHRoaXMuY29tcGFyZU1pZ3JhdGlvbnMoY29tcGFyZURCY29ubik7XG5cbiAgICAgIGF3YWl0IGNvbXBhcmVEQmNvbm4uZGVzdHJveSgpO1xuXG4gICAgICByZXR1cm4gZ2VuQ29kZXM7XG4gICAgfSkoKTtcblxuICAgIHJldHVybiB7XG4gICAgICBjb25uczogc3RhdHVzZXMsXG4gICAgICBjb2RlcyxcbiAgICAgIHByZXBhcmVkQ29kZXMsXG4gICAgfTtcbiAgICAvKlxuICAgIERCIOuniOydtOq3uOugiOydtOyFmCDsg4Htg5wg7ZmV7J24XG4gICAgMS4g7KCE7LK0IERC7ISk7KCV7JeQIOuMgO2VtOyEnCDtmITsnqwg66eI7J206re466CI7J207IWYIOyDge2DnCDtmZXsnbhcbiAgICAtIGNvbm5LZXk6IHN0cmluZ1xuICAgIC0gc3RhdHVzOiBudW1iZXJcbiAgICAtIGN1cnJlbnRWZXJzaW9uOiBzdHJpbmdcbiAgICAtIGxpc3Q6IHsgZmlsZTogc3RyaW5nOyBkaXJlY3Rvcnk6IHN0cmluZyB9W11cbiAgICBcbiAgICAqL1xuICB9XG5cbiAgLyoqXG4gICAqIOuniOydtOq3uOugiOydtOyFmOydhCDsoIHsmqntlZjqsbDrgpgg66Gk67Cx7ZWp64uI64ukLlxuICAgKiBTb25hbXUgVUnsl5DshJwg66eI7J206re466CI7J207IWYIOyekeyXheydhCDsiJjtlontlaAg65WMIOyCrOyaqeuQqeuLiOuLpC5cbiAgICpcbiAgICogQ0xJ7JmAIFNvbmFtdSBVSeyXkOyEnCDsgqzsmqnrkKnri4jri6QuXG4gICAqXG4gICAqIEBwYXJhbSBhY3Rpb24g7J6R7JeFIOycoO2YlSAoYXBwbHkvcm9sbGJhY2spXG4gICAqIEBwYXJhbSB0YXJnZXRzIOyekeyXhSDrjIDsg4EgREIg7ISk7KCVIO2CpCAoa2V5b2YgU29uYW11REJDb25maWcpXG4gICAqIEByZXR1cm5zIOyekeyXhSDqsrDqs7xcbiAgICovXG4gIGFzeW5jIHJ1bkFjdGlvbihcbiAgICBhY3Rpb246IFwiYXBwbHlcIiB8IFwicm9sbGJhY2tcIixcbiAgICB0YXJnZXRzOiAoa2V5b2YgU29uYW11REJDb25maWcpW11cbiAgKTogUHJvbWlzZTxcbiAgICB7XG4gICAgICBjb25uS2V5OiBzdHJpbmc7XG4gICAgICBiYXRjaE5vOiBudW1iZXI7XG4gICAgICBhcHBsaWVkOiBzdHJpbmdbXTtcbiAgICB9W11cbiAgPiB7XG4gICAgLy8gZ2V0IHVuaXEga25leCBjb25maWdzXG4gICAgY29uc3QgY29uZmlncyA9IF8udW5pcUJ5KFxuICAgICAgdGFyZ2V0c1xuICAgICAgICAubWFwKCh0YXJnZXQpID0+ICh7XG4gICAgICAgICAgY29ubktleTogdGFyZ2V0LFxuICAgICAgICAgIG9wdGlvbnM6IFNvbmFtdS5kYkNvbmZpZ1t0YXJnZXQgYXMga2V5b2YgdHlwZW9mIFNvbmFtdS5kYkNvbmZpZ10sXG4gICAgICAgIH0pKVxuICAgICAgICAuZmlsdGVyKChjKSA9PiBjLm9wdGlvbnMgIT09IHVuZGVmaW5lZCksXG4gICAgICAoeyBvcHRpb25zIH0pID0+XG4gICAgICAgIGAkeyhvcHRpb25zLmNvbm5lY3Rpb24gYXMgS25leC5NeVNxbDJDb25uZWN0aW9uQ29uZmlnKS5ob3N0fToke1xuICAgICAgICAgIChvcHRpb25zLmNvbm5lY3Rpb24gYXMgS25leC5NeVNxbDJDb25uZWN0aW9uQ29uZmlnKS5wb3J0ID8/IDMzMDZcbiAgICAgICAgfS8keyhvcHRpb25zLmNvbm5lY3Rpb24gYXMgS25leC5NeVNxbDJDb25uZWN0aW9uQ29uZmlnKS5kYXRhYmFzZX1gXG4gICAgKTtcblxuICAgIC8vIGdldCBjb25uZWN0aW9uc1xuICAgIGNvbnN0IGNvbm5zID0gYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICBjb25maWdzLm1hcChhc3luYyAoY29uZmlnKSA9PiAoe1xuICAgICAgICBjb25uS2V5OiBjb25maWcuY29ubktleSxcbiAgICAgICAga25leDoga25leChjb25maWcub3B0aW9ucyksXG4gICAgICB9KSlcbiAgICApO1xuXG4gICAgLy8gYWN0aW9uXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgKGFzeW5jICgpID0+IHtcbiAgICAgIHN3aXRjaCAoYWN0aW9uKSB7XG4gICAgICAgIGNhc2UgXCJhcHBseVwiOlxuICAgICAgICAgIHJldHVybiBQcm9taXNlLmFsbChcbiAgICAgICAgICAgIGNvbm5zLm1hcChhc3luYyAoeyBjb25uS2V5LCBrbmV4IH0pID0+IHtcbiAgICAgICAgICAgICAgY29uc3QgW2JhdGNoTm8sIGFwcGxpZWRdID0gYXdhaXQga25leC5taWdyYXRlLmxhdGVzdCgpO1xuICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIGNvbm5LZXksXG4gICAgICAgICAgICAgICAgYmF0Y2hObyxcbiAgICAgICAgICAgICAgICBhcHBsaWVkLFxuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgfSlcbiAgICAgICAgICApO1xuICAgICAgICBjYXNlIFwicm9sbGJhY2tcIjpcbiAgICAgICAgICByZXR1cm4gUHJvbWlzZS5hbGwoXG4gICAgICAgICAgICBjb25ucy5tYXAoYXN5bmMgKHsgY29ubktleSwga25leCB9KSA9PiB7XG4gICAgICAgICAgICAgIGNvbnN0IFtiYXRjaE5vLCBhcHBsaWVkXSA9IGF3YWl0IGtuZXgubWlncmF0ZS5yb2xsYmFjaygpO1xuICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIGNvbm5LZXksXG4gICAgICAgICAgICAgICAgYmF0Y2hObyxcbiAgICAgICAgICAgICAgICBhcHBsaWVkLFxuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgfSlcbiAgICAgICAgICApO1xuICAgICAgfVxuICAgIH0pKCk7XG5cbiAgICAvLyBkZXN0cm95XG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICBjb25ucy5tYXAoKHsga25leCB9KSA9PiB7XG4gICAgICAgIHJldHVybiBrbmV4LmRlc3Ryb3koKTtcbiAgICAgIH0pXG4gICAgKTtcblxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICAvKipcbiAgICog66eI7J206re466CI7J207IWYIOy9lOuTnCDtjIzsnbzsnYQg7IKt7KCc7ZWp64uI64ukLlxuICAgKlxuICAgKiBTb25hbXUgVUnsl5DshJwg7IKs7Jqp65Cp64uI64ukLlxuICAgKlxuICAgKiBAcGFyYW0gY29kZU5hbWVzIOyCreygnO2VoCDrp4jsnbTqt7jroIjsnbTshZgg7L2U65OcIO2MjOydvCDsnbTrpoQg67Cw7Je0XG4gICAqIEByZXR1cm5zIOyCreygnOuQnCDrp4jsnbTqt7jroIjsnbTshZgg7L2U65OcIO2MjOydvCDqsJzsiJhcbiAgICovXG4gIGFzeW5jIGRlbENvZGVzKGNvZGVOYW1lczogc3RyaW5nW10pOiBQcm9taXNlPG51bWJlcj4ge1xuICAgIGNvbnN0IHsgY29ubnMgfSA9IGF3YWl0IHRoaXMuZ2V0U3RhdHVzKCk7XG4gICAgaWYgKFxuICAgICAgY29ubnMuc29tZSgoY29ubikgPT4ge1xuICAgICAgICByZXR1cm4gY29kZU5hbWVzLnNvbWUoXG4gICAgICAgICAgKGNvZGVOYW1lKSA9PiBjb25uLnBlbmRpbmcuaW5jbHVkZXMoY29kZU5hbWUpID09PSBmYWxzZVxuICAgICAgICApO1xuICAgICAgfSlcbiAgICApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgXCJZb3UgY2Fubm90IGRlbGV0ZSBhIG1pZ3JhdGlvbiBmaWxlIGlmIHRoZXJlIGlzIGFscmVhZHkgYXBwbGllZC5cIlxuICAgICAgKTtcbiAgICB9XG5cbiAgICBjb25zdCBkZWxGaWxlcyA9IGNvZGVOYW1lcy5tYXAoXG4gICAgICAoY29kZU5hbWUpID0+IGAke1NvbmFtdS5hcGlSb290UGF0aH0vc3JjL21pZ3JhdGlvbnMvJHtjb2RlTmFtZX0udHNgXG4gICAgKTtcblxuICAgIGNvbnN0IHJlcyA9IGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgZGVsRmlsZXMubWFwKGFzeW5jIChkZWxGaWxlKSA9PiB7XG4gICAgICAgIGlmIChhd2FpdCBleGlzdHMoZGVsRmlsZSkpIHtcbiAgICAgICAgICBjb25zb2xlLmxvZyhjaGFsay5yZWQoYERFTEVURTogJHtkZWxGaWxlfWApKTtcbiAgICAgICAgICBhd2FpdCB1bmxpbmsoZGVsRmlsZSk7XG4gICAgICAgICAgcmV0dXJuIGRlbEZpbGVzLmluY2x1ZGVzKFwiLnRzXCIpID8gMSA6IDA7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIDA7XG4gICAgICB9KVxuICAgICk7XG4gICAgcmV0dXJuIF8uc3VtKHJlcyk7XG4gIH1cblxuICAvKipcbiAgICog66eI7J206re466CI7J207IWYIOy9lOuTnCDtjIzsnbzsnYQg7IOd7ISx7ZWp64uI64ukLlxuICAgKlxuICAgKiBTb25hbXUgVUnsl5DshJwg7IKs7Jqp65Cp64uI64ukLlxuICAgKlxuICAgKiBAcmV0dXJucyDsg53shLHrkJwg66eI7J206re466CI7J207IWYIOy9lOuTnCDtjIzsnbwg6rCc7IiYXG4gICAqL1xuICBhc3luYyBnZW5lcmF0ZVByZXBhcmVkQ29kZXMoKTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICBjb25zdCB7IHByZXBhcmVkQ29kZXMgfSA9IGF3YWl0IHRoaXMuZ2V0U3RhdHVzKCk7XG4gICAgaWYgKHByZXBhcmVkQ29kZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICBjb25zb2xlLmxvZyhjaGFsay5ncmVlbihcIlxcbu2YhOyerCDrqqjrkZAg7Iux7YGs65CcIOyDge2DnOyeheuLiOuLpC5cIikpO1xuICAgICAgcmV0dXJuIDA7XG4gICAgfVxuXG4gICAgLy8g7Iuk7KCcIOy9lOuTnCDsg53shLFcbiAgICBjb25zdCBtaWdyYXRpb25zRGlyID0gYCR7U29uYW11LmFwaVJvb3RQYXRofS9zcmMvbWlncmF0aW9uc2A7XG5cbiAgICBmb3IgKGNvbnN0IFtpbmRleCwgcGNvZGVdIG9mIHByZXBhcmVkQ29kZXMuZW50cmllcygpKSB7XG4gICAgICBpZiAocGNvZGUuZm9ybWF0dGVkKSB7XG4gICAgICAgIGNvbnN0IGRhdGVUYWcgPSBEYXRlVGltZS5sb2NhbCgpXG4gICAgICAgICAgLnBsdXMoeyBzZWNvbmRzOiBpbmRleCB9KVxuICAgICAgICAgIC50b0Zvcm1hdChcInl5eXlNTWRkSEhtbXNzXCIpO1xuICAgICAgICBjb25zdCBmaWxlUGF0aCA9IGAke21pZ3JhdGlvbnNEaXJ9LyR7ZGF0ZVRhZ31fJHtwY29kZS50aXRsZX0udHNgO1xuICAgICAgICBhd2FpdCB3cml0ZUZpbGUoZmlsZVBhdGgsIHBjb2RlLmZvcm1hdHRlZCEpO1xuICAgICAgICBjb25zb2xlLmxvZyhjaGFsay5ncmVlbihgTUlHUlRBSU9OIENSRUFURUQgJHtmaWxlUGF0aH1gKSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHByZXBhcmVkQ29kZXMubGVuZ3RoO1xuICB9XG5cbiAgLyoqXG4gICAqIHBlbmRpbmcg66eI7J206re466CI7J207IWYIOuqqeuhneydhCDsgq3soJztlanri4jri6QuXG4gICAqXG4gICAqIENMSeyXkOyEnCDsgqzsmqnrkKnri4jri6QuXG4gICAqL1xuICBhc3luYyBjbGVhclBlbmRpbmdMaXN0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IFssIHBlbmRpbmdMaXN0XSA9IChhd2FpdCB0aGlzLnRhcmdldHMucGVuZGluZy5taWdyYXRlLmxpc3QoKSkgYXMgW1xuICAgICAgdW5rbm93bixcbiAgICAgIHtcbiAgICAgICAgZmlsZTogc3RyaW5nO1xuICAgICAgICBkaXJlY3Rvcnk6IHN0cmluZztcbiAgICAgIH1bXSxcbiAgICBdO1xuICAgIGNvbnN0IG1pZ3JhdGlvbnNEaXIgPSBgJHtTb25hbXUuYXBpUm9vdFBhdGh9L3NyYy9taWdyYXRpb25zYDtcbiAgICBjb25zdCBkZWxMaXN0ID0gcGVuZGluZ0xpc3QubWFwKChkZikgPT4ge1xuICAgICAgcmV0dXJuIHBhdGguam9pbihtaWdyYXRpb25zRGlyLCBkZi5maWxlKTtcbiAgICB9KTtcbiAgICBmb3IgKGxldCBwIG9mIGRlbExpc3QpIHtcbiAgICAgIGlmIChhd2FpdCBleGlzdHMocCkpIHtcbiAgICAgICAgYXdhaXQgdW5saW5rKHApO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiDrp4jsnbTqt7jroIjsnbTshZgg7L2U65OcIO2MjOydvOydhCDtmZXsnbjtlanri4jri6QuXG4gICAqXG4gICAqIENMSeyXkOyEnCDsgqzsmqnrkKnri4jri6QuXG4gICAqL1xuICBhc3luYyBjaGVjaygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBjb2RlcyA9IGF3YWl0IHRoaXMuY29tcGFyZU1pZ3JhdGlvbnModGhpcy50YXJnZXRzLmNvbXBhcmUhKTtcbiAgICBpZiAoY29kZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICBjb25zb2xlLmxvZyhjaGFsay5ncmVlbihcIlxcbu2YhOyerCDrqqjrkZAg7Iux7YGs65CcIOyDge2DnOyeheuLiOuLpC5cIikpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIO2YhOyerCDsg53shLHrkJwg7L2U65OcIO2RnOq4sFxuICAgIGNvbnNvbGUudGFibGUoY29kZXMsIFtcInR5cGVcIiwgXCJ0aXRsZVwiXSk7XG4gICAgY29uc29sZS5sb2coY29kZXNbMF0pO1xuICB9XG5cbiAgLyoqXG4gICAqIOuniOydtOq3uOugiOydtOyFmOydhCDsiJjtlontlanri4jri6QuXG4gICAqXG4gICAqIHJ1bkFjdGlvbuydtCDsnbjsnpDroZwg65Ok7Ja07JioIO2DgOqyn+uTpOyXkCDrjIDtlbQg7KO87Ja07KeEIOuPmeyekShhcHBseS9yb2xsYmFjaynsnYQg7IiY7ZaJ7ZWc64uk66m0LFxuICAgKiDsnbQg7ZWo7IiY64qUIOyDneyEseyekOuhnCDrk6TslrTsmKggY29ubmVjdGlvbihrbmV4KeuTpOyXkCDrjIDtlbQg66eI7J206re466CI7J207IWY7J2EIOyImO2Wie2VqeuLiOuLpC5cbiAgICpcbiAgICogQ0xJ7JeQ7IScIOyCrOyaqeuQqeuLiOuLpC5cbiAgICovXG4gIGFzeW5jIHJ1bigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAvLyBwZW5kaW5nIOuniOydtOq3uOugiOydtOyFmCDtmZXsnbhcbiAgICBjb25zdCBbLCBwZW5kaW5nTGlzdF0gPSBhd2FpdCB0aGlzLnRhcmdldHMucGVuZGluZy5taWdyYXRlLmxpc3QoKTtcbiAgICBpZiAocGVuZGluZ0xpc3QubGVuZ3RoID4gMCkge1xuICAgICAgY29uc29sZS5sb2coXG4gICAgICAgIGNoYWxrLnJlZChcInBlbmRpbmcg65CcIOuniOydtOq3uOugiOydtOyFmOydtCDsobTsnqztlanri4jri6QuXCIpLFxuICAgICAgICBwZW5kaW5nTGlzdC5tYXAoKHBlbmRpbmc6IGFueSkgPT4gcGVuZGluZy5maWxlKVxuICAgICAgKTtcblxuICAgICAgLy8gcGVuZGluZ+ydtCDsnojripQg6rK97JqwIFNoYWRvdyBEQiDthYzsiqTtirgg7KeE7ZaJIOyXrOu2gCDsu6jtjoxcbiAgICAgIGNvbnN0IGFuc3dlciA9IGF3YWl0IHByb21wdHMoe1xuICAgICAgICB0eXBlOiBcImNvbmZpcm1cIixcbiAgICAgICAgbmFtZTogXCJ2YWx1ZVwiLFxuICAgICAgICBtZXNzYWdlOiBcIlNoYWRvdyBEQiDthYzsiqTtirjrpbwg7KeE7ZaJ7ZWY7Iuc6rKg7Iq164uI6rmMP1wiLFxuICAgICAgICBpbml0aWFsOiB0cnVlLFxuICAgICAgfSk7XG4gICAgICBpZiAoYW5zd2VyLnZhbHVlID09PSBmYWxzZSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGNvbnNvbGUudGltZShjaGFsay5ibHVlKFwiTWlncmF0b3IgLSBydW5TaGFkb3dUZXN0XCIpKTtcbiAgICAgIGF3YWl0IHRoaXMucnVuU2hhZG93VGVzdCgpO1xuICAgICAgY29uc29sZS50aW1lRW5kKGNoYWxrLmJsdWUoXCJNaWdyYXRvciAtIHJ1blNoYWRvd1Rlc3RcIikpO1xuICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICAgIHRoaXMudGFyZ2V0cy5hcHBseS5tYXAoYXN5bmMgKGFwcGx5RGIpID0+IHtcbiAgICAgICAgICBjb25zdCBsYWJlbCA9IGNoYWxrLmdyZWVuKFxuICAgICAgICAgICAgYEFQUExJRUQgJHtcbiAgICAgICAgICAgICAgYXBwbHlEYi5jbGllbnQuY29ubmVjdGlvblNldHRpbmdzLmhvc3RcbiAgICAgICAgICAgIH0gJHthcHBseURiLmNsaWVudC5kYXRhYmFzZSgpfWBcbiAgICAgICAgICApO1xuICAgICAgICAgIGNvbnNvbGUudGltZShsYWJlbCk7XG4gICAgICAgICAgY29uc3QgWyxdID0gYXdhaXQgYXBwbHlEYi5taWdyYXRlLmxhdGVzdCgpO1xuICAgICAgICAgIGNvbnNvbGUudGltZUVuZChsYWJlbCk7XG4gICAgICAgIH0pXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIEVudGl0eS1EQuqwhCDruYTqtZDtlZjsl6wg7L2U65OcIOyDneyEsSDrpqzthLRcbiAgICBjb25zdCBjb2RlcyA9IGF3YWl0IHRoaXMuY29tcGFyZU1pZ3JhdGlvbnModGhpcy50YXJnZXRzLmNvbXBhcmUhKTtcbiAgICBpZiAoY29kZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICBjb25zb2xlLmxvZyhjaGFsay5ncmVlbihcIlxcbu2YhOyerCDrqqjrkZAg7Iux7YGs65CcIOyDge2DnOyeheuLiOuLpC5cIikpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIO2YhOyerCDsg53shLHrkJwg7L2U65OcIO2RnOq4sFxuICAgIGNvbnNvbGUudGFibGUoY29kZXMsIFtcInR5cGVcIiwgXCJ0aXRsZVwiXSk7XG5cbiAgICAvKiBERUJVRzog65SU67KE6rmF7JqpIOy9lOuTnFxuICAgIGNvZGVzLm1hcCgoY29kZSkgPT4gY29uc29sZS5sb2coY29kZS5mb3JtYXR0ZWQpKTtcbiAgICBwcm9jZXNzLmV4aXQoKTtcbiAgICAgKi9cblxuICAgIC8vIOyLpOygnCDtjIzsnbwg7IOd7ISxIO2UhOuhrO2UhO2KuFxuICAgIGNvbnN0IGFuc3dlciA9IGF3YWl0IHByb21wdHMoe1xuICAgICAgdHlwZTogXCJjb25maXJtXCIsXG4gICAgICBuYW1lOiBcInZhbHVlXCIsXG4gICAgICBtZXNzYWdlOiBcIuuniOydtOq3uOugiOydtOyFmCDsvZTrk5zrpbwg7IOd7ISx7ZWY7Iuc6rKg7Iq164uI6rmMP1wiLFxuICAgICAgaW5pdGlhbDogZmFsc2UsXG4gICAgfSk7XG4gICAgaWYgKGFuc3dlci52YWx1ZSA9PT0gZmFsc2UpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyDsi6TsoJwg7L2U65OcIOyDneyEsVxuICAgIGNvbnN0IG1pZ3JhdGlvbnNEaXIgPSBgJHtTb25hbXUuYXBpUm9vdFBhdGh9L3NyYy9taWdyYXRpb25zYDtcblxuICAgIGZvciAoY29uc3QgW2luZGV4LCBjb2RlXSBvZiBjb2Rlcy5lbnRyaWVzKCkpIHtcbiAgICAgIGlmIChjb2RlLmZvcm1hdHRlZCkge1xuICAgICAgICBjb25zdCBkYXRlVGFnID0gRGF0ZVRpbWUubG9jYWwoKVxuICAgICAgICAgIC5wbHVzKHsgc2Vjb25kczogaW5kZXggfSlcbiAgICAgICAgICAudG9Gb3JtYXQoXCJ5eXl5TU1kZEhIbW1zc1wiKTtcbiAgICAgICAgY29uc3QgZmlsZVBhdGggPSBgJHttaWdyYXRpb25zRGlyfS8ke2RhdGVUYWd9XyR7Y29kZS50aXRsZX0udHNgO1xuICAgICAgICBhd2FpdCB3cml0ZUZpbGUoZmlsZVBhdGgsIGNvZGUuZm9ybWF0dGVkISk7XG4gICAgICAgIGNvbnNvbGUubG9nKGNoYWxrLmdyZWVuKGBNSUdSVEFJT04gQ1JFQVRFRCAke2ZpbGVQYXRofWApKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICog7YOA6rKf7Jy866GcIOyngOygleuQnCBEQuulvCDroaTrsLHtlanri4jri6QuXG4gICAqXG4gICAqIHJ1bkFjdGlvbuydtCDsnbjsnpDroZwg65Ok7Ja07JioIO2DgOqyn+uTpOyXkCDrjIDtlbQg7KO87Ja07KeEIOuPmeyekShhcHBseS9yb2xsYmFjaynsnYQg7IiY7ZaJ7ZWc64uk66m0LFxuICAgKiDsnbQg7ZWo7IiY64qUIOyDneyEseyekOuhnCDrk6TslrTsmKggY29ubmVjdGlvbihrbmV4KeuTpOyXkCDrjIDtlbQg66Gk67Cx7J2EIOyImO2Wie2VqeuLiOuLpC5cbiAgICpcbiAgICogQ0xJ7JeQ7IScIOyCrOyaqeuQqeuLiOuLpC5cbiAgICovXG4gIGFzeW5jIHJvbGxiYWNrKCkge1xuICAgIGNvbnNvbGUudGltZShjaGFsay5yZWQoXCJyb2xsYmFjazpcIikpO1xuICAgIGNvbnN0IHJvbGxiYWNrQWxsUmVzdWx0ID0gYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICB0aGlzLnRhcmdldHMuYXBwbHkubWFwKGFzeW5jIChkYikgPT4ge1xuICAgICAgICBhd2FpdCBkYi5taWdyYXRlLmZvcmNlRnJlZU1pZ3JhdGlvbnNMb2NrKCk7XG4gICAgICAgIHJldHVybiBkYi5taWdyYXRlLnJvbGxiYWNrKHVuZGVmaW5lZCwgZmFsc2UpO1xuICAgICAgfSlcbiAgICApO1xuICAgIGNvbnNvbGUuZGlyKHsgcm9sbGJhY2tBbGxSZXN1bHQgfSwgeyBkZXB0aDogbnVsbCB9KTtcbiAgICBjb25zb2xlLnRpbWVFbmQoY2hhbGsucmVkKFwicm9sbGJhY2s6XCIpKTtcbiAgfVxuICAvKipcbiAgICogU2hhZG93IERCIO2FjOyKpO2KuOulvCDsp4Ttlontlanri4jri6QuXG4gICAqXG4gICAqIFNvbmFtdSBVSeyXkOyEnCDsgqzsmqnrkKnri4jri6QuXG4gICAqXG4gICAqIEByZXR1cm5zIFNoYWRvdyBEQiDthYzsiqTtirgg6rKw6rO8XG4gICAqL1xuICBhc3luYyBydW5TaGFkb3dUZXN0KCk6IFByb21pc2U8XG4gICAge1xuICAgICAgY29ubktleTogc3RyaW5nO1xuICAgICAgYmF0Y2hObzogbnVtYmVyO1xuICAgICAgYXBwbGllZDogc3RyaW5nW107XG4gICAgfVtdXG4gID4ge1xuICAgIC8vIFNoYWRvd0RCIOyDneyEsSDtm4Qg7YWM7Iqk7Yq4IOynhO2WiVxuICAgIGNvbnN0IHRkYiA9IGtuZXgoU29uYW11LmRiQ29uZmlnLnRlc3QpO1xuICAgIGNvbnN0IHRkYkNvbm4gPSBTb25hbXUuZGJDb25maWcudGVzdFxuICAgICAgLmNvbm5lY3Rpb24gYXMgS25leC5NeVNxbDJDb25uZWN0aW9uQ29uZmlnO1xuICAgIGNvbnN0IHNoYWRvd0RhdGFiYXNlID0gdGRiQ29ubi5kYXRhYmFzZSArIFwiX19taWdyYXRpb25fc2hhZG93XCI7XG4gICAgY29uc3QgdG1wU3FsUGF0aCA9IGAvdG1wLyR7c2hhZG93RGF0YWJhc2V9LnNxbGA7XG5cbiAgICAvLyDthYzsiqTtirhEQiDrjaTtlIQg7ZuEIERhdGFiYXNl66qFIOy5mO2ZmFxuICAgIGNvbnNvbGUubG9nKFxuICAgICAgY2hhbGsubWFnZW50YShgJHt0ZGJDb25uLmRhdGFiYXNlfeydmCDrjbDsnbTthLAgJHt0bXBTcWxQYXRofeuhnCDrjaTtlIRgKVxuICAgICk7XG4gICAgZXhlY1N5bmMoXG4gICAgICBgbXlzcWxkdW1wIC1oJHt0ZGJDb25uLmhvc3R9IC1QJHt0ZGJDb25uLnBvcnQgPz8gMzMwNn0gLXUke3RkYkNvbm4udXNlcn0gLXAnJHt0ZGJDb25uLnBhc3N3b3JkfScgJHt0ZGJDb25uLmRhdGFiYXNlfSAtLXNpbmdsZS10cmFuc2FjdGlvbiAtLW5vLWNyZWF0ZS1kYiAtLXRyaWdnZXJzID4gJHt0bXBTcWxQYXRofTtgXG4gICAgKTtcbiAgICBleGVjU3luYyhcbiAgICAgIGBzZWQgLWknJyAtZSAncy9cXGAke3RkYkNvbm4uZGF0YWJhc2V9XFxgL1xcYCR7c2hhZG93RGF0YWJhc2V9XFxgL2cnICR7dG1wU3FsUGF0aH07YFxuICAgICk7XG5cbiAgICAvLyDquLDsobQgU2hhZG93REIg66as7IWLXG4gICAgY29uc29sZS5sb2coY2hhbGsubWFnZW50YShgJHtzaGFkb3dEYXRhYmFzZX0g66as7IWLYCkpO1xuICAgIGF3YWl0IHRkYi5yYXcoYERST1AgREFUQUJBU0UgSUYgRVhJU1RTIFxcYCR7c2hhZG93RGF0YWJhc2V9XFxgO2ApO1xuICAgIGF3YWl0IHRkYi5yYXcoYENSRUFURSBEQVRBQkFTRSBcXGAke3NoYWRvd0RhdGFiYXNlfVxcYDtgKTtcblxuICAgIC8vIFNoYWRvd0RCIO2FjOydtOu4lCArIOuNsOydtO2EsCDsg53shLFcbiAgICBjb25zb2xlLmxvZyhjaGFsay5tYWdlbnRhKGAke3NoYWRvd0RhdGFiYXNlfSDrjbDsnbTthLDrsqDsnbTsiqQg7IOd7ISxYCkpO1xuICAgIGV4ZWNTeW5jKFxuICAgICAgYG15c3FsIC1oJHt0ZGJDb25uLmhvc3R9IC1QJHt0ZGJDb25uLnBvcnQgPz8gMzMwNn0gLXUke3RkYkNvbm4udXNlcn0gLXAnJHt0ZGJDb25uLnBhc3N3b3JkfScgJHtzaGFkb3dEYXRhYmFzZX0gPCAke3RtcFNxbFBhdGh9O2BcbiAgICApO1xuXG4gICAgLy8gc2hhZG93IGRiIO2FjOyKpO2KuCDsp4TtlolcbiAgICBjb25zdCBzZGIgPSBrbmV4KHtcbiAgICAgIC4uLlNvbmFtdS5kYkNvbmZpZy50ZXN0LFxuICAgICAgY29ubmVjdGlvbjoge1xuICAgICAgICAuLi50ZGJDb25uLFxuICAgICAgICBkYXRhYmFzZTogc2hhZG93RGF0YWJhc2UsXG4gICAgICAgIHBhc3N3b3JkOiB0ZGJDb25uLnBhc3N3b3JkLFxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIC8vIHNoYWRvdyBkYiDthYzsiqTtirgg7KeE7ZaJXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IFtiYXRjaE5vLCBhcHBsaWVkXSA9IGF3YWl0IHNkYi5taWdyYXRlLmxhdGVzdCgpO1xuICAgICAgY29uc29sZS5sb2coY2hhbGsuZ3JlZW4oXCJTaGFkb3cgREIg7YWM7Iqk7Yq47JeQIOyEseqzte2WiOyKteuLiOuLpCFcIiksIHtcbiAgICAgICAgYmF0Y2hObyxcbiAgICAgICAgYXBwbGllZCxcbiAgICAgIH0pO1xuXG4gICAgICAvLyDsg53shLHtlZwgU2hhZG93IERCIOyCreygnFxuICAgICAgY29uc29sZS5sb2coY2hhbGsubWFnZW50YShgJHtzaGFkb3dEYXRhYmFzZX0g7IKt7KCcYCkpO1xuICAgICAgYXdhaXQgdGRiLnJhdyhgRFJPUCBEQVRBQkFTRSBJRiBFWElTVFMgXFxgJHtzaGFkb3dEYXRhYmFzZX1cXGA7YCk7XG5cbiAgICAgIHJldHVybiBbXG4gICAgICAgIHtcbiAgICAgICAgICBjb25uS2V5OiBcInNoYWRvd1wiLFxuICAgICAgICAgIGJhdGNoTm8sXG4gICAgICAgICAgYXBwbGllZCxcbiAgICAgICAgfSxcbiAgICAgIF07XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgY29uc29sZS5lcnJvcihlKTtcbiAgICAgIHRocm93IG5ldyBTZXJ2aWNlVW5hdmFpbGFibGVFeGNlcHRpb24oXCJTaGFkb3cgREIg7YWM7Iqk7Yq4IOynhO2WiSDspJEg7JeQ65+sXCIpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICBhd2FpdCB0ZGIuZGVzdHJveSgpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiDrqqjrk6AgRELrpbwg66Gk67Cx7ZWY6rOgIOyghOyytCDrp4jsnbTqt7jroIjsnbTshZgg7YyM7J287J2EIOyCreygnO2VqeuLiOuLpC5cbiAgICpcbiAgICogQ0xJ7JeQ7IScIOyCrOyaqeuQqeuLiOuLpC5cbiAgICpcbiAgICogQHJldHVybnNcbiAgICovXG4gIGFzeW5jIHJlc2V0QWxsKCkge1xuICAgIGNvbnN0IGFuc3dlciA9IGF3YWl0IHByb21wdHMoe1xuICAgICAgdHlwZTogXCJjb25maXJtXCIsXG4gICAgICBuYW1lOiBcInZhbHVlXCIsXG4gICAgICBtZXNzYWdlOiBcIuuqqOuToCBEQuulvCDroaTrsLHtlZjqs6Ag7KCE7LK0IOuniOydtOq3uOugiOydtOyFmCDtjIzsnbzsnYQg7IKt7KCc7ZWY7Iuc6rKg7Iq164uI6rmMP1wiLFxuICAgICAgaW5pdGlhbDogZmFsc2UsXG4gICAgfSk7XG4gICAgaWYgKGFuc3dlci52YWx1ZSA9PT0gZmFsc2UpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zb2xlLnRpbWUoY2hhbGsucmVkKFwicm9sbGJhY2stYWxsOlwiKSk7XG4gICAgY29uc3Qgcm9sbGJhY2tBbGxSZXN1bHQgPSBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgIHRoaXMudGFyZ2V0cy5hcHBseS5tYXAoYXN5bmMgKGRiKSA9PiB7XG4gICAgICAgIGF3YWl0IGRiLm1pZ3JhdGUuZm9yY2VGcmVlTWlncmF0aW9uc0xvY2soKTtcbiAgICAgICAgcmV0dXJuIGRiLm1pZ3JhdGUucm9sbGJhY2sodW5kZWZpbmVkLCB0cnVlKTtcbiAgICAgIH0pXG4gICAgKTtcbiAgICBjb25zb2xlLmxvZyh7IHJvbGxiYWNrQWxsUmVzdWx0IH0pO1xuICAgIGNvbnNvbGUudGltZUVuZChjaGFsay5yZWQoXCJyb2xsYmFjay1hbGw6XCIpKTtcblxuICAgIGNvbnN0IG1pZ3JhdGlvbnNEaXIgPSBgJHtTb25hbXUuYXBpUm9vdFBhdGh9L3NyYy9taWdyYXRpb25zYDtcbiAgICBjb25zb2xlLnRpbWUoY2hhbGsucmVkKFwiZGVsZXRlIG1pZ3JhdGlvbiBmaWxlc1wiKSk7XG4gICAgZXhlY1N5bmMoYHJtIC1mICR7bWlncmF0aW9uc0Rpcn0vKmApO1xuICAgIGV4ZWNTeW5jKGBybSAtZiAke21pZ3JhdGlvbnNEaXIucmVwbGFjZShcIi9zcmMvXCIsIFwiL2Rpc3QvXCIpfS8qYCk7XG4gICAgY29uc29sZS50aW1lRW5kKGNoYWxrLnJlZChcImRlbGV0ZSBtaWdyYXRpb24gZmlsZXNcIikpO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBjb21wYXJlTWlncmF0aW9ucyhcbiAgICBjb21wYXJlREI6IEtuZXhcbiAgKTogUHJvbWlzZTxHZW5NaWdyYXRpb25Db2RlW10+IHtcbiAgICAvLyBFbnRpdHkg7Iic7ZqM7ZWY7JesIOyLse2BrFxuICAgIGNvbnN0IGVudGl0eUlkcyA9IEVudGl0eU1hbmFnZXIuZ2V0QWxsSWRzKCk7XG5cbiAgICAvLyDsobDsnbjthYzsnbTruJQg7Y+s7ZWo7ZWY7JesIEVudGl0eeyXkOyEnCBNaWdyYXRpb25TZXQg7LaU7LacXG4gICAgY29uc3QgZW50aXR5U2V0c1dpdGhKb2luVGFibGUgPSBlbnRpdHlJZHNcbiAgICAgIC5maWx0ZXIoKGVudGl0eUlkKSA9PiBFbnRpdHlNYW5hZ2VyLmdldChlbnRpdHlJZCkucHJvcHMubGVuZ3RoID4gMClcbiAgICAgIC5tYXAoKGVudGl0eUlkKSA9PlxuICAgICAgICBnZXRNaWdyYXRpb25TZXRGcm9tRW50aXR5KEVudGl0eU1hbmFnZXIuZ2V0KGVudGl0eUlkKSlcbiAgICAgICk7XG5cbiAgICAvLyDsobDsnbjthYzsnbTruJTrp4wg7LaU7LacXG4gICAgY29uc3Qgam9pblRhYmxlc1dpdGhEdXAgPSBlbnRpdHlTZXRzV2l0aEpvaW5UYWJsZVxuICAgICAgLm1hcCgoZW50aXR5U2V0KSA9PiBlbnRpdHlTZXQuam9pblRhYmxlcylcbiAgICAgIC5mbGF0KCk7XG4gICAgLy8g7KSR67O1IOygnOqxsCAo7KSR67O17J24IOqyveyasCBpbmRleGVz66W8IOuzke2VqSlcbiAgICBjb25zdCBqb2luVGFibGVzID0gT2JqZWN0LnZhbHVlcyhcbiAgICAgIF8uZ3JvdXBCeShqb2luVGFibGVzV2l0aER1cCwgKGp0KSA9PiBqdC50YWJsZSlcbiAgICApLm1hcCgodGFibGVzKSA9PiB7XG4gICAgICBpZiAodGFibGVzLmxlbmd0aCA9PT0gMSkge1xuICAgICAgICByZXR1cm4gdGFibGVzWzBdO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgLi4udGFibGVzWzBdLFxuICAgICAgICBpbmRleGVzOiBfLnVuaXFCeShcbiAgICAgICAgICB0YWJsZXMuZmxhdE1hcCgodCkgPT4gdC5pbmRleGVzKSxcbiAgICAgICAgICAoaW5kZXgpID0+IFtpbmRleC50eXBlLCAuLi5pbmRleC5jb2x1bW5zLnNvcnQoKV0uam9pbihcIi1cIilcbiAgICAgICAgKSxcbiAgICAgIH07XG4gICAgfSk7XG5cbiAgICAvLyDsobDsnbjthYzsnbTruJQg7Y+s7ZWo7ZWY7JesIE1pZ3JhdGlvblNldCDrsLDsl7RcbiAgICBjb25zdCBlbnRpdHlTZXRzOiBNaWdyYXRpb25TZXRbXSA9IFtcbiAgICAgIC4uLmVudGl0eVNldHNXaXRoSm9pblRhYmxlLFxuICAgICAgLi4uam9pblRhYmxlcyxcbiAgICBdO1xuXG4gICAgY29uc3QgY29kZXM6IEdlbk1pZ3JhdGlvbkNvZGVbXSA9IChcbiAgICAgIGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgICBlbnRpdHlTZXRzLm1hcChhc3luYyAoZW50aXR5U2V0KSA9PiB7XG4gICAgICAgICAgY29uc3QgZGJTZXQgPSBhd2FpdCBnZXRNaWdyYXRpb25TZXRGcm9tREIoY29tcGFyZURCLCBlbnRpdHlTZXQudGFibGUpO1xuXG4gICAgICAgICAgaWYgKGRiU2V0ID09PSBudWxsKSB7XG4gICAgICAgICAgICAvLyDquLDsobQg7YWM7J2067iUIOyXhuydjCwg7IOI66GcIO2FjOydtOu4lCDsg53shLFcbiAgICAgICAgICAgIHJldHVybiBhd2FpdCBnZW5lcmF0ZUNyZWF0ZUNvZGUoZW50aXR5U2V0KTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8g6riw7KG0IO2FjOydtOu4lCDsobTsnqztlZjripQg7LyA7J207IqkXG4gICAgICAgICAgICByZXR1cm4gYXdhaXQgZ2VuZXJhdGVBbHRlckNvZGUoZW50aXR5U2V0LCBkYlNldCk7XG4gICAgICAgICAgfVxuICAgICAgICB9KVxuICAgICAgKVxuICAgICkuZmxhdCgpO1xuXG4gICAgLy8gbm9ybWFsIO2DgOyeheydtCDslZ7snLzroZwsIGZvcmVpZ27snbQg65Kk66GcXG4gICAgY29kZXMuc29ydCgoY29kZUEsIGNvZGVCKSA9PiB7XG4gICAgICBpZiAoY29kZUEudHlwZSA9PT0gXCJmb3JlaWduXCIgJiYgY29kZUIudHlwZSA9PSBcIm5vcm1hbFwiKSB7XG4gICAgICAgIHJldHVybiAxO1xuICAgICAgfSBlbHNlIGlmIChjb2RlQS50eXBlID09PSBcIm5vcm1hbFwiICYmIGNvZGVCLnR5cGUgPT09IFwiZm9yZWlnblwiKSB7XG4gICAgICAgIHJldHVybiAtMTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiAwO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgcmV0dXJuIGNvZGVzO1xuICB9XG5cbiAgLyoqXG4gICAqIOuniOydtOq3uOugiOydtOyFmCDrjIDsg4Eg7Luk64Sl7IWY7J2EIOyiheujjO2VqeuLiOuLpC5cbiAgICpcbiAgICogQ0xJ7JeQ7IScIOyCrOyaqeuQqeuLiOuLpC5cbiAgICpcbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IOyiheujjCDqsrDqs7xcbiAgICovXG4gIGFzeW5jIGRlc3Ryb3koKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICB0aGlzLnRhcmdldHMuYXBwbHkubWFwKChkYikgPT4ge1xuICAgICAgICByZXR1cm4gZGIuZGVzdHJveSgpO1xuICAgICAgfSlcbiAgICApO1xuICB9XG59XG4iXSwibmFtZXMiOlsiXyIsImtuZXgiLCJjaGFsayIsIkRhdGVUaW1lIiwibWtkaXIiLCJyZWFkZGlyIiwidW5saW5rIiwid3JpdGVGaWxlIiwiZXhpc3RzIiwicHJvbXB0cyIsImV4ZWNTeW5jIiwicGF0aCIsIkVudGl0eU1hbmFnZXIiLCJTb25hbXUiLCJTZXJ2aWNlVW5hdmFpbGFibGVFeGNlcHRpb24iLCJnZW5lcmF0ZUNyZWF0ZUNvZGUiLCJnZW5lcmF0ZUFsdGVyQ29kZSIsImdldE1pZ3JhdGlvblNldEZyb21EQiIsImdldE1pZ3JhdGlvblNldEZyb21FbnRpdHkiLCJNaWdyYXRvciIsInRhcmdldHMiLCJvcHRpb25zIiwiZGJDb25maWciLCJtb2RlIiwiZGV2REIiLCJkZXZlbG9wbWVudF9tYXN0ZXIiLCJ0ZXN0REIiLCJ0ZXN0IiwiZml4dHVyZUxvY2FsREIiLCJmaXh0dXJlX2xvY2FsIiwiYXBwbHlEQnMiLCJjb25uZWN0aW9uIiwiaG9zdCIsImZpeHR1cmVfcmVtb3RlIiwiZGF0YWJhc2UiLCJmaXh0dXJlUmVtb3RlREIiLCJwdXNoIiwiY29tcGFyZSIsInBlbmRpbmciLCJzaGFkb3ciLCJhcHBseSIsInByb2R1Y3Rpb25EQiIsInByb2R1Y3Rpb25fbWFzdGVyIiwiRXJyb3IiLCJnZXRNaWdyYXRpb25Db2RlcyIsInNyY01pZ3JhdGlvbnNEaXIiLCJqb2luIiwiYXBpUm9vdFBhdGgiLCJyZWN1cnNpdmUiLCJjb2RlcyIsImZpbHRlciIsImYiLCJlbmRzV2l0aCIsIm1hcCIsIm5hbWUiLCJyZXBsYWNlIiwic29ydCIsImEiLCJiIiwiZ2V0U3RhdHVzIiwiY29ubktleXMiLCJPYmplY3QiLCJrZXlzIiwia2V5Iiwic3RhdHVzZXMiLCJQcm9taXNlIiwiYWxsIiwiY29ubktleSIsImtuZXhPcHRpb25zIiwidENvbm4iLCJzdGF0dXMiLCJtaWdyYXRlIiwiZXJyIiwiY29uc29sZSIsIndhcm4iLCJ5ZWxsb3ciLCJKU09OIiwic3RyaW5naWZ5IiwiZmRMaXN0IiwibGlzdCIsImZkIiwiZmlsZSIsImN1cnJlbnRWZXJzaW9uIiwiZGVzdHJveSIsImNvbm5TdHJpbmciLCJ1c2VyIiwicG9ydCIsInByZXBhcmVkQ29kZXMiLCJzdGF0dXMwY29ubiIsImZpbmQiLCJ1bmRlZmluZWQiLCJjb21wYXJlREJjb25uIiwiZ2VuQ29kZXMiLCJjb21wYXJlTWlncmF0aW9ucyIsImNvbm5zIiwicnVuQWN0aW9uIiwiYWN0aW9uIiwiY29uZmlncyIsInVuaXFCeSIsInRhcmdldCIsImMiLCJjb25maWciLCJyZXN1bHQiLCJiYXRjaE5vIiwiYXBwbGllZCIsImxhdGVzdCIsInJvbGxiYWNrIiwiZGVsQ29kZXMiLCJjb2RlTmFtZXMiLCJzb21lIiwiY29ubiIsImNvZGVOYW1lIiwiaW5jbHVkZXMiLCJkZWxGaWxlcyIsInJlcyIsImRlbEZpbGUiLCJsb2ciLCJyZWQiLCJzdW0iLCJnZW5lcmF0ZVByZXBhcmVkQ29kZXMiLCJsZW5ndGgiLCJncmVlbiIsIm1pZ3JhdGlvbnNEaXIiLCJpbmRleCIsInBjb2RlIiwiZW50cmllcyIsImZvcm1hdHRlZCIsImRhdGVUYWciLCJsb2NhbCIsInBsdXMiLCJzZWNvbmRzIiwidG9Gb3JtYXQiLCJmaWxlUGF0aCIsInRpdGxlIiwiY2xlYXJQZW5kaW5nTGlzdCIsInBlbmRpbmdMaXN0IiwiZGVsTGlzdCIsImRmIiwicCIsImNoZWNrIiwidGFibGUiLCJydW4iLCJhbnN3ZXIiLCJ0eXBlIiwibWVzc2FnZSIsImluaXRpYWwiLCJ2YWx1ZSIsInRpbWUiLCJibHVlIiwicnVuU2hhZG93VGVzdCIsInRpbWVFbmQiLCJhcHBseURiIiwibGFiZWwiLCJjbGllbnQiLCJjb25uZWN0aW9uU2V0dGluZ3MiLCJjb2RlIiwicm9sbGJhY2tBbGxSZXN1bHQiLCJkYiIsImZvcmNlRnJlZU1pZ3JhdGlvbnNMb2NrIiwiZGlyIiwiZGVwdGgiLCJ0ZGIiLCJ0ZGJDb25uIiwic2hhZG93RGF0YWJhc2UiLCJ0bXBTcWxQYXRoIiwibWFnZW50YSIsInBhc3N3b3JkIiwicmF3Iiwic2RiIiwiZSIsImVycm9yIiwicmVzZXRBbGwiLCJjb21wYXJlREIiLCJlbnRpdHlJZHMiLCJnZXRBbGxJZHMiLCJlbnRpdHlTZXRzV2l0aEpvaW5UYWJsZSIsImVudGl0eUlkIiwiZ2V0IiwicHJvcHMiLCJqb2luVGFibGVzV2l0aER1cCIsImVudGl0eVNldCIsImpvaW5UYWJsZXMiLCJmbGF0IiwidmFsdWVzIiwiZ3JvdXBCeSIsImp0IiwidGFibGVzIiwiaW5kZXhlcyIsImZsYXRNYXAiLCJ0IiwiY29sdW1ucyIsImVudGl0eVNldHMiLCJkYlNldCIsImNvZGVBIiwiY29kZUIiXSwibWFwcGluZ3MiOiJBQUFBLFlBQVlBLE9BQU8sWUFB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
|