sonamu 0.8.26 → 0.9.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/bin/cli.js +60 -13
- package/dist/_virtual/rolldown_runtime.js +39 -0
- package/dist/ai/agents/agent.d.ts +3 -3
- package/dist/ai/agents/agent.d.ts.map +1 -1
- package/dist/ai/agents/agent.js +76 -73
- package/dist/ai/agents/index.js +3 -3
- package/dist/ai/agents/types.d.ts +3 -3
- package/dist/ai/agents/types.d.ts.map +1 -1
- package/dist/ai/agents/types.js +1 -3
- package/dist/ai/index.js +3 -2
- package/dist/ai/providers/rtzr/api.js +25 -25
- package/dist/ai/providers/rtzr/error.js +25 -26
- package/dist/ai/providers/rtzr/index.js +5 -5
- package/dist/ai/providers/rtzr/model.d.ts +1 -1
- package/dist/ai/providers/rtzr/model.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/model.js +117 -133
- package/dist/ai/providers/rtzr/options.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/options.js +35 -41
- package/dist/ai/providers/rtzr/provider.d.ts +1 -1
- package/dist/ai/providers/rtzr/provider.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/provider.js +53 -51
- package/dist/ai/providers/rtzr/utils.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/utils.js +84 -84
- package/dist/api/base-frame.d.ts +2 -2
- package/dist/api/base-frame.d.ts.map +1 -1
- package/dist/api/base-frame.js +29 -19
- package/dist/api/caster.d.ts +1 -1
- package/dist/api/caster.d.ts.map +1 -1
- package/dist/api/caster.js +51 -61
- package/dist/api/code-converters.d.ts +4 -3
- package/dist/api/code-converters.d.ts.map +1 -1
- package/dist/api/code-converters.js +226 -249
- package/dist/api/config.d.ts +17 -17
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +37 -30
- package/dist/api/context.d.ts +10 -10
- package/dist/api/context.d.ts.map +1 -1
- package/dist/api/context.js +8 -2
- package/dist/api/decorators.d.ts +8 -8
- package/dist/api/decorators.d.ts.map +1 -1
- package/dist/api/decorators.js +245 -268
- package/dist/api/index.js +39 -7
- package/dist/api/secret.js +22 -15
- package/dist/api/sonamu.d.ts +15 -15
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +1012 -1131
- package/dist/api/validator.js +88 -79
- package/dist/auth/auth-generator.d.ts.map +1 -1
- package/dist/auth/auth-generator.js +203 -200
- package/dist/auth/better-auth-entities.d.ts +2 -2
- package/dist/auth/better-auth-entities.d.ts.map +1 -1
- package/dist/auth/better-auth-entities.js +369 -429
- package/dist/auth/index.js +21 -6
- package/dist/auth/knex-adapter.d.ts +2 -2
- package/dist/auth/knex-adapter.d.ts.map +1 -1
- package/dist/auth/knex-adapter.js +153 -157
- package/dist/auth/plugins/entity-definitions/admin.d.ts +1 -1
- package/dist/auth/plugins/entity-definitions/admin.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/admin.js +58 -56
- package/dist/auth/plugins/entity-definitions/anonymous.d.ts +1 -1
- package/dist/auth/plugins/entity-definitions/anonymous.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/anonymous.js +20 -20
- package/dist/auth/plugins/entity-definitions/api-key.d.ts +1 -1
- package/dist/auth/plugins/entity-definitions/api-key.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/api-key.js +185 -196
- package/dist/auth/plugins/entity-definitions/index.d.ts +1 -1
- package/dist/auth/plugins/entity-definitions/index.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/index.js +26 -29
- package/dist/auth/plugins/entity-definitions/jwt.d.ts +1 -1
- package/dist/auth/plugins/entity-definitions/jwt.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/jwt.js +62 -64
- package/dist/auth/plugins/entity-definitions/organization.d.ts +1 -1
- package/dist/auth/plugins/entity-definitions/organization.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/organization.js +362 -421
- package/dist/auth/plugins/entity-definitions/passkey.d.ts +1 -1
- package/dist/auth/plugins/entity-definitions/passkey.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/passkey.js +115 -126
- package/dist/auth/plugins/entity-definitions/phone-number.d.ts +1 -1
- package/dist/auth/plugins/entity-definitions/phone-number.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/phone-number.js +31 -40
- package/dist/auth/plugins/entity-definitions/sso.d.ts +1 -1
- package/dist/auth/plugins/entity-definitions/sso.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/sso.js +94 -107
- package/dist/auth/plugins/entity-definitions/two-factor.d.ts +1 -1
- package/dist/auth/plugins/entity-definitions/two-factor.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/two-factor.js +78 -92
- package/dist/auth/plugins/entity-definitions/types.d.ts +1 -1
- package/dist/auth/plugins/entity-definitions/types.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/types.js +1 -10
- package/dist/auth/plugins/entity-definitions/username.d.ts +1 -1
- package/dist/auth/plugins/entity-definitions/username.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/username.js +31 -40
- package/dist/auth/plugins/index.js +12 -3
- package/dist/auth/plugins/wrappers/admin.d.ts +2 -2
- package/dist/auth/plugins/wrappers/admin.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/admin.js +28 -29
- package/dist/auth/plugins/wrappers/anonymous.d.ts +2 -1
- package/dist/auth/plugins/wrappers/anonymous.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/anonymous.js +23 -22
- package/dist/auth/plugins/wrappers/api-key.d.ts +2 -1
- package/dist/auth/plugins/wrappers/api-key.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/api-key.js +39 -34
- package/dist/auth/plugins/wrappers/index.js +11 -11
- package/dist/auth/plugins/wrappers/jwt.d.ts +2 -1
- package/dist/auth/plugins/wrappers/jwt.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/jwt.js +31 -26
- package/dist/auth/plugins/wrappers/organization.d.ts +2 -1
- package/dist/auth/plugins/wrappers/organization.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/organization.js +65 -62
- package/dist/auth/plugins/wrappers/passkey.d.ts +2 -1
- package/dist/auth/plugins/wrappers/passkey.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/passkey.js +33 -28
- package/dist/auth/plugins/wrappers/phone-number.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/phone-number.js +26 -23
- package/dist/auth/plugins/wrappers/sso.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/sso.js +37 -31
- package/dist/auth/plugins/wrappers/two-factor.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/two-factor.js +31 -28
- package/dist/auth/plugins/wrappers/username.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/username.js +23 -23
- package/dist/bin/build-config.js +31 -31
- package/dist/bin/cli.js +1063 -1204
- package/dist/bin/fixture.d.ts.map +1 -1
- package/dist/bin/fixture.js +266 -259
- package/dist/bin/hmr-hook-register.d.ts.map +1 -1
- package/dist/bin/hmr-hook-register.js +19 -18
- package/dist/bin/test-command.d.ts.map +1 -1
- package/dist/bin/test-command.js +180 -177
- package/dist/bin/ts-loader-register.js +13 -6
- package/dist/bin/ts-loader-registration.d.ts.map +1 -1
- package/dist/bin/ts-loader-registration.js +28 -38
- package/dist/cache/cache-manager.d.ts +1 -1
- package/dist/cache/cache-manager.d.ts.map +1 -1
- package/dist/cache/cache-manager.js +20 -15
- package/dist/cache/decorator.d.ts +1 -1
- package/dist/cache/decorator.d.ts.map +1 -1
- package/dist/cache/decorator.js +84 -76
- package/dist/cache/drivers.js +21 -34
- package/dist/cache/index.js +10 -7
- package/dist/cache/types.d.ts +2 -2
- package/dist/cache/types.d.ts.map +1 -1
- package/dist/cache/types.js +1 -6
- package/dist/cache-control/cache-control.d.ts +2 -2
- package/dist/cache-control/cache-control.d.ts.map +1 -1
- package/dist/cache-control/cache-control.js +106 -122
- package/dist/cache-control/types.d.ts +2 -2
- package/dist/cache-control/types.d.ts.map +1 -1
- package/dist/cache-control/types.js +1 -19
- package/dist/compress/compress.d.ts +1 -1
- package/dist/compress/compress.d.ts.map +1 -1
- package/dist/compress/compress.js +58 -56
- package/dist/compress/index.js +7 -2
- package/dist/compress/types.js +1 -11
- package/dist/cone/cone-generator.d.ts +1 -1
- package/dist/cone/cone-generator.d.ts.map +1 -1
- package/dist/cone/cone-generator.js +216 -219
- package/dist/database/_batch_update.d.ts +1 -1
- package/dist/database/_batch_update.d.ts.map +1 -1
- package/dist/database/_batch_update.js +107 -102
- package/dist/database/base-model.d.ts +8 -9
- package/dist/database/base-model.d.ts.map +1 -1
- package/dist/database/base-model.js +371 -392
- package/dist/database/base-model.types.d.ts +5 -5
- package/dist/database/base-model.types.d.ts.map +1 -1
- package/dist/database/base-model.types.js +1 -20
- package/dist/database/db.d.ts +5 -2
- package/dist/database/db.d.ts.map +1 -1
- package/dist/database/db.js +185 -171
- package/dist/database/knex.d.ts +1 -1
- package/dist/database/knex.d.ts.map +1 -1
- package/dist/database/knex.js +48 -42
- package/dist/database/puri-subset.types.d.ts +6 -7
- package/dist/database/puri-subset.types.d.ts.map +1 -1
- package/dist/database/puri-subset.types.js +1 -16
- package/dist/database/puri-wrapper.d.ts +6 -6
- package/dist/database/puri-wrapper.d.ts.map +1 -1
- package/dist/database/puri-wrapper.js +99 -101
- package/dist/database/puri.d.ts +4 -5
- package/dist/database/puri.d.ts.map +1 -1
- package/dist/database/puri.js +1021 -1227
- package/dist/database/puri.types.d.ts +6 -6
- package/dist/database/puri.types.d.ts.map +1 -1
- package/dist/database/puri.types.js +15 -6
- package/dist/database/transaction-context.d.ts +2 -2
- package/dist/database/transaction-context.d.ts.map +1 -1
- package/dist/database/transaction-context.js +22 -13
- package/dist/database/upsert-builder.d.ts +3 -3
- package/dist/database/upsert-builder.d.ts.map +1 -1
- package/dist/database/upsert-builder.js +405 -465
- package/dist/dict/en.js +72 -74
- package/dist/dict/index.js +13 -13
- package/dist/dict/ko.js +72 -74
- package/dist/dict/rc-keys.js +150 -168
- package/dist/dict/sd.d.ts +3 -1
- package/dist/dict/sd.d.ts.map +1 -1
- package/dist/dict/sd.js +54 -40
- package/dist/dict/sonamu-dictionary.d.ts +1 -1
- package/dist/dict/sonamu-dictionary.d.ts.map +1 -1
- package/dist/dict/sonamu-dictionary.js +887 -955
- package/dist/dict/types.js +1 -7
- package/dist/dict/utils.js +26 -24
- package/dist/entity/entity-manager.d.ts +9 -9
- package/dist/entity/entity-manager.d.ts.map +1 -1
- package/dist/entity/entity-manager.js +226 -223
- package/dist/entity/entity-template-cone.d.ts +1 -1
- package/dist/entity/entity-template-cone.d.ts.map +1 -1
- package/dist/entity/entity-template-cone.js +152 -151
- package/dist/entity/entity.d.ts.map +1 -1
- package/dist/entity/entity.js +952 -1089
- package/dist/exceptions/error-handler.d.ts +1 -1
- package/dist/exceptions/error-handler.d.ts.map +1 -1
- package/dist/exceptions/error-handler.js +32 -27
- package/dist/exceptions/so-exceptions.d.ts +1 -1
- package/dist/exceptions/so-exceptions.d.ts.map +1 -1
- package/dist/exceptions/so-exceptions.js +61 -68
- package/dist/filter/index.js +9 -3
- package/dist/filter/types.js +92 -88
- package/dist/filter/utils.d.ts +1 -1
- package/dist/filter/utils.d.ts.map +1 -1
- package/dist/filter/utils.js +147 -161
- package/dist/index.js +87 -40
- package/dist/logger/category.d.ts.map +1 -1
- package/dist/logger/category.js +30 -29
- package/dist/logger/configure.d.ts.map +1 -1
- package/dist/logger/configure.js +83 -107
- package/dist/migration/code-generation.d.ts +2 -2
- package/dist/migration/code-generation.d.ts.map +1 -1
- package/dist/migration/code-generation.js +1385 -1578
- package/dist/migration/migration-set.d.ts +1 -1
- package/dist/migration/migration-set.d.ts.map +1 -1
- package/dist/migration/migration-set.js +177 -227
- package/dist/migration/migrator.d.ts +4 -3
- package/dist/migration/migrator.d.ts.map +1 -1
- package/dist/migration/migrator.js +340 -345
- package/dist/migration/postgresql-schema-reader.d.ts +2 -2
- package/dist/migration/postgresql-schema-reader.d.ts.map +1 -1
- package/dist/migration/postgresql-schema-reader.js +506 -564
- package/dist/migration/slack-confirm.d.ts +2 -2
- package/dist/migration/slack-confirm.d.ts.map +1 -1
- package/dist/migration/slack-confirm.js +205 -193
- package/dist/migration/types.d.ts +2 -2
- package/dist/migration/types.d.ts.map +1 -1
- package/dist/migration/types.js +1 -3
- package/dist/naite/messaging-types.d.ts +1 -0
- package/dist/naite/messaging-types.d.ts.map +1 -1
- package/dist/naite/messaging-types.js +1 -7
- package/dist/naite/naite-reporter.d.ts +2 -2
- package/dist/naite/naite-reporter.d.ts.map +1 -1
- package/dist/naite/naite-reporter.js +127 -120
- package/dist/naite/naite.d.ts +3 -2
- package/dist/naite/naite.d.ts.map +1 -1
- package/dist/naite/naite.js +266 -300
- package/dist/ssr/index.d.ts +2 -2
- package/dist/ssr/index.d.ts.map +1 -1
- package/dist/ssr/index.js +13 -3
- package/dist/ssr/registry.d.ts +1 -1
- package/dist/ssr/registry.d.ts.map +1 -1
- package/dist/ssr/registry.js +45 -37
- package/dist/ssr/renderer.d.ts +4 -4
- package/dist/ssr/renderer.d.ts.map +1 -1
- package/dist/ssr/renderer.js +84 -91
- package/dist/ssr/types.d.ts +2 -2
- package/dist/ssr/types.d.ts.map +1 -1
- package/dist/ssr/types.js +1 -3
- package/dist/storage/base-file.js +54 -41
- package/dist/storage/buffered-file.d.ts +2 -2
- package/dist/storage/buffered-file.d.ts.map +1 -1
- package/dist/storage/buffered-file.js +51 -44
- package/dist/storage/drivers.d.ts +2 -2
- package/dist/storage/drivers.d.ts.map +1 -1
- package/dist/storage/drivers.js +12 -7
- package/dist/storage/index.js +14 -7
- package/dist/storage/s3-driver.d.ts +2 -2
- package/dist/storage/s3-driver.d.ts.map +1 -1
- package/dist/storage/s3-driver.js +52 -48
- package/dist/storage/storage-manager.d.ts +2 -2
- package/dist/storage/storage-manager.d.ts.map +1 -1
- package/dist/storage/storage-manager.js +33 -25
- package/dist/storage/types.d.ts +2 -2
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/types.js +1 -5
- package/dist/storage/uploaded-file.d.ts +1 -1
- package/dist/storage/uploaded-file.d.ts.map +1 -1
- package/dist/storage/uploaded-file.js +45 -35
- package/dist/stream/index.js +7 -2
- package/dist/stream/sse.d.ts +2 -2
- package/dist/stream/sse.d.ts.map +1 -1
- package/dist/stream/sse.js +72 -67
- package/dist/syncer/api-parser.d.ts +1 -1
- package/dist/syncer/api-parser.d.ts.map +1 -1
- package/dist/syncer/api-parser.js +224 -245
- package/dist/syncer/checksum.d.ts +1 -1
- package/dist/syncer/checksum.d.ts.map +1 -1
- package/dist/syncer/checksum.js +86 -72
- package/dist/syncer/code-generator.d.ts +2 -2
- package/dist/syncer/code-generator.d.ts.map +1 -1
- package/dist/syncer/code-generator.js +154 -160
- package/dist/syncer/entity-operations.d.ts +1 -1
- package/dist/syncer/entity-operations.d.ts.map +1 -1
- package/dist/syncer/entity-operations.js +63 -54
- package/dist/syncer/file-patterns.d.ts +1 -1
- package/dist/syncer/file-patterns.d.ts.map +1 -1
- package/dist/syncer/file-patterns.js +38 -38
- package/dist/syncer/index.js +19 -8
- package/dist/syncer/module-loader.d.ts +5 -5
- package/dist/syncer/module-loader.d.ts.map +1 -1
- package/dist/syncer/module-loader.js +83 -78
- package/dist/syncer/syncer-actions.d.ts +2 -2
- package/dist/syncer/syncer-actions.d.ts.map +1 -1
- package/dist/syncer/syncer-actions.js +76 -91
- package/dist/syncer/syncer.d.ts +7 -6
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/syncer/syncer.js +426 -492
- package/dist/tasks/decorator.d.ts +3 -3
- package/dist/tasks/decorator.d.ts.map +1 -1
- package/dist/tasks/decorator.js +32 -28
- package/dist/tasks/step-wrapper.d.ts +1 -1
- package/dist/tasks/step-wrapper.d.ts.map +1 -1
- package/dist/tasks/step-wrapper.js +42 -41
- package/dist/tasks/workflow-manager.d.ts +2 -2
- package/dist/tasks/workflow-manager.d.ts.map +1 -1
- package/dist/tasks/workflow-manager.js +192 -221
- package/dist/template/entity-converter.d.ts +1 -1
- package/dist/template/entity-converter.d.ts.map +1 -1
- package/dist/template/entity-converter.js +103 -103
- package/dist/template/helpers.d.ts.map +1 -1
- package/dist/template/helpers.js +163 -163
- package/dist/template/implementations/entity.template.d.ts +1 -1
- package/dist/template/implementations/entity.template.d.ts.map +1 -1
- package/dist/template/implementations/entity.template.js +76 -85
- package/dist/template/implementations/entry-server.template.d.ts +1 -1
- package/dist/template/implementations/entry-server.template.d.ts.map +1 -1
- package/dist/template/implementations/entry-server.template.js +32 -27
- package/dist/template/implementations/generated.template.d.ts +1 -1
- package/dist/template/implementations/generated.template.d.ts.map +1 -1
- package/dist/template/implementations/generated.template.js +254 -275
- package/dist/template/implementations/generated_http.template.d.ts +2 -2
- package/dist/template/implementations/generated_http.template.d.ts.map +1 -1
- package/dist/template/implementations/generated_http.template.js +114 -133
- package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
- package/dist/template/implementations/generated_sso.template.js +249 -275
- package/dist/template/implementations/init_types.template.d.ts +1 -1
- package/dist/template/implementations/init_types.template.d.ts.map +1 -1
- package/dist/template/implementations/init_types.template.js +40 -34
- package/dist/template/implementations/model.template.d.ts +1 -1
- package/dist/template/implementations/model.template.d.ts.map +1 -1
- package/dist/template/implementations/model.template.js +56 -53
- package/dist/template/implementations/model_test.template.d.ts +1 -1
- package/dist/template/implementations/model_test.template.d.ts.map +1 -1
- package/dist/template/implementations/model_test.template.js +32 -24
- package/dist/template/implementations/queries.template.d.ts +1 -1
- package/dist/template/implementations/queries.template.d.ts.map +1 -1
- package/dist/template/implementations/queries.template.js +84 -89
- package/dist/template/implementations/sd.template.d.ts +1 -1
- package/dist/template/implementations/sd.template.d.ts.map +1 -1
- package/dist/template/implementations/sd.template.js +137 -144
- package/dist/template/implementations/services.template.d.ts +1 -1
- package/dist/template/implementations/services.template.d.ts.map +1 -1
- package/dist/template/implementations/services.template.js +164 -189
- package/dist/template/implementations/view_form.template.d.ts +1 -1
- package/dist/template/implementations/view_form.template.d.ts.map +1 -1
- package/dist/template/implementations/view_form.template.js +258 -285
- package/dist/template/implementations/view_id_all_select.template.d.ts +1 -1
- package/dist/template/implementations/view_id_all_select.template.d.ts.map +1 -1
- package/dist/template/implementations/view_id_all_select.template.js +31 -25
- package/dist/template/implementations/view_list.template.d.ts +1 -1
- package/dist/template/implementations/view_list.template.d.ts.map +1 -1
- package/dist/template/implementations/view_list.template.js +304 -355
- package/dist/template/implementations/view_search_input.template.d.ts +1 -1
- package/dist/template/implementations/view_search_input.template.d.ts.map +1 -1
- package/dist/template/implementations/view_search_input.template.js +31 -27
- package/dist/template/index.js +21 -7
- package/dist/template/template-manager.d.ts +1 -1
- package/dist/template/template-manager.d.ts.map +1 -1
- package/dist/template/template-manager.js +132 -123
- package/dist/template/template-types.js +8 -6
- package/dist/template/template.d.ts +2 -2
- package/dist/template/template.d.ts.map +1 -1
- package/dist/template/template.js +73 -68
- package/dist/template/zod-converter.d.ts.map +1 -1
- package/dist/template/zod-converter.js +603 -657
- package/dist/testing/_relation-graph.d.ts +1 -1
- package/dist/testing/_relation-graph.d.ts.map +1 -1
- package/dist/testing/_relation-graph.js +93 -88
- package/dist/testing/bootstrap.d.ts +22 -13
- package/dist/testing/bootstrap.d.ts.map +1 -1
- package/dist/testing/bootstrap.js +114 -114
- package/dist/testing/data-explorer.d.ts +3 -3
- package/dist/testing/data-explorer.d.ts.map +1 -1
- package/dist/testing/data-explorer.js +237 -265
- package/dist/testing/dev-test-routes.d.ts +2 -2
- package/dist/testing/dev-test-routes.d.ts.map +1 -1
- package/dist/testing/dev-test-routes.js +258 -249
- package/dist/testing/dev-vitest-manager.d.ts +1 -1
- package/dist/testing/dev-vitest-manager.d.ts.map +1 -1
- package/dist/testing/dev-vitest-manager.js +514 -539
- package/dist/testing/faker-mappings.js +422 -420
- package/dist/testing/fixture-generator.d.ts +3 -3
- package/dist/testing/fixture-generator.d.ts.map +1 -1
- package/dist/testing/fixture-generator.js +1216 -1346
- package/dist/testing/fixture-loader.js +26 -25
- package/dist/testing/fixture-manager.d.ts +3 -3
- package/dist/testing/fixture-manager.d.ts.map +1 -1
- package/dist/testing/fixture-manager.js +706 -776
- package/dist/testing/global-setup.js +53 -49
- package/dist/testing/index.js +19 -11
- package/dist/testing/naite-vitest-reporter.js +18 -13
- package/dist/testing/parallel-db-manager.d.ts +1 -1
- package/dist/testing/parallel-db-manager.d.ts.map +1 -1
- package/dist/testing/parallel-db-manager.js +63 -78
- package/dist/testing/vitest-helpers.d.ts +1 -1
- package/dist/testing/vitest-helpers.d.ts.map +1 -1
- package/dist/testing/vitest-helpers.js +37 -33
- package/dist/types/types.d.ts +28 -28
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +764 -890
- package/dist/ui/ai-api.d.ts +1 -1
- package/dist/ui/ai-api.d.ts.map +1 -1
- package/dist/ui/ai-api.js +52 -42
- package/dist/ui/ai-client.d.ts +1 -2
- package/dist/ui/ai-client.d.ts.map +1 -1
- package/dist/ui/ai-client.js +353 -388
- package/dist/ui/api.d.ts +1 -1
- package/dist/ui/api.d.ts.map +1 -1
- package/dist/ui/api.js +903 -1145
- package/dist/ui/cdd-service.d.ts +1 -1
- package/dist/ui/cdd-service.d.ts.map +1 -1
- package/dist/ui/cdd-service.js +406 -407
- package/dist/ui/cdd-types.js +1 -3
- package/dist/ui-web/assets/index-C-Zz-wYg.css +1 -0
- package/dist/ui-web/assets/index-DejDON8K.js +238 -0
- package/dist/ui-web/index.html +3 -3
- package/dist/utils/async-utils.js +57 -45
- package/dist/utils/console-util.d.ts.map +1 -1
- package/dist/utils/console-util.js +104 -87
- package/dist/utils/controller.js +26 -19
- package/dist/utils/esm-utils.js +49 -38
- package/dist/utils/formatter.d.ts +1 -2
- package/dist/utils/formatter.d.ts.map +1 -1
- package/dist/utils/formatter.js +89 -115
- package/dist/utils/fs-utils.d.ts.map +1 -1
- package/dist/utils/fs-utils.js +68 -65
- package/dist/utils/lodash-able.js +11 -4
- package/dist/utils/model.d.ts +1 -1
- package/dist/utils/model.d.ts.map +1 -1
- package/dist/utils/model.js +21 -19
- package/dist/utils/object-utils.js +148 -186
- package/dist/utils/path-utils.js +67 -57
- package/dist/utils/process-utils.d.ts.map +1 -1
- package/dist/utils/process-utils.js +37 -31
- package/dist/utils/sql-parser.d.ts +1 -1
- package/dist/utils/sql-parser.d.ts.map +1 -1
- package/dist/utils/sql-parser.js +40 -40
- package/dist/utils/type-utils.js +44 -43
- package/dist/utils/utils.d.ts +2 -3
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js +81 -93
- package/dist/utils/zod-error.d.ts +1 -1
- package/dist/utils/zod-error.d.ts.map +1 -1
- package/dist/utils/zod-error.js +24 -17
- package/dist/vector/chunking.d.ts +1 -1
- package/dist/vector/chunking.d.ts.map +1 -1
- package/dist/vector/chunking.js +100 -94
- package/dist/vector/config.d.ts +1 -1
- package/dist/vector/config.d.ts.map +1 -1
- package/dist/vector/config.js +76 -78
- package/dist/vector/embedding.d.ts +1 -1
- package/dist/vector/embedding.d.ts.map +1 -1
- package/dist/vector/embedding.js +128 -125
- package/dist/vector/index.js +5 -5
- package/dist/vector/types.js +1 -5
- package/package.json +31 -36
- package/src/ai/agents/agent.ts +12 -5
- package/src/ai/agents/types.ts +5 -5
- package/src/ai/providers/rtzr/model.ts +8 -10
- package/src/ai/providers/rtzr/options.ts +2 -1
- package/src/ai/providers/rtzr/provider.ts +5 -3
- package/src/ai/providers/rtzr/utils.ts +2 -7
- package/src/api/__tests__/config.test.ts +15 -8
- package/src/api/base-frame.ts +5 -3
- package/src/api/caster.ts +7 -6
- package/src/api/code-converters.ts +23 -26
- package/src/api/config.ts +23 -17
- package/src/api/context.ts +18 -11
- package/src/api/decorators.ts +17 -18
- package/src/api/sonamu.ts +44 -49
- package/src/auth/auth-generator.ts +4 -6
- package/src/auth/better-auth-entities.ts +3 -2
- package/src/auth/knex-adapter.ts +6 -5
- package/src/auth/plugins/entity-definitions/admin.ts +1 -1
- package/src/auth/plugins/entity-definitions/anonymous.ts +1 -1
- package/src/auth/plugins/entity-definitions/api-key.ts +1 -1
- package/src/auth/plugins/entity-definitions/index.ts +1 -1
- package/src/auth/plugins/entity-definitions/jwt.ts +1 -1
- package/src/auth/plugins/entity-definitions/organization.ts +1 -1
- package/src/auth/plugins/entity-definitions/passkey.ts +1 -1
- package/src/auth/plugins/entity-definitions/phone-number.ts +1 -1
- package/src/auth/plugins/entity-definitions/sso.ts +1 -1
- package/src/auth/plugins/entity-definitions/two-factor.ts +1 -1
- package/src/auth/plugins/entity-definitions/types.ts +1 -1
- package/src/auth/plugins/entity-definitions/username.ts +1 -1
- package/src/auth/plugins/wrappers/admin.ts +3 -1
- package/src/auth/plugins/wrappers/anonymous.ts +3 -1
- package/src/auth/plugins/wrappers/api-key.ts +3 -1
- package/src/auth/plugins/wrappers/jwt.ts +3 -1
- package/src/auth/plugins/wrappers/organization.ts +3 -1
- package/src/auth/plugins/wrappers/passkey.ts +3 -1
- package/src/auth/plugins/wrappers/phone-number.ts +3 -1
- package/src/auth/plugins/wrappers/sso.ts +2 -1
- package/src/auth/plugins/wrappers/two-factor.ts +3 -1
- package/src/auth/plugins/wrappers/username.ts +3 -1
- package/src/bin/__tests__/ts-loader-register.test.ts +7 -12
- package/src/bin/build-config.ts +3 -3
- package/src/bin/cli.ts +27 -25
- package/src/bin/fixture.ts +4 -2
- package/src/bin/hmr-hook-register.ts +1 -0
- package/src/bin/test-command.ts +4 -2
- package/src/bin/ts-loader-registration.ts +6 -22
- package/src/cache/cache-manager.ts +2 -1
- package/src/cache/decorator.ts +2 -2
- package/src/cache/types.ts +3 -3
- package/src/cache-control/cache-control.ts +3 -2
- package/src/cache-control/types.ts +2 -2
- package/src/compress/compress.ts +1 -1
- package/src/cone/cone-generator.ts +5 -3
- package/src/database/_batch_update.ts +1 -1
- package/src/database/base-model.ts +20 -14
- package/src/database/base-model.types.ts +12 -11
- package/src/database/db.ts +56 -21
- package/src/database/knex.ts +2 -2
- package/src/database/puri-subset.test-d.ts +33 -32
- package/src/database/puri-subset.types.ts +6 -7
- package/src/database/puri-wrapper.ts +29 -26
- package/src/database/puri.ts +36 -34
- package/src/database/puri.types.test-d.ts +6 -5
- package/src/database/puri.types.ts +9 -12
- package/src/database/transaction-context.ts +2 -2
- package/src/database/upsert-builder.ts +17 -10
- package/src/dict/sd.ts +17 -4
- package/src/dict/sonamu-dictionary.ts +23 -17
- package/src/entity/entity-manager.ts +9 -7
- package/src/entity/entity-template-cone.ts +10 -3
- package/src/entity/entity.ts +20 -16
- package/src/exceptions/error-handler.ts +2 -1
- package/src/exceptions/so-exceptions.ts +1 -1
- package/src/filter/utils.ts +3 -2
- package/src/logger/category.ts +1 -0
- package/src/logger/configure.ts +5 -5
- package/src/migration/__tests__/code-generation.search-text.test.ts +2 -3
- package/src/migration/code-generation.ts +26 -25
- package/src/migration/migration-set.ts +16 -18
- package/src/migration/migrator.ts +38 -33
- package/src/migration/postgresql-schema-reader.ts +12 -12
- package/src/migration/slack-confirm.ts +5 -4
- package/src/migration/types.ts +2 -2
- package/src/naite/messaging-types.ts +1 -1
- package/src/naite/naite-reporter.ts +5 -3
- package/src/naite/naite.ts +12 -7
- package/src/shared/app.shared.ts.txt +2 -2
- package/src/shared/web.shared.ts.txt +2 -2
- package/src/skills/AGENTS.md +19 -18
- package/src/skills/commands/sonamu-skills.md +9 -9
- package/src/skills/sonamu/SKILL.md +111 -104
- package/src/skills/sonamu/ai-agents.md +27 -26
- package/src/skills/sonamu/api.md +81 -69
- package/src/skills/sonamu/auth-migration.md +13 -27
- package/src/skills/sonamu/auth-plugins.md +41 -31
- package/src/skills/sonamu/auth.md +30 -24
- package/src/skills/sonamu/cdd.md +26 -17
- package/src/skills/sonamu/cone.md +50 -50
- package/src/skills/sonamu/config.md +74 -51
- package/src/skills/sonamu/create-sonamu.md +31 -19
- package/src/skills/sonamu/database.md +43 -26
- package/src/skills/sonamu/entity-basic.md +61 -61
- package/src/skills/sonamu/entity-relations.md +84 -80
- package/src/skills/sonamu/entity-validation-checklist.md +19 -15
- package/src/skills/sonamu/fixture-cli.md +52 -30
- package/src/skills/sonamu/framework-change.md +9 -7
- package/src/skills/sonamu/frontend.md +64 -82
- package/src/skills/sonamu/i18n.md +45 -37
- package/src/skills/sonamu/migration.md +54 -31
- package/src/skills/sonamu/model.md +98 -66
- package/src/skills/sonamu/naite.md +34 -32
- package/src/skills/sonamu/project-init.md +28 -8
- package/src/skills/sonamu/puri.md +82 -91
- package/src/skills/sonamu/scaffolding.md +44 -32
- package/src/skills/sonamu/skill-contribution.md +50 -45
- package/src/skills/sonamu/subset.md +13 -13
- package/src/skills/sonamu/tasks.md +73 -58
- package/src/skills/sonamu/testing-devrunner.md +56 -36
- package/src/skills/sonamu/testing.md +23 -58
- package/src/skills/sonamu/upsert.md +32 -31
- package/src/skills/sonamu/vector.md +37 -36
- package/src/ssr/index.ts +2 -12
- package/src/ssr/registry.ts +1 -1
- package/src/ssr/renderer.ts +7 -5
- package/src/ssr/types.ts +2 -2
- package/src/storage/buffered-file.ts +4 -2
- package/src/storage/drivers.ts +3 -2
- package/src/storage/s3-driver.ts +7 -4
- package/src/storage/storage-manager.ts +3 -2
- package/src/storage/types.ts +3 -2
- package/src/storage/uploaded-file.ts +1 -1
- package/src/stream/sse.ts +2 -2
- package/src/syncer/api-parser.ts +8 -5
- package/src/syncer/checksum.ts +9 -5
- package/src/syncer/code-generator.ts +16 -8
- package/src/syncer/entity-operations.ts +5 -3
- package/src/syncer/file-patterns.ts +2 -1
- package/src/syncer/module-loader.ts +9 -6
- package/src/syncer/syncer-actions.ts +5 -3
- package/src/syncer/syncer.ts +18 -24
- package/src/tasks/decorator.ts +10 -8
- package/src/tasks/step-wrapper.ts +1 -1
- package/src/tasks/workflow-manager.ts +18 -15
- package/src/template/__tests__/generated.template.search-text.test.ts +1 -0
- package/src/template/entity-converter.ts +4 -2
- package/src/template/generated.template.test-d.ts +2 -1
- package/src/template/helpers.ts +5 -2
- package/src/template/implementations/entity.template.ts +9 -8
- package/src/template/implementations/entry-server.template.ts +1 -1
- package/src/template/implementations/generated.template.ts +21 -29
- package/src/template/implementations/generated_http.template.ts +9 -6
- package/src/template/implementations/generated_sso.template.ts +6 -4
- package/src/template/implementations/init_types.template.ts +3 -2
- package/src/template/implementations/model.template.ts +4 -2
- package/src/template/implementations/model_test.template.ts +3 -2
- package/src/template/implementations/queries.template.ts +6 -14
- package/src/template/implementations/sd.template.ts +4 -2
- package/src/template/implementations/services.template.ts +7 -11
- package/src/template/implementations/view_form.template.ts +5 -3
- package/src/template/implementations/view_id_all_select.template.ts +3 -2
- package/src/template/implementations/view_list.template.ts +7 -5
- package/src/template/implementations/view_search_input.template.ts +3 -2
- package/src/template/template-manager.ts +4 -3
- package/src/template/template.ts +4 -3
- package/src/template/zod-converter.ts +10 -7
- package/src/testing/__tests__/dev-test-routes.test.ts +3 -2
- package/src/testing/__tests__/dev-vitest-manager.test.ts +1 -0
- package/src/testing/_relation-graph.ts +2 -2
- package/src/testing/bootstrap.ts +55 -27
- package/src/testing/data-explorer.ts +5 -4
- package/src/testing/dev-test-routes.ts +8 -5
- package/src/testing/dev-vitest-manager.ts +13 -12
- package/src/testing/fixture-generator.ts +11 -17
- package/src/testing/fixture-manager.ts +21 -17
- package/src/testing/parallel-db-manager.ts +2 -1
- package/src/testing/vitest-helpers.ts +2 -1
- package/src/types/__tests__/entity-json-schema-search-text.test.ts +1 -0
- package/src/types/types.ts +8 -8
- package/src/typings/knex.d.ts +4 -4
- package/src/ui/ai-api.ts +5 -3
- package/src/ui/ai-client.ts +6 -5
- package/src/ui/api.ts +25 -23
- package/src/ui/cdd-service.ts +12 -11
- package/src/utils/console-util.ts +3 -1
- package/src/utils/formatter.ts +94 -102
- package/src/utils/fs-utils.ts +2 -1
- package/src/utils/model.ts +2 -2
- package/src/utils/object-utils.ts +3 -3
- package/src/utils/process-utils.ts +2 -1
- package/src/utils/sql-parser.ts +10 -1
- package/src/utils/type-utils.ts +3 -3
- package/src/utils/utils.ts +9 -7
- package/src/utils/zod-error.ts +1 -1
- package/src/vector/chunking.ts +1 -1
- package/src/vector/config.ts +1 -1
- package/src/vector/embedding.ts +11 -9
- package/tsdown.api.config.ts +50 -0
- package/.swcrc.project-default +0 -18
- package/dist/api/__tests__/config.test.js +0 -189
- package/dist/bin/__tests__/test-command.test.js +0 -112
- package/dist/bin/__tests__/ts-loader-register.test.js +0 -45
- package/dist/database/puri-subset.test-d.js +0 -89
- package/dist/database/puri.types.test-d.js +0 -129
- package/dist/migration/__tests__/code-generation.search-text.test.js +0 -435
- package/dist/template/__tests__/generated.template.search-text.test.js +0 -99
- package/dist/template/generated.template.test-d.js +0 -24
- package/dist/testing/__tests__/dev-test-routes.test.js +0 -144
- package/dist/testing/__tests__/dev-vitest-manager.test.js +0 -152
- package/dist/types/__tests__/entity-json-schema-search-text.test.js +0 -256
- package/dist/typings/knex.d.js +0 -3
- package/dist/ui-web/assets/index-CKo0Z2Iu.css +0 -1
- package/dist/ui-web/assets/index-DK-2aacv.js +0 -257
|
@@ -1,960 +1,892 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
1
|
+
import { __esmMin } from "../_virtual/rolldown_runtime.js";
|
|
2
|
+
import { SD, init_sd } from "./sd.js";
|
|
3
|
+
import { BadRequestException, init_so_exceptions } from "../exceptions/so-exceptions.js";
|
|
4
|
+
import { Sonamu, init_sonamu } from "../api/sonamu.js";
|
|
5
|
+
import { EntityManager, init_entity_manager } from "../entity/entity-manager.js";
|
|
6
|
+
import { formatCode, init_formatter } from "../utils/formatter.js";
|
|
4
7
|
import path from "path";
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import { execSync } from "child_process";
|
|
10
|
+
import { Workbook } from "@sheetkit/node";
|
|
5
11
|
import ts from "typescript";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import { BadRequestException } from "../exceptions/so-exceptions.js";
|
|
9
|
-
import { formatCode } from "../utils/formatter.js";
|
|
10
|
-
import { SD } from "./sd.js";
|
|
11
|
-
/**
|
|
12
|
-
* 0-based 컬럼 인덱스를 엑셀 컬럼 문자로 변환 (0 -> "A", 25 -> "Z", 26 -> "AA")
|
|
13
|
-
*/ function colLetter(index) {
|
|
14
|
-
let result = "";
|
|
15
|
-
let n = index;
|
|
16
|
-
while(n >= 0){
|
|
17
|
-
result = String.fromCharCode(65 + n % 26) + result;
|
|
18
|
-
n = Math.floor(n / 26) - 1;
|
|
19
|
-
}
|
|
20
|
-
return result;
|
|
21
|
-
}
|
|
12
|
+
|
|
13
|
+
//#region src/dict/sonamu-dictionary.ts
|
|
22
14
|
/**
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
* - 함수 값: "key": (param: Type) => `template`
|
|
34
|
-
*/ parseDictFile(filePath) {
|
|
35
|
-
const content = fs.readFileSync(filePath, "utf-8");
|
|
36
|
-
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
37
|
-
const entries = [];
|
|
38
|
-
ts.forEachChild(sourceFile, (node)=>{
|
|
39
|
-
if (ts.isExportAssignment(node)) {
|
|
40
|
-
const objectLiteral = this.unwrapToObjectLiteral(node.expression);
|
|
41
|
-
if (objectLiteral) {
|
|
42
|
-
this.extractEntriesFromObject(objectLiteral, sourceFile, entries);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
return entries;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* 파일에서 특정 이름의 const 선언을 찾아 ObjectLiteral 파싱
|
|
50
|
-
* 예: const entityLabels = { ... } as const;
|
|
51
|
-
*/ parseConstObjectDeclaration(filePath, varName) {
|
|
52
|
-
const content = fs.readFileSync(filePath, "utf-8");
|
|
53
|
-
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
54
|
-
const entries = [];
|
|
55
|
-
ts.forEachChild(sourceFile, (node)=>{
|
|
56
|
-
if (ts.isVariableStatement(node)) {
|
|
57
|
-
for (const decl of node.declarationList.declarations){
|
|
58
|
-
if (ts.isIdentifier(decl.name) && decl.name.text === varName && decl.initializer) {
|
|
59
|
-
const objectLiteral = this.unwrapToObjectLiteral(decl.initializer);
|
|
60
|
-
if (objectLiteral) {
|
|
61
|
-
this.extractEntriesFromObject(objectLiteral, sourceFile, entries);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
return entries;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* 문자열이 화살표 함수 또는 함수 표현식인지 판별
|
|
71
|
-
*/ isExpressionFunction(code) {
|
|
72
|
-
// 빈 문자열이나 공백만 있는 경우
|
|
73
|
-
if (!code.trim()) {
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
const ARROW_FUNCTION_PATTERN = /^\s*\([^)]*\)\s*=>/;
|
|
77
|
-
return ARROW_FUNCTION_PATTERN.test(code);
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* export default 표현식에서 ObjectLiteralExpression 추출
|
|
81
|
-
* - as const 처리
|
|
82
|
-
* - defineLocale({ ... }) 호출 처리
|
|
83
|
-
*/ unwrapToObjectLiteral(expr) {
|
|
84
|
-
// as const 처리
|
|
85
|
-
if (ts.isAsExpression(expr)) {
|
|
86
|
-
return this.unwrapToObjectLiteral(expr.expression);
|
|
87
|
-
}
|
|
88
|
-
// 직접 객체 리터럴
|
|
89
|
-
if (ts.isObjectLiteralExpression(expr)) {
|
|
90
|
-
return expr;
|
|
91
|
-
}
|
|
92
|
-
// defineLocale({ ... }) 호출
|
|
93
|
-
if (ts.isCallExpression(expr)) {
|
|
94
|
-
const firstArg = expr.arguments[0];
|
|
95
|
-
if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
|
|
96
|
-
return firstArg;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* ObjectLiteralExpression에서 DictEntry 추출
|
|
103
|
-
*/ extractEntriesFromObject(objectLiteral, sourceFile, entries) {
|
|
104
|
-
for (const prop of objectLiteral.properties){
|
|
105
|
-
const entry = this.extractDictEntry(prop, sourceFile);
|
|
106
|
-
if (entry) {
|
|
107
|
-
entries.push(entry);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* PropertyName에서 키 문자열 추출
|
|
113
|
-
* - 문자열 리터럴: "key"
|
|
114
|
-
* - 식별자: key (unquoted)
|
|
115
|
-
*/ getPropertyKey(name) {
|
|
116
|
-
if (ts.isStringLiteral(name)) {
|
|
117
|
-
return name.text;
|
|
118
|
-
}
|
|
119
|
-
if (ts.isIdentifier(name)) {
|
|
120
|
-
return name.text;
|
|
121
|
-
}
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* 프로퍼티에서 DictEntry 추출
|
|
126
|
-
* - 문자열: 실제 문자열 값
|
|
127
|
-
* - 함수: 원본 소스 (여러 줄은 한 줄로 정규화)
|
|
128
|
-
*/ extractDictEntry(prop, sourceFile) {
|
|
129
|
-
if (!ts.isPropertyAssignment(prop)) {
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
132
|
-
const key = this.getPropertyKey(prop.name);
|
|
133
|
-
if (!key) return null;
|
|
134
|
-
const init = prop.initializer;
|
|
135
|
-
// 화살표 함수
|
|
136
|
-
if (ts.isArrowFunction(init)) {
|
|
137
|
-
const funcText = init.getText(sourceFile);
|
|
138
|
-
const normalized = funcText.replace(/\s*\n\s*/g, " ").trim();
|
|
139
|
-
return {
|
|
140
|
-
key,
|
|
141
|
-
value: normalized,
|
|
142
|
-
isFunction: true
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
// 문자열 리터럴
|
|
146
|
-
if (ts.isStringLiteral(init)) {
|
|
147
|
-
return {
|
|
148
|
-
key,
|
|
149
|
-
value: init.text,
|
|
150
|
-
isFunction: false
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
// 템플릿 리터럴 (변수 없음)
|
|
154
|
-
if (ts.isNoSubstitutionTemplateLiteral(init)) {
|
|
155
|
-
return {
|
|
156
|
-
key,
|
|
157
|
-
value: init.text,
|
|
158
|
-
isFunction: false
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
// 기타 (예: 함수 표현식)
|
|
162
|
-
return {
|
|
163
|
-
key,
|
|
164
|
-
value: init.getText(sourceFile),
|
|
165
|
-
isFunction: ts.isFunctionExpression(init)
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* 프로젝트의 i18n dict 파일 경로를 반환합니다.
|
|
170
|
-
* @param locale - 로케일 (ko, en 등)
|
|
171
|
-
* @param target - 타겟 디렉토리 (api, web, app)
|
|
172
|
-
*/ getProjectDictPath(locale, target = "api") {
|
|
173
|
-
const dir = target === "api" ? Sonamu.config.api.dir : target;
|
|
174
|
-
return path.join(Sonamu.appRootPath, dir, "src", "i18n", `${locale}.ts`);
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* sonamu 내장 dict 파일 경로를 반환합니다.
|
|
178
|
-
*/ getSonamuDictPath(locale) {
|
|
179
|
-
const packageRoot = path.resolve(import.meta.dirname, "..", "..");
|
|
180
|
-
return path.join(packageRoot, "src", "dict", `${locale}.ts`);
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* 필요한 dict 키가 프로젝트에 존재하는지 확인하고, 없으면 추가합니다.
|
|
184
|
-
* defaultLocale에만 추가하며, 다른 locale은 사용자가 직접 번역해야 합니다.
|
|
185
|
-
*
|
|
186
|
-
* @param requiredKeys - 필요한 키 목록
|
|
187
|
-
* @param target - 타겟 디렉토리 (api, web, app)
|
|
188
|
-
* @returns 추가된 키 목록
|
|
189
|
-
*/ async ensureDictKeys(requiredKeys, target = "api") {
|
|
190
|
-
const { defaultLocale } = Sonamu.config.i18n;
|
|
191
|
-
const projectDictPath = this.getProjectDictPath(defaultLocale, target);
|
|
192
|
-
// 프로젝트 dict 파일이 없으면 아무것도 하지 않음
|
|
193
|
-
if (!fs.existsSync(projectDictPath)) {
|
|
194
|
-
return [];
|
|
195
|
-
}
|
|
196
|
-
// 프로젝트 dict에서 기존 키 파싱
|
|
197
|
-
const projectEntries = this.parseDictFile(projectDictPath);
|
|
198
|
-
const existingKeys = new Set(projectEntries.map((e)=>e.key));
|
|
199
|
-
// 누락된 키 찾기
|
|
200
|
-
const missingKeys = requiredKeys.filter((key)=>!existingKeys.has(key));
|
|
201
|
-
if (missingKeys.length === 0) {
|
|
202
|
-
return [];
|
|
203
|
-
}
|
|
204
|
-
// sonamu dict에서 기본값 가져오기
|
|
205
|
-
const sonamuDictPath = this.getSonamuDictPath(defaultLocale);
|
|
206
|
-
if (!fs.existsSync(sonamuDictPath)) {
|
|
207
|
-
return [];
|
|
208
|
-
}
|
|
209
|
-
const sonamuEntries = this.parseDictFile(sonamuDictPath);
|
|
210
|
-
const sonamuDict = new Map(sonamuEntries.map((e)=>[
|
|
211
|
-
e.key,
|
|
212
|
-
e
|
|
213
|
-
]));
|
|
214
|
-
// 추가할 엔트리 생성
|
|
215
|
-
const entriesToAdd = missingKeys.map((key)=>sonamuDict.get(key)).filter((entry)=>entry !== undefined);
|
|
216
|
-
if (entriesToAdd.length === 0) {
|
|
217
|
-
return [];
|
|
218
|
-
}
|
|
219
|
-
// 프로젝트 dict 파일에 추가
|
|
220
|
-
await this.appendEntriesToDictFile(projectDictPath, entriesToAdd, defaultLocale, true);
|
|
221
|
-
return entriesToAdd.map((e)=>e.key);
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* dict 파일에 엔트리를 추가합니다.
|
|
225
|
-
* 기존 파일을 파싱하고, 새 엔트리를 추가한 뒤, 전체 파일을 재생성합니다.
|
|
226
|
-
*/ async appendEntriesToDictFile(filePath, entries, locale, isDefaultLocale) {
|
|
227
|
-
// 기존 entries 파싱
|
|
228
|
-
const existingEntries = this.parseDictFile(filePath);
|
|
229
|
-
// 새 entries 추가
|
|
230
|
-
for (const entry of entries){
|
|
231
|
-
existingEntries.push(entry);
|
|
232
|
-
}
|
|
233
|
-
// 파일 재생성
|
|
234
|
-
const content = this.generateProjectDict(locale, existingEntries, isDefaultLocale);
|
|
235
|
-
const formatted = formatCode(content, "typescript", filePath);
|
|
236
|
-
fs.writeFileSync(filePath, formatted, "utf-8");
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* 함수 값들에서 사용되는 헬퍼 함수를 감지합니다.
|
|
240
|
-
*/ detectUsedHelpers(entries) {
|
|
241
|
-
const functionEntries = entries.filter((e)=>e.isFunction);
|
|
242
|
-
const helpers = [];
|
|
243
|
-
for (const helper of [
|
|
244
|
-
"plural",
|
|
245
|
-
"josa"
|
|
246
|
-
]){
|
|
247
|
-
// 함수명이 단어 경계로 사용되는지 확인 (예: plural( 또는 plural,)
|
|
248
|
-
const pattern = new RegExp(`\\b${helper}\\s*\\(`);
|
|
249
|
-
if (functionEntries.some((e)=>pattern.test(e.value))) {
|
|
250
|
-
helpers.push(helper);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
// format 사용 여부 별도 감지 (format.number(...), format.date(...) 등)
|
|
254
|
-
const formatPattern = /\bformat\.\w+\s*\(/;
|
|
255
|
-
const usesFormat = functionEntries.some((e)=>formatPattern.test(e.value));
|
|
256
|
-
return {
|
|
257
|
-
helpers,
|
|
258
|
-
usesFormat
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* Project dict 파일 생성
|
|
263
|
-
*/ generateProjectDict(locale, entries, isDefaultLocale) {
|
|
264
|
-
// key 알파벳 순 정렬
|
|
265
|
-
const sorted = [
|
|
266
|
-
...entries
|
|
267
|
-
].sort((a, b)=>a.key.localeCompare(b.key));
|
|
268
|
-
const lines = [];
|
|
269
|
-
// 함수 값에서 사용되는 헬퍼 함수 감지
|
|
270
|
-
const { helpers, usesFormat } = this.detectUsedHelpers(entries);
|
|
271
|
-
// 헬퍼 함수 import 추가
|
|
272
|
-
const imports = [
|
|
273
|
-
...helpers
|
|
274
|
-
];
|
|
275
|
-
if (usesFormat) {
|
|
276
|
-
imports.push("createFormat");
|
|
277
|
-
}
|
|
278
|
-
if (imports.length > 0) {
|
|
279
|
-
lines.push(`import { ${imports.join(", ")} } from "sonamu/dict";`);
|
|
280
|
-
}
|
|
281
|
-
if (!isDefaultLocale) {
|
|
282
|
-
lines.push('import { defineLocale } from "./sd.generated";');
|
|
283
|
-
}
|
|
284
|
-
if (imports.length > 0 || !isDefaultLocale) {
|
|
285
|
-
lines.push("");
|
|
286
|
-
}
|
|
287
|
-
// format 사용 시 createFormat 호출 추가
|
|
288
|
-
if (usesFormat) {
|
|
289
|
-
lines.push(`const format = createFormat("${locale}");`);
|
|
290
|
-
lines.push("");
|
|
291
|
-
}
|
|
292
|
-
lines.push("/**");
|
|
293
|
-
lines.push(` * Project ${locale.toUpperCase()} Dictionary`);
|
|
294
|
-
lines.push(" */");
|
|
295
|
-
if (isDefaultLocale) {
|
|
296
|
-
lines.push("export default {");
|
|
297
|
-
} else {
|
|
298
|
-
lines.push("export default defineLocale({");
|
|
299
|
-
}
|
|
300
|
-
for (const entry of sorted){
|
|
301
|
-
if (entry.isFunction) {
|
|
302
|
-
// 함수인 경우: 원형 그대로 출력
|
|
303
|
-
lines.push(` "${entry.key}": ${entry.value},`);
|
|
304
|
-
} else {
|
|
305
|
-
lines.push(` "${entry.key}": ${JSON.stringify(entry.value)},`);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
if (isDefaultLocale) {
|
|
309
|
-
lines.push("} as const;");
|
|
310
|
-
} else {
|
|
311
|
-
lines.push("});");
|
|
312
|
-
}
|
|
313
|
-
lines.push("");
|
|
314
|
-
return lines.join("\n");
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* i18n 설정을 가져옵니다.
|
|
318
|
-
*/ getI18nConfig() {
|
|
319
|
-
return Sonamu.config.i18n;
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* i18n 디렉토리 경로를 반환하고, 없으면 생성합니다.
|
|
323
|
-
*/ ensureI18nDir() {
|
|
324
|
-
const i18nDir = path.join(Sonamu.apiRootPath, "src", "i18n");
|
|
325
|
-
if (!fs.existsSync(i18nDir)) {
|
|
326
|
-
fs.mkdirSync(i18nDir, {
|
|
327
|
-
recursive: true
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
return i18nDir;
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* dict 파일을 저장합니다.
|
|
334
|
-
*/ saveDictFile(locale, entries, isDefaultLocale) {
|
|
335
|
-
const i18nDir = this.ensureI18nDir();
|
|
336
|
-
const dictPath = path.join(i18nDir, `${locale}.ts`);
|
|
337
|
-
const content = this.generateProjectDict(locale, entries, isDefaultLocale);
|
|
338
|
-
const formatted = formatCode(content, "typescript", dictPath);
|
|
339
|
-
fs.writeFileSync(dictPath, formatted, "utf-8");
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
|
-
* i18n key를 파싱하여 entity 관련 정보 추출
|
|
343
|
-
*/ parseEntityKey(key) {
|
|
344
|
-
// entity.{EntityId} (list, create, edit 제외)
|
|
345
|
-
const entityTitleMatch = key.match(/^entity\.([A-Z][a-zA-Z0-9]*)$/);
|
|
346
|
-
if (entityTitleMatch && !key.includes(".list") && !key.includes(".create") && !key.includes(".edit")) {
|
|
347
|
-
return {
|
|
348
|
-
type: "entityTitle",
|
|
349
|
-
entityId: entityTitleMatch[1]
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
// entity.{EntityId}.{propName}
|
|
353
|
-
const propDescMatch = key.match(/^entity\.([A-Z][a-zA-Z0-9]*)\.([a-z_][a-z0-9_]*)$/);
|
|
354
|
-
if (propDescMatch) {
|
|
355
|
-
return {
|
|
356
|
-
type: "propDesc",
|
|
357
|
-
entityId: propDescMatch[1],
|
|
358
|
-
propName: propDescMatch[2]
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
// enum.{EnumId}.{value}
|
|
362
|
-
const enumLabelMatch = key.match(/^enum\.([A-Z][a-zA-Z0-9]*)\.(.+)$/);
|
|
363
|
-
if (enumLabelMatch) {
|
|
364
|
-
return {
|
|
365
|
-
type: "enumLabel",
|
|
366
|
-
enumId: enumLabelMatch[1],
|
|
367
|
-
enumValue: enumLabelMatch[2]
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
return {
|
|
371
|
-
type: "other"
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
/**
|
|
375
|
-
* entity key에 대해 entity.json 업데이트 수행
|
|
376
|
-
* @returns 업데이트 여부
|
|
377
|
-
*/ async updateEntityByKey(key, value) {
|
|
378
|
-
const keyInfo = this.parseEntityKey(key);
|
|
379
|
-
switch(keyInfo.type){
|
|
380
|
-
case "entityTitle":
|
|
381
|
-
{
|
|
382
|
-
try {
|
|
383
|
-
const entity = EntityManager.get(keyInfo.entityId);
|
|
384
|
-
if (entity.title !== value) {
|
|
385
|
-
entity.title = value;
|
|
386
|
-
await entity.save();
|
|
387
|
-
return true;
|
|
388
|
-
}
|
|
389
|
-
} catch {
|
|
390
|
-
// entity not found
|
|
391
|
-
}
|
|
392
|
-
return false;
|
|
393
|
-
}
|
|
394
|
-
case "propDesc":
|
|
395
|
-
{
|
|
396
|
-
try {
|
|
397
|
-
const entity = EntityManager.get(keyInfo.entityId);
|
|
398
|
-
const propIndex = entity.props.findIndex((p)=>p.name === keyInfo.propName);
|
|
399
|
-
if (propIndex !== -1 && entity.props[propIndex].desc !== value) {
|
|
400
|
-
entity.props[propIndex].desc = value;
|
|
401
|
-
await entity.save();
|
|
402
|
-
return true;
|
|
403
|
-
}
|
|
404
|
-
} catch {
|
|
405
|
-
// entity not found
|
|
406
|
-
}
|
|
407
|
-
return false;
|
|
408
|
-
}
|
|
409
|
-
case "enumLabel":
|
|
410
|
-
{
|
|
411
|
-
for (const entityId of EntityManager.getAllIds()){
|
|
412
|
-
const entity = EntityManager.get(entityId);
|
|
413
|
-
if (entity.enumLabels[keyInfo.enumId]) {
|
|
414
|
-
if (entity.enumLabels[keyInfo.enumId][keyInfo.enumValue] !== value) {
|
|
415
|
-
entity.enumLabels[keyInfo.enumId][keyInfo.enumValue] = value;
|
|
416
|
-
await entity.save();
|
|
417
|
-
return true;
|
|
418
|
-
}
|
|
419
|
-
break;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
return false;
|
|
423
|
-
}
|
|
424
|
-
default:
|
|
425
|
-
return false;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
/**
|
|
429
|
-
* sd.generated.ts에서 entity labels 추출
|
|
430
|
-
* entity.json에서 관리되는 값만 포함 (.list, .create, .edit 제외)
|
|
431
|
-
*/ extractEntityLabels() {
|
|
432
|
-
const sdPath = path.join(Sonamu.apiRootPath, "src", "i18n", "sd.generated.ts");
|
|
433
|
-
if (!fs.existsSync(sdPath)) {
|
|
434
|
-
return [];
|
|
435
|
-
}
|
|
436
|
-
return this.parseConstObjectDeclaration(sdPath, "entityLabels");
|
|
437
|
-
}
|
|
438
|
-
/**
|
|
439
|
-
* sd.generated.ts에서 rc-keys 추출
|
|
440
|
-
* react-components에서 관리되는 i18n 키들
|
|
441
|
-
* @param locale - 로케일 (ko, en 등)
|
|
442
|
-
*/ extractRCKeys(locale) {
|
|
443
|
-
const sdPath = path.join(Sonamu.apiRootPath, "src", "i18n", "sd.generated.ts");
|
|
444
|
-
if (!fs.existsSync(sdPath)) {
|
|
445
|
-
return [];
|
|
446
|
-
}
|
|
447
|
-
// locale별 변수명 매핑 (sd.template.ts의 getRCKeysVarName과 동일)
|
|
448
|
-
const varName = (()=>{
|
|
449
|
-
if (locale === "ko") return "rcKeysKo";
|
|
450
|
-
if (locale === "en") return "rcKeysEn";
|
|
451
|
-
// 다른 locale은 en을 fallback으로 사용
|
|
452
|
-
return "rcKeysEn";
|
|
453
|
-
})();
|
|
454
|
-
return this.parseConstObjectDeclaration(sdPath, varName);
|
|
455
|
-
}
|
|
456
|
-
/**
|
|
457
|
-
* Project dict 파일([locale].ts)에서 딕셔너리 로드
|
|
458
|
-
*/ loadProjectDict(locale) {
|
|
459
|
-
const dictPath = path.join(Sonamu.apiRootPath, "src", "i18n", `${locale}.ts`);
|
|
460
|
-
if (!fs.existsSync(dictPath)) {
|
|
461
|
-
return {
|
|
462
|
-
entries: []
|
|
463
|
-
};
|
|
464
|
-
}
|
|
465
|
-
return {
|
|
466
|
-
entries: this.parseDictFile(dictPath)
|
|
467
|
-
};
|
|
468
|
-
}
|
|
469
|
-
/**
|
|
470
|
-
* 딕셔너리 데이터 수집 (sonamu + entity + project)
|
|
471
|
-
*/ async collectDictionary() {
|
|
472
|
-
const { defaultLocale, supportedLocales } = this.getI18nConfig();
|
|
473
|
-
const locales = supportedLocales;
|
|
474
|
-
const rows = [];
|
|
475
|
-
const rowMap = new Map();
|
|
476
|
-
// 1. RC Keys (sonamu source, 각 locale별)
|
|
477
|
-
for (const locale of locales){
|
|
478
|
-
const rcKeys = this.extractRCKeys(locale);
|
|
479
|
-
for (const rcKey of rcKeys){
|
|
480
|
-
let row = rowMap.get(rcKey.key);
|
|
481
|
-
if (!row) {
|
|
482
|
-
row = {
|
|
483
|
-
key: rcKey.key,
|
|
484
|
-
source: "sonamu",
|
|
485
|
-
isFunction: rcKey.isFunction ?? false
|
|
486
|
-
};
|
|
487
|
-
rowMap.set(rcKey.key, row);
|
|
488
|
-
}
|
|
489
|
-
row[locale] = rcKey.value;
|
|
490
|
-
if (rcKey.isFunction) {
|
|
491
|
-
row.isFunction = true;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
// 2. Entity labels (default locale 기준)
|
|
496
|
-
const entityLabels = this.extractEntityLabels();
|
|
497
|
-
for (const label of entityLabels){
|
|
498
|
-
const row = {
|
|
499
|
-
key: label.key,
|
|
500
|
-
source: "entity",
|
|
501
|
-
isFunction: label.isFunction ?? false,
|
|
502
|
-
[defaultLocale]: label.value
|
|
503
|
-
};
|
|
504
|
-
rowMap.set(label.key, row);
|
|
505
|
-
}
|
|
506
|
-
// 3. Project dict (각 locale별)
|
|
507
|
-
for (const locale of locales){
|
|
508
|
-
const { entries } = this.loadProjectDict(locale);
|
|
509
|
-
for (const entry of entries){
|
|
510
|
-
const existing = rowMap.get(entry.key);
|
|
511
|
-
if (existing) {
|
|
512
|
-
// sonamu, entity source가 있으면 해당 locale 값만 추가
|
|
513
|
-
existing[locale] = entry.value;
|
|
514
|
-
if (entry.isFunction) {
|
|
515
|
-
existing.isFunction = true;
|
|
516
|
-
}
|
|
517
|
-
} else {
|
|
518
|
-
// project source로 새로 추가
|
|
519
|
-
let row = rowMap.get(entry.key);
|
|
520
|
-
if (!row) {
|
|
521
|
-
row = {
|
|
522
|
-
key: entry.key,
|
|
523
|
-
source: "project",
|
|
524
|
-
isFunction: entry.isFunction
|
|
525
|
-
};
|
|
526
|
-
rowMap.set(entry.key, row);
|
|
527
|
-
}
|
|
528
|
-
row[locale] = entry.value;
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
rows.push(...rowMap.values());
|
|
533
|
-
rows.sort((a, b)=>a.key.localeCompare(b.key));
|
|
534
|
-
// 통계 계산: locale별 (채워진 값 / 전체 키 수)
|
|
535
|
-
const stats = {};
|
|
536
|
-
const total = rows.length;
|
|
537
|
-
for (const locale of locales){
|
|
538
|
-
const filled = rows.filter((row)=>row[locale] != null && row[locale] !== "").length;
|
|
539
|
-
const percent = total > 0 ? Math.round(filled / total * 100) : 0;
|
|
540
|
-
stats[locale] = {
|
|
541
|
-
total,
|
|
542
|
-
filled,
|
|
543
|
-
percent
|
|
544
|
-
};
|
|
545
|
-
}
|
|
546
|
-
return {
|
|
547
|
-
rows,
|
|
548
|
-
locales,
|
|
549
|
-
defaultLocale,
|
|
550
|
-
stats
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
/**
|
|
554
|
-
* 딕셔너리 조회
|
|
555
|
-
*/ async getDictionary() {
|
|
556
|
-
return this.collectDictionary();
|
|
557
|
-
}
|
|
558
|
-
/**
|
|
559
|
-
* Excel로 내보내기
|
|
560
|
-
*/ async exportToExcel() {
|
|
561
|
-
const { rows, locales } = await this.collectDictionary();
|
|
562
|
-
const wb = new Workbook();
|
|
563
|
-
const sheet = "i18n";
|
|
564
|
-
wb.setSheetName("Sheet1", sheet);
|
|
565
|
-
const projectName = `${Sonamu.config.projectName ?? "Sonamu"} Dictionary`;
|
|
566
|
-
const headers = [
|
|
567
|
-
"key",
|
|
568
|
-
"source",
|
|
569
|
-
...locales
|
|
570
|
-
];
|
|
571
|
-
// 스타일 정의
|
|
572
|
-
const titleStyleId = wb.addStyle({
|
|
573
|
-
font: {
|
|
574
|
-
size: 23
|
|
575
|
-
},
|
|
576
|
-
alignment: {
|
|
577
|
-
vertical: "center",
|
|
578
|
-
horizontal: "left"
|
|
579
|
-
}
|
|
580
|
-
});
|
|
581
|
-
const headerStyleId = wb.addStyle({
|
|
582
|
-
font: {
|
|
583
|
-
bold: true,
|
|
584
|
-
size: 11
|
|
585
|
-
},
|
|
586
|
-
alignment: {
|
|
587
|
-
horizontal: "center",
|
|
588
|
-
vertical: "center"
|
|
589
|
-
},
|
|
590
|
-
fill: {
|
|
591
|
-
pattern: "solid",
|
|
592
|
-
fgColor: "F1F1F1"
|
|
593
|
-
},
|
|
594
|
-
border: {
|
|
595
|
-
top: {
|
|
596
|
-
style: "thin",
|
|
597
|
-
color: "D0D0D0"
|
|
598
|
-
},
|
|
599
|
-
left: {
|
|
600
|
-
style: "thin",
|
|
601
|
-
color: "D0D0D0"
|
|
602
|
-
},
|
|
603
|
-
bottom: {
|
|
604
|
-
style: "thin",
|
|
605
|
-
color: "D0D0D0"
|
|
606
|
-
},
|
|
607
|
-
right: {
|
|
608
|
-
style: "thin",
|
|
609
|
-
color: "D0D0D0"
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
});
|
|
613
|
-
const dataStyleId = wb.addStyle({
|
|
614
|
-
font: {
|
|
615
|
-
size: 11
|
|
616
|
-
},
|
|
617
|
-
alignment: {
|
|
618
|
-
vertical: "center",
|
|
619
|
-
horizontal: "left"
|
|
620
|
-
}
|
|
621
|
-
});
|
|
622
|
-
// 행 1: 프로젝트명
|
|
623
|
-
wb.setCellValue(sheet, "A1", projectName);
|
|
624
|
-
wb.setCellStyle(sheet, "A1", titleStyleId);
|
|
625
|
-
wb.setRowHeight(sheet, 1, 28);
|
|
626
|
-
// 행 2: 빈 행 (기본값)
|
|
627
|
-
// 행 3: 헤더
|
|
628
|
-
wb.setRowValues(sheet, 3, "A", headers);
|
|
629
|
-
wb.setRowHeight(sheet, 3, 26);
|
|
630
|
-
for(let col = 0; col < headers.length; col++){
|
|
631
|
-
wb.setCellStyle(sheet, `${colLetter(col)}3`, headerStyleId);
|
|
632
|
-
}
|
|
633
|
-
// 행 4 이후: 데이터
|
|
634
|
-
for(let i = 0; i < rows.length; i++){
|
|
635
|
-
const row = rows[i];
|
|
636
|
-
const values = [
|
|
637
|
-
row.key,
|
|
638
|
-
row.source,
|
|
639
|
-
...locales.map((locale)=>row[locale] ?? "")
|
|
640
|
-
];
|
|
641
|
-
const rowNum = i + 4;
|
|
642
|
-
wb.setRowValues(sheet, rowNum, "A", values);
|
|
643
|
-
wb.setRowHeight(sheet, rowNum, 24);
|
|
644
|
-
for(let col = 0; col < values.length; col++){
|
|
645
|
-
wb.setCellStyle(sheet, `${colLetter(col)}${rowNum}`, dataStyleId);
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
// 컬럼 너비 계산
|
|
649
|
-
const MAX_WIDTH = 50;
|
|
650
|
-
const MIN_WIDTH = 10;
|
|
651
|
-
const columnWidths = headers.map((header)=>Math.max(header.length, MIN_WIDTH));
|
|
652
|
-
for (const row of rows){
|
|
653
|
-
const values = [
|
|
654
|
-
row.key,
|
|
655
|
-
row.source,
|
|
656
|
-
...locales.map((locale)=>row[locale] ?? "")
|
|
657
|
-
];
|
|
658
|
-
values.forEach((value, idx)=>{
|
|
659
|
-
const textLength = String(value).length;
|
|
660
|
-
columnWidths[idx] = Math.min(Math.max(columnWidths[idx], textLength), MAX_WIDTH);
|
|
661
|
-
});
|
|
662
|
-
}
|
|
663
|
-
// 컬럼 너비 적용
|
|
664
|
-
for(let col = 0; col < columnWidths.length; col++){
|
|
665
|
-
wb.setColWidth(sheet, colLetter(col), columnWidths[col] + 2);
|
|
666
|
-
}
|
|
667
|
-
return {
|
|
668
|
-
filename: `${projectName}-${new Date().toISOString().split("T")[0]}.xlsx`,
|
|
669
|
-
buffer: wb.writeBufferSync()
|
|
670
|
-
};
|
|
671
|
-
}
|
|
672
|
-
/**
|
|
673
|
-
* Excel에서 가져오기
|
|
674
|
-
*
|
|
675
|
-
* 형식:
|
|
676
|
-
* - 1행: 프로젝트명
|
|
677
|
-
* - 2행: 빈 행
|
|
678
|
-
* - 3행: 헤더 (key, source, locales...)
|
|
679
|
-
* - 4행 이후: 데이터
|
|
680
|
-
*/ async importFromExcel(buffer) {
|
|
681
|
-
const wb = Workbook.openBufferSync(buffer);
|
|
682
|
-
const sheet = wb.sheetNames[0];
|
|
683
|
-
const allRows = wb.getRows(sheet);
|
|
684
|
-
const { defaultLocale, supportedLocales } = this.getI18nConfig();
|
|
685
|
-
const locales = supportedLocales;
|
|
686
|
-
let updatedEntities = 0;
|
|
687
|
-
let updatedLocales = 0;
|
|
688
|
-
// locale별 project dict entries
|
|
689
|
-
const projectDictEntries = {};
|
|
690
|
-
for (const locale of locales){
|
|
691
|
-
projectDictEntries[locale] = [];
|
|
692
|
-
}
|
|
693
|
-
// 헤더 행 찾기: 첫 번째 컬럼(A)이 "key"인 행
|
|
694
|
-
let headerRowNum = 0;
|
|
695
|
-
for (const rowData of allRows){
|
|
696
|
-
const firstCell = rowData.cells.find((c)=>c.column === "A");
|
|
697
|
-
const firstCellValue = String(firstCell?.value ?? "").trim().toLowerCase();
|
|
698
|
-
if (firstCellValue === "key") {
|
|
699
|
-
headerRowNum = rowData.row;
|
|
700
|
-
break;
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
if (headerRowNum === 0) {
|
|
704
|
-
throw new BadRequestException(SD("sonamu.error.headerRowNotFound"));
|
|
705
|
-
}
|
|
706
|
-
// 헤더 행에서 컬럼 매핑 구성
|
|
707
|
-
const headerRowData = allRows.find((r)=>r.row === headerRowNum);
|
|
708
|
-
const colToHeader = new Map();
|
|
709
|
-
if (headerRowData) {
|
|
710
|
-
for (const cell of headerRowData.cells){
|
|
711
|
-
colToHeader.set(cell.column, String(cell.value ?? ""));
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
// 데이터 행 읽기 (헤더 다음 행부터)
|
|
715
|
-
for (const rowData of allRows){
|
|
716
|
-
if (rowData.row <= headerRowNum) continue;
|
|
717
|
-
const rowValues = {};
|
|
718
|
-
for (const cell of rowData.cells){
|
|
719
|
-
const headerName = colToHeader.get(cell.column);
|
|
720
|
-
if (headerName) {
|
|
721
|
-
rowValues[headerName] = String(cell.value ?? "");
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
const key = rowValues.key;
|
|
725
|
-
const source = rowValues.source;
|
|
726
|
-
if (!key || !source) continue;
|
|
727
|
-
if (source === "entity") {
|
|
728
|
-
// entity source: default locale만 entity.json에 저장
|
|
729
|
-
const defaultValue = rowValues[defaultLocale];
|
|
730
|
-
if (defaultValue) {
|
|
731
|
-
const updated = await this.updateEntityByKey(key, defaultValue);
|
|
732
|
-
if (updated) {
|
|
733
|
-
updatedEntities++;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
// non-default locale은 project dict에 저장
|
|
737
|
-
for (const locale of locales){
|
|
738
|
-
if (locale === defaultLocale) continue;
|
|
739
|
-
const cellValue = rowValues[locale]?.trim();
|
|
740
|
-
if (cellValue) {
|
|
741
|
-
projectDictEntries[locale].push({
|
|
742
|
-
key,
|
|
743
|
-
value: cellValue,
|
|
744
|
-
isFunction: this.isExpressionFunction(cellValue)
|
|
745
|
-
});
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
} else if (source === "project") {
|
|
749
|
-
// project source: 모든 locale을 project dict에 저장
|
|
750
|
-
for (const locale of locales){
|
|
751
|
-
const cellValue = rowValues[locale]?.trim();
|
|
752
|
-
if (cellValue) {
|
|
753
|
-
projectDictEntries[locale].push({
|
|
754
|
-
key,
|
|
755
|
-
value: cellValue,
|
|
756
|
-
isFunction: this.isExpressionFunction(cellValue)
|
|
757
|
-
});
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
// Project dict 파일 생성
|
|
763
|
-
for (const locale of locales){
|
|
764
|
-
const entries = projectDictEntries[locale];
|
|
765
|
-
if (entries.length > 0) {
|
|
766
|
-
this.saveDictFile(locale, entries, locale === defaultLocale);
|
|
767
|
-
updatedLocales++;
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
return {
|
|
771
|
-
success: true,
|
|
772
|
-
updatedEntities,
|
|
773
|
-
updatedLocales
|
|
774
|
-
};
|
|
775
|
-
}
|
|
776
|
-
/**
|
|
777
|
-
* 딕셔너리 항목 수정
|
|
778
|
-
*/ async updateEntry(params) {
|
|
779
|
-
const { oldKey, newKey, source, values } = params;
|
|
780
|
-
const { defaultLocale, supportedLocales } = this.getI18nConfig();
|
|
781
|
-
const locales = supportedLocales;
|
|
782
|
-
// entity source의 default locale 처리
|
|
783
|
-
if (source === "entity" && values[defaultLocale]) {
|
|
784
|
-
await this.updateEntityByKey(newKey, values[defaultLocale]);
|
|
785
|
-
}
|
|
786
|
-
// project dict 업데이트
|
|
787
|
-
// - entity의 non-default locale
|
|
788
|
-
// - project source의 모든 locale
|
|
789
|
-
// - sonamu source의 모든 locale (override)
|
|
790
|
-
for (const locale of locales){
|
|
791
|
-
// entity source의 default locale은 entity.json에서 처리했으므로 스킵
|
|
792
|
-
if (source === "entity" && locale === defaultLocale) continue;
|
|
793
|
-
const cellValue = values[locale]?.trim();
|
|
794
|
-
if (!cellValue) continue;
|
|
795
|
-
// 기존 dict 로드
|
|
796
|
-
const { entries } = this.loadProjectDict(locale);
|
|
797
|
-
// key 변경 시 기존 key 제거
|
|
798
|
-
if (oldKey !== newKey) {
|
|
799
|
-
const oldIndex = entries.findIndex((e)=>e.key === oldKey);
|
|
800
|
-
if (oldIndex !== -1) {
|
|
801
|
-
entries.splice(oldIndex, 1);
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
// 새 값 업데이트 또는 추가
|
|
805
|
-
const existingIndex = entries.findIndex((e)=>e.key === newKey);
|
|
806
|
-
const newEntry = {
|
|
807
|
-
key: newKey,
|
|
808
|
-
value: cellValue,
|
|
809
|
-
isFunction: this.isExpressionFunction(cellValue)
|
|
810
|
-
};
|
|
811
|
-
if (existingIndex !== -1) {
|
|
812
|
-
entries[existingIndex] = newEntry;
|
|
813
|
-
} else {
|
|
814
|
-
entries.push(newEntry);
|
|
815
|
-
}
|
|
816
|
-
// dict 파일 저장
|
|
817
|
-
this.saveDictFile(locale, entries, locale === defaultLocale);
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
/**
|
|
821
|
-
* 딕셔너리 항목 추가
|
|
822
|
-
*/ async createEntry(params) {
|
|
823
|
-
const { key, values } = params;
|
|
824
|
-
if (!key?.trim()) {
|
|
825
|
-
throw new BadRequestException(SD("sonamu.error.keyRequired"));
|
|
826
|
-
}
|
|
827
|
-
const { defaultLocale, supportedLocales } = this.getI18nConfig();
|
|
828
|
-
const locales = supportedLocales;
|
|
829
|
-
// 중복 키 체크
|
|
830
|
-
for (const locale of locales){
|
|
831
|
-
const { entries } = this.loadProjectDict(locale);
|
|
832
|
-
if (entries.some((e)=>e.key === key)) {
|
|
833
|
-
throw new BadRequestException(SD("sonamu.error.keyAlreadyExists")(key));
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
// 각 locale에 새 키 추가
|
|
837
|
-
for (const locale of locales){
|
|
838
|
-
const cellValue = values[locale]?.trim();
|
|
839
|
-
if (!cellValue) continue;
|
|
840
|
-
const { entries } = this.loadProjectDict(locale);
|
|
841
|
-
entries.push({
|
|
842
|
-
key,
|
|
843
|
-
value: cellValue,
|
|
844
|
-
isFunction: this.isExpressionFunction(cellValue)
|
|
845
|
-
});
|
|
846
|
-
this.saveDictFile(locale, entries, locale === defaultLocale);
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
/**
|
|
850
|
-
* 딕셔너리 항목 삭제
|
|
851
|
-
*/ async deleteEntry(key) {
|
|
852
|
-
if (!key) {
|
|
853
|
-
throw new BadRequestException(SD("sonamu.error.keyRequired"));
|
|
854
|
-
}
|
|
855
|
-
const { defaultLocale, supportedLocales } = this.getI18nConfig();
|
|
856
|
-
const locales = supportedLocales;
|
|
857
|
-
let deleted = false;
|
|
858
|
-
for (const locale of locales){
|
|
859
|
-
const { entries } = this.loadProjectDict(locale);
|
|
860
|
-
const index = entries.findIndex((e)=>e.key === key);
|
|
861
|
-
if (index !== -1) {
|
|
862
|
-
entries.splice(index, 1);
|
|
863
|
-
deleted = true;
|
|
864
|
-
this.saveDictFile(locale, entries, locale === defaultLocale);
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
if (!deleted) {
|
|
868
|
-
throw new BadRequestException(SD("sonamu.error.keyNotFound")(key));
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
/**
|
|
872
|
-
* 미사용 키 검사 (ast-grep 사용)
|
|
873
|
-
*/ async checkUsage(keys) {
|
|
874
|
-
// ast-grep 설치 확인
|
|
875
|
-
let sgPath = null;
|
|
876
|
-
try {
|
|
877
|
-
sgPath = execSync("which sg", {
|
|
878
|
-
encoding: "utf-8"
|
|
879
|
-
}).trim();
|
|
880
|
-
} catch {
|
|
881
|
-
try {
|
|
882
|
-
sgPath = execSync("which ast-grep", {
|
|
883
|
-
encoding: "utf-8"
|
|
884
|
-
}).trim();
|
|
885
|
-
} catch {
|
|
886
|
-
// ast-grep not installed
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
if (!sgPath) {
|
|
890
|
-
return {
|
|
891
|
-
error: "ast-grep이 설치되어 있지 않습니다. brew install ast-grep 또는 npm install -g @ast-grep/cli로 설치해주세요.",
|
|
892
|
-
unusedKeys: []
|
|
893
|
-
};
|
|
894
|
-
}
|
|
895
|
-
const searchPaths = [];
|
|
896
|
-
for (const entry of [
|
|
897
|
-
"api",
|
|
898
|
-
"web",
|
|
899
|
-
"app"
|
|
900
|
-
]){
|
|
901
|
-
const srcPath = path.join(Sonamu.appRootPath, entry, "src");
|
|
902
|
-
if (fs.existsSync(srcPath)) {
|
|
903
|
-
searchPaths.push(srcPath);
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
if (searchPaths.length === 0) {
|
|
907
|
-
return {
|
|
908
|
-
error: "검색할 src 디렉토리를 찾을 수 없습니다.",
|
|
909
|
-
unusedKeys: []
|
|
910
|
-
};
|
|
911
|
-
}
|
|
912
|
-
const usedKeys = new Set();
|
|
913
|
-
try {
|
|
914
|
-
// ast-grep으로 SD("...") 패턴 검색
|
|
915
|
-
// 패턴: SD("KEY") 또는 SD('KEY') 형태
|
|
916
|
-
const patterns = [
|
|
917
|
-
'SD("$KEY")',
|
|
918
|
-
"SD('$KEY')"
|
|
919
|
-
];
|
|
920
|
-
for (const searchPath of searchPaths){
|
|
921
|
-
for (const pattern of patterns){
|
|
922
|
-
try {
|
|
923
|
-
const result = execSync(`${sgPath} --pattern '${pattern}' --json ${searchPath}`, {
|
|
924
|
-
encoding: "utf-8",
|
|
925
|
-
maxBuffer: 50 * 1024 * 1024
|
|
926
|
-
});
|
|
927
|
-
if (result.trim()) {
|
|
928
|
-
const matches = JSON.parse(result);
|
|
929
|
-
for (const match of matches){
|
|
930
|
-
// metaVariables.single.KEY.text에서 키 추출
|
|
931
|
-
const keyText = match.metaVariables?.single?.KEY?.text;
|
|
932
|
-
if (keyText) {
|
|
933
|
-
// 따옴표 제거
|
|
934
|
-
const cleanKey = keyText.replace(/^["']|["']$/g, "");
|
|
935
|
-
usedKeys.add(cleanKey);
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
} catch {
|
|
940
|
-
// 패턴 매치 없으면 에러 (무시)
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
// keys 중에서 usedKeys에 없는 것들이 미사용 키
|
|
945
|
-
const unusedKeys = keys.filter((k)=>!usedKeys.has(k));
|
|
946
|
-
return {
|
|
947
|
-
unusedKeys,
|
|
948
|
-
usedKeysCount: usedKeys.size
|
|
949
|
-
};
|
|
950
|
-
} catch (e) {
|
|
951
|
-
return {
|
|
952
|
-
error: `검색 중 오류 발생: ${e instanceof Error ? e.message : String(e)}`,
|
|
953
|
-
unusedKeys: []
|
|
954
|
-
};
|
|
955
|
-
}
|
|
956
|
-
}
|
|
15
|
+
* 0-based 컬럼 인덱스를 엑셀 컬럼 문자로 변환 (0 -> "A", 25 -> "Z", 26 -> "AA")
|
|
16
|
+
*/
|
|
17
|
+
function colLetter(index) {
|
|
18
|
+
let result = "";
|
|
19
|
+
let n = index;
|
|
20
|
+
while (n >= 0) {
|
|
21
|
+
result = String.fromCharCode(65 + n % 26) + result;
|
|
22
|
+
n = Math.floor(n / 26) - 1;
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
957
25
|
}
|
|
958
|
-
|
|
26
|
+
var SonamuDictionary, sonamuDictionary;
|
|
27
|
+
var init_sonamu_dictionary = __esmMin((() => {
|
|
28
|
+
init_sonamu();
|
|
29
|
+
init_entity_manager();
|
|
30
|
+
init_so_exceptions();
|
|
31
|
+
init_formatter();
|
|
32
|
+
init_sd();
|
|
33
|
+
SonamuDictionary = class {
|
|
34
|
+
/**
|
|
35
|
+
* TypeScript Compiler API를 사용하여 dict 파일 파싱
|
|
36
|
+
*
|
|
37
|
+
* 지원 패턴:
|
|
38
|
+
* - export default { ... } as const;
|
|
39
|
+
* - export default defineLocale({ ... });
|
|
40
|
+
* - 문자열 값: "key": "value" 또는 key: `value`
|
|
41
|
+
* - 함수 값: "key": (param: Type) => `template`
|
|
42
|
+
*/
|
|
43
|
+
parseDictFile(filePath) {
|
|
44
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
45
|
+
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
46
|
+
const entries = [];
|
|
47
|
+
ts.forEachChild(sourceFile, (node) => {
|
|
48
|
+
if (ts.isExportAssignment(node)) {
|
|
49
|
+
const objectLiteral = this.unwrapToObjectLiteral(node.expression);
|
|
50
|
+
if (objectLiteral) {
|
|
51
|
+
this.extractEntriesFromObject(objectLiteral, sourceFile, entries);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return entries;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 파일에서 특정 이름의 const 선언을 찾아 ObjectLiteral 파싱
|
|
59
|
+
* 예: const entityLabels = { ... } as const;
|
|
60
|
+
*/
|
|
61
|
+
parseConstObjectDeclaration(filePath, varName) {
|
|
62
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
63
|
+
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
64
|
+
const entries = [];
|
|
65
|
+
ts.forEachChild(sourceFile, (node) => {
|
|
66
|
+
if (ts.isVariableStatement(node)) {
|
|
67
|
+
for (const decl of node.declarationList.declarations) {
|
|
68
|
+
if (ts.isIdentifier(decl.name) && decl.name.text === varName && decl.initializer) {
|
|
69
|
+
const objectLiteral = this.unwrapToObjectLiteral(decl.initializer);
|
|
70
|
+
if (objectLiteral) {
|
|
71
|
+
this.extractEntriesFromObject(objectLiteral, sourceFile, entries);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
return entries;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* 문자열이 화살표 함수 또는 함수 표현식인지 판별
|
|
81
|
+
*/
|
|
82
|
+
isExpressionFunction(code) {
|
|
83
|
+
if (!code.trim()) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
const ARROW_FUNCTION_PATTERN = /^\s*\([^)]*\)\s*=>/;
|
|
87
|
+
return ARROW_FUNCTION_PATTERN.test(code);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* export default 표현식에서 ObjectLiteralExpression 추출
|
|
91
|
+
* - as const 처리
|
|
92
|
+
* - defineLocale({ ... }) 호출 처리
|
|
93
|
+
*/
|
|
94
|
+
unwrapToObjectLiteral(expr) {
|
|
95
|
+
if (ts.isAsExpression(expr)) {
|
|
96
|
+
return this.unwrapToObjectLiteral(expr.expression);
|
|
97
|
+
}
|
|
98
|
+
if (ts.isObjectLiteralExpression(expr)) {
|
|
99
|
+
return expr;
|
|
100
|
+
}
|
|
101
|
+
if (ts.isCallExpression(expr)) {
|
|
102
|
+
const firstArg = expr.arguments[0];
|
|
103
|
+
if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
|
|
104
|
+
return firstArg;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* ObjectLiteralExpression에서 DictEntry 추출
|
|
111
|
+
*/
|
|
112
|
+
extractEntriesFromObject(objectLiteral, sourceFile, entries) {
|
|
113
|
+
for (const prop of objectLiteral.properties) {
|
|
114
|
+
const entry = this.extractDictEntry(prop, sourceFile);
|
|
115
|
+
if (entry) {
|
|
116
|
+
entries.push(entry);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* PropertyName에서 키 문자열 추출
|
|
122
|
+
* - 문자열 리터럴: "key"
|
|
123
|
+
* - 식별자: key (unquoted)
|
|
124
|
+
*/
|
|
125
|
+
getPropertyKey(name) {
|
|
126
|
+
if (ts.isStringLiteral(name)) {
|
|
127
|
+
return name.text;
|
|
128
|
+
}
|
|
129
|
+
if (ts.isIdentifier(name)) {
|
|
130
|
+
return name.text;
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* 프로퍼티에서 DictEntry 추출
|
|
136
|
+
* - 문자열: 실제 문자열 값
|
|
137
|
+
* - 함수: 원본 소스 (여러 줄은 한 줄로 정규화)
|
|
138
|
+
*/
|
|
139
|
+
extractDictEntry(prop, sourceFile) {
|
|
140
|
+
if (!ts.isPropertyAssignment(prop)) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
const key = this.getPropertyKey(prop.name);
|
|
144
|
+
if (!key) return null;
|
|
145
|
+
const init = prop.initializer;
|
|
146
|
+
if (ts.isArrowFunction(init)) {
|
|
147
|
+
const funcText = init.getText(sourceFile);
|
|
148
|
+
const normalized = funcText.replace(/\s*\n\s*/g, " ").trim();
|
|
149
|
+
return {
|
|
150
|
+
key,
|
|
151
|
+
value: normalized,
|
|
152
|
+
isFunction: true
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (ts.isStringLiteral(init)) {
|
|
156
|
+
return {
|
|
157
|
+
key,
|
|
158
|
+
value: init.text,
|
|
159
|
+
isFunction: false
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
if (ts.isNoSubstitutionTemplateLiteral(init)) {
|
|
163
|
+
return {
|
|
164
|
+
key,
|
|
165
|
+
value: init.text,
|
|
166
|
+
isFunction: false
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
key,
|
|
171
|
+
value: init.getText(sourceFile),
|
|
172
|
+
isFunction: ts.isFunctionExpression(init)
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* 프로젝트의 i18n dict 파일 경로를 반환합니다.
|
|
177
|
+
* @param locale - 로케일 (ko, en 등)
|
|
178
|
+
* @param target - 타겟 디렉토리 (api, web, app)
|
|
179
|
+
*/
|
|
180
|
+
getProjectDictPath(locale, target = "api") {
|
|
181
|
+
const dir = target === "api" ? Sonamu.config.api.dir : target;
|
|
182
|
+
return path.join(Sonamu.appRootPath, dir, "src", "i18n", `${locale}.ts`);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* sonamu 내장 dict 파일 경로를 반환합니다.
|
|
186
|
+
*/
|
|
187
|
+
getSonamuDictPath(locale) {
|
|
188
|
+
const packageRoot = path.resolve(import.meta.dirname, "..", "..");
|
|
189
|
+
return path.join(packageRoot, "src", "dict", `${locale}.ts`);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* 필요한 dict 키가 프로젝트에 존재하는지 확인하고, 없으면 추가합니다.
|
|
193
|
+
* defaultLocale에만 추가하며, 다른 locale은 사용자가 직접 번역해야 합니다.
|
|
194
|
+
*
|
|
195
|
+
* @param requiredKeys - 필요한 키 목록
|
|
196
|
+
* @param target - 타겟 디렉토리 (api, web, app)
|
|
197
|
+
* @returns 추가된 키 목록
|
|
198
|
+
*/
|
|
199
|
+
async ensureDictKeys(requiredKeys, target = "api") {
|
|
200
|
+
const { defaultLocale } = Sonamu.config.i18n;
|
|
201
|
+
const projectDictPath = this.getProjectDictPath(defaultLocale, target);
|
|
202
|
+
if (!fs.existsSync(projectDictPath)) {
|
|
203
|
+
return [];
|
|
204
|
+
}
|
|
205
|
+
const projectEntries = this.parseDictFile(projectDictPath);
|
|
206
|
+
const existingKeys = new Set(projectEntries.map((e) => e.key));
|
|
207
|
+
const missingKeys = requiredKeys.filter((key) => !existingKeys.has(key));
|
|
208
|
+
if (missingKeys.length === 0) {
|
|
209
|
+
return [];
|
|
210
|
+
}
|
|
211
|
+
const sonamuDictPath = this.getSonamuDictPath(defaultLocale);
|
|
212
|
+
if (!fs.existsSync(sonamuDictPath)) {
|
|
213
|
+
return [];
|
|
214
|
+
}
|
|
215
|
+
const sonamuEntries = this.parseDictFile(sonamuDictPath);
|
|
216
|
+
const sonamuDict = new Map(sonamuEntries.map((e) => [e.key, e]));
|
|
217
|
+
const entriesToAdd = missingKeys.map((key) => sonamuDict.get(key)).filter((entry) => entry !== undefined);
|
|
218
|
+
if (entriesToAdd.length === 0) {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
await this.appendEntriesToDictFile(projectDictPath, entriesToAdd, defaultLocale, true);
|
|
222
|
+
return entriesToAdd.map((e) => e.key);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* dict 파일에 엔트리를 추가합니다.
|
|
226
|
+
* 기존 파일을 파싱하고, 새 엔트리를 추가한 뒤, 전체 파일을 재생성합니다.
|
|
227
|
+
*/
|
|
228
|
+
async appendEntriesToDictFile(filePath, entries, locale, isDefaultLocale) {
|
|
229
|
+
const existingEntries = this.parseDictFile(filePath);
|
|
230
|
+
for (const entry of entries) {
|
|
231
|
+
existingEntries.push(entry);
|
|
232
|
+
}
|
|
233
|
+
const content = this.generateProjectDict(locale, existingEntries, isDefaultLocale);
|
|
234
|
+
const formatted = await formatCode(content, "typescript", filePath);
|
|
235
|
+
fs.writeFileSync(filePath, formatted, "utf-8");
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* 함수 값들에서 사용되는 헬퍼 함수를 감지합니다.
|
|
239
|
+
*/
|
|
240
|
+
detectUsedHelpers(entries) {
|
|
241
|
+
const functionEntries = entries.filter((e) => e.isFunction);
|
|
242
|
+
const helpers = [];
|
|
243
|
+
for (const helper of ["plural", "josa"]) {
|
|
244
|
+
const pattern = new RegExp(`\\b${helper}\\s*\\(`);
|
|
245
|
+
if (functionEntries.some((e) => pattern.test(e.value))) {
|
|
246
|
+
helpers.push(helper);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const formatPattern = /\bformat\.\w+\s*\(/;
|
|
250
|
+
const usesFormat = functionEntries.some((e) => formatPattern.test(e.value));
|
|
251
|
+
return {
|
|
252
|
+
helpers,
|
|
253
|
+
usesFormat
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Project dict 파일 생성
|
|
258
|
+
*/
|
|
259
|
+
generateProjectDict(locale, entries, isDefaultLocale) {
|
|
260
|
+
const sorted = [...entries].toSorted((a, b) => a.key.localeCompare(b.key));
|
|
261
|
+
const lines = [];
|
|
262
|
+
const { helpers, usesFormat } = this.detectUsedHelpers(entries);
|
|
263
|
+
const imports = [...helpers];
|
|
264
|
+
if (usesFormat) {
|
|
265
|
+
imports.push("createFormat");
|
|
266
|
+
}
|
|
267
|
+
if (imports.length > 0) {
|
|
268
|
+
lines.push(`import { ${imports.join(", ")} } from "sonamu/dict";`);
|
|
269
|
+
}
|
|
270
|
+
if (!isDefaultLocale) {
|
|
271
|
+
lines.push("import { defineLocale } from \"./sd.generated\";");
|
|
272
|
+
}
|
|
273
|
+
if (imports.length > 0 || !isDefaultLocale) {
|
|
274
|
+
lines.push("");
|
|
275
|
+
}
|
|
276
|
+
if (usesFormat) {
|
|
277
|
+
lines.push(`const format = createFormat("${locale}");`);
|
|
278
|
+
lines.push("");
|
|
279
|
+
}
|
|
280
|
+
lines.push("/**");
|
|
281
|
+
lines.push(` * Project ${locale.toUpperCase()} Dictionary`);
|
|
282
|
+
lines.push(" */");
|
|
283
|
+
if (isDefaultLocale) {
|
|
284
|
+
lines.push("export default {");
|
|
285
|
+
} else {
|
|
286
|
+
lines.push("export default defineLocale({");
|
|
287
|
+
}
|
|
288
|
+
for (const entry of sorted) {
|
|
289
|
+
if (entry.isFunction) {
|
|
290
|
+
lines.push(` "${entry.key}": ${entry.value},`);
|
|
291
|
+
} else {
|
|
292
|
+
lines.push(` "${entry.key}": ${JSON.stringify(entry.value)},`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (isDefaultLocale) {
|
|
296
|
+
lines.push("} as const;");
|
|
297
|
+
} else {
|
|
298
|
+
lines.push("});");
|
|
299
|
+
}
|
|
300
|
+
lines.push("");
|
|
301
|
+
return lines.join("\n");
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* i18n 설정을 가져옵니다.
|
|
305
|
+
*/
|
|
306
|
+
getI18nConfig() {
|
|
307
|
+
return Sonamu.config.i18n;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* i18n 디렉토리 경로를 반환하고, 없으면 생성합니다.
|
|
311
|
+
*/
|
|
312
|
+
ensureI18nDir() {
|
|
313
|
+
const i18nDir = path.join(Sonamu.apiRootPath, "src", "i18n");
|
|
314
|
+
if (!fs.existsSync(i18nDir)) {
|
|
315
|
+
fs.mkdirSync(i18nDir, { recursive: true });
|
|
316
|
+
}
|
|
317
|
+
return i18nDir;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* dict 파일을 저장합니다.
|
|
321
|
+
*/
|
|
322
|
+
async saveDictFile(locale, entries, isDefaultLocale) {
|
|
323
|
+
const i18nDir = this.ensureI18nDir();
|
|
324
|
+
const dictPath = path.join(i18nDir, `${locale}.ts`);
|
|
325
|
+
const content = this.generateProjectDict(locale, entries, isDefaultLocale);
|
|
326
|
+
const formatted = await formatCode(content, "typescript", dictPath);
|
|
327
|
+
fs.writeFileSync(dictPath, formatted, "utf-8");
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* i18n key를 파싱하여 entity 관련 정보 추출
|
|
331
|
+
*/
|
|
332
|
+
parseEntityKey(key) {
|
|
333
|
+
const entityTitleMatch = key.match(/^entity\.([A-Z][a-zA-Z0-9]*)$/);
|
|
334
|
+
if (entityTitleMatch && !key.includes(".list") && !key.includes(".create") && !key.includes(".edit")) {
|
|
335
|
+
return {
|
|
336
|
+
type: "entityTitle",
|
|
337
|
+
entityId: entityTitleMatch[1]
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
const propDescMatch = key.match(/^entity\.([A-Z][a-zA-Z0-9]*)\.([a-z_][a-z0-9_]*)$/);
|
|
341
|
+
if (propDescMatch) {
|
|
342
|
+
return {
|
|
343
|
+
type: "propDesc",
|
|
344
|
+
entityId: propDescMatch[1],
|
|
345
|
+
propName: propDescMatch[2]
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
const enumLabelMatch = key.match(/^enum\.([A-Z][a-zA-Z0-9]*)\.(.+)$/);
|
|
349
|
+
if (enumLabelMatch) {
|
|
350
|
+
return {
|
|
351
|
+
type: "enumLabel",
|
|
352
|
+
enumId: enumLabelMatch[1],
|
|
353
|
+
enumValue: enumLabelMatch[2]
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
return { type: "other" };
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* entity key에 대해 entity.json 업데이트 수행
|
|
360
|
+
* @returns 업데이트 여부
|
|
361
|
+
*/
|
|
362
|
+
async updateEntityByKey(key, value) {
|
|
363
|
+
const keyInfo = this.parseEntityKey(key);
|
|
364
|
+
switch (keyInfo.type) {
|
|
365
|
+
case "entityTitle": {
|
|
366
|
+
try {
|
|
367
|
+
const entity = EntityManager.get(keyInfo.entityId);
|
|
368
|
+
if (entity.title !== value) {
|
|
369
|
+
entity.title = value;
|
|
370
|
+
await entity.save();
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
} catch {}
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
case "propDesc": {
|
|
377
|
+
try {
|
|
378
|
+
const entity = EntityManager.get(keyInfo.entityId);
|
|
379
|
+
const propIndex = entity.props.findIndex((p) => p.name === keyInfo.propName);
|
|
380
|
+
if (propIndex !== -1 && entity.props[propIndex].desc !== value) {
|
|
381
|
+
entity.props[propIndex].desc = value;
|
|
382
|
+
await entity.save();
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
} catch {}
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
case "enumLabel": {
|
|
389
|
+
for (const entityId of EntityManager.getAllIds()) {
|
|
390
|
+
const entity = EntityManager.get(entityId);
|
|
391
|
+
if (entity.enumLabels[keyInfo.enumId]) {
|
|
392
|
+
if (entity.enumLabels[keyInfo.enumId][keyInfo.enumValue] !== value) {
|
|
393
|
+
entity.enumLabels[keyInfo.enumId][keyInfo.enumValue] = value;
|
|
394
|
+
await entity.save();
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
default: return false;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* sd.generated.ts에서 entity labels 추출
|
|
407
|
+
* entity.json에서 관리되는 값만 포함 (.list, .create, .edit 제외)
|
|
408
|
+
*/
|
|
409
|
+
extractEntityLabels() {
|
|
410
|
+
const sdPath = path.join(Sonamu.apiRootPath, "src", "i18n", "sd.generated.ts");
|
|
411
|
+
if (!fs.existsSync(sdPath)) {
|
|
412
|
+
return [];
|
|
413
|
+
}
|
|
414
|
+
return this.parseConstObjectDeclaration(sdPath, "entityLabels");
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* sd.generated.ts에서 rc-keys 추출
|
|
418
|
+
* react-components에서 관리되는 i18n 키들
|
|
419
|
+
* @param locale - 로케일 (ko, en 등)
|
|
420
|
+
*/
|
|
421
|
+
extractRCKeys(locale) {
|
|
422
|
+
const sdPath = path.join(Sonamu.apiRootPath, "src", "i18n", "sd.generated.ts");
|
|
423
|
+
if (!fs.existsSync(sdPath)) {
|
|
424
|
+
return [];
|
|
425
|
+
}
|
|
426
|
+
const varName = (() => {
|
|
427
|
+
if (locale === "ko") return "rcKeysKo";
|
|
428
|
+
if (locale === "en") return "rcKeysEn";
|
|
429
|
+
return "rcKeysEn";
|
|
430
|
+
})();
|
|
431
|
+
return this.parseConstObjectDeclaration(sdPath, varName);
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Project dict 파일([locale].ts)에서 딕셔너리 로드
|
|
435
|
+
*/
|
|
436
|
+
loadProjectDict(locale) {
|
|
437
|
+
const dictPath = path.join(Sonamu.apiRootPath, "src", "i18n", `${locale}.ts`);
|
|
438
|
+
if (!fs.existsSync(dictPath)) {
|
|
439
|
+
return { entries: [] };
|
|
440
|
+
}
|
|
441
|
+
return { entries: this.parseDictFile(dictPath) };
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* 딕셔너리 데이터 수집 (sonamu + entity + project)
|
|
445
|
+
*/
|
|
446
|
+
async collectDictionary() {
|
|
447
|
+
const { defaultLocale, supportedLocales } = this.getI18nConfig();
|
|
448
|
+
const locales = supportedLocales;
|
|
449
|
+
const rows = [];
|
|
450
|
+
const rowMap = new Map();
|
|
451
|
+
for (const locale of locales) {
|
|
452
|
+
const rcKeys = this.extractRCKeys(locale);
|
|
453
|
+
for (const rcKey of rcKeys) {
|
|
454
|
+
let row = rowMap.get(rcKey.key);
|
|
455
|
+
if (!row) {
|
|
456
|
+
row = {
|
|
457
|
+
key: rcKey.key,
|
|
458
|
+
source: "sonamu",
|
|
459
|
+
isFunction: rcKey.isFunction ?? false
|
|
460
|
+
};
|
|
461
|
+
rowMap.set(rcKey.key, row);
|
|
462
|
+
}
|
|
463
|
+
row[locale] = rcKey.value;
|
|
464
|
+
if (rcKey.isFunction) {
|
|
465
|
+
row.isFunction = true;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
const entityLabels = this.extractEntityLabels();
|
|
470
|
+
for (const label of entityLabels) {
|
|
471
|
+
const row = {
|
|
472
|
+
key: label.key,
|
|
473
|
+
source: "entity",
|
|
474
|
+
isFunction: label.isFunction ?? false,
|
|
475
|
+
[defaultLocale]: label.value
|
|
476
|
+
};
|
|
477
|
+
rowMap.set(label.key, row);
|
|
478
|
+
}
|
|
479
|
+
for (const locale of locales) {
|
|
480
|
+
const { entries } = this.loadProjectDict(locale);
|
|
481
|
+
for (const entry of entries) {
|
|
482
|
+
const existing = rowMap.get(entry.key);
|
|
483
|
+
if (existing) {
|
|
484
|
+
existing[locale] = entry.value;
|
|
485
|
+
if (entry.isFunction) {
|
|
486
|
+
existing.isFunction = true;
|
|
487
|
+
}
|
|
488
|
+
} else {
|
|
489
|
+
let row = rowMap.get(entry.key);
|
|
490
|
+
if (!row) {
|
|
491
|
+
row = {
|
|
492
|
+
key: entry.key,
|
|
493
|
+
source: "project",
|
|
494
|
+
isFunction: entry.isFunction
|
|
495
|
+
};
|
|
496
|
+
rowMap.set(entry.key, row);
|
|
497
|
+
}
|
|
498
|
+
row[locale] = entry.value;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
rows.push(...rowMap.values());
|
|
503
|
+
rows.sort((a, b) => a.key.localeCompare(b.key));
|
|
504
|
+
const stats = {};
|
|
505
|
+
const total = rows.length;
|
|
506
|
+
for (const locale of locales) {
|
|
507
|
+
const filled = rows.filter((row) => row[locale] != null && row[locale] !== "").length;
|
|
508
|
+
const percent = total > 0 ? Math.round(filled / total * 100) : 0;
|
|
509
|
+
stats[locale] = {
|
|
510
|
+
total,
|
|
511
|
+
filled,
|
|
512
|
+
percent
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
return {
|
|
516
|
+
rows,
|
|
517
|
+
locales,
|
|
518
|
+
defaultLocale,
|
|
519
|
+
stats
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* 딕셔너리 조회
|
|
524
|
+
*/
|
|
525
|
+
async getDictionary() {
|
|
526
|
+
return this.collectDictionary();
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Excel로 내보내기
|
|
530
|
+
*/
|
|
531
|
+
async exportToExcel() {
|
|
532
|
+
const { rows, locales } = await this.collectDictionary();
|
|
533
|
+
const wb = new Workbook();
|
|
534
|
+
const sheet = "i18n";
|
|
535
|
+
wb.setSheetName("Sheet1", sheet);
|
|
536
|
+
const projectName = `${Sonamu.config.projectName ?? "Sonamu"} Dictionary`;
|
|
537
|
+
const headers = [
|
|
538
|
+
"key",
|
|
539
|
+
"source",
|
|
540
|
+
...locales
|
|
541
|
+
];
|
|
542
|
+
const titleStyleId = wb.addStyle({
|
|
543
|
+
font: { size: 23 },
|
|
544
|
+
alignment: {
|
|
545
|
+
vertical: "center",
|
|
546
|
+
horizontal: "left"
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
const headerStyleId = wb.addStyle({
|
|
550
|
+
font: {
|
|
551
|
+
bold: true,
|
|
552
|
+
size: 11
|
|
553
|
+
},
|
|
554
|
+
alignment: {
|
|
555
|
+
horizontal: "center",
|
|
556
|
+
vertical: "center"
|
|
557
|
+
},
|
|
558
|
+
fill: {
|
|
559
|
+
pattern: "solid",
|
|
560
|
+
fgColor: "F1F1F1"
|
|
561
|
+
},
|
|
562
|
+
border: {
|
|
563
|
+
top: {
|
|
564
|
+
style: "thin",
|
|
565
|
+
color: "D0D0D0"
|
|
566
|
+
},
|
|
567
|
+
left: {
|
|
568
|
+
style: "thin",
|
|
569
|
+
color: "D0D0D0"
|
|
570
|
+
},
|
|
571
|
+
bottom: {
|
|
572
|
+
style: "thin",
|
|
573
|
+
color: "D0D0D0"
|
|
574
|
+
},
|
|
575
|
+
right: {
|
|
576
|
+
style: "thin",
|
|
577
|
+
color: "D0D0D0"
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
const dataStyleId = wb.addStyle({
|
|
582
|
+
font: { size: 11 },
|
|
583
|
+
alignment: {
|
|
584
|
+
vertical: "center",
|
|
585
|
+
horizontal: "left"
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
wb.setCellValue(sheet, "A1", projectName);
|
|
589
|
+
wb.setCellStyle(sheet, "A1", titleStyleId);
|
|
590
|
+
wb.setRowHeight(sheet, 1, 28);
|
|
591
|
+
wb.setRowValues(sheet, 3, "A", headers);
|
|
592
|
+
wb.setRowHeight(sheet, 3, 26);
|
|
593
|
+
for (let col = 0; col < headers.length; col++) {
|
|
594
|
+
wb.setCellStyle(sheet, `${colLetter(col)}3`, headerStyleId);
|
|
595
|
+
}
|
|
596
|
+
for (let i = 0; i < rows.length; i++) {
|
|
597
|
+
const row = rows[i];
|
|
598
|
+
const values = [
|
|
599
|
+
row.key,
|
|
600
|
+
row.source,
|
|
601
|
+
...locales.map((locale) => row[locale] ?? "")
|
|
602
|
+
];
|
|
603
|
+
const rowNum = i + 4;
|
|
604
|
+
wb.setRowValues(sheet, rowNum, "A", values);
|
|
605
|
+
wb.setRowHeight(sheet, rowNum, 24);
|
|
606
|
+
for (let col = 0; col < values.length; col++) {
|
|
607
|
+
wb.setCellStyle(sheet, `${colLetter(col)}${rowNum}`, dataStyleId);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
const MAX_WIDTH = 50;
|
|
611
|
+
const MIN_WIDTH = 10;
|
|
612
|
+
const columnWidths = headers.map((header) => Math.max(header.length, MIN_WIDTH));
|
|
613
|
+
for (const row of rows) {
|
|
614
|
+
const values = [
|
|
615
|
+
row.key,
|
|
616
|
+
row.source,
|
|
617
|
+
...locales.map((locale) => row[locale] ?? "")
|
|
618
|
+
];
|
|
619
|
+
values.forEach((value, idx) => {
|
|
620
|
+
const textLength = String(value).length;
|
|
621
|
+
columnWidths[idx] = Math.min(Math.max(columnWidths[idx], textLength), MAX_WIDTH);
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
for (let col = 0; col < columnWidths.length; col++) {
|
|
625
|
+
wb.setColWidth(sheet, colLetter(col), columnWidths[col] + 2);
|
|
626
|
+
}
|
|
627
|
+
return {
|
|
628
|
+
filename: `${projectName}-${new Date().toISOString().split("T")[0]}.xlsx`,
|
|
629
|
+
buffer: wb.writeBufferSync()
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Excel에서 가져오기
|
|
634
|
+
*
|
|
635
|
+
* 형식:
|
|
636
|
+
* - 1행: 프로젝트명
|
|
637
|
+
* - 2행: 빈 행
|
|
638
|
+
* - 3행: 헤더 (key, source, locales...)
|
|
639
|
+
* - 4행 이후: 데이터
|
|
640
|
+
*/
|
|
641
|
+
async importFromExcel(buffer) {
|
|
642
|
+
const wb = Workbook.openBufferSync(buffer);
|
|
643
|
+
const sheet = wb.sheetNames[0];
|
|
644
|
+
const allRows = wb.getRows(sheet);
|
|
645
|
+
const { defaultLocale, supportedLocales } = this.getI18nConfig();
|
|
646
|
+
const locales = supportedLocales;
|
|
647
|
+
let updatedEntities = 0;
|
|
648
|
+
let updatedLocales = 0;
|
|
649
|
+
const projectDictEntries = {};
|
|
650
|
+
for (const locale of locales) {
|
|
651
|
+
projectDictEntries[locale] = [];
|
|
652
|
+
}
|
|
653
|
+
let headerRowNum = 0;
|
|
654
|
+
for (const rowData of allRows) {
|
|
655
|
+
const firstCell = rowData.cells.find((c) => c.column === "A");
|
|
656
|
+
const firstCellValue = String(firstCell?.value ?? "").trim().toLowerCase();
|
|
657
|
+
if (firstCellValue === "key") {
|
|
658
|
+
headerRowNum = rowData.row;
|
|
659
|
+
break;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
if (headerRowNum === 0) {
|
|
663
|
+
throw new BadRequestException(SD("sonamu.error.headerRowNotFound"));
|
|
664
|
+
}
|
|
665
|
+
const headerRowData = allRows.find((r) => r.row === headerRowNum);
|
|
666
|
+
const colToHeader = new Map();
|
|
667
|
+
if (headerRowData) {
|
|
668
|
+
for (const cell of headerRowData.cells) {
|
|
669
|
+
colToHeader.set(cell.column, String(cell.value ?? ""));
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
for (const rowData of allRows) {
|
|
673
|
+
if (rowData.row <= headerRowNum) continue;
|
|
674
|
+
const rowValues = {};
|
|
675
|
+
for (const cell of rowData.cells) {
|
|
676
|
+
const headerName = colToHeader.get(cell.column);
|
|
677
|
+
if (headerName) {
|
|
678
|
+
rowValues[headerName] = String(cell.value ?? "");
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
const key = rowValues.key;
|
|
682
|
+
const source = rowValues.source;
|
|
683
|
+
if (!key || !source) continue;
|
|
684
|
+
if (source === "entity") {
|
|
685
|
+
const defaultValue = rowValues[defaultLocale];
|
|
686
|
+
if (defaultValue) {
|
|
687
|
+
const updated = await this.updateEntityByKey(key, defaultValue);
|
|
688
|
+
if (updated) {
|
|
689
|
+
updatedEntities++;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
for (const locale of locales) {
|
|
693
|
+
if (locale === defaultLocale) continue;
|
|
694
|
+
const cellValue = rowValues[locale]?.trim();
|
|
695
|
+
if (cellValue) {
|
|
696
|
+
projectDictEntries[locale].push({
|
|
697
|
+
key,
|
|
698
|
+
value: cellValue,
|
|
699
|
+
isFunction: this.isExpressionFunction(cellValue)
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
} else if (source === "project") {
|
|
704
|
+
for (const locale of locales) {
|
|
705
|
+
const cellValue = rowValues[locale]?.trim();
|
|
706
|
+
if (cellValue) {
|
|
707
|
+
projectDictEntries[locale].push({
|
|
708
|
+
key,
|
|
709
|
+
value: cellValue,
|
|
710
|
+
isFunction: this.isExpressionFunction(cellValue)
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
for (const locale of locales) {
|
|
717
|
+
const entries = projectDictEntries[locale];
|
|
718
|
+
if (entries.length > 0) {
|
|
719
|
+
await this.saveDictFile(locale, entries, locale === defaultLocale);
|
|
720
|
+
updatedLocales++;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return {
|
|
724
|
+
success: true,
|
|
725
|
+
updatedEntities,
|
|
726
|
+
updatedLocales
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* 딕셔너리 항목 수정
|
|
731
|
+
*/
|
|
732
|
+
async updateEntry(params) {
|
|
733
|
+
const { oldKey, newKey, source, values } = params;
|
|
734
|
+
const { defaultLocale, supportedLocales } = this.getI18nConfig();
|
|
735
|
+
const locales = supportedLocales;
|
|
736
|
+
if (source === "entity" && values[defaultLocale]) {
|
|
737
|
+
await this.updateEntityByKey(newKey, values[defaultLocale]);
|
|
738
|
+
}
|
|
739
|
+
for (const locale of locales) {
|
|
740
|
+
if (source === "entity" && locale === defaultLocale) continue;
|
|
741
|
+
const cellValue = values[locale]?.trim();
|
|
742
|
+
if (!cellValue) continue;
|
|
743
|
+
const { entries } = this.loadProjectDict(locale);
|
|
744
|
+
if (oldKey !== newKey) {
|
|
745
|
+
const oldIndex = entries.findIndex((e) => e.key === oldKey);
|
|
746
|
+
if (oldIndex !== -1) {
|
|
747
|
+
entries.splice(oldIndex, 1);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
const existingIndex = entries.findIndex((e) => e.key === newKey);
|
|
751
|
+
const newEntry = {
|
|
752
|
+
key: newKey,
|
|
753
|
+
value: cellValue,
|
|
754
|
+
isFunction: this.isExpressionFunction(cellValue)
|
|
755
|
+
};
|
|
756
|
+
if (existingIndex !== -1) {
|
|
757
|
+
entries[existingIndex] = newEntry;
|
|
758
|
+
} else {
|
|
759
|
+
entries.push(newEntry);
|
|
760
|
+
}
|
|
761
|
+
await this.saveDictFile(locale, entries, locale === defaultLocale);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* 딕셔너리 항목 추가
|
|
766
|
+
*/
|
|
767
|
+
async createEntry(params) {
|
|
768
|
+
const { key, values } = params;
|
|
769
|
+
if (!key?.trim()) {
|
|
770
|
+
throw new BadRequestException(SD("sonamu.error.keyRequired"));
|
|
771
|
+
}
|
|
772
|
+
const { defaultLocale, supportedLocales } = this.getI18nConfig();
|
|
773
|
+
const locales = supportedLocales;
|
|
774
|
+
for (const locale of locales) {
|
|
775
|
+
const { entries } = this.loadProjectDict(locale);
|
|
776
|
+
if (entries.some((e) => e.key === key)) {
|
|
777
|
+
throw new BadRequestException(SD("sonamu.error.keyAlreadyExists")(key));
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
for (const locale of locales) {
|
|
781
|
+
const cellValue = values[locale]?.trim();
|
|
782
|
+
if (!cellValue) continue;
|
|
783
|
+
const { entries } = this.loadProjectDict(locale);
|
|
784
|
+
entries.push({
|
|
785
|
+
key,
|
|
786
|
+
value: cellValue,
|
|
787
|
+
isFunction: this.isExpressionFunction(cellValue)
|
|
788
|
+
});
|
|
789
|
+
await this.saveDictFile(locale, entries, locale === defaultLocale);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* 딕셔너리 항목 삭제
|
|
794
|
+
*/
|
|
795
|
+
async deleteEntry(key) {
|
|
796
|
+
if (!key) {
|
|
797
|
+
throw new BadRequestException(SD("sonamu.error.keyRequired"));
|
|
798
|
+
}
|
|
799
|
+
const { defaultLocale, supportedLocales } = this.getI18nConfig();
|
|
800
|
+
const locales = supportedLocales;
|
|
801
|
+
let deleted = false;
|
|
802
|
+
for (const locale of locales) {
|
|
803
|
+
const { entries } = this.loadProjectDict(locale);
|
|
804
|
+
const index = entries.findIndex((e) => e.key === key);
|
|
805
|
+
if (index !== -1) {
|
|
806
|
+
entries.splice(index, 1);
|
|
807
|
+
deleted = true;
|
|
808
|
+
await this.saveDictFile(locale, entries, locale === defaultLocale);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
if (!deleted) {
|
|
812
|
+
throw new BadRequestException(SD("sonamu.error.keyNotFound")(key));
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* 미사용 키 검사 (ast-grep 사용)
|
|
817
|
+
*/
|
|
818
|
+
async checkUsage(keys) {
|
|
819
|
+
let sgPath = null;
|
|
820
|
+
try {
|
|
821
|
+
sgPath = execSync("which sg", { encoding: "utf-8" }).trim();
|
|
822
|
+
} catch {
|
|
823
|
+
try {
|
|
824
|
+
sgPath = execSync("which ast-grep", { encoding: "utf-8" }).trim();
|
|
825
|
+
} catch {}
|
|
826
|
+
}
|
|
827
|
+
if (!sgPath) {
|
|
828
|
+
return {
|
|
829
|
+
error: "ast-grep이 설치되어 있지 않습니다. brew install ast-grep 또는 npm install -g @ast-grep/cli로 설치해주세요.",
|
|
830
|
+
unusedKeys: []
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
const searchPaths = [];
|
|
834
|
+
for (const entry of [
|
|
835
|
+
"api",
|
|
836
|
+
"web",
|
|
837
|
+
"app"
|
|
838
|
+
]) {
|
|
839
|
+
const srcPath = path.join(Sonamu.appRootPath, entry, "src");
|
|
840
|
+
if (fs.existsSync(srcPath)) {
|
|
841
|
+
searchPaths.push(srcPath);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
if (searchPaths.length === 0) {
|
|
845
|
+
return {
|
|
846
|
+
error: "검색할 src 디렉토리를 찾을 수 없습니다.",
|
|
847
|
+
unusedKeys: []
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
const usedKeys = new Set();
|
|
851
|
+
try {
|
|
852
|
+
const patterns = ["SD(\"$KEY\")", "SD('$KEY')"];
|
|
853
|
+
for (const searchPath of searchPaths) {
|
|
854
|
+
for (const pattern of patterns) {
|
|
855
|
+
try {
|
|
856
|
+
const result = execSync(`${sgPath} --pattern '${pattern}' --json ${searchPath}`, {
|
|
857
|
+
encoding: "utf-8",
|
|
858
|
+
maxBuffer: 50 * 1024 * 1024
|
|
859
|
+
});
|
|
860
|
+
if (result.trim()) {
|
|
861
|
+
const matches = JSON.parse(result);
|
|
862
|
+
for (const match of matches) {
|
|
863
|
+
const keyText = match.metaVariables?.single?.KEY?.text;
|
|
864
|
+
if (keyText) {
|
|
865
|
+
const cleanKey = keyText.replace(/^["']|["']$/g, "");
|
|
866
|
+
usedKeys.add(cleanKey);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
} catch {}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
const unusedKeys = keys.filter((k) => !usedKeys.has(k));
|
|
874
|
+
return {
|
|
875
|
+
unusedKeys,
|
|
876
|
+
usedKeysCount: usedKeys.size
|
|
877
|
+
};
|
|
878
|
+
} catch (e) {
|
|
879
|
+
return {
|
|
880
|
+
error: `검색 중 오류 발생: ${e instanceof Error ? e.message : String(e)}`,
|
|
881
|
+
unusedKeys: []
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
sonamuDictionary = new SonamuDictionary();
|
|
887
|
+
}));
|
|
959
888
|
|
|
960
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWN0L3NvbmFtdS1kaWN0aW9uYXJ5LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFdvcmtib29rIH0gZnJvbSBcIkBzaGVldGtpdC9ub2RlXCI7XG5pbXBvcnQgeyBleGVjU3luYyB9IGZyb20gXCJjaGlsZF9wcm9jZXNzXCI7XG5pbXBvcnQgZnMgZnJvbSBcImZzXCI7XG5pbXBvcnQgcGF0aCBmcm9tIFwicGF0aFwiO1xuaW1wb3J0IHRzIGZyb20gXCJ0eXBlc2NyaXB0XCI7XG5pbXBvcnQgeyBTb25hbXUgfSBmcm9tIFwiLi4vYXBpL3NvbmFtdVwiO1xuaW1wb3J0IHsgRW50aXR5TWFuYWdlciB9IGZyb20gXCIuLi9lbnRpdHkvZW50aXR5LW1hbmFnZXJcIjtcbmltcG9ydCB7IEJhZFJlcXVlc3RFeGNlcHRpb24gfSBmcm9tIFwiLi4vZXhjZXB0aW9ucy9zby1leGNlcHRpb25zXCI7XG5pbXBvcnQgeyBmb3JtYXRDb2RlIH0gZnJvbSBcIi4uL3V0aWxzL2Zvcm1hdHRlclwiO1xuaW1wb3J0IHsgU0QgfSBmcm9tIFwiLi9zZFwiO1xuaW1wb3J0IHR5cGUge1xuICBEaWN0RW50cnksXG4gIERpY3Rpb25hcnlSZXN1bHQsXG4gIERpY3Rpb25hcnlSb3csXG4gIEVudGl0eUtleUluZm8sXG4gIEkxOG5Db25maWcsXG4gIEltcG9ydFJlc3VsdCxcbiAgVXNhZ2VSZXN1bHQsXG59IGZyb20gXCIuL3R5cGVzXCI7XG5cbi8qKlxuICogMC1iYXNlZCDsu6zrn7wg7J24642x7Iqk66W8IOyXkeyFgCDsu6zrn7wg66y47J6Q66GcIOuzgO2ZmCAoMCAtPiBcIkFcIiwgMjUgLT4gXCJaXCIsIDI2IC0+IFwiQUFcIilcbiAqL1xuZnVuY3Rpb24gY29sTGV0dGVyKGluZGV4OiBudW1iZXIpOiBzdHJpbmcge1xuICBsZXQgcmVzdWx0ID0gXCJcIjtcbiAgbGV0IG4gPSBpbmRleDtcbiAgd2hpbGUgKG4gPj0gMCkge1xuICAgIHJlc3VsdCA9IFN0cmluZy5mcm9tQ2hhckNvZGUoNjUgKyAobiAlIDI2KSkgKyByZXN1bHQ7XG4gICAgbiA9IE1hdGguZmxvb3IobiAvIDI2KSAtIDE7XG4gIH1cbiAgcmV0dXJuIHJlc3VsdDtcbn1cblxuLyoqXG4gKiBTb25hbXUgRGljdGlvbmFyeSDqtIDrpqwg7YG0656Y7IqkXG4gKiBpMThuIOuUleyFlOuEiOumrOydmCBDUlVEIOuwjyBFeGNlbCBpbXBvcnQvZXhwb3J066W8IOuLtOuLue2VqeuLiOuLpC5cbiAqL1xuZXhwb3J0IGNsYXNzIFNvbmFtdURpY3Rpb25hcnkge1xuICAvKipcbiAgICogVHlwZVNjcmlwdCBDb21waWxlciBBUEnrpbwg7IKs7Jqp7ZWY7JesIGRpY3Qg7YyM7J28IO2MjOyLsVxuICAgKlxuICAgKiDsp4Dsm5Ag7Yyo7YS0OlxuICAgKiAtIGV4cG9ydCBkZWZhdWx0IHsgLi4uIH0gYXMgY29uc3Q7XG4gICAqIC0gZXhwb3J0IGRlZmF1bHQgZGVmaW5lTG9jYWxlKHsgLi4uIH0pO1xuICAgKiAtIOusuOyekOyXtCDqsJI6IFwia2V5XCI6IFwidmFsdWVcIiDrmJDripQga2V5OiBgdmFsdWVgXG4gICAqIC0g7ZWo7IiYIOqwkjogXCJrZXlcIjogKHBhcmFtOiBUeXBlKSA9PiBgdGVtcGxhdGVgXG4gICAqL1xuICBwYXJzZURpY3RGaWxlKGZpbGVQYXRoOiBzdHJpbmcpOiBEaWN0RW50cnlbXSB7XG4gICAgY29uc3QgY29udGVudCA9IGZzLnJlYWRGaWxlU3luYyhmaWxlUGF0aCwgXCJ1dGYtOFwiKTtcbiAgICBjb25zdCBzb3VyY2VGaWxlID0gdHMuY3JlYXRlU291cmNlRmlsZShmaWxlUGF0aCwgY29udGVudCwgdHMuU2NyaXB0VGFyZ2V0LkxhdGVzdCwgdHJ1ZSk7XG5cbiAgICBjb25zdCBlbnRyaWVzOiBEaWN0RW50cnlbXSA9IFtdO1xuXG4gICAgdHMuZm9yRWFjaENoaWxkKHNvdXJjZUZpbGUsIChub2RlKSA9PiB7XG4gICAgICBpZiAodHMuaXNFeHBvcnRBc3NpZ25tZW50KG5vZGUpKSB7XG4gICAgICAgIGNvbnN0IG9iamVjdExpdGVyYWwgPSB0aGlzLnVud3JhcFRvT2JqZWN0TGl0ZXJhbChub2RlLmV4cHJlc3Npb24pO1xuICAgICAgICBpZiAob2JqZWN0TGl0ZXJhbCkge1xuICAgICAgICAgIHRoaXMuZXh0cmFjdEVudHJpZXNGcm9tT2JqZWN0KG9iamVjdExpdGVyYWwsIHNvdXJjZUZpbGUsIGVudHJpZXMpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICByZXR1cm4gZW50cmllcztcbiAgfVxuXG4gIC8qKlxuICAgKiDtjIzsnbzsl5DshJwg7Yq57KCVIOydtOumhOydmCBjb25zdCDshKDslrjsnYQg7LC+7JWEIE9iamVjdExpdGVyYWwg7YyM7IuxXG4gICAqIOyYiDogY29uc3QgZW50aXR5TGFiZWxzID0geyAuLi4gfSBhcyBjb25zdDtcbiAgICovXG4gIHBhcnNlQ29uc3RPYmplY3REZWNsYXJhdGlvbihmaWxlUGF0aDogc3RyaW5nLCB2YXJOYW1lOiBzdHJpbmcpOiBEaWN0RW50cnlbXSB7XG4gICAgY29uc3QgY29udGVudCA9IGZzLnJlYWRGaWxlU3luYyhmaWxlUGF0aCwgXCJ1dGYtOFwiKTtcbiAgICBjb25zdCBzb3VyY2VGaWxlID0gdHMuY3JlYXRlU291cmNlRmlsZShmaWxlUGF0aCwgY29udGVudCwgdHMuU2NyaXB0VGFyZ2V0LkxhdGVzdCwgdHJ1ZSk7XG5cbiAgICBjb25zdCBlbnRyaWVzOiBEaWN0RW50cnlbXSA9IFtdO1xuXG4gICAgdHMuZm9yRWFjaENoaWxkKHNvdXJjZUZpbGUsIChub2RlKSA9PiB7XG4gICAgICBpZiAodHMuaXNWYXJpYWJsZVN0YXRlbWVudChub2RlKSkge1xuICAgICAgICBmb3IgKGNvbnN0IGRlY2wgb2Ygbm9kZS5kZWNsYXJhdGlvbkxpc3QuZGVjbGFyYXRpb25zKSB7XG4gICAgICAgICAgaWYgKHRzLmlzSWRlbnRpZmllcihkZWNsLm5hbWUpICYmIGRlY2wubmFtZS50ZXh0ID09PSB2YXJOYW1lICYmIGRlY2wuaW5pdGlhbGl6ZXIpIHtcbiAgICAgICAgICAgIGNvbnN0IG9iamVjdExpdGVyYWwgPSB0aGlzLnVud3JhcFRvT2JqZWN0TGl0ZXJhbChkZWNsLmluaXRpYWxpemVyKTtcbiAgICAgICAgICAgIGlmIChvYmplY3RMaXRlcmFsKSB7XG4gICAgICAgICAgICAgIHRoaXMuZXh0cmFjdEVudHJpZXNGcm9tT2JqZWN0KG9iamVjdExpdGVyYWwsIHNvdXJjZUZpbGUsIGVudHJpZXMpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuXG4gICAgcmV0dXJuIGVudHJpZXM7XG4gIH1cblxuICAvKipcbiAgICog66y47J6Q7Je07J20IO2ZlOyCtO2RnCDtlajsiJgg65iQ64qUIO2VqOyImCDtkZztmITsi53snbjsp4Ag7YyQ67OEXG4gICAqL1xuICBpc0V4cHJlc3Npb25GdW5jdGlvbihjb2RlOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICAvLyDruYgg66y47J6Q7Je07J2064KYIOqzteuwseunjCDsnojripQg6rK97JqwXG4gICAgaWYgKCFjb2RlLnRyaW0oKSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIGNvbnN0IEFSUk9XX0ZVTkNUSU9OX1BBVFRFUk4gPSAvXlxccypcXChbXildKlxcKVxccyo9Pi87XG5cbiAgICByZXR1cm4gQVJST1dfRlVOQ1RJT05fUEFUVEVSTi50ZXN0KGNvZGUpO1xuICB9XG5cbiAgLyoqXG4gICAqIGV4cG9ydCBkZWZhdWx0IO2RnO2YhOyLneyXkOyEnCBPYmplY3RMaXRlcmFsRXhwcmVzc2lvbiDstpTstpxcbiAgICogLSBhcyBjb25zdCDsspjrpqxcbiAgICogLSBkZWZpbmVMb2NhbGUoeyAuLi4gfSkg7Zi47LacIOyymOumrFxuICAgKi9cbiAgcHJpdmF0ZSB1bndyYXBUb09iamVjdExpdGVyYWwoZXhwcjogdHMuRXhwcmVzc2lvbik6IHRzLk9iamVjdExpdGVyYWxFeHByZXNzaW9uIHwgbnVsbCB7XG4gICAgLy8gYXMgY29uc3Qg7LKY66asXG4gICAgaWYgKHRzLmlzQXNFeHByZXNzaW9uKGV4cHIpKSB7XG4gICAgICByZXR1cm4gdGhpcy51bndyYXBUb09iamVjdExpdGVyYWwoZXhwci5leHByZXNzaW9uKTtcbiAgICB9XG4gICAgLy8g7KeB7KCRIOqwneyytCDrpqzthLDrn7RcbiAgICBpZiAodHMuaXNPYmplY3RMaXRlcmFsRXhwcmVzc2lvbihleHByKSkge1xuICAgICAgcmV0dXJuIGV4cHI7XG4gICAgfVxuICAgIC8vIGRlZmluZUxvY2FsZSh7IC4uLiB9KSDtmLjstpxcbiAgICBpZiAodHMuaXNDYWxsRXhwcmVzc2lvbihleHByKSkge1xuICAgICAgY29uc3QgZmlyc3RBcmcgPSBleHByLmFyZ3VtZW50c1swXTtcbiAgICAgIGlmIChmaXJzdEFyZyAmJiB0cy5pc09iamVjdExpdGVyYWxFeHByZXNzaW9uKGZpcnN0QXJnKSkge1xuICAgICAgICByZXR1cm4gZmlyc3RBcmc7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIE9iamVjdExpdGVyYWxFeHByZXNzaW9u7JeQ7IScIERpY3RFbnRyeSDstpTstpxcbiAgICovXG4gIHByaXZhdGUgZXh0cmFjdEVudHJpZXNGcm9tT2JqZWN0KFxuICAgIG9iamVjdExpdGVyYWw6IHRzLk9iamVjdExpdGVyYWxFeHByZXNzaW9uLFxuICAgIHNvdXJjZUZpbGU6IHRzLlNvdXJjZUZpbGUsXG4gICAgZW50cmllczogRGljdEVudHJ5W10sXG4gICk6IHZvaWQge1xuICAgIGZvciAoY29uc3QgcHJvcCBvZiBvYmplY3RMaXRlcmFsLnByb3BlcnRpZXMpIHtcbiAgICAgIGNvbnN0IGVudHJ5ID0gdGhpcy5leHRyYWN0RGljdEVudHJ5KHByb3AsIHNvdXJjZUZpbGUpO1xuICAgICAgaWYgKGVudHJ5KSB7XG4gICAgICAgIGVudHJpZXMucHVzaChlbnRyeSk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFByb3BlcnR5TmFtZeyXkOyEnCDtgqQg66y47J6Q7Je0IOy2lOy2nFxuICAgKiAtIOusuOyekOyXtCDrpqzthLDrn7Q6IFwia2V5XCJcbiAgICogLSDsi53rs4TsnpA6IGtleSAodW5xdW90ZWQpXG4gICAqL1xuICBwcml2YXRlIGdldFByb3BlcnR5S2V5KG5hbWU6IHRzLlByb3BlcnR5TmFtZSk6IHN0cmluZyB8IG51bGwge1xuICAgIGlmICh0cy5pc1N0cmluZ0xpdGVyYWwobmFtZSkpIHtcbiAgICAgIHJldHVybiBuYW1lLnRleHQ7XG4gICAgfVxuICAgIGlmICh0cy5pc0lkZW50aWZpZXIobmFtZSkpIHtcbiAgICAgIHJldHVybiBuYW1lLnRleHQ7XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIO2UhOuhnO2NvO2LsOyXkOyEnCBEaWN0RW50cnkg7LaU7LacXG4gICAqIC0g66y47J6Q7Je0OiDsi6TsoJwg66y47J6Q7Je0IOqwklxuICAgKiAtIO2VqOyImDog7JuQ67O4IOyGjOyKpCAo7Jes65+sIOykhOydgCDtlZwg7KSE66GcIOygleq3nO2ZlClcbiAgICovXG4gIHByaXZhdGUgZXh0cmFjdERpY3RFbnRyeShcbiAgICBwcm9wOiB0cy5PYmplY3RMaXRlcmFsRWxlbWVudExpa2UsXG4gICAgc291cmNlRmlsZTogdHMuU291cmNlRmlsZSxcbiAgKTogRGljdEVudHJ5IHwgbnVsbCB7XG4gICAgaWYgKCF0cy5pc1Byb3BlcnR5QXNzaWdubWVudChwcm9wKSkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgY29uc3Qga2V5ID0gdGhpcy5nZXRQcm9wZXJ0eUtleShwcm9wLm5hbWUpO1xuICAgIGlmICgha2V5KSByZXR1cm4gbnVsbDtcblxuICAgIGNvbnN0IGluaXQgPSBwcm9wLmluaXRpYWxpemVyO1xuXG4gICAgLy8g7ZmU7IK07ZGcIO2VqOyImFxuICAgIGlmICh0cy5pc0Fycm93RnVuY3Rpb24oaW5pdCkpIHtcbiAgICAgIGNvbnN0IGZ1bmNUZXh0ID0gaW5pdC5nZXRUZXh0KHNvdXJjZUZpbGUpO1xuICAgICAgY29uc3Qgbm9ybWFsaXplZCA9IGZ1bmNUZXh0LnJlcGxhY2UoL1xccypcXG5cXHMqL2csIFwiIFwiKS50cmltKCk7XG4gICAgICByZXR1cm4geyBrZXksIHZhbHVlOiBub3JtYWxpemVkLCBpc0Z1bmN0aW9uOiB0cnVlIH07XG4gICAgfVxuXG4gICAgLy8g66y47J6Q7Je0IOumrO2EsOuftFxuICAgIGlmICh0cy5pc1N0cmluZ0xpdGVyYWwoaW5pdCkpIHtcbiAgICAgIHJldHVybiB7IGtleSwgdmFsdWU6IGluaXQudGV4dCwgaXNGdW5jdGlvbjogZmFsc2UgfTtcbiAgICB9XG5cbiAgICAvLyDthZztlIzrpr8g66as7YSw65+0ICjrs4DsiJgg7JeG7J2MKVxuICAgIGlmICh0cy5pc05vU3Vic3RpdHV0aW9uVGVtcGxhdGVMaXRlcmFsKGluaXQpKSB7XG4gICAgICByZXR1cm4geyBrZXksIHZhbHVlOiBpbml0LnRleHQsIGlzRnVuY3Rpb246IGZhbHNlIH07XG4gICAgfVxuXG4gICAgLy8g6riw7YOAICjsmIg6IO2VqOyImCDtkZztmITsi50pXG4gICAgcmV0dXJuIHtcbiAgICAgIGtleSxcbiAgICAgIHZhbHVlOiBpbml0LmdldFRleHQoc291cmNlRmlsZSksXG4gICAgICBpc0Z1bmN0aW9uOiB0cy5pc0Z1bmN0aW9uRXhwcmVzc2lvbihpbml0KSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIO2UhOuhnOygne2KuOydmCBpMThuIGRpY3Qg7YyM7J28IOqyveuhnOulvCDrsJjtmZjtlanri4jri6QuXG4gICAqIEBwYXJhbSBsb2NhbGUgLSDroZzsvIDsnbwgKGtvLCBlbiDrk7EpXG4gICAqIEBwYXJhbSB0YXJnZXQgLSDtg4Dqsp8g65SU66CJ7Yag66asIChhcGksIHdlYiwgYXBwKVxuICAgKi9cbiAgZ2V0UHJvamVjdERpY3RQYXRoKGxvY2FsZTogc3RyaW5nLCB0YXJnZXQ6IFwiYXBpXCIgfCBcIndlYlwiIHwgXCJhcHBcIiA9IFwiYXBpXCIpOiBzdHJpbmcge1xuICAgIGNvbnN0IGRpciA9IHRhcmdldCA9PT0gXCJhcGlcIiA/IFNvbmFtdS5jb25maWcuYXBpLmRpciA6IHRhcmdldDtcbiAgICByZXR1cm4gcGF0aC5qb2luKFNvbmFtdS5hcHBSb290UGF0aCwgZGlyLCBcInNyY1wiLCBcImkxOG5cIiwgYCR7bG9jYWxlfS50c2ApO1xuICB9XG5cbiAgLyoqXG4gICAqIHNvbmFtdSDrgrTsnqUgZGljdCDtjIzsnbwg6rK966Gc66W8IOuwmO2ZmO2VqeuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgZ2V0U29uYW11RGljdFBhdGgobG9jYWxlOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGNvbnN0IHBhY2thZ2VSb290ID0gcGF0aC5yZXNvbHZlKGltcG9ydC5tZXRhLmRpcm5hbWUsIFwiLi5cIiwgXCIuLlwiKTtcbiAgICByZXR1cm4gcGF0aC5qb2luKHBhY2thZ2VSb290LCBcInNyY1wiLCBcImRpY3RcIiwgYCR7bG9jYWxlfS50c2ApO1xuICB9XG5cbiAgLyoqXG4gICAqIO2VhOyalO2VnCBkaWN0IO2CpOqwgCDtlITroZzsoJ3tirjsl5Ag7KG07J6s7ZWY64qU7KeAIO2ZleyduO2VmOqzoCwg7JeG7Jy866m0IOy2lOqwgO2VqeuLiOuLpC5cbiAgICogZGVmYXVsdExvY2FsZeyXkOunjCDstpTqsIDtlZjrqbAsIOuLpOuluCBsb2NhbGXsnYAg7IKs7Jqp7J6Q6rCAIOyngeygkSDrsojsl63tlbTslbwg7ZWp64uI64ukLlxuICAgKlxuICAgKiBAcGFyYW0gcmVxdWlyZWRLZXlzIC0g7ZWE7JqU7ZWcIO2CpCDrqqnroZ1cbiAgICogQHBhcmFtIHRhcmdldCAtIO2DgOqynyDrlJTroInthqDrpqwgKGFwaSwgd2ViLCBhcHApXG4gICAqIEByZXR1cm5zIOy2lOqwgOuQnCDtgqQg66qp66GdXG4gICAqL1xuICBhc3luYyBlbnN1cmVEaWN0S2V5cyhcbiAgICByZXF1aXJlZEtleXM6IHN0cmluZ1tdLFxuICAgIHRhcmdldDogXCJhcGlcIiB8IFwid2ViXCIgfCBcImFwcFwiID0gXCJhcGlcIixcbiAgKTogUHJvbWlzZTxzdHJpbmdbXT4ge1xuICAgIGNvbnN0IHsgZGVmYXVsdExvY2FsZSB9ID0gU29uYW11LmNvbmZpZy5pMThuO1xuICAgIGNvbnN0IHByb2plY3REaWN0UGF0aCA9IHRoaXMuZ2V0UHJvamVjdERpY3RQYXRoKGRlZmF1bHRMb2NhbGUsIHRhcmdldCk7XG5cbiAgICAvLyDtlITroZzsoJ3tirggZGljdCDtjIzsnbzsnbQg7JeG7Jy866m0IOyVhOustOqyg+uPhCDtlZjsp4Ag7JWK7J2MXG4gICAgaWYgKCFmcy5leGlzdHNTeW5jKHByb2plY3REaWN0UGF0aCkpIHtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG5cbiAgICAvLyDtlITroZzsoJ3tirggZGljdOyXkOyEnCDquLDsobQg7YKkIO2MjOyLsVxuICAgIGNvbnN0IHByb2plY3RFbnRyaWVzID0gdGhpcy5wYXJzZURpY3RGaWxlKHByb2plY3REaWN0UGF0aCk7XG4gICAgY29uc3QgZXhpc3RpbmdLZXlzID0gbmV3IFNldChwcm9qZWN0RW50cmllcy5tYXAoKGUpID0+IGUua2V5KSk7XG5cbiAgICAvLyDriITrnb3rkJwg7YKkIOywvuq4sFxuICAgIGNvbnN0IG1pc3NpbmdLZXlzID0gcmVxdWlyZWRLZXlzLmZpbHRlcigoa2V5KSA9PiAhZXhpc3RpbmdLZXlzLmhhcyhrZXkpKTtcblxuICAgIGlmIChtaXNzaW5nS2V5cy5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG5cbiAgICAvLyBzb25hbXUgZGljdOyXkOyEnCDquLDrs7jqsJIg6rCA7KC47Jik6riwXG4gICAgY29uc3Qgc29uYW11RGljdFBhdGggPSB0aGlzLmdldFNvbmFtdURpY3RQYXRoKGRlZmF1bHRMb2NhbGUpO1xuICAgIGlmICghZnMuZXhpc3RzU3luYyhzb25hbXVEaWN0UGF0aCkpIHtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG5cbiAgICBjb25zdCBzb25hbXVFbnRyaWVzID0gdGhpcy5wYXJzZURpY3RGaWxlKHNvbmFtdURpY3RQYXRoKTtcbiAgICBjb25zdCBzb25hbXVEaWN0ID0gbmV3IE1hcChzb25hbXVFbnRyaWVzLm1hcCgoZSkgPT4gW2Uua2V5LCBlXSkpO1xuXG4gICAgLy8g7LaU6rCA7ZWgIOyXlO2KuOumrCDsg53shLFcbiAgICBjb25zdCBlbnRyaWVzVG9BZGQgPSBtaXNzaW5nS2V5c1xuICAgICAgLm1hcCgoa2V5KSA9PiBzb25hbXVEaWN0LmdldChrZXkpKVxuICAgICAgLmZpbHRlcigoZW50cnkpOiBlbnRyeSBpcyBOb25OdWxsYWJsZTx0eXBlb2YgZW50cnk+ID0+IGVudHJ5ICE9PSB1bmRlZmluZWQpO1xuXG4gICAgaWYgKGVudHJpZXNUb0FkZC5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG5cbiAgICAvLyDtlITroZzsoJ3tirggZGljdCDtjIzsnbzsl5Ag7LaU6rCAXG4gICAgYXdhaXQgdGhpcy5hcHBlbmRFbnRyaWVzVG9EaWN0RmlsZShwcm9qZWN0RGljdFBhdGgsIGVudHJpZXNUb0FkZCwgZGVmYXVsdExvY2FsZSwgdHJ1ZSk7XG5cbiAgICByZXR1cm4gZW50cmllc1RvQWRkLm1hcCgoZSkgPT4gZS5rZXkpO1xuICB9XG5cbiAgLyoqXG4gICAqIGRpY3Qg7YyM7J287JeQIOyXlO2KuOumrOulvCDstpTqsIDtlanri4jri6QuXG4gICAqIOq4sOyhtCDtjIzsnbzsnYQg7YyM7Iux7ZWY6rOgLCDsg4gg7JeU7Yq466as66W8IOy2lOqwgO2VnCDrkqQsIOyghOyytCDtjIzsnbzsnYQg7J6s7IOd7ISx7ZWp64uI64ukLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBhcHBlbmRFbnRyaWVzVG9EaWN0RmlsZShcbiAgICBmaWxlUGF0aDogc3RyaW5nLFxuICAgIGVudHJpZXM6IERpY3RFbnRyeVtdLFxuICAgIGxvY2FsZTogc3RyaW5nLFxuICAgIGlzRGVmYXVsdExvY2FsZTogYm9vbGVhbixcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8g6riw7KG0IGVudHJpZXMg7YyM7IuxXG4gICAgY29uc3QgZXhpc3RpbmdFbnRyaWVzID0gdGhpcy5wYXJzZURpY3RGaWxlKGZpbGVQYXRoKTtcblxuICAgIC8vIOyDiCBlbnRyaWVzIOy2lOqwgFxuICAgIGZvciAoY29uc3QgZW50cnkgb2YgZW50cmllcykge1xuICAgICAgZXhpc3RpbmdFbnRyaWVzLnB1c2goZW50cnkpO1xuICAgIH1cblxuICAgIC8vIO2MjOydvCDsnqzsg53shLFcbiAgICBjb25zdCBjb250ZW50ID0gdGhpcy5nZW5lcmF0ZVByb2plY3REaWN0KGxvY2FsZSwgZXhpc3RpbmdFbnRyaWVzLCBpc0RlZmF1bHRMb2NhbGUpO1xuICAgIGNvbnN0IGZvcm1hdHRlZCA9IGZvcm1hdENvZGUoY29udGVudCwgXCJ0eXBlc2NyaXB0XCIsIGZpbGVQYXRoKTtcbiAgICBmcy53cml0ZUZpbGVTeW5jKGZpbGVQYXRoLCBmb3JtYXR0ZWQsIFwidXRmLThcIik7XG4gIH1cblxuICAvKipcbiAgICog7ZWo7IiYIOqwkuuTpOyXkOyEnCDsgqzsmqnrkJjripQg7Zes7Y28IO2VqOyImOulvCDqsJDsp4Dtlanri4jri6QuXG4gICAqL1xuICBwcml2YXRlIGRldGVjdFVzZWRIZWxwZXJzKGVudHJpZXM6IERpY3RFbnRyeVtdKToge1xuICAgIGhlbHBlcnM6IHN0cmluZ1tdO1xuICAgIHVzZXNGb3JtYXQ6IGJvb2xlYW47XG4gIH0ge1xuICAgIGNvbnN0IGZ1bmN0aW9uRW50cmllcyA9IGVudHJpZXMuZmlsdGVyKChlKSA9PiBlLmlzRnVuY3Rpb24pO1xuICAgIGNvbnN0IGhlbHBlcnM6IHN0cmluZ1tdID0gW107XG5cbiAgICBmb3IgKGNvbnN0IGhlbHBlciBvZiBbXCJwbHVyYWxcIiwgXCJqb3NhXCJdKSB7XG4gICAgICAvLyDtlajsiJjrqoXsnbQg64uo7Ja0IOqyveqzhOuhnCDsgqzsmqnrkJjripTsp4Ag7ZmV7J24ICjsmIg6IHBsdXJhbCgg65iQ64qUIHBsdXJhbCwpXG4gICAgICBjb25zdCBwYXR0ZXJuID0gbmV3IFJlZ0V4cChgXFxcXGIke2hlbHBlcn1cXFxccypcXFxcKGApO1xuICAgICAgaWYgKGZ1bmN0aW9uRW50cmllcy5zb21lKChlKSA9PiBwYXR0ZXJuLnRlc3QoZS52YWx1ZSkpKSB7XG4gICAgICAgIGhlbHBlcnMucHVzaChoZWxwZXIpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIGZvcm1hdCDsgqzsmqkg7Jes67aAIOuzhOuPhCDqsJDsp4AgKGZvcm1hdC5udW1iZXIoLi4uKSwgZm9ybWF0LmRhdGUoLi4uKSDrk7EpXG4gICAgY29uc3QgZm9ybWF0UGF0dGVybiA9IC9cXGJmb3JtYXRcXC5cXHcrXFxzKlxcKC87XG4gICAgY29uc3QgdXNlc0Zvcm1hdCA9IGZ1bmN0aW9uRW50cmllcy5zb21lKChlKSA9PiBmb3JtYXRQYXR0ZXJuLnRlc3QoZS52YWx1ZSkpO1xuXG4gICAgcmV0dXJuIHsgaGVscGVycywgdXNlc0Zvcm1hdCB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFByb2plY3QgZGljdCDtjIzsnbwg7IOd7ISxXG4gICAqL1xuICBnZW5lcmF0ZVByb2plY3REaWN0KGxvY2FsZTogc3RyaW5nLCBlbnRyaWVzOiBEaWN0RW50cnlbXSwgaXNEZWZhdWx0TG9jYWxlOiBib29sZWFuKTogc3RyaW5nIHtcbiAgICAvLyBrZXkg7JWM7YyM67KzIOyInCDsoJXroKxcbiAgICBjb25zdCBzb3J0ZWQgPSBbLi4uZW50cmllc10uc29ydCgoYSwgYikgPT4gYS5rZXkubG9jYWxlQ29tcGFyZShiLmtleSkpO1xuXG4gICAgY29uc3QgbGluZXM6IHN0cmluZ1tdID0gW107XG5cbiAgICAvLyDtlajsiJgg6rCS7JeQ7IScIOyCrOyaqeuQmOuKlCDtl6ztjbwg7ZWo7IiYIOqwkOyngFxuICAgIGNvbnN0IHsgaGVscGVycywgdXNlc0Zvcm1hdCB9ID0gdGhpcy5kZXRlY3RVc2VkSGVscGVycyhlbnRyaWVzKTtcblxuICAgIC8vIO2XrO2NvCDtlajsiJggaW1wb3J0IOy2lOqwgFxuICAgIGNvbnN0IGltcG9ydHMgPSBbLi4uaGVscGVyc107XG4gICAgaWYgKHVzZXNGb3JtYXQpIHtcbiAgICAgIGltcG9ydHMucHVzaChcImNyZWF0ZUZvcm1hdFwiKTtcbiAgICB9XG4gICAgaWYgKGltcG9ydHMubGVuZ3RoID4gMCkge1xuICAgICAgbGluZXMucHVzaChgaW1wb3J0IHsgJHtpbXBvcnRzLmpvaW4oXCIsIFwiKX0gfSBmcm9tIFwic29uYW11L2RpY3RcIjtgKTtcbiAgICB9XG5cbiAgICBpZiAoIWlzRGVmYXVsdExvY2FsZSkge1xuICAgICAgbGluZXMucHVzaCgnaW1wb3J0IHsgZGVmaW5lTG9jYWxlIH0gZnJvbSBcIi4vc2QuZ2VuZXJhdGVkXCI7Jyk7XG4gICAgfVxuXG4gICAgaWYgKGltcG9ydHMubGVuZ3RoID4gMCB8fCAhaXNEZWZhdWx0TG9jYWxlKSB7XG4gICAgICBsaW5lcy5wdXNoKFwiXCIpO1xuICAgIH1cblxuICAgIC8vIGZvcm1hdCDsgqzsmqkg7IucIGNyZWF0ZUZvcm1hdCDtmLjstpwg7LaU6rCAXG4gICAgaWYgKHVzZXNGb3JtYXQpIHtcbiAgICAgIGxpbmVzLnB1c2goYGNvbnN0IGZvcm1hdCA9IGNyZWF0ZUZvcm1hdChcIiR7bG9jYWxlfVwiKTtgKTtcbiAgICAgIGxpbmVzLnB1c2goXCJcIik7XG4gICAgfVxuXG4gICAgbGluZXMucHVzaChcIi8qKlwiKTtcbiAgICBsaW5lcy5wdXNoKGAgKiBQcm9qZWN0ICR7bG9jYWxlLnRvVXBwZXJDYXNlKCl9IERpY3Rpb25hcnlgKTtcbiAgICBsaW5lcy5wdXNoKFwiICovXCIpO1xuXG4gICAgaWYgKGlzRGVmYXVsdExvY2FsZSkge1xuICAgICAgbGluZXMucHVzaChcImV4cG9ydCBkZWZhdWx0IHtcIik7XG4gICAgfSBlbHNlIHtcbiAgICAgIGxpbmVzLnB1c2goXCJleHBvcnQgZGVmYXVsdCBkZWZpbmVMb2NhbGUoe1wiKTtcbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIHNvcnRlZCkge1xuICAgICAgaWYgKGVudHJ5LmlzRnVuY3Rpb24pIHtcbiAgICAgICAgLy8g7ZWo7IiY7J24IOqyveyasDog7JuQ7ZiVIOq3uOuMgOuhnCDstpzroKVcbiAgICAgICAgbGluZXMucHVzaChgICBcIiR7ZW50cnkua2V5fVwiOiAke2VudHJ5LnZhbHVlfSxgKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGxpbmVzLnB1c2goYCAgXCIke2VudHJ5LmtleX1cIjogJHtKU09OLnN0cmluZ2lmeShlbnRyeS52YWx1ZSl9LGApO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChpc0RlZmF1bHRMb2NhbGUpIHtcbiAgICAgIGxpbmVzLnB1c2goXCJ9IGFzIGNvbnN0O1wiKTtcbiAgICB9IGVsc2Uge1xuICAgICAgbGluZXMucHVzaChcIn0pO1wiKTtcbiAgICB9XG4gICAgbGluZXMucHVzaChcIlwiKTtcblxuICAgIHJldHVybiBsaW5lcy5qb2luKFwiXFxuXCIpO1xuICB9XG5cbiAgLyoqXG4gICAqIGkxOG4g7ISk7KCV7J2EIOqwgOyguOyYteuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgZ2V0STE4bkNvbmZpZygpOiBJMThuQ29uZmlnIHtcbiAgICByZXR1cm4gU29uYW11LmNvbmZpZy5pMThuO1xuICB9XG5cbiAgLyoqXG4gICAqIGkxOG4g65SU66CJ7Yag66asIOqyveuhnOulvCDrsJjtmZjtlZjqs6AsIOyXhuycvOuptCDsg53shLHtlanri4jri6QuXG4gICAqL1xuICBwcml2YXRlIGVuc3VyZUkxOG5EaXIoKTogc3RyaW5nIHtcbiAgICBjb25zdCBpMThuRGlyID0gcGF0aC5qb2luKFNvbmFtdS5hcGlSb290UGF0aCwgXCJzcmNcIiwgXCJpMThuXCIpO1xuICAgIGlmICghZnMuZXhpc3RzU3luYyhpMThuRGlyKSkge1xuICAgICAgZnMubWtkaXJTeW5jKGkxOG5EaXIsIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgIH1cbiAgICByZXR1cm4gaTE4bkRpcjtcbiAgfVxuXG4gIC8qKlxuICAgKiBkaWN0IO2MjOydvOydhCDsoIDsnqXtlanri4jri6QuXG4gICAqL1xuICBwcml2YXRlIHNhdmVEaWN0RmlsZShsb2NhbGU6IHN0cmluZywgZW50cmllczogRGljdEVudHJ5W10sIGlzRGVmYXVsdExvY2FsZTogYm9vbGVhbik6IHZvaWQge1xuICAgIGNvbnN0IGkxOG5EaXIgPSB0aGlzLmVuc3VyZUkxOG5EaXIoKTtcbiAgICBjb25zdCBkaWN0UGF0aCA9IHBhdGguam9pbihpMThuRGlyLCBgJHtsb2NhbGV9LnRzYCk7XG4gICAgY29uc3QgY29udGVudCA9IHRoaXMuZ2VuZXJhdGVQcm9qZWN0RGljdChsb2NhbGUsIGVudHJpZXMsIGlzRGVmYXVsdExvY2FsZSk7XG4gICAgY29uc3QgZm9ybWF0dGVkID0gZm9ybWF0Q29kZShjb250ZW50LCBcInR5cGVzY3JpcHRcIiwgZGljdFBhdGgpO1xuICAgIGZzLndyaXRlRmlsZVN5bmMoZGljdFBhdGgsIGZvcm1hdHRlZCwgXCJ1dGYtOFwiKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBpMThuIGtleeulvCDtjIzsi7HtlZjsl6wgZW50aXR5IOq0gOugqCDsoJXrs7Qg7LaU7LacXG4gICAqL1xuICBwYXJzZUVudGl0eUtleShrZXk6IHN0cmluZyk6IEVudGl0eUtleUluZm8ge1xuICAgIC8vIGVudGl0eS57RW50aXR5SWR9IChsaXN0LCBjcmVhdGUsIGVkaXQg7KCc7Jm4KVxuICAgIGNvbnN0IGVudGl0eVRpdGxlTWF0Y2ggPSBrZXkubWF0Y2goL15lbnRpdHlcXC4oW0EtWl1bYS16QS1aMC05XSopJC8pO1xuICAgIGlmIChcbiAgICAgIGVudGl0eVRpdGxlTWF0Y2ggJiZcbiAgICAgICFrZXkuaW5jbHVkZXMoXCIubGlzdFwiKSAmJlxuICAgICAgIWtleS5pbmNsdWRlcyhcIi5jcmVhdGVcIikgJiZcbiAgICAgICFrZXkuaW5jbHVkZXMoXCIuZWRpdFwiKVxuICAgICkge1xuICAgICAgcmV0dXJuIHsgdHlwZTogXCJlbnRpdHlUaXRsZVwiLCBlbnRpdHlJZDogZW50aXR5VGl0bGVNYXRjaFsxXSB9O1xuICAgIH1cblxuICAgIC8vIGVudGl0eS57RW50aXR5SWR9Lntwcm9wTmFtZX1cbiAgICBjb25zdCBwcm9wRGVzY01hdGNoID0ga2V5Lm1hdGNoKC9eZW50aXR5XFwuKFtBLVpdW2EtekEtWjAtOV0qKVxcLihbYS16X11bYS16MC05X10qKSQvKTtcbiAgICBpZiAocHJvcERlc2NNYXRjaCkge1xuICAgICAgcmV0dXJuIHsgdHlwZTogXCJwcm9wRGVzY1wiLCBlbnRpdHlJZDogcHJvcERlc2NNYXRjaFsxXSwgcHJvcE5hbWU6IHByb3BEZXNjTWF0Y2hbMl0gfTtcbiAgICB9XG5cbiAgICAvLyBlbnVtLntFbnVtSWR9Lnt2YWx1ZX1cbiAgICBjb25zdCBlbnVtTGFiZWxNYXRjaCA9IGtleS5tYXRjaCgvXmVudW1cXC4oW0EtWl1bYS16QS1aMC05XSopXFwuKC4rKSQvKTtcbiAgICBpZiAoZW51bUxhYmVsTWF0Y2gpIHtcbiAgICAgIHJldHVybiB7IHR5cGU6IFwiZW51bUxhYmVsXCIsIGVudW1JZDogZW51bUxhYmVsTWF0Y2hbMV0sIGVudW1WYWx1ZTogZW51bUxhYmVsTWF0Y2hbMl0gfTtcbiAgICB9XG5cbiAgICByZXR1cm4geyB0eXBlOiBcIm90aGVyXCIgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBlbnRpdHkga2V57JeQIOuMgO2VtCBlbnRpdHkuanNvbiDsl4XrjbDsnbTtirgg7IiY7ZaJXG4gICAqIEByZXR1cm5zIOyXheuNsOydtO2KuCDsl6zrtoBcbiAgICovXG4gIGFzeW5jIHVwZGF0ZUVudGl0eUJ5S2V5KGtleTogc3RyaW5nLCB2YWx1ZTogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgY29uc3Qga2V5SW5mbyA9IHRoaXMucGFyc2VFbnRpdHlLZXkoa2V5KTtcblxuICAgIHN3aXRjaCAoa2V5SW5mby50eXBlKSB7XG4gICAgICBjYXNlIFwiZW50aXR5VGl0bGVcIjoge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IGVudGl0eSA9IEVudGl0eU1hbmFnZXIuZ2V0KGtleUluZm8uZW50aXR5SWQpO1xuICAgICAgICAgIGlmIChlbnRpdHkudGl0bGUgIT09IHZhbHVlKSB7XG4gICAgICAgICAgICBlbnRpdHkudGl0bGUgPSB2YWx1ZTtcbiAgICAgICAgICAgIGF3YWl0IGVudGl0eS5zYXZlKCk7XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgIC8vIGVudGl0eSBub3QgZm91bmRcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG5cbiAgICAgIGNhc2UgXCJwcm9wRGVzY1wiOiB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgZW50aXR5ID0gRW50aXR5TWFuYWdlci5nZXQoa2V5SW5mby5lbnRpdHlJZCk7XG4gICAgICAgICAgY29uc3QgcHJvcEluZGV4ID0gZW50aXR5LnByb3BzLmZpbmRJbmRleCgocCkgPT4gcC5uYW1lID09PSBrZXlJbmZvLnByb3BOYW1lKTtcbiAgICAgICAgICBpZiAocHJvcEluZGV4ICE9PSAtMSAmJiBlbnRpdHkucHJvcHNbcHJvcEluZGV4XS5kZXNjICE9PSB2YWx1ZSkge1xuICAgICAgICAgICAgZW50aXR5LnByb3BzW3Byb3BJbmRleF0uZGVzYyA9IHZhbHVlO1xuICAgICAgICAgICAgYXdhaXQgZW50aXR5LnNhdmUoKTtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgLy8gZW50aXR5IG5vdCBmb3VuZFxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cblxuICAgICAgY2FzZSBcImVudW1MYWJlbFwiOiB7XG4gICAgICAgIGZvciAoY29uc3QgZW50aXR5SWQgb2YgRW50aXR5TWFuYWdlci5nZXRBbGxJZHMoKSkge1xuICAgICAgICAgIGNvbnN0IGVudGl0eSA9IEVudGl0eU1hbmFnZXIuZ2V0KGVudGl0eUlkKTtcbiAgICAgICAgICBpZiAoZW50aXR5LmVudW1MYWJlbHNba2V5SW5mby5lbnVtSWRdKSB7XG4gICAgICAgICAgICBpZiAoZW50aXR5LmVudW1MYWJlbHNba2V5SW5mby5lbnVtSWRdW2tleUluZm8uZW51bVZhbHVlXSAhPT0gdmFsdWUpIHtcbiAgICAgICAgICAgICAgZW50aXR5LmVudW1MYWJlbHNba2V5SW5mby5lbnVtSWRdW2tleUluZm8uZW51bVZhbHVlXSA9IHZhbHVlO1xuICAgICAgICAgICAgICBhd2FpdCBlbnRpdHkuc2F2ZSgpO1xuICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG5cbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogc2QuZ2VuZXJhdGVkLnRz7JeQ7IScIGVudGl0eSBsYWJlbHMg7LaU7LacXG4gICAqIGVudGl0eS5qc29u7JeQ7IScIOq0gOumrOuQmOuKlCDqsJLrp4wg7Y+s7ZWoICgubGlzdCwgLmNyZWF0ZSwgLmVkaXQg7KCc7Jm4KVxuICAgKi9cbiAgZXh0cmFjdEVudGl0eUxhYmVscygpOiBEaWN0RW50cnlbXSB7XG4gICAgY29uc3Qgc2RQYXRoID0gcGF0aC5qb2luKFNvbmFtdS5hcGlSb290UGF0aCwgXCJzcmNcIiwgXCJpMThuXCIsIFwic2QuZ2VuZXJhdGVkLnRzXCIpO1xuICAgIGlmICghZnMuZXhpc3RzU3luYyhzZFBhdGgpKSB7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMucGFyc2VDb25zdE9iamVjdERlY2xhcmF0aW9uKHNkUGF0aCwgXCJlbnRpdHlMYWJlbHNcIik7XG4gIH1cblxuICAvKipcbiAgICogc2QuZ2VuZXJhdGVkLnRz7JeQ7IScIHJjLWtleXMg7LaU7LacXG4gICAqIHJlYWN0LWNvbXBvbmVudHPsl5DshJwg6rSA66as65CY64qUIGkxOG4g7YKk65OkXG4gICAqIEBwYXJhbSBsb2NhbGUgLSDroZzsvIDsnbwgKGtvLCBlbiDrk7EpXG4gICAqL1xuICBleHRyYWN0UkNLZXlzKGxvY2FsZTogc3RyaW5nKTogRGljdEVudHJ5W10ge1xuICAgIGNvbnN0IHNkUGF0aCA9IHBhdGguam9pbihTb25hbXUuYXBpUm9vdFBhdGgsIFwic3JjXCIsIFwiaTE4blwiLCBcInNkLmdlbmVyYXRlZC50c1wiKTtcbiAgICBpZiAoIWZzLmV4aXN0c1N5bmMoc2RQYXRoKSkge1xuICAgICAgcmV0dXJuIFtdO1xuICAgIH1cblxuICAgIC8vIGxvY2FsZeuzhCDrs4DsiJjrqoUg66ek7ZWRIChzZC50ZW1wbGF0ZS50c+ydmCBnZXRSQ0tleXNWYXJOYW1l6rO8IOuPmeydvClcbiAgICBjb25zdCB2YXJOYW1lID0gKCgpID0+IHtcbiAgICAgIGlmIChsb2NhbGUgPT09IFwia29cIikgcmV0dXJuIFwicmNLZXlzS29cIjtcbiAgICAgIGlmIChsb2NhbGUgPT09IFwiZW5cIikgcmV0dXJuIFwicmNLZXlzRW5cIjtcbiAgICAgIC8vIOuLpOuluCBsb2NhbGXsnYAgZW7snYQgZmFsbGJhY2vsnLzroZwg7IKs7JqpXG4gICAgICByZXR1cm4gXCJyY0tleXNFblwiO1xuICAgIH0pKCk7XG5cbiAgICByZXR1cm4gdGhpcy5wYXJzZUNvbnN0T2JqZWN0RGVjbGFyYXRpb24oc2RQYXRoLCB2YXJOYW1lKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQcm9qZWN0IGRpY3Qg7YyM7J28KFtsb2NhbGVdLnRzKeyXkOyEnCDrlJXshZTrhIjrpqwg66Gc65OcXG4gICAqL1xuICBsb2FkUHJvamVjdERpY3QobG9jYWxlOiBzdHJpbmcpOiB7IGVudHJpZXM6IERpY3RFbnRyeVtdIH0ge1xuICAgIGNvbnN0IGRpY3RQYXRoID0gcGF0aC5qb2luKFNvbmFtdS5hcGlSb290UGF0aCwgXCJzcmNcIiwgXCJpMThuXCIsIGAke2xvY2FsZX0udHNgKTtcbiAgICBpZiAoIWZzLmV4aXN0c1N5bmMoZGljdFBhdGgpKSB7XG4gICAgICByZXR1cm4geyBlbnRyaWVzOiBbXSB9O1xuICAgIH1cbiAgICByZXR1cm4geyBlbnRyaWVzOiB0aGlzLnBhcnNlRGljdEZpbGUoZGljdFBhdGgpIH07XG4gIH1cblxuICAvKipcbiAgICog65SV7IWU64SI66asIOuNsOydtO2EsCDsiJjsp5EgKHNvbmFtdSArIGVudGl0eSArIHByb2plY3QpXG4gICAqL1xuICBhc3luYyBjb2xsZWN0RGljdGlvbmFyeSgpOiBQcm9taXNlPERpY3Rpb25hcnlSZXN1bHQ+IHtcbiAgICBjb25zdCB7IGRlZmF1bHRMb2NhbGUsIHN1cHBvcnRlZExvY2FsZXMgfSA9IHRoaXMuZ2V0STE4bkNvbmZpZygpO1xuICAgIGNvbnN0IGxvY2FsZXMgPSBzdXBwb3J0ZWRMb2NhbGVzO1xuXG4gICAgY29uc3Qgcm93czogRGljdGlvbmFyeVJvd1tdID0gW107XG4gICAgY29uc3Qgcm93TWFwID0gbmV3IE1hcDxzdHJpbmcsIERpY3Rpb25hcnlSb3c+KCk7XG5cbiAgICAvLyAxLiBSQyBLZXlzIChzb25hbXUgc291cmNlLCDqsIEgbG9jYWxl67OEKVxuICAgIGZvciAoY29uc3QgbG9jYWxlIG9mIGxvY2FsZXMpIHtcbiAgICAgIGNvbnN0IHJjS2V5cyA9IHRoaXMuZXh0cmFjdFJDS2V5cyhsb2NhbGUpO1xuICAgICAgZm9yIChjb25zdCByY0tleSBvZiByY0tleXMpIHtcbiAgICAgICAgbGV0IHJvdyA9IHJvd01hcC5nZXQocmNLZXkua2V5KTtcbiAgICAgICAgaWYgKCFyb3cpIHtcbiAgICAgICAgICByb3cgPSB7XG4gICAgICAgICAgICBrZXk6IHJjS2V5LmtleSxcbiAgICAgICAgICAgIHNvdXJjZTogXCJzb25hbXVcIixcbiAgICAgICAgICAgIGlzRnVuY3Rpb246IHJjS2V5LmlzRnVuY3Rpb24gPz8gZmFsc2UsXG4gICAgICAgICAgfTtcbiAgICAgICAgICByb3dNYXAuc2V0KHJjS2V5LmtleSwgcm93KTtcbiAgICAgICAgfVxuICAgICAgICByb3dbbG9jYWxlXSA9IHJjS2V5LnZhbHVlO1xuICAgICAgICBpZiAocmNLZXkuaXNGdW5jdGlvbikge1xuICAgICAgICAgIHJvdy5pc0Z1bmN0aW9uID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIDIuIEVudGl0eSBsYWJlbHMgKGRlZmF1bHQgbG9jYWxlIOq4sOykgClcbiAgICBjb25zdCBlbnRpdHlMYWJlbHMgPSB0aGlzLmV4dHJhY3RFbnRpdHlMYWJlbHMoKTtcbiAgICBmb3IgKGNvbnN0IGxhYmVsIG9mIGVudGl0eUxhYmVscykge1xuICAgICAgY29uc3Qgcm93OiBEaWN0aW9uYXJ5Um93ID0ge1xuICAgICAgICBrZXk6IGxhYmVsLmtleSxcbiAgICAgICAgc291cmNlOiBcImVudGl0eVwiLFxuICAgICAgICBpc0Z1bmN0aW9uOiBsYWJlbC5pc0Z1bmN0aW9uID8/IGZhbHNlLFxuICAgICAgICBbZGVmYXVsdExvY2FsZV06IGxhYmVsLnZhbHVlLFxuICAgICAgfTtcbiAgICAgIHJvd01hcC5zZXQobGFiZWwua2V5LCByb3cpO1xuICAgIH1cblxuICAgIC8vIDMuIFByb2plY3QgZGljdCAo6rCBIGxvY2FsZeuzhClcbiAgICBmb3IgKGNvbnN0IGxvY2FsZSBvZiBsb2NhbGVzKSB7XG4gICAgICBjb25zdCB7IGVudHJpZXMgfSA9IHRoaXMubG9hZFByb2plY3REaWN0KGxvY2FsZSk7XG4gICAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGVudHJpZXMpIHtcbiAgICAgICAgY29uc3QgZXhpc3RpbmcgPSByb3dNYXAuZ2V0KGVudHJ5LmtleSk7XG4gICAgICAgIGlmIChleGlzdGluZykge1xuICAgICAgICAgIC8vIHNvbmFtdSwgZW50aXR5IHNvdXJjZeqwgCDsnojsnLzrqbQg7ZW064u5IGxvY2FsZSDqsJLrp4wg7LaU6rCAXG4gICAgICAgICAgZXhpc3RpbmdbbG9jYWxlXSA9IGVudHJ5LnZhbHVlO1xuICAgICAgICAgIGlmIChlbnRyeS5pc0Z1bmN0aW9uKSB7XG4gICAgICAgICAgICBleGlzdGluZy5pc0Z1bmN0aW9uID0gdHJ1ZTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gcHJvamVjdCBzb3VyY2XroZwg7IOI66GcIOy2lOqwgFxuICAgICAgICAgIGxldCByb3cgPSByb3dNYXAuZ2V0KGVudHJ5LmtleSk7XG4gICAgICAgICAgaWYgKCFyb3cpIHtcbiAgICAgICAgICAgIHJvdyA9IHtcbiAgICAgICAgICAgICAga2V5OiBlbnRyeS5rZXksXG4gICAgICAgICAgICAgIHNvdXJjZTogXCJwcm9qZWN0XCIsXG4gICAgICAgICAgICAgIGlzRnVuY3Rpb246IGVudHJ5LmlzRnVuY3Rpb24sXG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgcm93TWFwLnNldChlbnRyeS5rZXksIHJvdyk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJvd1tsb2NhbGVdID0gZW50cnkudmFsdWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICByb3dzLnB1c2goLi4ucm93TWFwLnZhbHVlcygpKTtcbiAgICByb3dzLnNvcnQoKGEsIGIpID0+IGEua2V5LmxvY2FsZUNvbXBhcmUoYi5rZXkpKTtcblxuICAgIC8vIO2GteqzhCDqs4TsgrA6IGxvY2FsZeuzhCAo7LGE7JuM7KeEIOqwkiAvIOyghOyytCDtgqQg7IiYKVxuICAgIGNvbnN0IHN0YXRzOiBSZWNvcmQ8c3RyaW5nLCB7IHRvdGFsOiBudW1iZXI7IGZpbGxlZDogbnVtYmVyOyBwZXJjZW50OiBudW1iZXIgfT4gPSB7fTtcbiAgICBjb25zdCB0b3RhbCA9IHJvd3MubGVuZ3RoO1xuICAgIGZvciAoY29uc3QgbG9jYWxlIG9mIGxvY2FsZXMpIHtcbiAgICAgIGNvbnN0IGZpbGxlZCA9IHJvd3MuZmlsdGVyKChyb3cpID0+IHJvd1tsb2NhbGVdICE9IG51bGwgJiYgcm93W2xvY2FsZV0gIT09IFwiXCIpLmxlbmd0aDtcbiAgICAgIGNvbnN0IHBlcmNlbnQgPSB0b3RhbCA+IDAgPyBNYXRoLnJvdW5kKChmaWxsZWQgLyB0b3RhbCkgKiAxMDApIDogMDtcbiAgICAgIHN0YXRzW2xvY2FsZV0gPSB7IHRvdGFsLCBmaWxsZWQsIHBlcmNlbnQgfTtcbiAgICB9XG5cbiAgICByZXR1cm4geyByb3dzLCBsb2NhbGVzLCBkZWZhdWx0TG9jYWxlLCBzdGF0cyB9O1xuICB9XG5cbiAgLyoqXG4gICAqIOuUleyFlOuEiOumrCDsobDtmoxcbiAgICovXG4gIGFzeW5jIGdldERpY3Rpb25hcnkoKTogUHJvbWlzZTxEaWN0aW9uYXJ5UmVzdWx0PiB7XG4gICAgcmV0dXJuIHRoaXMuY29sbGVjdERpY3Rpb25hcnkoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFeGNlbOuhnCDrgrTrs7TrgrTquLBcbiAgICovXG4gIGFzeW5jIGV4cG9ydFRvRXhjZWwoKTogUHJvbWlzZTx7IGZpbGVuYW1lOiBzdHJpbmc7IGJ1ZmZlcjogQnVmZmVyIH0+IHtcbiAgICBjb25zdCB7IHJvd3MsIGxvY2FsZXMgfSA9IGF3YWl0IHRoaXMuY29sbGVjdERpY3Rpb25hcnkoKTtcblxuICAgIGNvbnN0IHdiID0gbmV3IFdvcmtib29rKCk7XG4gICAgY29uc3Qgc2hlZXQgPSBcImkxOG5cIjtcbiAgICB3Yi5zZXRTaGVldE5hbWUoXCJTaGVldDFcIiwgc2hlZXQpO1xuXG4gICAgY29uc3QgcHJvamVjdE5hbWUgPSBgJHtTb25hbXUuY29uZmlnLnByb2plY3ROYW1lID8/IFwiU29uYW11XCJ9IERpY3Rpb25hcnlgO1xuICAgIGNvbnN0IGhlYWRlcnMgPSBbXCJrZXlcIiwgXCJzb3VyY2VcIiwgLi4ubG9jYWxlc107XG5cbiAgICAvLyDsiqTtg4Dsnbwg7KCV7J2YXG4gICAgY29uc3QgdGl0bGVTdHlsZUlkID0gd2IuYWRkU3R5bGUoe1xuICAgICAgZm9udDogeyBzaXplOiAyMyB9LFxuICAgICAgYWxpZ25tZW50OiB7IHZlcnRpY2FsOiBcImNlbnRlclwiLCBob3Jpem9udGFsOiBcImxlZnRcIiB9LFxuICAgIH0pO1xuICAgIGNvbnN0IGhlYWRlclN0eWxlSWQgPSB3Yi5hZGRTdHlsZSh7XG4gICAgICBmb250OiB7IGJvbGQ6IHRydWUsIHNpemU6IDExIH0sXG4gICAgICBhbGlnbm1lbnQ6IHsgaG9yaXpvbnRhbDogXCJjZW50ZXJcIiwgdmVydGljYWw6IFwiY2VudGVyXCIgfSxcbiAgICAgIGZpbGw6IHsgcGF0dGVybjogXCJzb2xpZFwiLCBmZ0NvbG9yOiBcIkYxRjFGMVwiIH0sXG4gICAgICBib3JkZXI6IHtcbiAgICAgICAgdG9wOiB7IHN0eWxlOiBcInRoaW5cIiwgY29sb3I6IFwiRDBEMEQwXCIgfSxcbiAgICAgICAgbGVmdDogeyBzdHlsZTogXCJ0aGluXCIsIGNvbG9yOiBcIkQwRDBEMFwiIH0sXG4gICAgICAgIGJvdHRvbTogeyBzdHlsZTogXCJ0aGluXCIsIGNvbG9yOiBcIkQwRDBEMFwiIH0sXG4gICAgICAgIHJpZ2h0OiB7IHN0eWxlOiBcInRoaW5cIiwgY29sb3I6IFwiRDBEMEQwXCIgfSxcbiAgICAgIH0sXG4gICAgfSk7XG4gICAgY29uc3QgZGF0YVN0eWxlSWQgPSB3Yi5hZGRTdHlsZSh7XG4gICAgICBmb250OiB7IHNpemU6IDExIH0sXG4gICAgICBhbGlnbm1lbnQ6IHsgdmVydGljYWw6IFwiY2VudGVyXCIsIGhvcml6b250YWw6IFwibGVmdFwiIH0sXG4gICAgfSk7XG5cbiAgICAvLyDtlokgMTog7ZSE66Gc7KCd7Yq466qFXG4gICAgd2Iuc2V0Q2VsbFZhbHVlKHNoZWV0LCBcIkExXCIsIHByb2plY3ROYW1lKTtcbiAgICB3Yi5zZXRDZWxsU3R5bGUoc2hlZXQsIFwiQTFcIiwgdGl0bGVTdHlsZUlkKTtcbiAgICB3Yi5zZXRSb3dIZWlnaHQoc2hlZXQsIDEsIDI4KTtcblxuICAgIC8vIO2WiSAyOiDruYgg7ZaJICjquLDrs7jqsJIpXG5cbiAgICAvLyDtlokgMzog7Zek642UXG4gICAgd2Iuc2V0Um93VmFsdWVzKHNoZWV0LCAzLCBcIkFcIiwgaGVhZGVycyk7XG4gICAgd2Iuc2V0Um93SGVpZ2h0KHNoZWV0LCAzLCAyNik7XG4gICAgZm9yIChsZXQgY29sID0gMDsgY29sIDwgaGVhZGVycy5sZW5ndGg7IGNvbCsrKSB7XG4gICAgICB3Yi5zZXRDZWxsU3R5bGUoc2hlZXQsIGAke2NvbExldHRlcihjb2wpfTNgLCBoZWFkZXJTdHlsZUlkKTtcbiAgICB9XG5cbiAgICAvLyDtlokgNCDsnbTtm4Q6IOuNsOydtO2EsFxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcm93cy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3Qgcm93ID0gcm93c1tpXTtcbiAgICAgIGNvbnN0IHZhbHVlcyA9IFtyb3cua2V5LCByb3cuc291cmNlLCAuLi5sb2NhbGVzLm1hcCgobG9jYWxlKSA9PiByb3dbbG9jYWxlXSA/PyBcIlwiKV07XG4gICAgICBjb25zdCByb3dOdW0gPSBpICsgNDtcbiAgICAgIHdiLnNldFJvd1ZhbHVlcyhzaGVldCwgcm93TnVtLCBcIkFcIiwgdmFsdWVzKTtcbiAgICAgIHdiLnNldFJvd0hlaWdodChzaGVldCwgcm93TnVtLCAyNCk7XG4gICAgICBmb3IgKGxldCBjb2wgPSAwOyBjb2wgPCB2YWx1ZXMubGVuZ3RoOyBjb2wrKykge1xuICAgICAgICB3Yi5zZXRDZWxsU3R5bGUoc2hlZXQsIGAke2NvbExldHRlcihjb2wpfSR7cm93TnVtfWAsIGRhdGFTdHlsZUlkKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyDsu6zrn7wg64SI67mEIOqzhOyCsFxuICAgIGNvbnN0IE1BWF9XSURUSCA9IDUwO1xuICAgIGNvbnN0IE1JTl9XSURUSCA9IDEwO1xuICAgIGNvbnN0IGNvbHVtbldpZHRoczogbnVtYmVyW10gPSBoZWFkZXJzLm1hcCgoaGVhZGVyKSA9PiBNYXRoLm1heChoZWFkZXIubGVuZ3RoLCBNSU5fV0lEVEgpKTtcblxuICAgIGZvciAoY29uc3Qgcm93IG9mIHJvd3MpIHtcbiAgICAgIGNvbnN0IHZhbHVlcyA9IFtyb3cua2V5LCByb3cuc291cmNlLCAuLi5sb2NhbGVzLm1hcCgobG9jYWxlKSA9PiByb3dbbG9jYWxlXSA/PyBcIlwiKV07XG4gICAgICB2YWx1ZXMuZm9yRWFjaCgodmFsdWUsIGlkeCkgPT4ge1xuICAgICAgICBjb25zdCB0ZXh0TGVuZ3RoID0gU3RyaW5nKHZhbHVlKS5sZW5ndGg7XG4gICAgICAgIGNvbHVtbldpZHRoc1tpZHhdID0gTWF0aC5taW4oTWF0aC5tYXgoY29sdW1uV2lkdGhzW2lkeF0sIHRleHRMZW5ndGgpLCBNQVhfV0lEVEgpO1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8g7Lus65+8IOuEiOu5hCDsoIHsmqlcbiAgICBmb3IgKGxldCBjb2wgPSAwOyBjb2wgPCBjb2x1bW5XaWR0aHMubGVuZ3RoOyBjb2wrKykge1xuICAgICAgd2Iuc2V0Q29sV2lkdGgoc2hlZXQsIGNvbExldHRlcihjb2wpLCBjb2x1bW5XaWR0aHNbY29sXSArIDIpO1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICBmaWxlbmFtZTogYCR7cHJvamVjdE5hbWV9LSR7bmV3IERhdGUoKS50b0lTT1N0cmluZygpLnNwbGl0KFwiVFwiKVswXX0ueGxzeGAsXG4gICAgICBidWZmZXI6IHdiLndyaXRlQnVmZmVyU3luYygpLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogRXhjZWzsl5DshJwg6rCA7KC47Jik6riwXG4gICAqXG4gICAqIO2YleyLnTpcbiAgICogLSAx7ZaJOiDtlITroZzsoJ3tirjrqoVcbiAgICogLSAy7ZaJOiDruYgg7ZaJXG4gICAqIC0gM+2WiTog7Zek642UIChrZXksIHNvdXJjZSwgbG9jYWxlcy4uLilcbiAgICogLSA07ZaJIOydtO2bhDog642w7J207YSwXG4gICAqL1xuICBhc3luYyBpbXBvcnRGcm9tRXhjZWwoYnVmZmVyOiBCdWZmZXIpOiBQcm9taXNlPEltcG9ydFJlc3VsdD4ge1xuICAgIGNvbnN0IHdiID0gV29ya2Jvb2sub3BlbkJ1ZmZlclN5bmMoYnVmZmVyKTtcbiAgICBjb25zdCBzaGVldCA9IHdiLnNoZWV0TmFtZXNbMF07XG4gICAgY29uc3QgYWxsUm93cyA9IHdiLmdldFJvd3Moc2hlZXQpO1xuXG4gICAgY29uc3QgeyBkZWZhdWx0TG9jYWxlLCBzdXBwb3J0ZWRMb2NhbGVzIH0gPSB0aGlzLmdldEkxOG5Db25maWcoKTtcbiAgICBjb25zdCBsb2NhbGVzID0gc3VwcG9ydGVkTG9jYWxlcztcblxuICAgIGxldCB1cGRhdGVkRW50aXRpZXMgPSAwO1xuICAgIGxldCB1cGRhdGVkTG9jYWxlcyA9IDA7XG5cbiAgICAvLyBsb2NhbGXrs4QgcHJvamVjdCBkaWN0IGVudHJpZXNcbiAgICBjb25zdCBwcm9qZWN0RGljdEVudHJpZXM6IFJlY29yZDxzdHJpbmcsIERpY3RFbnRyeVtdPiA9IHt9O1xuICAgIGZvciAoY29uc3QgbG9jYWxlIG9mIGxvY2FsZXMpIHtcbiAgICAgIHByb2plY3REaWN0RW50cmllc1tsb2NhbGVdID0gW107XG4gICAgfVxuXG4gICAgLy8g7Zek642UIO2WiSDssL7quLA6IOyyqyDrsojsp7gg7Lus65+8KEEp7J20IFwia2V5XCLsnbgg7ZaJXG4gICAgbGV0IGhlYWRlclJvd051bSA9IDA7XG4gICAgZm9yIChjb25zdCByb3dEYXRhIG9mIGFsbFJvd3MpIHtcbiAgICAgIGNvbnN0IGZpcnN0Q2VsbCA9IHJvd0RhdGEuY2VsbHMuZmluZCgoYykgPT4gYy5jb2x1bW4gPT09IFwiQVwiKTtcbiAgICAgIGNvbnN0IGZpcnN0Q2VsbFZhbHVlID0gU3RyaW5nKGZpcnN0Q2VsbD8udmFsdWUgPz8gXCJcIilcbiAgICAgICAgLnRyaW0oKVxuICAgICAgICAudG9Mb3dlckNhc2UoKTtcbiAgICAgIGlmIChmaXJzdENlbGxWYWx1ZSA9PT0gXCJrZXlcIikge1xuICAgICAgICBoZWFkZXJSb3dOdW0gPSByb3dEYXRhLnJvdztcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKGhlYWRlclJvd051bSA9PT0gMCkge1xuICAgICAgdGhyb3cgbmV3IEJhZFJlcXVlc3RFeGNlcHRpb24oU0QoXCJzb25hbXUuZXJyb3IuaGVhZGVyUm93Tm90Rm91bmRcIikpO1xuICAgIH1cblxuICAgIC8vIO2XpOuNlCDtlonsl5DshJwg7Lus65+8IOunpO2VkSDqtazshLFcbiAgICBjb25zdCBoZWFkZXJSb3dEYXRhID0gYWxsUm93cy5maW5kKChyKSA9PiByLnJvdyA9PT0gaGVhZGVyUm93TnVtKTtcbiAgICBjb25zdCBjb2xUb0hlYWRlcjogTWFwPHN0cmluZywgc3RyaW5nPiA9IG5ldyBNYXAoKTtcbiAgICBpZiAoaGVhZGVyUm93RGF0YSkge1xuICAgICAgZm9yIChjb25zdCBjZWxsIG9mIGhlYWRlclJvd0RhdGEuY2VsbHMpIHtcbiAgICAgICAgY29sVG9IZWFkZXIuc2V0KGNlbGwuY29sdW1uLCBTdHJpbmcoY2VsbC52YWx1ZSA/PyBcIlwiKSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8g642w7J207YSwIO2WiSDsnb3quLAgKO2XpOuNlCDri6TsnYwg7ZaJ67aA7YSwKVxuICAgIGZvciAoY29uc3Qgcm93RGF0YSBvZiBhbGxSb3dzKSB7XG4gICAgICBpZiAocm93RGF0YS5yb3cgPD0gaGVhZGVyUm93TnVtKSBjb250aW51ZTtcblxuICAgICAgY29uc3Qgcm93VmFsdWVzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG4gICAgICBmb3IgKGNvbnN0IGNlbGwgb2Ygcm93RGF0YS5jZWxscykge1xuICAgICAgICBjb25zdCBoZWFkZXJOYW1lID0gY29sVG9IZWFkZXIuZ2V0KGNlbGwuY29sdW1uKTtcbiAgICAgICAgaWYgKGhlYWRlck5hbWUpIHtcbiAgICAgICAgICByb3dWYWx1ZXNbaGVhZGVyTmFtZV0gPSBTdHJpbmcoY2VsbC52YWx1ZSA/PyBcIlwiKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBjb25zdCBrZXkgPSByb3dWYWx1ZXMua2V5O1xuICAgICAgY29uc3Qgc291cmNlID0gcm93VmFsdWVzLnNvdXJjZSBhcyBcImVudGl0eVwiIHwgXCJwcm9qZWN0XCI7XG5cbiAgICAgIGlmICgha2V5IHx8ICFzb3VyY2UpIGNvbnRpbnVlO1xuXG4gICAgICBpZiAoc291cmNlID09PSBcImVudGl0eVwiKSB7XG4gICAgICAgIC8vIGVudGl0eSBzb3VyY2U6IGRlZmF1bHQgbG9jYWxl66eMIGVudGl0eS5qc29u7JeQIOyggOyepVxuICAgICAgICBjb25zdCBkZWZhdWx0VmFsdWUgPSByb3dWYWx1ZXNbZGVmYXVsdExvY2FsZV07XG4gICAgICAgIGlmIChkZWZhdWx0VmFsdWUpIHtcbiAgICAgICAgICBjb25zdCB1cGRhdGVkID0gYXdhaXQgdGhpcy51cGRhdGVFbnRpdHlCeUtleShrZXksIGRlZmF1bHRWYWx1ZSk7XG4gICAgICAgICAgaWYgKHVwZGF0ZWQpIHtcbiAgICAgICAgICAgIHVwZGF0ZWRFbnRpdGllcysrO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIG5vbi1kZWZhdWx0IGxvY2FsZeydgCBwcm9qZWN0IGRpY3Tsl5Ag7KCA7J6lXG4gICAgICAgIGZvciAoY29uc3QgbG9jYWxlIG9mIGxvY2FsZXMpIHtcbiAgICAgICAgICBpZiAobG9jYWxlID09PSBkZWZhdWx0TG9jYWxlKSBjb250aW51ZTtcbiAgICAgICAgICBjb25zdCBjZWxsVmFsdWUgPSByb3dWYWx1ZXNbbG9jYWxlXT8udHJpbSgpO1xuICAgICAgICAgIGlmIChjZWxsVmFsdWUpIHtcbiAgICAgICAgICAgIHByb2plY3REaWN0RW50cmllc1tsb2NhbGVdLnB1c2goe1xuICAgICAgICAgICAgICBrZXksXG4gICAgICAgICAgICAgIHZhbHVlOiBjZWxsVmFsdWUsXG4gICAgICAgICAgICAgIGlzRnVuY3Rpb246IHRoaXMuaXNFeHByZXNzaW9uRnVuY3Rpb24oY2VsbFZhbHVlKSxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChzb3VyY2UgPT09IFwicHJvamVjdFwiKSB7XG4gICAgICAgIC8vIHByb2plY3Qgc291cmNlOiDrqqjrk6AgbG9jYWxl7J2EIHByb2plY3QgZGljdOyXkCDsoIDsnqVcbiAgICAgICAgZm9yIChjb25zdCBsb2NhbGUgb2YgbG9jYWxlcykge1xuICAgICAgICAgIGNvbnN0IGNlbGxWYWx1ZSA9IHJvd1ZhbHVlc1tsb2NhbGVdPy50cmltKCk7XG4gICAgICAgICAgaWYgKGNlbGxWYWx1ZSkge1xuICAgICAgICAgICAgcHJvamVjdERpY3RFbnRyaWVzW2xvY2FsZV0ucHVzaCh7XG4gICAgICAgICAgICAgIGtleSxcbiAgICAgICAgICAgICAgdmFsdWU6IGNlbGxWYWx1ZSxcbiAgICAgICAgICAgICAgaXNGdW5jdGlvbjogdGhpcy5pc0V4cHJlc3Npb25GdW5jdGlvbihjZWxsVmFsdWUpLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gUHJvamVjdCBkaWN0IO2MjOydvCDsg53shLFcbiAgICBmb3IgKGNvbnN0IGxvY2FsZSBvZiBsb2NhbGVzKSB7XG4gICAgICBjb25zdCBlbnRyaWVzID0gcHJvamVjdERpY3RFbnRyaWVzW2xvY2FsZV07XG4gICAgICBpZiAoZW50cmllcy5sZW5ndGggPiAwKSB7XG4gICAgICAgIHRoaXMuc2F2ZURpY3RGaWxlKGxvY2FsZSwgZW50cmllcywgbG9jYWxlID09PSBkZWZhdWx0TG9jYWxlKTtcbiAgICAgICAgdXBkYXRlZExvY2FsZXMrKztcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgIHVwZGF0ZWRFbnRpdGllcyxcbiAgICAgIHVwZGF0ZWRMb2NhbGVzLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICog65SV7IWU64SI66asIO2VreuqqSDsiJjsoJVcbiAgICovXG4gIGFzeW5jIHVwZGF0ZUVudHJ5KHBhcmFtczoge1xuICAgIG9sZEtleTogc3RyaW5nO1xuICAgIG5ld0tleTogc3RyaW5nO1xuICAgIHNvdXJjZTogXCJlbnRpdHlcIiB8IFwicHJvamVjdFwiIHwgXCJzb25hbXVcIjtcbiAgICB2YWx1ZXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz47XG4gIH0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB7IG9sZEtleSwgbmV3S2V5LCBzb3VyY2UsIHZhbHVlcyB9ID0gcGFyYW1zO1xuXG4gICAgY29uc3QgeyBkZWZhdWx0TG9jYWxlLCBzdXBwb3J0ZWRMb2NhbGVzIH0gPSB0aGlzLmdldEkxOG5Db25maWcoKTtcbiAgICBjb25zdCBsb2NhbGVzID0gc3VwcG9ydGVkTG9jYWxlcztcblxuICAgIC8vIGVudGl0eSBzb3VyY2XsnZggZGVmYXVsdCBsb2NhbGUg7LKY66asXG4gICAgaWYgKHNvdXJjZSA9PT0gXCJlbnRpdHlcIiAmJiB2YWx1ZXNbZGVmYXVsdExvY2FsZV0pIHtcbiAgICAgIGF3YWl0IHRoaXMudXBkYXRlRW50aXR5QnlLZXkobmV3S2V5LCB2YWx1ZXNbZGVmYXVsdExvY2FsZV0pO1xuICAgIH1cblxuICAgIC8vIHByb2plY3QgZGljdCDsl4XrjbDsnbTtirhcbiAgICAvLyAtIGVudGl0eeydmCBub24tZGVmYXVsdCBsb2NhbGVcbiAgICAvLyAtIHByb2plY3Qgc291cmNl7J2YIOuqqOuToCBsb2NhbGVcbiAgICAvLyAtIHNvbmFtdSBzb3VyY2XsnZgg66qo65OgIGxvY2FsZSAob3ZlcnJpZGUpXG4gICAgZm9yIChjb25zdCBsb2NhbGUgb2YgbG9jYWxlcykge1xuICAgICAgLy8gZW50aXR5IHNvdXJjZeydmCBkZWZhdWx0IGxvY2FsZeydgCBlbnRpdHkuanNvbuyXkOyEnCDsspjrpqztlojsnLzrr4DroZwg7Iqk7YK1XG4gICAgICBpZiAoc291cmNlID09PSBcImVudGl0eVwiICYmIGxvY2FsZSA9PT0gZGVmYXVsdExvY2FsZSkgY29udGludWU7XG5cbiAgICAgIGNvbnN0IGNlbGxWYWx1ZSA9IHZhbHVlc1tsb2NhbGVdPy50cmltKCk7XG4gICAgICBpZiAoIWNlbGxWYWx1ZSkgY29udGludWU7XG5cbiAgICAgIC8vIOq4sOyhtCBkaWN0IOuhnOuTnFxuICAgICAgY29uc3QgeyBlbnRyaWVzIH0gPSB0aGlzLmxvYWRQcm9qZWN0RGljdChsb2NhbGUpO1xuXG4gICAgICAvLyBrZXkg67OA6rK9IOyLnCDquLDsobQga2V5IOygnOqxsFxuICAgICAgaWYgKG9sZEtleSAhPT0gbmV3S2V5KSB7XG4gICAgICAgIGNvbnN0IG9sZEluZGV4ID0gZW50cmllcy5maW5kSW5kZXgoKGUpID0+IGUua2V5ID09PSBvbGRLZXkpO1xuICAgICAgICBpZiAob2xkSW5kZXggIT09IC0xKSB7XG4gICAgICAgICAgZW50cmllcy5zcGxpY2Uob2xkSW5kZXgsIDEpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIOyDiCDqsJIg7JeF642w7J207Yq4IOuYkOuKlCDstpTqsIBcbiAgICAgIGNvbnN0IGV4aXN0aW5nSW5kZXggPSBlbnRyaWVzLmZpbmRJbmRleCgoZSkgPT4gZS5rZXkgPT09IG5ld0tleSk7XG4gICAgICBjb25zdCBuZXdFbnRyeTogRGljdEVudHJ5ID0ge1xuICAgICAgICBrZXk6IG5ld0tleSxcbiAgICAgICAgdmFsdWU6IGNlbGxWYWx1ZSxcbiAgICAgICAgaXNGdW5jdGlvbjogdGhpcy5pc0V4cHJlc3Npb25GdW5jdGlvbihjZWxsVmFsdWUpLFxuICAgICAgfTtcblxuICAgICAgaWYgKGV4aXN0aW5nSW5kZXggIT09IC0xKSB7XG4gICAgICAgIGVudHJpZXNbZXhpc3RpbmdJbmRleF0gPSBuZXdFbnRyeTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGVudHJpZXMucHVzaChuZXdFbnRyeSk7XG4gICAgICB9XG5cbiAgICAgIC8vIGRpY3Qg7YyM7J28IOyggOyepVxuICAgICAgdGhpcy5zYXZlRGljdEZpbGUobG9jYWxlLCBlbnRyaWVzLCBsb2NhbGUgPT09IGRlZmF1bHRMb2NhbGUpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiDrlJXshZTrhIjrpqwg7ZWt66qpIOy2lOqwgFxuICAgKi9cbiAgYXN5bmMgY3JlYXRlRW50cnkocGFyYW1zOiB7IGtleTogc3RyaW5nOyB2YWx1ZXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gfSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHsga2V5LCB2YWx1ZXMgfSA9IHBhcmFtcztcblxuICAgIGlmICgha2V5Py50cmltKCkpIHtcbiAgICAgIHRocm93IG5ldyBCYWRSZXF1ZXN0RXhjZXB0aW9uKFNEKFwic29uYW11LmVycm9yLmtleVJlcXVpcmVkXCIpKTtcbiAgICB9XG5cbiAgICBjb25zdCB7IGRlZmF1bHRMb2NhbGUsIHN1cHBvcnRlZExvY2FsZXMgfSA9IHRoaXMuZ2V0STE4bkNvbmZpZygpO1xuICAgIGNvbnN0IGxvY2FsZXMgPSBzdXBwb3J0ZWRMb2NhbGVzO1xuXG4gICAgLy8g7KSR67O1IO2CpCDssrTtgaxcbiAgICBmb3IgKGNvbnN0IGxvY2FsZSBvZiBsb2NhbGVzKSB7XG4gICAgICBjb25zdCB7IGVudHJpZXMgfSA9IHRoaXMubG9hZFByb2plY3REaWN0KGxvY2FsZSk7XG4gICAgICBpZiAoZW50cmllcy5zb21lKChlKSA9PiBlLmtleSA9PT0ga2V5KSkge1xuICAgICAgICB0aHJvdyBuZXcgQmFkUmVxdWVzdEV4Y2VwdGlvbihTRChcInNvbmFtdS5lcnJvci5rZXlBbHJlYWR5RXhpc3RzXCIpKGtleSkpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIOqwgSBsb2NhbGXsl5Ag7IOIIO2CpCDstpTqsIBcbiAgICBmb3IgKGNvbnN0IGxvY2FsZSBvZiBsb2NhbGVzKSB7XG4gICAgICBjb25zdCBjZWxsVmFsdWUgPSB2YWx1ZXNbbG9jYWxlXT8udHJpbSgpO1xuICAgICAgaWYgKCFjZWxsVmFsdWUpIGNvbnRpbnVlO1xuXG4gICAgICBjb25zdCB7IGVudHJpZXMgfSA9IHRoaXMubG9hZFByb2plY3REaWN0KGxvY2FsZSk7XG4gICAgICBlbnRyaWVzLnB1c2goe1xuICAgICAgICBrZXksXG4gICAgICAgIHZhbHVlOiBjZWxsVmFsdWUsXG4gICAgICAgIGlzRnVuY3Rpb246IHRoaXMuaXNFeHByZXNzaW9uRnVuY3Rpb24oY2VsbFZhbHVlKSxcbiAgICAgIH0pO1xuXG4gICAgICB0aGlzLnNhdmVEaWN0RmlsZShsb2NhbGUsIGVudHJpZXMsIGxvY2FsZSA9PT0gZGVmYXVsdExvY2FsZSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIOuUleyFlOuEiOumrCDtla3rqqkg7IKt7KCcXG4gICAqL1xuICBhc3luYyBkZWxldGVFbnRyeShrZXk6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICgha2V5KSB7XG4gICAgICB0aHJvdyBuZXcgQmFkUmVxdWVzdEV4Y2VwdGlvbihTRChcInNvbmFtdS5lcnJvci5rZXlSZXF1aXJlZFwiKSk7XG4gICAgfVxuXG4gICAgY29uc3QgeyBkZWZhdWx0TG9jYWxlLCBzdXBwb3J0ZWRMb2NhbGVzIH0gPSB0aGlzLmdldEkxOG5Db25maWcoKTtcbiAgICBjb25zdCBsb2NhbGVzID0gc3VwcG9ydGVkTG9jYWxlcztcblxuICAgIGxldCBkZWxldGVkID0gZmFsc2U7XG4gICAgZm9yIChjb25zdCBsb2NhbGUgb2YgbG9jYWxlcykge1xuICAgICAgY29uc3QgeyBlbnRyaWVzIH0gPSB0aGlzLmxvYWRQcm9qZWN0RGljdChsb2NhbGUpO1xuICAgICAgY29uc3QgaW5kZXggPSBlbnRyaWVzLmZpbmRJbmRleCgoZSkgPT4gZS5rZXkgPT09IGtleSk7XG4gICAgICBpZiAoaW5kZXggIT09IC0xKSB7XG4gICAgICAgIGVudHJpZXMuc3BsaWNlKGluZGV4LCAxKTtcbiAgICAgICAgZGVsZXRlZCA9IHRydWU7XG5cbiAgICAgICAgdGhpcy5zYXZlRGljdEZpbGUobG9jYWxlLCBlbnRyaWVzLCBsb2NhbGUgPT09IGRlZmF1bHRMb2NhbGUpO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmICghZGVsZXRlZCkge1xuICAgICAgdGhyb3cgbmV3IEJhZFJlcXVlc3RFeGNlcHRpb24oU0QoXCJzb25hbXUuZXJyb3Iua2V5Tm90Rm91bmRcIikoa2V5KSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIOuvuOyCrOyaqSDtgqQg6rKA7IKsIChhc3QtZ3JlcCDsgqzsmqkpXG4gICAqL1xuICBhc3luYyBjaGVja1VzYWdlKGtleXM6IHN0cmluZ1tdKTogUHJvbWlzZTxVc2FnZVJlc3VsdD4ge1xuICAgIC8vIGFzdC1ncmVwIOyEpOy5mCDtmZXsnbhcbiAgICBsZXQgc2dQYXRoOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgICB0cnkge1xuICAgICAgc2dQYXRoID0gZXhlY1N5bmMoXCJ3aGljaCBzZ1wiLCB7IGVuY29kaW5nOiBcInV0Zi04XCIgfSkudHJpbSgpO1xuICAgIH0gY2F0Y2gge1xuICAgICAgdHJ5IHtcbiAgICAgICAgc2dQYXRoID0gZXhlY1N5bmMoXCJ3aGljaCBhc3QtZ3JlcFwiLCB7IGVuY29kaW5nOiBcInV0Zi04XCIgfSkudHJpbSgpO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIC8vIGFzdC1ncmVwIG5vdCBpbnN0YWxsZWRcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoIXNnUGF0aCkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgZXJyb3I6XG4gICAgICAgICAgXCJhc3QtZ3JlcOydtCDshKTsuZjrkJjslrQg7J6I7KeAIOyViuyKteuLiOuLpC4gYnJldyBpbnN0YWxsIGFzdC1ncmVwIOuYkOuKlCBucG0gaW5zdGFsbCAtZyBAYXN0LWdyZXAvY2xp66GcIOyEpOy5mO2VtOyjvOyEuOyalC5cIixcbiAgICAgICAgdW51c2VkS2V5czogW10sXG4gICAgICB9O1xuICAgIH1cblxuICAgIGNvbnN0IHNlYXJjaFBhdGhzOiBzdHJpbmdbXSA9IFtdO1xuICAgIGZvciAoY29uc3QgZW50cnkgb2YgW1wiYXBpXCIsIFwid2ViXCIsIFwiYXBwXCJdKSB7XG4gICAgICBjb25zdCBzcmNQYXRoID0gcGF0aC5qb2luKFNvbmFtdS5hcHBSb290UGF0aCwgZW50cnksIFwic3JjXCIpO1xuICAgICAgaWYgKGZzLmV4aXN0c1N5bmMoc3JjUGF0aCkpIHtcbiAgICAgICAgc2VhcmNoUGF0aHMucHVzaChzcmNQYXRoKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoc2VhcmNoUGF0aHMubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBlcnJvcjogXCLqsoDsg4ntlaAgc3JjIOuUlOugie2GoOumrOulvCDssL7snYQg7IiYIOyXhuyKteuLiOuLpC5cIixcbiAgICAgICAgdW51c2VkS2V5czogW10sXG4gICAgICB9O1xuICAgIH1cblxuICAgIGNvbnN0IHVzZWRLZXlzID0gbmV3IFNldDxzdHJpbmc+KCk7XG5cbiAgICB0cnkge1xuICAgICAgLy8gYXN0LWdyZXDsnLzroZwgU0QoXCIuLi5cIikg7Yyo7YS0IOqygOyDiVxuICAgICAgLy8g7Yyo7YS0OiBTRChcIktFWVwiKSDrmJDripQgU0QoJ0tFWScpIO2Yle2DnFxuICAgICAgY29uc3QgcGF0dGVybnMgPSBbJ1NEKFwiJEtFWVwiKScsIFwiU0QoJyRLRVknKVwiXTtcblxuICAgICAgZm9yIChjb25zdCBzZWFyY2hQYXRoIG9mIHNlYXJjaFBhdGhzKSB7XG4gICAgICAgIGZvciAoY29uc3QgcGF0dGVybiBvZiBwYXR0ZXJucykge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCByZXN1bHQgPSBleGVjU3luYyhgJHtzZ1BhdGh9IC0tcGF0dGVybiAnJHtwYXR0ZXJufScgLS1qc29uICR7c2VhcmNoUGF0aH1gLCB7XG4gICAgICAgICAgICAgIGVuY29kaW5nOiBcInV0Zi04XCIsXG4gICAgICAgICAgICAgIG1heEJ1ZmZlcjogNTAgKiAxMDI0ICogMTAyNCwgLy8gNTBNQlxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIGlmIChyZXN1bHQudHJpbSgpKSB7XG4gICAgICAgICAgICAgIGNvbnN0IG1hdGNoZXMgPSBKU09OLnBhcnNlKHJlc3VsdCk7XG4gICAgICAgICAgICAgIGZvciAoY29uc3QgbWF0Y2ggb2YgbWF0Y2hlcykge1xuICAgICAgICAgICAgICAgIC8vIG1ldGFWYXJpYWJsZXMuc2luZ2xlLktFWS50ZXh07JeQ7IScIO2CpCDstpTstpxcbiAgICAgICAgICAgICAgICBjb25zdCBrZXlUZXh0ID0gbWF0Y2gubWV0YVZhcmlhYmxlcz8uc2luZ2xlPy5LRVk/LnRleHQ7XG4gICAgICAgICAgICAgICAgaWYgKGtleVRleHQpIHtcbiAgICAgICAgICAgICAgICAgIC8vIOuUsOyYtO2RnCDsoJzqsbBcbiAgICAgICAgICAgICAgICAgIGNvbnN0IGNsZWFuS2V5ID0ga2V5VGV4dC5yZXBsYWNlKC9eW1wiJ118W1wiJ10kL2csIFwiXCIpO1xuICAgICAgICAgICAgICAgICAgdXNlZEtleXMuYWRkKGNsZWFuS2V5KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAgIC8vIO2MqO2EtCDrp6TsuZgg7JeG7Jy866m0IOyXkOufrCAo66y07IucKVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBrZXlzIOykkeyXkOyEnCB1c2VkS2V5c+yXkCDsl4bripQg6rKD65Ok7J20IOuvuOyCrOyaqSDtgqRcbiAgICAgIGNvbnN0IHVudXNlZEtleXMgPSBrZXlzLmZpbHRlcigoaykgPT4gIXVzZWRLZXlzLmhhcyhrKSk7XG5cbiAgICAgIHJldHVybiB7IHVudXNlZEtleXMsIHVzZWRLZXlzQ291bnQ6IHVzZWRLZXlzLnNpemUgfTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBlcnJvcjogYOqygOyDiSDspJEg7Jik66WYIOuwnOyDnTogJHtlIGluc3RhbmNlb2YgRXJyb3IgPyBlLm1lc3NhZ2UgOiBTdHJpbmcoZSl9YCxcbiAgICAgICAgdW51c2VkS2V5czogW10sXG4gICAgICB9O1xuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgY29uc3Qgc29uYW11RGljdGlvbmFyeSA9IG5ldyBTb25hbXVEaWN0aW9uYXJ5KCk7XG4iXSwibmFtZXMiOlsiV29ya2Jvb2siLCJleGVjU3luYyIsImZzIiwicGF0aCIsInRzIiwiU29uYW11IiwiRW50aXR5TWFuYWdlciIsIkJhZFJlcXVlc3RFeGNlcHRpb24iLCJmb3JtYXRDb2RlIiwiU0QiLCJjb2xMZXR0ZXIiLCJpbmRleCIsInJlc3VsdCIsIm4iLCJTdHJpbmciLCJmcm9tQ2hhckNvZGUiLCJNYXRoIiwiZmxvb3IiLCJTb25hbXVEaWN0aW9uYXJ5IiwicGFyc2VEaWN0RmlsZSIsImZpbGVQYXRoIiwiY29udGVudCIsInJlYWRGaWxlU3luYyIsInNvdXJjZUZpbGUiLCJjcmVhdGVTb3VyY2VGaWxlIiwiU2NyaXB0VGFyZ2V0IiwiTGF0ZXN0IiwiZW50cmllcyIsImZvckVhY2hDaGlsZCIsIm5vZGUiLCJpc0V4cG9ydEFzc2lnbm1lbnQiLCJvYmplY3RMaXRlcmFsIiwidW53cmFwVG9PYmplY3RMaXRlcmFsIiwiZXhwcmVzc2lvbiIsImV4dHJhY3RFbnRyaWVzRnJvbU9iamVjdCIsInBhcnNlQ29uc3RPYmplY3REZWNsYXJhdGlvbiIsInZhck5hbWUiLCJpc1ZhcmlhYmxlU3RhdGVtZW50IiwiZGVjbCIsImRlY2xhcmF0aW9uTGlzdCIsImRlY2xhcmF0aW9ucyIsImlzSWRlbnRpZmllciIsIm5hbWUiLCJ0ZXh0IiwiaW5pdGlhbGl6ZXIiLCJpc0V4cHJlc3Npb25GdW5jdGlvbiIsImNvZGUiLCJ0cmltIiwiQVJST1dfRlVOQ1RJT05fUEFUVEVSTiIsInRlc3QiLCJleHByIiwiaXNBc0V4cHJlc3Npb24iLCJpc09iamVjdExpdGVyYWxFeHByZXNzaW9uIiwiaXNDYWxsRXhwcmVzc2lvbiIsImZpcnN0QXJnIiwiYXJndW1lbnRzIiwicHJvcCIsInByb3BlcnRpZXMiLCJlbnRyeSIsImV4dHJhY3REaWN0RW50cnkiLCJwdXNoIiwiZ2V0UHJvcGVydHlLZXkiLCJpc1N0cmluZ0xpdGVyYWwiLCJpc1Byb3BlcnR5QXNzaWdubWVudCIsImtleSIsImluaXQiLCJpc0Fycm93RnVuY3Rpb24iLCJmdW5jVGV4dCIsImdldFRleHQiLCJub3JtYWxpemVkIiwicmVwbGFjZSIsInZhbHVlIiwiaXNGdW5jdGlvbiIsImlzTm9TdWJzdGl0dXRpb25UZW1wbGF0ZUxpdGVyYWwiLCJpc0Z1bmN0aW9uRXhwcmVzc2lvbiIsImdldFByb2plY3REaWN0UGF0aCIsImxvY2FsZSIsInRhcmdldCIsImRpciIsImNvbmZpZyIsImFwaSIsImpvaW4iLCJhcHBSb290UGF0aCIsImdldFNvbmFtdURpY3RQYXRoIiwicGFja2FnZVJvb3QiLCJyZXNvbHZlIiwiZGlybmFtZSIsImVuc3VyZURpY3RLZXlzIiwicmVxdWlyZWRLZXlzIiwiZGVmYXVsdExvY2FsZSIsImkxOG4iLCJwcm9qZWN0RGljdFBhdGgiLCJleGlzdHNTeW5jIiwicHJvamVjdEVudHJpZXMiLCJleGlzdGluZ0tleXMiLCJTZXQiLCJtYXAiLCJlIiwibWlzc2luZ0tleXMiLCJmaWx0ZXIiLCJoYXMiLCJsZW5ndGgiLCJzb25hbXVEaWN0UGF0aCIsInNvbmFtdUVudHJpZXMiLCJzb25hbXVEaWN0IiwiTWFwIiwiZW50cmllc1RvQWRkIiwiZ2V0IiwidW5kZWZpbmVkIiwiYXBwZW5kRW50cmllc1RvRGljdEZpbGUiLCJpc0RlZmF1bHRMb2NhbGUiLCJleGlzdGluZ0VudHJpZXMiLCJnZW5lcmF0ZVByb2plY3REaWN0IiwiZm9ybWF0dGVkIiwid3JpdGVGaWxlU3luYyIsImRldGVjdFVzZWRIZWxwZXJzIiwiZnVuY3Rpb25FbnRyaWVzIiwiaGVscGVycyIsImhlbHBlciIsInBhdHRlcm4iLCJSZWdFeHAiLCJzb21lIiwiZm9ybWF0UGF0dGVybiIsInVzZXNGb3JtYXQiLCJzb3J0ZWQiLCJzb3J0IiwiYSIsImIiLCJsb2NhbGVDb21wYXJlIiwibGluZXMiLCJpbXBvcnRzIiwidG9VcHBlckNhc2UiLCJKU09OIiwic3RyaW5naWZ5IiwiZ2V0STE4bkNvbmZpZyIsImVuc3VyZUkxOG5EaXIiLCJpMThuRGlyIiwiYXBpUm9vdFBhdGgiLCJta2RpclN5bmMiLCJyZWN1cnNpdmUiLCJzYXZlRGljdEZpbGUiLCJkaWN0UGF0aCIsInBhcnNlRW50aXR5S2V5IiwiZW50aXR5VGl0bGVNYXRjaCIsIm1hdGNoIiwiaW5jbHVkZXMiLCJ0eXBlIiwiZW50aXR5SWQiLCJwcm9wRGVzY01hdGNoIiwicHJvcE5hbWUiLCJlbnVtTGFiZWxNYXRjaCIsImVudW1JZCIsImVudW1WYWx1ZSIsInVwZGF0ZUVudGl0eUJ5S2V5Iiwia2V5SW5mbyIsImVudGl0eSIsInRpdGxlIiwic2F2ZSIsInByb3BJbmRleCIsInByb3BzIiwiZmluZEluZGV4IiwicCIsImRlc2MiLCJnZXRBbGxJZHMiLCJlbnVtTGFiZWxzIiwiZXh0cmFjdEVudGl0eUxhYmVscyIsInNkUGF0aCIsImV4dHJhY3RSQ0tleXMiLCJsb2FkUHJvamVjdERpY3QiLCJjb2xsZWN0RGljdGlvbmFyeSIsInN1cHBvcnRlZExvY2FsZXMiLCJsb2NhbGVzIiwicm93cyIsInJvd01hcCIsInJjS2V5cyIsInJjS2V5Iiwicm93Iiwic291cmNlIiwic2V0IiwiZW50aXR5TGFiZWxzIiwibGFiZWwiLCJleGlzdGluZyIsInZhbHVlcyIsInN0YXRzIiwidG90YWwiLCJmaWxsZWQiLCJwZXJjZW50Iiwicm91bmQiLCJnZXREaWN0aW9uYXJ5IiwiZXhwb3J0VG9FeGNlbCIsIndiIiwic2hlZXQiLCJzZXRTaGVldE5hbWUiLCJwcm9qZWN0TmFtZSIsImhlYWRlcnMiLCJ0aXRsZVN0eWxlSWQiLCJhZGRTdHlsZSIsImZvbnQiLCJzaXplIiwiYWxpZ25tZW50IiwidmVydGljYWwiLCJob3Jpem9udGFsIiwiaGVhZGVyU3R5bGVJZCIsImJvbGQiLCJmaWxsIiwiZmdDb2xvciIsImJvcmRlciIsInRvcCIsInN0eWxlIiwiY29sb3IiLCJsZWZ0IiwiYm90dG9tIiwicmlnaHQiLCJkYXRhU3R5bGVJZCIsInNldENlbGxWYWx1ZSIsInNldENlbGxTdHlsZSIsInNldFJvd0hlaWdodCIsInNldFJvd1ZhbHVlcyIsImNvbCIsImkiLCJyb3dOdW0iLCJNQVhfV0lEVEgiLCJNSU5fV0lEVEgiLCJjb2x1bW5XaWR0aHMiLCJoZWFkZXIiLCJtYXgiLCJmb3JFYWNoIiwiaWR4IiwidGV4dExlbmd0aCIsIm1pbiIsInNldENvbFdpZHRoIiwiZmlsZW5hbWUiLCJEYXRlIiwidG9JU09TdHJpbmciLCJzcGxpdCIsImJ1ZmZlciIsIndyaXRlQnVmZmVyU3luYyIsImltcG9ydEZyb21FeGNlbCIsIm9wZW5CdWZmZXJTeW5jIiwic2hlZXROYW1lcyIsImFsbFJvd3MiLCJnZXRSb3dzIiwidXBkYXRlZEVudGl0aWVzIiwidXBkYXRlZExvY2FsZXMiLCJwcm9qZWN0RGljdEVudHJpZXMiLCJoZWFkZXJSb3dOdW0iLCJyb3dEYXRhIiwiZmlyc3RDZWxsIiwiY2VsbHMiLCJmaW5kIiwiYyIsImNvbHVtbiIsImZpcnN0Q2VsbFZhbHVlIiwidG9Mb3dlckNhc2UiLCJoZWFkZXJSb3dEYXRhIiwiciIsImNvbFRvSGVhZGVyIiwiY2VsbCIsInJvd1ZhbHVlcyIsImhlYWRlck5hbWUiLCJkZWZhdWx0VmFsdWUiLCJ1cGRhdGVkIiwiY2VsbFZhbHVlIiwic3VjY2VzcyIsInVwZGF0ZUVudHJ5IiwicGFyYW1zIiwib2xkS2V5IiwibmV3S2V5Iiwib2xkSW5kZXgiLCJzcGxpY2UiLCJleGlzdGluZ0luZGV4IiwibmV3RW50cnkiLCJjcmVhdGVFbnRyeSIsImRlbGV0ZUVudHJ5IiwiZGVsZXRlZCIsImNoZWNrVXNhZ2UiLCJrZXlzIiwic2dQYXRoIiwiZW5jb2RpbmciLCJlcnJvciIsInVudXNlZEtleXMiLCJzZWFyY2hQYXRocyIsInNyY1BhdGgiLCJ1c2VkS2V5cyIsInBhdHRlcm5zIiwic2VhcmNoUGF0aCIsIm1heEJ1ZmZlciIsIm1hdGNoZXMiLCJwYXJzZSIsImtleVRleHQiLCJtZXRhVmFyaWFibGVzIiwic2luZ2xlIiwiS0VZIiwiY2xlYW5LZXkiLCJh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
|
|
889
|
+
//#endregion
|
|
890
|
+
init_sonamu_dictionary();
|
|
891
|
+
export { SonamuDictionary, init_sonamu_dictionary, sonamuDictionary };
|
|
892
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic29uYW11LWRpY3Rpb25hcnkuanMiLCJuYW1lcyI6WyJlbnRyaWVzOiBEaWN0RW50cnlbXSIsImhlbHBlcnM6IHN0cmluZ1tdIiwibGluZXM6IHN0cmluZ1tdIiwicm93czogRGljdGlvbmFyeVJvd1tdIiwicm93OiBEaWN0aW9uYXJ5Um93Iiwic3RhdHM6IFJlY29yZDxzdHJpbmcsIHsgdG90YWw6IG51bWJlcjsgZmlsbGVkOiBudW1iZXI7IHBlcmNlbnQ6IG51bWJlciB9PiIsImNvbHVtbldpZHRoczogbnVtYmVyW10iLCJwcm9qZWN0RGljdEVudHJpZXM6IFJlY29yZDxzdHJpbmcsIERpY3RFbnRyeVtdPiIsImNvbFRvSGVhZGVyOiBNYXA8c3RyaW5nLCBzdHJpbmc+Iiwicm93VmFsdWVzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+IiwibmV3RW50cnk6IERpY3RFbnRyeSIsInNnUGF0aDogc3RyaW5nIHwgbnVsbCIsInNlYXJjaFBhdGhzOiBzdHJpbmdbXSJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWN0L3NvbmFtdS1kaWN0aW9uYXJ5LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGV4ZWNTeW5jIH0gZnJvbSBcImNoaWxkX3Byb2Nlc3NcIjtcbmltcG9ydCBmcyBmcm9tIFwiZnNcIjtcbmltcG9ydCBwYXRoIGZyb20gXCJwYXRoXCI7XG5cbmltcG9ydCB7IFdvcmtib29rIH0gZnJvbSBcIkBzaGVldGtpdC9ub2RlXCI7XG5pbXBvcnQgdHMgZnJvbSBcInR5cGVzY3JpcHRcIjtcblxuaW1wb3J0IHsgU29uYW11IH0gZnJvbSBcIi4uL2FwaS9zb25hbXVcIjtcbmltcG9ydCB7IEVudGl0eU1hbmFnZXIgfSBmcm9tIFwiLi4vZW50aXR5L2VudGl0eS1tYW5hZ2VyXCI7XG5pbXBvcnQgeyBCYWRSZXF1ZXN0RXhjZXB0aW9uIH0gZnJvbSBcIi4uL2V4Y2VwdGlvbnMvc28tZXhjZXB0aW9uc1wiO1xuaW1wb3J0IHsgZm9ybWF0Q29kZSB9IGZyb20gXCIuLi91dGlscy9mb3JtYXR0ZXJcIjtcbmltcG9ydCB7IFNEIH0gZnJvbSBcIi4vc2RcIjtcbmltcG9ydCB7XG4gIHR5cGUgRGljdEVudHJ5LFxuICB0eXBlIERpY3Rpb25hcnlSZXN1bHQsXG4gIHR5cGUgRGljdGlvbmFyeVJvdyxcbiAgdHlwZSBFbnRpdHlLZXlJbmZvLFxuICB0eXBlIEkxOG5Db25maWcsXG4gIHR5cGUgSW1wb3J0UmVzdWx0LFxuICB0eXBlIFVzYWdlUmVzdWx0LFxufSBmcm9tIFwiLi90eXBlc1wiO1xuXG4vKipcbiAqIDAtYmFzZWQg7Lus65+8IOyduOuNseyKpOulvCDsl5HshYAg7Lus65+8IOusuOyekOuhnCDrs4DtmZggKDAgLT4gXCJBXCIsIDI1IC0+IFwiWlwiLCAyNiAtPiBcIkFBXCIpXG4gKi9cbmZ1bmN0aW9uIGNvbExldHRlcihpbmRleDogbnVtYmVyKTogc3RyaW5nIHtcbiAgbGV0IHJlc3VsdCA9IFwiXCI7XG4gIGxldCBuID0gaW5kZXg7XG4gIHdoaWxlIChuID49IDApIHtcbiAgICByZXN1bHQgPSBTdHJpbmcuZnJvbUNoYXJDb2RlKDY1ICsgKG4gJSAyNikpICsgcmVzdWx0O1xuICAgIG4gPSBNYXRoLmZsb29yKG4gLyAyNikgLSAxO1xuICB9XG4gIHJldHVybiByZXN1bHQ7XG59XG5cbi8qKlxuICogU29uYW11IERpY3Rpb25hcnkg6rSA66asIO2BtOuemOyKpFxuICogaTE4biDrlJXshZTrhIjrpqzsnZggQ1JVRCDrsI8gRXhjZWwgaW1wb3J0L2V4cG9ydOulvCDri7Tri7ntlanri4jri6QuXG4gKi9cbmV4cG9ydCBjbGFzcyBTb25hbXVEaWN0aW9uYXJ5IHtcbiAgLyoqXG4gICAqIFR5cGVTY3JpcHQgQ29tcGlsZXIgQVBJ66W8IOyCrOyaqe2VmOyXrCBkaWN0IO2MjOydvCDtjIzsi7FcbiAgICpcbiAgICog7KeA7JuQIO2MqO2EtDpcbiAgICogLSBleHBvcnQgZGVmYXVsdCB7IC4uLiB9IGFzIGNvbnN0O1xuICAgKiAtIGV4cG9ydCBkZWZhdWx0IGRlZmluZUxvY2FsZSh7IC4uLiB9KTtcbiAgICogLSDrrLjsnpDsl7Qg6rCSOiBcImtleVwiOiBcInZhbHVlXCIg65iQ64qUIGtleTogYHZhbHVlYFxuICAgKiAtIO2VqOyImCDqsJI6IFwia2V5XCI6IChwYXJhbTogVHlwZSkgPT4gYHRlbXBsYXRlYFxuICAgKi9cbiAgcGFyc2VEaWN0RmlsZShmaWxlUGF0aDogc3RyaW5nKTogRGljdEVudHJ5W10ge1xuICAgIGNvbnN0IGNvbnRlbnQgPSBmcy5yZWFkRmlsZVN5bmMoZmlsZVBhdGgsIFwidXRmLThcIik7XG4gICAgY29uc3Qgc291cmNlRmlsZSA9IHRzLmNyZWF0ZVNvdXJjZUZpbGUoZmlsZVBhdGgsIGNvbnRlbnQsIHRzLlNjcmlwdFRhcmdldC5MYXRlc3QsIHRydWUpO1xuXG4gICAgY29uc3QgZW50cmllczogRGljdEVudHJ5W10gPSBbXTtcblxuICAgIHRzLmZvckVhY2hDaGlsZChzb3VyY2VGaWxlLCAobm9kZSkgPT4ge1xuICAgICAgaWYgKHRzLmlzRXhwb3J0QXNzaWdubWVudChub2RlKSkge1xuICAgICAgICBjb25zdCBvYmplY3RMaXRlcmFsID0gdGhpcy51bndyYXBUb09iamVjdExpdGVyYWwobm9kZS5leHByZXNzaW9uKTtcbiAgICAgICAgaWYgKG9iamVjdExpdGVyYWwpIHtcbiAgICAgICAgICB0aGlzLmV4dHJhY3RFbnRyaWVzRnJvbU9iamVjdChvYmplY3RMaXRlcmFsLCBzb3VyY2VGaWxlLCBlbnRyaWVzKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuXG4gICAgcmV0dXJuIGVudHJpZXM7XG4gIH1cblxuICAvKipcbiAgICog7YyM7J287JeQ7IScIO2KueyglSDsnbTrpoTsnZggY29uc3Qg7ISg7Ja47J2EIOywvuyVhCBPYmplY3RMaXRlcmFsIO2MjOyLsVxuICAgKiDsmIg6IGNvbnN0IGVudGl0eUxhYmVscyA9IHsgLi4uIH0gYXMgY29uc3Q7XG4gICAqL1xuICBwYXJzZUNvbnN0T2JqZWN0RGVjbGFyYXRpb24oZmlsZVBhdGg6IHN0cmluZywgdmFyTmFtZTogc3RyaW5nKTogRGljdEVudHJ5W10ge1xuICAgIGNvbnN0IGNvbnRlbnQgPSBmcy5yZWFkRmlsZVN5bmMoZmlsZVBhdGgsIFwidXRmLThcIik7XG4gICAgY29uc3Qgc291cmNlRmlsZSA9IHRzLmNyZWF0ZVNvdXJjZUZpbGUoZmlsZVBhdGgsIGNvbnRlbnQsIHRzLlNjcmlwdFRhcmdldC5MYXRlc3QsIHRydWUpO1xuXG4gICAgY29uc3QgZW50cmllczogRGljdEVudHJ5W10gPSBbXTtcblxuICAgIHRzLmZvckVhY2hDaGlsZChzb3VyY2VGaWxlLCAobm9kZSkgPT4ge1xuICAgICAgaWYgKHRzLmlzVmFyaWFibGVTdGF0ZW1lbnQobm9kZSkpIHtcbiAgICAgICAgZm9yIChjb25zdCBkZWNsIG9mIG5vZGUuZGVjbGFyYXRpb25MaXN0LmRlY2xhcmF0aW9ucykge1xuICAgICAgICAgIGlmICh0cy5pc0lkZW50aWZpZXIoZGVjbC5uYW1lKSAmJiBkZWNsLm5hbWUudGV4dCA9PT0gdmFyTmFtZSAmJiBkZWNsLmluaXRpYWxpemVyKSB7XG4gICAgICAgICAgICBjb25zdCBvYmplY3RMaXRlcmFsID0gdGhpcy51bndyYXBUb09iamVjdExpdGVyYWwoZGVjbC5pbml0aWFsaXplcik7XG4gICAgICAgICAgICBpZiAob2JqZWN0TGl0ZXJhbCkge1xuICAgICAgICAgICAgICB0aGlzLmV4dHJhY3RFbnRyaWVzRnJvbU9iamVjdChvYmplY3RMaXRlcmFsLCBzb3VyY2VGaWxlLCBlbnRyaWVzKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcblxuICAgIHJldHVybiBlbnRyaWVzO1xuICB9XG5cbiAgLyoqXG4gICAqIOusuOyekOyXtOydtCDtmZTsgrTtkZwg7ZWo7IiYIOuYkOuKlCDtlajsiJgg7ZGc7ZiE7Iud7J247KeAIO2MkOuzhFxuICAgKi9cbiAgaXNFeHByZXNzaW9uRnVuY3Rpb24oY29kZTogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgLy8g67mIIOusuOyekOyXtOydtOuCmCDqs7XrsLHrp4wg7J6I64qUIOqyveyasFxuICAgIGlmICghY29kZS50cmltKCkpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICBjb25zdCBBUlJPV19GVU5DVElPTl9QQVRURVJOID0gL15cXHMqXFwoW14pXSpcXClcXHMqPT4vO1xuXG4gICAgcmV0dXJuIEFSUk9XX0ZVTkNUSU9OX1BBVFRFUk4udGVzdChjb2RlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBleHBvcnQgZGVmYXVsdCDtkZztmITsi53sl5DshJwgT2JqZWN0TGl0ZXJhbEV4cHJlc3Npb24g7LaU7LacXG4gICAqIC0gYXMgY29uc3Qg7LKY66asXG4gICAqIC0gZGVmaW5lTG9jYWxlKHsgLi4uIH0pIO2YuOy2nCDsspjrpqxcbiAgICovXG4gIHByaXZhdGUgdW53cmFwVG9PYmplY3RMaXRlcmFsKGV4cHI6IHRzLkV4cHJlc3Npb24pOiB0cy5PYmplY3RMaXRlcmFsRXhwcmVzc2lvbiB8IG51bGwge1xuICAgIC8vIGFzIGNvbnN0IOyymOumrFxuICAgIGlmICh0cy5pc0FzRXhwcmVzc2lvbihleHByKSkge1xuICAgICAgcmV0dXJuIHRoaXMudW53cmFwVG9PYmplY3RMaXRlcmFsKGV4cHIuZXhwcmVzc2lvbik7XG4gICAgfVxuICAgIC8vIOyngeygkSDqsJ3ssrQg66as7YSw65+0XG4gICAgaWYgKHRzLmlzT2JqZWN0TGl0ZXJhbEV4cHJlc3Npb24oZXhwcikpIHtcbiAgICAgIHJldHVybiBleHByO1xuICAgIH1cbiAgICAvLyBkZWZpbmVMb2NhbGUoeyAuLi4gfSkg7Zi47LacXG4gICAgaWYgKHRzLmlzQ2FsbEV4cHJlc3Npb24oZXhwcikpIHtcbiAgICAgIGNvbnN0IGZpcnN0QXJnID0gZXhwci5hcmd1bWVudHNbMF07XG4gICAgICBpZiAoZmlyc3RBcmcgJiYgdHMuaXNPYmplY3RMaXRlcmFsRXhwcmVzc2lvbihmaXJzdEFyZykpIHtcbiAgICAgICAgcmV0dXJuIGZpcnN0QXJnO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBPYmplY3RMaXRlcmFsRXhwcmVzc2lvbuyXkOyEnCBEaWN0RW50cnkg7LaU7LacXG4gICAqL1xuICBwcml2YXRlIGV4dHJhY3RFbnRyaWVzRnJvbU9iamVjdChcbiAgICBvYmplY3RMaXRlcmFsOiB0cy5PYmplY3RMaXRlcmFsRXhwcmVzc2lvbixcbiAgICBzb3VyY2VGaWxlOiB0cy5Tb3VyY2VGaWxlLFxuICAgIGVudHJpZXM6IERpY3RFbnRyeVtdLFxuICApOiB2b2lkIHtcbiAgICBmb3IgKGNvbnN0IHByb3Agb2Ygb2JqZWN0TGl0ZXJhbC5wcm9wZXJ0aWVzKSB7XG4gICAgICBjb25zdCBlbnRyeSA9IHRoaXMuZXh0cmFjdERpY3RFbnRyeShwcm9wLCBzb3VyY2VGaWxlKTtcbiAgICAgIGlmIChlbnRyeSkge1xuICAgICAgICBlbnRyaWVzLnB1c2goZW50cnkpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBQcm9wZXJ0eU5hbWXsl5DshJwg7YKkIOusuOyekOyXtCDstpTstpxcbiAgICogLSDrrLjsnpDsl7Qg66as7YSw65+0OiBcImtleVwiXG4gICAqIC0g7Iud67OE7J6QOiBrZXkgKHVucXVvdGVkKVxuICAgKi9cbiAgcHJpdmF0ZSBnZXRQcm9wZXJ0eUtleShuYW1lOiB0cy5Qcm9wZXJ0eU5hbWUpOiBzdHJpbmcgfCBudWxsIHtcbiAgICBpZiAodHMuaXNTdHJpbmdMaXRlcmFsKG5hbWUpKSB7XG4gICAgICByZXR1cm4gbmFtZS50ZXh0O1xuICAgIH1cbiAgICBpZiAodHMuaXNJZGVudGlmaWVyKG5hbWUpKSB7XG4gICAgICByZXR1cm4gbmFtZS50ZXh0O1xuICAgIH1cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiDtlITroZztjbzti7Dsl5DshJwgRGljdEVudHJ5IOy2lOy2nFxuICAgKiAtIOusuOyekOyXtDog7Iuk7KCcIOusuOyekOyXtCDqsJJcbiAgICogLSDtlajsiJg6IOybkOuzuCDshozsiqQgKOyXrOufrCDspITsnYAg7ZWcIOykhOuhnCDsoJXqt5ztmZQpXG4gICAqL1xuICBwcml2YXRlIGV4dHJhY3REaWN0RW50cnkoXG4gICAgcHJvcDogdHMuT2JqZWN0TGl0ZXJhbEVsZW1lbnRMaWtlLFxuICAgIHNvdXJjZUZpbGU6IHRzLlNvdXJjZUZpbGUsXG4gICk6IERpY3RFbnRyeSB8IG51bGwge1xuICAgIGlmICghdHMuaXNQcm9wZXJ0eUFzc2lnbm1lbnQocHJvcCkpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGNvbnN0IGtleSA9IHRoaXMuZ2V0UHJvcGVydHlLZXkocHJvcC5uYW1lKTtcbiAgICBpZiAoIWtleSkgcmV0dXJuIG51bGw7XG5cbiAgICBjb25zdCBpbml0ID0gcHJvcC5pbml0aWFsaXplcjtcblxuICAgIC8vIO2ZlOyCtO2RnCDtlajsiJhcbiAgICBpZiAodHMuaXNBcnJvd0Z1bmN0aW9uKGluaXQpKSB7XG4gICAgICBjb25zdCBmdW5jVGV4dCA9IGluaXQuZ2V0VGV4dChzb3VyY2VGaWxlKTtcbiAgICAgIGNvbnN0IG5vcm1hbGl6ZWQgPSBmdW5jVGV4dC5yZXBsYWNlKC9cXHMqXFxuXFxzKi9nLCBcIiBcIikudHJpbSgpO1xuICAgICAgcmV0dXJuIHsga2V5LCB2YWx1ZTogbm9ybWFsaXplZCwgaXNGdW5jdGlvbjogdHJ1ZSB9O1xuICAgIH1cblxuICAgIC8vIOusuOyekOyXtCDrpqzthLDrn7RcbiAgICBpZiAodHMuaXNTdHJpbmdMaXRlcmFsKGluaXQpKSB7XG4gICAgICByZXR1cm4geyBrZXksIHZhbHVlOiBpbml0LnRleHQsIGlzRnVuY3Rpb246IGZhbHNlIH07XG4gICAgfVxuXG4gICAgLy8g7YWc7ZSM66a/IOumrO2EsOuftCAo67OA7IiYIOyXhuydjClcbiAgICBpZiAodHMuaXNOb1N1YnN0aXR1dGlvblRlbXBsYXRlTGl0ZXJhbChpbml0KSkge1xuICAgICAgcmV0dXJuIHsga2V5LCB2YWx1ZTogaW5pdC50ZXh0LCBpc0Z1bmN0aW9uOiBmYWxzZSB9O1xuICAgIH1cblxuICAgIC8vIOq4sO2DgCAo7JiIOiDtlajsiJgg7ZGc7ZiE7IudKVxuICAgIHJldHVybiB7XG4gICAgICBrZXksXG4gICAgICB2YWx1ZTogaW5pdC5nZXRUZXh0KHNvdXJjZUZpbGUpLFxuICAgICAgaXNGdW5jdGlvbjogdHMuaXNGdW5jdGlvbkV4cHJlc3Npb24oaW5pdCksXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiDtlITroZzsoJ3tirjsnZggaTE4biBkaWN0IO2MjOydvCDqsr3roZzrpbwg67CY7ZmY7ZWp64uI64ukLlxuICAgKiBAcGFyYW0gbG9jYWxlIC0g66Gc7LyA7J28IChrbywgZW4g65OxKVxuICAgKiBAcGFyYW0gdGFyZ2V0IC0g7YOA6rKfIOuUlOugie2GoOumrCAoYXBpLCB3ZWIsIGFwcClcbiAgICovXG4gIGdldFByb2plY3REaWN0UGF0aChsb2NhbGU6IHN0cmluZywgdGFyZ2V0OiBcImFwaVwiIHwgXCJ3ZWJcIiB8IFwiYXBwXCIgPSBcImFwaVwiKTogc3RyaW5nIHtcbiAgICBjb25zdCBkaXIgPSB0YXJnZXQgPT09IFwiYXBpXCIgPyBTb25hbXUuY29uZmlnLmFwaS5kaXIgOiB0YXJnZXQ7XG4gICAgcmV0dXJuIHBhdGguam9pbihTb25hbXUuYXBwUm9vdFBhdGgsIGRpciwgXCJzcmNcIiwgXCJpMThuXCIsIGAke2xvY2FsZX0udHNgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBzb25hbXUg64K07J6lIGRpY3Qg7YyM7J28IOqyveuhnOulvCDrsJjtmZjtlanri4jri6QuXG4gICAqL1xuICBwcml2YXRlIGdldFNvbmFtdURpY3RQYXRoKGxvY2FsZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBjb25zdCBwYWNrYWdlUm9vdCA9IHBhdGgucmVzb2x2ZShpbXBvcnQubWV0YS5kaXJuYW1lLCBcIi4uXCIsIFwiLi5cIik7XG4gICAgcmV0dXJuIHBhdGguam9pbihwYWNrYWdlUm9vdCwgXCJzcmNcIiwgXCJkaWN0XCIsIGAke2xvY2FsZX0udHNgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiDtlYTsmpTtlZwgZGljdCDtgqTqsIAg7ZSE66Gc7KCd7Yq47JeQIOyhtOyerO2VmOuKlOyngCDtmZXsnbjtlZjqs6AsIOyXhuycvOuptCDstpTqsIDtlanri4jri6QuXG4gICAqIGRlZmF1bHRMb2NhbGXsl5Drp4wg7LaU6rCA7ZWY66mwLCDri6TrpbggbG9jYWxl7J2AIOyCrOyaqeyekOqwgCDsp4HsoJEg67KI7Jet7ZW07JW8IO2VqeuLiOuLpC5cbiAgICpcbiAgICogQHBhcmFtIHJlcXVpcmVkS2V5cyAtIO2VhOyalO2VnCDtgqQg66qp66GdXG4gICAqIEBwYXJhbSB0YXJnZXQgLSDtg4Dqsp8g65SU66CJ7Yag66asIChhcGksIHdlYiwgYXBwKVxuICAgKiBAcmV0dXJucyDstpTqsIDrkJwg7YKkIOuqqeuhnVxuICAgKi9cbiAgYXN5bmMgZW5zdXJlRGljdEtleXMoXG4gICAgcmVxdWlyZWRLZXlzOiBzdHJpbmdbXSxcbiAgICB0YXJnZXQ6IFwiYXBpXCIgfCBcIndlYlwiIHwgXCJhcHBcIiA9IFwiYXBpXCIsXG4gICk6IFByb21pc2U8c3RyaW5nW10+IHtcbiAgICBjb25zdCB7IGRlZmF1bHRMb2NhbGUgfSA9IFNvbmFtdS5jb25maWcuaTE4bjtcbiAgICBjb25zdCBwcm9qZWN0RGljdFBhdGggPSB0aGlzLmdldFByb2plY3REaWN0UGF0aChkZWZhdWx0TG9jYWxlLCB0YXJnZXQpO1xuXG4gICAgLy8g7ZSE66Gc7KCd7Yq4IGRpY3Qg7YyM7J287J20IOyXhuycvOuptCDslYTrrLTqsoPrj4Qg7ZWY7KeAIOyViuydjFxuICAgIGlmICghZnMuZXhpc3RzU3luYyhwcm9qZWN0RGljdFBhdGgpKSB7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuXG4gICAgLy8g7ZSE66Gc7KCd7Yq4IGRpY3Tsl5DshJwg6riw7KG0IO2CpCDtjIzsi7FcbiAgICBjb25zdCBwcm9qZWN0RW50cmllcyA9IHRoaXMucGFyc2VEaWN0RmlsZShwcm9qZWN0RGljdFBhdGgpO1xuICAgIGNvbnN0IGV4aXN0aW5nS2V5cyA9IG5ldyBTZXQocHJvamVjdEVudHJpZXMubWFwKChlKSA9PiBlLmtleSkpO1xuXG4gICAgLy8g64iE652965CcIO2CpCDssL7quLBcbiAgICBjb25zdCBtaXNzaW5nS2V5cyA9IHJlcXVpcmVkS2V5cy5maWx0ZXIoKGtleSkgPT4gIWV4aXN0aW5nS2V5cy5oYXMoa2V5KSk7XG5cbiAgICBpZiAobWlzc2luZ0tleXMubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuXG4gICAgLy8gc29uYW11IGRpY3Tsl5DshJwg6riw67O46rCSIOqwgOyguOyYpOq4sFxuICAgIGNvbnN0IHNvbmFtdURpY3RQYXRoID0gdGhpcy5nZXRTb25hbXVEaWN0UGF0aChkZWZhdWx0TG9jYWxlKTtcbiAgICBpZiAoIWZzLmV4aXN0c1N5bmMoc29uYW11RGljdFBhdGgpKSB7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuXG4gICAgY29uc3Qgc29uYW11RW50cmllcyA9IHRoaXMucGFyc2VEaWN0RmlsZShzb25hbXVEaWN0UGF0aCk7XG4gICAgY29uc3Qgc29uYW11RGljdCA9IG5ldyBNYXAoc29uYW11RW50cmllcy5tYXAoKGUpID0+IFtlLmtleSwgZV0pKTtcblxuICAgIC8vIOy2lOqwgO2VoCDsl5Ttirjrpqwg7IOd7ISxXG4gICAgY29uc3QgZW50cmllc1RvQWRkID0gbWlzc2luZ0tleXNcbiAgICAgIC5tYXAoKGtleSkgPT4gc29uYW11RGljdC5nZXQoa2V5KSlcbiAgICAgIC5maWx0ZXIoKGVudHJ5KTogZW50cnkgaXMgTm9uTnVsbGFibGU8dHlwZW9mIGVudHJ5PiA9PiBlbnRyeSAhPT0gdW5kZWZpbmVkKTtcblxuICAgIGlmIChlbnRyaWVzVG9BZGQubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuXG4gICAgLy8g7ZSE66Gc7KCd7Yq4IGRpY3Qg7YyM7J287JeQIOy2lOqwgFxuICAgIGF3YWl0IHRoaXMuYXBwZW5kRW50cmllc1RvRGljdEZpbGUocHJvamVjdERpY3RQYXRoLCBlbnRyaWVzVG9BZGQsIGRlZmF1bHRMb2NhbGUsIHRydWUpO1xuXG4gICAgcmV0dXJuIGVudHJpZXNUb0FkZC5tYXAoKGUpID0+IGUua2V5KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBkaWN0IO2MjOydvOyXkCDsl5Ttirjrpqzrpbwg7LaU6rCA7ZWp64uI64ukLlxuICAgKiDquLDsobQg7YyM7J287J2EIO2MjOyLse2VmOqzoCwg7IOIIOyXlO2KuOumrOulvCDstpTqsIDtlZwg65KkLCDsoITssrQg7YyM7J287J2EIOyerOyDneyEse2VqeuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgYXBwZW5kRW50cmllc1RvRGljdEZpbGUoXG4gICAgZmlsZVBhdGg6IHN0cmluZyxcbiAgICBlbnRyaWVzOiBEaWN0RW50cnlbXSxcbiAgICBsb2NhbGU6IHN0cmluZyxcbiAgICBpc0RlZmF1bHRMb2NhbGU6IGJvb2xlYW4sXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIC8vIOq4sOyhtCBlbnRyaWVzIO2MjOyLsVxuICAgIGNvbnN0IGV4aXN0aW5nRW50cmllcyA9IHRoaXMucGFyc2VEaWN0RmlsZShmaWxlUGF0aCk7XG5cbiAgICAvLyDsg4ggZW50cmllcyDstpTqsIBcbiAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGVudHJpZXMpIHtcbiAgICAgIGV4aXN0aW5nRW50cmllcy5wdXNoKGVudHJ5KTtcbiAgICB9XG5cbiAgICAvLyDtjIzsnbwg7J6s7IOd7ISxXG4gICAgY29uc3QgY29udGVudCA9IHRoaXMuZ2VuZXJhdGVQcm9qZWN0RGljdChsb2NhbGUsIGV4aXN0aW5nRW50cmllcywgaXNEZWZhdWx0TG9jYWxlKTtcbiAgICBjb25zdCBmb3JtYXR0ZWQgPSBhd2FpdCBmb3JtYXRDb2RlKGNvbnRlbnQsIFwidHlwZXNjcmlwdFwiLCBmaWxlUGF0aCk7XG4gICAgZnMud3JpdGVGaWxlU3luYyhmaWxlUGF0aCwgZm9ybWF0dGVkLCBcInV0Zi04XCIpO1xuICB9XG5cbiAgLyoqXG4gICAqIO2VqOyImCDqsJLrk6Tsl5DshJwg7IKs7Jqp65CY64qUIO2XrO2NvCDtlajsiJjrpbwg6rCQ7KeA7ZWp64uI64ukLlxuICAgKi9cbiAgcHJpdmF0ZSBkZXRlY3RVc2VkSGVscGVycyhlbnRyaWVzOiBEaWN0RW50cnlbXSk6IHtcbiAgICBoZWxwZXJzOiBzdHJpbmdbXTtcbiAgICB1c2VzRm9ybWF0OiBib29sZWFuO1xuICB9IHtcbiAgICBjb25zdCBmdW5jdGlvbkVudHJpZXMgPSBlbnRyaWVzLmZpbHRlcigoZSkgPT4gZS5pc0Z1bmN0aW9uKTtcbiAgICBjb25zdCBoZWxwZXJzOiBzdHJpbmdbXSA9IFtdO1xuXG4gICAgZm9yIChjb25zdCBoZWxwZXIgb2YgW1wicGx1cmFsXCIsIFwiam9zYVwiXSkge1xuICAgICAgLy8g7ZWo7IiY66qF7J20IOuLqOyWtCDqsr3qs4TroZwg7IKs7Jqp65CY64qU7KeAIO2ZleyduCAo7JiIOiBwbHVyYWwoIOuYkOuKlCBwbHVyYWwsKVxuICAgICAgY29uc3QgcGF0dGVybiA9IG5ldyBSZWdFeHAoYFxcXFxiJHtoZWxwZXJ9XFxcXHMqXFxcXChgKTtcbiAgICAgIGlmIChmdW5jdGlvbkVudHJpZXMuc29tZSgoZSkgPT4gcGF0dGVybi50ZXN0KGUudmFsdWUpKSkge1xuICAgICAgICBoZWxwZXJzLnB1c2goaGVscGVyKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBmb3JtYXQg7IKs7JqpIOyXrOu2gCDrs4Trj4Qg6rCQ7KeAIChmb3JtYXQubnVtYmVyKC4uLiksIGZvcm1hdC5kYXRlKC4uLikg65OxKVxuICAgIGNvbnN0IGZvcm1hdFBhdHRlcm4gPSAvXFxiZm9ybWF0XFwuXFx3K1xccypcXCgvO1xuICAgIGNvbnN0IHVzZXNGb3JtYXQgPSBmdW5jdGlvbkVudHJpZXMuc29tZSgoZSkgPT4gZm9ybWF0UGF0dGVybi50ZXN0KGUudmFsdWUpKTtcblxuICAgIHJldHVybiB7IGhlbHBlcnMsIHVzZXNGb3JtYXQgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQcm9qZWN0IGRpY3Qg7YyM7J28IOyDneyEsVxuICAgKi9cbiAgZ2VuZXJhdGVQcm9qZWN0RGljdChsb2NhbGU6IHN0cmluZywgZW50cmllczogRGljdEVudHJ5W10sIGlzRGVmYXVsdExvY2FsZTogYm9vbGVhbik6IHN0cmluZyB7XG4gICAgLy8ga2V5IOyVjO2MjOuysyDsiJwg7KCV66CsXG4gICAgY29uc3Qgc29ydGVkID0gWy4uLmVudHJpZXNdLnRvU29ydGVkKChhLCBiKSA9PiBhLmtleS5sb2NhbGVDb21wYXJlKGIua2V5KSk7XG5cbiAgICBjb25zdCBsaW5lczogc3RyaW5nW10gPSBbXTtcblxuICAgIC8vIO2VqOyImCDqsJLsl5DshJwg7IKs7Jqp65CY64qUIO2XrO2NvCDtlajsiJgg6rCQ7KeAXG4gICAgY29uc3QgeyBoZWxwZXJzLCB1c2VzRm9ybWF0IH0gPSB0aGlzLmRldGVjdFVzZWRIZWxwZXJzKGVudHJpZXMpO1xuXG4gICAgLy8g7Zes7Y28IO2VqOyImCBpbXBvcnQg7LaU6rCAXG4gICAgY29uc3QgaW1wb3J0cyA9IFsuLi5oZWxwZXJzXTtcbiAgICBpZiAodXNlc0Zvcm1hdCkge1xuICAgICAgaW1wb3J0cy5wdXNoKFwiY3JlYXRlRm9ybWF0XCIpO1xuICAgIH1cbiAgICBpZiAoaW1wb3J0cy5sZW5ndGggPiAwKSB7XG4gICAgICBsaW5lcy5wdXNoKGBpbXBvcnQgeyAke2ltcG9ydHMuam9pbihcIiwgXCIpfSB9IGZyb20gXCJzb25hbXUvZGljdFwiO2ApO1xuICAgIH1cblxuICAgIGlmICghaXNEZWZhdWx0TG9jYWxlKSB7XG4gICAgICBsaW5lcy5wdXNoKCdpbXBvcnQgeyBkZWZpbmVMb2NhbGUgfSBmcm9tIFwiLi9zZC5nZW5lcmF0ZWRcIjsnKTtcbiAgICB9XG5cbiAgICBpZiAoaW1wb3J0cy5sZW5ndGggPiAwIHx8ICFpc0RlZmF1bHRMb2NhbGUpIHtcbiAgICAgIGxpbmVzLnB1c2goXCJcIik7XG4gICAgfVxuXG4gICAgLy8gZm9ybWF0IOyCrOyaqSDsi5wgY3JlYXRlRm9ybWF0IO2YuOy2nCDstpTqsIBcbiAgICBpZiAodXNlc0Zvcm1hdCkge1xuICAgICAgbGluZXMucHVzaChgY29uc3QgZm9ybWF0ID0gY3JlYXRlRm9ybWF0KFwiJHtsb2NhbGV9XCIpO2ApO1xuICAgICAgbGluZXMucHVzaChcIlwiKTtcbiAgICB9XG5cbiAgICBsaW5lcy5wdXNoKFwiLyoqXCIpO1xuICAgIGxpbmVzLnB1c2goYCAqIFByb2plY3QgJHtsb2NhbGUudG9VcHBlckNhc2UoKX0gRGljdGlvbmFyeWApO1xuICAgIGxpbmVzLnB1c2goXCIgKi9cIik7XG5cbiAgICBpZiAoaXNEZWZhdWx0TG9jYWxlKSB7XG4gICAgICBsaW5lcy5wdXNoKFwiZXhwb3J0IGRlZmF1bHQge1wiKTtcbiAgICB9IGVsc2Uge1xuICAgICAgbGluZXMucHVzaChcImV4cG9ydCBkZWZhdWx0IGRlZmluZUxvY2FsZSh7XCIpO1xuICAgIH1cblxuICAgIGZvciAoY29uc3QgZW50cnkgb2Ygc29ydGVkKSB7XG4gICAgICBpZiAoZW50cnkuaXNGdW5jdGlvbikge1xuICAgICAgICAvLyDtlajsiJjsnbgg6rK97JqwOiDsm5DtmJUg6re464yA66GcIOy2nOugpVxuICAgICAgICBsaW5lcy5wdXNoKGAgIFwiJHtlbnRyeS5rZXl9XCI6ICR7ZW50cnkudmFsdWV9LGApO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbGluZXMucHVzaChgICBcIiR7ZW50cnkua2V5fVwiOiAke0pTT04uc3RyaW5naWZ5KGVudHJ5LnZhbHVlKX0sYCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKGlzRGVmYXVsdExvY2FsZSkge1xuICAgICAgbGluZXMucHVzaChcIn0gYXMgY29uc3Q7XCIpO1xuICAgIH0gZWxzZSB7XG4gICAgICBsaW5lcy5wdXNoKFwifSk7XCIpO1xuICAgIH1cbiAgICBsaW5lcy5wdXNoKFwiXCIpO1xuXG4gICAgcmV0dXJuIGxpbmVzLmpvaW4oXCJcXG5cIik7XG4gIH1cblxuICAvKipcbiAgICogaTE4biDshKTsoJXsnYQg6rCA7KC47Ji164uI64ukLlxuICAgKi9cbiAgcHJpdmF0ZSBnZXRJMThuQ29uZmlnKCk6IEkxOG5Db25maWcge1xuICAgIHJldHVybiBTb25hbXUuY29uZmlnLmkxOG47XG4gIH1cblxuICAvKipcbiAgICogaTE4biDrlJTroInthqDrpqwg6rK966Gc66W8IOuwmO2ZmO2VmOqzoCwg7JeG7Jy866m0IOyDneyEse2VqeuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgZW5zdXJlSTE4bkRpcigpOiBzdHJpbmcge1xuICAgIGNvbnN0IGkxOG5EaXIgPSBwYXRoLmpvaW4oU29uYW11LmFwaVJvb3RQYXRoLCBcInNyY1wiLCBcImkxOG5cIik7XG4gICAgaWYgKCFmcy5leGlzdHNTeW5jKGkxOG5EaXIpKSB7XG4gICAgICBmcy5ta2RpclN5bmMoaTE4bkRpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgfVxuICAgIHJldHVybiBpMThuRGlyO1xuICB9XG5cbiAgLyoqXG4gICAqIGRpY3Qg7YyM7J287J2EIOyggOyepe2VqeuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgc2F2ZURpY3RGaWxlKFxuICAgIGxvY2FsZTogc3RyaW5nLFxuICAgIGVudHJpZXM6IERpY3RFbnRyeVtdLFxuICAgIGlzRGVmYXVsdExvY2FsZTogYm9vbGVhbixcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgaTE4bkRpciA9IHRoaXMuZW5zdXJlSTE4bkRpcigpO1xuICAgIGNvbnN0IGRpY3RQYXRoID0gcGF0aC5qb2luKGkxOG5EaXIsIGAke2xvY2FsZX0udHNgKTtcbiAgICBjb25zdCBjb250ZW50ID0gdGhpcy5nZW5lcmF0ZVByb2plY3REaWN0KGxvY2FsZSwgZW50cmllcywgaXNEZWZhdWx0TG9jYWxlKTtcbiAgICBjb25zdCBmb3JtYXR0ZWQgPSBhd2FpdCBmb3JtYXRDb2RlKGNvbnRlbnQsIFwidHlwZXNjcmlwdFwiLCBkaWN0UGF0aCk7XG4gICAgZnMud3JpdGVGaWxlU3luYyhkaWN0UGF0aCwgZm9ybWF0dGVkLCBcInV0Zi04XCIpO1xuICB9XG5cbiAgLyoqXG4gICAqIGkxOG4ga2V566W8IO2MjOyLse2VmOyXrCBlbnRpdHkg6rSA66CoIOygleuztCDstpTstpxcbiAgICovXG4gIHBhcnNlRW50aXR5S2V5KGtleTogc3RyaW5nKTogRW50aXR5S2V5SW5mbyB7XG4gICAgLy8gZW50aXR5LntFbnRpdHlJZH0gKGxpc3QsIGNyZWF0ZSwgZWRpdCDsoJzsmbgpXG4gICAgY29uc3QgZW50aXR5VGl0bGVNYXRjaCA9IGtleS5tYXRjaCgvXmVudGl0eVxcLihbQS1aXVthLXpBLVowLTldKikkLyk7XG4gICAgaWYgKFxuICAgICAgZW50aXR5VGl0bGVNYXRjaCAmJlxuICAgICAgIWtleS5pbmNsdWRlcyhcIi5saXN0XCIpICYmXG4gICAgICAha2V5LmluY2x1ZGVzKFwiLmNyZWF0ZVwiKSAmJlxuICAgICAgIWtleS5pbmNsdWRlcyhcIi5lZGl0XCIpXG4gICAgKSB7XG4gICAgICByZXR1cm4geyB0eXBlOiBcImVudGl0eVRpdGxlXCIsIGVudGl0eUlkOiBlbnRpdHlUaXRsZU1hdGNoWzFdIH07XG4gICAgfVxuXG4gICAgLy8gZW50aXR5LntFbnRpdHlJZH0ue3Byb3BOYW1lfVxuICAgIGNvbnN0IHByb3BEZXNjTWF0Y2ggPSBrZXkubWF0Y2goL15lbnRpdHlcXC4oW0EtWl1bYS16QS1aMC05XSopXFwuKFthLXpfXVthLXowLTlfXSopJC8pO1xuICAgIGlmIChwcm9wRGVzY01hdGNoKSB7XG4gICAgICByZXR1cm4geyB0eXBlOiBcInByb3BEZXNjXCIsIGVudGl0eUlkOiBwcm9wRGVzY01hdGNoWzFdLCBwcm9wTmFtZTogcHJvcERlc2NNYXRjaFsyXSB9O1xuICAgIH1cblxuICAgIC8vIGVudW0ue0VudW1JZH0ue3ZhbHVlfVxuICAgIGNvbnN0IGVudW1MYWJlbE1hdGNoID0ga2V5Lm1hdGNoKC9eZW51bVxcLihbQS1aXVthLXpBLVowLTldKilcXC4oLispJC8pO1xuICAgIGlmIChlbnVtTGFiZWxNYXRjaCkge1xuICAgICAgcmV0dXJuIHsgdHlwZTogXCJlbnVtTGFiZWxcIiwgZW51bUlkOiBlbnVtTGFiZWxNYXRjaFsxXSwgZW51bVZhbHVlOiBlbnVtTGFiZWxNYXRjaFsyXSB9O1xuICAgIH1cblxuICAgIHJldHVybiB7IHR5cGU6IFwib3RoZXJcIiB9O1xuICB9XG5cbiAgLyoqXG4gICAqIGVudGl0eSBrZXnsl5Ag64yA7ZW0IGVudGl0eS5qc29uIOyXheuNsOydtO2KuCDsiJjtlolcbiAgICogQHJldHVybnMg7JeF642w7J207Yq4IOyXrOu2gFxuICAgKi9cbiAgYXN5bmMgdXBkYXRlRW50aXR5QnlLZXkoa2V5OiBzdHJpbmcsIHZhbHVlOiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCBrZXlJbmZvID0gdGhpcy5wYXJzZUVudGl0eUtleShrZXkpO1xuXG4gICAgc3dpdGNoIChrZXlJbmZvLnR5cGUpIHtcbiAgICAgIGNhc2UgXCJlbnRpdHlUaXRsZVwiOiB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgZW50aXR5ID0gRW50aXR5TWFuYWdlci5nZXQoa2V5SW5mby5lbnRpdHlJZCk7XG4gICAgICAgICAgaWYgKGVudGl0eS50aXRsZSAhPT0gdmFsdWUpIHtcbiAgICAgICAgICAgIGVudGl0eS50aXRsZSA9IHZhbHVlO1xuICAgICAgICAgICAgYXdhaXQgZW50aXR5LnNhdmUoKTtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgLy8gZW50aXR5IG5vdCBmb3VuZFxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cblxuICAgICAgY2FzZSBcInByb3BEZXNjXCI6IHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBlbnRpdHkgPSBFbnRpdHlNYW5hZ2VyLmdldChrZXlJbmZvLmVudGl0eUlkKTtcbiAgICAgICAgICBjb25zdCBwcm9wSW5kZXggPSBlbnRpdHkucHJvcHMuZmluZEluZGV4KChwKSA9PiBwLm5hbWUgPT09IGtleUluZm8ucHJvcE5hbWUpO1xuICAgICAgICAgIGlmIChwcm9wSW5kZXggIT09IC0xICYmIGVudGl0eS5wcm9wc1twcm9wSW5kZXhdLmRlc2MgIT09IHZhbHVlKSB7XG4gICAgICAgICAgICBlbnRpdHkucHJvcHNbcHJvcEluZGV4XS5kZXNjID0gdmFsdWU7XG4gICAgICAgICAgICBhd2FpdCBlbnRpdHkuc2F2ZSgpO1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgfVxuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAvLyBlbnRpdHkgbm90IGZvdW5kXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuXG4gICAgICBjYXNlIFwiZW51bUxhYmVsXCI6IHtcbiAgICAgICAgZm9yIChjb25zdCBlbnRpdHlJZCBvZiBFbnRpdHlNYW5hZ2VyLmdldEFsbElkcygpKSB7XG4gICAgICAgICAgY29uc3QgZW50aXR5ID0gRW50aXR5TWFuYWdlci5nZXQoZW50aXR5SWQpO1xuICAgICAgICAgIGlmIChlbnRpdHkuZW51bUxhYmVsc1trZXlJbmZvLmVudW1JZF0pIHtcbiAgICAgICAgICAgIGlmIChlbnRpdHkuZW51bUxhYmVsc1trZXlJbmZvLmVudW1JZF1ba2V5SW5mby5lbnVtVmFsdWVdICE9PSB2YWx1ZSkge1xuICAgICAgICAgICAgICBlbnRpdHkuZW51bUxhYmVsc1trZXlJbmZvLmVudW1JZF1ba2V5SW5mby5lbnVtVmFsdWVdID0gdmFsdWU7XG4gICAgICAgICAgICAgIGF3YWl0IGVudGl0eS5zYXZlKCk7XG4gICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cblxuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBzZC5nZW5lcmF0ZWQudHPsl5DshJwgZW50aXR5IGxhYmVscyDstpTstpxcbiAgICogZW50aXR5Lmpzb27sl5DshJwg6rSA66as65CY64qUIOqwkuunjCDtj6ztlaggKC5saXN0LCAuY3JlYXRlLCAuZWRpdCDsoJzsmbgpXG4gICAqL1xuICBleHRyYWN0RW50aXR5TGFiZWxzKCk6IERpY3RFbnRyeVtdIHtcbiAgICBjb25zdCBzZFBhdGggPSBwYXRoLmpvaW4oU29uYW11LmFwaVJvb3RQYXRoLCBcInNyY1wiLCBcImkxOG5cIiwgXCJzZC5nZW5lcmF0ZWQudHNcIik7XG4gICAgaWYgKCFmcy5leGlzdHNTeW5jKHNkUGF0aCkpIHtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5wYXJzZUNvbnN0T2JqZWN0RGVjbGFyYXRpb24oc2RQYXRoLCBcImVudGl0eUxhYmVsc1wiKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBzZC5nZW5lcmF0ZWQudHPsl5DshJwgcmMta2V5cyDstpTstpxcbiAgICogcmVhY3QtY29tcG9uZW50c+yXkOyEnCDqtIDrpqzrkJjripQgaTE4biDtgqTrk6RcbiAgICogQHBhcmFtIGxvY2FsZSAtIOuhnOy8gOydvCAoa28sIGVuIOuTsSlcbiAgICovXG4gIGV4dHJhY3RSQ0tleXMobG9jYWxlOiBzdHJpbmcpOiBEaWN0RW50cnlbXSB7XG4gICAgY29uc3Qgc2RQYXRoID0gcGF0aC5qb2luKFNvbmFtdS5hcGlSb290UGF0aCwgXCJzcmNcIiwgXCJpMThuXCIsIFwic2QuZ2VuZXJhdGVkLnRzXCIpO1xuICAgIGlmICghZnMuZXhpc3RzU3luYyhzZFBhdGgpKSB7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuXG4gICAgLy8gbG9jYWxl67OEIOuzgOyImOuqhSDrp6TtlZEgKHNkLnRlbXBsYXRlLnRz7J2YIGdldFJDS2V5c1Zhck5hbWXqs7wg64+Z7J28KVxuICAgIGNvbnN0IHZhck5hbWUgPSAoKCkgPT4ge1xuICAgICAgaWYgKGxvY2FsZSA9PT0gXCJrb1wiKSByZXR1cm4gXCJyY0tleXNLb1wiO1xuICAgICAgaWYgKGxvY2FsZSA9PT0gXCJlblwiKSByZXR1cm4gXCJyY0tleXNFblwiO1xuICAgICAgLy8g64uk66W4IGxvY2FsZeydgCBlbuydhCBmYWxsYmFja+ycvOuhnCDsgqzsmqlcbiAgICAgIHJldHVybiBcInJjS2V5c0VuXCI7XG4gICAgfSkoKTtcblxuICAgIHJldHVybiB0aGlzLnBhcnNlQ29uc3RPYmplY3REZWNsYXJhdGlvbihzZFBhdGgsIHZhck5hbWUpO1xuICB9XG5cbiAgLyoqXG4gICAqIFByb2plY3QgZGljdCDtjIzsnbwoW2xvY2FsZV0udHMp7JeQ7IScIOuUleyFlOuEiOumrCDroZzrk5xcbiAgICovXG4gIGxvYWRQcm9qZWN0RGljdChsb2NhbGU6IHN0cmluZyk6IHsgZW50cmllczogRGljdEVudHJ5W10gfSB7XG4gICAgY29uc3QgZGljdFBhdGggPSBwYXRoLmpvaW4oU29uYW11LmFwaVJvb3RQYXRoLCBcInNyY1wiLCBcImkxOG5cIiwgYCR7bG9jYWxlfS50c2ApO1xuICAgIGlmICghZnMuZXhpc3RzU3luYyhkaWN0UGF0aCkpIHtcbiAgICAgIHJldHVybiB7IGVudHJpZXM6IFtdIH07XG4gICAgfVxuICAgIHJldHVybiB7IGVudHJpZXM6IHRoaXMucGFyc2VEaWN0RmlsZShkaWN0UGF0aCkgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiDrlJXshZTrhIjrpqwg642w7J207YSwIOyImOynkSAoc29uYW11ICsgZW50aXR5ICsgcHJvamVjdClcbiAgICovXG4gIGFzeW5jIGNvbGxlY3REaWN0aW9uYXJ5KCk6IFByb21pc2U8RGljdGlvbmFyeVJlc3VsdD4ge1xuICAgIGNvbnN0IHsgZGVmYXVsdExvY2FsZSwgc3VwcG9ydGVkTG9jYWxlcyB9ID0gdGhpcy5nZXRJMThuQ29uZmlnKCk7XG4gICAgY29uc3QgbG9jYWxlcyA9IHN1cHBvcnRlZExvY2FsZXM7XG5cbiAgICBjb25zdCByb3dzOiBEaWN0aW9uYXJ5Um93W10gPSBbXTtcbiAgICBjb25zdCByb3dNYXAgPSBuZXcgTWFwPHN0cmluZywgRGljdGlvbmFyeVJvdz4oKTtcblxuICAgIC8vIDEuIFJDIEtleXMgKHNvbmFtdSBzb3VyY2UsIOqwgSBsb2NhbGXrs4QpXG4gICAgZm9yIChjb25zdCBsb2NhbGUgb2YgbG9jYWxlcykge1xuICAgICAgY29uc3QgcmNLZXlzID0gdGhpcy5leHRyYWN0UkNLZXlzKGxvY2FsZSk7XG4gICAgICBmb3IgKGNvbnN0IHJjS2V5IG9mIHJjS2V5cykge1xuICAgICAgICBsZXQgcm93ID0gcm93TWFwLmdldChyY0tleS5rZXkpO1xuICAgICAgICBpZiAoIXJvdykge1xuICAgICAgICAgIHJvdyA9IHtcbiAgICAgICAgICAgIGtleTogcmNLZXkua2V5LFxuICAgICAgICAgICAgc291cmNlOiBcInNvbmFtdVwiLFxuICAgICAgICAgICAgaXNGdW5jdGlvbjogcmNLZXkuaXNGdW5jdGlvbiA/PyBmYWxzZSxcbiAgICAgICAgICB9O1xuICAgICAgICAgIHJvd01hcC5zZXQocmNLZXkua2V5LCByb3cpO1xuICAgICAgICB9XG4gICAgICAgIHJvd1tsb2NhbGVdID0gcmNLZXkudmFsdWU7XG4gICAgICAgIGlmIChyY0tleS5pc0Z1bmN0aW9uKSB7XG4gICAgICAgICAgcm93LmlzRnVuY3Rpb24gPSB0cnVlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gMi4gRW50aXR5IGxhYmVscyAoZGVmYXVsdCBsb2NhbGUg6riw7KSAKVxuICAgIGNvbnN0IGVudGl0eUxhYmVscyA9IHRoaXMuZXh0cmFjdEVudGl0eUxhYmVscygpO1xuICAgIGZvciAoY29uc3QgbGFiZWwgb2YgZW50aXR5TGFiZWxzKSB7XG4gICAgICBjb25zdCByb3c6IERpY3Rpb25hcnlSb3cgPSB7XG4gICAgICAgIGtleTogbGFiZWwua2V5LFxuICAgICAgICBzb3VyY2U6IFwiZW50aXR5XCIsXG4gICAgICAgIGlzRnVuY3Rpb246IGxhYmVsLmlzRnVuY3Rpb24gPz8gZmFsc2UsXG4gICAgICAgIFtkZWZhdWx0TG9jYWxlXTogbGFiZWwudmFsdWUsXG4gICAgICB9O1xuICAgICAgcm93TWFwLnNldChsYWJlbC5rZXksIHJvdyk7XG4gICAgfVxuXG4gICAgLy8gMy4gUHJvamVjdCBkaWN0ICjqsIEgbG9jYWxl67OEKVxuICAgIGZvciAoY29uc3QgbG9jYWxlIG9mIGxvY2FsZXMpIHtcbiAgICAgIGNvbnN0IHsgZW50cmllcyB9ID0gdGhpcy5sb2FkUHJvamVjdERpY3QobG9jYWxlKTtcbiAgICAgIGZvciAoY29uc3QgZW50cnkgb2YgZW50cmllcykge1xuICAgICAgICBjb25zdCBleGlzdGluZyA9IHJvd01hcC5nZXQoZW50cnkua2V5KTtcbiAgICAgICAgaWYgKGV4aXN0aW5nKSB7XG4gICAgICAgICAgLy8gc29uYW11LCBlbnRpdHkgc291cmNl6rCAIOyeiOycvOuptCDtlbTri7kgbG9jYWxlIOqwkuunjCDstpTqsIBcbiAgICAgICAgICBleGlzdGluZ1tsb2NhbGVdID0gZW50cnkudmFsdWU7XG4gICAgICAgICAgaWYgKGVudHJ5LmlzRnVuY3Rpb24pIHtcbiAgICAgICAgICAgIGV4aXN0aW5nLmlzRnVuY3Rpb24gPSB0cnVlO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBwcm9qZWN0IHNvdXJjZeuhnCDsg4jroZwg7LaU6rCAXG4gICAgICAgICAgbGV0IHJvdyA9IHJvd01hcC5nZXQoZW50cnkua2V5KTtcbiAgICAgICAgICBpZiAoIXJvdykge1xuICAgICAgICAgICAgcm93ID0ge1xuICAgICAgICAgICAgICBrZXk6IGVudHJ5LmtleSxcbiAgICAgICAgICAgICAgc291cmNlOiBcInByb2plY3RcIixcbiAgICAgICAgICAgICAgaXNGdW5jdGlvbjogZW50cnkuaXNGdW5jdGlvbixcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICByb3dNYXAuc2V0KGVudHJ5LmtleSwgcm93KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcm93W2xvY2FsZV0gPSBlbnRyeS52YWx1ZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIHJvd3MucHVzaCguLi5yb3dNYXAudmFsdWVzKCkpO1xuICAgIHJvd3Muc29ydCgoYSwgYikgPT4gYS5rZXkubG9jYWxlQ29tcGFyZShiLmtleSkpO1xuXG4gICAgLy8g7Ya16rOEIOqzhOyCsDogbG9jYWxl67OEICjssYTsm4zsp4Qg6rCSIC8g7KCE7LK0IO2CpCDsiJgpXG4gICAgY29uc3Qgc3RhdHM6IFJlY29yZDxzdHJpbmcsIHsgdG90YWw6IG51bWJlcjsgZmlsbGVkOiBudW1iZXI7IHBlcmNlbnQ6IG51bWJlciB9PiA9IHt9O1xuICAgIGNvbnN0IHRvdGFsID0gcm93cy5sZW5ndGg7XG4gICAgZm9yIChjb25zdCBsb2NhbGUgb2YgbG9jYWxlcykge1xuICAgICAgY29uc3QgZmlsbGVkID0gcm93cy5maWx0ZXIoKHJvdykgPT4gcm93W2xvY2FsZV0gIT0gbnVsbCAmJiByb3dbbG9jYWxlXSAhPT0gXCJcIikubGVuZ3RoO1xuICAgICAgY29uc3QgcGVyY2VudCA9IHRvdGFsID4gMCA/IE1hdGgucm91bmQoKGZpbGxlZCAvIHRvdGFsKSAqIDEwMCkgOiAwO1xuICAgICAgc3RhdHNbbG9jYWxlXSA9IHsgdG90YWwsIGZpbGxlZCwgcGVyY2VudCB9O1xuICAgIH1cblxuICAgIHJldHVybiB7IHJvd3MsIGxvY2FsZXMsIGRlZmF1bHRMb2NhbGUsIHN0YXRzIH07XG4gIH1cblxuICAvKipcbiAgICog65SV7IWU64SI66asIOyhsO2ajFxuICAgKi9cbiAgYXN5bmMgZ2V0RGljdGlvbmFyeSgpOiBQcm9taXNlPERpY3Rpb25hcnlSZXN1bHQ+IHtcbiAgICByZXR1cm4gdGhpcy5jb2xsZWN0RGljdGlvbmFyeSgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4Y2Vs66GcIOuCtOuztOuCtOq4sFxuICAgKi9cbiAgYXN5bmMgZXhwb3J0VG9FeGNlbCgpOiBQcm9taXNlPHsgZmlsZW5hbWU6IHN0cmluZzsgYnVmZmVyOiBCdWZmZXIgfT4ge1xuICAgIGNvbnN0IHsgcm93cywgbG9jYWxlcyB9ID0gYXdhaXQgdGhpcy5jb2xsZWN0RGljdGlvbmFyeSgpO1xuXG4gICAgY29uc3Qgd2IgPSBuZXcgV29ya2Jvb2soKTtcbiAgICBjb25zdCBzaGVldCA9IFwiaTE4blwiO1xuICAgIHdiLnNldFNoZWV0TmFtZShcIlNoZWV0MVwiLCBzaGVldCk7XG5cbiAgICBjb25zdCBwcm9qZWN0TmFtZSA9IGAke1NvbmFtdS5jb25maWcucHJvamVjdE5hbWUgPz8gXCJTb25hbXVcIn0gRGljdGlvbmFyeWA7XG4gICAgY29uc3QgaGVhZGVycyA9IFtcImtleVwiLCBcInNvdXJjZVwiLCAuLi5sb2NhbGVzXTtcblxuICAgIC8vIOyKpO2DgOydvCDsoJXsnZhcbiAgICBjb25zdCB0aXRsZVN0eWxlSWQgPSB3Yi5hZGRTdHlsZSh7XG4gICAgICBmb250OiB7IHNpemU6IDIzIH0sXG4gICAgICBhbGlnbm1lbnQ6IHsgdmVydGljYWw6IFwiY2VudGVyXCIsIGhvcml6b250YWw6IFwibGVmdFwiIH0sXG4gICAgfSk7XG4gICAgY29uc3QgaGVhZGVyU3R5bGVJZCA9IHdiLmFkZFN0eWxlKHtcbiAgICAgIGZvbnQ6IHsgYm9sZDogdHJ1ZSwgc2l6ZTogMTEgfSxcbiAgICAgIGFsaWdubWVudDogeyBob3Jpem9udGFsOiBcImNlbnRlclwiLCB2ZXJ0aWNhbDogXCJjZW50ZXJcIiB9LFxuICAgICAgZmlsbDogeyBwYXR0ZXJuOiBcInNvbGlkXCIsIGZnQ29sb3I6IFwiRjFGMUYxXCIgfSxcbiAgICAgIGJvcmRlcjoge1xuICAgICAgICB0b3A6IHsgc3R5bGU6IFwidGhpblwiLCBjb2xvcjogXCJEMEQwRDBcIiB9LFxuICAgICAgICBsZWZ0OiB7IHN0eWxlOiBcInRoaW5cIiwgY29sb3I6IFwiRDBEMEQwXCIgfSxcbiAgICAgICAgYm90dG9tOiB7IHN0eWxlOiBcInRoaW5cIiwgY29sb3I6IFwiRDBEMEQwXCIgfSxcbiAgICAgICAgcmlnaHQ6IHsgc3R5bGU6IFwidGhpblwiLCBjb2xvcjogXCJEMEQwRDBcIiB9LFxuICAgICAgfSxcbiAgICB9KTtcbiAgICBjb25zdCBkYXRhU3R5bGVJZCA9IHdiLmFkZFN0eWxlKHtcbiAgICAgIGZvbnQ6IHsgc2l6ZTogMTEgfSxcbiAgICAgIGFsaWdubWVudDogeyB2ZXJ0aWNhbDogXCJjZW50ZXJcIiwgaG9yaXpvbnRhbDogXCJsZWZ0XCIgfSxcbiAgICB9KTtcblxuICAgIC8vIO2WiSAxOiDtlITroZzsoJ3tirjrqoVcbiAgICB3Yi5zZXRDZWxsVmFsdWUoc2hlZXQsIFwiQTFcIiwgcHJvamVjdE5hbWUpO1xuICAgIHdiLnNldENlbGxTdHlsZShzaGVldCwgXCJBMVwiLCB0aXRsZVN0eWxlSWQpO1xuICAgIHdiLnNldFJvd0hlaWdodChzaGVldCwgMSwgMjgpO1xuXG4gICAgLy8g7ZaJIDI6IOu5iCDtlokgKOq4sOuzuOqwkilcblxuICAgIC8vIO2WiSAzOiDtl6TrjZRcbiAgICB3Yi5zZXRSb3dWYWx1ZXMoc2hlZXQsIDMsIFwiQVwiLCBoZWFkZXJzKTtcbiAgICB3Yi5zZXRSb3dIZWlnaHQoc2hlZXQsIDMsIDI2KTtcbiAgICBmb3IgKGxldCBjb2wgPSAwOyBjb2wgPCBoZWFkZXJzLmxlbmd0aDsgY29sKyspIHtcbiAgICAgIHdiLnNldENlbGxTdHlsZShzaGVldCwgYCR7Y29sTGV0dGVyKGNvbCl9M2AsIGhlYWRlclN0eWxlSWQpO1xuICAgIH1cblxuICAgIC8vIO2WiSA0IOydtO2bhDog642w7J207YSwXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCByb3dzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCByb3cgPSByb3dzW2ldO1xuICAgICAgY29uc3QgdmFsdWVzID0gW3Jvdy5rZXksIHJvdy5zb3VyY2UsIC4uLmxvY2FsZXMubWFwKChsb2NhbGUpID0+IHJvd1tsb2NhbGVdID8/IFwiXCIpXTtcbiAgICAgIGNvbnN0IHJvd051bSA9IGkgKyA0O1xuICAgICAgd2Iuc2V0Um93VmFsdWVzKHNoZWV0LCByb3dOdW0sIFwiQVwiLCB2YWx1ZXMpO1xuICAgICAgd2Iuc2V0Um93SGVpZ2h0KHNoZWV0LCByb3dOdW0sIDI0KTtcbiAgICAgIGZvciAobGV0IGNvbCA9IDA7IGNvbCA8IHZhbHVlcy5sZW5ndGg7IGNvbCsrKSB7XG4gICAgICAgIHdiLnNldENlbGxTdHlsZShzaGVldCwgYCR7Y29sTGV0dGVyKGNvbCl9JHtyb3dOdW19YCwgZGF0YVN0eWxlSWQpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIOy7rOufvCDrhIjruYQg6rOE7IKwXG4gICAgY29uc3QgTUFYX1dJRFRIID0gNTA7XG4gICAgY29uc3QgTUlOX1dJRFRIID0gMTA7XG4gICAgY29uc3QgY29sdW1uV2lkdGhzOiBudW1iZXJbXSA9IGhlYWRlcnMubWFwKChoZWFkZXIpID0+IE1hdGgubWF4KGhlYWRlci5sZW5ndGgsIE1JTl9XSURUSCkpO1xuXG4gICAgZm9yIChjb25zdCByb3cgb2Ygcm93cykge1xuICAgICAgY29uc3QgdmFsdWVzID0gW3Jvdy5rZXksIHJvdy5zb3VyY2UsIC4uLmxvY2FsZXMubWFwKChsb2NhbGUpID0+IHJvd1tsb2NhbGVdID8/IFwiXCIpXTtcbiAgICAgIHZhbHVlcy5mb3JFYWNoKCh2YWx1ZSwgaWR4KSA9PiB7XG4gICAgICAgIGNvbnN0IHRleHRMZW5ndGggPSBTdHJpbmcodmFsdWUpLmxlbmd0aDtcbiAgICAgICAgY29sdW1uV2lkdGhzW2lkeF0gPSBNYXRoLm1pbihNYXRoLm1heChjb2x1bW5XaWR0aHNbaWR4XSwgdGV4dExlbmd0aCksIE1BWF9XSURUSCk7XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICAvLyDsu6zrn7wg64SI67mEIOyggeyaqVxuICAgIGZvciAobGV0IGNvbCA9IDA7IGNvbCA8IGNvbHVtbldpZHRocy5sZW5ndGg7IGNvbCsrKSB7XG4gICAgICB3Yi5zZXRDb2xXaWR0aChzaGVldCwgY29sTGV0dGVyKGNvbCksIGNvbHVtbldpZHRoc1tjb2xdICsgMik7XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGZpbGVuYW1lOiBgJHtwcm9qZWN0TmFtZX0tJHtuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCkuc3BsaXQoXCJUXCIpWzBdfS54bHN4YCxcbiAgICAgIGJ1ZmZlcjogd2Iud3JpdGVCdWZmZXJTeW5jKCksXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFeGNlbOyXkOyEnCDqsIDsoLjsmKTquLBcbiAgICpcbiAgICog7ZiV7IudOlxuICAgKiAtIDHtlok6IO2UhOuhnOygne2KuOuqhVxuICAgKiAtIDLtlok6IOu5iCDtlolcbiAgICogLSAz7ZaJOiDtl6TrjZQgKGtleSwgc291cmNlLCBsb2NhbGVzLi4uKVxuICAgKiAtIDTtlokg7J207ZuEOiDrjbDsnbTthLBcbiAgICovXG4gIGFzeW5jIGltcG9ydEZyb21FeGNlbChidWZmZXI6IEJ1ZmZlcik6IFByb21pc2U8SW1wb3J0UmVzdWx0PiB7XG4gICAgY29uc3Qgd2IgPSBXb3JrYm9vay5vcGVuQnVmZmVyU3luYyhidWZmZXIpO1xuICAgIGNvbnN0IHNoZWV0ID0gd2Iuc2hlZXROYW1lc1swXTtcbiAgICBjb25zdCBhbGxSb3dzID0gd2IuZ2V0Um93cyhzaGVldCk7XG5cbiAgICBjb25zdCB7IGRlZmF1bHRMb2NhbGUsIHN1cHBvcnRlZExvY2FsZXMgfSA9IHRoaXMuZ2V0STE4bkNvbmZpZygpO1xuICAgIGNvbnN0IGxvY2FsZXMgPSBzdXBwb3J0ZWRMb2NhbGVzO1xuXG4gICAgbGV0IHVwZGF0ZWRFbnRpdGllcyA9IDA7XG4gICAgbGV0IHVwZGF0ZWRMb2NhbGVzID0gMDtcblxuICAgIC8vIGxvY2FsZeuzhCBwcm9qZWN0IGRpY3QgZW50cmllc1xuICAgIGNvbnN0IHByb2plY3REaWN0RW50cmllczogUmVjb3JkPHN0cmluZywgRGljdEVudHJ5W10+ID0ge307XG4gICAgZm9yIChjb25zdCBsb2NhbGUgb2YgbG9jYWxlcykge1xuICAgICAgcHJvamVjdERpY3RFbnRyaWVzW2xvY2FsZV0gPSBbXTtcbiAgICB9XG5cbiAgICAvLyDtl6TrjZQg7ZaJIOywvuq4sDog7LKrIOuyiOynuCDsu6zrn7woQSnsnbQgXCJrZXlcIuyduCDtlolcbiAgICBsZXQgaGVhZGVyUm93TnVtID0gMDtcbiAgICBmb3IgKGNvbnN0IHJvd0RhdGEgb2YgYWxsUm93cykge1xuICAgICAgY29uc3QgZmlyc3RDZWxsID0gcm93RGF0YS5jZWxscy5maW5kKChjKSA9PiBjLmNvbHVtbiA9PT0gXCJBXCIpO1xuICAgICAgY29uc3QgZmlyc3RDZWxsVmFsdWUgPSBTdHJpbmcoZmlyc3RDZWxsPy52YWx1ZSA/PyBcIlwiKVxuICAgICAgICAudHJpbSgpXG4gICAgICAgIC50b0xvd2VyQ2FzZSgpO1xuICAgICAgaWYgKGZpcnN0Q2VsbFZhbHVlID09PSBcImtleVwiKSB7XG4gICAgICAgIGhlYWRlclJvd051bSA9IHJvd0RhdGEucm93O1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoaGVhZGVyUm93TnVtID09PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgQmFkUmVxdWVzdEV4Y2VwdGlvbihTRChcInNvbmFtdS5lcnJvci5oZWFkZXJSb3dOb3RGb3VuZFwiKSk7XG4gICAgfVxuXG4gICAgLy8g7Zek642UIO2WieyXkOyEnCDsu6zrn7wg66ek7ZWRIOq1rOyEsVxuICAgIGNvbnN0IGhlYWRlclJvd0RhdGEgPSBhbGxSb3dzLmZpbmQoKHIpID0+IHIucm93ID09PSBoZWFkZXJSb3dOdW0pO1xuICAgIGNvbnN0IGNvbFRvSGVhZGVyOiBNYXA8c3RyaW5nLCBzdHJpbmc+ID0gbmV3IE1hcCgpO1xuICAgIGlmIChoZWFkZXJSb3dEYXRhKSB7XG4gICAgICBmb3IgKGNvbnN0IGNlbGwgb2YgaGVhZGVyUm93RGF0YS5jZWxscykge1xuICAgICAgICBjb2xUb0hlYWRlci5zZXQoY2VsbC5jb2x1bW4sIFN0cmluZyhjZWxsLnZhbHVlID8/IFwiXCIpKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyDrjbDsnbTthLAg7ZaJIOydveq4sCAo7Zek642UIOuLpOydjCDtlonrtoDthLApXG4gICAgZm9yIChjb25zdCByb3dEYXRhIG9mIGFsbFJvd3MpIHtcbiAgICAgIGlmIChyb3dEYXRhLnJvdyA8PSBoZWFkZXJSb3dOdW0pIGNvbnRpbnVlO1xuXG4gICAgICBjb25zdCByb3dWYWx1ZXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fTtcbiAgICAgIGZvciAoY29uc3QgY2VsbCBvZiByb3dEYXRhLmNlbGxzKSB7XG4gICAgICAgIGNvbnN0IGhlYWRlck5hbWUgPSBjb2xUb0hlYWRlci5nZXQoY2VsbC5jb2x1bW4pO1xuICAgICAgICBpZiAoaGVhZGVyTmFtZSkge1xuICAgICAgICAgIHJvd1ZhbHVlc1toZWFkZXJOYW1lXSA9IFN0cmluZyhjZWxsLnZhbHVlID8/IFwiXCIpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGtleSA9IHJvd1ZhbHVlcy5rZXk7XG4gICAgICBjb25zdCBzb3VyY2UgPSByb3dWYWx1ZXMuc291cmNlIGFzIFwiZW50aXR5XCIgfCBcInByb2plY3RcIjtcblxuICAgICAgaWYgKCFrZXkgfHwgIXNvdXJjZSkgY29udGludWU7XG5cbiAgICAgIGlmIChzb3VyY2UgPT09IFwiZW50aXR5XCIpIHtcbiAgICAgICAgLy8gZW50aXR5IHNvdXJjZTogZGVmYXVsdCBsb2NhbGXrp4wgZW50aXR5Lmpzb27sl5Ag7KCA7J6lXG4gICAgICAgIGNvbnN0IGRlZmF1bHRWYWx1ZSA9IHJvd1ZhbHVlc1tkZWZhdWx0TG9jYWxlXTtcbiAgICAgICAgaWYgKGRlZmF1bHRWYWx1ZSkge1xuICAgICAgICAgIGNvbnN0IHVwZGF0ZWQgPSBhd2FpdCB0aGlzLnVwZGF0ZUVudGl0eUJ5S2V5KGtleSwgZGVmYXVsdFZhbHVlKTtcbiAgICAgICAgICBpZiAodXBkYXRlZCkge1xuICAgICAgICAgICAgdXBkYXRlZEVudGl0aWVzKys7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gbm9uLWRlZmF1bHQgbG9jYWxl7J2AIHByb2plY3QgZGljdOyXkCDsoIDsnqVcbiAgICAgICAgZm9yIChjb25zdCBsb2NhbGUgb2YgbG9jYWxlcykge1xuICAgICAgICAgIGlmIChsb2NhbGUgPT09IGRlZmF1bHRMb2NhbGUpIGNvbnRpbnVlO1xuICAgICAgICAgIGNvbnN0IGNlbGxWYWx1ZSA9IHJvd1ZhbHVlc1tsb2NhbGVdPy50cmltKCk7XG4gICAgICAgICAgaWYgKGNlbGxWYWx1ZSkge1xuICAgICAgICAgICAgcHJvamVjdERpY3RFbnRyaWVzW2xvY2FsZV0ucHVzaCh7XG4gICAgICAgICAgICAgIGtleSxcbiAgICAgICAgICAgICAgdmFsdWU6IGNlbGxWYWx1ZSxcbiAgICAgICAgICAgICAgaXNGdW5jdGlvbjogdGhpcy5pc0V4cHJlc3Npb25GdW5jdGlvbihjZWxsVmFsdWUpLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKHNvdXJjZSA9PT0gXCJwcm9qZWN0XCIpIHtcbiAgICAgICAgLy8gcHJvamVjdCBzb3VyY2U6IOuqqOuToCBsb2NhbGXsnYQgcHJvamVjdCBkaWN07JeQIOyggOyepVxuICAgICAgICBmb3IgKGNvbnN0IGxvY2FsZSBvZiBsb2NhbGVzKSB7XG4gICAgICAgICAgY29uc3QgY2VsbFZhbHVlID0gcm93VmFsdWVzW2xvY2FsZV0/LnRyaW0oKTtcbiAgICAgICAgICBpZiAoY2VsbFZhbHVlKSB7XG4gICAgICAgICAgICBwcm9qZWN0RGljdEVudHJpZXNbbG9jYWxlXS5wdXNoKHtcbiAgICAgICAgICAgICAga2V5LFxuICAgICAgICAgICAgICB2YWx1ZTogY2VsbFZhbHVlLFxuICAgICAgICAgICAgICBpc0Z1bmN0aW9uOiB0aGlzLmlzRXhwcmVzc2lvbkZ1bmN0aW9uKGNlbGxWYWx1ZSksXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBQcm9qZWN0IGRpY3Qg7YyM7J28IOyDneyEsVxuICAgIGZvciAoY29uc3QgbG9jYWxlIG9mIGxvY2FsZXMpIHtcbiAgICAgIGNvbnN0IGVudHJpZXMgPSBwcm9qZWN0RGljdEVudHJpZXNbbG9jYWxlXTtcbiAgICAgIGlmIChlbnRyaWVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgYXdhaXQgdGhpcy5zYXZlRGljdEZpbGUobG9jYWxlLCBlbnRyaWVzLCBsb2NhbGUgPT09IGRlZmF1bHRMb2NhbGUpO1xuICAgICAgICB1cGRhdGVkTG9jYWxlcysrO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgdXBkYXRlZEVudGl0aWVzLFxuICAgICAgdXBkYXRlZExvY2FsZXMsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiDrlJXshZTrhIjrpqwg7ZWt66qpIOyImOyglVxuICAgKi9cbiAgYXN5bmMgdXBkYXRlRW50cnkocGFyYW1zOiB7XG4gICAgb2xkS2V5OiBzdHJpbmc7XG4gICAgbmV3S2V5OiBzdHJpbmc7XG4gICAgc291cmNlOiBcImVudGl0eVwiIHwgXCJwcm9qZWN0XCIgfCBcInNvbmFtdVwiO1xuICAgIHZhbHVlczogUmVjb3JkPHN0cmluZywgc3RyaW5nPjtcbiAgfSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHsgb2xkS2V5LCBuZXdLZXksIHNvdXJjZSwgdmFsdWVzIH0gPSBwYXJhbXM7XG5cbiAgICBjb25zdCB7IGRlZmF1bHRMb2NhbGUsIHN1cHBvcnRlZExvY2FsZXMgfSA9IHRoaXMuZ2V0STE4bkNvbmZpZygpO1xuICAgIGNvbnN0IGxvY2FsZXMgPSBzdXBwb3J0ZWRMb2NhbGVzO1xuXG4gICAgLy8gZW50aXR5IHNvdXJjZeydmCBkZWZhdWx0IGxvY2FsZSDsspjrpqxcbiAgICBpZiAoc291cmNlID09PSBcImVudGl0eVwiICYmIHZhbHVlc1tkZWZhdWx0TG9jYWxlXSkge1xuICAgICAgYXdhaXQgdGhpcy51cGRhdGVFbnRpdHlCeUtleShuZXdLZXksIHZhbHVlc1tkZWZhdWx0TG9jYWxlXSk7XG4gICAgfVxuXG4gICAgLy8gcHJvamVjdCBkaWN0IOyXheuNsOydtO2KuFxuICAgIC8vIC0gZW50aXR57J2YIG5vbi1kZWZhdWx0IGxvY2FsZVxuICAgIC8vIC0gcHJvamVjdCBzb3VyY2XsnZgg66qo65OgIGxvY2FsZVxuICAgIC8vIC0gc29uYW11IHNvdXJjZeydmCDrqqjrk6AgbG9jYWxlIChvdmVycmlkZSlcbiAgICBmb3IgKGNvbnN0IGxvY2FsZSBvZiBsb2NhbGVzKSB7XG4gICAgICAvLyBlbnRpdHkgc291cmNl7J2YIGRlZmF1bHQgbG9jYWxl7J2AIGVudGl0eS5qc29u7JeQ7IScIOyymOumrO2WiOycvOuvgOuhnCDsiqTtgrVcbiAgICAgIGlmIChzb3VyY2UgPT09IFwiZW50aXR5XCIgJiYgbG9jYWxlID09PSBkZWZhdWx0TG9jYWxlKSBjb250aW51ZTtcblxuICAgICAgY29uc3QgY2VsbFZhbHVlID0gdmFsdWVzW2xvY2FsZV0/LnRyaW0oKTtcbiAgICAgIGlmICghY2VsbFZhbHVlKSBjb250aW51ZTtcblxuICAgICAgLy8g6riw7KG0IGRpY3Qg66Gc65OcXG4gICAgICBjb25zdCB7IGVudHJpZXMgfSA9IHRoaXMubG9hZFByb2plY3REaWN0KGxvY2FsZSk7XG5cbiAgICAgIC8vIGtleSDrs4Dqsr0g7IucIOq4sOyhtCBrZXkg7KCc6rGwXG4gICAgICBpZiAob2xkS2V5ICE9PSBuZXdLZXkpIHtcbiAgICAgICAgY29uc3Qgb2xkSW5kZXggPSBlbnRyaWVzLmZpbmRJbmRleCgoZSkgPT4gZS5rZXkgPT09IG9sZEtleSk7XG4gICAgICAgIGlmIChvbGRJbmRleCAhPT0gLTEpIHtcbiAgICAgICAgICBlbnRyaWVzLnNwbGljZShvbGRJbmRleCwgMSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8g7IOIIOqwkiDsl4XrjbDsnbTtirgg65iQ64qUIOy2lOqwgFxuICAgICAgY29uc3QgZXhpc3RpbmdJbmRleCA9IGVudHJpZXMuZmluZEluZGV4KChlKSA9PiBlLmtleSA9PT0gbmV3S2V5KTtcbiAgICAgIGNvbnN0IG5ld0VudHJ5OiBEaWN0RW50cnkgPSB7XG4gICAgICAgIGtleTogbmV3S2V5LFxuICAgICAgICB2YWx1ZTogY2VsbFZhbHVlLFxuICAgICAgICBpc0Z1bmN0aW9uOiB0aGlzLmlzRXhwcmVzc2lvbkZ1bmN0aW9uKGNlbGxWYWx1ZSksXG4gICAgICB9O1xuXG4gICAgICBpZiAoZXhpc3RpbmdJbmRleCAhPT0gLTEpIHtcbiAgICAgICAgZW50cmllc1tleGlzdGluZ0luZGV4XSA9IG5ld0VudHJ5O1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZW50cmllcy5wdXNoKG5ld0VudHJ5KTtcbiAgICAgIH1cblxuICAgICAgLy8gZGljdCDtjIzsnbwg7KCA7J6lXG4gICAgICBhd2FpdCB0aGlzLnNhdmVEaWN0RmlsZShsb2NhbGUsIGVudHJpZXMsIGxvY2FsZSA9PT0gZGVmYXVsdExvY2FsZSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIOuUleyFlOuEiOumrCDtla3rqqkg7LaU6rCAXG4gICAqL1xuICBhc3luYyBjcmVhdGVFbnRyeShwYXJhbXM6IHsga2V5OiBzdHJpbmc7IHZhbHVlczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiB9KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgeyBrZXksIHZhbHVlcyB9ID0gcGFyYW1zO1xuXG4gICAgaWYgKCFrZXk/LnRyaW0oKSkge1xuICAgICAgdGhyb3cgbmV3IEJhZFJlcXVlc3RFeGNlcHRpb24oU0QoXCJzb25hbXUuZXJyb3Iua2V5UmVxdWlyZWRcIikpO1xuICAgIH1cblxuICAgIGNvbnN0IHsgZGVmYXVsdExvY2FsZSwgc3VwcG9ydGVkTG9jYWxlcyB9ID0gdGhpcy5nZXRJMThuQ29uZmlnKCk7XG4gICAgY29uc3QgbG9jYWxlcyA9IHN1cHBvcnRlZExvY2FsZXM7XG5cbiAgICAvLyDspJHrs7Ug7YKkIOyytO2BrFxuICAgIGZvciAoY29uc3QgbG9jYWxlIG9mIGxvY2FsZXMpIHtcbiAgICAgIGNvbnN0IHsgZW50cmllcyB9ID0gdGhpcy5sb2FkUHJvamVjdERpY3QobG9jYWxlKTtcbiAgICAgIGlmIChlbnRyaWVzLnNvbWUoKGUpID0+IGUua2V5ID09PSBrZXkpKSB7XG4gICAgICAgIHRocm93IG5ldyBCYWRSZXF1ZXN0RXhjZXB0aW9uKFNEKFwic29uYW11LmVycm9yLmtleUFscmVhZHlFeGlzdHNcIikoa2V5KSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8g6rCBIGxvY2FsZeyXkCDsg4gg7YKkIOy2lOqwgFxuICAgIGZvciAoY29uc3QgbG9jYWxlIG9mIGxvY2FsZXMpIHtcbiAgICAgIGNvbnN0IGNlbGxWYWx1ZSA9IHZhbHVlc1tsb2NhbGVdPy50cmltKCk7XG4gICAgICBpZiAoIWNlbGxWYWx1ZSkgY29udGludWU7XG5cbiAgICAgIGNvbnN0IHsgZW50cmllcyB9ID0gdGhpcy5sb2FkUHJvamVjdERpY3QobG9jYWxlKTtcbiAgICAgIGVudHJpZXMucHVzaCh7XG4gICAgICAgIGtleSxcbiAgICAgICAgdmFsdWU6IGNlbGxWYWx1ZSxcbiAgICAgICAgaXNGdW5jdGlvbjogdGhpcy5pc0V4cHJlc3Npb25GdW5jdGlvbihjZWxsVmFsdWUpLFxuICAgICAgfSk7XG5cbiAgICAgIGF3YWl0IHRoaXMuc2F2ZURpY3RGaWxlKGxvY2FsZSwgZW50cmllcywgbG9jYWxlID09PSBkZWZhdWx0TG9jYWxlKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICog65SV7IWU64SI66asIO2VreuqqSDsgq3soJxcbiAgICovXG4gIGFzeW5jIGRlbGV0ZUVudHJ5KGtleTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKCFrZXkpIHtcbiAgICAgIHRocm93IG5ldyBCYWRSZXF1ZXN0RXhjZXB0aW9uKFNEKFwic29uYW11LmVycm9yLmtleVJlcXVpcmVkXCIpKTtcbiAgICB9XG5cbiAgICBjb25zdCB7IGRlZmF1bHRMb2NhbGUsIHN1cHBvcnRlZExvY2FsZXMgfSA9IHRoaXMuZ2V0STE4bkNvbmZpZygpO1xuICAgIGNvbnN0IGxvY2FsZXMgPSBzdXBwb3J0ZWRMb2NhbGVzO1xuXG4gICAgbGV0IGRlbGV0ZWQgPSBmYWxzZTtcbiAgICBmb3IgKGNvbnN0IGxvY2FsZSBvZiBsb2NhbGVzKSB7XG4gICAgICBjb25zdCB7IGVudHJpZXMgfSA9IHRoaXMubG9hZFByb2plY3REaWN0KGxvY2FsZSk7XG4gICAgICBjb25zdCBpbmRleCA9IGVudHJpZXMuZmluZEluZGV4KChlKSA9PiBlLmtleSA9PT0ga2V5KTtcbiAgICAgIGlmIChpbmRleCAhPT0gLTEpIHtcbiAgICAgICAgZW50cmllcy5zcGxpY2UoaW5kZXgsIDEpO1xuICAgICAgICBkZWxldGVkID0gdHJ1ZTtcblxuICAgICAgICBhd2FpdCB0aGlzLnNhdmVEaWN0RmlsZShsb2NhbGUsIGVudHJpZXMsIGxvY2FsZSA9PT0gZGVmYXVsdExvY2FsZSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKCFkZWxldGVkKSB7XG4gICAgICB0aHJvdyBuZXcgQmFkUmVxdWVzdEV4Y2VwdGlvbihTRChcInNvbmFtdS5lcnJvci5rZXlOb3RGb3VuZFwiKShrZXkpKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICog66+47IKs7JqpIO2CpCDqsoDsgqwgKGFzdC1ncmVwIOyCrOyaqSlcbiAgICovXG4gIGFzeW5jIGNoZWNrVXNhZ2Uoa2V5czogc3RyaW5nW10pOiBQcm9taXNlPFVzYWdlUmVzdWx0PiB7XG4gICAgLy8gYXN0LWdyZXAg7ISk7LmYIO2ZleyduFxuICAgIGxldCBzZ1BhdGg6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuICAgIHRyeSB7XG4gICAgICBzZ1BhdGggPSBleGVjU3luYyhcIndoaWNoIHNnXCIsIHsgZW5jb2Rpbmc6IFwidXRmLThcIiB9KS50cmltKCk7XG4gICAgfSBjYXRjaCB7XG4gICAgICB0cnkge1xuICAgICAgICBzZ1BhdGggPSBleGVjU3luYyhcIndoaWNoIGFzdC1ncmVwXCIsIHsgZW5jb2Rpbmc6IFwidXRmLThcIiB9KS50cmltKCk7XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLy8gYXN0LWdyZXAgbm90IGluc3RhbGxlZFxuICAgICAgfVxuICAgIH1cblxuICAgIGlmICghc2dQYXRoKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBlcnJvcjpcbiAgICAgICAgICBcImFzdC1ncmVw7J20IOyEpOy5mOuQmOyWtCDsnojsp4Ag7JWK7Iq164uI64ukLiBicmV3IGluc3RhbGwgYXN0LWdyZXAg65iQ64qUIG5wbSBpbnN0YWxsIC1nIEBhc3QtZ3JlcC9jbGnroZwg7ISk7LmY7ZW07KO87IS47JqULlwiLFxuICAgICAgICB1bnVzZWRLZXlzOiBbXSxcbiAgICAgIH07XG4gICAgfVxuXG4gICAgY29uc3Qgc2VhcmNoUGF0aHM6IHN0cmluZ1tdID0gW107XG4gICAgZm9yIChjb25zdCBlbnRyeSBvZiBbXCJhcGlcIiwgXCJ3ZWJcIiwgXCJhcHBcIl0pIHtcbiAgICAgIGNvbnN0IHNyY1BhdGggPSBwYXRoLmpvaW4oU29uYW11LmFwcFJvb3RQYXRoLCBlbnRyeSwgXCJzcmNcIik7XG4gICAgICBpZiAoZnMuZXhpc3RzU3luYyhzcmNQYXRoKSkge1xuICAgICAgICBzZWFyY2hQYXRocy5wdXNoKHNyY1BhdGgpO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChzZWFyY2hQYXRocy5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGVycm9yOiBcIuqygOyDie2VoCBzcmMg65SU66CJ7Yag66as66W8IOywvuydhCDsiJgg7JeG7Iq164uI64ukLlwiLFxuICAgICAgICB1bnVzZWRLZXlzOiBbXSxcbiAgICAgIH07XG4gICAgfVxuXG4gICAgY29uc3QgdXNlZEtleXMgPSBuZXcgU2V0PHN0cmluZz4oKTtcblxuICAgIHRyeSB7XG4gICAgICAvLyBhc3QtZ3JlcOycvOuhnCBTRChcIi4uLlwiKSDtjKjthLQg6rKA7IOJXG4gICAgICAvLyDtjKjthLQ6IFNEKFwiS0VZXCIpIOuYkOuKlCBTRCgnS0VZJykg7ZiV7YOcXG4gICAgICBjb25zdCBwYXR0ZXJucyA9IFsnU0QoXCIkS0VZXCIpJywgXCJTRCgnJEtFWScpXCJdO1xuXG4gICAgICBmb3IgKGNvbnN0IHNlYXJjaFBhdGggb2Ygc2VhcmNoUGF0aHMpIHtcbiAgICAgICAgZm9yIChjb25zdCBwYXR0ZXJuIG9mIHBhdHRlcm5zKSB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IGV4ZWNTeW5jKGAke3NnUGF0aH0gLS1wYXR0ZXJuICcke3BhdHRlcm59JyAtLWpzb24gJHtzZWFyY2hQYXRofWAsIHtcbiAgICAgICAgICAgICAgZW5jb2Rpbmc6IFwidXRmLThcIixcbiAgICAgICAgICAgICAgbWF4QnVmZmVyOiA1MCAqIDEwMjQgKiAxMDI0LCAvLyA1ME1CXG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgaWYgKHJlc3VsdC50cmltKCkpIHtcbiAgICAgICAgICAgICAgY29uc3QgbWF0Y2hlcyA9IEpTT04ucGFyc2UocmVzdWx0KTtcbiAgICAgICAgICAgICAgZm9yIChjb25zdCBtYXRjaCBvZiBtYXRjaGVzKSB7XG4gICAgICAgICAgICAgICAgLy8gbWV0YVZhcmlhYmxlcy5zaW5nbGUuS0VZLnRleHTsl5DshJwg7YKkIOy2lOy2nFxuICAgICAgICAgICAgICAgIGNvbnN0IGtleVRleHQgPSBtYXRjaC5tZXRhVmFyaWFibGVzPy5zaW5nbGU/LktFWT8udGV4dDtcbiAgICAgICAgICAgICAgICBpZiAoa2V5VGV4dCkge1xuICAgICAgICAgICAgICAgICAgLy8g65Sw7Ji07ZGcIOygnOqxsFxuICAgICAgICAgICAgICAgICAgY29uc3QgY2xlYW5LZXkgPSBrZXlUZXh0LnJlcGxhY2UoL15bXCInXXxbXCInXSQvZywgXCJcIik7XG4gICAgICAgICAgICAgICAgICB1c2VkS2V5cy5hZGQoY2xlYW5LZXkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgICAgLy8g7Yyo7YS0IOunpOy5mCDsl4bsnLzrqbQg7JeQ65+sICjrrLTsi5wpXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIGtleXMg7KSR7JeQ7IScIHVzZWRLZXlz7JeQIOyXhuuKlCDqsoPrk6TsnbQg66+47IKs7JqpIO2CpFxuICAgICAgY29uc3QgdW51c2VkS2V5cyA9IGtleXMuZmlsdGVyKChrKSA9PiAhdXNlZEtleXMuaGFzKGspKTtcblxuICAgICAgcmV0dXJuIHsgdW51c2VkS2V5cywgdXNlZEtleXNDb3VudDogdXNlZEtleXMuc2l6ZSB9O1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGVycm9yOiBg6rKA7IOJIOykkSDsmKTrpZgg67Cc7IOdOiAke2UgaW5zdGFuY2VvZiBFcnJvciA/IGUubWVzc2FnZSA6IFN0cmluZyhlKX1gLFxuICAgICAgICB1bnVzZWRLZXlzOiBbXSxcbiAgICAgIH07XG4gICAgfVxuICB9XG59XG5cbmV4cG9ydCBjb25zdCBzb25hbXVEaWN0aW9uYXJ5ID0gbmV3IFNvbmFtdURpY3Rpb25hcnkoKTtcbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7OztBQXlCQSxTQUFTLFVBQVUsT0FBdUI7Q0FDeEMsSUFBSSxTQUFTO0NBQ2IsSUFBSSxJQUFJO0FBQ1IsUUFBTyxLQUFLLEdBQUc7QUFDYixXQUFTLE9BQU8sYUFBYSxLQUFNLElBQUksR0FBSSxHQUFHO0FBQzlDLE1BQUksS0FBSyxNQUFNLElBQUksR0FBRyxHQUFHOztBQUUzQixRQUFPOzs7O2NBekI4QjtzQkFDa0I7cUJBQ1M7aUJBQ2xCO1VBQ3RCO0NBNEJiLG1CQUFiLE1BQThCOzs7Ozs7Ozs7O0VBVTVCLGNBQWMsVUFBK0I7R0FDM0MsTUFB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
|