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
package/dist/api/sonamu.js
CHANGED
|
@@ -1,1136 +1,1017 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { __esmMin } from "../_virtual/rolldown_runtime.js";
|
|
2
|
+
import { DB, init_db } from "../database/db.js";
|
|
3
|
+
import { BASE_FIELD_MAPPINGS, init_better_auth_entities } from "../auth/better-auth-entities.js";
|
|
4
|
+
import { CachePresets, applyCacheHeaders, init_cache_control } from "../cache-control/cache-control.js";
|
|
5
|
+
import { init_compress, toFastifyCompressOption } from "../compress/compress.js";
|
|
6
|
+
import { SD, init_sd, setSDConfig } from "../dict/sd.js";
|
|
7
|
+
import { NotFoundException, init_so_exceptions } from "../exceptions/so-exceptions.js";
|
|
8
|
+
import { BufferedFile, init_buffered_file } from "../storage/buffered-file.js";
|
|
9
|
+
import { UploadedFile, init_uploaded_file } from "../storage/uploaded-file.js";
|
|
10
|
+
import { createMockSSEFactory, init_sse } from "../stream/sse.js";
|
|
11
|
+
import { init_controller, isDaemonServer } from "../utils/controller.js";
|
|
12
|
+
import { exists, fileExists, init_fs_utils } from "../utils/fs-utils.js";
|
|
13
|
+
import { convertFastifyHeadersToStandard, init_utils, merge } from "../utils/utils.js";
|
|
14
|
+
import { getSecrets, init_secret } from "./secret.js";
|
|
3
15
|
import { AsyncLocalStorage } from "async_hooks";
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
16
|
+
import { dispose } from "@logtape/logtape";
|
|
17
|
+
import assert from "assert";
|
|
18
|
+
import fs from "fs/promises";
|
|
7
19
|
import path from "path";
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import { Naite } from "../naite/naite.js";
|
|
13
|
-
import { BufferedFile } from "../storage/buffered-file.js";
|
|
14
|
-
import { UploadedFile } from "../storage/uploaded-file.js";
|
|
15
|
-
import { exists, fileExists } from "../utils/fs-utils.js";
|
|
16
|
-
import { getSecrets } from "./secret.js";
|
|
17
|
-
class SonamuClass {
|
|
18
|
-
isInitialized = false;
|
|
19
|
-
forTesting = false;
|
|
20
|
-
asyncLocalStorage = new AsyncLocalStorage();
|
|
21
|
-
getContext() {
|
|
22
|
-
const store = this.asyncLocalStorage.getStore();
|
|
23
|
-
if (store?.context) {
|
|
24
|
-
return store.context;
|
|
25
|
-
}
|
|
26
|
-
if (process.env.NODE_ENV === "test") {
|
|
27
|
-
// 테스팅 환경에서 컨텍스트가 주입되지 않은 경우 빈 컨텍스트 리턴
|
|
28
|
-
return {
|
|
29
|
-
request: null,
|
|
30
|
-
reply: null,
|
|
31
|
-
headers: {},
|
|
32
|
-
createSSE: (schema)=>createMockSSEFactory(schema),
|
|
33
|
-
// biome-ignore lint/suspicious/noExplicitAny: 테스팅 환경에서 컨텍스트가 주입되지 않은 경우 빈 컨텍스트 리턴
|
|
34
|
-
naiteStore: new Map()
|
|
35
|
-
};
|
|
36
|
-
} else {
|
|
37
|
-
throw new Error("Sonamu cannot find context");
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
_apiRootPath = null;
|
|
41
|
-
set apiRootPath(apiRootPath) {
|
|
42
|
-
this._apiRootPath = apiRootPath;
|
|
43
|
-
}
|
|
44
|
-
get apiRootPath() {
|
|
45
|
-
if (this._apiRootPath === null) {
|
|
46
|
-
throw new Error("Sonamu has not been initialized");
|
|
47
|
-
}
|
|
48
|
-
return this._apiRootPath;
|
|
49
|
-
}
|
|
50
|
-
get appRootPath() {
|
|
51
|
-
return this.apiRootPath.split(path.sep).slice(0, -1).join(path.sep);
|
|
52
|
-
}
|
|
53
|
-
_dbConfig = null;
|
|
54
|
-
set dbConfig(dbConfig) {
|
|
55
|
-
this._dbConfig = dbConfig;
|
|
56
|
-
}
|
|
57
|
-
get dbConfig() {
|
|
58
|
-
if (this._dbConfig === null) {
|
|
59
|
-
throw new Error("Sonamu has not been initialized");
|
|
60
|
-
}
|
|
61
|
-
return this._dbConfig;
|
|
62
|
-
}
|
|
63
|
-
_syncer = null;
|
|
64
|
-
set syncer(syncer) {
|
|
65
|
-
this._syncer = syncer;
|
|
66
|
-
}
|
|
67
|
-
get syncer() {
|
|
68
|
-
if (this._syncer === null) {
|
|
69
|
-
throw new Error("Sonamu has not been initialized");
|
|
70
|
-
}
|
|
71
|
-
return this._syncer;
|
|
72
|
-
}
|
|
73
|
-
_config = null;
|
|
74
|
-
set config(config) {
|
|
75
|
-
this._config = config;
|
|
76
|
-
}
|
|
77
|
-
get config() {
|
|
78
|
-
if (this._config === null) {
|
|
79
|
-
throw new Error("Sonamu has not been initialized");
|
|
80
|
-
}
|
|
81
|
-
return this._config;
|
|
82
|
-
}
|
|
83
|
-
secrets = getSecrets();
|
|
84
|
-
_storage = null;
|
|
85
|
-
/**
|
|
86
|
-
* StorageManager 인스턴스
|
|
87
|
-
*/ get storage() {
|
|
88
|
-
if (!this._storage) {
|
|
89
|
-
throw new Error("Storage has not been initialized. Check storage config.");
|
|
90
|
-
}
|
|
91
|
-
return this._storage;
|
|
92
|
-
}
|
|
93
|
-
_cache = null;
|
|
94
|
-
/**
|
|
95
|
-
* CacheManager 인스턴스 (BentoCache)
|
|
96
|
-
*/ get cache() {
|
|
97
|
-
if (!this._cache) {
|
|
98
|
-
throw new Error("Cache has not been initialized. Check cache config in sonamu.config.ts.");
|
|
99
|
-
}
|
|
100
|
-
return this._cache;
|
|
101
|
-
}
|
|
102
|
-
_workflows = null;
|
|
103
|
-
get workflows() {
|
|
104
|
-
if (this._workflows === null) {
|
|
105
|
-
throw new Error("Sonamu has not been initialized");
|
|
106
|
-
}
|
|
107
|
-
return this._workflows;
|
|
108
|
-
}
|
|
109
|
-
_auth = null;
|
|
110
|
-
get auth() {
|
|
111
|
-
if (!this._auth) {
|
|
112
|
-
throw new Error("Auth has not been initialized. Check auth config in sonamu.config.ts.");
|
|
113
|
-
}
|
|
114
|
-
return this._auth;
|
|
115
|
-
}
|
|
116
|
-
_devVitestManager = null;
|
|
117
|
-
get devVitestManager() {
|
|
118
|
-
return this._devVitestManager;
|
|
119
|
-
}
|
|
120
|
-
set devVitestManager(manager) {
|
|
121
|
-
this._devVitestManager = manager;
|
|
122
|
-
}
|
|
123
|
-
// HMR 처리
|
|
124
|
-
watcher = null;
|
|
125
|
-
pendingFiles = [];
|
|
126
|
-
hmrStartTime = 0;
|
|
127
|
-
server = null;
|
|
128
|
-
async initForTesting() {
|
|
129
|
-
await this.init(true, false, undefined, true);
|
|
130
|
-
}
|
|
131
|
-
async init(doSilent = false, enableSync = true, apiRootPath, forTesting = false) {
|
|
132
|
-
this.forTesting = forTesting;
|
|
133
|
-
if (this.isInitialized) {
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
if (!doSilent) {
|
|
137
|
-
const chalk = (await import("chalk")).default;
|
|
138
|
-
console.time(chalk.cyan(`Sonamu.init${forTesting ? " for testing" : ""}`));
|
|
139
|
-
}
|
|
140
|
-
// API 루트 패스
|
|
141
|
-
const { findApiRootPath } = await import("../utils/utils.js");
|
|
142
|
-
this.apiRootPath = apiRootPath ?? findApiRootPath();
|
|
143
|
-
// 설정을 로딩하는 것부터 시작
|
|
144
|
-
const { loadConfig } = await import("./config.js");
|
|
145
|
-
this.config = await loadConfig(this.apiRootPath);
|
|
146
|
-
// sonamu.config.ts 기본값 설정
|
|
147
|
-
this.config.database.database = this.config.database.database ?? "pg";
|
|
148
|
-
this.config.database.defaultOptions.client = this.config.database.database ?? "pg";
|
|
149
|
-
// 로깅 설정
|
|
150
|
-
const { configureLogTape } = await import("../logger/configure.js");
|
|
151
|
-
if (this.config.logging !== false) {
|
|
152
|
-
await configureLogTape({
|
|
153
|
-
...this.config.logging
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
// DB 로드
|
|
157
|
-
const { DB } = await import("../database/db.js");
|
|
158
|
-
this.dbConfig = DB.generateDBConfig(this.config.database);
|
|
159
|
-
if (!doSilent) {
|
|
160
|
-
const chalk = (await import("chalk")).default;
|
|
161
|
-
console.log(chalk.green("DB Config Loaded!"));
|
|
162
|
-
}
|
|
163
|
-
// Entity 로드
|
|
164
|
-
// 테스트에서도 Entity 정보는 필요합니다.
|
|
165
|
-
// upsert가 제대로 작동하려면 entity의 unique index 정보가 필요하기 때문입니다.
|
|
166
|
-
const { EntityManager } = await import("../entity/entity-manager.js");
|
|
167
|
-
await EntityManager.autoload(doSilent);
|
|
168
|
-
// Cache 초기화
|
|
169
|
-
await this.initializeCache(this.config.server.cache, forTesting);
|
|
170
|
-
// BetterAuth 초기화
|
|
171
|
-
const authConfig = this.config.server.auth;
|
|
172
|
-
if (authConfig) {
|
|
173
|
-
// 사용자 설정과 기본값을 merge
|
|
174
|
-
const mergedFieldMappings = merge(BASE_FIELD_MAPPINGS, authConfig);
|
|
175
|
-
// better-auth 인스턴스 생성
|
|
176
|
-
const { betterAuth } = await import("better-auth");
|
|
177
|
-
const { sonamuKnexAdapter } = await import("../auth/knex-adapter.js");
|
|
178
|
-
this._auth = betterAuth({
|
|
179
|
-
database: sonamuKnexAdapter(),
|
|
180
|
-
...mergedFieldMappings
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
// 테스팅인 경우 싱크 없이 중단
|
|
184
|
-
if (forTesting) {
|
|
185
|
-
this.isInitialized = true;
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
// Task 등록
|
|
189
|
-
await this.initializeWorkflows(this.config.tasks);
|
|
190
|
-
// Syncer
|
|
191
|
-
const { Syncer } = await import("../syncer/syncer.js");
|
|
192
|
-
this.syncer = new Syncer();
|
|
193
|
-
// Autoload: Models / Types / APIs / Workflows / Templates / SSR Routes
|
|
194
|
-
await this.syncer.autoloadTypes();
|
|
195
|
-
await this.syncer.autoloadModels();
|
|
196
|
-
await this.syncer.autoloadApis();
|
|
197
|
-
await this.syncer.autoloadWorkflows();
|
|
198
|
-
const { TemplateManager } = await import("../template/index.js");
|
|
199
|
-
await TemplateManager.autoload();
|
|
200
|
-
await this.syncer.autoloadSSRRoutes();
|
|
201
|
-
const { isLocal, isTest } = await import("../utils/controller.js");
|
|
202
|
-
if (isLocal()) {
|
|
203
|
-
// 로컬에서는 코드 생성을 위해 Biome 셋업이 필요함 (현재 apiRootPath 전달하여 실행)
|
|
204
|
-
(await import("../utils/formatter.js")).setupBiome(this.apiRootPath);
|
|
205
|
-
}
|
|
206
|
-
const { isHotReloadServer } = await import("../utils/controller.js");
|
|
207
|
-
if (isLocal() && !isTest() && isHotReloadServer() && enableSync) {
|
|
208
|
-
await this.syncer.sync();
|
|
209
|
-
await this.startWatcher();
|
|
210
|
-
}
|
|
211
|
-
this.isInitialized = true;
|
|
212
|
-
if (!doSilent) {
|
|
213
|
-
const chalk = (await import("chalk")).default;
|
|
214
|
-
console.timeEnd(chalk.cyan("Sonamu.init"));
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
async createServer(initOptions) {
|
|
218
|
-
if (this.isInitialized === false) {
|
|
219
|
-
await this.init(initOptions?.doSilent, initOptions?.enableSync);
|
|
220
|
-
}
|
|
221
|
-
const options = this.config.server;
|
|
222
|
-
const { default: fastify } = await import("fastify");
|
|
223
|
-
const { getLogTapeFastifyLogger } = await import("@logtape/fastify");
|
|
224
|
-
const server = fastify({
|
|
225
|
-
...options.fastify,
|
|
226
|
-
logger: this.config.logging !== false ? getLogTapeFastifyLogger({
|
|
227
|
-
category: this.config.logging?.fastifyCategory ?? [
|
|
228
|
-
"fastify"
|
|
229
|
-
]
|
|
230
|
-
}) : undefined
|
|
231
|
-
});
|
|
232
|
-
this.server = server;
|
|
233
|
-
// Storage 설정 → StorageManager 생성
|
|
234
|
-
if (options.storage) {
|
|
235
|
-
const { StorageManager } = await import("../storage/storage-manager.js");
|
|
236
|
-
this._storage = new StorageManager(options.storage);
|
|
237
|
-
}
|
|
238
|
-
// 플러그인 등록
|
|
239
|
-
if (options.plugins) {
|
|
240
|
-
await this.registerPlugins(server, options.plugins);
|
|
241
|
-
}
|
|
242
|
-
if (options.auth) {
|
|
243
|
-
await this.registerBetterAuth(server, options.auth);
|
|
244
|
-
}
|
|
245
|
-
// API 라우팅 설정
|
|
246
|
-
await this.withFastify(server, options.apiConfig, {
|
|
247
|
-
enableSync: initOptions?.enableSync,
|
|
248
|
-
doSilent: initOptions?.doSilent
|
|
249
|
-
});
|
|
250
|
-
// 서버 시작
|
|
251
|
-
await this.boot(server, options);
|
|
252
|
-
return server;
|
|
253
|
-
}
|
|
254
|
-
async withFastify(server, config, options) {
|
|
255
|
-
if (this.isInitialized === false) {
|
|
256
|
-
await this.init(options?.doSilent, options?.enableSync);
|
|
257
|
-
}
|
|
258
|
-
this.server = server;
|
|
259
|
-
// timezone 설정
|
|
260
|
-
const timezone = this.config.api.timezone;
|
|
261
|
-
if (timezone) {
|
|
262
|
-
// 타임존에 맞게 응답 날짜 스트링을 변환해주어야 합니다.
|
|
263
|
-
// 가령 timezone이 "Asia/Seoul" 이면
|
|
264
|
-
// "2025-11-21T00:00:00.000Z" 를 "2025-11-21T09:00:00+09:00" 으로 변환해주어야 합니다.
|
|
265
|
-
const { formatInTimeZone } = await import("date-fns-tz");
|
|
266
|
-
// ISO 8601 날짜 형식 정규식 (예: 2024-01-15T09:30:00.000Z)
|
|
267
|
-
const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/;
|
|
268
|
-
// T를 둘러싼 작은따옴표가 없다면 "2025-11-19176354618900018:56:29+09:00"와 같은 결과가 나옵니다.
|
|
269
|
-
// 이는 date-fns 특입니다.
|
|
270
|
-
// 이렇게 해도 괜찮습니다. "2025-11-19T18:56:29+09:00" 모양으로 잘 나옵니다.
|
|
271
|
-
const DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ssXXX";
|
|
272
|
-
server.setReplySerializer((payload)=>{
|
|
273
|
-
return JSON.stringify(payload, (_key, value)=>{
|
|
274
|
-
if (typeof value === "string" && ISO_DATE_REGEX.test(value)) {
|
|
275
|
-
return formatInTimeZone(new Date(value), timezone, DATE_FORMAT);
|
|
276
|
-
}
|
|
277
|
-
return value;
|
|
278
|
-
});
|
|
279
|
-
});
|
|
280
|
-
if (!options?.doSilent) {
|
|
281
|
-
const chalk = (await import("chalk")).default;
|
|
282
|
-
console.log(chalk.green(`Timezone set to ${timezone}`));
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
// 전체 라우팅 리스트
|
|
286
|
-
server.get(`${this.config.api.route.prefix}/routes`, async (_request, _reply)=>{
|
|
287
|
-
return this.syncer.apis;
|
|
288
|
-
});
|
|
289
|
-
// Healthcheck API
|
|
290
|
-
server.get(`${this.config.api.route.prefix}/healthcheck`, async (_request, _reply)=>{
|
|
291
|
-
return "ok";
|
|
292
|
-
});
|
|
293
|
-
// Sonamu UI API (로컬 환경에서만)
|
|
294
|
-
const { isLocal } = await import("../utils/controller.js");
|
|
295
|
-
if (isLocal()) {
|
|
296
|
-
const { sonamuUIApiPlugin } = await import("../ui/api.js");
|
|
297
|
-
server.register(sonamuUIApiPlugin);
|
|
298
|
-
}
|
|
299
|
-
// DevRunner 테스트 엔드포인트 (로컬 환경 + devRunner 활성화 시)
|
|
300
|
-
if (isLocal() && this.config.test?.devRunner?.enabled) {
|
|
301
|
-
const { registerDevTestRoutes } = await import("../testing/dev-test-routes.js");
|
|
302
|
-
await registerDevTestRoutes(server, this.config.test.devRunner);
|
|
303
|
-
}
|
|
304
|
-
const webPath = path.join(this.appRootPath, "web");
|
|
305
|
-
const hasWeb = await exists(webPath);
|
|
306
|
-
// 전역 compress 옵션 계산 (route.compress: true일 때 사용)
|
|
307
|
-
const pluginCompress = this.config.server.plugins?.compress;
|
|
308
|
-
const globalCompressOptions = pluginCompress ? pluginCompress === true ? {
|
|
309
|
-
threshold: 1024,
|
|
310
|
-
encodings: [
|
|
311
|
-
"br",
|
|
312
|
-
"gzip",
|
|
313
|
-
"deflate"
|
|
314
|
-
]
|
|
315
|
-
} : {
|
|
316
|
-
threshold: pluginCompress.threshold,
|
|
317
|
-
encodings: pluginCompress.encodings,
|
|
318
|
-
customTypes: pluginCompress.customTypes
|
|
319
|
-
} : undefined;
|
|
320
|
-
if (isLocal()) {
|
|
321
|
-
// 로컬 개발 환경: catch-all로 API를 동적 매칭하여 HMR을 지원합니다.
|
|
322
|
-
// SONAMU_DISABLE_INTEGRATED_WEB=yes로 설정하면 dev_api 모드에서 Vite 통합을 비활성화할 수 있습니다.
|
|
323
|
-
const disableIntegratedWeb = process.env.SONAMU_DISABLE_INTEGRATED_WEB === "yes";
|
|
324
|
-
if (hasWeb && !disableIntegratedWeb) {
|
|
325
|
-
await this.setupDevServerWithVite(server, webPath, config);
|
|
326
|
-
} else {
|
|
327
|
-
this.setupDevServer(server, config);
|
|
328
|
-
}
|
|
329
|
-
} else {
|
|
330
|
-
// 프로덕션 환경: 개별 API 라우트 + 정적 파일 서빙
|
|
331
|
-
for (const api of this.syncer.apis){
|
|
332
|
-
if (this.syncer.models[api.modelName] === undefined) {
|
|
333
|
-
throw new Error(`정의되지 않은 모델에 접근 ${api.modelName}`);
|
|
334
|
-
}
|
|
335
|
-
server.route({
|
|
336
|
-
method: api.options.httpMethod ?? "GET",
|
|
337
|
-
url: this.config.api.route.prefix + api.path,
|
|
338
|
-
handler: this.createApiHandler(api, config),
|
|
339
|
-
compress: toFastifyCompressOption(api.options.compress, globalCompressOptions)
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
// 프로덕션에서는 web 소스(appRoot/web) 유무와 무관하게,
|
|
343
|
-
// api/web-dist 존재 여부를 setupStaticWebServer 내부에서 판단합니다.
|
|
344
|
-
await this.setupStaticWebServer(server, config, globalCompressOptions);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
/**
|
|
348
|
-
* dev 모드 공통: catch-all에서 syncer.apis를 동적으로 탐색하여 API 요청을 처리합니다.
|
|
349
|
-
* server.route()로 개별 등록하면 handler가 고정되어 HMR이 동작하지 않으므로,
|
|
350
|
-
* 매 요청마다 syncer.apis를 조회하는 이 방식을 사용합니다.
|
|
351
|
-
*
|
|
352
|
-
* 요청이 /api(정확히는 this.config.api.route.prefix)로 시작하지 않는 경우라면 null을 반환하며 끝냅니다.
|
|
353
|
-
*/ handleDevApiRequest(request, config) {
|
|
354
|
-
const url = this.getPathnameFromUrl(request.url);
|
|
355
|
-
const method = request.method;
|
|
356
|
-
if (!url.startsWith(this.config.api.route.prefix)) {
|
|
357
|
-
return null;
|
|
358
|
-
}
|
|
359
|
-
// syncer.apis의 path는 :param 형태를 포함할 수 있으므로 세그먼트 단위로 매칭합니다.
|
|
360
|
-
// 정규식 생성 방식은 path 문자열 내 특수문자(., +, (, [ 등)로 오작동할 수 있어 사용하지 않습니다.
|
|
361
|
-
const matchedApi = this.syncer.apis.find((api)=>{
|
|
362
|
-
if (this.syncer.models[api.modelName] === undefined) {
|
|
363
|
-
return false;
|
|
364
|
-
}
|
|
365
|
-
const apiMethod = api.options.httpMethod ?? "GET";
|
|
366
|
-
if (apiMethod !== method) return false;
|
|
367
|
-
const fullPath = this.config.api.route.prefix + api.path;
|
|
368
|
-
return this.isPathPatternMatch(fullPath, url);
|
|
369
|
-
});
|
|
370
|
-
if (!matchedApi) {
|
|
371
|
-
throw new NotFoundException(SD("error.api.notFound"));
|
|
372
|
-
}
|
|
373
|
-
return this.createApiHandler(matchedApi, config);
|
|
374
|
-
}
|
|
375
|
-
/**
|
|
376
|
-
* dev api 모드: Vite 없이 API 동적 라우팅만 제공합니다.
|
|
377
|
-
* HMR을 위해 catch-all에서 매 요청마다 syncer.apis를 조회합니다.
|
|
378
|
-
*/ setupDevServer(server, config) {
|
|
379
|
-
server.route({
|
|
380
|
-
method: [
|
|
381
|
-
"GET",
|
|
382
|
-
"HEAD",
|
|
383
|
-
"POST",
|
|
384
|
-
"PUT",
|
|
385
|
-
"DELETE",
|
|
386
|
-
"PATCH"
|
|
387
|
-
],
|
|
388
|
-
url: `${this.config.api.route.prefix}/*`,
|
|
389
|
-
handler: async (request, reply)=>{
|
|
390
|
-
const handler = this.handleDevApiRequest(request, config);
|
|
391
|
-
if (handler) {
|
|
392
|
-
return handler(request, reply);
|
|
393
|
-
}
|
|
394
|
-
// 등록된 API와 일치하지 않는 요청에 대한 fallback입니다.
|
|
395
|
-
throw new NotFoundException(SD("error.api.notFound"));
|
|
396
|
-
}
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
// biome-ignore lint/suspicious/noExplicitAny: ViteDevServer 타입을 동적으로 로드해야 함
|
|
400
|
-
viteServer = null;
|
|
401
|
-
/**
|
|
402
|
-
* dev all 모드: Vite Dev Server를 통합하여 API + SSR + CSR을 모두 제공합니다.
|
|
403
|
-
* API 동적 매칭은 handleDevApiRequest를 공유합니다.
|
|
404
|
-
*/ async setupDevServerWithVite(server, webPath, config) {
|
|
405
|
-
// @fastify/middie 등록 (Connect-style middleware 지원)
|
|
406
|
-
await server.register((await import("@fastify/middie")).default);
|
|
407
|
-
const vite = await import("vite");
|
|
408
|
-
this.viteServer = await vite.createServer({
|
|
409
|
-
root: webPath,
|
|
410
|
-
server: {
|
|
411
|
-
middlewareMode: true,
|
|
412
|
-
hmr: {
|
|
413
|
-
server: server.server
|
|
414
|
-
}
|
|
415
|
-
},
|
|
416
|
-
appType: "custom"
|
|
417
|
-
});
|
|
418
|
-
// Vite middleware 등록 (Vite 에셋 처리)
|
|
419
|
-
server.use((req, res, next)=>{
|
|
420
|
-
// API와 Sonamu UI는 Fastify 라우트가 처리하도록 skip
|
|
421
|
-
if (req.url?.startsWith(this.config.api.route.prefix) || req.url?.startsWith("/sonamu-ui")) {
|
|
422
|
-
return next();
|
|
423
|
-
}
|
|
424
|
-
// 나머지는 Vite middleware로 전달
|
|
425
|
-
return this.viteServer.middlewares(req, res, next);
|
|
426
|
-
});
|
|
427
|
-
// catch-all 라우트에서 동적으로 API/SSR 처리
|
|
428
|
-
// 개발 환경에서는 라우트별 compress 옵션을 포기하고 HMR 이점을 취합니다.
|
|
429
|
-
server.route({
|
|
430
|
-
method: [
|
|
431
|
-
"GET",
|
|
432
|
-
"HEAD",
|
|
433
|
-
"POST",
|
|
434
|
-
"PUT",
|
|
435
|
-
"DELETE",
|
|
436
|
-
"PATCH"
|
|
437
|
-
],
|
|
438
|
-
url: "/*",
|
|
439
|
-
handler: async (request, reply)=>{
|
|
440
|
-
// 1. API 요청 처리
|
|
441
|
-
const result = this.handleDevApiRequest(request, config);
|
|
442
|
-
if (result) {
|
|
443
|
-
return result(request, reply);
|
|
444
|
-
}
|
|
445
|
-
const url = request.url;
|
|
446
|
-
// 2. SSR 라우트 처리
|
|
447
|
-
const { matchSSRRoute, renderSSR } = await import("../ssr/index.js");
|
|
448
|
-
const ssrMatch = matchSSRRoute(url);
|
|
449
|
-
if (ssrMatch) {
|
|
450
|
-
console.log(`[SSR] Matched route: ${ssrMatch.route.path}`);
|
|
451
|
-
const html = await renderSSR(url, ssrMatch.route, ssrMatch.params, request, reply, config, this.viteServer);
|
|
452
|
-
reply.type("text/html");
|
|
453
|
-
return html;
|
|
454
|
-
}
|
|
455
|
-
// 3. CSR fallback
|
|
456
|
-
try {
|
|
457
|
-
const fs = await import("node:fs/promises");
|
|
458
|
-
let template = await fs.readFile(path.join(this.viteServer.config.root, "index.html"), "utf-8");
|
|
459
|
-
template = await this.viteServer.transformIndexHtml(url, template);
|
|
460
|
-
reply.type("text/html");
|
|
461
|
-
return template;
|
|
462
|
-
} catch (e) {
|
|
463
|
-
this.viteServer.ssrFixStacktrace(e);
|
|
464
|
-
console.error(e);
|
|
465
|
-
reply.status(500);
|
|
466
|
-
return e.message;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
});
|
|
470
|
-
// 서버 종료 시 Vite도 종료
|
|
471
|
-
server.addHook("onClose", async ()=>{
|
|
472
|
-
await this.viteServer.close();
|
|
473
|
-
});
|
|
474
|
-
console.log("✓ Vite dev server integrated");
|
|
475
|
-
}
|
|
476
|
-
async setupStaticWebServer(server, config, globalCompressOptions) {
|
|
477
|
-
// 경로 명확화: api/web-dist/client (정적 파일), api/web-dist/server (SSR entry), api/dist/ssr (SSR routes - API 소유)
|
|
478
|
-
const webDistPath = path.join(this.apiRootPath, "web-dist", "client");
|
|
479
|
-
const ssrPath = path.join(this.apiRootPath, "web-dist", "server");
|
|
480
|
-
const ssrEntryPath = path.join(ssrPath, "entry-server.generated.js");
|
|
481
|
-
const ssrRoutesPath = path.join(this.apiRootPath, "dist", "ssr", "routes.js");
|
|
482
|
-
if (!await exists(webDistPath)) {
|
|
483
|
-
console.warn(`⚠ Web dist not found: ${webDistPath}`);
|
|
484
|
-
return;
|
|
485
|
-
}
|
|
486
|
-
// SSR entry 존재 여부 확인
|
|
487
|
-
const ssrAvailable = await exists(ssrEntryPath);
|
|
488
|
-
if (!ssrAvailable) {
|
|
489
|
-
console.warn(`⚠ SSR entry not found: ${ssrEntryPath}`);
|
|
490
|
-
console.warn(" SSR will be disabled. Only CSR will work.");
|
|
491
|
-
}
|
|
492
|
-
// SSR 라우트 로드 (production에서만, 사용자 프로젝트의 ssr/routes.ts)
|
|
493
|
-
if (ssrAvailable) {
|
|
494
|
-
if (await exists(ssrRoutesPath)) {
|
|
495
|
-
// ts-loader라면 "file://"로 시작하는 fully-resolved path만 받기에 이를 처리해주는 importMembers를 사용해야 했겠지만,
|
|
496
|
-
// 여기는 프로덕션 환경에서 loader 없이 돌아가기 때문에 "진짜 js 파일"의 "그냥" 절대경로를 바로 import해도 됩니다.
|
|
497
|
-
// 이 내용은 이 함수 내에서 아래에 나올 다른 import 호출에도 동일하게 적용됩니다.
|
|
498
|
-
await import(ssrRoutesPath);
|
|
499
|
-
console.log("✓ SSR routes loaded");
|
|
500
|
-
} else {
|
|
501
|
-
console.warn(`⚠ SSR routes not found: ${ssrRoutesPath}`);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
// 롤링 업데이트 대응: asset hash 불일치 시 현재 버전 직접 서빙
|
|
505
|
-
server.get("/assets/:filename", async (request, reply)=>{
|
|
506
|
-
const requestedFile = request.params.filename;
|
|
507
|
-
const assetsDir = path.join(webDistPath, "assets");
|
|
508
|
-
const safeFilePath = this.resolvePathWithinBaseDir(assetsDir, requestedFile);
|
|
509
|
-
if (safeFilePath === null) {
|
|
510
|
-
reply.status(403).send();
|
|
511
|
-
return;
|
|
512
|
-
}
|
|
513
|
-
const normalizedRequestedFile = path.relative(assetsDir, safeFilePath).replace(/\\/g, "/");
|
|
514
|
-
const assetPath = `/assets/${normalizedRequestedFile}`;
|
|
515
|
-
// Cache-Control 헤더 결정
|
|
516
|
-
const getCacheControlForAsset = ()=>{
|
|
517
|
-
const cacheReq = {
|
|
518
|
-
type: "assets",
|
|
519
|
-
url: request.url,
|
|
520
|
-
path: assetPath,
|
|
521
|
-
method: request.method
|
|
522
|
-
};
|
|
523
|
-
// 사용자 정의 핸들러 우선
|
|
524
|
-
if (config.cacheControlHandler) {
|
|
525
|
-
const result = config.cacheControlHandler(cacheReq);
|
|
526
|
-
if (result) return result;
|
|
527
|
-
}
|
|
528
|
-
// 기본값: immutable
|
|
529
|
-
return CachePresets.immutable;
|
|
530
|
-
};
|
|
531
|
-
// index-*.js 또는 index-*.css 요청인 경우
|
|
532
|
-
if (/^index-[a-f0-9]+\.(js|css)$/.test(normalizedRequestedFile)) {
|
|
533
|
-
const ext = normalizedRequestedFile.split(".").pop();
|
|
534
|
-
const files = await fs.readdir(assetsDir);
|
|
535
|
-
const currentFile = files.find((f)=>f.startsWith("index-") && f.endsWith(`.${ext}`));
|
|
536
|
-
if (currentFile) {
|
|
537
|
-
const filePath = path.join(assetsDir, currentFile);
|
|
538
|
-
const content = await fs.readFile(filePath);
|
|
539
|
-
reply.type(ext === "js" ? "application/javascript" : "text/css");
|
|
540
|
-
applyCacheHeaders(reply, getCacheControlForAsset());
|
|
541
|
-
return reply.send(content);
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
// 일반 파일 서빙
|
|
545
|
-
const filePath = safeFilePath;
|
|
546
|
-
if (await exists(filePath)) {
|
|
547
|
-
const content = await fs.readFile(filePath);
|
|
548
|
-
const ext = normalizedRequestedFile.split(".").pop();
|
|
549
|
-
reply.type(ext === "js" ? "application/javascript" : ext === "css" ? "text/css" : "");
|
|
550
|
-
if (normalizedRequestedFile.includes("-")) {
|
|
551
|
-
applyCacheHeaders(reply, getCacheControlForAsset());
|
|
552
|
-
}
|
|
553
|
-
return reply.send(content);
|
|
554
|
-
}
|
|
555
|
-
reply.status(404).send();
|
|
556
|
-
});
|
|
557
|
-
// SSR 라우트 개별 등록 (compress 옵션이 라우트별로 적용되도록)
|
|
558
|
-
if (ssrAvailable) {
|
|
559
|
-
const { getSSRRoutes } = await import("../ssr/index.js");
|
|
560
|
-
const { renderSSR } = await import("../ssr/renderer.js");
|
|
561
|
-
const ssrRoutes = getSSRRoutes();
|
|
562
|
-
for (const route of ssrRoutes){
|
|
563
|
-
server.route({
|
|
564
|
-
method: [
|
|
565
|
-
"GET",
|
|
566
|
-
"HEAD"
|
|
567
|
-
],
|
|
568
|
-
url: route.path,
|
|
569
|
-
compress: toFastifyCompressOption(route.compress ?? true, globalCompressOptions),
|
|
570
|
-
handler: async (request, reply)=>{
|
|
571
|
-
const url = request.url;
|
|
572
|
-
console.log(`[SSR] Matched route: ${route.path}`);
|
|
573
|
-
const params = this.extractPathParams(route.path, url);
|
|
574
|
-
const html = await renderSSR(url, route, params, request, reply, config);
|
|
575
|
-
reply.type("text/html");
|
|
576
|
-
return html;
|
|
577
|
-
}
|
|
578
|
-
});
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
// CSR or Static File Fallback (SSR 라우트에 매칭되지 않는 모든 요청)
|
|
582
|
-
server.route({
|
|
583
|
-
method: [
|
|
584
|
-
"GET",
|
|
585
|
-
"HEAD"
|
|
586
|
-
],
|
|
587
|
-
url: "*",
|
|
588
|
-
handler: async (request, reply)=>{
|
|
589
|
-
// /api, /sonamu-ui는 404 그대로
|
|
590
|
-
if (request.url.startsWith("/api") || request.url.startsWith("/sonamu-ui")) {
|
|
591
|
-
reply.status(404).send();
|
|
592
|
-
return;
|
|
593
|
-
}
|
|
594
|
-
// CSR용 Cache-Control 헤더 설정
|
|
595
|
-
if (config.cacheControlHandler) {
|
|
596
|
-
const csrCacheReq = {
|
|
597
|
-
type: "csr",
|
|
598
|
-
url: request.url,
|
|
599
|
-
path: request.url.split("?")[0],
|
|
600
|
-
method: request.method
|
|
601
|
-
};
|
|
602
|
-
const csrCacheConfig = config.cacheControlHandler(csrCacheReq);
|
|
603
|
-
if (csrCacheConfig) {
|
|
604
|
-
applyCacheHeaders(reply, csrCacheConfig);
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
// 정적 파일이 존재할 경우, 정적 파일을 먼저 서빙해야함
|
|
608
|
-
const requestPath = this.getPathnameFromUrl(request.url);
|
|
609
|
-
const safeFilePath = this.resolvePathWithinBaseDir(webDistPath, requestPath);
|
|
610
|
-
if (safeFilePath === null) {
|
|
611
|
-
reply.status(403).send();
|
|
612
|
-
return;
|
|
613
|
-
}
|
|
614
|
-
if (await fileExists(safeFilePath)) {
|
|
615
|
-
const content = await fs.readFile(safeFilePath);
|
|
616
|
-
return reply.type(mimeLookup(safeFilePath) || "application/octet-stream").send(content);
|
|
617
|
-
}
|
|
618
|
-
// CSR fallback: index.html 서빙
|
|
619
|
-
const indexPath = path.join(webDistPath, "index.html");
|
|
620
|
-
return reply.type("text/html").send(await fs.readFile(indexPath, "utf-8"));
|
|
621
|
-
}
|
|
622
|
-
});
|
|
623
|
-
console.log(`✓ Static web server configured with ${ssrAvailable ? "SSR" : "CSR only"} support`);
|
|
624
|
-
}
|
|
625
|
-
createApiHandler(api, config) {
|
|
626
|
-
return async (request, reply)=>{
|
|
627
|
-
// Context 생성
|
|
628
|
-
const context = await this.createContext(config, request, reply);
|
|
629
|
-
return this.asyncLocalStorage.run({
|
|
630
|
-
context
|
|
631
|
-
}, async ()=>{
|
|
632
|
-
// guards 처리
|
|
633
|
-
(api.options.guards ?? []).every((guard)=>config.guardHandler(guard, request, api));
|
|
634
|
-
// 파라미터 정보로 zod 스키마 빌드
|
|
635
|
-
const { getZodObjectFromApi } = await import("./code-converters.js");
|
|
636
|
-
const ReqType = getZodObjectFromApi(api, this.syncer.types);
|
|
637
|
-
// request 파싱
|
|
638
|
-
const which = api.options.httpMethod === "GET" ? "query" : "body";
|
|
639
|
-
let reqBody;
|
|
640
|
-
// 파일 업로드 있는 경우 임시 데이터
|
|
641
|
-
const files = {
|
|
642
|
-
bufferedFiles: [],
|
|
643
|
-
uploadedFiles: []
|
|
644
|
-
};
|
|
645
|
-
try {
|
|
646
|
-
const body = request[which] ?? {};
|
|
647
|
-
if (api.uploadOptions) {
|
|
648
|
-
const parts = request.parts({
|
|
649
|
-
limits: api.uploadOptions.limits
|
|
650
|
-
});
|
|
651
|
-
// FormData의 field들을 임시로 저장
|
|
652
|
-
const fields = {};
|
|
653
|
-
if (api.uploadOptions.consume === "buffer" || !api.uploadOptions.consume) {
|
|
654
|
-
// Buffer 모드: 메모리에 로드
|
|
655
|
-
for await (const part of parts){
|
|
656
|
-
if (part.type === "file") {
|
|
657
|
-
// CRITICAL: 파일 스트림을 즉시 consume해야 다음 part로 넘어갈 수 있음
|
|
658
|
-
// 이 호출이 없으면 종종 multipart 파싱이 pending 상태로 타임아웃 발생
|
|
659
|
-
const buffer = await part.toBuffer();
|
|
660
|
-
files.bufferedFiles.push(new BufferedFile(part, buffer));
|
|
661
|
-
} else if (part.type === "field") {
|
|
662
|
-
fields[part.fieldname] = String(part.value);
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
} else if (api.uploadOptions.consume === "stream") {
|
|
666
|
-
// Stream 모드: 즉시 저장소로 스트리밍
|
|
667
|
-
const diskName = api.uploadOptions.destination;
|
|
668
|
-
const disk = this.storage.use(diskName);
|
|
669
|
-
// 우선순위: 데코레이터 > 전역 설정 > 기본값
|
|
670
|
-
const keyGenerator = api.uploadOptions.keyGenerator ?? this.config.server.storage?.keyGenerator ?? defaultKeyGenerator;
|
|
671
|
-
for await (const part of parts){
|
|
672
|
-
if (part.type === "file") {
|
|
673
|
-
const key = await keyGenerator({
|
|
674
|
-
filename: part.filename,
|
|
675
|
-
mimetype: part.mimetype
|
|
676
|
-
});
|
|
677
|
-
await disk.putStream(key, part.file, {
|
|
678
|
-
contentType: part.mimetype
|
|
679
|
-
});
|
|
680
|
-
const url = await disk.getUrl(key);
|
|
681
|
-
const signedUrl = await disk.getSignedUrl(key);
|
|
682
|
-
files.uploadedFiles.push(new UploadedFile({
|
|
683
|
-
filename: part.filename,
|
|
684
|
-
mimetype: part.mimetype,
|
|
685
|
-
size: part.file.bytesRead,
|
|
686
|
-
url,
|
|
687
|
-
signedUrl,
|
|
688
|
-
key,
|
|
689
|
-
diskName
|
|
690
|
-
}));
|
|
691
|
-
} else if (part.type === "field") {
|
|
692
|
-
fields[part.fieldname] = String(part.value);
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
// qs로 중첩 구조 파싱: params[category] → { params: { category: "test" } }
|
|
697
|
-
const qs = await import("qs");
|
|
698
|
-
const parsed = qs.default.parse(fields);
|
|
699
|
-
Object.assign(body, parsed);
|
|
700
|
-
}
|
|
701
|
-
const { fastifyCaster } = await import("./caster.js");
|
|
702
|
-
reqBody = fastifyCaster(ReqType).parse(body);
|
|
703
|
-
} catch (e) {
|
|
704
|
-
const { ZodError } = await import("zod");
|
|
705
|
-
if (e instanceof ZodError) {
|
|
706
|
-
const { humanizeZodError } = await import("../utils/zod-error.js");
|
|
707
|
-
const messages = humanizeZodError(e).map((issue)=>issue.message).join(" ");
|
|
708
|
-
const { BadRequestException } = await import("../exceptions/so-exceptions.js");
|
|
709
|
-
throw new BadRequestException(messages, {
|
|
710
|
-
zodError: e
|
|
711
|
-
});
|
|
712
|
-
} else {
|
|
713
|
-
throw e;
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
// Content-Type
|
|
717
|
-
reply.type(api.options.contentType ?? "application/json");
|
|
718
|
-
// Cache-Control 헤더 설정
|
|
719
|
-
const apiCacheConfig = this.getApiCacheControl(api, request, config);
|
|
720
|
-
if (apiCacheConfig) {
|
|
721
|
-
applyCacheHeaders(reply, apiCacheConfig);
|
|
722
|
-
}
|
|
723
|
-
// 업로드 옵션이 있는 경우 파일 데이터를 Context에 추가
|
|
724
|
-
if (api.uploadOptions) {
|
|
725
|
-
const consume = api.uploadOptions.consume ?? "buffer";
|
|
726
|
-
if (consume === "buffer") {
|
|
727
|
-
context.bufferedFiles = files.bufferedFiles;
|
|
728
|
-
} else if (consume === "stream") {
|
|
729
|
-
context.uploadedFiles = files.uploadedFiles;
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
// 모델 메소드 args 생성하여 호출
|
|
733
|
-
const { ApiParamType } = await import("../types/types.js");
|
|
734
|
-
const args = api.parameters.map((param)=>{
|
|
735
|
-
// Context 인젝션
|
|
736
|
-
if (ApiParamType.isContext(param.type)) {
|
|
737
|
-
return context;
|
|
738
|
-
} else {
|
|
739
|
-
return reqBody[param.name];
|
|
740
|
-
}
|
|
741
|
-
});
|
|
742
|
-
return this.invokeModelMethod(api, args, reply);
|
|
743
|
-
});
|
|
744
|
-
};
|
|
745
|
-
}
|
|
746
|
-
/**
|
|
747
|
-
* URL에서 path params를 추출합니다.
|
|
748
|
-
* 예: pattern="/admin/companies/:companyId", url="/admin/companies/123" → { companyId: "123" }
|
|
749
|
-
*/ extractPathParams(pattern, url) {
|
|
750
|
-
const patternParts = pattern.split("/").filter(Boolean);
|
|
751
|
-
const urlParts = this.getPathnameFromUrl(url).split("/").filter(Boolean);
|
|
752
|
-
const params = {};
|
|
753
|
-
for(let i = 0; i < patternParts.length; i++){
|
|
754
|
-
if (patternParts[i].startsWith(":")) {
|
|
755
|
-
params[patternParts[i].slice(1)] = urlParts[i];
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
return params;
|
|
759
|
-
}
|
|
760
|
-
isPathPatternMatch(pattern, url) {
|
|
761
|
-
const patternParts = pattern.split("/").filter(Boolean);
|
|
762
|
-
const urlParts = this.getPathnameFromUrl(url).split("/").filter(Boolean);
|
|
763
|
-
if (patternParts.length !== urlParts.length) {
|
|
764
|
-
return false;
|
|
765
|
-
}
|
|
766
|
-
for(let i = 0; i < patternParts.length; i++){
|
|
767
|
-
const patternPart = patternParts[i];
|
|
768
|
-
const urlPart = urlParts[i];
|
|
769
|
-
if (patternPart.startsWith(":")) {
|
|
770
|
-
continue;
|
|
771
|
-
}
|
|
772
|
-
if (patternPart !== urlPart) {
|
|
773
|
-
return false;
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
return true;
|
|
777
|
-
}
|
|
778
|
-
getPathnameFromUrl(url) {
|
|
779
|
-
return url.split("?")[0];
|
|
780
|
-
}
|
|
781
|
-
resolvePathWithinBaseDir(baseDir, inputPath) {
|
|
782
|
-
try {
|
|
783
|
-
const decoded = decodeURIComponent(inputPath).replace(/\\/g, "/");
|
|
784
|
-
if (decoded.includes("\0")) {
|
|
785
|
-
return null;
|
|
786
|
-
}
|
|
787
|
-
const relativePath = decoded.replace(/^\/+/, "");
|
|
788
|
-
const resolvedPath = path.resolve(baseDir, relativePath);
|
|
789
|
-
const relativeFromBase = path.relative(baseDir, resolvedPath);
|
|
790
|
-
if (relativeFromBase.startsWith("..") || path.isAbsolute(relativeFromBase)) {
|
|
791
|
-
return null;
|
|
792
|
-
}
|
|
793
|
-
return resolvedPath;
|
|
794
|
-
} catch {
|
|
795
|
-
return null;
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
/**
|
|
799
|
-
* API 응답에 적용할 Cache-Control 설정을 결정합니다.
|
|
800
|
-
* 우선순위: 개별 지정 > cacheControlHandler
|
|
801
|
-
*/ getApiCacheControl(api, request, config) {
|
|
802
|
-
// 데코레이터 설정 우선
|
|
803
|
-
if (api.options.cacheControl) {
|
|
804
|
-
return api.options.cacheControl;
|
|
805
|
-
}
|
|
806
|
-
// 전역 핸들러
|
|
807
|
-
if (config.cacheControlHandler) {
|
|
808
|
-
const cacheReq = {
|
|
809
|
-
type: "api",
|
|
810
|
-
url: request.url,
|
|
811
|
-
path: request.routeOptions?.url ?? request.url.split("?")[0],
|
|
812
|
-
method: request.method,
|
|
813
|
-
api
|
|
814
|
-
};
|
|
815
|
-
const result = config.cacheControlHandler(cacheReq);
|
|
816
|
-
if (result) return result;
|
|
817
|
-
}
|
|
818
|
-
return null;
|
|
819
|
-
}
|
|
820
|
-
/**
|
|
821
|
-
* SSR용 API 호출 (HTTP 오버헤드 없이 직접 호출)
|
|
822
|
-
* createApiHandler의 로직을 재사용하되, request 파싱 대신 params 직접 사용
|
|
823
|
-
*/ async invokeApiForSSR(api, // biome-ignore lint/suspicious/noExplicitAny: SSR에서 다양한 타입의 params를 받아야 함
|
|
824
|
-
params, config, request, reply) {
|
|
825
|
-
// Context 생성 (기존 메소드 재사용)
|
|
826
|
-
const context = await this.createContext(config, request, reply);
|
|
827
|
-
return this.asyncLocalStorage.run({
|
|
828
|
-
context
|
|
829
|
-
}, async ()=>{
|
|
830
|
-
// args 생성: Context 파라미터는 주입, 나머지는 params에서 가져오기
|
|
831
|
-
const { ApiParamType } = await import("../types/types.js");
|
|
832
|
-
let paramsIndex = 0;
|
|
833
|
-
const args = api.parameters.map((param)=>{
|
|
834
|
-
if (ApiParamType.isContext(param.type)) {
|
|
835
|
-
return context;
|
|
836
|
-
}
|
|
837
|
-
return params[paramsIndex++];
|
|
838
|
-
});
|
|
839
|
-
// 모델 메서드 호출 (기존 메서드 재사용)
|
|
840
|
-
return this.invokeModelMethod(api, args, reply);
|
|
841
|
-
});
|
|
842
|
-
}
|
|
843
|
-
async invokeModelMethod(api, args, reply) {
|
|
844
|
-
const model = this.syncer.models[api.modelName];
|
|
845
|
-
// biome-ignore lint/suspicious/noExplicitAny: model은 모델 인스턴스이므로 메서드 호출 가능
|
|
846
|
-
const result = await model[api.methodName].apply(model, args);
|
|
847
|
-
reply.type(api.options.contentType ?? "application/json");
|
|
848
|
-
return result;
|
|
849
|
-
}
|
|
850
|
-
async createContext(config, request, reply) {
|
|
851
|
-
// createSSEFactory 함수에 미리 request의 socket과 reply를 바인딩.
|
|
852
|
-
const { createSSEFactory } = await import("../stream/sse.js");
|
|
853
|
-
const createSSE = ((_request, _reply, _events)=>createSSEFactory(_request.socket, _reply, _events)).bind(null, request, reply);
|
|
854
|
-
// locale 감지
|
|
855
|
-
const locale = this.detectLocale(request.headers["accept-language"], this.config.i18n.supportedLocales) ?? this.config.i18n.defaultLocale;
|
|
856
|
-
// auth context 추가
|
|
857
|
-
const headers = convertFastifyHeadersToStandard(request.headers);
|
|
858
|
-
const session = await this._auth?.api.getSession({
|
|
859
|
-
headers
|
|
860
|
-
}) ?? null;
|
|
861
|
-
const context = {
|
|
862
|
-
...await Promise.resolve(config.contextProvider({
|
|
863
|
-
request,
|
|
864
|
-
reply,
|
|
865
|
-
headers: request.headers,
|
|
866
|
-
createSSE,
|
|
867
|
-
naiteStore: Naite.createStore(),
|
|
868
|
-
locale,
|
|
869
|
-
// auth
|
|
870
|
-
user: session?.user ?? null,
|
|
871
|
-
session: session?.session ?? null
|
|
872
|
-
}, request, reply))
|
|
873
|
-
};
|
|
874
|
-
return context;
|
|
875
|
-
}
|
|
876
|
-
/**
|
|
877
|
-
* Accept-Language 헤더에서 지원하는 locale을 찾습니다.
|
|
878
|
-
* @example "ko-KR,ko;q=0.9,en;q=0.8" → "ko"
|
|
879
|
-
*/ detectLocale(acceptLanguage, supported) {
|
|
880
|
-
if (!acceptLanguage) return undefined;
|
|
881
|
-
// Accept-Language: ko-KR,ko;q=0.9,en;q=0.8
|
|
882
|
-
const langs = acceptLanguage.split(",").map((lang)=>{
|
|
883
|
-
const [code] = lang.split(";");
|
|
884
|
-
return code.trim().split("-")[0]; // ko-KR → ko
|
|
885
|
-
});
|
|
886
|
-
return langs.find((lang)=>supported.includes(lang));
|
|
887
|
-
}
|
|
888
|
-
async startWatcher() {
|
|
889
|
-
const watchPath = [
|
|
890
|
-
path.join(this.apiRootPath, "src")
|
|
891
|
-
];
|
|
892
|
-
const chokidar = (await import("chokidar")).default;
|
|
893
|
-
this.watcher = chokidar.watch(watchPath, {
|
|
894
|
-
ignored: (path, stats)=>!!stats?.isFile() && !path.endsWith(".ts") && !path.endsWith(".json"),
|
|
895
|
-
persistent: true,
|
|
896
|
-
ignoreInitial: true
|
|
897
|
-
});
|
|
898
|
-
this.watcher.on("all", async (event, filePath)=>{
|
|
899
|
-
const absolutePath = filePath;
|
|
900
|
-
assert(absolutePath.startsWith(this.apiRootPath), "File path is not within the API root path");
|
|
901
|
-
if (event !== "change" && event !== "add") {
|
|
902
|
-
return;
|
|
903
|
-
}
|
|
904
|
-
try {
|
|
905
|
-
// sonamu.config.ts 변경 시 재시작
|
|
906
|
-
const isConfigTs = filePath === path.join(this.apiRootPath, "src", "sonamu.config.ts");
|
|
907
|
-
if (isConfigTs) {
|
|
908
|
-
const relativePath = filePath.replace(this.apiRootPath, "api");
|
|
909
|
-
const chalk = (await import("chalk")).default;
|
|
910
|
-
console.log(chalk.bold(`Detected(${event}): ${chalk.blue(relativePath)} - Restarting...`));
|
|
911
|
-
process.kill(process.pid, "SIGUSR2");
|
|
912
|
-
return;
|
|
913
|
-
}
|
|
914
|
-
await this.handleFileChange(event, absolutePath);
|
|
915
|
-
} catch (e) {
|
|
916
|
-
console.error(e);
|
|
917
|
-
}
|
|
918
|
-
});
|
|
919
|
-
}
|
|
920
|
-
/*
|
|
921
|
-
A function that automatically handles init and destroy when using Sonamu via scripts.
|
|
922
|
-
*/ async runScript(fn) {
|
|
923
|
-
await this.init(true, false, undefined, false);
|
|
924
|
-
try {
|
|
925
|
-
await fn();
|
|
926
|
-
} finally{
|
|
927
|
-
await this.destroy();
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
async registerPlugins(server, plugins) {
|
|
931
|
-
if (!plugins) {
|
|
932
|
-
return;
|
|
933
|
-
}
|
|
934
|
-
// compress 플러그인은 다른 플러그인보다 먼저 등록되어야 합니다.
|
|
935
|
-
if (plugins.compress) {
|
|
936
|
-
const compressPlugin = (await import("@fastify/compress")).default;
|
|
937
|
-
const defaultOptions = {
|
|
938
|
-
threshold: 1024,
|
|
939
|
-
encodings: [
|
|
940
|
-
"br",
|
|
941
|
-
"gzip",
|
|
942
|
-
"deflate"
|
|
943
|
-
]
|
|
944
|
-
};
|
|
945
|
-
if (plugins.compress === true) {
|
|
946
|
-
server.register(compressPlugin, defaultOptions);
|
|
947
|
-
} else {
|
|
948
|
-
server.register(compressPlugin, {
|
|
949
|
-
...defaultOptions,
|
|
950
|
-
...plugins.compress
|
|
951
|
-
});
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
const pluginsModules = {
|
|
955
|
-
cors: "@fastify/cors",
|
|
956
|
-
formbody: "@fastify/formbody",
|
|
957
|
-
multipart: "@fastify/multipart",
|
|
958
|
-
qs: "fastify-qs",
|
|
959
|
-
sse: "fastify-sse-v2",
|
|
960
|
-
static: "@fastify/static"
|
|
961
|
-
};
|
|
962
|
-
const registerPlugin = async (key, pluginName)=>{
|
|
963
|
-
const option = plugins[key];
|
|
964
|
-
if (!option) return;
|
|
965
|
-
if (option === true) {
|
|
966
|
-
server.register((await import(pluginName)).default);
|
|
967
|
-
} else {
|
|
968
|
-
server.register((await import(pluginName)).default, option);
|
|
969
|
-
}
|
|
970
|
-
};
|
|
971
|
-
for (const [key, pluginName] of Object.entries(pluginsModules)){
|
|
972
|
-
await registerPlugin(key, pluginName);
|
|
973
|
-
}
|
|
974
|
-
if (plugins.custom) {
|
|
975
|
-
plugins.custom(server);
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
/**
|
|
979
|
-
* better-auth 라우트를 등록합니다.
|
|
980
|
-
* /api/auth/* 경로로 인증 API가 자동 등록됩니다.
|
|
981
|
-
*/ async registerBetterAuth(server, options) {
|
|
982
|
-
if (!options) return;
|
|
983
|
-
const basePath = options.basePath ?? "/api/auth";
|
|
984
|
-
// better-auth 라우트 등록
|
|
985
|
-
server.route({
|
|
986
|
-
method: [
|
|
987
|
-
"GET",
|
|
988
|
-
"POST"
|
|
989
|
-
],
|
|
990
|
-
url: `${basePath}/*`,
|
|
991
|
-
handler: async (request, reply)=>{
|
|
992
|
-
const url = new URL(request.url, `http://${request.headers.host}`);
|
|
993
|
-
const headers = convertFastifyHeadersToStandard(request.headers);
|
|
994
|
-
const req = new Request(url.toString(), {
|
|
995
|
-
method: request.method,
|
|
996
|
-
headers,
|
|
997
|
-
...request.body ? {
|
|
998
|
-
body: JSON.stringify(request.body)
|
|
999
|
-
} : {}
|
|
1000
|
-
});
|
|
1001
|
-
const response = await this.auth.handler(req);
|
|
1002
|
-
reply.status(response.status);
|
|
1003
|
-
response.headers.forEach((value, key)=>{
|
|
1004
|
-
reply.header(key, value);
|
|
1005
|
-
});
|
|
1006
|
-
return reply.send(response.body ? await response.text() : null);
|
|
1007
|
-
}
|
|
1008
|
-
});
|
|
1009
|
-
const chalk = (await import("chalk")).default;
|
|
1010
|
-
console.log(chalk.green(`✓ better-auth registered at ${basePath}/*`));
|
|
1011
|
-
}
|
|
1012
|
-
async initializeCache(config, forTesting) {
|
|
1013
|
-
const { setCacheManagerRef } = await import("../cache/decorator.js");
|
|
1014
|
-
// 테스트 환경에서 메모리 드라이버 자동 사용
|
|
1015
|
-
if (forTesting) {
|
|
1016
|
-
const { createTestCacheManager } = await import("../cache/cache-manager.js");
|
|
1017
|
-
this._cache = createTestCacheManager();
|
|
1018
|
-
setCacheManagerRef(this._cache);
|
|
1019
|
-
return;
|
|
1020
|
-
}
|
|
1021
|
-
// 설정이 없으면 캐시 비활성화
|
|
1022
|
-
if (!config) {
|
|
1023
|
-
setCacheManagerRef(null);
|
|
1024
|
-
return;
|
|
1025
|
-
}
|
|
1026
|
-
// 설정에 따라 CacheManager 생성
|
|
1027
|
-
const { createCacheManager } = await import("../cache/cache-manager.js");
|
|
1028
|
-
this._cache = createCacheManager(config);
|
|
1029
|
-
setCacheManagerRef(this._cache);
|
|
1030
|
-
}
|
|
1031
|
-
async initializeWorkflows(options) {
|
|
1032
|
-
const { WorkflowManager } = await import("../tasks/workflow-manager.js");
|
|
1033
|
-
// NOTE: @sonamu-kit/tasks 안에선 knex config를 수정하기 때문에 connection이 아닌 config 째로 보냅니다.
|
|
1034
|
-
this._workflows = new WorkflowManager(DB.getDBConfig("w"));
|
|
1035
|
-
if (!options) {
|
|
1036
|
-
return;
|
|
1037
|
-
}
|
|
1038
|
-
const enableWorker = options.enableWorker ?? isDaemonServer();
|
|
1039
|
-
const defaultWorkerOptions = {
|
|
1040
|
-
concurrency: os.cpus().length - 1,
|
|
1041
|
-
usePubSub: true,
|
|
1042
|
-
listenDelay: 500
|
|
1043
|
-
};
|
|
1044
|
-
if (enableWorker) {
|
|
1045
|
-
this.workflows.setupWorker({
|
|
1046
|
-
...defaultWorkerOptions,
|
|
1047
|
-
...options.workerOptions
|
|
1048
|
-
});
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
async boot(server, options) {
|
|
1052
|
-
const port = options.listen?.port ?? 3000;
|
|
1053
|
-
const host = options.listen?.host ?? "localhost";
|
|
1054
|
-
server.addHook("onClose", async ()=>{
|
|
1055
|
-
await options.lifecycle?.onShutdown?.(server);
|
|
1056
|
-
await this.workflows.destroy();
|
|
1057
|
-
await this.destroy();
|
|
1058
|
-
});
|
|
1059
|
-
const shutdown = async ()=>{
|
|
1060
|
-
try {
|
|
1061
|
-
await server.close();
|
|
1062
|
-
process.exit(0);
|
|
1063
|
-
} catch (err) {
|
|
1064
|
-
console.error("Error during shutdown:", err);
|
|
1065
|
-
process.exit(1);
|
|
1066
|
-
}
|
|
1067
|
-
};
|
|
1068
|
-
process.on("SIGINT", shutdown);
|
|
1069
|
-
process.on("SIGTERM", shutdown);
|
|
1070
|
-
if (options.lifecycle?.onError) {
|
|
1071
|
-
server.setErrorHandler(options.lifecycle?.onError);
|
|
1072
|
-
}
|
|
1073
|
-
server.listen({
|
|
1074
|
-
port,
|
|
1075
|
-
host
|
|
1076
|
-
}).then(async ()=>{
|
|
1077
|
-
await this.workflows.startWorker();
|
|
1078
|
-
await options.lifecycle?.onStart?.(server);
|
|
1079
|
-
}).catch(async (err)=>{
|
|
1080
|
-
const chalk = (await import("chalk")).default;
|
|
1081
|
-
console.error(chalk.red("Failed to start server:", err));
|
|
1082
|
-
await shutdown();
|
|
1083
|
-
});
|
|
1084
|
-
}
|
|
1085
|
-
async handleFileChange(event, filePath) {
|
|
1086
|
-
// 첫 번째 파일이면 HMR 시작 시간 기록
|
|
1087
|
-
if (this.pendingFiles.length === 0) {
|
|
1088
|
-
this.hmrStartTime = Date.now();
|
|
1089
|
-
}
|
|
1090
|
-
this.pendingFiles.push(filePath);
|
|
1091
|
-
const relativePath = path.relative(this.apiRootPath, filePath);
|
|
1092
|
-
const chalk = (await import("chalk")).default;
|
|
1093
|
-
console.log(chalk.bold(`Detected(${event}): ${chalk.blue(relativePath)}`));
|
|
1094
|
-
await this.syncer.syncFromWatcher(event, filePath);
|
|
1095
|
-
// 처리 완료된 파일을 대기 목록에서 제거
|
|
1096
|
-
this.pendingFiles = this.pendingFiles.slice(1);
|
|
1097
|
-
// 모든 파일 처리가 완료되면 최종 메시지 출력
|
|
1098
|
-
if (this.pendingFiles.length === 0) {
|
|
1099
|
-
await this.finishHMR();
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
async finishHMR() {
|
|
1103
|
-
await this.syncer.renewChecksums();
|
|
1104
|
-
const endTime = Date.now();
|
|
1105
|
-
const totalTime = endTime - this.hmrStartTime;
|
|
1106
|
-
const [chalk, { centerText }] = await Promise.all([
|
|
1107
|
-
(await import("chalk")).default,
|
|
1108
|
-
import("../utils/console-util.js")
|
|
1109
|
-
]);
|
|
1110
|
-
const msg = `HMR Done! ${chalk.bold.white(`${totalTime}ms`)}`;
|
|
1111
|
-
console.log(chalk.black.bgGreen(centerText(msg)));
|
|
1112
|
-
}
|
|
1113
|
-
async destroy() {
|
|
1114
|
-
const { BaseModel } = await import("../database/base-model.js");
|
|
1115
|
-
// 먼저 처리해야함.
|
|
1116
|
-
await BaseModel.destroy();
|
|
1117
|
-
await Promise.allSettled([
|
|
1118
|
-
this._workflows?.destroy() ?? Promise.resolve(),
|
|
1119
|
-
this._cache?.disconnect() ?? Promise.resolve(),
|
|
1120
|
-
this._devVitestManager?.shutdown() ?? Promise.resolve(),
|
|
1121
|
-
this.watcher?.close() ?? Promise.resolve(),
|
|
1122
|
-
logtapeDispose()
|
|
1123
|
-
]);
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
export const Sonamu = new SonamuClass();
|
|
20
|
+
import os from "os";
|
|
21
|
+
import mime, { lookup } from "mime-types";
|
|
22
|
+
|
|
23
|
+
//#region src/api/sonamu.ts
|
|
1127
24
|
/**
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
25
|
+
* stream 모드에서 키 생성 함수가 지정되지 않았을 때 사용하는 기본 함수입니다.
|
|
26
|
+
*/
|
|
27
|
+
function defaultKeyGenerator(file) {
|
|
28
|
+
const ext = mime.extension(file.mimetype) || "bin";
|
|
29
|
+
const timestamp = Date.now();
|
|
30
|
+
const random = Math.random().toString(36).slice(2, 8);
|
|
31
|
+
return `uploads/${timestamp}-${random}.${ext}`;
|
|
1134
32
|
}
|
|
33
|
+
var SonamuClass, Sonamu;
|
|
34
|
+
var init_sonamu = __esmMin((() => {
|
|
35
|
+
init_better_auth_entities();
|
|
36
|
+
init_cache_control();
|
|
37
|
+
init_compress();
|
|
38
|
+
init_db();
|
|
39
|
+
init_sd();
|
|
40
|
+
init_so_exceptions();
|
|
41
|
+
init_buffered_file();
|
|
42
|
+
init_uploaded_file();
|
|
43
|
+
init_sse();
|
|
44
|
+
init_controller();
|
|
45
|
+
init_fs_utils();
|
|
46
|
+
init_utils();
|
|
47
|
+
init_secret();
|
|
48
|
+
SonamuClass = class {
|
|
49
|
+
isInitialized = false;
|
|
50
|
+
forTesting = false;
|
|
51
|
+
asyncLocalStorage = new AsyncLocalStorage();
|
|
52
|
+
getContext() {
|
|
53
|
+
const store = this.asyncLocalStorage.getStore();
|
|
54
|
+
if (store?.context) {
|
|
55
|
+
return store.context;
|
|
56
|
+
}
|
|
57
|
+
if (process.env.NODE_ENV === "test") {
|
|
58
|
+
return {
|
|
59
|
+
request: null,
|
|
60
|
+
reply: null,
|
|
61
|
+
headers: {},
|
|
62
|
+
createSSE: (schema) => createMockSSEFactory(schema),
|
|
63
|
+
naiteStore: new Map()
|
|
64
|
+
};
|
|
65
|
+
} else {
|
|
66
|
+
throw new Error("Sonamu cannot find context");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
_apiRootPath = null;
|
|
70
|
+
set apiRootPath(apiRootPath) {
|
|
71
|
+
this._apiRootPath = apiRootPath;
|
|
72
|
+
}
|
|
73
|
+
get apiRootPath() {
|
|
74
|
+
if (this._apiRootPath === null) {
|
|
75
|
+
throw new Error("Sonamu has not been initialized");
|
|
76
|
+
}
|
|
77
|
+
return this._apiRootPath;
|
|
78
|
+
}
|
|
79
|
+
get appRootPath() {
|
|
80
|
+
return this.apiRootPath.split(path.sep).slice(0, -1).join(path.sep);
|
|
81
|
+
}
|
|
82
|
+
_dbConfig = null;
|
|
83
|
+
set dbConfig(dbConfig) {
|
|
84
|
+
this._dbConfig = dbConfig;
|
|
85
|
+
}
|
|
86
|
+
get dbConfig() {
|
|
87
|
+
if (this._dbConfig === null) {
|
|
88
|
+
throw new Error("Sonamu has not been initialized");
|
|
89
|
+
}
|
|
90
|
+
return this._dbConfig;
|
|
91
|
+
}
|
|
92
|
+
_syncer = null;
|
|
93
|
+
set syncer(syncer) {
|
|
94
|
+
this._syncer = syncer;
|
|
95
|
+
}
|
|
96
|
+
get syncer() {
|
|
97
|
+
if (this._syncer === null) {
|
|
98
|
+
throw new Error("Sonamu has not been initialized");
|
|
99
|
+
}
|
|
100
|
+
return this._syncer;
|
|
101
|
+
}
|
|
102
|
+
_config = null;
|
|
103
|
+
set config(config) {
|
|
104
|
+
this._config = config;
|
|
105
|
+
}
|
|
106
|
+
get config() {
|
|
107
|
+
if (this._config === null) {
|
|
108
|
+
throw new Error("Sonamu has not been initialized");
|
|
109
|
+
}
|
|
110
|
+
return this._config;
|
|
111
|
+
}
|
|
112
|
+
secrets = getSecrets();
|
|
113
|
+
_storage = null;
|
|
114
|
+
/**
|
|
115
|
+
* StorageManager 인스턴스
|
|
116
|
+
*/
|
|
117
|
+
get storage() {
|
|
118
|
+
if (!this._storage) {
|
|
119
|
+
throw new Error("Storage has not been initialized. Check storage config.");
|
|
120
|
+
}
|
|
121
|
+
return this._storage;
|
|
122
|
+
}
|
|
123
|
+
_cache = null;
|
|
124
|
+
/**
|
|
125
|
+
* CacheManager 인스턴스 (BentoCache)
|
|
126
|
+
*/
|
|
127
|
+
get cache() {
|
|
128
|
+
if (!this._cache) {
|
|
129
|
+
throw new Error("Cache has not been initialized. Check cache config in sonamu.config.ts.");
|
|
130
|
+
}
|
|
131
|
+
return this._cache;
|
|
132
|
+
}
|
|
133
|
+
_workflows = null;
|
|
134
|
+
get workflows() {
|
|
135
|
+
if (this._workflows === null) {
|
|
136
|
+
throw new Error("Sonamu has not been initialized");
|
|
137
|
+
}
|
|
138
|
+
return this._workflows;
|
|
139
|
+
}
|
|
140
|
+
_auth = null;
|
|
141
|
+
get auth() {
|
|
142
|
+
if (!this._auth) {
|
|
143
|
+
throw new Error("Auth has not been initialized. Check auth config in sonamu.config.ts.");
|
|
144
|
+
}
|
|
145
|
+
return this._auth;
|
|
146
|
+
}
|
|
147
|
+
_devVitestManager = null;
|
|
148
|
+
get devVitestManager() {
|
|
149
|
+
return this._devVitestManager;
|
|
150
|
+
}
|
|
151
|
+
set devVitestManager(manager) {
|
|
152
|
+
this._devVitestManager = manager;
|
|
153
|
+
}
|
|
154
|
+
watcher = null;
|
|
155
|
+
pendingFiles = [];
|
|
156
|
+
hmrStartTime = 0;
|
|
157
|
+
server = null;
|
|
158
|
+
async initForTesting() {
|
|
159
|
+
await this.init(true, false, undefined, true);
|
|
160
|
+
}
|
|
161
|
+
async init(doSilent = false, enableSync = true, apiRootPath, forTesting = false) {
|
|
162
|
+
this.forTesting = forTesting;
|
|
163
|
+
if (this.isInitialized) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (!doSilent) {
|
|
167
|
+
const chalk = (await import("chalk")).default;
|
|
168
|
+
console.time(chalk.cyan(`Sonamu.init${forTesting ? " for testing" : ""}`));
|
|
169
|
+
}
|
|
170
|
+
const { findApiRootPath } = await import("../utils/utils.js");
|
|
171
|
+
this.apiRootPath = apiRootPath ?? findApiRootPath();
|
|
172
|
+
const { loadConfig } = await import("./config.js");
|
|
173
|
+
this.config = await loadConfig(this.apiRootPath);
|
|
174
|
+
setSDConfig(this.config.i18n);
|
|
175
|
+
this.config.database.database = this.config.database.database ?? "pg";
|
|
176
|
+
this.config.database.defaultOptions.client = this.config.database.database ?? "pg";
|
|
177
|
+
const { configureLogTape } = await import("../logger/configure.js");
|
|
178
|
+
if (this.config.logging !== false) {
|
|
179
|
+
await configureLogTape({ ...this.config.logging });
|
|
180
|
+
}
|
|
181
|
+
const { DB: DB$1 } = await import("../database/db.js");
|
|
182
|
+
this.dbConfig = DB$1.generateDBConfig(this.config.database);
|
|
183
|
+
DB$1.setConfig(this.dbConfig);
|
|
184
|
+
if (!doSilent) {
|
|
185
|
+
const chalk = (await import("chalk")).default;
|
|
186
|
+
console.log(chalk.green("DB Config Loaded!"));
|
|
187
|
+
}
|
|
188
|
+
const { EntityManager } = await import("../entity/entity-manager.js");
|
|
189
|
+
await EntityManager.autoload(doSilent);
|
|
190
|
+
await this.initializeCache(this.config.server.cache, forTesting);
|
|
191
|
+
const authConfig = this.config.server.auth;
|
|
192
|
+
if (authConfig) {
|
|
193
|
+
const mergedFieldMappings = merge(BASE_FIELD_MAPPINGS, authConfig);
|
|
194
|
+
const { betterAuth } = await import("better-auth");
|
|
195
|
+
const { sonamuKnexAdapter } = await import("../auth/knex-adapter.js");
|
|
196
|
+
this._auth = betterAuth({
|
|
197
|
+
database: sonamuKnexAdapter(),
|
|
198
|
+
...mergedFieldMappings
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
if (forTesting) {
|
|
202
|
+
this.isInitialized = true;
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
await this.initializeWorkflows(this.config.tasks);
|
|
206
|
+
const { Syncer } = await import("../syncer/syncer.js");
|
|
207
|
+
this.syncer = new Syncer();
|
|
208
|
+
await this.syncer.autoloadTypes();
|
|
209
|
+
await this.syncer.autoloadModels();
|
|
210
|
+
await this.syncer.autoloadApis();
|
|
211
|
+
await this.syncer.autoloadWorkflows();
|
|
212
|
+
const { TemplateManager } = await import("../template/index.js");
|
|
213
|
+
await TemplateManager.autoload();
|
|
214
|
+
await this.syncer.autoloadSSRRoutes();
|
|
215
|
+
const { isLocal, isTest, isHotReloadServer } = await import("../utils/controller.js");
|
|
216
|
+
if (isLocal() && !isTest() && isHotReloadServer() && enableSync) {
|
|
217
|
+
await this.syncer.sync();
|
|
218
|
+
await this.startWatcher();
|
|
219
|
+
}
|
|
220
|
+
this.isInitialized = true;
|
|
221
|
+
if (!doSilent) {
|
|
222
|
+
const chalk = (await import("chalk")).default;
|
|
223
|
+
console.timeEnd(chalk.cyan("Sonamu.init"));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
async createServer(initOptions) {
|
|
227
|
+
if (!this.isInitialized) {
|
|
228
|
+
await this.init(initOptions?.doSilent, initOptions?.enableSync);
|
|
229
|
+
}
|
|
230
|
+
const options = this.config.server;
|
|
231
|
+
const { default: fastify } = await import("fastify");
|
|
232
|
+
const { getLogTapeFastifyLogger } = await import("@logtape/fastify");
|
|
233
|
+
const server = fastify({
|
|
234
|
+
...options.fastify,
|
|
235
|
+
logger: this.config.logging !== false ? getLogTapeFastifyLogger({ category: this.config.logging?.fastifyCategory ?? ["fastify"] }) : undefined
|
|
236
|
+
});
|
|
237
|
+
this.server = server;
|
|
238
|
+
if (options.storage) {
|
|
239
|
+
const { StorageManager } = await import("../storage/storage-manager.js");
|
|
240
|
+
this._storage = new StorageManager(options.storage);
|
|
241
|
+
}
|
|
242
|
+
if (options.plugins) {
|
|
243
|
+
await this.registerPlugins(server, options.plugins);
|
|
244
|
+
}
|
|
245
|
+
if (options.auth) {
|
|
246
|
+
await this.registerBetterAuth(server, options.auth);
|
|
247
|
+
}
|
|
248
|
+
await this.withFastify(server, options.apiConfig, {
|
|
249
|
+
enableSync: initOptions?.enableSync,
|
|
250
|
+
doSilent: initOptions?.doSilent
|
|
251
|
+
});
|
|
252
|
+
await this.boot(server, options);
|
|
253
|
+
return server;
|
|
254
|
+
}
|
|
255
|
+
async withFastify(server, config, options) {
|
|
256
|
+
if (!this.isInitialized) {
|
|
257
|
+
await this.init(options?.doSilent, options?.enableSync);
|
|
258
|
+
}
|
|
259
|
+
this.server = server;
|
|
260
|
+
const timezone = this.config.api.timezone;
|
|
261
|
+
if (timezone) {
|
|
262
|
+
const { formatInTimeZone } = await import("date-fns-tz");
|
|
263
|
+
const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/;
|
|
264
|
+
const DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ssXXX";
|
|
265
|
+
server.setReplySerializer((payload) => {
|
|
266
|
+
return JSON.stringify(payload, (_key, value) => {
|
|
267
|
+
if (typeof value === "string" && ISO_DATE_REGEX.test(value)) {
|
|
268
|
+
return formatInTimeZone(new Date(value), timezone, DATE_FORMAT);
|
|
269
|
+
}
|
|
270
|
+
return value;
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
if (!options?.doSilent) {
|
|
274
|
+
const chalk = (await import("chalk")).default;
|
|
275
|
+
console.log(chalk.green(`Timezone set to ${timezone}`));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
server.get(`${this.config.api.route.prefix}/routes`, async (_request, _reply) => {
|
|
279
|
+
return this.syncer.apis;
|
|
280
|
+
});
|
|
281
|
+
server.get(`${this.config.api.route.prefix}/healthcheck`, async (_request, _reply) => {
|
|
282
|
+
return "ok";
|
|
283
|
+
});
|
|
284
|
+
const { isLocal } = await import("../utils/controller.js");
|
|
285
|
+
if (isLocal()) {
|
|
286
|
+
const { sonamuUIApiPlugin } = await import("../ui/api.js");
|
|
287
|
+
server.register(sonamuUIApiPlugin);
|
|
288
|
+
}
|
|
289
|
+
if (isLocal() && this.config.test?.devRunner?.enabled) {
|
|
290
|
+
const { registerDevTestRoutes } = await import("../testing/dev-test-routes.js");
|
|
291
|
+
await registerDevTestRoutes(server, this.config.test.devRunner);
|
|
292
|
+
}
|
|
293
|
+
const webPath = path.join(this.appRootPath, "web");
|
|
294
|
+
const hasWeb = await exists(webPath);
|
|
295
|
+
const pluginCompress = this.config.server.plugins?.compress;
|
|
296
|
+
const globalCompressOptions = pluginCompress ? pluginCompress === true ? {
|
|
297
|
+
threshold: 1024,
|
|
298
|
+
encodings: [
|
|
299
|
+
"br",
|
|
300
|
+
"gzip",
|
|
301
|
+
"deflate"
|
|
302
|
+
]
|
|
303
|
+
} : {
|
|
304
|
+
threshold: pluginCompress.threshold,
|
|
305
|
+
encodings: pluginCompress.encodings,
|
|
306
|
+
customTypes: pluginCompress.customTypes
|
|
307
|
+
} : undefined;
|
|
308
|
+
if (isLocal()) {
|
|
309
|
+
const disableIntegratedWeb = process.env.SONAMU_DISABLE_INTEGRATED_WEB === "yes";
|
|
310
|
+
if (hasWeb && !disableIntegratedWeb) {
|
|
311
|
+
await this.setupDevServerWithVite(server, webPath, config);
|
|
312
|
+
} else {
|
|
313
|
+
this.setupDevServer(server, config);
|
|
314
|
+
}
|
|
315
|
+
} else {
|
|
316
|
+
for (const api of this.syncer.apis) {
|
|
317
|
+
if (this.syncer.models[api.modelName] === undefined) {
|
|
318
|
+
throw new Error(`정의되지 않은 모델에 접근 ${api.modelName}`);
|
|
319
|
+
}
|
|
320
|
+
server.route({
|
|
321
|
+
method: api.options.httpMethod ?? "GET",
|
|
322
|
+
url: this.config.api.route.prefix + api.path,
|
|
323
|
+
handler: this.createApiHandler(api, config),
|
|
324
|
+
compress: toFastifyCompressOption(api.options.compress, globalCompressOptions)
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
await this.setupStaticWebServer(server, config, globalCompressOptions);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* dev 모드 공통: catch-all에서 syncer.apis를 동적으로 탐색하여 API 요청을 처리합니다.
|
|
332
|
+
* server.route()로 개별 등록하면 handler가 고정되어 HMR이 동작하지 않으므로,
|
|
333
|
+
* 매 요청마다 syncer.apis를 조회하는 이 방식을 사용합니다.
|
|
334
|
+
*
|
|
335
|
+
* 요청이 /api(정확히는 this.config.api.route.prefix)로 시작하지 않는 경우라면 null을 반환하며 끝냅니다.
|
|
336
|
+
*/
|
|
337
|
+
handleDevApiRequest(request, config) {
|
|
338
|
+
const url = this.getPathnameFromUrl(request.url);
|
|
339
|
+
const method = request.method;
|
|
340
|
+
if (!url.startsWith(this.config.api.route.prefix)) {
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
const matchedApi = this.syncer.apis.find((api) => {
|
|
344
|
+
if (this.syncer.models[api.modelName] === undefined) {
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
const apiMethod = api.options.httpMethod ?? "GET";
|
|
348
|
+
if (apiMethod !== method) return false;
|
|
349
|
+
const fullPath = this.config.api.route.prefix + api.path;
|
|
350
|
+
return this.isPathPatternMatch(fullPath, url);
|
|
351
|
+
});
|
|
352
|
+
if (!matchedApi) {
|
|
353
|
+
throw new NotFoundException(SD("error.api.notFound"));
|
|
354
|
+
}
|
|
355
|
+
return this.createApiHandler(matchedApi, config);
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* dev api 모드: Vite 없이 API 동적 라우팅만 제공합니다.
|
|
359
|
+
* HMR을 위해 catch-all에서 매 요청마다 syncer.apis를 조회합니다.
|
|
360
|
+
*/
|
|
361
|
+
setupDevServer(server, config) {
|
|
362
|
+
server.route({
|
|
363
|
+
method: [
|
|
364
|
+
"GET",
|
|
365
|
+
"HEAD",
|
|
366
|
+
"POST",
|
|
367
|
+
"PUT",
|
|
368
|
+
"DELETE",
|
|
369
|
+
"PATCH"
|
|
370
|
+
],
|
|
371
|
+
url: `${this.config.api.route.prefix}/*`,
|
|
372
|
+
handler: async (request, reply) => {
|
|
373
|
+
const handler = this.handleDevApiRequest(request, config);
|
|
374
|
+
if (handler) {
|
|
375
|
+
return handler(request, reply);
|
|
376
|
+
}
|
|
377
|
+
throw new NotFoundException(SD("error.api.notFound"));
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
viteServer = null;
|
|
382
|
+
/**
|
|
383
|
+
* dev all 모드: Vite Dev Server를 통합하여 API + SSR + CSR을 모두 제공합니다.
|
|
384
|
+
* API 동적 매칭은 handleDevApiRequest를 공유합니다.
|
|
385
|
+
*/
|
|
386
|
+
async setupDevServerWithVite(server, webPath, config) {
|
|
387
|
+
await server.register((await import("@fastify/middie")).default);
|
|
388
|
+
const vite = await import("vite");
|
|
389
|
+
this.viteServer = await vite.createServer({
|
|
390
|
+
root: webPath,
|
|
391
|
+
server: {
|
|
392
|
+
middlewareMode: true,
|
|
393
|
+
hmr: { server: server.server }
|
|
394
|
+
},
|
|
395
|
+
appType: "custom"
|
|
396
|
+
});
|
|
397
|
+
server.use((req, res, next) => {
|
|
398
|
+
if (req.url?.startsWith(this.config.api.route.prefix) || req.url?.startsWith("/sonamu-ui")) {
|
|
399
|
+
return next();
|
|
400
|
+
}
|
|
401
|
+
return this.viteServer.middlewares(req, res, next);
|
|
402
|
+
});
|
|
403
|
+
server.route({
|
|
404
|
+
method: [
|
|
405
|
+
"GET",
|
|
406
|
+
"HEAD",
|
|
407
|
+
"POST",
|
|
408
|
+
"PUT",
|
|
409
|
+
"DELETE",
|
|
410
|
+
"PATCH"
|
|
411
|
+
],
|
|
412
|
+
url: "/*",
|
|
413
|
+
handler: async (request, reply) => {
|
|
414
|
+
const result = this.handleDevApiRequest(request, config);
|
|
415
|
+
if (result) {
|
|
416
|
+
return result(request, reply);
|
|
417
|
+
}
|
|
418
|
+
const url = request.url;
|
|
419
|
+
const { matchSSRRoute, renderSSR } = await import("../ssr/index.js");
|
|
420
|
+
const ssrMatch = matchSSRRoute(url);
|
|
421
|
+
if (ssrMatch) {
|
|
422
|
+
console.log(`[SSR] Matched route: ${ssrMatch.route.path}`);
|
|
423
|
+
const html = await renderSSR(url, ssrMatch.route, ssrMatch.params, request, reply, config, this.viteServer);
|
|
424
|
+
reply.type("text/html");
|
|
425
|
+
return html;
|
|
426
|
+
}
|
|
427
|
+
try {
|
|
428
|
+
const fs$1 = await import("node:fs/promises");
|
|
429
|
+
let template = await fs$1.readFile(path.join(this.viteServer.config.root, "index.html"), "utf-8");
|
|
430
|
+
template = await this.viteServer.transformIndexHtml(url, template);
|
|
431
|
+
reply.type("text/html");
|
|
432
|
+
return template;
|
|
433
|
+
} catch (e) {
|
|
434
|
+
this.viteServer.ssrFixStacktrace(e);
|
|
435
|
+
console.error(e);
|
|
436
|
+
reply.status(500);
|
|
437
|
+
return e.message;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
server.addHook("onClose", async () => {
|
|
442
|
+
await this.viteServer.close();
|
|
443
|
+
});
|
|
444
|
+
console.log("✓ Vite dev server integrated");
|
|
445
|
+
}
|
|
446
|
+
async setupStaticWebServer(server, config, globalCompressOptions) {
|
|
447
|
+
const webDistPath = path.join(this.apiRootPath, "web-dist", "client");
|
|
448
|
+
const ssrPath = path.join(this.apiRootPath, "web-dist", "server");
|
|
449
|
+
const ssrEntryPath = path.join(ssrPath, "entry-server.generated.js");
|
|
450
|
+
const ssrRoutesPath = path.join(this.apiRootPath, "dist", "ssr", "routes.js");
|
|
451
|
+
if (!await exists(webDistPath)) {
|
|
452
|
+
console.warn(`⚠ Web dist not found: ${webDistPath}`);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
const ssrAvailable = await exists(ssrEntryPath);
|
|
456
|
+
if (!ssrAvailable) {
|
|
457
|
+
console.warn(`⚠ SSR entry not found: ${ssrEntryPath}`);
|
|
458
|
+
console.warn(" SSR will be disabled. Only CSR will work.");
|
|
459
|
+
}
|
|
460
|
+
if (ssrAvailable) {
|
|
461
|
+
if (await exists(ssrRoutesPath)) {
|
|
462
|
+
await import(ssrRoutesPath);
|
|
463
|
+
console.log("✓ SSR routes loaded");
|
|
464
|
+
} else {
|
|
465
|
+
console.warn(`⚠ SSR routes not found: ${ssrRoutesPath}`);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
server.get("/assets/:filename", async (request, reply) => {
|
|
469
|
+
const requestedFile = request.params.filename;
|
|
470
|
+
const assetsDir = path.join(webDistPath, "assets");
|
|
471
|
+
const safeFilePath = this.resolvePathWithinBaseDir(assetsDir, requestedFile);
|
|
472
|
+
if (safeFilePath === null) {
|
|
473
|
+
reply.status(403).send();
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const normalizedRequestedFile = path.relative(assetsDir, safeFilePath).replace(/\\/g, "/");
|
|
477
|
+
const assetPath = `/assets/${normalizedRequestedFile}`;
|
|
478
|
+
const getCacheControlForAsset = () => {
|
|
479
|
+
const cacheReq = {
|
|
480
|
+
type: "assets",
|
|
481
|
+
url: request.url,
|
|
482
|
+
path: assetPath,
|
|
483
|
+
method: request.method
|
|
484
|
+
};
|
|
485
|
+
if (config.cacheControlHandler) {
|
|
486
|
+
const result = config.cacheControlHandler(cacheReq);
|
|
487
|
+
if (result) return result;
|
|
488
|
+
}
|
|
489
|
+
return CachePresets.immutable;
|
|
490
|
+
};
|
|
491
|
+
if (/^index-[a-f0-9]+\.(js|css)$/.test(normalizedRequestedFile)) {
|
|
492
|
+
const ext = normalizedRequestedFile.split(".").pop();
|
|
493
|
+
const files = await fs.readdir(assetsDir);
|
|
494
|
+
const currentFile = files.find((f) => f.startsWith("index-") && f.endsWith(`.${ext}`));
|
|
495
|
+
if (currentFile) {
|
|
496
|
+
const filePath$1 = path.join(assetsDir, currentFile);
|
|
497
|
+
const content = await fs.readFile(filePath$1);
|
|
498
|
+
reply.type(ext === "js" ? "application/javascript" : "text/css");
|
|
499
|
+
applyCacheHeaders(reply, getCacheControlForAsset());
|
|
500
|
+
return reply.send(content);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
const filePath = safeFilePath;
|
|
504
|
+
if (await exists(filePath)) {
|
|
505
|
+
const content = await fs.readFile(filePath);
|
|
506
|
+
const ext = normalizedRequestedFile.split(".").pop();
|
|
507
|
+
reply.type(ext === "js" ? "application/javascript" : ext === "css" ? "text/css" : "");
|
|
508
|
+
if (normalizedRequestedFile.includes("-")) {
|
|
509
|
+
applyCacheHeaders(reply, getCacheControlForAsset());
|
|
510
|
+
}
|
|
511
|
+
return reply.send(content);
|
|
512
|
+
}
|
|
513
|
+
reply.status(404).send();
|
|
514
|
+
});
|
|
515
|
+
if (ssrAvailable) {
|
|
516
|
+
const { getSSRRoutes } = await import("../ssr/index.js");
|
|
517
|
+
const { renderSSR } = await import("../ssr/renderer.js");
|
|
518
|
+
const ssrRoutes = getSSRRoutes();
|
|
519
|
+
for (const route of ssrRoutes) {
|
|
520
|
+
server.route({
|
|
521
|
+
method: ["GET", "HEAD"],
|
|
522
|
+
url: route.path,
|
|
523
|
+
compress: toFastifyCompressOption(route.compress ?? true, globalCompressOptions),
|
|
524
|
+
handler: async (request, reply) => {
|
|
525
|
+
const url = request.url;
|
|
526
|
+
console.log(`[SSR] Matched route: ${route.path}`);
|
|
527
|
+
const params = this.extractPathParams(route.path, url);
|
|
528
|
+
const html = await renderSSR(url, route, params, request, reply, config);
|
|
529
|
+
reply.type("text/html");
|
|
530
|
+
return html;
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
server.route({
|
|
536
|
+
method: ["GET", "HEAD"],
|
|
537
|
+
url: "*",
|
|
538
|
+
handler: async (request, reply) => {
|
|
539
|
+
if (request.url.startsWith("/api") || request.url.startsWith("/sonamu-ui")) {
|
|
540
|
+
reply.status(404).send();
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
if (config.cacheControlHandler) {
|
|
544
|
+
const csrCacheReq = {
|
|
545
|
+
type: "csr",
|
|
546
|
+
url: request.url,
|
|
547
|
+
path: request.url.split("?")[0],
|
|
548
|
+
method: request.method
|
|
549
|
+
};
|
|
550
|
+
const csrCacheConfig = config.cacheControlHandler(csrCacheReq);
|
|
551
|
+
if (csrCacheConfig) {
|
|
552
|
+
applyCacheHeaders(reply, csrCacheConfig);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
const requestPath = this.getPathnameFromUrl(request.url);
|
|
556
|
+
const safeFilePath = this.resolvePathWithinBaseDir(webDistPath, requestPath);
|
|
557
|
+
if (safeFilePath === null) {
|
|
558
|
+
reply.status(403).send();
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
if (await fileExists(safeFilePath)) {
|
|
562
|
+
const content = await fs.readFile(safeFilePath);
|
|
563
|
+
return reply.type(lookup(safeFilePath) || "application/octet-stream").send(content);
|
|
564
|
+
}
|
|
565
|
+
const indexPath = path.join(webDistPath, "index.html");
|
|
566
|
+
return reply.type("text/html").send(await fs.readFile(indexPath, "utf-8"));
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
console.log(`✓ Static web server configured with ${ssrAvailable ? "SSR" : "CSR only"} support`);
|
|
570
|
+
}
|
|
571
|
+
createApiHandler(api, config) {
|
|
572
|
+
return async (request, reply) => {
|
|
573
|
+
const context = await this.createContext(config, request, reply);
|
|
574
|
+
return this.asyncLocalStorage.run({ context }, async () => {
|
|
575
|
+
(api.options.guards ?? []).every((guard) => config.guardHandler(guard, request, api));
|
|
576
|
+
const { getZodObjectFromApi } = await import("./code-converters.js");
|
|
577
|
+
const ReqType = getZodObjectFromApi(api, this.syncer.types);
|
|
578
|
+
const which = api.options.httpMethod === "GET" ? "query" : "body";
|
|
579
|
+
let reqBody;
|
|
580
|
+
const files = {
|
|
581
|
+
bufferedFiles: [],
|
|
582
|
+
uploadedFiles: []
|
|
583
|
+
};
|
|
584
|
+
try {
|
|
585
|
+
const body = request[which] ?? {};
|
|
586
|
+
if (api.uploadOptions) {
|
|
587
|
+
const parts = request.parts({ limits: api.uploadOptions.limits });
|
|
588
|
+
const fields = {};
|
|
589
|
+
if (api.uploadOptions.consume === "buffer" || !api.uploadOptions.consume) {
|
|
590
|
+
for await (const part of parts) {
|
|
591
|
+
if (part.type === "file") {
|
|
592
|
+
const buffer = await part.toBuffer();
|
|
593
|
+
files.bufferedFiles.push(new BufferedFile(part, buffer));
|
|
594
|
+
} else if (part.type === "field") {
|
|
595
|
+
fields[part.fieldname] = String(part.value);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
} else if (api.uploadOptions.consume === "stream") {
|
|
599
|
+
const diskName = api.uploadOptions.destination;
|
|
600
|
+
const disk = this.storage.use(diskName);
|
|
601
|
+
const keyGenerator = api.uploadOptions.keyGenerator ?? this.config.server.storage?.keyGenerator ?? defaultKeyGenerator;
|
|
602
|
+
for await (const part of parts) {
|
|
603
|
+
if (part.type === "file") {
|
|
604
|
+
const key = await keyGenerator({
|
|
605
|
+
filename: part.filename,
|
|
606
|
+
mimetype: part.mimetype
|
|
607
|
+
});
|
|
608
|
+
await disk.putStream(key, part.file, { contentType: part.mimetype });
|
|
609
|
+
const url = await disk.getUrl(key);
|
|
610
|
+
const signedUrl = await disk.getSignedUrl(key);
|
|
611
|
+
files.uploadedFiles.push(new UploadedFile({
|
|
612
|
+
filename: part.filename,
|
|
613
|
+
mimetype: part.mimetype,
|
|
614
|
+
size: part.file.bytesRead,
|
|
615
|
+
url,
|
|
616
|
+
signedUrl,
|
|
617
|
+
key,
|
|
618
|
+
diskName
|
|
619
|
+
}));
|
|
620
|
+
} else if (part.type === "field") {
|
|
621
|
+
fields[part.fieldname] = String(part.value);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
const qs = await import("qs");
|
|
626
|
+
const parsed = qs.default.parse(fields);
|
|
627
|
+
Object.assign(body, parsed);
|
|
628
|
+
}
|
|
629
|
+
const { fastifyCaster } = await import("./caster.js");
|
|
630
|
+
reqBody = fastifyCaster(ReqType).parse(body);
|
|
631
|
+
} catch (e) {
|
|
632
|
+
const { ZodError } = await import("zod");
|
|
633
|
+
if (e instanceof ZodError) {
|
|
634
|
+
const { humanizeZodError } = await import("../utils/zod-error.js");
|
|
635
|
+
const messages = humanizeZodError(e).map((issue) => issue.message).join(" ");
|
|
636
|
+
const { BadRequestException } = await import("../exceptions/so-exceptions.js");
|
|
637
|
+
throw new BadRequestException(messages, { zodError: e });
|
|
638
|
+
} else {
|
|
639
|
+
throw e;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
reply.type(api.options.contentType ?? "application/json");
|
|
643
|
+
const apiCacheConfig = this.getApiCacheControl(api, request, config);
|
|
644
|
+
if (apiCacheConfig) {
|
|
645
|
+
applyCacheHeaders(reply, apiCacheConfig);
|
|
646
|
+
}
|
|
647
|
+
if (api.uploadOptions) {
|
|
648
|
+
const consume = api.uploadOptions.consume ?? "buffer";
|
|
649
|
+
if (consume === "buffer") {
|
|
650
|
+
context.bufferedFiles = files.bufferedFiles;
|
|
651
|
+
} else if (consume === "stream") {
|
|
652
|
+
context.uploadedFiles = files.uploadedFiles;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
const { ApiParamType } = await import("../types/types.js");
|
|
656
|
+
const args = api.parameters.map((param) => {
|
|
657
|
+
if (ApiParamType.isContext(param.type)) {
|
|
658
|
+
return context;
|
|
659
|
+
} else {
|
|
660
|
+
return reqBody[param.name];
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
return this.invokeModelMethod(api, args, reply);
|
|
664
|
+
});
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* URL에서 path params를 추출합니다.
|
|
669
|
+
* 예: pattern="/admin/companies/:companyId", url="/admin/companies/123" → { companyId: "123" }
|
|
670
|
+
*/
|
|
671
|
+
extractPathParams(pattern, url) {
|
|
672
|
+
const patternParts = pattern.split("/").filter(Boolean);
|
|
673
|
+
const urlParts = this.getPathnameFromUrl(url).split("/").filter(Boolean);
|
|
674
|
+
const params = {};
|
|
675
|
+
for (let i = 0; i < patternParts.length; i++) {
|
|
676
|
+
if (patternParts[i].startsWith(":")) {
|
|
677
|
+
params[patternParts[i].slice(1)] = urlParts[i];
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return params;
|
|
681
|
+
}
|
|
682
|
+
isPathPatternMatch(pattern, url) {
|
|
683
|
+
const patternParts = pattern.split("/").filter(Boolean);
|
|
684
|
+
const urlParts = this.getPathnameFromUrl(url).split("/").filter(Boolean);
|
|
685
|
+
if (patternParts.length !== urlParts.length) {
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
688
|
+
for (let i = 0; i < patternParts.length; i++) {
|
|
689
|
+
const patternPart = patternParts[i];
|
|
690
|
+
const urlPart = urlParts[i];
|
|
691
|
+
if (patternPart.startsWith(":")) {
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
if (patternPart !== urlPart) {
|
|
695
|
+
return false;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
return true;
|
|
699
|
+
}
|
|
700
|
+
getPathnameFromUrl(url) {
|
|
701
|
+
return url.split("?")[0];
|
|
702
|
+
}
|
|
703
|
+
resolvePathWithinBaseDir(baseDir, inputPath) {
|
|
704
|
+
try {
|
|
705
|
+
const decoded = decodeURIComponent(inputPath).replace(/\\/g, "/");
|
|
706
|
+
if (decoded.includes("\0")) {
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
const relativePath = decoded.replace(/^\/+/, "");
|
|
710
|
+
const resolvedPath = path.resolve(baseDir, relativePath);
|
|
711
|
+
const relativeFromBase = path.relative(baseDir, resolvedPath);
|
|
712
|
+
if (relativeFromBase.startsWith("..") || path.isAbsolute(relativeFromBase)) {
|
|
713
|
+
return null;
|
|
714
|
+
}
|
|
715
|
+
return resolvedPath;
|
|
716
|
+
} catch {
|
|
717
|
+
return null;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* API 응답에 적용할 Cache-Control 설정을 결정합니다.
|
|
722
|
+
* 우선순위: 개별 지정 > cacheControlHandler
|
|
723
|
+
*/
|
|
724
|
+
getApiCacheControl(api, request, config) {
|
|
725
|
+
if (api.options.cacheControl) {
|
|
726
|
+
return api.options.cacheControl;
|
|
727
|
+
}
|
|
728
|
+
if (config.cacheControlHandler) {
|
|
729
|
+
const cacheReq = {
|
|
730
|
+
type: "api",
|
|
731
|
+
url: request.url,
|
|
732
|
+
path: request.routeOptions?.url ?? request.url.split("?")[0],
|
|
733
|
+
method: request.method,
|
|
734
|
+
api
|
|
735
|
+
};
|
|
736
|
+
const result = config.cacheControlHandler(cacheReq);
|
|
737
|
+
if (result) return result;
|
|
738
|
+
}
|
|
739
|
+
return null;
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* SSR용 API 호출 (HTTP 오버헤드 없이 직접 호출)
|
|
743
|
+
* createApiHandler의 로직을 재사용하되, request 파싱 대신 params 직접 사용
|
|
744
|
+
*/
|
|
745
|
+
async invokeApiForSSR(api, params, config, request, reply) {
|
|
746
|
+
const context = await this.createContext(config, request, reply);
|
|
747
|
+
return this.asyncLocalStorage.run({ context }, async () => {
|
|
748
|
+
const { ApiParamType } = await import("../types/types.js");
|
|
749
|
+
let paramsIndex = 0;
|
|
750
|
+
const args = api.parameters.map((param) => {
|
|
751
|
+
if (ApiParamType.isContext(param.type)) {
|
|
752
|
+
return context;
|
|
753
|
+
}
|
|
754
|
+
return params[paramsIndex++];
|
|
755
|
+
});
|
|
756
|
+
return this.invokeModelMethod(api, args, reply);
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
async invokeModelMethod(api, args, reply) {
|
|
760
|
+
const model = this.syncer.models[api.modelName];
|
|
761
|
+
const result = await model[api.methodName].apply(model, args);
|
|
762
|
+
reply.type(api.options.contentType ?? "application/json");
|
|
763
|
+
return result;
|
|
764
|
+
}
|
|
765
|
+
async createContext(config, request, reply) {
|
|
766
|
+
const { createSSEFactory } = await import("../stream/sse.js");
|
|
767
|
+
const createSSE = ((_request, _reply, _events) => createSSEFactory(_request.socket, _reply, _events)).bind(null, request, reply);
|
|
768
|
+
const locale = this.detectLocale(request.headers["accept-language"], this.config.i18n.supportedLocales) ?? this.config.i18n.defaultLocale;
|
|
769
|
+
const headers = convertFastifyHeadersToStandard(request.headers);
|
|
770
|
+
const session = await this._auth?.api.getSession({ headers }) ?? null;
|
|
771
|
+
const context = { ...await Promise.resolve(config.contextProvider({
|
|
772
|
+
request,
|
|
773
|
+
reply,
|
|
774
|
+
headers: request.headers,
|
|
775
|
+
createSSE,
|
|
776
|
+
naiteStore: new Map(),
|
|
777
|
+
locale,
|
|
778
|
+
user: session?.user ?? null,
|
|
779
|
+
session: session?.session ?? null
|
|
780
|
+
}, request, reply)) };
|
|
781
|
+
return context;
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Accept-Language 헤더에서 지원하는 locale을 찾습니다.
|
|
785
|
+
* @example "ko-KR,ko;q=0.9,en;q=0.8" → "ko"
|
|
786
|
+
*/
|
|
787
|
+
detectLocale(acceptLanguage, supported) {
|
|
788
|
+
if (!acceptLanguage) return undefined;
|
|
789
|
+
const langs = acceptLanguage.split(",").map((lang) => {
|
|
790
|
+
const [code] = lang.split(";");
|
|
791
|
+
return code.trim().split("-")[0];
|
|
792
|
+
});
|
|
793
|
+
return langs.find((lang) => supported.includes(lang));
|
|
794
|
+
}
|
|
795
|
+
async startWatcher() {
|
|
796
|
+
const watchPath = [path.join(this.apiRootPath, "src")];
|
|
797
|
+
const chokidar = (await import("chokidar")).default;
|
|
798
|
+
this.watcher = chokidar.watch(watchPath, {
|
|
799
|
+
ignored: (path$1, stats) => !!stats?.isFile() && !path$1.endsWith(".ts") && !path$1.endsWith(".json"),
|
|
800
|
+
persistent: true,
|
|
801
|
+
ignoreInitial: true
|
|
802
|
+
});
|
|
803
|
+
this.watcher.on("all", async (event, filePath) => {
|
|
804
|
+
const absolutePath = filePath;
|
|
805
|
+
assert(absolutePath.startsWith(this.apiRootPath), "File path is not within the API root path");
|
|
806
|
+
if (event !== "change" && event !== "add") {
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
try {
|
|
810
|
+
const isConfigTs = filePath === path.join(this.apiRootPath, "src", "sonamu.config.ts");
|
|
811
|
+
if (isConfigTs) {
|
|
812
|
+
const relativePath = filePath.replace(this.apiRootPath, "api");
|
|
813
|
+
const chalk = (await import("chalk")).default;
|
|
814
|
+
console.log(chalk.bold(`Detected(${event}): ${chalk.blue(relativePath)} - Restarting...`));
|
|
815
|
+
process.kill(process.pid, "SIGUSR2");
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
await this.handleFileChange(event, absolutePath);
|
|
819
|
+
} catch (e) {
|
|
820
|
+
console.error(e);
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
async runScript(fn) {
|
|
825
|
+
await this.init(true, false, undefined, false);
|
|
826
|
+
try {
|
|
827
|
+
await fn();
|
|
828
|
+
} finally {
|
|
829
|
+
await this.destroy();
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
async registerPlugins(server, plugins) {
|
|
833
|
+
if (!plugins) {
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
if (plugins.compress) {
|
|
837
|
+
const compressPlugin = (await import("@fastify/compress")).default;
|
|
838
|
+
const defaultOptions = {
|
|
839
|
+
threshold: 1024,
|
|
840
|
+
encodings: [
|
|
841
|
+
"br",
|
|
842
|
+
"gzip",
|
|
843
|
+
"deflate"
|
|
844
|
+
]
|
|
845
|
+
};
|
|
846
|
+
if (plugins.compress === true) {
|
|
847
|
+
server.register(compressPlugin, defaultOptions);
|
|
848
|
+
} else {
|
|
849
|
+
server.register(compressPlugin, {
|
|
850
|
+
...defaultOptions,
|
|
851
|
+
...plugins.compress
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
const pluginsModules = {
|
|
856
|
+
cors: "@fastify/cors",
|
|
857
|
+
formbody: "@fastify/formbody",
|
|
858
|
+
multipart: "@fastify/multipart",
|
|
859
|
+
qs: "fastify-qs",
|
|
860
|
+
sse: "fastify-sse-v2",
|
|
861
|
+
static: "@fastify/static"
|
|
862
|
+
};
|
|
863
|
+
const registerPlugin = async (key, pluginName) => {
|
|
864
|
+
const option = plugins[key];
|
|
865
|
+
if (!option) return;
|
|
866
|
+
if (option === true) {
|
|
867
|
+
server.register((await import(pluginName)).default);
|
|
868
|
+
} else {
|
|
869
|
+
server.register((await import(pluginName)).default, option);
|
|
870
|
+
}
|
|
871
|
+
};
|
|
872
|
+
for (const [key, pluginName] of Object.entries(pluginsModules)) {
|
|
873
|
+
await registerPlugin(key, pluginName);
|
|
874
|
+
}
|
|
875
|
+
if (plugins.custom) {
|
|
876
|
+
plugins.custom(server);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* better-auth 라우트를 등록합니다.
|
|
881
|
+
* /api/auth/* 경로로 인증 API가 자동 등록됩니다.
|
|
882
|
+
*/
|
|
883
|
+
async registerBetterAuth(server, options) {
|
|
884
|
+
if (!options) return;
|
|
885
|
+
const basePath = options.basePath ?? "/api/auth";
|
|
886
|
+
server.route({
|
|
887
|
+
method: ["GET", "POST"],
|
|
888
|
+
url: `${basePath}/*`,
|
|
889
|
+
handler: async (request, reply) => {
|
|
890
|
+
const url = new URL(request.url, `http://${request.headers.host}`);
|
|
891
|
+
const headers = convertFastifyHeadersToStandard(request.headers);
|
|
892
|
+
const req = new Request(url.toString(), {
|
|
893
|
+
method: request.method,
|
|
894
|
+
headers,
|
|
895
|
+
...request.body ? { body: JSON.stringify(request.body) } : {}
|
|
896
|
+
});
|
|
897
|
+
const response = await this.auth.handler(req);
|
|
898
|
+
reply.status(response.status);
|
|
899
|
+
response.headers.forEach((value, key) => {
|
|
900
|
+
reply.header(key, value);
|
|
901
|
+
});
|
|
902
|
+
return reply.send(response.body ? await response.text() : null);
|
|
903
|
+
}
|
|
904
|
+
});
|
|
905
|
+
const chalk = (await import("chalk")).default;
|
|
906
|
+
console.log(chalk.green(`✓ better-auth registered at ${basePath}/*`));
|
|
907
|
+
}
|
|
908
|
+
async initializeCache(config, forTesting) {
|
|
909
|
+
const { setCacheManagerRef } = await import("../cache/decorator.js");
|
|
910
|
+
if (forTesting) {
|
|
911
|
+
const { createTestCacheManager } = await import("../cache/cache-manager.js");
|
|
912
|
+
this._cache = createTestCacheManager();
|
|
913
|
+
setCacheManagerRef(this._cache);
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
if (!config) {
|
|
917
|
+
setCacheManagerRef(null);
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
const { createCacheManager } = await import("../cache/cache-manager.js");
|
|
921
|
+
this._cache = createCacheManager(config);
|
|
922
|
+
setCacheManagerRef(this._cache);
|
|
923
|
+
}
|
|
924
|
+
async initializeWorkflows(options) {
|
|
925
|
+
const { WorkflowManager } = await import("../tasks/workflow-manager.js");
|
|
926
|
+
this._workflows = new WorkflowManager(DB.getDBConfig("w"));
|
|
927
|
+
if (!options) {
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
const enableWorker = options.enableWorker ?? isDaemonServer();
|
|
931
|
+
const defaultWorkerOptions = {
|
|
932
|
+
concurrency: os.cpus().length - 1,
|
|
933
|
+
usePubSub: true,
|
|
934
|
+
listenDelay: 500
|
|
935
|
+
};
|
|
936
|
+
if (enableWorker) {
|
|
937
|
+
this.workflows.setupWorker({
|
|
938
|
+
...defaultWorkerOptions,
|
|
939
|
+
...options.workerOptions
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
async boot(server, options) {
|
|
944
|
+
const port = options.listen?.port ?? 3e3;
|
|
945
|
+
const host = options.listen?.host ?? "localhost";
|
|
946
|
+
server.addHook("onClose", async () => {
|
|
947
|
+
await options.lifecycle?.onShutdown?.(server);
|
|
948
|
+
await this.workflows.destroy();
|
|
949
|
+
await this.destroy();
|
|
950
|
+
});
|
|
951
|
+
const shutdown = async () => {
|
|
952
|
+
try {
|
|
953
|
+
await server.close();
|
|
954
|
+
process.exit(0);
|
|
955
|
+
} catch (err) {
|
|
956
|
+
console.error("Error during shutdown:", err);
|
|
957
|
+
process.exit(1);
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
process.on("SIGINT", shutdown);
|
|
961
|
+
process.on("SIGTERM", shutdown);
|
|
962
|
+
if (options.lifecycle?.onError) {
|
|
963
|
+
server.setErrorHandler(options.lifecycle?.onError);
|
|
964
|
+
}
|
|
965
|
+
server.listen({
|
|
966
|
+
port,
|
|
967
|
+
host
|
|
968
|
+
}).then(async () => {
|
|
969
|
+
await this.workflows.startWorker();
|
|
970
|
+
await options.lifecycle?.onStart?.(server);
|
|
971
|
+
}).catch(async (err) => {
|
|
972
|
+
const chalk = (await import("chalk")).default;
|
|
973
|
+
console.error(chalk.red("Failed to start server:", err));
|
|
974
|
+
await shutdown();
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
async handleFileChange(event, filePath) {
|
|
978
|
+
if (this.pendingFiles.length === 0) {
|
|
979
|
+
this.hmrStartTime = Date.now();
|
|
980
|
+
}
|
|
981
|
+
this.pendingFiles.push(filePath);
|
|
982
|
+
const relativePath = path.relative(this.apiRootPath, filePath);
|
|
983
|
+
const chalk = (await import("chalk")).default;
|
|
984
|
+
console.log(chalk.bold(`Detected(${event}): ${chalk.blue(relativePath)}`));
|
|
985
|
+
await this.syncer.syncFromWatcher(event, filePath);
|
|
986
|
+
this.pendingFiles = this.pendingFiles.slice(1);
|
|
987
|
+
if (this.pendingFiles.length === 0) {
|
|
988
|
+
await this.finishHMR();
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
async finishHMR() {
|
|
992
|
+
await this.syncer.renewChecksums();
|
|
993
|
+
const endTime = Date.now();
|
|
994
|
+
const totalTime = endTime - this.hmrStartTime;
|
|
995
|
+
const [chalk, { centerText }] = await Promise.all([(await import("chalk")).default, import("../utils/console-util.js")]);
|
|
996
|
+
const msg = `HMR Done! ${chalk.bold.white(`${totalTime}ms`)}`;
|
|
997
|
+
console.log(chalk.black.bgGreen(centerText(msg)));
|
|
998
|
+
}
|
|
999
|
+
async destroy() {
|
|
1000
|
+
const { BaseModel } = await import("../database/base-model.js");
|
|
1001
|
+
await BaseModel.destroy();
|
|
1002
|
+
await Promise.allSettled([
|
|
1003
|
+
this._workflows?.destroy() ?? Promise.resolve(),
|
|
1004
|
+
this._cache?.disconnect() ?? Promise.resolve(),
|
|
1005
|
+
this._devVitestManager?.shutdown() ?? Promise.resolve(),
|
|
1006
|
+
this.watcher?.close() ?? Promise.resolve(),
|
|
1007
|
+
dispose()
|
|
1008
|
+
]);
|
|
1009
|
+
}
|
|
1010
|
+
};
|
|
1011
|
+
Sonamu = new SonamuClass();
|
|
1012
|
+
}));
|
|
1135
1013
|
|
|
1136
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hcGkvc29uYW11LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGRpc3Bvc2UgYXMgbG9ndGFwZURpc3Bvc2UgfSBmcm9tIFwiQGxvZ3RhcGUvbG9ndGFwZVwiO1xuaW1wb3J0IGFzc2VydCBmcm9tIFwiYXNzZXJ0XCI7XG5pbXBvcnQgeyBBc3luY0xvY2FsU3RvcmFnZSB9IGZyb20gXCJhc3luY19ob29rc1wiO1xuaW1wb3J0IHR5cGUgeyBBdXRoIH0gZnJvbSBcImJldHRlci1hdXRoXCI7XG5pbXBvcnQgdHlwZSB7IEZTV2F0Y2hlciB9IGZyb20gXCJjaG9raWRhclwiO1xuaW1wb3J0IHR5cGUgeyBGYXN0aWZ5SW5zdGFuY2UsIEZhc3RpZnlSZXBseSwgRmFzdGlmeVJlcXVlc3QgfSBmcm9tIFwiZmFzdGlmeVwiO1xuaW1wb3J0IGZzIGZyb20gXCJmcy9wcm9taXNlc1wiO1xuaW1wb3J0IHR5cGUgeyBJbmNvbWluZ01lc3NhZ2UsIFNlcnZlciwgU2VydmVyUmVzcG9uc2UgfSBmcm9tIFwiaHR0cFwiO1xuaW1wb3J0IG1pbWUsIHsgbG9va3VwIGFzIG1pbWVMb29rdXAgfSBmcm9tIFwibWltZS10eXBlc1wiO1xuaW1wb3J0IG9zIGZyb20gXCJvc1wiO1xuaW1wb3J0IHBhdGggZnJvbSBcInBhdGhcIjtcbmltcG9ydCB0eXBlIHsgWm9kT2JqZWN0IH0gZnJvbSBcInpvZFwiO1xuaW1wb3J0IHtcbiAgQkFTRV9GSUVMRF9NQVBQSU5HUyxcbiAgY29udmVydEZhc3RpZnlIZWFkZXJzVG9TdGFuZGFyZCxcbiAgY3JlYXRlTW9ja1NTRUZhY3RvcnksXG4gIERCLFxuICBpc0RhZW1vblNlcnZlcixcbiAgbWVyZ2UsXG4gIE5vdEZvdW5kRXhjZXB0aW9uLFxufSBmcm9tIFwiLi5cIjtcbmltcG9ydCB0eXBlIHsgQ2FjaGVDb25maWcsIENhY2hlTWFuYWdlciB9IGZyb20gXCIuLi9jYWNoZS90eXBlc1wiO1xuaW1wb3J0IHsgYXBwbHlDYWNoZUhlYWRlcnMsIENhY2hlUHJlc2V0cyB9IGZyb20gXCIuLi9jYWNoZS1jb250cm9sL2NhY2hlLWNvbnRyb2xcIjtcbmltcG9ydCB0eXBlIHsgQ2FjaGVDb250cm9sQ29uZmlnLCBDYWNoZUNvbnRyb2xSZXF1ZXN0IH0gZnJvbSBcIi4uL2NhY2hlLWNvbnRyb2wvdHlwZXNcIjtcbmltcG9ydCB7IHRvRmFzdGlmeUNvbXByZXNzT3B0aW9uIH0gZnJvbSBcIi4uL2NvbXByZXNzL2NvbXByZXNzXCI7XG5pbXBvcnQgdHlwZSB7IENvbXByZXNzT3B0aW9ucyB9IGZyb20gXCIuLi9jb21wcmVzcy90eXBlc1wiO1xuaW1wb3J0IHR5cGUgeyBTb25hbXVEQkNvbmZpZyB9IGZyb20gXCIuLi9kYXRhYmFzZS9kYlwiO1xuaW1wb3J0IHsgU0QgfSBmcm9tIFwiLi4vZGljdC9zZFwiO1xuaW1wb3J0IHR5cGUgeyBMb2NhbGl6ZWRTdHJpbmcgfSBmcm9tIFwiLi4vZGljdC90eXBlc1wiO1xuaW1wb3J0IHsgTmFpdGUgfSBmcm9tIFwiLi4vbmFpdGUvbmFpdGVcIjtcbmltcG9ydCB7IEJ1ZmZlcmVkRmlsZSB9IGZyb20gXCIuLi9zdG9yYWdlL2J1ZmZlcmVkLWZpbGVcIjtcbmltcG9ydCB0eXBlIHsgU3RvcmFnZU1hbmFnZXIgfSBmcm9tIFwiLi4vc3RvcmFnZS9zdG9yYWdlLW1hbmFnZXJcIjtcbmltcG9ydCB0eXBlIHsgS2V5R2VuZXJhdG9yIH0gZnJvbSBcIi4uL3N0b3JhZ2UvdHlwZXNcIjtcbmltcG9ydCB7IFVwbG9hZGVkRmlsZSB9IGZyb20gXCIuLi9zdG9yYWdlL3VwbG9hZGVkLWZpbGVcIjtcbmltcG9ydCB0eXBlIHsgU3luY2VyIH0gZnJvbSBcIi4uL3N5bmNlci9zeW5jZXJcIjtcbmltcG9ydCB0eXBlIHsgV29ya2Zsb3dNYW5hZ2VyIH0gZnJvbSBcIi4uL3Rhc2tzL3dvcmtmbG93LW1hbmFnZXJcIjtcbmltcG9ydCB0eXBlIHsgRGV2Vml0ZXN0TWFuYWdlciB9IGZyb20gXCIuLi90ZXN0aW5nL2Rldi12aXRlc3QtbWFuYWdlclwiO1xuaW1wb3J0IHR5cGUgeyBTb25hbXVGYXN0aWZ5Q29uZmlnIH0gZnJvbSBcIi4uL3R5cGVzL3R5cGVzXCI7XG5pbXBvcnQgeyBleGlzdHMsIGZpbGVFeGlzdHMgfSBmcm9tIFwiLi4vdXRpbHMvZnMtdXRpbHNcIjtcbmltcG9ydCB0eXBlIHsgQWJzb2x1dGVQYXRoIH0gZnJvbSBcIi4uL3V0aWxzL3BhdGgtdXRpbHNcIjtcbmltcG9ydCB0eXBlIHsgU29uYW11Q29uZmlnLCBTb25hbXVTZXJ2ZXJPcHRpb25zLCBTb25hbXVUYXNrT3B0aW9ucyB9IGZyb20gXCIuL2NvbmZpZ1wiO1xuaW1wb3J0IHR5cGUgeyBDb250ZXh0IH0gZnJvbSBcIi4vY29udGV4dFwiO1xuaW1wb3J0IHR5cGUgeyBFeHRlbmRlZEFwaSB9IGZyb20gXCIuL2RlY29yYXRvcnNcIjtcbmltcG9ydCB7IGdldFNlY3JldHMsIHR5cGUgU29uYW11U2VjcmV0cyB9IGZyb20gXCIuL3NlY3JldFwiO1xuXG5jbGFzcyBTb25hbXVDbGFzcyB7XG4gIHB1YmxpYyBpc0luaXRpYWxpemVkOiBib29sZWFuID0gZmFsc2U7XG4gIHB1YmxpYyBmb3JUZXN0aW5nOiBib29sZWFuID0gZmFsc2U7XG4gIHB1YmxpYyBhc3luY0xvY2FsU3RvcmFnZTogQXN5bmNMb2NhbFN0b3JhZ2U8e1xuICAgIGNvbnRleHQ6IENvbnRleHQ7XG4gIH0+ID0gbmV3IEFzeW5jTG9jYWxTdG9yYWdlKCk7XG5cbiAgcHVibGljIGdldENvbnRleHQoKTogQ29udGV4dCB7XG4gICAgY29uc3Qgc3RvcmUgPSB0aGlzLmFzeW5jTG9jYWxTdG9yYWdlLmdldFN0b3JlKCk7XG4gICAgaWYgKHN0b3JlPy5jb250ZXh0KSB7XG4gICAgICByZXR1cm4gc3RvcmUuY29udGV4dCBhcyBDb250ZXh0O1xuICAgIH1cblxuICAgIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViA9PT0gXCJ0ZXN0XCIpIHtcbiAgICAgIC8vIO2FjOyKpO2MhSDtmZjqsr3sl5DshJwg7Luo7YWN7Iqk7Yq46rCAIOyjvOyeheuQmOyngCDslYrsnYAg6rK97JqwIOu5iCDsu6jthY3siqTtirgg66as7YS0XG4gICAgICByZXR1cm4ge1xuICAgICAgICByZXF1ZXN0OiBudWxsLFxuICAgICAgICByZXBseTogbnVsbCxcbiAgICAgICAgaGVhZGVyczoge30sXG4gICAgICAgIGNyZWF0ZVNTRTogKHNjaGVtYTogWm9kT2JqZWN0KSA9PiBjcmVhdGVNb2NrU1NFRmFjdG9yeShzY2hlbWEpLFxuICAgICAgICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vRXhwbGljaXRBbnk6IO2FjOyKpO2MhSDtmZjqsr3sl5DshJwg7Luo7YWN7Iqk7Yq46rCAIOyjvOyeheuQmOyngCDslYrsnYAg6rK97JqwIOu5iCDsu6jthY3siqTtirgg66as7YS0XG4gICAgICAgIG5haXRlU3RvcmU6IG5ldyBNYXA8c3RyaW5nLCBhbnk+KCksXG4gICAgICB9IGFzIHVua25vd24gYXMgQ29udGV4dDtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiU29uYW11IGNhbm5vdCBmaW5kIGNvbnRleHRcIik7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBfYXBpUm9vdFBhdGg6IEFic29sdXRlUGF0aCB8IG51bGwgPSBudWxsO1xuICBzZXQgYXBpUm9vdFBhdGgoYXBpUm9vdFBhdGg6IEFic29sdXRlUGF0aCkge1xuICAgIHRoaXMuX2FwaVJvb3RQYXRoID0gYXBpUm9vdFBhdGg7XG4gIH1cbiAgZ2V0IGFwaVJvb3RQYXRoKCk6IEFic29sdXRlUGF0aCB7XG4gICAgaWYgKHRoaXMuX2FwaVJvb3RQYXRoID09PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJTb25hbXUgaGFzIG5vdCBiZWVuIGluaXRpYWxpemVkXCIpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fYXBpUm9vdFBhdGg7XG4gIH1cbiAgZ2V0IGFwcFJvb3RQYXRoKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuYXBpUm9vdFBhdGguc3BsaXQocGF0aC5zZXApLnNsaWNlKDAsIC0xKS5qb2luKHBhdGguc2VwKTtcbiAgfVxuXG4gIHByaXZhdGUgX2RiQ29uZmlnOiBTb25hbXVEQkNvbmZpZyB8IG51bGwgPSBudWxsO1xuICBzZXQgZGJDb25maWcoZGJDb25maWc6IFNvbmFtdURCQ29uZmlnKSB7XG4gICAgdGhpcy5fZGJDb25maWcgPSBkYkNvbmZpZztcbiAgfVxuICBnZXQgZGJDb25maWcoKTogU29uYW11REJDb25maWcge1xuICAgIGlmICh0aGlzLl9kYkNvbmZpZyA9PT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiU29uYW11IGhhcyBub3QgYmVlbiBpbml0aWFsaXplZFwiKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuX2RiQ29uZmlnO1xuICB9XG5cbiAgcHJpdmF0ZSBfc3luY2VyOiBTeW5jZXIgfCBudWxsID0gbnVsbDtcbiAgc2V0IHN5bmNlcihzeW5jZXI6IFN5bmNlcikge1xuICAgIHRoaXMuX3N5bmNlciA9IHN5bmNlcjtcbiAgfVxuICBnZXQgc3luY2VyKCk6IFN5bmNlciB7XG4gICAgaWYgKHRoaXMuX3N5bmNlciA9PT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiU29uYW11IGhhcyBub3QgYmVlbiBpbml0aWFsaXplZFwiKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuX3N5bmNlcjtcbiAgfVxuXG4gIHByaXZhdGUgX2NvbmZpZzogU29uYW11Q29uZmlnIHwgbnVsbCA9IG51bGw7XG4gIHNldCBjb25maWcoY29uZmlnOiBTb25hbXVDb25maWcpIHtcbiAgICB0aGlzLl9jb25maWcgPSBjb25maWc7XG4gIH1cbiAgZ2V0IGNvbmZpZygpOiBTb25hbXVDb25maWcge1xuICAgIGlmICh0aGlzLl9jb25maWcgPT09IG51bGwpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIlNvbmFtdSBoYXMgbm90IGJlZW4gaW5pdGlhbGl6ZWRcIik7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9jb25maWc7XG4gIH1cblxuICBwdWJsaWMgcmVhZG9ubHkgc2VjcmV0czogU29uYW11U2VjcmV0cyA9IGdldFNlY3JldHMoKTtcblxuICBwcml2YXRlIF9zdG9yYWdlOiBTdG9yYWdlTWFuYWdlciB8IG51bGwgPSBudWxsO1xuICAvKipcbiAgICogU3RvcmFnZU1hbmFnZXIg7J247Iqk7YS07IqkXG4gICAqL1xuICBnZXQgc3RvcmFnZSgpOiBTdG9yYWdlTWFuYWdlciB7XG4gICAgaWYgKCF0aGlzLl9zdG9yYWdlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJTdG9yYWdlIGhhcyBub3QgYmVlbiBpbml0aWFsaXplZC4gQ2hlY2sgc3RvcmFnZSBjb25maWcuXCIpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fc3RvcmFnZTtcbiAgfVxuXG4gIHByaXZhdGUgX2NhY2hlOiBDYWNoZU1hbmFnZXIgfCBudWxsID0gbnVsbDtcbiAgLyoqXG4gICAqIENhY2hlTWFuYWdlciDsnbjsiqTthLTsiqQgKEJlbnRvQ2FjaGUpXG4gICAqL1xuICBnZXQgY2FjaGUoKTogQ2FjaGVNYW5hZ2VyIHtcbiAgICBpZiAoIXRoaXMuX2NhY2hlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJDYWNoZSBoYXMgbm90IGJlZW4gaW5pdGlhbGl6ZWQuIENoZWNrIGNhY2hlIGNvbmZpZyBpbiBzb25hbXUuY29uZmlnLnRzLlwiKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuX2NhY2hlO1xuICB9XG5cbiAgcHJpdmF0ZSBfd29ya2Zsb3dzOiBXb3JrZmxvd01hbmFnZXIgfCBudWxsID0gbnVsbDtcbiAgZ2V0IHdvcmtmbG93cygpOiBXb3JrZmxvd01hbmFnZXIge1xuICAgIGlmICh0aGlzLl93b3JrZmxvd3MgPT09IG51bGwpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIlNvbmFtdSBoYXMgbm90IGJlZW4gaW5pdGlhbGl6ZWRcIik7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuX3dvcmtmbG93cztcbiAgfVxuXG4gIHByaXZhdGUgX2F1dGg6IEF1dGggfCBudWxsID0gbnVsbDtcbiAgZ2V0IGF1dGgoKTogQXV0aCB7XG4gICAgaWYgKCF0aGlzLl9hdXRoKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJBdXRoIGhhcyBub3QgYmVlbiBpbml0aWFsaXplZC4gQ2hlY2sgYXV0aCBjb25maWcgaW4gc29uYW11LmNvbmZpZy50cy5cIik7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9hdXRoO1xuICB9XG5cbiAgcHJpdmF0ZSBfZGV2Vml0ZXN0TWFuYWdlcjogRGV2Vml0ZXN0TWFuYWdlciB8IG51bGwgPSBudWxsO1xuICBnZXQgZGV2Vml0ZXN0TWFuYWdlcigpOiBEZXZWaXRlc3RNYW5hZ2VyIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMuX2RldlZpdGVzdE1hbmFnZXI7XG4gIH1cbiAgc2V0IGRldlZpdGVzdE1hbmFnZXIobWFuYWdlcjogRGV2Vml0ZXN0TWFuYWdlciB8IG51bGwpIHtcbiAgICB0aGlzLl9kZXZWaXRlc3RNYW5hZ2VyID0gbWFuYWdlcjtcbiAgfVxuXG4gIC8vIEhNUiDsspjrpqxcbiAgcHVibGljIHdhdGNoZXI6IEZTV2F0Y2hlciB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIHBlbmRpbmdGaWxlczogc3RyaW5nW10gPSBbXTtcbiAgcHJpdmF0ZSBobXJTdGFydFRpbWU6IG51bWJlciA9IDA7XG5cbiAgcHVibGljIHNlcnZlcjogRmFzdGlmeUluc3RhbmNlIHwgbnVsbCA9IG51bGw7XG5cbiAgYXN5bmMgaW5pdEZvclRlc3RpbmcoKSB7XG4gICAgYXdhaXQgdGhpcy5pbml0KHRydWUsIGZhbHNlLCB1bmRlZmluZWQsIHRydWUpO1xuICB9XG5cbiAgYXN5bmMgaW5pdChcbiAgICBkb1NpbGVudDogYm9vbGVhbiA9IGZhbHNlLFxuICAgIGVuYWJsZVN5bmM6IGJvb2xlYW4gPSB0cnVlLFxuICAgIGFwaVJvb3RQYXRoPzogQWJzb2x1dGVQYXRoLFxuICAgIGZvclRlc3Rpbmc6IGJvb2xlYW4gPSBmYWxzZSxcbiAgKSB7XG4gICAgdGhpcy5mb3JUZXN0aW5nID0gZm9yVGVzdGluZztcblxuICAgIGlmICh0aGlzLmlzSW5pdGlhbGl6ZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoIWRvU2lsZW50KSB7XG4gICAgICBjb25zdCBjaGFsayA9IChhd2FpdCBpbXBvcnQoXCJjaGFsa1wiKSkuZGVmYXVsdDtcbiAgICAgIGNvbnNvbGUudGltZShjaGFsay5jeWFuKGBTb25hbXUuaW5pdCR7Zm9yVGVzdGluZyA/IFwiIGZvciB0ZXN0aW5nXCIgOiBcIlwifWApKTtcbiAgICB9XG5cbiAgICAvLyBBUEkg66Oo7Yq4IO2MqOyKpFxuICAgIGNvbnN0IHsgZmluZEFwaVJvb3RQYXRoIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi91dGlscy91dGlsc1wiKTtcbiAgICB0aGlzLmFwaVJvb3RQYXRoID0gYXBpUm9vdFBhdGggPz8gZmluZEFwaVJvb3RQYXRoKCk7XG5cbiAgICAvLyDshKTsoJXsnYQg66Gc65Sp7ZWY64qUIOqyg+u2gO2EsCDsi5zsnpFcbiAgICBjb25zdCB7IGxvYWRDb25maWcgfSA9IGF3YWl0IGltcG9ydChcIi4vY29uZmlnXCIpO1xuICAgIHRoaXMuY29uZmlnID0gYXdhaXQgbG9hZENvbmZpZyh0aGlzLmFwaVJvb3RQYXRoKTtcbiAgICAvLyBzb25hbXUuY29uZmlnLnRzIOq4sOuzuOqwkiDshKTsoJVcbiAgICB0aGlzLmNvbmZpZy5kYXRhYmFzZS5kYXRhYmFzZSA9IHRoaXMuY29uZmlnLmRhdGFiYXNlLmRhdGFiYXNlID8/IFwicGdcIjtcbiAgICB0aGlzLmNvbmZpZy5kYXRhYmFzZS5kZWZhdWx0T3B0aW9ucy5jbGllbnQgPSB0aGlzLmNvbmZpZy5kYXRhYmFzZS5kYXRhYmFzZSA/PyBcInBnXCI7XG5cbiAgICAvLyDroZzquYUg7ISk7KCVXG4gICAgY29uc3QgeyBjb25maWd1cmVMb2dUYXBlIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9sb2dnZXIvY29uZmlndXJlXCIpO1xuICAgIGlmICh0aGlzLmNvbmZpZy5sb2dnaW5nICE9PSBmYWxzZSkge1xuICAgICAgYXdhaXQgY29uZmlndXJlTG9nVGFwZSh7XG4gICAgICAgIC4uLnRoaXMuY29uZmlnLmxvZ2dpbmcsXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICAvLyBEQiDroZzrk5xcbiAgICBjb25zdCB7IERCIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9kYXRhYmFzZS9kYlwiKTtcbiAgICB0aGlzLmRiQ29uZmlnID0gREIuZ2VuZXJhdGVEQkNvbmZpZyh0aGlzLmNvbmZpZy5kYXRhYmFzZSk7XG4gICAgaWYgKCFkb1NpbGVudCkge1xuICAgICAgY29uc3QgY2hhbGsgPSAoYXdhaXQgaW1wb3J0KFwiY2hhbGtcIikpLmRlZmF1bHQ7XG4gICAgICBjb25zb2xlLmxvZyhjaGFsay5ncmVlbihcIkRCIENvbmZpZyBMb2FkZWQhXCIpKTtcbiAgICB9XG5cbiAgICAvLyBFbnRpdHkg66Gc65OcXG4gICAgLy8g7YWM7Iqk7Yq47JeQ7ISc64+EIEVudGl0eSDsoJXrs7TripQg7ZWE7JqU7ZWp64uI64ukLlxuICAgIC8vIHVwc2VydOqwgCDsoJzrjIDroZwg7J6R64+Z7ZWY66Ck66m0IGVudGl0eeydmCB1bmlxdWUgaW5kZXgg7KCV67O06rCAIO2VhOyalO2VmOq4sCDrlYzrrLjsnoXri4jri6QuXG4gICAgY29uc3QgeyBFbnRpdHlNYW5hZ2VyIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9lbnRpdHkvZW50aXR5LW1hbmFnZXJcIik7XG4gICAgYXdhaXQgRW50aXR5TWFuYWdlci5hdXRvbG9hZChkb1NpbGVudCk7XG5cbiAgICAvLyBDYWNoZSDstIjquLDtmZRcbiAgICBhd2FpdCB0aGlzLmluaXRpYWxpemVDYWNoZSh0aGlzLmNvbmZpZy5zZXJ2ZXIuY2FjaGUsIGZvclRlc3RpbmcpO1xuXG4gICAgLy8gQmV0dGVyQXV0aCDstIjquLDtmZRcbiAgICBjb25zdCBhdXRoQ29uZmlnID0gdGhpcy5jb25maWcuc2VydmVyLmF1dGg7XG4gICAgaWYgKGF1dGhDb25maWcpIHtcbiAgICAgIC8vIOyCrOyaqeyekCDshKTsoJXqs7wg6riw67O46rCS7J2EIG1lcmdlXG4gICAgICBjb25zdCBtZXJnZWRGaWVsZE1hcHBpbmdzID0gbWVyZ2UoQkFTRV9GSUVMRF9NQVBQSU5HUywgYXV0aENvbmZpZyk7XG5cbiAgICAgIC8vIGJldHRlci1hdXRoIOyduOyKpO2EtOyKpCDsg53shLFcbiAgICAgIGNvbnN0IHsgYmV0dGVyQXV0aCB9ID0gYXdhaXQgaW1wb3J0KFwiYmV0dGVyLWF1dGhcIik7XG4gICAgICBjb25zdCB7IHNvbmFtdUtuZXhBZGFwdGVyIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9hdXRoL2tuZXgtYWRhcHRlclwiKTtcblxuICAgICAgdGhpcy5fYXV0aCA9IGJldHRlckF1dGgoe1xuICAgICAgICBkYXRhYmFzZTogc29uYW11S25leEFkYXB0ZXIoKSxcbiAgICAgICAgLi4ubWVyZ2VkRmllbGRNYXBwaW5ncyxcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIO2FjOyKpO2MheyduCDqsr3smrAg7Iux7YGsIOyXhuydtCDspJHri6hcbiAgICBpZiAoZm9yVGVzdGluZykge1xuICAgICAgdGhpcy5pc0luaXRpYWxpemVkID0gdHJ1ZTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBUYXNrIOuTseuhnVxuICAgIGF3YWl0IHRoaXMuaW5pdGlhbGl6ZVdvcmtmbG93cyh0aGlzLmNvbmZpZy50YXNrcyk7XG5cbiAgICAvLyBTeW5jZXJcbiAgICBjb25zdCB7IFN5bmNlciB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vc3luY2VyL3N5bmNlclwiKTtcbiAgICB0aGlzLnN5bmNlciA9IG5ldyBTeW5jZXIoKTtcblxuICAgIC8vIEF1dG9sb2FkOiBNb2RlbHMgLyBUeXBlcyAvIEFQSXMgLyBXb3JrZmxvd3MgLyBUZW1wbGF0ZXMgLyBTU1IgUm91dGVzXG4gICAgYXdhaXQgdGhpcy5zeW5jZXIuYXV0b2xvYWRUeXBlcygpO1xuICAgIGF3YWl0IHRoaXMuc3luY2VyLmF1dG9sb2FkTW9kZWxzKCk7XG4gICAgYXdhaXQgdGhpcy5zeW5jZXIuYXV0b2xvYWRBcGlzKCk7XG4gICAgYXdhaXQgdGhpcy5zeW5jZXIuYXV0b2xvYWRXb3JrZmxvd3MoKTtcbiAgICBjb25zdCB7IFRlbXBsYXRlTWFuYWdlciB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vdGVtcGxhdGVcIik7XG4gICAgYXdhaXQgVGVtcGxhdGVNYW5hZ2VyLmF1dG9sb2FkKCk7XG4gICAgYXdhaXQgdGhpcy5zeW5jZXIuYXV0b2xvYWRTU1JSb3V0ZXMoKTtcblxuICAgIGNvbnN0IHsgaXNMb2NhbCwgaXNUZXN0IH0gPSBhd2FpdCBpbXBvcnQoXCIuLi91dGlscy9jb250cm9sbGVyXCIpO1xuICAgIGlmIChpc0xvY2FsKCkpIHtcbiAgICAgIC8vIOuhnOy7rOyXkOyEnOuKlCDsvZTrk5wg7IOd7ISx7J2EIOychO2VtCBCaW9tZSDshYvsl4XsnbQg7ZWE7JqU7ZWoICjtmITsnqwgYXBpUm9vdFBhdGgg7KCE64us7ZWY7JesIOyLpO2WiSlcbiAgICAgIChhd2FpdCBpbXBvcnQoXCIuLi91dGlscy9mb3JtYXR0ZXJcIikpLnNldHVwQmlvbWUodGhpcy5hcGlSb290UGF0aCk7XG4gICAgfVxuXG4gICAgY29uc3QgeyBpc0hvdFJlbG9hZFNlcnZlciB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vdXRpbHMvY29udHJvbGxlclwiKTtcbiAgICBpZiAoaXNMb2NhbCgpICYmICFpc1Rlc3QoKSAmJiBpc0hvdFJlbG9hZFNlcnZlcigpICYmIGVuYWJsZVN5bmMpIHtcbiAgICAgIGF3YWl0IHRoaXMuc3luY2VyLnN5bmMoKTtcbiAgICAgIGF3YWl0IHRoaXMuc3RhcnRXYXRjaGVyKCk7XG4gICAgfVxuXG4gICAgdGhpcy5pc0luaXRpYWxpemVkID0gdHJ1ZTtcbiAgICBpZiAoIWRvU2lsZW50KSB7XG4gICAgICBjb25zdCBjaGFsayA9IChhd2FpdCBpbXBvcnQoXCJjaGFsa1wiKSkuZGVmYXVsdDtcbiAgICAgIGNvbnNvbGUudGltZUVuZChjaGFsay5jeWFuKFwiU29uYW11LmluaXRcIikpO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGNyZWF0ZVNlcnZlcihpbml0T3B0aW9ucz86IHsgZW5hYmxlU3luYz86IGJvb2xlYW47IGRvU2lsZW50PzogYm9vbGVhbiB9KSB7XG4gICAgaWYgKHRoaXMuaXNJbml0aWFsaXplZCA9PT0gZmFsc2UpIHtcbiAgICAgIGF3YWl0IHRoaXMuaW5pdChpbml0T3B0aW9ucz8uZG9TaWxlbnQsIGluaXRPcHRpb25zPy5lbmFibGVTeW5jKTtcbiAgICB9XG5cbiAgICBjb25zdCBvcHRpb25zID0gdGhpcy5jb25maWcuc2VydmVyO1xuICAgIGNvbnN0IHsgZGVmYXVsdDogZmFzdGlmeSB9ID0gYXdhaXQgaW1wb3J0KFwiZmFzdGlmeVwiKTtcbiAgICBjb25zdCB7IGdldExvZ1RhcGVGYXN0aWZ5TG9nZ2VyIH0gPSBhd2FpdCBpbXBvcnQoXCJAbG9ndGFwZS9mYXN0aWZ5XCIpO1xuICAgIGNvbnN0IHNlcnZlciA9IGZhc3RpZnkoe1xuICAgICAgLi4ub3B0aW9ucy5mYXN0aWZ5LFxuICAgICAgbG9nZ2VyOlxuICAgICAgICB0aGlzLmNvbmZpZy5sb2dnaW5nICE9PSBmYWxzZVxuICAgICAgICAgID8gZ2V0TG9nVGFwZUZhc3RpZnlMb2dnZXIoe1xuICAgICAgICAgICAgICBjYXRlZ29yeTogdGhpcy5jb25maWcubG9nZ2luZz8uZmFzdGlmeUNhdGVnb3J5ID8/IFtcImZhc3RpZnlcIl0sXG4gICAgICAgICAgICB9KVxuICAgICAgICAgIDogdW5kZWZpbmVkLFxuICAgIH0pO1xuICAgIHRoaXMuc2VydmVyID0gc2VydmVyO1xuXG4gICAgLy8gU3RvcmFnZSDshKTsoJUg4oaSIFN0b3JhZ2VNYW5hZ2VyIOyDneyEsVxuICAgIGlmIChvcHRpb25zLnN0b3JhZ2UpIHtcbiAgICAgIGNvbnN0IHsgU3RvcmFnZU1hbmFnZXIgfSA9IGF3YWl0IGltcG9ydChcIi4uL3N0b3JhZ2Uvc3RvcmFnZS1tYW5hZ2VyXCIpO1xuICAgICAgdGhpcy5fc3RvcmFnZSA9IG5ldyBTdG9yYWdlTWFuYWdlcihvcHRpb25zLnN0b3JhZ2UpO1xuICAgIH1cblxuICAgIC8vIO2UjOufrOq3uOyduCDrk7HroZ1cbiAgICBpZiAob3B0aW9ucy5wbHVnaW5zKSB7XG4gICAgICBhd2FpdCB0aGlzLnJlZ2lzdGVyUGx1Z2lucyhzZXJ2ZXIsIG9wdGlvbnMucGx1Z2lucyk7XG4gICAgfVxuXG4gICAgaWYgKG9wdGlvbnMuYXV0aCkge1xuICAgICAgYXdhaXQgdGhpcy5yZWdpc3RlckJldHRlckF1dGgoc2VydmVyLCBvcHRpb25zLmF1dGgpO1xuICAgIH1cblxuICAgIC8vIEFQSSDrnbzsmrDtjIUg7ISk7KCVXG4gICAgYXdhaXQgdGhpcy53aXRoRmFzdGlmeShzZXJ2ZXIsIG9wdGlvbnMuYXBpQ29uZmlnLCB7XG4gICAgICBlbmFibGVTeW5jOiBpbml0T3B0aW9ucz8uZW5hYmxlU3luYyxcbiAgICAgIGRvU2lsZW50OiBpbml0T3B0aW9ucz8uZG9TaWxlbnQsXG4gICAgfSk7XG5cbiAgICAvLyDshJzrsoQg7Iuc7J6RXG4gICAgYXdhaXQgdGhpcy5ib290KHNlcnZlciwgb3B0aW9ucyk7XG5cbiAgICByZXR1cm4gc2VydmVyO1xuICB9XG5cbiAgYXN5bmMgd2l0aEZhc3RpZnkoXG4gICAgc2VydmVyOiBGYXN0aWZ5SW5zdGFuY2U8U2VydmVyLCBJbmNvbWluZ01lc3NhZ2UsIFNlcnZlclJlc3BvbnNlPixcbiAgICBjb25maWc6IFNvbmFtdUZhc3RpZnlDb25maWcsXG4gICAgb3B0aW9ucz86IHtcbiAgICAgIGVuYWJsZVN5bmM/OiBib29sZWFuO1xuICAgICAgZG9TaWxlbnQ/OiBib29sZWFuO1xuICAgIH0sXG4gICkge1xuICAgIGlmICh0aGlzLmlzSW5pdGlhbGl6ZWQgPT09IGZhbHNlKSB7XG4gICAgICBhd2FpdCB0aGlzLmluaXQob3B0aW9ucz8uZG9TaWxlbnQsIG9wdGlvbnM/LmVuYWJsZVN5bmMpO1xuICAgIH1cblxuICAgIHRoaXMuc2VydmVyID0gc2VydmVyO1xuXG4gICAgLy8gdGltZXpvbmUg7ISk7KCVXG4gICAgY29uc3QgdGltZXpvbmUgPSB0aGlzLmNvbmZpZy5hcGkudGltZXpvbmU7XG4gICAgaWYgKHRpbWV6b25lKSB7XG4gICAgICAvLyDtg4DsnoTsobTsl5Ag66ee6rKMIOydkeuLtSDrgqDsp5wg7Iqk7Yq466eB7J2EIOuzgO2ZmO2VtOyjvOyWtOyVvCDtlanri4jri6QuXG4gICAgICAvLyDqsIDroLkgdGltZXpvbmXsnbQgXCJBc2lhL1Nlb3VsXCIg7J2066m0XG4gICAgICAvLyBcIjIwMjUtMTEtMjFUMDA6MDA6MDAuMDAwWlwiIOulvCBcIjIwMjUtMTEtMjFUMDk6MDA6MDArMDk6MDBcIiDsnLzroZwg67OA7ZmY7ZW07KO87Ja07JW8IO2VqeuLiOuLpC5cbiAgICAgIGNvbnN0IHsgZm9ybWF0SW5UaW1lWm9uZSB9ID0gYXdhaXQgaW1wb3J0KFwiZGF0ZS1mbnMtdHpcIik7XG5cbiAgICAgIC8vIElTTyA4NjAxIOuCoOynnCDtmJXsi50g7KCV6rec7IudICjsmIg6IDIwMjQtMDEtMTVUMDk6MzA6MDAuMDAwWilcbiAgICAgIGNvbnN0IElTT19EQVRFX1JFR0VYID0gL15cXGR7NH0tXFxkezJ9LVxcZHsyfVRcXGR7Mn06XFxkezJ9OlxcZHsyfShcXC5cXGR7M30pP1okLztcblxuICAgICAgLy8gVOulvCDrkZjrn6zsi7wg7J6R7J2A65Sw7Ji07ZGc6rCAIOyXhuuLpOuptCBcIjIwMjUtMTEtMTkxNzYzNTQ2MTg5MDAwMTg6NTY6MjkrMDk6MDBcIuyZgCDqsJnsnYAg6rKw6rO86rCAIOuCmOyYteuLiOuLpC5cbiAgICAgIC8vIOydtOuKlCBkYXRlLWZucyDtirnsnoXri4jri6QuXG4gICAgICAvLyDsnbTroIfqsowg7ZW064+EIOq0nOywruyKteuLiOuLpC4gXCIyMDI1LTExLTE5VDE4OjU2OjI5KzA5OjAwXCIg66qo7JaR7Jy866GcIOyemCDrgpjsmLXri4jri6QuXG4gICAgICBjb25zdCBEQVRFX0ZPUk1BVCA9IFwieXl5eS1NTS1kZCdUJ0hIOm1tOnNzWFhYXCI7XG5cbiAgICAgIHNlcnZlci5zZXRSZXBseVNlcmlhbGl6ZXIoKHBheWxvYWQpID0+IHtcbiAgICAgICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KHBheWxvYWQsIChfa2V5LCB2YWx1ZSkgPT4ge1xuICAgICAgICAgIGlmICh0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIgJiYgSVNPX0RBVEVfUkVHRVgudGVzdCh2YWx1ZSkpIHtcbiAgICAgICAgICAgIHJldHVybiBmb3JtYXRJblRpbWVab25lKFxuICAgICAgICAgICAgICBuZXcgRGF0ZSh2YWx1ZSksXG4gICAgICAgICAgICAgIHRpbWV6b25lIGFzIGAke3N0cmluZ30vJHtzdHJpbmd9YCxcbiAgICAgICAgICAgICAgREFURV9GT1JNQVQsXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gdmFsdWU7XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG4gICAgICBpZiAoIW9wdGlvbnM/LmRvU2lsZW50KSB7XG4gICAgICAgIGNvbnN0IGNoYWxrID0gKGF3YWl0IGltcG9ydChcImNoYWxrXCIpKS5kZWZhdWx0O1xuICAgICAgICBjb25zb2xlLmxvZyhjaGFsay5ncmVlbihgVGltZXpvbmUgc2V0IHRvICR7dGltZXpvbmV9YCkpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIOyghOyytCDrnbzsmrDtjIUg66as7Iqk7Yq4XG4gICAgc2VydmVyLmdldChcbiAgICAgIGAke3RoaXMuY29uZmlnLmFwaS5yb3V0ZS5wcmVmaXh9L3JvdXRlc2AsXG4gICAgICBhc3luYyAoX3JlcXVlc3QsIF9yZXBseSk6IFByb21pc2U8dHlwZW9mIHRoaXMuc3luY2VyLmFwaXM+ID0+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc3luY2VyLmFwaXM7XG4gICAgICB9LFxuICAgICk7XG5cbiAgICAvLyBIZWFsdGhjaGVjayBBUElcbiAgICBzZXJ2ZXIuZ2V0KFxuICAgICAgYCR7dGhpcy5jb25maWcuYXBpLnJvdXRlLnByZWZpeH0vaGVhbHRoY2hlY2tgLFxuICAgICAgYXN5bmMgKF9yZXF1ZXN0LCBfcmVwbHkpOiBQcm9taXNlPHN0cmluZz4gPT4ge1xuICAgICAgICByZXR1cm4gXCJva1wiO1xuICAgICAgfSxcbiAgICApO1xuXG4gICAgLy8gU29uYW11IFVJIEFQSSAo66Gc7LusIO2ZmOqyveyXkOyEnOunjClcbiAgICBjb25zdCB7IGlzTG9jYWwgfSA9IGF3YWl0IGltcG9ydChcIi4uL3V0aWxzL2NvbnRyb2xsZXJcIik7XG4gICAgaWYgKGlzTG9jYWwoKSkge1xuICAgICAgY29uc3QgeyBzb25hbXVVSUFwaVBsdWdpbiB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vdWkvYXBpXCIpO1xuICAgICAgc2VydmVyLnJlZ2lzdGVyKHNvbmFtdVVJQXBpUGx1Z2luKTtcbiAgICB9XG5cbiAgICAvLyBEZXZSdW5uZXIg7YWM7Iqk7Yq4IOyXlOuTnO2PrOyduO2KuCAo66Gc7LusIO2ZmOqyvSArIGRldlJ1bm5lciDtmZzshLHtmZQg7IucKVxuICAgIGlmIChpc0xvY2FsKCkgJiYgdGhpcy5jb25maWcudGVzdD8uZGV2UnVubmVyPy5lbmFibGVkKSB7XG4gICAgICBjb25zdCB7IHJlZ2lzdGVyRGV2VGVzdFJvdXRlcyB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vdGVzdGluZy9kZXYtdGVzdC1yb3V0ZXNcIik7XG4gICAgICBhd2FpdCByZWdpc3RlckRldlRlc3RSb3V0ZXMoc2VydmVyLCB0aGlzLmNvbmZpZy50ZXN0LmRldlJ1bm5lcik7XG4gICAgfVxuXG4gICAgY29uc3Qgd2ViUGF0aCA9IHBhdGguam9pbih0aGlzLmFwcFJvb3RQYXRoLCBcIndlYlwiKTtcbiAgICBjb25zdCBoYXNXZWIgPSBhd2FpdCBleGlzdHMod2ViUGF0aCk7XG5cbiAgICAvLyDsoITsl60gY29tcHJlc3Mg7Ji17IWYIOqzhOyCsCAocm91dGUuY29tcHJlc3M6IHRydWXsnbwg65WMIOyCrOyaqSlcbiAgICBjb25zdCBwbHVnaW5Db21wcmVzcyA9IHRoaXMuY29uZmlnLnNlcnZlci5wbHVnaW5zPy5jb21wcmVzcztcbiAgICBjb25zdCBnbG9iYWxDb21wcmVzc09wdGlvbnM6IENvbXByZXNzT3B0aW9ucyB8IHVuZGVmaW5lZCA9IHBsdWdpbkNvbXByZXNzXG4gICAgICA/IHBsdWdpbkNvbXByZXNzID09PSB0cnVlXG4gICAgICAgID8geyB0aHJlc2hvbGQ6IDEwMjQsIGVuY29kaW5nczogW1wiYnJcIiwgXCJnemlwXCIsIFwiZGVmbGF0ZVwiXSB9XG4gICAgICAgIDoge1xuICAgICAgICAgICAgdGhyZXNob2xkOiBwbHVnaW5Db21wcmVzcy50aHJlc2hvbGQsXG4gICAgICAgICAgICBlbmNvZGluZ3M6IHBsdWdpbkNvbXByZXNzLmVuY29kaW5ncyxcbiAgICAgICAgICAgIGN1c3RvbVR5cGVzOiBwbHVnaW5Db21wcmVzcy5jdXN0b21UeXBlcyxcbiAgICAgICAgICB9XG4gICAgICA6IHVuZGVmaW5lZDtcblxuICAgIGlmIChpc0xvY2FsKCkpIHtcbiAgICAgIC8vIOuhnOy7rCDqsJzrsJwg7ZmY6rK9OiBjYXRjaC1hbGzroZwgQVBJ66W8IOuPmeyggSDrp6Tsua3tlZjsl6wgSE1S7J2EIOyngOybkO2VqeuLiOuLpC5cbiAgICAgIC8vIFNPTkFNVV9ESVNBQkxFX0lOVEVHUkFURURfV0VCPXllc+uhnCDshKTsoJXtlZjrqbQgZGV2X2FwaSDrqqjrk5zsl5DshJwgVml0ZSDthrXtlansnYQg67mE7Zmc7ISx7ZmU7ZWgIOyImCDsnojsirXri4jri6QuXG4gICAgICBjb25zdCBkaXNhYmxlSW50ZWdyYXRlZFdlYiA9IHByb2Nlc3MuZW52LlNPTkFNVV9ESVNBQkxFX0lOVEVHUkFURURfV0VCID09PSBcInllc1wiO1xuICAgICAgaWYgKGhhc1dlYiAmJiAhZGlzYWJsZUludGVncmF0ZWRXZWIpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5zZXR1cERldlNlcnZlcldpdGhWaXRlKHNlcnZlciwgd2ViUGF0aCwgY29uZmlnKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuc2V0dXBEZXZTZXJ2ZXIoc2VydmVyLCBjb25maWcpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyDtlITroZzrjZXshZgg7ZmY6rK9OiDqsJzrs4QgQVBJIOudvOyasO2KuCArIOygleyggSDtjIzsnbwg7ISc67mZXG4gICAgICBmb3IgKGNvbnN0IGFwaSBvZiB0aGlzLnN5bmNlci5hcGlzKSB7XG4gICAgICAgIGlmICh0aGlzLnN5bmNlci5tb2RlbHNbYXBpLm1vZGVsTmFtZV0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihg7KCV7J2Y65CY7KeAIOyViuydgCDrqqjrjbjsl5Ag7KCR6re8ICR7YXBpLm1vZGVsTmFtZX1gKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHNlcnZlci5yb3V0ZSh7XG4gICAgICAgICAgbWV0aG9kOiBhcGkub3B0aW9ucy5odHRwTWV0aG9kID8/IFwiR0VUXCIsXG4gICAgICAgICAgdXJsOiB0aGlzLmNvbmZpZy5hcGkucm91dGUucHJlZml4ICsgYXBpLnBhdGgsXG4gICAgICAgICAgaGFuZGxlcjogdGhpcy5jcmVhdGVBcGlIYW5kbGVyKGFwaSwgY29uZmlnKSxcbiAgICAgICAgICBjb21wcmVzczogdG9GYXN0aWZ5Q29tcHJlc3NPcHRpb24oYXBpLm9wdGlvbnMuY29tcHJlc3MsIGdsb2JhbENvbXByZXNzT3B0aW9ucyksXG4gICAgICAgIH0pO1xuICAgICAgfVxuXG4gICAgICAvLyDtlITroZzrjZXshZjsl5DshJzripQgd2ViIOyGjOyKpChhcHBSb290L3dlYikg7Jyg66y07JmAIOustOq0gO2VmOqyjCxcbiAgICAgIC8vIGFwaS93ZWItZGlzdCDsobTsnqwg7Jes67aA66W8IHNldHVwU3RhdGljV2ViU2VydmVyIOuCtOu2gOyXkOyEnCDtjJDri6jtlanri4jri6QuXG4gICAgICBhd2FpdCB0aGlzLnNldHVwU3RhdGljV2ViU2VydmVyKHNlcnZlciwgY29uZmlnLCBnbG9iYWxDb21wcmVzc09wdGlvbnMpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBkZXYg66qo65OcIOqzte2GtTogY2F0Y2gtYWxs7JeQ7IScIHN5bmNlci5hcGlz66W8IOuPmeyggeycvOuhnCDtg5Dsg4ntlZjsl6wgQVBJIOyalOyyreydhCDsspjrpqztlanri4jri6QuXG4gICAqIHNlcnZlci5yb3V0ZSgp66GcIOqwnOuzhCDrk7HroZ3tlZjrqbQgaGFuZGxlcuqwgCDqs6DsoJXrkJjslrQgSE1S7J20IOuPmeyeke2VmOyngCDslYrsnLzrr4DroZwsXG4gICAqIOunpCDsmpTssq3rp4jri6Qgc3luY2VyLmFwaXPrpbwg7KGw7ZqM7ZWY64qUIOydtCDrsKnsi53snYQg7IKs7Jqp7ZWp64uI64ukLlxuICAgKlxuICAgKiDsmpTssq3snbQgL2FwaSjsoJXtmZXtnojripQgdGhpcy5jb25maWcuYXBpLnJvdXRlLnByZWZpeCnroZwg7Iuc7J6R7ZWY7KeAIOyViuuKlCDqsr3smrDrnbzrqbQgbnVsbOydhCDrsJjtmZjtlZjrqbAg64Gd64OF64uI64ukLlxuICAgKi9cbiAgcHJpdmF0ZSBoYW5kbGVEZXZBcGlSZXF1ZXN0KFxuICAgIHJlcXVlc3Q6IEZhc3RpZnlSZXF1ZXN0LFxuICAgIGNvbmZpZzogU29uYW11RmFzdGlmeUNvbmZpZyxcbiAgKTogKChyZXF1ZXN0OiBGYXN0aWZ5UmVxdWVzdCwgcmVwbHk6IEZhc3RpZnlSZXBseSkgPT4gUHJvbWlzZTx1bmtub3duPikgfCBudWxsIHtcbiAgICBjb25zdCB1cmwgPSB0aGlzLmdldFBhdGhuYW1lRnJvbVVybChyZXF1ZXN0LnVybCk7XG4gICAgY29uc3QgbWV0aG9kID0gcmVxdWVzdC5tZXRob2Q7XG5cbiAgICBpZiAoIXVybC5zdGFydHNXaXRoKHRoaXMuY29uZmlnLmFwaS5yb3V0ZS5wcmVmaXgpKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICAvLyBzeW5jZXIuYXBpc+ydmCBwYXRo64qUIDpwYXJhbSDtmJXtg5zrpbwg7Y+s7ZWo7ZWgIOyImCDsnojsnLzrr4DroZwg7IS46re466i87Yq4IOuLqOychOuhnCDrp6Tsua3tlanri4jri6QuXG4gICAgLy8g7KCV6rec7IudIOyDneyEsSDrsKnsi53snYAgcGF0aCDrrLjsnpDsl7Qg64K0IO2KueyImOusuOyekCguLCArLCAoLCBbIOuTsSnroZwg7Jik7J6R64+Z7ZWgIOyImCDsnojslrQg7IKs7Jqp7ZWY7KeAIOyViuyKteuLiOuLpC5cbiAgICBjb25zdCBtYXRjaGVkQXBpID0gdGhpcy5zeW5jZXIuYXBpcy5maW5kKChhcGkpID0+IHtcbiAgICAgIGlmICh0aGlzLnN5bmNlci5tb2RlbHNbYXBpLm1vZGVsTmFtZV0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgICBjb25zdCBhcGlNZXRob2QgPSBhcGkub3B0aW9ucy5odHRwTWV0aG9kID8/IFwiR0VUXCI7XG4gICAgICBpZiAoYXBpTWV0aG9kICE9PSBtZXRob2QpIHJldHVybiBmYWxzZTtcblxuICAgICAgY29uc3QgZnVsbFBhdGggPSB0aGlzLmNvbmZpZy5hcGkucm91dGUucHJlZml4ICsgYXBpLnBhdGg7XG4gICAgICByZXR1cm4gdGhpcy5pc1BhdGhQYXR0ZXJuTWF0Y2goZnVsbFBhdGgsIHVybCk7XG4gICAgfSk7XG5cbiAgICBpZiAoIW1hdGNoZWRBcGkpIHtcbiAgICAgIHRocm93IG5ldyBOb3RGb3VuZEV4Y2VwdGlvbihTRChcImVycm9yLmFwaS5ub3RGb3VuZFwiKSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuY3JlYXRlQXBpSGFuZGxlcihtYXRjaGVkQXBpLCBjb25maWcpO1xuICB9XG5cbiAgLyoqXG4gICAqIGRldiBhcGkg66qo65OcOiBWaXRlIOyXhuydtCBBUEkg64+Z7KCBIOudvOyasO2MheunjCDsoJzqs7Xtlanri4jri6QuXG4gICAqIEhNUuydhCDsnITtlbQgY2F0Y2gtYWxs7JeQ7IScIOunpCDsmpTssq3rp4jri6Qgc3luY2VyLmFwaXPrpbwg7KGw7ZqM7ZWp64uI64ukLlxuICAgKi9cbiAgcHJpdmF0ZSBzZXR1cERldlNlcnZlcihcbiAgICBzZXJ2ZXI6IEZhc3RpZnlJbnN0YW5jZTxTZXJ2ZXIsIEluY29taW5nTWVzc2FnZSwgU2VydmVyUmVzcG9uc2U+LFxuICAgIGNvbmZpZzogU29uYW11RmFzdGlmeUNvbmZpZyxcbiAgKTogdm9pZCB7XG4gICAgc2VydmVyLnJvdXRlKHtcbiAgICAgIG1ldGhvZDogW1wiR0VUXCIsIFwiSEVBRFwiLCBcIlBPU1RcIiwgXCJQVVRcIiwgXCJERUxFVEVcIiwgXCJQQVRDSFwiXSxcbiAgICAgIHVybDogYCR7dGhpcy5jb25maWcuYXBpLnJvdXRlLnByZWZpeH0vKmAsXG4gICAgICBoYW5kbGVyOiBhc3luYyAocmVxdWVzdCwgcmVwbHkpID0+IHtcbiAgICAgICAgY29uc3QgaGFuZGxlciA9IHRoaXMuaGFuZGxlRGV2QXBpUmVxdWVzdChyZXF1ZXN0LCBjb25maWcpO1xuICAgICAgICBpZiAoaGFuZGxlcikge1xuICAgICAgICAgIHJldHVybiBoYW5kbGVyKHJlcXVlc3QsIHJlcGx5KTtcbiAgICAgICAgfVxuICAgICAgICAvLyDrk7HroZ3rkJwgQVBJ7JmAIOydvOy5mO2VmOyngCDslYrripQg7JqU7LKt7JeQIOuMgO2VnCBmYWxsYmFja+yeheuLiOuLpC5cbiAgICAgICAgdGhyb3cgbmV3IE5vdEZvdW5kRXhjZXB0aW9uKFNEKFwiZXJyb3IuYXBpLm5vdEZvdW5kXCIpKTtcbiAgICAgIH0sXG4gICAgfSk7XG4gIH1cblxuICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vRXhwbGljaXRBbnk6IFZpdGVEZXZTZXJ2ZXIg7YOA7J6F7J2EIOuPmeyggeycvOuhnCDroZzrk5ztlbTslbwg7ZWoXG4gIHByaXZhdGUgdml0ZVNlcnZlcjogYW55ID0gbnVsbDtcblxuICAvKipcbiAgICogZGV2IGFsbCDrqqjrk5w6IFZpdGUgRGV2IFNlcnZlcuulvCDthrXtlantlZjsl6wgQVBJICsgU1NSICsgQ1NS7J2EIOuqqOuRkCDsoJzqs7Xtlanri4jri6QuXG4gICAqIEFQSSDrj5nsoIEg66ek7Lmt7J2AIGhhbmRsZURldkFwaVJlcXVlc3Trpbwg6rO17Jyg7ZWp64uI64ukLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBzZXR1cERldlNlcnZlcldpdGhWaXRlKFxuICAgIHNlcnZlcjogRmFzdGlmeUluc3RhbmNlPFNlcnZlciwgSW5jb21pbmdNZXNzYWdlLCBTZXJ2ZXJSZXNwb25zZT4sXG4gICAgd2ViUGF0aDogc3RyaW5nLFxuICAgIGNvbmZpZzogU29uYW11RmFzdGlmeUNvbmZpZyxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gQGZhc3RpZnkvbWlkZGllIOuTseuhnSAoQ29ubmVjdC1zdHlsZSBtaWRkbGV3YXJlIOyngOybkClcbiAgICBhd2FpdCBzZXJ2ZXIucmVnaXN0ZXIoKGF3YWl0IGltcG9ydChcIkBmYXN0aWZ5L21pZGRpZVwiKSkuZGVmYXVsdCk7XG5cbiAgICBjb25zdCB2aXRlID0gYXdhaXQgaW1wb3J0KFwidml0ZVwiKTtcblxuICAgIHRoaXMudml0ZVNlcnZlciA9IGF3YWl0IHZpdGUuY3JlYXRlU2VydmVyKHtcbiAgICAgIHJvb3Q6IHdlYlBhdGgsXG4gICAgICBzZXJ2ZXI6IHtcbiAgICAgICAgbWlkZGxld2FyZU1vZGU6IHRydWUsXG4gICAgICAgIGhtcjoge1xuICAgICAgICAgIHNlcnZlcjogc2VydmVyLnNlcnZlcixcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgICBhcHBUeXBlOiBcImN1c3RvbVwiLFxuICAgIH0pO1xuXG4gICAgLy8gVml0ZSBtaWRkbGV3YXJlIOuTseuhnSAoVml0ZSDsl5DshYsg7LKY66asKVxuICAgIHNlcnZlci51c2UoKHJlcSwgcmVzLCBuZXh0KSA9PiB7XG4gICAgICAvLyBBUEnsmYAgU29uYW11IFVJ64qUIEZhc3RpZnkg65287Jqw7Yq46rCAIOyymOumrO2VmOuPhOuhnSBza2lwXG4gICAgICBpZiAocmVxLnVybD8uc3RhcnRzV2l0aCh0aGlzLmNvbmZpZy5hcGkucm91dGUucHJlZml4KSB8fCByZXEudXJsPy5zdGFydHNXaXRoKFwiL3NvbmFtdS11aVwiKSkge1xuICAgICAgICByZXR1cm4gbmV4dCgpO1xuICAgICAgfVxuICAgICAgLy8g64KY66i47KeA64qUIFZpdGUgbWlkZGxld2FyZeuhnCDsoITri6xcbiAgICAgIHJldHVybiB0aGlzLnZpdGVTZXJ2ZXIubWlkZGxld2FyZXMocmVxLCByZXMsIG5leHQpO1xuICAgIH0pO1xuXG4gICAgLy8gY2F0Y2gtYWxsIOudvOyasO2KuOyXkOyEnCDrj5nsoIHsnLzroZwgQVBJL1NTUiDsspjrpqxcbiAgICAvLyDqsJzrsJwg7ZmY6rK97JeQ7ISc64qUIOudvOyasO2KuOuzhCBjb21wcmVzcyDsmLXshZjsnYQg7Y+s6riw7ZWY6rOgIEhNUiDsnbTsoJDsnYQg7Leo7ZWp64uI64ukLlxuICAgIHNlcnZlci5yb3V0ZSh7XG4gICAgICBtZXRob2Q6IFtcIkdFVFwiLCBcIkhFQURcIiwgXCJQT1NUXCIsIFwiUFVUXCIsIFwiREVMRVRFXCIsIFwiUEFUQ0hcIl0sXG4gICAgICB1cmw6IFwiLypcIixcbiAgICAgIGhhbmRsZXI6IGFzeW5jIChyZXF1ZXN0LCByZXBseSkgPT4ge1xuICAgICAgICAvLyAxLiBBUEkg7JqU7LKtIOyymOumrFxuICAgICAgICBjb25zdCByZXN1bHQgPSB0aGlzLmhhbmRsZURldkFwaVJlcXVlc3QocmVxdWVzdCwgY29uZmlnKTtcbiAgICAgICAgaWYgKHJlc3VsdCkge1xuICAgICAgICAgIHJldHVybiByZXN1bHQocmVxdWVzdCwgcmVwbHkpO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgdXJsID0gcmVxdWVzdC51cmw7XG5cbiAgICAgICAgLy8gMi4gU1NSIOudvOyasO2KuCDsspjrpqxcbiAgICAgICAgY29uc3QgeyBtYXRjaFNTUlJvdXRlLCByZW5kZXJTU1IgfSA9IGF3YWl0IGltcG9ydChcIi4uL3NzclwiKTtcbiAgICAgICAgY29uc3Qgc3NyTWF0Y2ggPSBtYXRjaFNTUlJvdXRlKHVybCk7XG4gICAgICAgIGlmIChzc3JNYXRjaCkge1xuICAgICAgICAgIGNvbnNvbGUubG9nKGBbU1NSXSBNYXRjaGVkIHJvdXRlOiAke3Nzck1hdGNoLnJvdXRlLnBhdGh9YCk7XG4gICAgICAgICAgY29uc3QgaHRtbCA9IGF3YWl0IHJlbmRlclNTUihcbiAgICAgICAgICAgIHVybCxcbiAgICAgICAgICAgIHNzck1hdGNoLnJvdXRlLFxuICAgICAgICAgICAgc3NyTWF0Y2gucGFyYW1zLFxuICAgICAgICAgICAgcmVxdWVzdCxcbiAgICAgICAgICAgIHJlcGx5LFxuICAgICAgICAgICAgY29uZmlnLFxuICAgICAgICAgICAgdGhpcy52aXRlU2VydmVyLFxuICAgICAgICAgICk7XG4gICAgICAgICAgcmVwbHkudHlwZShcInRleHQvaHRtbFwiKTtcbiAgICAgICAgICByZXR1cm4gaHRtbDtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIDMuIENTUiBmYWxsYmFja1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IGZzID0gYXdhaXQgaW1wb3J0KFwibm9kZTpmcy9wcm9taXNlc1wiKTtcbiAgICAgICAgICBsZXQgdGVtcGxhdGUgPSBhd2FpdCBmcy5yZWFkRmlsZShcbiAgICAgICAgICAgIHBhdGguam9pbih0aGlzLnZpdGVTZXJ2ZXIuY29uZmlnLnJvb3QsIFwiaW5kZXguaHRtbFwiKSxcbiAgICAgICAgICAgIFwidXRmLThcIixcbiAgICAgICAgICApO1xuICAgICAgICAgIHRlbXBsYXRlID0gYXdhaXQgdGhpcy52aXRlU2VydmVyLnRyYW5zZm9ybUluZGV4SHRtbCh1cmwsIHRlbXBsYXRlKTtcblxuICAgICAgICAgIHJlcGx5LnR5cGUoXCJ0ZXh0L2h0bWxcIik7XG4gICAgICAgICAgcmV0dXJuIHRlbXBsYXRlO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgdGhpcy52aXRlU2VydmVyLnNzckZpeFN0YWNrdHJhY2UoZSBhcyBFcnJvcik7XG4gICAgICAgICAgY29uc29sZS5lcnJvcihlKTtcbiAgICAgICAgICByZXBseS5zdGF0dXMoNTAwKTtcbiAgICAgICAgICByZXR1cm4gKGUgYXMgRXJyb3IpLm1lc3NhZ2U7XG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICAvLyDshJzrsoQg7KKF66OMIOyLnCBWaXRl64+EIOyiheujjFxuICAgIHNlcnZlci5hZGRIb29rKFwib25DbG9zZVwiLCBhc3luYyAoKSA9PiB7XG4gICAgICBhd2FpdCB0aGlzLnZpdGVTZXJ2ZXIuY2xvc2UoKTtcbiAgICB9KTtcblxuICAgIGNvbnNvbGUubG9nKFwi4pyTIFZpdGUgZGV2IHNlcnZlciBpbnRlZ3JhdGVkXCIpO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBzZXR1cFN0YXRpY1dlYlNlcnZlcihcbiAgICBzZXJ2ZXI6IEZhc3RpZnlJbnN0YW5jZTxTZXJ2ZXIsIEluY29taW5nTWVzc2FnZSwgU2VydmVyUmVzcG9uc2U+LFxuICAgIGNvbmZpZzogU29uYW11RmFzdGlmeUNvbmZpZyxcbiAgICBnbG9iYWxDb21wcmVzc09wdGlvbnM6IENvbXByZXNzT3B0aW9ucyB8IHVuZGVmaW5lZCxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8g6rK966GcIOuqhe2Zle2ZlDogYXBpL3dlYi1kaXN0L2NsaWVudCAo7KCV7KCBIO2MjOydvCksIGFwaS93ZWItZGlzdC9zZXJ2ZXIgKFNTUiBlbnRyeSksIGFwaS9kaXN0L3NzciAoU1NSIHJvdXRlcyAtIEFQSSDshozsnKApXG4gICAgY29uc3Qgd2ViRGlzdFBhdGggPSBwYXRoLmpvaW4odGhpcy5hcGlSb290UGF0aCwgXCJ3ZWItZGlzdFwiLCBcImNsaWVudFwiKTtcbiAgICBjb25zdCBzc3JQYXRoID0gcGF0aC5qb2luKHRoaXMuYXBpUm9vdFBhdGgsIFwid2ViLWRpc3RcIiwgXCJzZXJ2ZXJcIik7XG4gICAgY29uc3Qgc3NyRW50cnlQYXRoID0gcGF0aC5qb2luKHNzclBhdGgsIFwiZW50cnktc2VydmVyLmdlbmVyYXRlZC5qc1wiKTtcbiAgICBjb25zdCBzc3JSb3V0ZXNQYXRoID0gcGF0aC5qb2luKHRoaXMuYXBpUm9vdFBhdGgsIFwiZGlzdFwiLCBcInNzclwiLCBcInJvdXRlcy5qc1wiKTtcblxuICAgIGlmICghKGF3YWl0IGV4aXN0cyh3ZWJEaXN0UGF0aCkpKSB7XG4gICAgICBjb25zb2xlLndhcm4oYOKaoCBXZWIgZGlzdCBub3QgZm91bmQ6ICR7d2ViRGlzdFBhdGh9YCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gU1NSIGVudHJ5IOyhtOyerCDsl6zrtoAg7ZmV7J24XG4gICAgY29uc3Qgc3NyQXZhaWxhYmxlID0gYXdhaXQgZXhpc3RzKHNzckVudHJ5UGF0aCk7XG5cbiAgICBpZiAoIXNzckF2YWlsYWJsZSkge1xuICAgICAgY29uc29sZS53YXJuKGDimqAgU1NSIGVudHJ5IG5vdCBmb3VuZDogJHtzc3JFbnRyeVBhdGh9YCk7XG4gICAgICBjb25zb2xlLndhcm4oXCIgIFNTUiB3aWxsIGJlIGRpc2FibGVkLiBPbmx5IENTUiB3aWxsIHdvcmsuXCIpO1xuICAgIH1cblxuICAgIC8vIFNTUiDrnbzsmrDtirgg66Gc65OcIChwcm9kdWN0aW9u7JeQ7ISc66eMLCDsgqzsmqnsnpAg7ZSE66Gc7KCd7Yq47J2YIHNzci9yb3V0ZXMudHMpXG4gICAgaWYgKHNzckF2YWlsYWJsZSkge1xuICAgICAgaWYgKGF3YWl0IGV4aXN0cyhzc3JSb3V0ZXNQYXRoKSkge1xuICAgICAgICAvLyB0cy1sb2FkZXLrnbzrqbQgXCJmaWxlOi8vXCLroZwg7Iuc7J6R7ZWY64qUIGZ1bGx5LXJlc29sdmVkIHBhdGjrp4wg67Cb6riw7JeQIOydtOulvCDsspjrpqztlbTso7zripQgaW1wb3J0TWVtYmVyc+ulvCDsgqzsmqntlbTslbwg7ZaI6rKg7KeA66eMLFxuICAgICAgICAvLyDsl6zquLDripQg7ZSE66Gc642V7IWYIO2ZmOqyveyXkOyEnCBsb2FkZXIg7JeG7J20IOuPjOyVhOqwgOq4sCDrlYzrrLjsl5AgXCLsp4Tsp5wganMg7YyM7J28XCLsnZggXCLqt7jrg6VcIiDsoIjrjIDqsr3roZzrpbwg67CU66GcIGltcG9ydO2VtOuPhCDrkKnri4jri6QuXG4gICAgICAgIC8vIOydtCDrgrTsmqnsnYAg7J20IO2VqOyImCDrgrTsl5DshJwg7JWE656Y7JeQIOuCmOyYrCDri6TrpbggaW1wb3J0IO2YuOy2nOyXkOuPhCDrj5nsnbztlZjqsowg7KCB7Jqp65Cp64uI64ukLlxuICAgICAgICBhd2FpdCBpbXBvcnQoc3NyUm91dGVzUGF0aCk7XG4gICAgICAgIGNvbnNvbGUubG9nKFwi4pyTIFNTUiByb3V0ZXMgbG9hZGVkXCIpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc29sZS53YXJuKGDimqAgU1NSIHJvdXRlcyBub3QgZm91bmQ6ICR7c3NyUm91dGVzUGF0aH1gKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyDroaTrp4Eg7JeF642w7J207Yq4IOuMgOydkTogYXNzZXQgaGFzaCDrtojsnbzsuZgg7IucIO2YhOyerCDrsoTsoIQg7KeB7KCRIOyEnOu5mVxuICAgIHNlcnZlci5nZXQoXCIvYXNzZXRzLzpmaWxlbmFtZVwiLCBhc3luYyAocmVxdWVzdCwgcmVwbHkpID0+IHtcbiAgICAgIGNvbnN0IHJlcXVlc3RlZEZpbGUgPSAocmVxdWVzdC5wYXJhbXMgYXMgeyBmaWxlbmFtZTogc3RyaW5nIH0pLmZpbGVuYW1lO1xuICAgICAgY29uc3QgYXNzZXRzRGlyID0gcGF0aC5qb2luKHdlYkRpc3RQYXRoLCBcImFzc2V0c1wiKTtcbiAgICAgIGNvbnN0IHNhZmVGaWxlUGF0aCA9IHRoaXMucmVzb2x2ZVBhdGhXaXRoaW5CYXNlRGlyKGFzc2V0c0RpciwgcmVxdWVzdGVkRmlsZSk7XG4gICAgICBpZiAoc2FmZUZpbGVQYXRoID09PSBudWxsKSB7XG4gICAgICAgIHJlcGx5LnN0YXR1cyg0MDMpLnNlbmQoKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY29uc3Qgbm9ybWFsaXplZFJlcXVlc3RlZEZpbGUgPSBwYXRoLnJlbGF0aXZlKGFzc2V0c0Rpciwgc2FmZUZpbGVQYXRoKS5yZXBsYWNlKC9cXFxcL2csIFwiL1wiKTtcblxuICAgICAgY29uc3QgYXNzZXRQYXRoID0gYC9hc3NldHMvJHtub3JtYWxpemVkUmVxdWVzdGVkRmlsZX1gO1xuXG4gICAgICAvLyBDYWNoZS1Db250cm9sIO2XpOuNlCDqsrDsoJVcbiAgICAgIGNvbnN0IGdldENhY2hlQ29udHJvbEZvckFzc2V0ID0gKCk6IENhY2hlQ29udHJvbENvbmZpZyA9PiB7XG4gICAgICAgIGNvbnN0IGNhY2hlUmVxOiBDYWNoZUNvbnRyb2xSZXF1ZXN0ID0ge1xuICAgICAgICAgIHR5cGU6IFwiYXNzZXRzXCIsXG4gICAgICAgICAgdXJsOiByZXF1ZXN0LnVybCxcbiAgICAgICAgICBwYXRoOiBhc3NldFBhdGgsXG4gICAgICAgICAgbWV0aG9kOiByZXF1ZXN0Lm1ldGhvZCxcbiAgICAgICAgfTtcblxuICAgICAgICAvLyDsgqzsmqnsnpAg7KCV7J2YIO2VuOuTpOufrCDsmrDshKBcbiAgICAgICAgaWYgKGNvbmZpZy5jYWNoZUNvbnRyb2xIYW5kbGVyKSB7XG4gICAgICAgICAgY29uc3QgcmVzdWx0ID0gY29uZmlnLmNhY2hlQ29udHJvbEhhbmRsZXIoY2FjaGVSZXEpO1xuICAgICAgICAgIGlmIChyZXN1bHQpIHJldHVybiByZXN1bHQ7XG4gICAgICAgIH1cblxuICAgICAgICAvLyDquLDrs7jqsJI6IGltbXV0YWJsZVxuICAgICAgICByZXR1cm4gQ2FjaGVQcmVzZXRzLmltbXV0YWJsZTtcbiAgICAgIH07XG5cbiAgICAgIC8vIGluZGV4LSouanMg65iQ64qUIGluZGV4LSouY3NzIOyalOyyreyduCDqsr3smrBcbiAgICAgIGlmICgvXmluZGV4LVthLWYwLTldK1xcLihqc3xjc3MpJC8udGVzdChub3JtYWxpemVkUmVxdWVzdGVkRmlsZSkpIHtcbiAgICAgICAgY29uc3QgZXh0ID0gbm9ybWFsaXplZFJlcXVlc3RlZEZpbGUuc3BsaXQoXCIuXCIpLnBvcCgpO1xuICAgICAgICBjb25zdCBmaWxlcyA9IGF3YWl0IGZzLnJlYWRkaXIoYXNzZXRzRGlyKTtcbiAgICAgICAgY29uc3QgY3VycmVudEZpbGUgPSBmaWxlcy5maW5kKChmKSA9PiBmLnN0YXJ0c1dpdGgoXCJpbmRleC1cIikgJiYgZi5lbmRzV2l0aChgLiR7ZXh0fWApKTtcblxuICAgICAgICBpZiAoY3VycmVudEZpbGUpIHtcbiAgICAgICAgICBjb25zdCBmaWxlUGF0aCA9IHBhdGguam9pbihhc3NldHNEaXIsIGN1cnJlbnRGaWxlKTtcbiAgICAgICAgICBjb25zdCBjb250ZW50ID0gYXdhaXQgZnMucmVhZEZpbGUoZmlsZVBhdGgpO1xuICAgICAgICAgIHJlcGx5LnR5cGUoZXh0ID09PSBcImpzXCIgPyBcImFwcGxpY2F0aW9uL2phdmFzY3JpcHRcIiA6IFwidGV4dC9jc3NcIik7XG4gICAgICAgICAgYXBwbHlDYWNoZUhlYWRlcnMocmVwbHksIGdldENhY2hlQ29udHJvbEZvckFzc2V0KCkpO1xuICAgICAgICAgIHJldHVybiByZXBseS5zZW5kKGNvbnRlbnQpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIOydvOuwmCDtjIzsnbwg7ISc67mZXG4gICAgICBjb25zdCBmaWxlUGF0aCA9IHNhZmVGaWxlUGF0aDtcbiAgICAgIGlmIChhd2FpdCBleGlzdHMoZmlsZVBhdGgpKSB7XG4gICAgICAgIGNvbnN0IGNvbnRlbnQgPSBhd2FpdCBmcy5yZWFkRmlsZShmaWxlUGF0aCk7XG4gICAgICAgIGNvbnN0IGV4dCA9IG5vcm1hbGl6ZWRSZXF1ZXN0ZWRGaWxlLnNwbGl0KFwiLlwiKS5wb3AoKTtcbiAgICAgICAgcmVwbHkudHlwZShleHQgPT09IFwianNcIiA/IFwiYXBwbGljYXRpb24vamF2YXNjcmlwdFwiIDogZXh0ID09PSBcImNzc1wiID8gXCJ0ZXh0L2Nzc1wiIDogXCJcIik7XG4gICAgICAgIGlmIChub3JtYWxpemVkUmVxdWVzdGVkRmlsZS5pbmNsdWRlcyhcIi1cIikpIHtcbiAgICAgICAgICBhcHBseUNhY2hlSGVhZGVycyhyZXBseSwgZ2V0Q2FjaGVDb250cm9sRm9yQXNzZXQoKSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJlcGx5LnNlbmQoY29udGVudCk7XG4gICAgICB9XG5cbiAgICAgIHJlcGx5LnN0YXR1cyg0MDQpLnNlbmQoKTtcbiAgICB9KTtcblxuICAgIC8vIFNTUiDrnbzsmrDtirgg6rCc67OEIOuTseuhnSAoY29tcHJlc3Mg7Ji17IWY7J20IOudvOyasO2KuOuzhOuhnCDsoIHsmqnrkJjrj4TroZ0pXG4gICAgaWYgKHNzckF2YWlsYWJsZSkge1xuICAgICAgY29uc3QgeyBnZXRTU1JSb3V0ZXMgfSA9IGF3YWl0IGltcG9ydChcIi4uL3NzclwiKTtcbiAgICAgIGNvbnN0IHsgcmVuZGVyU1NSIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9zc3IvcmVuZGVyZXJcIik7XG4gICAgICBjb25zdCBzc3JSb3V0ZXMgPSBnZXRTU1JSb3V0ZXMoKTtcblxuICAgICAgZm9yIChjb25zdCByb3V0ZSBvZiBzc3JSb3V0ZXMpIHtcbiAgICAgICAgc2VydmVyLnJvdXRlKHtcbiAgICAgICAgICBtZXRob2Q6IFtcIkdFVFwiLCBcIkhFQURcIl0sXG4gICAgICAgICAgdXJsOiByb3V0ZS5wYXRoLFxuICAgICAgICAgIGNvbXByZXNzOiB0b0Zhc3RpZnlDb21wcmVzc09wdGlvbihyb3V0ZS5jb21wcmVzcyA/PyB0cnVlLCBnbG9iYWxDb21wcmVzc09wdGlvbnMpLFxuICAgICAgICAgIGhhbmRsZXI6IGFzeW5jIChyZXF1ZXN0LCByZXBseSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdXJsID0gcmVxdWVzdC51cmw7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhgW1NTUl0gTWF0Y2hlZCByb3V0ZTogJHtyb3V0ZS5wYXRofWApO1xuXG4gICAgICAgICAgICBjb25zdCBwYXJhbXMgPSB0aGlzLmV4dHJhY3RQYXRoUGFyYW1zKHJvdXRlLnBhdGgsIHVybCk7XG4gICAgICAgICAgICBjb25zdCBodG1sID0gYXdhaXQgcmVuZGVyU1NSKHVybCwgcm91dGUsIHBhcmFtcywgcmVxdWVzdCwgcmVwbHksIGNvbmZpZyk7XG5cbiAgICAgICAgICAgIHJlcGx5LnR5cGUoXCJ0ZXh0L2h0bWxcIik7XG4gICAgICAgICAgICByZXR1cm4gaHRtbDtcbiAgICAgICAgICB9LFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBDU1Igb3IgU3RhdGljIEZpbGUgRmFsbGJhY2sgKFNTUiDrnbzsmrDtirjsl5Ag66ek7Lmt65CY7KeAIOyViuuKlCDrqqjrk6Ag7JqU7LKtKVxuICAgIHNlcnZlci5yb3V0ZSh7XG4gICAgICBtZXRob2Q6IFtcIkdFVFwiLCBcIkhFQURcIl0sXG4gICAgICB1cmw6IFwiKlwiLFxuICAgICAgaGFuZGxlcjogYXN5bmMgKHJlcXVlc3QsIHJlcGx5KSA9PiB7XG4gICAgICAgIC8vIC9hcGksIC9zb25hbXUtdWnripQgNDA0IOq3uOuMgOuhnFxuICAgICAgICBpZiAocmVxdWVzdC51cmwuc3RhcnRzV2l0aChcIi9hcGlcIikgfHwgcmVxdWVzdC51cmwuc3RhcnRzV2l0aChcIi9zb25hbXUtdWlcIikpIHtcbiAgICAgICAgICByZXBseS5zdGF0dXMoNDA0KS5zZW5kKCk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gQ1NS7JqpIENhY2hlLUNvbnRyb2wg7Zek642UIOyEpOyglVxuICAgICAgICBpZiAoY29uZmlnLmNhY2hlQ29udHJvbEhhbmRsZXIpIHtcbiAgICAgICAgICBjb25zdCBjc3JDYWNoZVJlcTogQ2FjaGVDb250cm9sUmVxdWVzdCA9IHtcbiAgICAgICAgICAgIHR5cGU6IFwiY3NyXCIsXG4gICAgICAgICAgICB1cmw6IHJlcXVlc3QudXJsLFxuICAgICAgICAgICAgcGF0aDogcmVxdWVzdC51cmwuc3BsaXQoXCI/XCIpWzBdLFxuICAgICAgICAgICAgbWV0aG9kOiByZXF1ZXN0Lm1ldGhvZCxcbiAgICAgICAgICB9O1xuICAgICAgICAgIGNvbnN0IGNzckNhY2hlQ29uZmlnID0gY29uZmlnLmNhY2hlQ29udHJvbEhhbmRsZXIoY3NyQ2FjaGVSZXEpO1xuXG4gICAgICAgICAgaWYgKGNzckNhY2hlQ29uZmlnKSB7XG4gICAgICAgICAgICBhcHBseUNhY2hlSGVhZGVycyhyZXBseSwgY3NyQ2FjaGVDb25maWcpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIOygleyggSDtjIzsnbzsnbQg7KG07J6s7ZWgIOqyveyasCwg7KCV7KCBIO2MjOydvOydhCDrqLzsoIAg7ISc67mZ7ZW07JW87ZWoXG4gICAgICAgIGNvbnN0IHJlcXVlc3RQYXRoID0gdGhpcy5nZXRQYXRobmFtZUZyb21VcmwocmVxdWVzdC51cmwpO1xuICAgICAgICBjb25zdCBzYWZlRmlsZVBhdGggPSB0aGlzLnJlc29sdmVQYXRoV2l0aGluQmFzZURpcih3ZWJEaXN0UGF0aCwgcmVxdWVzdFBhdGgpO1xuICAgICAgICBpZiAoc2FmZUZpbGVQYXRoID09PSBudWxsKSB7XG4gICAgICAgICAgcmVwbHkuc3RhdHVzKDQwMykuc2VuZCgpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoYXdhaXQgZmlsZUV4aXN0cyhzYWZlRmlsZVBhdGgpKSB7XG4gICAgICAgICAgY29uc3QgY29udGVudCA9IGF3YWl0IGZzLnJlYWRGaWxlKHNhZmVGaWxlUGF0aCk7XG4gICAgICAgICAgcmV0dXJuIHJlcGx5LnR5cGUobWltZUxvb2t1cChzYWZlRmlsZVBhdGgpIHx8IFwiYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtXCIpLnNlbmQoY29udGVudCk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBDU1IgZmFsbGJhY2s6IGluZGV4Lmh0bWwg7ISc67mZXG4gICAgICAgIGNvbnN0IGluZGV4UGF0aCA9IHBhdGguam9pbih3ZWJEaXN0UGF0aCwgXCJpbmRleC5odG1sXCIpO1xuICAgICAgICByZXR1cm4gcmVwbHkudHlwZShcInRleHQvaHRtbFwiKS5zZW5kKGF3YWl0IGZzLnJlYWRGaWxlKGluZGV4UGF0aCwgXCJ1dGYtOFwiKSk7XG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgY29uc29sZS5sb2coYOKckyBTdGF0aWMgd2ViIHNlcnZlciBjb25maWd1cmVkIHdpdGggJHtzc3JBdmFpbGFibGUgPyBcIlNTUlwiIDogXCJDU1Igb25seVwifSBzdXBwb3J0YCk7XG4gIH1cblxuICBjcmVhdGVBcGlIYW5kbGVyKFxuICAgIGFwaTogRXh0ZW5kZWRBcGksXG4gICAgY29uZmlnOiBTb25hbXVGYXN0aWZ5Q29uZmlnLFxuICApOiAocmVxdWVzdDogRmFzdGlmeVJlcXVlc3QsIHJlcGx5OiBGYXN0aWZ5UmVwbHkpID0+IFByb21pc2U8dW5rbm93bj4ge1xuICAgIHJldHVybiBhc3luYyAocmVxdWVzdDogRmFzdGlmeVJlcXVlc3QsIHJlcGx5OiBGYXN0aWZ5UmVwbHkpOiBQcm9taXNlPHVua25vd24+ID0+IHtcbiAgICAgIC8vIENvbnRleHQg7IOd7ISxXG4gICAgICBjb25zdCBjb250ZXh0OiBDb250ZXh0ID0gYXdhaXQgdGhpcy5jcmVhdGVDb250ZXh0KGNvbmZpZywgcmVxdWVzdCwgcmVwbHkpO1xuXG4gICAgICByZXR1cm4gdGhpcy5hc3luY0xvY2FsU3RvcmFnZS5ydW4oeyBjb250ZXh0IH0sIGFzeW5jICgpID0+IHtcbiAgICAgICAgLy8gZ3VhcmRzIOyymOumrFxuICAgICAgICAoYXBpLm9wdGlvbnMuZ3VhcmRzID8/IFtdKS5ldmVyeSgoZ3VhcmQpID0+IGNvbmZpZy5ndWFyZEhhbmRsZXIoZ3VhcmQsIHJlcXVlc3QsIGFwaSkpO1xuXG4gICAgICAgIC8vIO2MjOudvOuvuO2EsCDsoJXrs7TroZwgem9kIOyKpO2CpOuniCDruYzrk5xcbiAgICAgICAgY29uc3QgeyBnZXRab2RPYmplY3RGcm9tQXBpIH0gPSBhd2FpdCBpbXBvcnQoXCIuL2NvZGUtY29udmVydGVyc1wiKTtcbiAgICAgICAgY29uc3QgUmVxVHlwZSA9IGdldFpvZE9iamVjdEZyb21BcGkoYXBpLCB0aGlzLnN5bmNlci50eXBlcyk7XG5cbiAgICAgICAgLy8gcmVxdWVzdCDtjIzsi7FcbiAgICAgICAgY29uc3Qgd2hpY2ggPSBhcGkub3B0aW9ucy5odHRwTWV0aG9kID09PSBcIkdFVFwiID8gXCJxdWVyeVwiIDogXCJib2R5XCI7XG4gICAgICAgIGxldCByZXFCb2R5OiB7XG4gICAgICAgICAgW2tleTogc3RyaW5nXTogdW5rbm93bjtcbiAgICAgICAgfTtcbiAgICAgICAgLy8g7YyM7J28IOyXheuhnOuTnCDsnojripQg6rK97JqwIOyehOyLnCDrjbDsnbTthLBcbiAgICAgICAgY29uc3QgZmlsZXM6IHtcbiAgICAgICAgICBidWZmZXJlZEZpbGVzOiBCdWZmZXJlZEZpbGVbXTtcbiAgICAgICAgICB1cGxvYWRlZEZpbGVzOiBVcGxvYWRlZEZpbGVbXTtcbiAgICAgICAgfSA9IHtcbiAgICAgICAgICBidWZmZXJlZEZpbGVzOiBbXSxcbiAgICAgICAgICB1cGxvYWRlZEZpbGVzOiBbXSxcbiAgICAgICAgfTtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IGJvZHkgPSAocmVxdWVzdFt3aGljaF0gPz8ge30pIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAgICAgICAgIGlmIChhcGkudXBsb2FkT3B0aW9ucykge1xuICAgICAgICAgICAgY29uc3QgcGFydHMgPSByZXF1ZXN0LnBhcnRzKHtcbiAgICAgICAgICAgICAgbGltaXRzOiBhcGkudXBsb2FkT3B0aW9ucy5saW1pdHMsXG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgLy8gRm9ybURhdGHsnZggZmllbGTrk6TsnYQg7J6E7Iuc66GcIOyggOyepVxuICAgICAgICAgICAgY29uc3QgZmllbGRzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG5cbiAgICAgICAgICAgIGlmIChhcGkudXBsb2FkT3B0aW9ucy5jb25zdW1lID09PSBcImJ1ZmZlclwiIHx8ICFhcGkudXBsb2FkT3B0aW9ucy5jb25zdW1lKSB7XG4gICAgICAgICAgICAgIC8vIEJ1ZmZlciDrqqjrk5w6IOuplOuqqOumrOyXkCDroZzrk5xcbiAgICAgICAgICAgICAgZm9yIGF3YWl0IChjb25zdCBwYXJ0IG9mIHBhcnRzKSB7XG4gICAgICAgICAgICAgICAgaWYgKHBhcnQudHlwZSA9PT0gXCJmaWxlXCIpIHtcbiAgICAgICAgICAgICAgICAgIC8vIENSSVRJQ0FMOiDtjIzsnbwg7Iqk7Yq466a87J2EIOymieyLnCBjb25zdW1l7ZW07JW8IOuLpOydjCBwYXJ066GcIOuEmOyWtOqwiCDsiJgg7J6I7J2MXG4gICAgICAgICAgICAgICAgICAvLyDsnbQg7Zi47Lac7J20IOyXhuycvOuptCDsooXsooUgbXVsdGlwYXJ0IO2MjOyLseydtCBwZW5kaW5nIOyDge2DnOuhnCDtg4DsnoTslYTsm4Mg67Cc7IOdXG4gICAgICAgICAgICAgICAgICBjb25zdCBidWZmZXIgPSBhd2FpdCBwYXJ0LnRvQnVmZmVyKCk7XG4gICAgICAgICAgICAgICAgICBmaWxlcy5idWZmZXJlZEZpbGVzLnB1c2gobmV3IEJ1ZmZlcmVkRmlsZShwYXJ0LCBidWZmZXIpKTtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHBhcnQudHlwZSA9PT0gXCJmaWVsZFwiKSB7XG4gICAgICAgICAgICAgICAgICBmaWVsZHNbcGFydC5maWVsZG5hbWVdID0gU3RyaW5nKHBhcnQudmFsdWUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIGlmIChhcGkudXBsb2FkT3B0aW9ucy5jb25zdW1lID09PSBcInN0cmVhbVwiKSB7XG4gICAgICAgICAgICAgIC8vIFN0cmVhbSDrqqjrk5w6IOymieyLnCDsoIDsnqXshozroZwg7Iqk7Yq466as67CNXG4gICAgICAgICAgICAgIGNvbnN0IGRpc2tOYW1lID0gYXBpLnVwbG9hZE9wdGlvbnMuZGVzdGluYXRpb247XG4gICAgICAgICAgICAgIGNvbnN0IGRpc2sgPSB0aGlzLnN0b3JhZ2UudXNlKGRpc2tOYW1lKTtcblxuICAgICAgICAgICAgICAvLyDsmrDshKDsiJzsnIQ6IOuNsOy9lOugiOydtO2EsCA+IOyghOyXrSDshKTsoJUgPiDquLDrs7jqsJJcbiAgICAgICAgICAgICAgY29uc3Qga2V5R2VuZXJhdG9yOiBLZXlHZW5lcmF0b3IgPVxuICAgICAgICAgICAgICAgIGFwaS51cGxvYWRPcHRpb25zLmtleUdlbmVyYXRvciA/P1xuICAgICAgICAgICAgICAgIHRoaXMuY29uZmlnLnNlcnZlci5zdG9yYWdlPy5rZXlHZW5lcmF0b3IgPz9cbiAgICAgICAgICAgICAgICBkZWZhdWx0S2V5R2VuZXJhdG9yO1xuXG4gICAgICAgICAgICAgIGZvciBhd2FpdCAoY29uc3QgcGFydCBvZiBwYXJ0cykge1xuICAgICAgICAgICAgICAgIGlmIChwYXJ0LnR5cGUgPT09IFwiZmlsZVwiKSB7XG4gICAgICAgICAgICAgICAgICBjb25zdCBrZXkgPSBhd2FpdCBrZXlHZW5lcmF0b3Ioe1xuICAgICAgICAgICAgICAgICAgICBmaWxlbmFtZTogcGFydC5maWxlbmFtZSxcbiAgICAgICAgICAgICAgICAgICAgbWltZXR5cGU6IHBhcnQubWltZXR5cGUsXG4gICAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgICAgYXdhaXQgZGlzay5wdXRTdHJlYW0oa2V5LCBwYXJ0LmZpbGUsIHtcbiAgICAgICAgICAgICAgICAgICAgY29udGVudFR5cGU6IHBhcnQubWltZXR5cGUsXG4gICAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgICAgY29uc3QgdXJsID0gYXdhaXQgZGlzay5nZXRVcmwoa2V5KTtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IHNpZ25lZFVybCA9IGF3YWl0IGRpc2suZ2V0U2lnbmVkVXJsKGtleSk7XG5cbiAgICAgICAgICAgICAgICAgIGZpbGVzLnVwbG9hZGVkRmlsZXMucHVzaChcbiAgICAgICAgICAgICAgICAgICAgbmV3IFVwbG9hZGVkRmlsZSh7XG4gICAgICAgICAgICAgICAgICAgICAgZmlsZW5hbWU6IHBhcnQuZmlsZW5hbWUsXG4gICAgICAgICAgICAgICAgICAgICAgbWltZXR5cGU6IHBhcnQubWltZXR5cGUsXG4gICAgICAgICAgICAgICAgICAgICAgc2l6ZTogcGFydC5maWxlLmJ5dGVzUmVhZCxcbiAgICAgICAgICAgICAgICAgICAgICB1cmwsXG4gICAgICAgICAgICAgICAgICAgICAgc2lnbmVkVXJsLFxuICAgICAgICAgICAgICAgICAgICAgIGtleSxcbiAgICAgICAgICAgICAgICAgICAgICBkaXNrTmFtZSxcbiAgICAgICAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAocGFydC50eXBlID09PSBcImZpZWxkXCIpIHtcbiAgICAgICAgICAgICAgICAgIGZpZWxkc1twYXJ0LmZpZWxkbmFtZV0gPSBTdHJpbmcocGFydC52YWx1ZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIHFz66GcIOykkeyyqSDqtazsobAg7YyM7IuxOiBwYXJhbXNbY2F0ZWdvcnldIOKGkiB7IHBhcmFtczogeyBjYXRlZ29yeTogXCJ0ZXN0XCIgfSB9XG4gICAgICAgICAgICBjb25zdCBxcyA9IGF3YWl0IGltcG9ydChcInFzXCIpO1xuICAgICAgICAgICAgY29uc3QgcGFyc2VkID0gcXMuZGVmYXVsdC5wYXJzZShmaWVsZHMpO1xuICAgICAgICAgICAgT2JqZWN0LmFzc2lnbihib2R5LCBwYXJzZWQpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGNvbnN0IHsgZmFzdGlmeUNhc3RlciB9ID0gYXdhaXQgaW1wb3J0KFwiLi9jYXN0ZXJcIik7XG4gICAgICAgICAgcmVxQm9keSA9IGZhc3RpZnlDYXN0ZXIoUmVxVHlwZSkucGFyc2UoYm9keSk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICBjb25zdCB7IFpvZEVycm9yIH0gPSBhd2FpdCBpbXBvcnQoXCJ6b2RcIik7XG4gICAgICAgICAgaWYgKGUgaW5zdGFuY2VvZiBab2RFcnJvcikge1xuICAgICAgICAgICAgY29uc3QgeyBodW1hbml6ZVpvZEVycm9yIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi91dGlscy96b2QtZXJyb3JcIik7XG4gICAgICAgICAgICBjb25zdCBtZXNzYWdlcyA9IGh1bWFuaXplWm9kRXJyb3IoZSlcbiAgICAgICAgICAgICAgLm1hcCgoaXNzdWUpID0+IGlzc3VlLm1lc3NhZ2UpXG4gICAgICAgICAgICAgIC5qb2luKFwiIFwiKTtcbiAgICAgICAgICAgIGNvbnN0IHsgQmFkUmVxdWVzdEV4Y2VwdGlvbiB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vZXhjZXB0aW9ucy9zby1leGNlcHRpb25zXCIpO1xuICAgICAgICAgICAgdGhyb3cgbmV3IEJhZFJlcXVlc3RFeGNlcHRpb24obWVzc2FnZXMgYXMgTG9jYWxpemVkU3RyaW5nLCB7XG4gICAgICAgICAgICAgIHpvZEVycm9yOiBlLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRocm93IGU7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gQ29udGVudC1UeXBlXG4gICAgICAgIHJlcGx5LnR5cGUoYXBpLm9wdGlvbnMuY29udGVudFR5cGUgPz8gXCJhcHBsaWNhdGlvbi9qc29uXCIpO1xuXG4gICAgICAgIC8vIENhY2hlLUNvbnRyb2wg7Zek642UIOyEpOyglVxuICAgICAgICBjb25zdCBhcGlDYWNoZUNvbmZpZyA9IHRoaXMuZ2V0QXBpQ2FjaGVDb250cm9sKGFwaSwgcmVxdWVzdCwgY29uZmlnKTtcbiAgICAgICAgaWYgKGFwaUNhY2hlQ29uZmlnKSB7XG4gICAgICAgICAgYXBwbHlDYWNoZUhlYWRlcnMocmVwbHksIGFwaUNhY2hlQ29uZmlnKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIOyXheuhnOuTnCDsmLXshZjsnbQg7J6I64qUIOqyveyasCDtjIzsnbwg642w7J207YSw66W8IENvbnRleHTsl5Ag7LaU6rCAXG4gICAgICAgIGlmIChhcGkudXBsb2FkT3B0aW9ucykge1xuICAgICAgICAgIGNvbnN0IGNvbnN1bWUgPSBhcGkudXBsb2FkT3B0aW9ucy5jb25zdW1lID8/IFwiYnVmZmVyXCI7XG4gICAgICAgICAgaWYgKGNvbnN1bWUgPT09IFwiYnVmZmVyXCIpIHtcbiAgICAgICAgICAgIGNvbnRleHQuYnVmZmVyZWRGaWxlcyA9IGZpbGVzLmJ1ZmZlcmVkRmlsZXM7XG4gICAgICAgICAgfSBlbHNlIGlmIChjb25zdW1lID09PSBcInN0cmVhbVwiKSB7XG4gICAgICAgICAgICBjb250ZXh0LnVwbG9hZGVkRmlsZXMgPSBmaWxlcy51cGxvYWRlZEZpbGVzO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIOuqqOuNuCDrqZTshozrk5wgYXJncyDsg53shLHtlZjsl6wg7Zi47LacXG4gICAgICAgIGNvbnN0IHsgQXBpUGFyYW1UeXBlIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi90eXBlcy90eXBlc1wiKTtcbiAgICAgICAgY29uc3QgYXJncyA9IGFwaS5wYXJhbWV0ZXJzLm1hcCgocGFyYW0pID0+IHtcbiAgICAgICAgICAvLyBDb250ZXh0IOyduOygneyFmFxuICAgICAgICAgIGlmIChBcGlQYXJhbVR5cGUuaXNDb250ZXh0KHBhcmFtLnR5cGUpKSB7XG4gICAgICAgICAgICByZXR1cm4gY29udGV4dDtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIHJlcUJvZHlbcGFyYW0ubmFtZV07XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICByZXR1cm4gdGhpcy5pbnZva2VNb2RlbE1ldGhvZChhcGksIGFyZ3MsIHJlcGx5KTtcbiAgICAgIH0pO1xuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogVVJM7JeQ7IScIHBhdGggcGFyYW1z66W8IOy2lOy2nO2VqeuLiOuLpC5cbiAgICog7JiIOiBwYXR0ZXJuPVwiL2FkbWluL2NvbXBhbmllcy86Y29tcGFueUlkXCIsIHVybD1cIi9hZG1pbi9jb21wYW5pZXMvMTIzXCIg4oaSIHsgY29tcGFueUlkOiBcIjEyM1wiIH1cbiAgICovXG4gIHByaXZhdGUgZXh0cmFjdFBhdGhQYXJhbXMocGF0dGVybjogc3RyaW5nLCB1cmw6IHN0cmluZyk6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4ge1xuICAgIGNvbnN0IHBhdHRlcm5QYXJ0cyA9IHBhdHRlcm4uc3BsaXQoXCIvXCIpLmZpbHRlcihCb29sZWFuKTtcbiAgICBjb25zdCB1cmxQYXJ0cyA9IHRoaXMuZ2V0UGF0aG5hbWVGcm9tVXJsKHVybCkuc3BsaXQoXCIvXCIpLmZpbHRlcihCb29sZWFuKTtcbiAgICBjb25zdCBwYXJhbXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fTtcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcGF0dGVyblBhcnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBpZiAocGF0dGVyblBhcnRzW2ldLnN0YXJ0c1dpdGgoXCI6XCIpKSB7XG4gICAgICAgIHBhcmFtc1twYXR0ZXJuUGFydHNbaV0uc2xpY2UoMSldID0gdXJsUGFydHNbaV07XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBwYXJhbXM7XG4gIH1cblxuICBwcml2YXRlIGlzUGF0aFBhdHRlcm5NYXRjaChwYXR0ZXJuOiBzdHJpbmcsIHVybDogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgY29uc3QgcGF0dGVyblBhcnRzID0gcGF0dGVybi5zcGxpdChcIi9cIikuZmlsdGVyKEJvb2xlYW4pO1xuICAgIGNvbnN0IHVybFBhcnRzID0gdGhpcy5nZXRQYXRobmFtZUZyb21VcmwodXJsKS5zcGxpdChcIi9cIikuZmlsdGVyKEJvb2xlYW4pO1xuXG4gICAgaWYgKHBhdHRlcm5QYXJ0cy5sZW5ndGggIT09IHVybFBhcnRzLmxlbmd0aCkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcGF0dGVyblBhcnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBwYXR0ZXJuUGFydCA9IHBhdHRlcm5QYXJ0c1tpXTtcbiAgICAgIGNvbnN0IHVybFBhcnQgPSB1cmxQYXJ0c1tpXTtcbiAgICAgIGlmIChwYXR0ZXJuUGFydC5zdGFydHNXaXRoKFwiOlwiKSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIGlmIChwYXR0ZXJuUGFydCAhPT0gdXJsUGFydCkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICBwcml2YXRlIGdldFBhdGhuYW1lRnJvbVVybCh1cmw6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHVybC5zcGxpdChcIj9cIilbMF07XG4gIH1cblxuICBwcml2YXRlIHJlc29sdmVQYXRoV2l0aGluQmFzZURpcihiYXNlRGlyOiBzdHJpbmcsIGlucHV0UGF0aDogc3RyaW5nKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGRlY29kZWQgPSBkZWNvZGVVUklDb21wb25lbnQoaW5wdXRQYXRoKS5yZXBsYWNlKC9cXFxcL2csIFwiL1wiKTtcbiAgICAgIGlmIChkZWNvZGVkLmluY2x1ZGVzKFwiXFwwXCIpKSB7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuICAgICAgY29uc3QgcmVsYXRpdmVQYXRoID0gZGVjb2RlZC5yZXBsYWNlKC9eXFwvKy8sIFwiXCIpO1xuICAgICAgY29uc3QgcmVzb2x2ZWRQYXRoID0gcGF0aC5yZXNvbHZlKGJhc2VEaXIsIHJlbGF0aXZlUGF0aCk7XG4gICAgICBjb25zdCByZWxhdGl2ZUZyb21CYXNlID0gcGF0aC5yZWxhdGl2ZShiYXNlRGlyLCByZXNvbHZlZFBhdGgpO1xuICAgICAgaWYgKHJlbGF0aXZlRnJvbUJhc2Uuc3RhcnRzV2l0aChcIi4uXCIpIHx8IHBhdGguaXNBYnNvbHV0ZShyZWxhdGl2ZUZyb21CYXNlKSkge1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cbiAgICAgIHJldHVybiByZXNvbHZlZFBhdGg7XG4gICAgfSBjYXRjaCB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQVBJIOydkeuLteyXkCDsoIHsmqntlaAgQ2FjaGUtQ29udHJvbCDshKTsoJXsnYQg6rKw7KCV7ZWp64uI64ukLlxuICAgKiDsmrDshKDsiJzsnIQ6IOqwnOuzhCDsp4DsoJUgPiBjYWNoZUNvbnRyb2xIYW5kbGVyXG4gICAqL1xuICBwcml2YXRlIGdldEFwaUNhY2hlQ29udHJvbChcbiAgICBhcGk6IEV4dGVuZGVkQXBpLFxuICAgIHJlcXVlc3Q6IEZhc3RpZnlSZXF1ZXN0LFxuICAgIGNvbmZpZzogU29uYW11RmFzdGlmeUNvbmZpZyxcbiAgKSB7XG4gICAgLy8g642w7L2U66CI7J207YSwIOyEpOyglSDsmrDshKBcbiAgICBpZiAoYXBpLm9wdGlvbnMuY2FjaGVDb250cm9sKSB7XG4gICAgICByZXR1cm4gYXBpLm9wdGlvbnMuY2FjaGVDb250cm9sO1xuICAgIH1cblxuICAgIC8vIOyghOyXrSDtlbjrk6Trn6xcbiAgICBpZiAoY29uZmlnLmNhY2hlQ29udHJvbEhhbmRsZXIpIHtcbiAgICAgIGNvbnN0IGNhY2hlUmVxOiBDYWNoZUNvbnRyb2xSZXF1ZXN0ID0ge1xuICAgICAgICB0eXBlOiBcImFwaVwiLFxuICAgICAgICB1cmw6IHJlcXVlc3QudXJsLFxuICAgICAgICBwYXRoOiByZXF1ZXN0LnJvdXRlT3B0aW9ucz8udXJsID8/IHJlcXVlc3QudXJsLnNwbGl0KFwiP1wiKVswXSxcbiAgICAgICAgbWV0aG9kOiByZXF1ZXN0Lm1ldGhvZCxcbiAgICAgICAgYXBpLFxuICAgICAgfTtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGNvbmZpZy5jYWNoZUNvbnRyb2xIYW5kbGVyKGNhY2hlUmVxKTtcbiAgICAgIGlmIChyZXN1bHQpIHJldHVybiByZXN1bHQ7XG4gICAgfVxuXG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICAvKipcbiAgICogU1NS7JqpIEFQSSDtmLjstpwgKEhUVFAg7Jik67KE7Zek65OcIOyXhuydtCDsp4HsoJEg7Zi47LacKVxuICAgKiBjcmVhdGVBcGlIYW5kbGVy7J2YIOuhnOyngeydhCDsnqzsgqzsmqntlZjrkJgsIHJlcXVlc3Qg7YyM7IuxIOuMgOyLoCBwYXJhbXMg7KeB7KCRIOyCrOyaqVxuICAgKi9cbiAgYXN5bmMgaW52b2tlQXBpRm9yU1NSKFxuICAgIGFwaTogRXh0ZW5kZWRBcGksXG4gICAgLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0V4cGxpY2l0QW55OiBTU1Lsl5DshJwg64uk7JaR7ZWcIO2DgOyeheydmCBwYXJhbXPrpbwg67Cb7JWE7JW8IO2VqFxuICAgIHBhcmFtczogYW55W10sXG4gICAgY29uZmlnOiBTb25hbXVGYXN0aWZ5Q29uZmlnLFxuICAgIHJlcXVlc3Q6IEZhc3RpZnlSZXF1ZXN0LFxuICAgIHJlcGx5OiBGYXN0aWZ5UmVwbHksXG4gICk6IFByb21pc2U8dW5rbm93bj4ge1xuICAgIC8vIENvbnRleHQg7IOd7ISxICjquLDsobQg66mU7IaM65OcIOyerOyCrOyaqSlcbiAgICBjb25zdCBjb250ZXh0ID0gYXdhaXQgdGhpcy5jcmVhdGVDb250ZXh0KGNvbmZpZywgcmVxdWVzdCwgcmVwbHkpO1xuXG4gICAgcmV0dXJuIHRoaXMuYXN5bmNMb2NhbFN0b3JhZ2UucnVuKHsgY29udGV4dCB9LCBhc3luYyAoKSA9PiB7XG4gICAgICAvLyBhcmdzIOyDneyEsTogQ29udGV4dCDtjIzrnbzrr7jthLDripQg7KO87J6FLCDrgpjrqLjsp4DripQgcGFyYW1z7JeQ7IScIOqwgOyguOyYpOq4sFxuICAgICAgY29uc3QgeyBBcGlQYXJhbVR5cGUgfSA9IGF3YWl0IGltcG9ydChcIi4uL3R5cGVzL3R5cGVzXCIpO1xuICAgICAgbGV0IHBhcmFtc0luZGV4ID0gMDtcbiAgICAgIGNvbnN0IGFyZ3MgPSBhcGkucGFyYW1ldGVycy5tYXAoKHBhcmFtKSA9PiB7XG4gICAgICAgIGlmIChBcGlQYXJhbVR5cGUuaXNDb250ZXh0KHBhcmFtLnR5cGUpKSB7XG4gICAgICAgICAgcmV0dXJuIGNvbnRleHQ7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHBhcmFtc1twYXJhbXNJbmRleCsrXTtcbiAgICAgIH0pO1xuXG4gICAgICAvLyDrqqjrjbgg66mU7ISc65OcIO2YuOy2nCAo6riw7KG0IOuplOyEnOuTnCDsnqzsgqzsmqkpXG4gICAgICByZXR1cm4gdGhpcy5pbnZva2VNb2RlbE1ldGhvZChhcGksIGFyZ3MsIHJlcGx5KTtcbiAgICB9KTtcbiAgfVxuXG4gIGFzeW5jIGludm9rZU1vZGVsTWV0aG9kKFxuICAgIGFwaTogRXh0ZW5kZWRBcGksXG4gICAgYXJnczogdW5rbm93bltdLFxuICAgIHJlcGx5OiBGYXN0aWZ5UmVwbHksXG4gICk6IFByb21pc2U8dW5rbm93bj4ge1xuICAgIGNvbnN0IG1vZGVsID0gdGhpcy5zeW5jZXIubW9kZWxzW2FwaS5tb2RlbE5hbWVdO1xuICAgIC8vIGJpb21lLWlnbm9yZSBsaW50L3N1c3BpY2lvdXMvbm9FeHBsaWNpdEFueTogbW9kZWzsnYAg66qo6424IOyduOyKpO2EtOyKpOydtOuvgOuhnCDrqZTshJzrk5wg7Zi47LacIOqwgOuKpVxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IChtb2RlbCBhcyBhbnkpW2FwaS5tZXRob2ROYW1lXS5hcHBseShtb2RlbCwgYXJncyk7XG4gICAgcmVwbHkudHlwZShhcGkub3B0aW9ucy5jb250ZW50VHlwZSA/PyBcImFwcGxpY2F0aW9uL2pzb25cIik7XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgYXN5bmMgY3JlYXRlQ29udGV4dChcbiAgICBjb25maWc6IFNvbmFtdUZhc3RpZnlDb25maWcsXG4gICAgcmVxdWVzdDogRmFzdGlmeVJlcXVlc3QsXG4gICAgcmVwbHk6IEZhc3RpZnlSZXBseSxcbiAgKTogUHJvbWlzZTxDb250ZXh0PiB7XG4gICAgLy8gY3JlYXRlU1NFRmFjdG9yeSDtlajsiJjsl5Ag66+466asIHJlcXVlc3TsnZggc29ja2V06rO8IHJlcGx566W8IOuwlOyduOuUqS5cbiAgICBjb25zdCB7IGNyZWF0ZVNTRUZhY3RvcnkgfSA9IGF3YWl0IGltcG9ydChcIi4uL3N0cmVhbS9zc2VcIik7XG4gICAgY29uc3QgY3JlYXRlU1NFID0gKDxUIGV4dGVuZHMgWm9kT2JqZWN0PihcbiAgICAgIF9yZXF1ZXN0OiBGYXN0aWZ5UmVxdWVzdCxcbiAgICAgIF9yZXBseTogRmFzdGlmeVJlcGx5LFxuICAgICAgX2V2ZW50czogVCxcbiAgICApID0+IGNyZWF0ZVNTRUZhY3RvcnkoX3JlcXVlc3Quc29ja2V0LCBfcmVwbHksIF9ldmVudHMpKS5iaW5kKG51bGwsIHJlcXVlc3QsIHJlcGx5KTtcblxuICAgIC8vIGxvY2FsZSDqsJDsp4BcbiAgICBjb25zdCBsb2NhbGUgPVxuICAgICAgdGhpcy5kZXRlY3RMb2NhbGUocmVxdWVzdC5oZWFkZXJzW1wiYWNjZXB0LWxhbmd1YWdlXCJdLCB0aGlzLmNvbmZpZy5pMThuLnN1cHBvcnRlZExvY2FsZXMpID8/XG4gICAgICB0aGlzLmNvbmZpZy5pMThuLmRlZmF1bHRMb2NhbGU7XG5cbiAgICAvLyBhdXRoIGNvbnRleHQg7LaU6rCAXG4gICAgY29uc3QgaGVhZGVycyA9IGNvbnZlcnRGYXN0aWZ5SGVhZGVyc1RvU3RhbmRhcmQocmVxdWVzdC5oZWFkZXJzKTtcbiAgICBjb25zdCBzZXNzaW9uID0gKGF3YWl0IHRoaXMuX2F1dGg/LmFwaS5nZXRTZXNzaW9uKHsgaGVhZGVycyB9KSkgPz8gbnVsbDtcblxuICAgIGNvbnN0IGNvbnRleHQ6IENvbnRleHQgPSB7XG4gICAgICAuLi4oYXdhaXQgUHJvbWlzZS5yZXNvbHZlKFxuICAgICAgICBjb25maWcuY29udGV4dFByb3ZpZGVyKFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIHJlcXVlc3QsXG4gICAgICAgICAgICByZXBseSxcbiAgICAgICAgICAgIGhlYWRlcnM6IHJlcXVlc3QuaGVhZGVycyxcbiAgICAgICAgICAgIGNyZWF0ZVNTRSxcbiAgICAgICAgICAgIG5haXRlU3RvcmU6IE5haXRlLmNyZWF0ZVN0b3JlKCksXG4gICAgICAgICAgICBsb2NhbGUsXG4gICAgICAgICAgICAvLyBhdXRoXG4gICAgICAgICAgICB1c2VyOiBzZXNzaW9uPy51c2VyID8/IG51bGwsXG4gICAgICAgICAgICBzZXNzaW9uOiBzZXNzaW9uPy5zZXNzaW9uID8/IG51bGwsXG4gICAgICAgICAgfSxcbiAgICAgICAgICByZXF1ZXN0LFxuICAgICAgICAgIHJlcGx5LFxuICAgICAgICApLFxuICAgICAgKSksXG4gICAgfTtcbiAgICByZXR1cm4gY29udGV4dDtcbiAgfVxuXG4gIC8qKlxuICAgKiBBY2NlcHQtTGFuZ3VhZ2Ug7Zek642U7JeQ7IScIOyngOybkO2VmOuKlCBsb2NhbGXsnYQg7LC+7Iq164uI64ukLlxuICAgKiBAZXhhbXBsZSBcImtvLUtSLGtvO3E9MC45LGVuO3E9MC44XCIg4oaSIFwia29cIlxuICAgKi9cbiAgcHJpdmF0ZSBkZXRlY3RMb2NhbGUoXG4gICAgYWNjZXB0TGFuZ3VhZ2U6IHN0cmluZyB8IHVuZGVmaW5lZCxcbiAgICBzdXBwb3J0ZWQ6IHN0cmluZ1tdLFxuICApOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICAgIGlmICghYWNjZXB0TGFuZ3VhZ2UpIHJldHVybiB1bmRlZmluZWQ7XG5cbiAgICAvLyBBY2NlcHQtTGFuZ3VhZ2U6IGtvLUtSLGtvO3E9MC45LGVuO3E9MC44XG4gICAgY29uc3QgbGFuZ3MgPSBhY2NlcHRMYW5ndWFnZS5zcGxpdChcIixcIikubWFwKChsYW5nKSA9PiB7XG4gICAgICBjb25zdCBbY29kZV0gPSBsYW5nLnNwbGl0KFwiO1wiKTtcbiAgICAgIHJldHVybiBjb2RlLnRyaW0oKS5zcGxpdChcIi1cIilbMF07IC8vIGtvLUtSIOKGkiBrb1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIGxhbmdzLmZpbmQoKGxhbmcpID0+IHN1cHBvcnRlZC5pbmNsdWRlcyhsYW5nKSk7XG4gIH1cblxuICBhc3luYyBzdGFydFdhdGNoZXIoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3Qgd2F0Y2hQYXRoID0gW3BhdGguam9pbih0aGlzLmFwaVJvb3RQYXRoLCBcInNyY1wiKV07XG5cbiAgICBjb25zdCBjaG9raWRhciA9IChhd2FpdCBpbXBvcnQoXCJjaG9raWRhclwiKSkuZGVmYXVsdDtcbiAgICB0aGlzLndhdGNoZXIgPSBjaG9raWRhci53YXRjaCh3YXRjaFBhdGgsIHtcbiAgICAgIGlnbm9yZWQ6IChwYXRoLCBzdGF0cykgPT5cbiAgICAgICAgISFzdGF0cz8uaXNGaWxlKCkgJiYgIXBhdGguZW5kc1dpdGgoXCIudHNcIikgJiYgIXBhdGguZW5kc1dpdGgoXCIuanNvblwiKSxcbiAgICAgIHBlcnNpc3RlbnQ6IHRydWUsXG4gICAgICBpZ25vcmVJbml0aWFsOiB0cnVlLFxuICAgIH0pO1xuXG4gICAgdGhpcy53YXRjaGVyLm9uKFwiYWxsXCIsIGFzeW5jIChldmVudDogc3RyaW5nLCBmaWxlUGF0aDogc3RyaW5nKSA9PiB7XG4gICAgICBjb25zdCBhYnNvbHV0ZVBhdGggPSBmaWxlUGF0aCBhcyBBYnNvbHV0ZVBhdGg7XG4gICAgICBhc3NlcnQoXG4gICAgICAgIGFic29sdXRlUGF0aC5zdGFydHNXaXRoKHRoaXMuYXBpUm9vdFBhdGgpLFxuICAgICAgICBcIkZpbGUgcGF0aCBpcyBub3Qgd2l0aGluIHRoZSBBUEkgcm9vdCBwYXRoXCIsXG4gICAgICApO1xuXG4gICAgICBpZiAoZXZlbnQgIT09IFwiY2hhbmdlXCIgJiYgZXZlbnQgIT09IFwiYWRkXCIpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICB0cnkge1xuICAgICAgICAvLyBzb25hbXUuY29uZmlnLnRzIOuzgOqyvSDsi5wg7J6s7Iuc7J6RXG4gICAgICAgIGNvbnN0IGlzQ29uZmlnVHMgPSBmaWxlUGF0aCA9PT0gcGF0aC5qb2luKHRoaXMuYXBpUm9vdFBhdGgsIFwic3JjXCIsIFwic29uYW11LmNvbmZpZy50c1wiKTtcblxuICAgICAgICBpZiAoaXNDb25maWdUcykge1xuICAgICAgICAgIGNvbnN0IHJlbGF0aXZlUGF0aCA9IGZpbGVQYXRoLnJlcGxhY2UodGhpcy5hcGlSb290UGF0aCwgXCJhcGlcIik7XG4gICAgICAgICAgY29uc3QgY2hhbGsgPSAoYXdhaXQgaW1wb3J0KFwiY2hhbGtcIikpLmRlZmF1bHQ7XG4gICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICBjaGFsay5ib2xkKGBEZXRlY3RlZCgke2V2ZW50fSk6ICR7Y2hhbGsuYmx1ZShyZWxhdGl2ZVBhdGgpfSAtIFJlc3RhcnRpbmcuLi5gKSxcbiAgICAgICAgICApO1xuICAgICAgICAgIHByb2Nlc3Mua2lsbChwcm9jZXNzLnBpZCwgXCJTSUdVU1IyXCIpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGF3YWl0IHRoaXMuaGFuZGxlRmlsZUNoYW5nZShldmVudCwgYWJzb2x1dGVQYXRoKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihlKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIC8qXG4gICAgIEEgZnVuY3Rpb24gdGhhdCBhdXRvbWF0aWNhbGx5IGhhbmRsZXMgaW5pdCBhbmQgZGVzdHJveSB3aGVuIHVzaW5nIFNvbmFtdSB2aWEgc2NyaXB0cy5cbiAgKi9cbiAgYXN5bmMgcnVuU2NyaXB0KGZuOiAoKSA9PiBQcm9taXNlPHZvaWQ+KSB7XG4gICAgYXdhaXQgdGhpcy5pbml0KHRydWUsIGZhbHNlLCB1bmRlZmluZWQsIGZhbHNlKTtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgZm4oKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgYXdhaXQgdGhpcy5kZXN0cm95KCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyByZWdpc3RlclBsdWdpbnMoc2VydmVyOiBGYXN0aWZ5SW5zdGFuY2UsIHBsdWdpbnM6IFNvbmFtdVNlcnZlck9wdGlvbnNbXCJwbHVnaW5zXCJdKSB7XG4gICAgaWYgKCFwbHVnaW5zKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gY29tcHJlc3Mg7ZSM65+s6re47J247J2AIOuLpOuluCDtlIzrn6zqt7jsnbjrs7Tri6Qg66i87KCAIOuTseuhneuQmOyWtOyVvCDtlanri4jri6QuXG4gICAgaWYgKHBsdWdpbnMuY29tcHJlc3MpIHtcbiAgICAgIGNvbnN0IGNvbXByZXNzUGx1Z2luID0gKGF3YWl0IGltcG9ydChcIkBmYXN0aWZ5L2NvbXByZXNzXCIpKS5kZWZhdWx0O1xuICAgICAgY29uc3QgZGVmYXVsdE9wdGlvbnMgPSB7XG4gICAgICAgIHRocmVzaG9sZDogMTAyNCxcbiAgICAgICAgZW5jb2RpbmdzOiBbXCJiclwiLCBcImd6aXBcIiwgXCJkZWZsYXRlXCJdIGFzIChcImJyXCIgfCBcImd6aXBcIiB8IFwiZGVmbGF0ZVwiKVtdLFxuICAgICAgfTtcblxuICAgICAgaWYgKHBsdWdpbnMuY29tcHJlc3MgPT09IHRydWUpIHtcbiAgICAgICAgc2VydmVyLnJlZ2lzdGVyKGNvbXByZXNzUGx1Z2luLCBkZWZhdWx0T3B0aW9ucyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBzZXJ2ZXIucmVnaXN0ZXIoY29tcHJlc3NQbHVnaW4sIHtcbiAgICAgICAgICAuLi5kZWZhdWx0T3B0aW9ucyxcbiAgICAgICAgICAuLi5wbHVnaW5zLmNvbXByZXNzLFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCBwbHVnaW5zTW9kdWxlcyA9IHtcbiAgICAgIGNvcnM6IFwiQGZhc3RpZnkvY29yc1wiLFxuICAgICAgZm9ybWJvZHk6IFwiQGZhc3RpZnkvZm9ybWJvZHlcIixcbiAgICAgIG11bHRpcGFydDogXCJAZmFzdGlmeS9tdWx0aXBhcnRcIixcbiAgICAgIHFzOiBcImZhc3RpZnktcXNcIixcbiAgICAgIHNzZTogXCJmYXN0aWZ5LXNzZS12MlwiLFxuICAgICAgc3RhdGljOiBcIkBmYXN0aWZ5L3N0YXRpY1wiLFxuICAgIH0gYXMgY29uc3Q7XG5cbiAgICBjb25zdCByZWdpc3RlclBsdWdpbiA9IGFzeW5jIDxLIGV4dGVuZHMga2V5b2YgTm9uTnVsbGFibGU8dHlwZW9mIHBsdWdpbnM+PihcbiAgICAgIGtleTogSyxcbiAgICAgIHBsdWdpbk5hbWU6IHN0cmluZyxcbiAgICApID0+IHtcbiAgICAgIGNvbnN0IG9wdGlvbiA9IHBsdWdpbnNba2V5XTtcbiAgICAgIGlmICghb3B0aW9uKSByZXR1cm47XG5cbiAgICAgIGlmIChvcHRpb24gPT09IHRydWUpIHtcbiAgICAgICAgc2VydmVyLnJlZ2lzdGVyKChhd2FpdCBpbXBvcnQocGx1Z2luTmFtZSkpLmRlZmF1bHQpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgc2VydmVyLnJlZ2lzdGVyKChhd2FpdCBpbXBvcnQocGx1Z2luTmFtZSkpLmRlZmF1bHQsIG9wdGlvbik7XG4gICAgICB9XG4gICAgfTtcblxuICAgIGZvciAoY29uc3QgW2tleSwgcGx1Z2luTmFtZV0gb2YgT2JqZWN0LmVudHJpZXMocGx1Z2luc01vZHVsZXMpKSB7XG4gICAgICBhd2FpdCByZWdpc3RlclBsdWdpbihrZXkgYXMga2V5b2YgdHlwZW9mIHBsdWdpbnMsIHBsdWdpbk5hbWUpO1xuICAgIH1cblxuICAgIGlmIChwbHVnaW5zLmN1c3RvbSkge1xuICAgICAgcGx1Z2lucy5jdXN0b20oc2VydmVyKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogYmV0dGVyLWF1dGgg65287Jqw7Yq466W8IOuTseuhne2VqeuLiOuLpC5cbiAgICogL2FwaS9hdXRoLyog6rK966Gc66GcIOyduOymnSBBUEnqsIAg7J6Q64+ZIOuTseuhneuQqeuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcmVnaXN0ZXJCZXR0ZXJBdXRoKFxuICAgIHNlcnZlcjogRmFzdGlmeUluc3RhbmNlLFxuICAgIG9wdGlvbnM6IE5vbk51bGxhYmxlPFNvbmFtdVNlcnZlck9wdGlvbnNbXCJhdXRoXCJdPixcbiAgKSB7XG4gICAgaWYgKCFvcHRpb25zKSByZXR1cm47XG5cbiAgICBjb25zdCBiYXNlUGF0aCA9IG9wdGlvbnMuYmFzZVBhdGggPz8gXCIvYXBpL2F1dGhcIjtcblxuICAgIC8vIGJldHRlci1hdXRoIOudvOyasO2KuCDrk7HroZ1cbiAgICBzZXJ2ZXIucm91dGUoe1xuICAgICAgbWV0aG9kOiBbXCJHRVRcIiwgXCJQT1NUXCJdLFxuICAgICAgdXJsOiBgJHtiYXNlUGF0aH0vKmAsXG4gICAgICBoYW5kbGVyOiBhc3luYyAocmVxdWVzdCwgcmVwbHkpID0+IHtcbiAgICAgICAgY29uc3QgdXJsID0gbmV3IFVSTChyZXF1ZXN0LnVybCwgYGh0dHA6Ly8ke3JlcXVlc3QuaGVhZGVycy5ob3N0fWApO1xuICAgICAgICBjb25zdCBoZWFkZXJzID0gY29udmVydEZhc3RpZnlIZWFkZXJzVG9TdGFuZGFyZChyZXF1ZXN0LmhlYWRlcnMpO1xuICAgICAgICBjb25zdCByZXEgPSBuZXcgUmVxdWVzdCh1cmwudG9TdHJpbmcoKSwge1xuICAgICAgICAgIG1ldGhvZDogcmVxdWVzdC5tZXRob2QsXG4gICAgICAgICAgaGVhZGVycyxcbiAgICAgICAgICAuLi4ocmVxdWVzdC5ib2R5ID8geyBib2R5OiBKU09OLnN0cmluZ2lmeShyZXF1ZXN0LmJvZHkpIH0gOiB7fSksXG4gICAgICAgIH0pO1xuXG4gICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgdGhpcy5hdXRoLmhhbmRsZXIocmVxKTtcblxuICAgICAgICByZXBseS5zdGF0dXMocmVzcG9uc2Uuc3RhdHVzKTtcbiAgICAgICAgcmVzcG9uc2UuaGVhZGVycy5mb3JFYWNoKCh2YWx1ZTogc3RyaW5nLCBrZXk6IHN0cmluZykgPT4ge1xuICAgICAgICAgIHJlcGx5LmhlYWRlcihrZXksIHZhbHVlKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiByZXBseS5zZW5kKHJlc3BvbnNlLmJvZHkgPyBhd2FpdCByZXNwb25zZS50ZXh0KCkgOiBudWxsKTtcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBjb25zdCBjaGFsayA9IChhd2FpdCBpbXBvcnQoXCJjaGFsa1wiKSkuZGVmYXVsdDtcbiAgICBjb25zb2xlLmxvZyhjaGFsay5ncmVlbihg4pyTIGJldHRlci1hdXRoIHJlZ2lzdGVyZWQgYXQgJHtiYXNlUGF0aH0vKmApKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgaW5pdGlhbGl6ZUNhY2hlKGNvbmZpZzogQ2FjaGVDb25maWcgfCB1bmRlZmluZWQsIGZvclRlc3Rpbmc6IGJvb2xlYW4pIHtcbiAgICBjb25zdCB7IHNldENhY2hlTWFuYWdlclJlZiB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vY2FjaGUvZGVjb3JhdG9yXCIpO1xuXG4gICAgLy8g7YWM7Iqk7Yq4IO2ZmOqyveyXkOyEnCDrqZTrqqjrpqwg65Oc65287J2067KEIOyekOuPmSDsgqzsmqlcbiAgICBpZiAoZm9yVGVzdGluZykge1xuICAgICAgY29uc3QgeyBjcmVhdGVUZXN0Q2FjaGVNYW5hZ2VyIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9jYWNoZS9jYWNoZS1tYW5hZ2VyXCIpO1xuICAgICAgdGhpcy5fY2FjaGUgPSBjcmVhdGVUZXN0Q2FjaGVNYW5hZ2VyKCk7XG4gICAgICBzZXRDYWNoZU1hbmFnZXJSZWYodGhpcy5fY2FjaGUpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIOyEpOygleydtCDsl4bsnLzrqbQg7LqQ7IucIOu5hO2ZnOyEse2ZlFxuICAgIGlmICghY29uZmlnKSB7XG4gICAgICBzZXRDYWNoZU1hbmFnZXJSZWYobnVsbCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8g7ISk7KCV7JeQIOuUsOudvCBDYWNoZU1hbmFnZXIg7IOd7ISxXG4gICAgY29uc3QgeyBjcmVhdGVDYWNoZU1hbmFnZXIgfSA9IGF3YWl0IGltcG9ydChcIi4uL2NhY2hlL2NhY2hlLW1hbmFnZXJcIik7XG4gICAgdGhpcy5fY2FjaGUgPSBjcmVhdGVDYWNoZU1hbmFnZXIoY29uZmlnKTtcbiAgICBzZXRDYWNoZU1hbmFnZXJSZWYodGhpcy5fY2FjaGUpO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBpbml0aWFsaXplV29ya2Zsb3dzKG9wdGlvbnM6IFNvbmFtdVRhc2tPcHRpb25zIHwgdW5kZWZpbmVkKSB7XG4gICAgY29uc3QgeyBXb3JrZmxvd01hbmFnZXIgfSA9IGF3YWl0IGltcG9ydChcIi4uL3Rhc2tzL3dvcmtmbG93LW1hbmFnZXJcIik7XG4gICAgLy8gTk9URTogQHNvbmFtdS1raXQvdGFza3Mg7JWI7JeQ7ISgIGtuZXggY29uZmln66W8IOyImOygle2VmOq4sCDrlYzrrLjsl5AgY29ubmVjdGlvbuydtCDslYTri4wgY29uZmlnIOynuOuhnCDrs7Trg4Xri4jri6QuXG4gICAgdGhpcy5fd29ya2Zsb3dzID0gbmV3IFdvcmtmbG93TWFuYWdlcihEQi5nZXREQkNvbmZpZyhcIndcIikpO1xuICAgIGlmICghb3B0aW9ucykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IGVuYWJsZVdvcmtlciA9IG9wdGlvbnMuZW5hYmxlV29ya2VyID8/IGlzRGFlbW9uU2VydmVyKCk7XG4gICAgY29uc3QgZGVmYXVsdFdvcmtlck9wdGlvbnMgPSB7XG4gICAgICBjb25jdXJyZW5jeTogb3MuY3B1cygpLmxlbmd0aCAtIDEsXG4gICAgICB1c2VQdWJTdWI6IHRydWUsXG4gICAgICBsaXN0ZW5EZWxheTogNTAwLFxuICAgIH07XG5cbiAgICBpZiAoZW5hYmxlV29ya2VyKSB7XG4gICAgICB0aGlzLndvcmtmbG93cy5zZXR1cFdvcmtlcih7XG4gICAgICAgIC4uLmRlZmF1bHRXb3JrZXJPcHRpb25zLFxuICAgICAgICAuLi5vcHRpb25zLndvcmtlck9wdGlvbnMsXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGJvb3Qoc2VydmVyOiBGYXN0aWZ5SW5zdGFuY2UsIG9wdGlvbnM6IFNvbmFtdVNlcnZlck9wdGlvbnMpIHtcbiAgICBjb25zdCBwb3J0ID0gb3B0aW9ucy5saXN0ZW4/LnBvcnQgPz8gMzAwMDtcbiAgICBjb25zdCBob3N0ID0gb3B0aW9ucy5saXN0ZW4/Lmhvc3QgPz8gXCJsb2NhbGhvc3RcIjtcblxuICAgIHNlcnZlci5hZGRIb29rKFwib25DbG9zZVwiLCBhc3luYyAoKSA9PiB7XG4gICAgICBhd2FpdCBvcHRpb25zLmxpZmVjeWNsZT8ub25TaHV0ZG93bj8uKHNlcnZlcik7XG4gICAgICBhd2FpdCB0aGlzLndvcmtmbG93cy5kZXN0cm95KCk7XG4gICAgICBhd2FpdCB0aGlzLmRlc3Ryb3koKTtcbiAgICB9KTtcblxuICAgIGNvbnN0IHNodXRkb3duID0gYXN5bmMgKCkgPT4ge1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgc2VydmVyLmNsb3NlKCk7XG4gICAgICAgIHByb2Nlc3MuZXhpdCgwKTtcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBjb25zb2xlLmVycm9yKFwiRXJyb3IgZHVyaW5nIHNodXRkb3duOlwiLCBlcnIpO1xuICAgICAgICBwcm9jZXNzLmV4aXQoMSk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIHByb2Nlc3Mub24oXCJTSUdJTlRcIiwgc2h1dGRvd24pO1xuICAgIHByb2Nlc3Mub24oXCJTSUdURVJNXCIsIHNodXRkb3duKTtcblxuICAgIGlmIChvcHRpb25zLmxpZmVjeWNsZT8ub25FcnJvcikge1xuICAgICAgc2VydmVyLnNldEVycm9ySGFuZGxlcihvcHRpb25zLmxpZmVjeWNsZT8ub25FcnJvcik7XG4gICAgfVxuXG4gICAgc2VydmVyXG4gICAgICAubGlzdGVuKHsgcG9ydCwgaG9zdCB9KVxuICAgICAgLnRoZW4oYXN5bmMgKCkgPT4ge1xuICAgICAgICBhd2FpdCB0aGlzLndvcmtmbG93cy5zdGFydFdvcmtlcigpO1xuICAgICAgICBhd2FpdCBvcHRpb25zLmxpZmVjeWNsZT8ub25TdGFydD8uKHNlcnZlcik7XG4gICAgICB9KVxuICAgICAgLmNhdGNoKGFzeW5jIChlcnIpID0+IHtcbiAgICAgICAgY29uc3QgY2hhbGsgPSAoYXdhaXQgaW1wb3J0KFwiY2hhbGtcIikpLmRlZmF1bHQ7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoY2hhbGsucmVkKFwiRmFpbGVkIHRvIHN0YXJ0IHNlcnZlcjpcIiwgZXJyKSk7XG4gICAgICAgIGF3YWl0IHNodXRkb3duKCk7XG4gICAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgaGFuZGxlRmlsZUNoYW5nZShldmVudDogc3RyaW5nLCBmaWxlUGF0aDogQWJzb2x1dGVQYXRoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8g7LKrIOuyiOynuCDtjIzsnbzsnbTrqbQgSE1SIOyLnOyekSDsi5zqsIQg6riw66GdXG4gICAgaWYgKHRoaXMucGVuZGluZ0ZpbGVzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhpcy5obXJTdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuICAgIH1cbiAgICB0aGlzLnBlbmRpbmdGaWxlcy5wdXNoKGZpbGVQYXRoKTtcblxuICAgIGNvbnN0IHJlbGF0aXZlUGF0aCA9IHBhdGgucmVsYXRpdmUodGhpcy5hcGlSb290UGF0aCwgZmlsZVBhdGgpO1xuICAgIGNvbnN0IGNoYWxrID0gKGF3YWl0IGltcG9ydChcImNoYWxrXCIpKS5kZWZhdWx0O1xuICAgIGNvbnNvbGUubG9nKGNoYWxrLmJvbGQoYERldGVjdGVkKCR7ZXZlbnR9KTogJHtjaGFsay5ibHVlKHJlbGF0aXZlUGF0aCl9YCkpO1xuXG4gICAgYXdhaXQgdGhpcy5zeW5jZXIuc3luY0Zyb21XYXRjaGVyKGV2ZW50LCBmaWxlUGF0aCk7XG5cbiAgICAvLyDsspjrpqwg7JmE66OM65CcIO2MjOydvOydhCDrjIDquLAg66qp66Gd7JeQ7IScIOygnOqxsFxuICAgIHRoaXMucGVuZGluZ0ZpbGVzID0gdGhpcy5wZW5kaW5nRmlsZXMuc2xpY2UoMSk7XG5cbiAgICAvLyDrqqjrk6Ag7YyM7J28IOyymOumrOqwgCDsmYTro4zrkJjrqbQg7LWc7KKFIOuplOyLnOyngCDstpzroKVcbiAgICBpZiAodGhpcy5wZW5kaW5nRmlsZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICBhd2FpdCB0aGlzLmZpbmlzaEhNUigpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZmluaXNoSE1SKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMuc3luY2VyLnJlbmV3Q2hlY2tzdW1zKCk7XG5cbiAgICBjb25zdCBlbmRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICBjb25zdCB0b3RhbFRpbWUgPSBlbmRUaW1lIC0gdGhpcy5obXJTdGFydFRpbWU7XG4gICAgY29uc3QgW2NoYWxrLCB7IGNlbnRlclRleHQgfV0gPSBhd2FpdCBQcm9taXNlLmFsbChbXG4gICAgICAoYXdhaXQgaW1wb3J0KFwiY2hhbGtcIikpLmRlZmF1bHQsXG4gICAgICBpbXBvcnQoXCIuLi91dGlscy9jb25zb2xlLXV0aWxcIiksXG4gICAgXSk7XG4gICAgY29uc3QgbXNnID0gYEhNUiBEb25lISAke2NoYWxrLmJvbGQud2hpdGUoYCR7dG90YWxUaW1lfW1zYCl9YDtcblxuICAgIGNvbnNvbGUubG9nKGNoYWxrLmJsYWNrLmJnR3JlZW4oY2VudGVyVGV4dChtc2cpKSk7XG4gIH1cblxuICBhc3luYyBkZXN0cm95KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHsgQmFzZU1vZGVsIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9kYXRhYmFzZS9iYXNlLW1vZGVsXCIpO1xuICAgIC8vIOuovOyggCDsspjrpqztlbTslbztlaguXG4gICAgYXdhaXQgQmFzZU1vZGVsLmRlc3Ryb3koKTtcbiAgICBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoW1xuICAgICAgdGhpcy5fd29ya2Zsb3dzPy5kZXN0cm95KCkgPz8gUHJvbWlzZS5yZXNvbHZlKCksXG4gICAgICB0aGlzLl9jYWNoZT8uZGlzY29ubmVjdCgpID8/IFByb21pc2UucmVzb2x2ZSgpLFxuICAgICAgdGhpcy5fZGV2Vml0ZXN0TWFuYWdlcj8uc2h1dGRvd24oKSA/PyBQcm9taXNlLnJlc29sdmUoKSxcbiAgICAgIHRoaXMud2F0Y2hlcj8uY2xvc2UoKSA/PyBQcm9taXNlLnJlc29sdmUoKSxcbiAgICAgIGxvZ3RhcGVEaXNwb3NlKCksXG4gICAgXSk7XG4gIH1cbn1cblxuZXhwb3J0IGNvbnN0IFNvbmFtdSA9IG5ldyBTb25hbXVDbGFzcygpO1xuXG4vKipcbiAqIHN0cmVhbSDrqqjrk5zsl5DshJwg7YKkIOyDneyEsSDtlajsiJjqsIAg7KeA7KCV65CY7KeAIOyViuyVmOydhCDrlYwg7IKs7Jqp7ZWY64qUIOq4sOuzuCDtlajsiJjsnoXri4jri6QuXG4gKi9cbmZ1bmN0aW9uIGRlZmF1bHRLZXlHZW5lcmF0b3IoZmlsZTogeyBmaWxlbmFtZTogc3RyaW5nOyBtaW1ldHlwZTogc3RyaW5nIH0pOiBzdHJpbmcge1xuICBjb25zdCBleHQgPSBtaW1lLmV4dGVuc2lvbihmaWxlLm1pbWV0eXBlKSB8fCBcImJpblwiO1xuICBjb25zdCB0aW1lc3RhbXAgPSBEYXRlLm5vdygpO1xuICBjb25zdCByYW5kb20gPSBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zbGljZSgyLCA4KTtcbiAgcmV0dXJuIGB1cGxvYWRzLyR7dGltZXN0YW1wfS0ke3JhbmRvbX0uJHtleHR9YDtcbn1cbiJdLCJuYW1lcyI6WyJkaXNwb3NlIiwibG9ndGFwZURpc3Bvc2UiLCJhc3NlcnQiLCJBc3luY0xvY2FsU3RvcmFnZSIsImZzIiwibWltZSIsImxvb2t1cCIsIm1pbWVMb29rdXAiLCJvcyIsInBhdGgiLCJCQVNFX0ZJRUxEX01BUFBJTkdTIiwiY29udmVydEZhc3RpZnlIZWFkZXJzVG9TdGFuZGFyZCIsImNyZWF0ZU1vY2tTU0VGYWN0b3J5IiwiREIiLCJpc0RhZW1vblNlcnZlciIsIm1lcmdlIiwiTm90Rm91bmRFeGNlcHRpb24iLCJhcHBseUNhY2hlSGVhZGVycyIsIkNhY2hlUHJlc2V0cyIsInRvRmFzdGlmeUNvbXByZXNzT3B0aW9uIiwiU0QiLCJOYWl0ZSIsIkJ1ZmZlcmVkRmlsZSIsIlVwbG9hZGVkRmlsZSIsImV4aXN0cyIsImZpbGVFeGlzdHMiLCJnZXRTZWNyZXRzIiwiU29uYW11Q2xhc3MiLCJpc0luaXRpYWxpemVkIiwiZm9yVGVzdGluZyIsImFzeW5jTG9jYWxTdG9yYWdlIiwiZ2V0Q29udGV4dCIsInN0b3JlIiwiZ2V0U3RvcmUiLCJjb250ZXh0IiwicHJvY2VzcyIsImVudiIsIk5PREVfRU5WIiwicmVxdWVzdCIsInJlcGx5IiwiaGVhZGVycyIsImNyZWF0ZVNTRSIsInNjaGVtYSIsIm5haXRlU3RvcmUiLCJNYXAiLCJFcnJvciIsIl9hcGlSb290UGF0aCIsImFwaVJvb3RQYXRoIiwiYXBwUm9vdFBhdGgiLCJzcGxpdCIsInNlcCIsInNsaWNlIiwiam9pbiIsIl9kYkNvbmZpZyIsImRiQ29uZmlnIiwiX3N5bmNlciIsInN5bmNlciIsIl9jb25maWciLCJjb25maWciLCJzZWNyZXRzIiwiX3N0b3JhZ2UiLCJzdG9yYWdlIiwiX2NhY2hlIiwiY2FjaGUiLCJfd29ya2Zsb3dzIiwid29ya2Zsb3dzIiwiX2F1dGgiLCJhdXRoIiwiX2RldlZpdGVzdE1hbmFnZXIiLCJkZXZWaXRlc3RNYW5hZ2VyIiwibWFuYWdlciIsIndhdGNoZXIiLCJwZW5kaW5nRmlsZXMiLCJobXJTdGFydFRpbWUiLCJzZXJ2ZXIiLCJpbml0Rm9yVGVzdGluZyIsImluaXQiLCJ1bmRlZmluZWQiLCJkb1NpbGVudCIsImVuYWJsZVN5bmMiLCJjaGFsayIsImRlZmF1bHQiLCJjb25zb2xlIiwidGltZSIsImN5YW4iLCJmaW5kQXBpUm9vdFBhdGgiLCJsb2FkQ29uZmlnIiwiZGF0YWJhc2UiLCJkZWZhdWx0T3B0aW9ucyIsImNsaWVudCIsImNvbmZpZ3VyZUxvZ1RhcGUiLCJsb2dnaW5nIiwiZ2VuZXJhdGVEQkNvbmZpZyIsImxvZyIsImdyZWVuIiwiRW50aXR5TWFuYWdlciIsImF1dG9sb2FkIiwiaW5pdGlhbGl6ZUNhY2hlIiwiYXV0aENvbmZpZyIsIm1lcmdlZEZpZWxkTWFwcGluZ3MiLCJiZXR0ZXJBdXRoIiwic29uYW11S25leEFkYXB0ZXIiLCJpbml0aWFsaXplV29ya2Zsb3dzIiwidGFza3MiLCJTeW5jZXIiLCJhdXRvbG9hZFR5cGVzIiwiYXV0b2xvYWRNb2RlbHMiLCJhdXRvbG9hZEFwaXMiLCJhdXRvbG9hZFdvcmtmbG93cyIsIlRlbXBsYXRlTWFuYWdlciIsImF1dG9sb2FkU1NSUm91dGVzIiwiaXNMb2NhbCIsImlzVGVzdCIsInNldHVwQmlvbWUiLCJpc0hvdFJlbG9hZFNlcnZlciIsInN5bmMiLCJzdGFydFdhdGNoZXIiLCJ0aW1lRW5kIiwiY3JlYXRlU2VydmVyIiwiaW5pdE9wdGlvbnMiLCJvcHRpb25zIiwiZmFzdGlmeSIsImdldExvZ1RhcGVGYXN0aWZ5TG9nZ2VyIiwibG9nZ2VyIiwiY2F0ZWdvcnkiLCJmYXN0aWZ5Q2F0ZWdvcnkiLCJTdG9yYWdlTWFuYWdlciIsInBsdWdpbnMiLCJyZWdpc3RlclBsdWdpbnMiLCJyZWdpc3RlckJldHRlckF1dGgiLCJ3aXRoRmFzdGlmeSIsImFwaUNvbmZpZyIsImJvb3QiLCJ0aW1lem9uZSIsImFwaSIsImZvcm1hdEluVGltZVpvbmUiLCJJU09fREFURV9SRUdFWCIsIkRBVEVfRk9STUFUIiwic2V0UmVwbHlTZXJpYWxpemVyIiwicGF5bG9hZCIsIkpTT04iLCJzdHJpbmdpZnkiLCJfa2V5IiwidmFsdWUiLCJ0ZXN0IiwiRGF0ZSIsImdldCIsInJvdXRlIiwicHJlZml4IiwiX3JlcXVlc3QiLCJfcmVwbHkiLCJhcGlzIiwic29uYW11VUlBcGlQbHVnaW4iLCJyZWdpc3RlciIsImRldlJ1bm5lciIsImVuYWJsZWQiLCJyZWdpc3RlckRldlRlc3RSb3V0ZXMiLCJ3ZWJQYXRoIiwiaGFzV2ViIiwicGx1Z2luQ29tcHJlc3MiLCJjb21wcmVzcyIsImdsb2JhbENvbXByZXNzT3B0aW9ucyIsInRocmVzaG9sZCIsImVuY29kaW5ncyIsImN1c3RvbVR5cGVzIiwiZGlzYWJsZUludGVncmF0ZWRXZWIiLCJTT05BTVVfRElTQUJMRV9JTlRFR1JBVEVEX1dFQiIsInNldHVwRGV2U2VydmVyV2l0aFZpdGUiLCJzZXR1cERldlNlcnZlciIsIm1vZGVscyIsIm1vZGVsTmFtZSIsIm1ldGhvZCIsImh0dHBNZXRob2QiLCJ1cmwiLCJoYW5kbGVyIiwiY3JlYXRlQXBpSGFuZGxlciIsInNldHVwU3RhdGljV2ViU2VydmVyIiwiaGFuZGxlRGV2QXBpUmVxdWVzdCIsImdldFBhdGhuYW1lRnJvbVVybCIsInN0YXJ0c1dpdGgiLCJtYXRjaGVkQXBpIiwiZmluZCIsImFwaU1ldGhvZCIsImZ1bGxQYXRoIiwiaXNQYXRoUGF0dGVybk1hdGNoIiwidml0ZVNlcnZlciIsInZpdGUiLCJyb290IiwibWlkZGxld2FyZU1vZGUiLCJobXIiLCJhcHBUeXBlIiwidXNlIiwicmVxIiwicmVzIiwibmV4dCIsIm1pZGRsZXdhcmVzIiwicmVzdWx0IiwibWF0Y2hTU1JSb3V0ZSIsInJlbmRlclNTUiIsInNzck1hdGNoIiwiaHRtbCIsInBhcmFtcyIsInR5cGUiLCJ0ZW1wbGF0ZSIsInJlYWRGaWxlIiwidHJhbnNmb3JtSW5kZXhIdG1sIiwiZSIsInNzckZpeFN0YWNrdHJhY2UiLCJlcnJvciIsInN0YXR1cyIsIm1lc3NhZ2UiLCJhZGRIb29rIiwiY2xvc2UiLCJ3ZWJEaXN0UGF0aCIsInNzclBhdGgiLCJzc3JFbnRyeVBhdGgiLCJzc3JSb3V0ZXNQYXRoIiwid2FybiIsInNzckF2YWlsYWJsZSIsInJlcXVlc3RlZEZpbGUiLCJmaWxlbmFtZSIsImFzc2V0c0RpciIsInNhZmVGaWxlUGF0aCIsInJlc29sdmVQYXRoV2l0aGluQmFzZURpciIsInNlbmQiLCJub3JtYWxpemVkUmVxdWVzdGVkRmlsZSIsInJlbGF0aXZlIiwicmVwbGFjZSIsImFzc2V0UGF0aCIsImdldENhY2hlQ29udHJvbEZvckFzc2V0IiwiY2FjaGVSZXEiLCJjYWNoZUNvbnRyb2xIYW5kbGVyIiwiaW1tdXRhYmxlIiwiZXh0IiwicG9wIiwiZmlsZXMiLCJyZWFkZGlyIiwiY3VycmVudEZpbGUiLCJmIiwiZW5kc1dpdGgiLCJmaWxlUGF0aCIsImNvbnRlbnQiLCJpbmNsdWRlcyIsImdldFNTUlJvdXRlcyIsInNzclJvdXRlcyIsImV4dHJhY3RQYXRoUGFyYW1zIiwiY3NyQ2FjaGVSZXEiLCJjc3JDYWNoZUNvbmZpZyIsInJlcXVlc3RQYXRoIiwiaW5kZXhQYXRoIiwiY3JlYXRlQ29udGV4dCIsInJ1biIsImd1YXJkcyIsImV2ZXJ5IiwiZ3VhcmQiLCJndWFyZEhhbmRsZXIiLCJnZXRab2RPYmplY3RGcm9tQXBpIiwiUmVxVHlwZSIsInR5cGVzIiwid2hpY2giLCJyZXFCb2R5IiwiYnVmZmVyZWRGaWxlcyIsInVwbG9hZGVkRmlsZXMiLCJib2R5IiwidXBsb2FkT3B0aW9ucyIsInBhcnRzIiwibGltaXRzIiwiZmllbGRzIiwiY29uc3VtZSIsInBhcnQiLCJidWZmZXIiLCJ0b0J1ZmZlciIsInB1c2giLCJmaWVsZG5hbWUiLCJTdHJpbmciLCJkaXNrTmFtZSIsImRlc3RpbmF0aW9uIiwiZGlzayIsImtleUdlbmVyYXRvciIsImRlZmF1bHRLZXlHZW5lcmF0b3IiLCJrZXkiLCJtaW1ldHlwZSIsInB1dFN0cmVhbSIsImZpbGUiLCJjb250ZW50VHlwZSIsImdldFVybCIsInNpZ25lZFVybCIsImdldFNpZ25lZFVybCIsInNpemUiLCJieXRlc1JlYWQiLCJxcyIsInBhcnNlZCIsInBhcnNlIiwiT2JqZWN0IiwiYXNzaWduIiwiZmFzdGlmeUNhc3RlciIsIlpvZEVycm9yIiwiaHVtYW5pemVab2RFcnJvciIsIm1lc3NhZ2VzIiwibWFwIiwiaXNzdWUiLCJCYWRSZXF1ZXN0RXhjZXB0aW9uIiwiem9kRXJyb3IiLCJhcGlDYWNoZUNvbmZpZyIsImdldEFwaUNhY2hlQ29udHJvbCIsIkFwaVBhcmFtVHlwZSIsImFyZ3MiLCJwYXJhbWV0ZXJzIiwicGFyYW0iLCJpc0NvbnRleHQiLCJuYW1lIiwiaW52b2tlTW9kZWxNZXRob2QiLCJwYXR0ZXJuIiwicGF0dGVyblBhcnRzIiwiZmlsdGVyIiwiQm9vbGVhbiIsInVybFBhcnRzIiwiaSIsImxlbmd0aCIsInBhdHRlcm5QYXJ0IiwidXJsUGFydCIsImJhc2VEaXIiLCJpbnB1dFBhdGgiLCJkZWNvZGVkIiwiZGVjb2RlVVJJQ29tcG9uZW50IiwicmVsYXRpdmVQYXRoIiwicmVzb2x2ZWRQYXRoIiwicmVzb2x2ZSIsInJlbGF0aXZlRnJvbUJhc2UiLCJpc0Fic29sdXRlIiwiY2FjaGVDb250cm9sIiwicm91dGVPcHRpb25zIiwiaW52b2tlQXBpRm9yU1NSIiwicGFyYW1zSW5kZXgiLCJtb2RlbCIsIm1ldGhvZE5hbWUiLCJhcHBseSIsImNyZWF0ZVNTRUZhY3RvcnkiLCJfZXZlbnRzIiwic29ja2V0IiwiYmluZCIsImxvY2FsZSIsImRldGVjdExvY2FsZSIsImkxOG4iLCJzdXBwb3J0ZWRMb2NhbGVzIiwiZGVmYXVsdExvY2FsZSIsInNlc3Npb24iLCJnZXRTZXNzaW9uIiwiUHJvbWlzZSIsImNvbnRleHRQcm92aWRlciIsImNyZWF0ZVN0b3JlIiwidXNlciIsImFjY2VwdExhbmd1YWdlIiwic3VwcG9ydGVkIiwibGFuZ3MiLCJsYW5nIiwiY29kZSIsInRyaW0iLCJ3YXRjaFBhdGgiLCJjaG9raWRhciIsIndhdGNoIiwiaWdub3JlZCIsInN0YXRzIiwiaXNGaWxlIiwicGVyc2lzdGVudCIsImlnbm9yZUluaXRpYWwiLCJvbiIsImV2ZW50IiwiYWJzb2x1dGVQYXRoIiwiaXNDb25maWdUcyIsImJvbGQiLCJibHVlIiwia2lsbCIsInBpZCIsImhhbmRsZUZpbGVDaGFuZ2UiLCJydW5TY3JpcHQiLCJmbiIsImRlc3Ryb3kiLCJjb21wcmVzc1BsdWdpbiIsInBsdWdpbnNNb2R1bGVzIiwiY29ycyIsImZvcm1ib2R5IiwibXVsdGlwYXJ0Iiwic3NlIiwic3RhdGljIiwicmVnaXN0ZXJQbHVnaW4iLCJwbHVnaW5OYW1lIiwib3B0aW9uIiwiZW50cmllcyIsImN1c3RvbSIsImJhc2VQYXRoIiwiVVJMIiwiaG9zdCIsIlJlcXVlc3QiLCJ0b1N0cmluZyIsInJlc3BvbnNlIiwiZm9yRWFjaCIsImhlYWRlciIsInRleHQiLCJzZXRDYWNoZU1hbmFnZXJSZWYiLCJjcmVhdGVUZXN0Q2FjaGVNYW5hZ2VyIiwiY3JlYXRlQ2FjaGVNYW5hZ2VyIiwiV29ya2Zsb3dNYW5hZ2VyIiwiZ2V0REJDb25maWciLCJlbmFibGVXb3JrZXIiLCJkZWZhdWx0V29ya2VyT3B0aW9ucyIsImNvbmN1cnJlbmN5IiwiY3B1cyIsInVzZVB1YlN1YiIsImxpc3RlbkRlbGF5Iiwic2V0dXBXb3JrZXIiLCJ3b3JrZXJPcHRpb25zIiwicG9ydCIsImxpc3RlbiIsImxpZmVjeWNsZSIsIm9uU2h1dGRvd24iLCJzaHV0ZG93biIsImV4aXQiLCJlcnIiLCJvbkVycm9yIiwic2V0RXJyb3JIYW5kbGVyIiwidGhlbiIsInN0YXJ0V29ya2VyIiwib25TdGFydCIsImNhdGNoIiwicmVkIiwibm93Iiwic3luY0Zyb21XYXRjaGVyIiwiZmluaXNoSE1SIiwicmVuZXdDaGVja3N1bXMiLCJlbmRUaW1lIiwidG90YWxUaW1lIiwiY2VudGVyVGV4dCIsImFsbCIsIm1zZyIsIndoaXRlIiwiYmxhY2siLCJiZ0dyZWVuIiwiQmFzZU1vZGVsIiwiYWxsU2V0dGxlZCIsImRpc2Nvbm5lY3QiLCJTb25hbXUiLCJleHRlbnNpb24iLCJ0aW1lc3RhbXAiLCJyYW5kb20iLCJNYXRoIl0sIm1hcHBpbmdzIjoiQUFBQSxTQUFTQSxXQUFXQyxjQUFjLFFBQVEsbUJBQW1CO0FBQzdELE9BQU9DLFlBQVksU0FBUztBQUM1QixTQUFTQyxpQkFBaUIsUUFBUSxjQUFjO0FBSWhELE9BQU9DLFFBQVEsbUJBQWM7QUFFN0IsT0FBT0MsUUFBUUMsVUFBVUMsVUFBVSxRQUFRLGFBQWE7QUFDeEQsT0FBT0MsUUFBUSxLQUFLO0FBQ3BCLE9BQU9DLFVBQVUsT0FBTztB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
|
|
1014
|
+
//#endregion
|
|
1015
|
+
init_sonamu();
|
|
1016
|
+
export { Sonamu, init_sonamu };
|
|
1017
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic29uYW11LmpzIiwibmFtZXMiOlsiREIiLCJnbG9iYWxDb21wcmVzc09wdGlvbnM6IENvbXByZXNzT3B0aW9ucyB8IHVuZGVmaW5lZCIsImZzIiwiY2FjaGVSZXE6IENhY2hlQ29udHJvbFJlcXVlc3QiLCJmaWxlUGF0aCIsImNzckNhY2hlUmVxOiBDYWNoZUNvbnRyb2xSZXF1ZXN0IiwibWltZUxvb2t1cCIsImNvbnRleHQ6IENvbnRleHQiLCJyZXFCb2R5OiB7XG4gICAgICAgICAgW2tleTogc3RyaW5nXTogdW5rbm93bjtcbiAgICAgICAgfSIsImZpbGVzOiB7XG4gICAgICAgICAgYnVmZmVyZWRGaWxlczogQnVmZmVyZWRGaWxlW107XG4gICAgICAgICAgdXBsb2FkZWRGaWxlczogVXBsb2FkZWRGaWxlW107XG4gICAgICAgIH0iLCJmaWVsZHM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4iLCJrZXlHZW5lcmF0b3I6IEtleUdlbmVyYXRvciIsInBhcmFtczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiIsInBhdGgiLCJsb2d0YXBlRGlzcG9zZSJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hcGkvc29uYW11LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBhc3NlcnQgZnJvbSBcImFzc2VydFwiO1xuaW1wb3J0IHsgQXN5bmNMb2NhbFN0b3JhZ2UgfSBmcm9tIFwiYXN5bmNfaG9va3NcIjtcbmltcG9ydCBmcyBmcm9tIFwiZnMvcHJvbWlzZXNcIjtcbmltcG9ydCB7IHR5cGUgSW5jb21pbmdNZXNzYWdlLCB0eXBlIFNlcnZlciwgdHlwZSBTZXJ2ZXJSZXNwb25zZSB9IGZyb20gXCJodHRwXCI7XG5pbXBvcnQgb3MgZnJvbSBcIm9zXCI7XG5pbXBvcnQgcGF0aCBmcm9tIFwicGF0aFwiO1xuXG5pbXBvcnQgeyBkaXNwb3NlIGFzIGxvZ3RhcGVEaXNwb3NlIH0gZnJvbSBcIkBsb2d0YXBlL2xvZ3RhcGVcIjtcbmltcG9ydCB7IHR5cGUgQXV0aCB9IGZyb20gXCJiZXR0ZXItYXV0aFwiO1xuaW1wb3J0IHsgdHlwZSBGU1dhdGNoZXIgfSBmcm9tIFwiY2hva2lkYXJcIjtcbmltcG9ydCB7IHR5cGUgRmFzdGlmeUluc3RhbmNlLCB0eXBlIEZhc3RpZnlSZXBseSwgdHlwZSBGYXN0aWZ5UmVxdWVzdCB9IGZyb20gXCJmYXN0aWZ5XCI7XG5pbXBvcnQgbWltZSwgeyBsb29rdXAgYXMgbWltZUxvb2t1cCB9IGZyb20gXCJtaW1lLXR5cGVzXCI7XG5pbXBvcnQgeyB0eXBlIFpvZE9iamVjdCB9IGZyb20gXCJ6b2RcIjtcblxuaW1wb3J0IHsgQkFTRV9GSUVMRF9NQVBQSU5HUyB9IGZyb20gXCIuLi9hdXRoL2JldHRlci1hdXRoLWVudGl0aWVzXCI7XG5pbXBvcnQgeyBhcHBseUNhY2hlSGVhZGVycywgQ2FjaGVQcmVzZXRzIH0gZnJvbSBcIi4uL2NhY2hlLWNvbnRyb2wvY2FjaGUtY29udHJvbFwiO1xuaW1wb3J0IHsgdHlwZSBDYWNoZUNvbnRyb2xDb25maWcsIHR5cGUgQ2FjaGVDb250cm9sUmVxdWVzdCB9IGZyb20gXCIuLi9jYWNoZS1jb250cm9sL3R5cGVzXCI7XG5pbXBvcnQgeyB0eXBlIENhY2hlQ29uZmlnLCB0eXBlIENhY2hlTWFuYWdlciB9IGZyb20gXCIuLi9jYWNoZS90eXBlc1wiO1xuaW1wb3J0IHsgdG9GYXN0aWZ5Q29tcHJlc3NPcHRpb24gfSBmcm9tIFwiLi4vY29tcHJlc3MvY29tcHJlc3NcIjtcbmltcG9ydCB7IHR5cGUgQ29tcHJlc3NPcHRpb25zIH0gZnJvbSBcIi4uL2NvbXByZXNzL3R5cGVzXCI7XG5pbXBvcnQgeyBEQiB9IGZyb20gXCIuLi9kYXRhYmFzZS9kYlwiO1xuaW1wb3J0IHsgdHlwZSBTb25hbXVEQkNvbmZpZyB9IGZyb20gXCIuLi9kYXRhYmFzZS9kYlwiO1xuaW1wb3J0IHsgU0QsIHNldFNEQ29uZmlnIH0gZnJvbSBcIi4uL2RpY3Qvc2RcIjtcbmltcG9ydCB7IHR5cGUgTG9jYWxpemVkU3RyaW5nIH0gZnJvbSBcIi4uL2RpY3QvdHlwZXNcIjtcbmltcG9ydCB7IE5vdEZvdW5kRXhjZXB0aW9uIH0gZnJvbSBcIi4uL2V4Y2VwdGlvbnMvc28tZXhjZXB0aW9uc1wiO1xuaW1wb3J0IHsgQnVmZmVyZWRGaWxlIH0gZnJvbSBcIi4uL3N0b3JhZ2UvYnVmZmVyZWQtZmlsZVwiO1xuaW1wb3J0IHsgdHlwZSBTdG9yYWdlTWFuYWdlciB9IGZyb20gXCIuLi9zdG9yYWdlL3N0b3JhZ2UtbWFuYWdlclwiO1xuaW1wb3J0IHsgdHlwZSBLZXlHZW5lcmF0b3IgfSBmcm9tIFwiLi4vc3RvcmFnZS90eXBlc1wiO1xuaW1wb3J0IHsgVXBsb2FkZWRGaWxlIH0gZnJvbSBcIi4uL3N0b3JhZ2UvdXBsb2FkZWQtZmlsZVwiO1xuaW1wb3J0IHsgY3JlYXRlTW9ja1NTRUZhY3RvcnkgfSBmcm9tIFwiLi4vc3RyZWFtL3NzZVwiO1xuaW1wb3J0IHsgdHlwZSBTeW5jZXIgfSBmcm9tIFwiLi4vc3luY2VyL3N5bmNlclwiO1xuaW1wb3J0IHsgdHlwZSBXb3JrZmxvd01hbmFnZXIgfSBmcm9tIFwiLi4vdGFza3Mvd29ya2Zsb3ctbWFuYWdlclwiO1xuaW1wb3J0IHsgdHlwZSBEZXZWaXRlc3RNYW5hZ2VyIH0gZnJvbSBcIi4uL3Rlc3RpbmcvZGV2LXZpdGVzdC1tYW5hZ2VyXCI7XG5pbXBvcnQgeyB0eXBlIFNvbmFtdUZhc3RpZnlDb25maWcgfSBmcm9tIFwiLi4vdHlwZXMvdHlwZXNcIjtcbmltcG9ydCB7IGlzRGFlbW9uU2VydmVyIH0gZnJvbSBcIi4uL3V0aWxzL2NvbnRyb2xsZXJcIjtcbmltcG9ydCB7IGV4aXN0cywgZmlsZUV4aXN0cyB9IGZyb20gXCIuLi91dGlscy9mcy11dGlsc1wiO1xuaW1wb3J0IHsgdHlwZSBBYnNvbHV0ZVBhdGggfSBmcm9tIFwiLi4vdXRpbHMvcGF0aC11dGlsc1wiO1xuaW1wb3J0IHsgY29udmVydEZhc3RpZnlIZWFkZXJzVG9TdGFuZGFyZCwgbWVyZ2UgfSBmcm9tIFwiLi4vdXRpbHMvdXRpbHNcIjtcbmltcG9ydCB7IHR5cGUgU29uYW11Q29uZmlnLCB0eXBlIFNvbmFtdVNlcnZlck9wdGlvbnMsIHR5cGUgU29uYW11VGFza09wdGlvbnMgfSBmcm9tIFwiLi9jb25maWdcIjtcbmltcG9ydCB7IHR5cGUgQ29udGV4dCB9IGZyb20gXCIuL2NvbnRleHRcIjtcbmltcG9ydCB7IHR5cGUgRXh0ZW5kZWRBcGkgfSBmcm9tIFwiLi9kZWNvcmF0b3JzXCI7XG5pbXBvcnQgeyBnZXRTZWNyZXRzIH0gZnJvbSBcIi4vc2VjcmV0XCI7XG5pbXBvcnQgeyB0eXBlIFNvbmFtdVNlY3JldHMgfSBmcm9tIFwiLi9zZWNyZXRcIjtcblxuY2xhc3MgU29uYW11Q2xhc3Mge1xuICBwdWJsaWMgaXNJbml0aWFsaXplZDogYm9vbGVhbiA9IGZhbHNlO1xuICBwdWJsaWMgZm9yVGVzdGluZzogYm9vbGVhbiA9IGZhbHNlO1xuICBwdWJsaWMgYXN5bmNMb2NhbFN0b3JhZ2U6IEFzeW5jTG9jYWxTdG9yYWdlPHtcbiAgICBjb250ZXh0OiBDb250ZXh0O1xuICB9PiA9IG5ldyBBc3luY0xvY2FsU3RvcmFnZSgpO1xuXG4gIHB1YmxpYyBnZXRDb250ZXh0KCk6IENvbnRleHQge1xuICAgIGNvbnN0IHN0b3JlID0gdGhpcy5hc3luY0xvY2FsU3RvcmFnZS5nZXRTdG9yZSgpO1xuICAgIGlmIChzdG9yZT8uY29udGV4dCkge1xuICAgICAgcmV0dXJuIHN0b3JlLmNvbnRleHQ7XG4gICAgfVxuXG4gICAgaWYgKHByb2Nlc3MuZW52Lk5PREVfRU5WID09PSBcInRlc3RcIikge1xuICAgICAgLy8g7YWM7Iqk7YyFIO2ZmOqyveyXkOyEnCDsu6jthY3siqTtirjqsIAg7KO87J6F65CY7KeAIOyViuydgCDqsr3smrAg67mIIOy7qO2FjeyKpO2KuCDrpqzthLRcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHJlcXVlc3Q6IG51bGwsXG4gICAgICAgIHJlcGx5OiBudWxsLFxuICAgICAgICBoZWFkZXJzOiB7fSxcbiAgICAgICAgY3JlYXRlU1NFOiAoc2NoZW1hOiBab2RPYmplY3QpID0+IGNyZWF0ZU1vY2tTU0VGYWN0b3J5KHNjaGVtYSksXG4gICAgICAgIC8vIG94bGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55IC0tIO2FjOyKpO2MhSDtmZjqsr3sl5DshJwg7Luo7YWN7Iqk7Yq46rCAIOyjvOyeheuQmOyngCDslYrsnYAg6rK97JqwIOu5iCDsu6jthY3siqTtirgg66as7YS0XG4gICAgICAgIG5haXRlU3RvcmU6IG5ldyBNYXA8c3RyaW5nLCBhbnk+KCksXG4gICAgICB9IGFzIHVua25vd24gYXMgQ29udGV4dDtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiU29uYW11IGNhbm5vdCBmaW5kIGNvbnRleHRcIik7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBfYXBpUm9vdFBhdGg6IEFic29sdXRlUGF0aCB8IG51bGwgPSBudWxsO1xuICBzZXQgYXBpUm9vdFBhdGgoYXBpUm9vdFBhdGg6IEFic29sdXRlUGF0aCkge1xuICAgIHRoaXMuX2FwaVJvb3RQYXRoID0gYXBpUm9vdFBhdGg7XG4gIH1cbiAgZ2V0IGFwaVJvb3RQYXRoKCk6IEFic29sdXRlUGF0aCB7XG4gICAgaWYgKHRoaXMuX2FwaVJvb3RQYXRoID09PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJTb25hbXUgaGFzIG5vdCBiZWVuIGluaXRpYWxpemVkXCIpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fYXBpUm9vdFBhdGg7XG4gIH1cbiAgZ2V0IGFwcFJvb3RQYXRoKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuYXBpUm9vdFBhdGguc3BsaXQocGF0aC5zZXApLnNsaWNlKDAsIC0xKS5qb2luKHBhdGguc2VwKTtcbiAgfVxuXG4gIHByaXZhdGUgX2RiQ29uZmlnOiBTb25hbXVEQkNvbmZpZyB8IG51bGwgPSBudWxsO1xuICBzZXQgZGJDb25maWcoZGJDb25maWc6IFNvbmFtdURCQ29uZmlnKSB7XG4gICAgdGhpcy5fZGJDb25maWcgPSBkYkNvbmZpZztcbiAgfVxuICBnZXQgZGJDb25maWcoKTogU29uYW11REJDb25maWcge1xuICAgIGlmICh0aGlzLl9kYkNvbmZpZyA9PT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiU29uYW11IGhhcyBub3QgYmVlbiBpbml0aWFsaXplZFwiKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuX2RiQ29uZmlnO1xuICB9XG5cbiAgcHJpdmF0ZSBfc3luY2VyOiBTeW5jZXIgfCBudWxsID0gbnVsbDtcbiAgc2V0IHN5bmNlcihzeW5jZXI6IFN5bmNlcikge1xuICAgIHRoaXMuX3N5bmNlciA9IHN5bmNlcjtcbiAgfVxuICBnZXQgc3luY2VyKCk6IFN5bmNlciB7XG4gICAgaWYgKHRoaXMuX3N5bmNlciA9PT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiU29uYW11IGhhcyBub3QgYmVlbiBpbml0aWFsaXplZFwiKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuX3N5bmNlcjtcbiAgfVxuXG4gIHByaXZhdGUgX2NvbmZpZzogU29uYW11Q29uZmlnIHwgbnVsbCA9IG51bGw7XG4gIHNldCBjb25maWcoY29uZmlnOiBTb25hbXVDb25maWcpIHtcbiAgICB0aGlzLl9jb25maWcgPSBjb25maWc7XG4gIH1cbiAgZ2V0IGNvbmZpZygpOiBTb25hbXVDb25maWcge1xuICAgIGlmICh0aGlzLl9jb25maWcgPT09IG51bGwpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIlNvbmFtdSBoYXMgbm90IGJlZW4gaW5pdGlhbGl6ZWRcIik7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9jb25maWc7XG4gIH1cblxuICBwdWJsaWMgcmVhZG9ubHkgc2VjcmV0czogU29uYW11U2VjcmV0cyA9IGdldFNlY3JldHMoKTtcblxuICBwcml2YXRlIF9zdG9yYWdlOiBTdG9yYWdlTWFuYWdlciB8IG51bGwgPSBudWxsO1xuICAvKipcbiAgICogU3RvcmFnZU1hbmFnZXIg7J247Iqk7YS07IqkXG4gICAqL1xuICBnZXQgc3RvcmFnZSgpOiBTdG9yYWdlTWFuYWdlciB7XG4gICAgaWYgKCF0aGlzLl9zdG9yYWdlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJTdG9yYWdlIGhhcyBub3QgYmVlbiBpbml0aWFsaXplZC4gQ2hlY2sgc3RvcmFnZSBjb25maWcuXCIpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fc3RvcmFnZTtcbiAgfVxuXG4gIHByaXZhdGUgX2NhY2hlOiBDYWNoZU1hbmFnZXIgfCBudWxsID0gbnVsbDtcbiAgLyoqXG4gICAqIENhY2hlTWFuYWdlciDsnbjsiqTthLTsiqQgKEJlbnRvQ2FjaGUpXG4gICAqL1xuICBnZXQgY2FjaGUoKTogQ2FjaGVNYW5hZ2VyIHtcbiAgICBpZiAoIXRoaXMuX2NhY2hlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJDYWNoZSBoYXMgbm90IGJlZW4gaW5pdGlhbGl6ZWQuIENoZWNrIGNhY2hlIGNvbmZpZyBpbiBzb25hbXUuY29uZmlnLnRzLlwiKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuX2NhY2hlO1xuICB9XG5cbiAgcHJpdmF0ZSBfd29ya2Zsb3dzOiBXb3JrZmxvd01hbmFnZXIgfCBudWxsID0gbnVsbDtcbiAgZ2V0IHdvcmtmbG93cygpOiBXb3JrZmxvd01hbmFnZXIge1xuICAgIGlmICh0aGlzLl93b3JrZmxvd3MgPT09IG51bGwpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIlNvbmFtdSBoYXMgbm90IGJlZW4gaW5pdGlhbGl6ZWRcIik7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuX3dvcmtmbG93cztcbiAgfVxuXG4gIHByaXZhdGUgX2F1dGg6IEF1dGggfCBudWxsID0gbnVsbDtcbiAgZ2V0IGF1dGgoKTogQXV0aCB7XG4gICAgaWYgKCF0aGlzLl9hdXRoKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJBdXRoIGhhcyBub3QgYmVlbiBpbml0aWFsaXplZC4gQ2hlY2sgYXV0aCBjb25maWcgaW4gc29uYW11LmNvbmZpZy50cy5cIik7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9hdXRoO1xuICB9XG5cbiAgcHJpdmF0ZSBfZGV2Vml0ZXN0TWFuYWdlcjogRGV2Vml0ZXN0TWFuYWdlciB8IG51bGwgPSBudWxsO1xuICBnZXQgZGV2Vml0ZXN0TWFuYWdlcigpOiBEZXZWaXRlc3RNYW5hZ2VyIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMuX2RldlZpdGVzdE1hbmFnZXI7XG4gIH1cbiAgc2V0IGRldlZpdGVzdE1hbmFnZXIobWFuYWdlcjogRGV2Vml0ZXN0TWFuYWdlciB8IG51bGwpIHtcbiAgICB0aGlzLl9kZXZWaXRlc3RNYW5hZ2VyID0gbWFuYWdlcjtcbiAgfVxuXG4gIC8vIEhNUiDsspjrpqxcbiAgcHVibGljIHdhdGNoZXI6IEZTV2F0Y2hlciB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIHBlbmRpbmdGaWxlczogc3RyaW5nW10gPSBbXTtcbiAgcHJpdmF0ZSBobXJTdGFydFRpbWU6IG51bWJlciA9IDA7XG5cbiAgcHVibGljIHNlcnZlcjogRmFzdGlmeUluc3RhbmNlIHwgbnVsbCA9IG51bGw7XG5cbiAgYXN5bmMgaW5pdEZvclRlc3RpbmcoKSB7XG4gICAgYXdhaXQgdGhpcy5pbml0KHRydWUsIGZhbHNlLCB1bmRlZmluZWQsIHRydWUpO1xuICB9XG5cbiAgYXN5bmMgaW5pdChcbiAgICBkb1NpbGVudDogYm9vbGVhbiA9IGZhbHNlLFxuICAgIGVuYWJsZVN5bmM6IGJvb2xlYW4gPSB0cnVlLFxuICAgIGFwaVJvb3RQYXRoPzogQWJzb2x1dGVQYXRoLFxuICAgIGZvclRlc3Rpbmc6IGJvb2xlYW4gPSBmYWxzZSxcbiAgKSB7XG4gICAgdGhpcy5mb3JUZXN0aW5nID0gZm9yVGVzdGluZztcblxuICAgIGlmICh0aGlzLmlzSW5pdGlhbGl6ZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoIWRvU2lsZW50KSB7XG4gICAgICBjb25zdCBjaGFsayA9IChhd2FpdCBpbXBvcnQoXCJjaGFsa1wiKSkuZGVmYXVsdDtcbiAgICAgIGNvbnNvbGUudGltZShjaGFsay5jeWFuKGBTb25hbXUuaW5pdCR7Zm9yVGVzdGluZyA/IFwiIGZvciB0ZXN0aW5nXCIgOiBcIlwifWApKTtcbiAgICB9XG5cbiAgICAvLyBBUEkg66Oo7Yq4IO2MqOyKpFxuICAgIGNvbnN0IHsgZmluZEFwaVJvb3RQYXRoIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi91dGlscy91dGlsc1wiKTtcbiAgICB0aGlzLmFwaVJvb3RQYXRoID0gYXBpUm9vdFBhdGggPz8gZmluZEFwaVJvb3RQYXRoKCk7XG5cbiAgICAvLyDshKTsoJXsnYQg66Gc65Sp7ZWY64qUIOqyg+u2gO2EsCDsi5zsnpFcbiAgICBjb25zdCB7IGxvYWRDb25maWcgfSA9IGF3YWl0IGltcG9ydChcIi4vY29uZmlnXCIpO1xuICAgIHRoaXMuY29uZmlnID0gYXdhaXQgbG9hZENvbmZpZyh0aGlzLmFwaVJvb3RQYXRoKTtcbiAgICBzZXRTRENvbmZpZyh0aGlzLmNvbmZpZy5pMThuKTtcbiAgICAvLyBzb25hbXUuY29uZmlnLnRzIOq4sOuzuOqwkiDshKTsoJVcbiAgICB0aGlzLmNvbmZpZy5kYXRhYmFzZS5kYXRhYmFzZSA9IHRoaXMuY29uZmlnLmRhdGFiYXNlLmRhdGFiYXNlID8/IFwicGdcIjtcbiAgICB0aGlzLmNvbmZpZy5kYXRhYmFzZS5kZWZhdWx0T3B0aW9ucy5jbGllbnQgPSB0aGlzLmNvbmZpZy5kYXRhYmFzZS5kYXRhYmFzZSA/PyBcInBnXCI7XG5cbiAgICAvLyDroZzquYUg7ISk7KCVXG4gICAgY29uc3QgeyBjb25maWd1cmVMb2dUYXBlIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9sb2dnZXIvY29uZmlndXJlXCIpO1xuICAgIGlmICh0aGlzLmNvbmZpZy5sb2dnaW5nICE9PSBmYWxzZSkge1xuICAgICAgYXdhaXQgY29uZmlndXJlTG9nVGFwZSh7XG4gICAgICAgIC4uLnRoaXMuY29uZmlnLmxvZ2dpbmcsXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICAvLyBEQiDroZzrk5xcbiAgICBjb25zdCB7IERCIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9kYXRhYmFzZS9kYlwiKTtcbiAgICB0aGlzLmRiQ29uZmlnID0gREIuZ2VuZXJhdGVEQkNvbmZpZyh0aGlzLmNvbmZpZy5kYXRhYmFzZSk7XG4gICAgREIuc2V0Q29uZmlnKHRoaXMuZGJDb25maWcpO1xuICAgIGlmICghZG9TaWxlbnQpIHtcbiAgICAgIGNvbnN0IGNoYWxrID0gKGF3YWl0IGltcG9ydChcImNoYWxrXCIpKS5kZWZhdWx0O1xuICAgICAgY29uc29sZS5sb2coY2hhbGsuZ3JlZW4oXCJEQiBDb25maWcgTG9hZGVkIVwiKSk7XG4gICAgfVxuXG4gICAgLy8gRW50aXR5IOuhnOuTnFxuICAgIC8vIO2FjOyKpO2KuOyXkOyEnOuPhCBFbnRpdHkg7KCV67O064qUIO2VhOyalO2VqeuLiOuLpC5cbiAgICAvLyB1cHNlcnTqsIAg7KCc64yA66GcIOyekeuPme2VmOugpOuptCBlbnRpdHnsnZggdW5pcXVlIGluZGV4IOygleuztOqwgCDtlYTsmpTtlZjquLAg65WM66y47J6F64uI64ukLlxuICAgIGNvbnN0IHsgRW50aXR5TWFuYWdlciB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vZW50aXR5L2VudGl0eS1tYW5hZ2VyXCIpO1xuICAgIGF3YWl0IEVudGl0eU1hbmFnZXIuYXV0b2xvYWQoZG9TaWxlbnQpO1xuXG4gICAgLy8gQ2FjaGUg7LSI6riw7ZmUXG4gICAgYXdhaXQgdGhpcy5pbml0aWFsaXplQ2FjaGUodGhpcy5jb25maWcuc2VydmVyLmNhY2hlLCBmb3JUZXN0aW5nKTtcblxuICAgIC8vIEJldHRlckF1dGgg7LSI6riw7ZmUXG4gICAgY29uc3QgYXV0aENvbmZpZyA9IHRoaXMuY29uZmlnLnNlcnZlci5hdXRoO1xuICAgIGlmIChhdXRoQ29uZmlnKSB7XG4gICAgICAvLyDsgqzsmqnsnpAg7ISk7KCV6rO8IOq4sOuzuOqwkuydhCBtZXJnZVxuICAgICAgY29uc3QgbWVyZ2VkRmllbGRNYXBwaW5ncyA9IG1lcmdlKEJBU0VfRklFTERfTUFQUElOR1MsIGF1dGhDb25maWcpO1xuXG4gICAgICAvLyBiZXR0ZXItYXV0aCDsnbjsiqTthLTsiqQg7IOd7ISxXG4gICAgICBjb25zdCB7IGJldHRlckF1dGggfSA9IGF3YWl0IGltcG9ydChcImJldHRlci1hdXRoXCIpO1xuICAgICAgY29uc3QgeyBzb25hbXVLbmV4QWRhcHRlciB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vYXV0aC9rbmV4LWFkYXB0ZXJcIik7XG5cbiAgICAgIHRoaXMuX2F1dGggPSBiZXR0ZXJBdXRoKHtcbiAgICAgICAgZGF0YWJhc2U6IHNvbmFtdUtuZXhBZGFwdGVyKCksXG4gICAgICAgIC4uLm1lcmdlZEZpZWxkTWFwcGluZ3MsXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICAvLyDthYzsiqTtjIXsnbgg6rK97JqwIOyLse2BrCDsl4bsnbQg7KSR64uoXG4gICAgaWYgKGZvclRlc3RpbmcpIHtcbiAgICAgIHRoaXMuaXNJbml0aWFsaXplZCA9IHRydWU7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gVGFzayDrk7HroZ1cbiAgICBhd2FpdCB0aGlzLmluaXRpYWxpemVXb3JrZmxvd3ModGhpcy5jb25maWcudGFza3MpO1xuXG4gICAgLy8gU3luY2VyXG4gICAgY29uc3QgeyBTeW5jZXIgfSA9IGF3YWl0IGltcG9ydChcIi4uL3N5bmNlci9zeW5jZXJcIik7XG4gICAgdGhpcy5zeW5jZXIgPSBuZXcgU3luY2VyKCk7XG5cbiAgICAvLyBBdXRvbG9hZDogTW9kZWxzIC8gVHlwZXMgLyBBUElzIC8gV29ya2Zsb3dzIC8gVGVtcGxhdGVzIC8gU1NSIFJvdXRlc1xuICAgIGF3YWl0IHRoaXMuc3luY2VyLmF1dG9sb2FkVHlwZXMoKTtcbiAgICBhd2FpdCB0aGlzLnN5bmNlci5hdXRvbG9hZE1vZGVscygpO1xuICAgIGF3YWl0IHRoaXMuc3luY2VyLmF1dG9sb2FkQXBpcygpO1xuICAgIGF3YWl0IHRoaXMuc3luY2VyLmF1dG9sb2FkV29ya2Zsb3dzKCk7XG4gICAgY29uc3QgeyBUZW1wbGF0ZU1hbmFnZXIgfSA9IGF3YWl0IGltcG9ydChcIi4uL3RlbXBsYXRlXCIpO1xuICAgIGF3YWl0IFRlbXBsYXRlTWFuYWdlci5hdXRvbG9hZCgpO1xuICAgIGF3YWl0IHRoaXMuc3luY2VyLmF1dG9sb2FkU1NSUm91dGVzKCk7XG5cbiAgICBjb25zdCB7IGlzTG9jYWwsIGlzVGVzdCwgaXNIb3RSZWxvYWRTZXJ2ZXIgfSA9IGF3YWl0IGltcG9ydChcIi4uL3V0aWxzL2NvbnRyb2xsZXJcIik7XG4gICAgaWYgKGlzTG9jYWwoKSAmJiAhaXNUZXN0KCkgJiYgaXNIb3RSZWxvYWRTZXJ2ZXIoKSAmJiBlbmFibGVTeW5jKSB7XG4gICAgICBhd2FpdCB0aGlzLnN5bmNlci5zeW5jKCk7XG4gICAgICBhd2FpdCB0aGlzLnN0YXJ0V2F0Y2hlcigpO1xuICAgIH1cblxuICAgIHRoaXMuaXNJbml0aWFsaXplZCA9IHRydWU7XG4gICAgaWYgKCFkb1NpbGVudCkge1xuICAgICAgY29uc3QgY2hhbGsgPSAoYXdhaXQgaW1wb3J0KFwiY2hhbGtcIikpLmRlZmF1bHQ7XG4gICAgICBjb25zb2xlLnRpbWVFbmQoY2hhbGsuY3lhbihcIlNvbmFtdS5pbml0XCIpKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBjcmVhdGVTZXJ2ZXIoaW5pdE9wdGlvbnM/OiB7IGVuYWJsZVN5bmM/OiBib29sZWFuOyBkb1NpbGVudD86IGJvb2xlYW4gfSkge1xuICAgIGlmICghdGhpcy5pc0luaXRpYWxpemVkKSB7XG4gICAgICBhd2FpdCB0aGlzLmluaXQoaW5pdE9wdGlvbnM/LmRvU2lsZW50LCBpbml0T3B0aW9ucz8uZW5hYmxlU3luYyk7XG4gICAgfVxuXG4gICAgY29uc3Qgb3B0aW9ucyA9IHRoaXMuY29uZmlnLnNlcnZlcjtcbiAgICBjb25zdCB7IGRlZmF1bHQ6IGZhc3RpZnkgfSA9IGF3YWl0IGltcG9ydChcImZhc3RpZnlcIik7XG4gICAgY29uc3QgeyBnZXRMb2dUYXBlRmFzdGlmeUxvZ2dlciB9ID0gYXdhaXQgaW1wb3J0KFwiQGxvZ3RhcGUvZmFzdGlmeVwiKTtcbiAgICBjb25zdCBzZXJ2ZXIgPSBmYXN0aWZ5KHtcbiAgICAgIC4uLm9wdGlvbnMuZmFzdGlmeSxcbiAgICAgIGxvZ2dlcjpcbiAgICAgICAgdGhpcy5jb25maWcubG9nZ2luZyAhPT0gZmFsc2VcbiAgICAgICAgICA/IGdldExvZ1RhcGVGYXN0aWZ5TG9nZ2VyKHtcbiAgICAgICAgICAgICAgY2F0ZWdvcnk6IHRoaXMuY29uZmlnLmxvZ2dpbmc/LmZhc3RpZnlDYXRlZ29yeSA/PyBbXCJmYXN0aWZ5XCJdLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICA6IHVuZGVmaW5lZCxcbiAgICB9KTtcbiAgICB0aGlzLnNlcnZlciA9IHNlcnZlcjtcblxuICAgIC8vIFN0b3JhZ2Ug7ISk7KCVIOKGkiBTdG9yYWdlTWFuYWdlciDsg53shLFcbiAgICBpZiAob3B0aW9ucy5zdG9yYWdlKSB7XG4gICAgICBjb25zdCB7IFN0b3JhZ2VNYW5hZ2VyIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9zdG9yYWdlL3N0b3JhZ2UtbWFuYWdlclwiKTtcbiAgICAgIHRoaXMuX3N0b3JhZ2UgPSBuZXcgU3RvcmFnZU1hbmFnZXIob3B0aW9ucy5zdG9yYWdlKTtcbiAgICB9XG5cbiAgICAvLyDtlIzrn6zqt7jsnbgg65Ox66GdXG4gICAgaWYgKG9wdGlvbnMucGx1Z2lucykge1xuICAgICAgYXdhaXQgdGhpcy5yZWdpc3RlclBsdWdpbnMoc2VydmVyLCBvcHRpb25zLnBsdWdpbnMpO1xuICAgIH1cblxuICAgIGlmIChvcHRpb25zLmF1dGgpIHtcbiAgICAgIGF3YWl0IHRoaXMucmVnaXN0ZXJCZXR0ZXJBdXRoKHNlcnZlciwgb3B0aW9ucy5hdXRoKTtcbiAgICB9XG5cbiAgICAvLyBBUEkg65287Jqw7YyFIOyEpOyglVxuICAgIGF3YWl0IHRoaXMud2l0aEZhc3RpZnkoc2VydmVyLCBvcHRpb25zLmFwaUNvbmZpZywge1xuICAgICAgZW5hYmxlU3luYzogaW5pdE9wdGlvbnM/LmVuYWJsZVN5bmMsXG4gICAgICBkb1NpbGVudDogaW5pdE9wdGlvbnM/LmRvU2lsZW50LFxuICAgIH0pO1xuXG4gICAgLy8g7ISc67KEIOyLnOyekVxuICAgIGF3YWl0IHRoaXMuYm9vdChzZXJ2ZXIsIG9wdGlvbnMpO1xuXG4gICAgcmV0dXJuIHNlcnZlcjtcbiAgfVxuXG4gIGFzeW5jIHdpdGhGYXN0aWZ5KFxuICAgIHNlcnZlcjogRmFzdGlmeUluc3RhbmNlPFNlcnZlciwgSW5jb21pbmdNZXNzYWdlLCBTZXJ2ZXJSZXNwb25zZT4sXG4gICAgY29uZmlnOiBTb25hbXVGYXN0aWZ5Q29uZmlnLFxuICAgIG9wdGlvbnM/OiB7XG4gICAgICBlbmFibGVTeW5jPzogYm9vbGVhbjtcbiAgICAgIGRvU2lsZW50PzogYm9vbGVhbjtcbiAgICB9LFxuICApIHtcbiAgICBpZiAoIXRoaXMuaXNJbml0aWFsaXplZCkge1xuICAgICAgYXdhaXQgdGhpcy5pbml0KG9wdGlvbnM/LmRvU2lsZW50LCBvcHRpb25zPy5lbmFibGVTeW5jKTtcbiAgICB9XG5cbiAgICB0aGlzLnNlcnZlciA9IHNlcnZlcjtcblxuICAgIC8vIHRpbWV6b25lIOyEpOyglVxuICAgIGNvbnN0IHRpbWV6b25lID0gdGhpcy5jb25maWcuYXBpLnRpbWV6b25lO1xuICAgIGlmICh0aW1lem9uZSkge1xuICAgICAgLy8g7YOA7J6E7KG07JeQIOunnuqyjCDsnZHri7Ug64Kg7KecIOyKpO2KuOungeydhCDrs4DtmZjtlbTso7zslrTslbwg7ZWp64uI64ukLlxuICAgICAgLy8g6rCA66C5IHRpbWV6b25l7J20IFwiQXNpYS9TZW91bFwiIOydtOuptFxuICAgICAgLy8gXCIyMDI1LTExLTIxVDAwOjAwOjAwLjAwMFpcIiDrpbwgXCIyMDI1LTExLTIxVDA5OjAwOjAwKzA5OjAwXCIg7Jy866GcIOuzgO2ZmO2VtOyjvOyWtOyVvCDtlanri4jri6QuXG4gICAgICBjb25zdCB7IGZvcm1hdEluVGltZVpvbmUgfSA9IGF3YWl0IGltcG9ydChcImRhdGUtZm5zLXR6XCIpO1xuXG4gICAgICAvLyBJU08gODYwMSDrgqDsp5wg7ZiV7IudIOygleq3nOyLnSAo7JiIOiAyMDI0LTAxLTE1VDA5OjMwOjAwLjAwMFopXG4gICAgICBjb25zdCBJU09fREFURV9SRUdFWCA9IC9eXFxkezR9LVxcZHsyfS1cXGR7Mn1UXFxkezJ9OlxcZHsyfTpcXGR7Mn0oXFwuXFxkezN9KT9aJC87XG5cbiAgICAgIC8vIFTrpbwg65GY65+s7Iu8IOyekeydgOuUsOyYtO2RnOqwgCDsl4bri6TrqbQgXCIyMDI1LTExLTE5MTc2MzU0NjE4OTAwMDE4OjU2OjI5KzA5OjAwXCLsmYAg6rCZ7J2AIOqysOqzvOqwgCDrgpjsmLXri4jri6QuXG4gICAgICAvLyDsnbTripQgZGF0ZS1mbnMg7Yq57J6F64uI64ukLlxuICAgICAgLy8g7J2066CH6rKMIO2VtOuPhCDqtJzssK7sirXri4jri6QuIFwiMjAyNS0xMS0xOVQxODo1NjoyOSswOTowMFwiIOuqqOyWkeycvOuhnCDsnpgg64KY7Ji164uI64ukLlxuICAgICAgY29uc3QgREFURV9GT1JNQVQgPSBcInl5eXktTU0tZGQnVCdISDptbTpzc1hYWFwiO1xuXG4gICAgICBzZXJ2ZXIuc2V0UmVwbHlTZXJpYWxpemVyKChwYXlsb2FkKSA9PiB7XG4gICAgICAgIHJldHVybiBKU09OLnN0cmluZ2lmeShwYXlsb2FkLCAoX2tleSwgdmFsdWUpID0+IHtcbiAgICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiICYmIElTT19EQVRFX1JFR0VYLnRlc3QodmFsdWUpKSB7XG4gICAgICAgICAgICByZXR1cm4gZm9ybWF0SW5UaW1lWm9uZShcbiAgICAgICAgICAgICAgbmV3IERhdGUodmFsdWUpLFxuICAgICAgICAgICAgICB0aW1lem9uZSBhcyBgJHtzdHJpbmd9LyR7c3RyaW5nfWAsXG4gICAgICAgICAgICAgIERBVEVfRk9STUFULFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIHZhbHVlO1xuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgICAgaWYgKCFvcHRpb25zPy5kb1NpbGVudCkge1xuICAgICAgICBjb25zdCBjaGFsayA9IChhd2FpdCBpbXBvcnQoXCJjaGFsa1wiKSkuZGVmYXVsdDtcbiAgICAgICAgY29uc29sZS5sb2coY2hhbGsuZ3JlZW4oYFRpbWV6b25lIHNldCB0byAke3RpbWV6b25lfWApKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyDsoITssrQg65287Jqw7YyFIOumrOyKpO2KuFxuICAgIHNlcnZlci5nZXQoXG4gICAgICBgJHt0aGlzLmNvbmZpZy5hcGkucm91dGUucHJlZml4fS9yb3V0ZXNgLFxuICAgICAgYXN5bmMgKF9yZXF1ZXN0LCBfcmVwbHkpOiBQcm9taXNlPHR5cGVvZiB0aGlzLnN5bmNlci5hcGlzPiA9PiB7XG4gICAgICAgIHJldHVybiB0aGlzLnN5bmNlci5hcGlzO1xuICAgICAgfSxcbiAgICApO1xuXG4gICAgLy8gSGVhbHRoY2hlY2sgQVBJXG4gICAgc2VydmVyLmdldChcbiAgICAgIGAke3RoaXMuY29uZmlnLmFwaS5yb3V0ZS5wcmVmaXh9L2hlYWx0aGNoZWNrYCxcbiAgICAgIGFzeW5jIChfcmVxdWVzdCwgX3JlcGx5KTogUHJvbWlzZTxzdHJpbmc+ID0+IHtcbiAgICAgICAgcmV0dXJuIFwib2tcIjtcbiAgICAgIH0sXG4gICAgKTtcblxuICAgIC8vIFNvbmFtdSBVSSBBUEkgKOuhnOy7rCDtmZjqsr3sl5DshJzrp4wpXG4gICAgY29uc3QgeyBpc0xvY2FsIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi91dGlscy9jb250cm9sbGVyXCIpO1xuICAgIGlmIChpc0xvY2FsKCkpIHtcbiAgICAgIGNvbnN0IHsgc29uYW11VUlBcGlQbHVnaW4gfSA9IGF3YWl0IGltcG9ydChcIi4uL3VpL2FwaVwiKTtcbiAgICAgIHNlcnZlci5yZWdpc3Rlcihzb25hbXVVSUFwaVBsdWdpbik7XG4gICAgfVxuXG4gICAgLy8gRGV2UnVubmVyIO2FjOyKpO2KuCDsl5Trk5ztj6zsnbjtirggKOuhnOy7rCDtmZjqsr0gKyBkZXZSdW5uZXIg7Zmc7ISx7ZmUIOyLnClcbiAgICBpZiAoaXNMb2NhbCgpICYmIHRoaXMuY29uZmlnLnRlc3Q/LmRldlJ1bm5lcj8uZW5hYmxlZCkge1xuICAgICAgY29uc3QgeyByZWdpc3RlckRldlRlc3RSb3V0ZXMgfSA9IGF3YWl0IGltcG9ydChcIi4uL3Rlc3RpbmcvZGV2LXRlc3Qtcm91dGVzXCIpO1xuICAgICAgYXdhaXQgcmVnaXN0ZXJEZXZUZXN0Um91dGVzKHNlcnZlciwgdGhpcy5jb25maWcudGVzdC5kZXZSdW5uZXIpO1xuICAgIH1cblxuICAgIGNvbnN0IHdlYlBhdGggPSBwYXRoLmpvaW4odGhpcy5hcHBSb290UGF0aCwgXCJ3ZWJcIik7XG4gICAgY29uc3QgaGFzV2ViID0gYXdhaXQgZXhpc3RzKHdlYlBhdGgpO1xuXG4gICAgLy8g7KCE7JetIGNvbXByZXNzIOyYteyFmCDqs4TsgrAgKHJvdXRlLmNvbXByZXNzOiB0cnVl7J28IOuVjCDsgqzsmqkpXG4gICAgY29uc3QgcGx1Z2luQ29tcHJlc3MgPSB0aGlzLmNvbmZpZy5zZXJ2ZXIucGx1Z2lucz8uY29tcHJlc3M7XG4gICAgY29uc3QgZ2xvYmFsQ29tcHJlc3NPcHRpb25zOiBDb21wcmVzc09wdGlvbnMgfCB1bmRlZmluZWQgPSBwbHVnaW5Db21wcmVzc1xuICAgICAgPyBwbHVnaW5Db21wcmVzcyA9PT0gdHJ1ZVxuICAgICAgICA/IHsgdGhyZXNob2xkOiAxMDI0LCBlbmNvZGluZ3M6IFtcImJyXCIsIFwiZ3ppcFwiLCBcImRlZmxhdGVcIl0gfVxuICAgICAgICA6IHtcbiAgICAgICAgICAgIHRocmVzaG9sZDogcGx1Z2luQ29tcHJlc3MudGhyZXNob2xkLFxuICAgICAgICAgICAgZW5jb2RpbmdzOiBwbHVnaW5Db21wcmVzcy5lbmNvZGluZ3MsXG4gICAgICAgICAgICBjdXN0b21UeXBlczogcGx1Z2luQ29tcHJlc3MuY3VzdG9tVHlwZXMsXG4gICAgICAgICAgfVxuICAgICAgOiB1bmRlZmluZWQ7XG5cbiAgICBpZiAoaXNMb2NhbCgpKSB7XG4gICAgICAvLyDroZzsu6wg6rCc67CcIO2ZmOqyvTogY2F0Y2gtYWxs66GcIEFQSeulvCDrj5nsoIEg66ek7Lmt7ZWY7JesIEhNUuydhCDsp4Dsm5Dtlanri4jri6QuXG4gICAgICAvLyBTT05BTVVfRElTQUJMRV9JTlRFR1JBVEVEX1dFQj15ZXProZwg7ISk7KCV7ZWY66m0IGRldl9hcGkg66qo65Oc7JeQ7IScIFZpdGUg7Ya17ZWp7J2EIOu5hO2ZnOyEse2ZlO2VoCDsiJgg7J6I7Iq164uI64ukLlxuICAgICAgY29uc3QgZGlzYWJsZUludGVncmF0ZWRXZWIgPSBwcm9jZXNzLmVudi5TT05BTVVfRElTQUJMRV9JTlRFR1JBVEVEX1dFQiA9PT0gXCJ5ZXNcIjtcbiAgICAgIGlmIChoYXNXZWIgJiYgIWRpc2FibGVJbnRlZ3JhdGVkV2ViKSB7XG4gICAgICAgIGF3YWl0IHRoaXMuc2V0dXBEZXZTZXJ2ZXJXaXRoVml0ZShzZXJ2ZXIsIHdlYlBhdGgsIGNvbmZpZyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLnNldHVwRGV2U2VydmVyKHNlcnZlciwgY29uZmlnKTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgLy8g7ZSE66Gc642V7IWYIO2ZmOqyvTog6rCc67OEIEFQSSDrnbzsmrDtirggKyDsoJXsoIEg7YyM7J28IOyEnOu5mVxuICAgICAgZm9yIChjb25zdCBhcGkgb2YgdGhpcy5zeW5jZXIuYXBpcykge1xuICAgICAgICBpZiAodGhpcy5zeW5jZXIubW9kZWxzW2FwaS5tb2RlbE5hbWVdID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYOygleydmOuQmOyngCDslYrsnYAg66qo64247JeQIOygkeq3vCAke2FwaS5tb2RlbE5hbWV9YCk7XG4gICAgICAgIH1cblxuICAgICAgICBzZXJ2ZXIucm91dGUoe1xuICAgICAgICAgIG1ldGhvZDogYXBpLm9wdGlvbnMuaHR0cE1ldGhvZCA/PyBcIkdFVFwiLFxuICAgICAgICAgIHVybDogdGhpcy5jb25maWcuYXBpLnJvdXRlLnByZWZpeCArIGFwaS5wYXRoLFxuICAgICAgICAgIGhhbmRsZXI6IHRoaXMuY3JlYXRlQXBpSGFuZGxlcihhcGksIGNvbmZpZyksXG4gICAgICAgICAgY29tcHJlc3M6IHRvRmFzdGlmeUNvbXByZXNzT3B0aW9uKGFwaS5vcHRpb25zLmNvbXByZXNzLCBnbG9iYWxDb21wcmVzc09wdGlvbnMpLFxuICAgICAgICB9KTtcbiAgICAgIH1cblxuICAgICAgLy8g7ZSE66Gc642V7IWY7JeQ7ISc64qUIHdlYiDshozsiqQoYXBwUm9vdC93ZWIpIOycoOustOyZgCDrrLTqtIDtlZjqsowsXG4gICAgICAvLyBhcGkvd2ViLWRpc3Qg7KG07J6sIOyXrOu2gOulvCBzZXR1cFN0YXRpY1dlYlNlcnZlciDrgrTrtoDsl5DshJwg7YyQ64uo7ZWp64uI64ukLlxuICAgICAgYXdhaXQgdGhpcy5zZXR1cFN0YXRpY1dlYlNlcnZlcihzZXJ2ZXIsIGNvbmZpZywgZ2xvYmFsQ29tcHJlc3NPcHRpb25zKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogZGV2IOuqqOuTnCDqs7XthrU6IGNhdGNoLWFsbOyXkOyEnCBzeW5jZXIuYXBpc+ulvCDrj5nsoIHsnLzroZwg7YOQ7IOJ7ZWY7JesIEFQSSDsmpTssq3snYQg7LKY66as7ZWp64uI64ukLlxuICAgKiBzZXJ2ZXIucm91dGUoKeuhnCDqsJzrs4Qg65Ox66Gd7ZWY66m0IGhhbmRsZXLqsIAg6rOg7KCV65CY7Ja0IEhNUuydtCDrj5nsnpHtlZjsp4Ag7JWK7Jy866+A66GcLFxuICAgKiDrp6Qg7JqU7LKt66eI64ukIHN5bmNlci5hcGlz66W8IOyhsO2ajO2VmOuKlCDsnbQg67Cp7Iud7J2EIOyCrOyaqe2VqeuLiOuLpC5cbiAgICpcbiAgICog7JqU7LKt7J20IC9hcGko7KCV7ZmV7Z6I64qUIHRoaXMuY29uZmlnLmFwaS5yb3V0ZS5wcmVmaXgp66GcIOyLnOyeke2VmOyngCDslYrripQg6rK97Jqw652866m0IG51bGzsnYQg67CY7ZmY7ZWY66mwIOuBneuDheuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgaGFuZGxlRGV2QXBpUmVxdWVzdChcbiAgICByZXF1ZXN0OiBGYXN0aWZ5UmVxdWVzdCxcbiAgICBjb25maWc6IFNvbmFtdUZhc3RpZnlDb25maWcsXG4gICk6ICgocmVxdWVzdDogRmFzdGlmeVJlcXVlc3QsIHJlcGx5OiBGYXN0aWZ5UmVwbHkpID0+IFByb21pc2U8dW5rbm93bj4pIHwgbnVsbCB7XG4gICAgY29uc3QgdXJsID0gdGhpcy5nZXRQYXRobmFtZUZyb21VcmwocmVxdWVzdC51cmwpO1xuICAgIGNvbnN0IG1ldGhvZCA9IHJlcXVlc3QubWV0aG9kO1xuXG4gICAgaWYgKCF1cmwuc3RhcnRzV2l0aCh0aGlzLmNvbmZpZy5hcGkucm91dGUucHJlZml4KSkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgLy8gc3luY2VyLmFwaXPsnZggcGF0aOuKlCA6cGFyYW0g7ZiV7YOc66W8IO2PrO2VqO2VoCDsiJgg7J6I7Jy866+A66GcIOyEuOq3uOuovO2KuCDri6jsnITroZwg66ek7Lmt7ZWp64uI64ukLlxuICAgIC8vIOygleq3nOyLnSDsg53shLEg67Cp7Iud7J2AIHBhdGgg66y47J6Q7Je0IOuCtCDtirnsiJjrrLjsnpAoLiwgKywgKCwgWyDrk7Ep66GcIOyYpOyekeuPme2VoCDsiJgg7J6I7Ja0IOyCrOyaqe2VmOyngCDslYrsirXri4jri6QuXG4gICAgY29uc3QgbWF0Y2hlZEFwaSA9IHRoaXMuc3luY2VyLmFwaXMuZmluZCgoYXBpKSA9PiB7XG4gICAgICBpZiAodGhpcy5zeW5jZXIubW9kZWxzW2FwaS5tb2RlbE5hbWVdID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuICAgICAgY29uc3QgYXBpTWV0aG9kID0gYXBpLm9wdGlvbnMuaHR0cE1ldGhvZCA/PyBcIkdFVFwiO1xuICAgICAgaWYgKGFwaU1ldGhvZCAhPT0gbWV0aG9kKSByZXR1cm4gZmFsc2U7XG5cbiAgICAgIGNvbnN0IGZ1bGxQYXRoID0gdGhpcy5jb25maWcuYXBpLnJvdXRlLnByZWZpeCArIGFwaS5wYXRoO1xuICAgICAgcmV0dXJuIHRoaXMuaXNQYXRoUGF0dGVybk1hdGNoKGZ1bGxQYXRoLCB1cmwpO1xuICAgIH0pO1xuXG4gICAgaWYgKCFtYXRjaGVkQXBpKSB7XG4gICAgICB0aHJvdyBuZXcgTm90Rm91bmRFeGNlcHRpb24oU0QoXCJlcnJvci5hcGkubm90Rm91bmRcIikpO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLmNyZWF0ZUFwaUhhbmRsZXIobWF0Y2hlZEFwaSwgY29uZmlnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBkZXYgYXBpIOuqqOuTnDogVml0ZSDsl4bsnbQgQVBJIOuPmeyggSDrnbzsmrDtjIXrp4wg7KCc6rO17ZWp64uI64ukLlxuICAgKiBITVLsnYQg7JyE7ZW0IGNhdGNoLWFsbOyXkOyEnCDrp6Qg7JqU7LKt66eI64ukIHN5bmNlci5hcGlz66W8IOyhsO2ajO2VqeuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgc2V0dXBEZXZTZXJ2ZXIoXG4gICAgc2VydmVyOiBGYXN0aWZ5SW5zdGFuY2U8U2VydmVyLCBJbmNvbWluZ01lc3NhZ2UsIFNlcnZlclJlc3BvbnNlPixcbiAgICBjb25maWc6IFNvbmFtdUZhc3RpZnlDb25maWcsXG4gICk6IHZvaWQge1xuICAgIHNlcnZlci5yb3V0ZSh7XG4gICAgICBtZXRob2Q6IFtcIkdFVFwiLCBcIkhFQURcIiwgXCJQT1NUXCIsIFwiUFVUXCIsIFwiREVMRVRFXCIsIFwiUEFUQ0hcIl0sXG4gICAgICB1cmw6IGAke3RoaXMuY29uZmlnLmFwaS5yb3V0ZS5wcmVmaXh9LypgLFxuICAgICAgaGFuZGxlcjogYXN5bmMgKHJlcXVlc3QsIHJlcGx5KSA9PiB7XG4gICAgICAgIGNvbnN0IGhhbmRsZXIgPSB0aGlzLmhhbmRsZURldkFwaVJlcXVlc3QocmVxdWVzdCwgY29uZmlnKTtcbiAgICAgICAgaWYgKGhhbmRsZXIpIHtcbiAgICAgICAgICByZXR1cm4gaGFuZGxlcihyZXF1ZXN0LCByZXBseSk7XG4gICAgICAgIH1cbiAgICAgICAgLy8g65Ox66Gd65CcIEFQSeyZgCDsnbzsuZjtlZjsp4Ag7JWK64qUIOyalOyyreyXkCDrjIDtlZwgZmFsbGJhY2vsnoXri4jri6QuXG4gICAgICAgIHRocm93IG5ldyBOb3RGb3VuZEV4Y2VwdGlvbihTRChcImVycm9yLmFwaS5ub3RGb3VuZFwiKSk7XG4gICAgICB9LFxuICAgIH0pO1xuICB9XG5cbiAgLy8gb3hsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnkgLS0gVml0ZURldlNlcnZlciDtg4DsnoXsnYQg64+Z7KCB7Jy866GcIOuhnOuTnO2VtOyVvCDtlahcbiAgcHJpdmF0ZSB2aXRlU2VydmVyOiBhbnkgPSBudWxsO1xuXG4gIC8qKlxuICAgKiBkZXYgYWxsIOuqqOuTnDogVml0ZSBEZXYgU2VydmVy66W8IO2Gte2Vqe2VmOyXrCBBUEkgKyBTU1IgKyBDU1LsnYQg66qo65GQIOygnOqzte2VqeuLiOuLpC5cbiAgICogQVBJIOuPmeyggSDrp6Tsua3snYAgaGFuZGxlRGV2QXBpUmVxdWVzdOulvCDqs7XsnKDtlanri4jri6QuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHNldHVwRGV2U2VydmVyV2l0aFZpdGUoXG4gICAgc2VydmVyOiBGYXN0aWZ5SW5zdGFuY2U8U2VydmVyLCBJbmNvbWluZ01lc3NhZ2UsIFNlcnZlclJlc3BvbnNlPixcbiAgICB3ZWJQYXRoOiBzdHJpbmcsXG4gICAgY29uZmlnOiBTb25hbXVGYXN0aWZ5Q29uZmlnLFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAvLyBAZmFzdGlmeS9taWRkaWUg65Ox66GdIChDb25uZWN0LXN0eWxlIG1pZGRsZXdhcmUg7KeA7JuQKVxuICAgIGF3YWl0IHNlcnZlci5yZWdpc3RlcigoYXdhaXQgaW1wb3J0KFwiQGZhc3RpZnkvbWlkZGllXCIpKS5kZWZhdWx0KTtcblxuICAgIGNvbnN0IHZpdGUgPSBhd2FpdCBpbXBvcnQoXCJ2aXRlXCIpO1xuXG4gICAgdGhpcy52aXRlU2VydmVyID0gYXdhaXQgdml0ZS5jcmVhdGVTZXJ2ZXIoe1xuICAgICAgcm9vdDogd2ViUGF0aCxcbiAgICAgIHNlcnZlcjoge1xuICAgICAgICBtaWRkbGV3YXJlTW9kZTogdHJ1ZSxcbiAgICAgICAgaG1yOiB7XG4gICAgICAgICAgc2VydmVyOiBzZXJ2ZXIuc2VydmVyLFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICAgIGFwcFR5cGU6IFwiY3VzdG9tXCIsXG4gICAgfSk7XG5cbiAgICAvLyBWaXRlIG1pZGRsZXdhcmUg65Ox66GdIChWaXRlIOyXkOyFiyDsspjrpqwpXG4gICAgc2VydmVyLnVzZSgocmVxLCByZXMsIG5leHQpID0+IHtcbiAgICAgIC8vIEFQSeyZgCBTb25hbXUgVUnripQgRmFzdGlmeSDrnbzsmrDtirjqsIAg7LKY66as7ZWY64+E66GdIHNraXBcbiAgICAgIGlmIChyZXEudXJsPy5zdGFydHNXaXRoKHRoaXMuY29uZmlnLmFwaS5yb3V0ZS5wcmVmaXgpIHx8IHJlcS51cmw/LnN0YXJ0c1dpdGgoXCIvc29uYW11LXVpXCIpKSB7XG4gICAgICAgIHJldHVybiBuZXh0KCk7XG4gICAgICB9XG4gICAgICAvLyDrgpjrqLjsp4DripQgVml0ZSBtaWRkbGV3YXJl66GcIOyghOuLrFxuICAgICAgcmV0dXJuIHRoaXMudml0ZVNlcnZlci5taWRkbGV3YXJlcyhyZXEsIHJlcywgbmV4dCk7XG4gICAgfSk7XG5cbiAgICAvLyBjYXRjaC1hbGwg65287Jqw7Yq47JeQ7IScIOuPmeyggeycvOuhnCBBUEkvU1NSIOyymOumrFxuICAgIC8vIOqwnOuwnCDtmZjqsr3sl5DshJzripQg65287Jqw7Yq467OEIGNvbXByZXNzIOyYteyFmOydhCDtj6zquLDtlZjqs6AgSE1SIOydtOygkOydhCDst6jtlanri4jri6QuXG4gICAgc2VydmVyLnJvdXRlKHtcbiAgICAgIG1ldGhvZDogW1wiR0VUXCIsIFwiSEVBRFwiLCBcIlBPU1RcIiwgXCJQVVRcIiwgXCJERUxFVEVcIiwgXCJQQVRDSFwiXSxcbiAgICAgIHVybDogXCIvKlwiLFxuICAgICAgaGFuZGxlcjogYXN5bmMgKHJlcXVlc3QsIHJlcGx5KSA9PiB7XG4gICAgICAgIC8vIDEuIEFQSSDsmpTssq0g7LKY66asXG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMuaGFuZGxlRGV2QXBpUmVxdWVzdChyZXF1ZXN0LCBjb25maWcpO1xuICAgICAgICBpZiAocmVzdWx0KSB7XG4gICAgICAgICAgcmV0dXJuIHJlc3VsdChyZXF1ZXN0LCByZXBseSk7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCB1cmwgPSByZXF1ZXN0LnVybDtcblxuICAgICAgICAvLyAyLiBTU1Ig65287Jqw7Yq4IOyymOumrFxuICAgICAgICBjb25zdCB7IG1hdGNoU1NSUm91dGUsIHJlbmRlclNTUiB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vc3NyXCIpO1xuICAgICAgICBjb25zdCBzc3JNYXRjaCA9IG1hdGNoU1NSUm91dGUodXJsKTtcbiAgICAgICAgaWYgKHNzck1hdGNoKSB7XG4gICAgICAgICAgY29uc29sZS5sb2coYFtTU1JdIE1hdGNoZWQgcm91dGU6ICR7c3NyTWF0Y2gucm91dGUucGF0aH1gKTtcbiAgICAgICAgICBjb25zdCBodG1sID0gYXdhaXQgcmVuZGVyU1NSKFxuICAgICAgICAgICAgdXJsLFxuICAgICAgICAgICAgc3NyTWF0Y2gucm91dGUsXG4gICAgICAgICAgICBzc3JNYXRjaC5wYXJhbXMsXG4gICAgICAgICAgICByZXF1ZXN0LFxuICAgICAgICAgICAgcmVwbHksXG4gICAgICAgICAgICBjb25maWcsXG4gICAgICAgICAgICB0aGlzLnZpdGVTZXJ2ZXIsXG4gICAgICAgICAgKTtcbiAgICAgICAgICByZXBseS50eXBlKFwidGV4dC9odG1sXCIpO1xuICAgICAgICAgIHJldHVybiBodG1sO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gMy4gQ1NSIGZhbGxiYWNrXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgZnMgPSBhd2FpdCBpbXBvcnQoXCJub2RlOmZzL3Byb21pc2VzXCIpO1xuICAgICAgICAgIGxldCB0ZW1wbGF0ZSA9IGF3YWl0IGZzLnJlYWRGaWxlKFxuICAgICAgICAgICAgcGF0aC5qb2luKHRoaXMudml0ZVNlcnZlci5jb25maWcucm9vdCwgXCJpbmRleC5odG1sXCIpLFxuICAgICAgICAgICAgXCJ1dGYtOFwiLFxuICAgICAgICAgICk7XG4gICAgICAgICAgdGVtcGxhdGUgPSBhd2FpdCB0aGlzLnZpdGVTZXJ2ZXIudHJhbnNmb3JtSW5kZXhIdG1sKHVybCwgdGVtcGxhdGUpO1xuXG4gICAgICAgICAgcmVwbHkudHlwZShcInRleHQvaHRtbFwiKTtcbiAgICAgICAgICByZXR1cm4gdGVtcGxhdGU7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICB0aGlzLnZpdGVTZXJ2ZXIuc3NyRml4U3RhY2t0cmFjZShlIGFzIEVycm9yKTtcbiAgICAgICAgICBjb25zb2xlLmVycm9yKGUpO1xuICAgICAgICAgIHJlcGx5LnN0YXR1cyg1MDApO1xuICAgICAgICAgIHJldHVybiAoZSBhcyBFcnJvcikubWVzc2FnZTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIC8vIOyEnOuyhCDsooXro4wg7IucIFZpdGXrj4Qg7KKF66OMXG4gICAgc2VydmVyLmFkZEhvb2soXCJvbkNsb3NlXCIsIGFzeW5jICgpID0+IHtcbiAgICAgIGF3YWl0IHRoaXMudml0ZVNlcnZlci5jbG9zZSgpO1xuICAgIH0pO1xuXG4gICAgY29uc29sZS5sb2coXCLinJMgVml0ZSBkZXYgc2VydmVyIGludGVncmF0ZWRcIik7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHNldHVwU3RhdGljV2ViU2VydmVyKFxuICAgIHNlcnZlcjogRmFzdGlmeUluc3RhbmNlPFNlcnZlciwgSW5jb21pbmdNZXNzYWdlLCBTZXJ2ZXJSZXNwb25zZT4sXG4gICAgY29uZmlnOiBTb25hbXVGYXN0aWZ5Q29uZmlnLFxuICAgIGdsb2JhbENvbXByZXNzT3B0aW9uczogQ29tcHJlc3NPcHRpb25zIHwgdW5kZWZpbmVkLFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAvLyDqsr3roZwg66qF7ZmV7ZmUOiBhcGkvd2ViLWRpc3QvY2xpZW50ICjsoJXsoIEg7YyM7J28KSwgYXBpL3dlYi1kaXN0L3NlcnZlciAoU1NSIGVudHJ5KSwgYXBpL2Rpc3Qvc3NyIChTU1Igcm91dGVzIC0gQVBJIOyGjOycoClcbiAgICBjb25zdCB3ZWJEaXN0UGF0aCA9IHBhdGguam9pbih0aGlzLmFwaVJvb3RQYXRoLCBcIndlYi1kaXN0XCIsIFwiY2xpZW50XCIpO1xuICAgIGNvbnN0IHNzclBhdGggPSBwYXRoLmpvaW4odGhpcy5hcGlSb290UGF0aCwgXCJ3ZWItZGlzdFwiLCBcInNlcnZlclwiKTtcbiAgICBjb25zdCBzc3JFbnRyeVBhdGggPSBwYXRoLmpvaW4oc3NyUGF0aCwgXCJlbnRyeS1zZXJ2ZXIuZ2VuZXJhdGVkLmpzXCIpO1xuICAgIGNvbnN0IHNzclJvdXRlc1BhdGggPSBwYXRoLmpvaW4odGhpcy5hcGlSb290UGF0aCwgXCJkaXN0XCIsIFwic3NyXCIsIFwicm91dGVzLmpzXCIpO1xuXG4gICAgaWYgKCEoYXdhaXQgZXhpc3RzKHdlYkRpc3RQYXRoKSkpIHtcbiAgICAgIGNvbnNvbGUud2Fybihg4pqgIFdlYiBkaXN0IG5vdCBmb3VuZDogJHt3ZWJEaXN0UGF0aH1gKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBTU1IgZW50cnkg7KG07J6sIOyXrOu2gCDtmZXsnbhcbiAgICBjb25zdCBzc3JBdmFpbGFibGUgPSBhd2FpdCBleGlzdHMoc3NyRW50cnlQYXRoKTtcblxuICAgIGlmICghc3NyQXZhaWxhYmxlKSB7XG4gICAgICBjb25zb2xlLndhcm4oYOKaoCBTU1IgZW50cnkgbm90IGZvdW5kOiAke3NzckVudHJ5UGF0aH1gKTtcbiAgICAgIGNvbnNvbGUud2FybihcIiAgU1NSIHdpbGwgYmUgZGlzYWJsZWQuIE9ubHkgQ1NSIHdpbGwgd29yay5cIik7XG4gICAgfVxuXG4gICAgLy8gU1NSIOudvOyasO2KuCDroZzrk5wgKHByb2R1Y3Rpb27sl5DshJzrp4wsIOyCrOyaqeyekCDtlITroZzsoJ3tirjsnZggc3NyL3JvdXRlcy50cylcbiAgICBpZiAoc3NyQXZhaWxhYmxlKSB7XG4gICAgICBpZiAoYXdhaXQgZXhpc3RzKHNzclJvdXRlc1BhdGgpKSB7XG4gICAgICAgIC8vIHRzLWxvYWRlcuudvOuptCBcImZpbGU6Ly9cIuuhnCDsi5zsnpHtlZjripQgZnVsbHktcmVzb2x2ZWQgcGF0aOunjCDrsJvquLDsl5Ag7J2066W8IOyymOumrO2VtOyjvOuKlCBpbXBvcnRNZW1iZXJz66W8IOyCrOyaqe2VtOyVvCDtlojqsqDsp4Drp4wsXG4gICAgICAgIC8vIOyXrOq4sOuKlCDtlITroZzrjZXshZgg7ZmY6rK97JeQ7IScIGxvYWRlciDsl4bsnbQg64+M7JWE6rCA6riwIOuVjOusuOyXkCBcIuynhOynnCBqcyDtjIzsnbxcIuydmCBcIuq3uOuDpVwiIOygiOuMgOqyveuhnOulvCDrsJTroZwgaW1wb3J07ZW064+EIOuQqeuLiOuLpC5cbiAgICAgICAgLy8g7J20IOuCtOyaqeydgCDsnbQg7ZWo7IiYIOuCtOyXkOyEnCDslYTrnpjsl5Ag64KY7JisIOuLpOuluCBpbXBvcnQg7Zi47Lac7JeQ64+EIOuPmeydvO2VmOqyjCDsoIHsmqnrkKnri4jri6QuXG4gICAgICAgIGF3YWl0IGltcG9ydChzc3JSb3V0ZXNQYXRoKTtcbiAgICAgICAgY29uc29sZS5sb2coXCLinJMgU1NSIHJvdXRlcyBsb2FkZWRcIik7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zb2xlLndhcm4oYOKaoCBTU1Igcm91dGVzIG5vdCBmb3VuZDogJHtzc3JSb3V0ZXNQYXRofWApO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIOuhpOungSDsl4XrjbDsnbTtirgg64yA7J2ROiBhc3NldCBoYXNoIOu2iOydvOy5mCDsi5wg7ZiE7J6sIOuyhOyghCDsp4HsoJEg7ISc67mZXG4gICAgc2VydmVyLmdldChcIi9hc3NldHMvOmZpbGVuYW1lXCIsIGFzeW5jIChyZXF1ZXN0LCByZXBseSkgPT4ge1xuICAgICAgY29uc3QgcmVxdWVzdGVkRmlsZSA9IChyZXF1ZXN0LnBhcmFtcyBhcyB7IGZpbGVuYW1lOiBzdHJpbmcgfSkuZmlsZW5hbWU7XG4gICAgICBjb25zdCBhc3NldHNEaXIgPSBwYXRoLmpvaW4od2ViRGlzdFBhdGgsIFwiYXNzZXRzXCIpO1xuICAgICAgY29uc3Qgc2FmZUZpbGVQYXRoID0gdGhpcy5yZXNvbHZlUGF0aFdpdGhpbkJhc2VEaXIoYXNzZXRzRGlyLCByZXF1ZXN0ZWRGaWxlKTtcbiAgICAgIGlmIChzYWZlRmlsZVBhdGggPT09IG51bGwpIHtcbiAgICAgICAgcmVwbHkuc3RhdHVzKDQwMykuc2VuZCgpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjb25zdCBub3JtYWxpemVkUmVxdWVzdGVkRmlsZSA9IHBhdGgucmVsYXRpdmUoYXNzZXRzRGlyLCBzYWZlRmlsZVBhdGgpLnJlcGxhY2UoL1xcXFwvZywgXCIvXCIpO1xuXG4gICAgICBjb25zdCBhc3NldFBhdGggPSBgL2Fzc2V0cy8ke25vcm1hbGl6ZWRSZXF1ZXN0ZWRGaWxlfWA7XG5cbiAgICAgIC8vIENhY2hlLUNvbnRyb2wg7Zek642UIOqysOyglVxuICAgICAgY29uc3QgZ2V0Q2FjaGVDb250cm9sRm9yQXNzZXQgPSAoKTogQ2FjaGVDb250cm9sQ29uZmlnID0+IHtcbiAgICAgICAgY29uc3QgY2FjaGVSZXE6IENhY2hlQ29udHJvbFJlcXVlc3QgPSB7XG4gICAgICAgICAgdHlwZTogXCJhc3NldHNcIixcbiAgICAgICAgICB1cmw6IHJlcXVlc3QudXJsLFxuICAgICAgICAgIHBhdGg6IGFzc2V0UGF0aCxcbiAgICAgICAgICBtZXRob2Q6IHJlcXVlc3QubWV0aG9kLFxuICAgICAgICB9O1xuXG4gICAgICAgIC8vIOyCrOyaqeyekCDsoJXsnZgg7ZW465Ok65+sIOyasOyEoFxuICAgICAgICBpZiAoY29uZmlnLmNhY2hlQ29udHJvbEhhbmRsZXIpIHtcbiAgICAgICAgICBjb25zdCByZXN1bHQgPSBjb25maWcuY2FjaGVDb250cm9sSGFuZGxlcihjYWNoZVJlcSk7XG4gICAgICAgICAgaWYgKHJlc3VsdCkgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIOq4sOuzuOqwkjogaW1tdXRhYmxlXG4gICAgICAgIHJldHVybiBDYWNoZVByZXNldHMuaW1tdXRhYmxlO1xuICAgICAgfTtcblxuICAgICAgLy8gaW5kZXgtKi5qcyDrmJDripQgaW5kZXgtKi5jc3Mg7JqU7LKt7J24IOqyveyasFxuICAgICAgaWYgKC9eaW5kZXgtW2EtZjAtOV0rXFwuKGpzfGNzcykkLy50ZXN0KG5vcm1hbGl6ZWRSZXF1ZXN0ZWRGaWxlKSkge1xuICAgICAgICBjb25zdCBleHQgPSBub3JtYWxpemVkUmVxdWVzdGVkRmlsZS5zcGxpdChcIi5cIikucG9wKCk7XG4gICAgICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgZnMucmVhZGRpcihhc3NldHNEaXIpO1xuICAgICAgICBjb25zdCBjdXJyZW50RmlsZSA9IGZpbGVzLmZpbmQoKGYpID0+IGYuc3RhcnRzV2l0aChcImluZGV4LVwiKSAmJiBmLmVuZHNXaXRoKGAuJHtleHR9YCkpO1xuXG4gICAgICAgIGlmIChjdXJyZW50RmlsZSkge1xuICAgICAgICAgIGNvbnN0IGZpbGVQYXRoID0gcGF0aC5qb2luKGFzc2V0c0RpciwgY3VycmVudEZpbGUpO1xuICAgICAgICAgIGNvbnN0IGNvbnRlbnQgPSBhd2FpdCBmcy5yZWFkRmlsZShmaWxlUGF0aCk7XG4gICAgICAgICAgcmVwbHkudHlwZShleHQgPT09IFwianNcIiA/IFwiYXBwbGljYXRpb24vamF2YXNjcmlwdFwiIDogXCJ0ZXh0L2Nzc1wiKTtcbiAgICAgICAgICBhcHBseUNhY2hlSGVhZGVycyhyZXBseSwgZ2V0Q2FjaGVDb250cm9sRm9yQXNzZXQoKSk7XG4gICAgICAgICAgcmV0dXJuIHJlcGx5LnNlbmQoY29udGVudCk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8g7J2867CYIO2MjOydvCDshJzruZlcbiAgICAgIGNvbnN0IGZpbGVQYXRoID0gc2FmZUZpbGVQYXRoO1xuICAgICAgaWYgKGF3YWl0IGV4aXN0cyhmaWxlUGF0aCkpIHtcbiAgICAgICAgY29uc3QgY29udGVudCA9IGF3YWl0IGZzLnJlYWRGaWxlKGZpbGVQYXRoKTtcbiAgICAgICAgY29uc3QgZXh0ID0gbm9ybWFsaXplZFJlcXVlc3RlZEZpbGUuc3BsaXQoXCIuXCIpLnBvcCgpO1xuICAgICAgICByZXBseS50eXBlKGV4dCA9PT0gXCJqc1wiID8gXCJhcHBsaWNhdGlvbi9qYXZhc2NyaXB0XCIgOiBleHQgPT09IFwiY3NzXCIgPyBcInRleHQvY3NzXCIgOiBcIlwiKTtcbiAgICAgICAgaWYgKG5vcm1hbGl6ZWRSZXF1ZXN0ZWRGaWxlLmluY2x1ZGVzKFwiLVwiKSkge1xuICAgICAgICAgIGFwcGx5Q2FjaGVIZWFkZXJzKHJlcGx5LCBnZXRDYWNoZUNvbnRyb2xGb3JBc3NldCgpKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmVwbHkuc2VuZChjb250ZW50KTtcbiAgICAgIH1cblxuICAgICAgcmVwbHkuc3RhdHVzKDQwNCkuc2VuZCgpO1xuICAgIH0pO1xuXG4gICAgLy8gU1NSIOudvOyasO2KuCDqsJzrs4Qg65Ox66GdIChjb21wcmVzcyDsmLXshZjsnbQg65287Jqw7Yq467OE66GcIOyggeyaqeuQmOuPhOuhnSlcbiAgICBpZiAoc3NyQXZhaWxhYmxlKSB7XG4gICAgICBjb25zdCB7IGdldFNTUlJvdXRlcyB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vc3NyXCIpO1xuICAgICAgY29uc3QgeyByZW5kZXJTU1IgfSA9IGF3YWl0IGltcG9ydChcIi4uL3Nzci9yZW5kZXJlclwiKTtcbiAgICAgIGNvbnN0IHNzclJvdXRlcyA9IGdldFNTUlJvdXRlcygpO1xuXG4gICAgICBmb3IgKGNvbnN0IHJvdXRlIG9mIHNzclJvdXRlcykge1xuICAgICAgICBzZXJ2ZXIucm91dGUoe1xuICAgICAgICAgIG1ldGhvZDogW1wiR0VUXCIsIFwiSEVBRFwiXSxcbiAgICAgICAgICB1cmw6IHJvdXRlLnBhdGgsXG4gICAgICAgICAgY29tcHJlc3M6IHRvRmFzdGlmeUNvbXByZXNzT3B0aW9uKHJvdXRlLmNvbXByZXNzID8/IHRydWUsIGdsb2JhbENvbXByZXNzT3B0aW9ucyksXG4gICAgICAgICAgaGFuZGxlcjogYXN5bmMgKHJlcXVlc3QsIHJlcGx5KSA9PiB7XG4gICAgICAgICAgICBjb25zdCB1cmwgPSByZXF1ZXN0LnVybDtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGBbU1NSXSBNYXRjaGVkIHJvdXRlOiAke3JvdXRlLnBhdGh9YCk7XG5cbiAgICAgICAgICAgIGNvbnN0IHBhcmFtcyA9IHRoaXMuZXh0cmFjdFBhdGhQYXJhbXMocm91dGUucGF0aCwgdXJsKTtcbiAgICAgICAgICAgIGNvbnN0IGh0bWwgPSBhd2FpdCByZW5kZXJTU1IodXJsLCByb3V0ZSwgcGFyYW1zLCByZXF1ZXN0LCByZXBseSwgY29uZmlnKTtcblxuICAgICAgICAgICAgcmVwbHkudHlwZShcInRleHQvaHRtbFwiKTtcbiAgICAgICAgICAgIHJldHVybiBodG1sO1xuICAgICAgICAgIH0sXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIENTUiBvciBTdGF0aWMgRmlsZSBGYWxsYmFjayAoU1NSIOudvOyasO2KuOyXkCDrp6Tsua3rkJjsp4Ag7JWK64qUIOuqqOuToCDsmpTssq0pXG4gICAgc2VydmVyLnJvdXRlKHtcbiAgICAgIG1ldGhvZDogW1wiR0VUXCIsIFwiSEVBRFwiXSxcbiAgICAgIHVybDogXCIqXCIsXG4gICAgICBoYW5kbGVyOiBhc3luYyAocmVxdWVzdCwgcmVwbHkpID0+IHtcbiAgICAgICAgLy8gL2FwaSwgL3NvbmFtdS11aeuKlCA0MDQg6re464yA66GcXG4gICAgICAgIGlmIChyZXF1ZXN0LnVybC5zdGFydHNXaXRoKFwiL2FwaVwiKSB8fCByZXF1ZXN0LnVybC5zdGFydHNXaXRoKFwiL3NvbmFtdS11aVwiKSkge1xuICAgICAgICAgIHJlcGx5LnN0YXR1cyg0MDQpLnNlbmQoKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBDU1LsmqkgQ2FjaGUtQ29udHJvbCDtl6TrjZQg7ISk7KCVXG4gICAgICAgIGlmIChjb25maWcuY2FjaGVDb250cm9sSGFuZGxlcikge1xuICAgICAgICAgIGNvbnN0IGNzckNhY2hlUmVxOiBDYWNoZUNvbnRyb2xSZXF1ZXN0ID0ge1xuICAgICAgICAgICAgdHlwZTogXCJjc3JcIixcbiAgICAgICAgICAgIHVybDogcmVxdWVzdC51cmwsXG4gICAgICAgICAgICBwYXRoOiByZXF1ZXN0LnVybC5zcGxpdChcIj9cIilbMF0sXG4gICAgICAgICAgICBtZXRob2Q6IHJlcXVlc3QubWV0aG9kLFxuICAgICAgICAgIH07XG4gICAgICAgICAgY29uc3QgY3NyQ2FjaGVDb25maWcgPSBjb25maWcuY2FjaGVDb250cm9sSGFuZGxlcihjc3JDYWNoZVJlcSk7XG5cbiAgICAgICAgICBpZiAoY3NyQ2FjaGVDb25maWcpIHtcbiAgICAgICAgICAgIGFwcGx5Q2FjaGVIZWFkZXJzKHJlcGx5LCBjc3JDYWNoZUNvbmZpZyk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8g7KCV7KCBIO2MjOydvOydtCDsobTsnqztlaAg6rK97JqwLCDsoJXsoIEg7YyM7J287J2EIOuovOyggCDshJzruZntlbTslbztlahcbiAgICAgICAgY29uc3QgcmVxdWVzdFBhdGggPSB0aGlzLmdldFBhdGhuYW1lRnJvbVVybChyZXF1ZXN0LnVybCk7XG4gICAgICAgIGNvbnN0IHNhZmVGaWxlUGF0aCA9IHRoaXMucmVzb2x2ZVBhdGhXaXRoaW5CYXNlRGlyKHdlYkRpc3RQYXRoLCByZXF1ZXN0UGF0aCk7XG4gICAgICAgIGlmIChzYWZlRmlsZVBhdGggPT09IG51bGwpIHtcbiAgICAgICAgICByZXBseS5zdGF0dXMoNDAzKS5zZW5kKCk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGlmIChhd2FpdCBmaWxlRXhpc3RzKHNhZmVGaWxlUGF0aCkpIHtcbiAgICAgICAgICBjb25zdCBjb250ZW50ID0gYXdhaXQgZnMucmVhZEZpbGUoc2FmZUZpbGVQYXRoKTtcbiAgICAgICAgICByZXR1cm4gcmVwbHkudHlwZShtaW1lTG9va3VwKHNhZmVGaWxlUGF0aCkgfHwgXCJhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW1cIikuc2VuZChjb250ZW50KTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIENTUiBmYWxsYmFjazogaW5kZXguaHRtbCDshJzruZlcbiAgICAgICAgY29uc3QgaW5kZXhQYXRoID0gcGF0aC5qb2luKHdlYkRpc3RQYXRoLCBcImluZGV4Lmh0bWxcIik7XG4gICAgICAgIHJldHVybiByZXBseS50eXBlKFwidGV4dC9odG1sXCIpLnNlbmQoYXdhaXQgZnMucmVhZEZpbGUoaW5kZXhQYXRoLCBcInV0Zi04XCIpKTtcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBjb25zb2xlLmxvZyhg4pyTIFN0YXRpYyB3ZWIgc2VydmVyIGNvbmZpZ3VyZWQgd2l0aCAke3NzckF2YWlsYWJsZSA/IFwiU1NSXCIgOiBcIkNTUiBvbmx5XCJ9IHN1cHBvcnRgKTtcbiAgfVxuXG4gIGNyZWF0ZUFwaUhhbmRsZXIoXG4gICAgYXBpOiBFeHRlbmRlZEFwaSxcbiAgICBjb25maWc6IFNvbmFtdUZhc3RpZnlDb25maWcsXG4gICk6IChyZXF1ZXN0OiBGYXN0aWZ5UmVxdWVzdCwgcmVwbHk6IEZhc3RpZnlSZXBseSkgPT4gUHJvbWlzZTx1bmtub3duPiB7XG4gICAgcmV0dXJuIGFzeW5jIChyZXF1ZXN0OiBGYXN0aWZ5UmVxdWVzdCwgcmVwbHk6IEZhc3RpZnlSZXBseSk6IFByb21pc2U8dW5rbm93bj4gPT4ge1xuICAgICAgLy8gQ29udGV4dCDsg53shLFcbiAgICAgIGNvbnN0IGNvbnRleHQ6IENvbnRleHQgPSBhd2FpdCB0aGlzLmNyZWF0ZUNvbnRleHQoY29uZmlnLCByZXF1ZXN0LCByZXBseSk7XG5cbiAgICAgIHJldHVybiB0aGlzLmFzeW5jTG9jYWxTdG9yYWdlLnJ1bih7IGNvbnRleHQgfSwgYXN5bmMgKCkgPT4ge1xuICAgICAgICAvLyBndWFyZHMg7LKY66asXG4gICAgICAgIChhcGkub3B0aW9ucy5ndWFyZHMgPz8gW10pLmV2ZXJ5KChndWFyZCkgPT4gY29uZmlnLmd1YXJkSGFuZGxlcihndWFyZCwgcmVxdWVzdCwgYXBpKSk7XG5cbiAgICAgICAgLy8g7YyM652866+47YSwIOygleuztOuhnCB6b2Qg7Iqk7YKk66eIIOu5jOuTnFxuICAgICAgICBjb25zdCB7IGdldFpvZE9iamVjdEZyb21BcGkgfSA9IGF3YWl0IGltcG9ydChcIi4vY29kZS1jb252ZXJ0ZXJzXCIpO1xuICAgICAgICBjb25zdCBSZXFUeXBlID0gZ2V0Wm9kT2JqZWN0RnJvbUFwaShhcGksIHRoaXMuc3luY2VyLnR5cGVzKTtcblxuICAgICAgICAvLyByZXF1ZXN0IO2MjOyLsVxuICAgICAgICBjb25zdCB3aGljaCA9IGFwaS5vcHRpb25zLmh0dHBNZXRob2QgPT09IFwiR0VUXCIgPyBcInF1ZXJ5XCIgOiBcImJvZHlcIjtcbiAgICAgICAgbGV0IHJlcUJvZHk6IHtcbiAgICAgICAgICBba2V5OiBzdHJpbmddOiB1bmtub3duO1xuICAgICAgICB9O1xuICAgICAgICAvLyDtjIzsnbwg7JeF66Gc65OcIOyeiOuKlCDqsr3smrAg7J6E7IucIOuNsOydtO2EsFxuICAgICAgICBjb25zdCBmaWxlczoge1xuICAgICAgICAgIGJ1ZmZlcmVkRmlsZXM6IEJ1ZmZlcmVkRmlsZVtdO1xuICAgICAgICAgIHVwbG9hZGVkRmlsZXM6IFVwbG9hZGVkRmlsZVtdO1xuICAgICAgICB9ID0ge1xuICAgICAgICAgIGJ1ZmZlcmVkRmlsZXM6IFtdLFxuICAgICAgICAgIHVwbG9hZGVkRmlsZXM6IFtdLFxuICAgICAgICB9O1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgYm9keSA9IChyZXF1ZXN0W3doaWNoXSA/PyB7fSkgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gICAgICAgICAgaWYgKGFwaS51cGxvYWRPcHRpb25zKSB7XG4gICAgICAgICAgICBjb25zdCBwYXJ0cyA9IHJlcXVlc3QucGFydHMoe1xuICAgICAgICAgICAgICBsaW1pdHM6IGFwaS51cGxvYWRPcHRpb25zLmxpbWl0cyxcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAvLyBGb3JtRGF0YeydmCBmaWVsZOuTpOydhCDsnoTsi5zroZwg7KCA7J6lXG4gICAgICAgICAgICBjb25zdCBmaWVsZHM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fTtcblxuICAgICAgICAgICAgaWYgKGFwaS51cGxvYWRPcHRpb25zLmNvbnN1bWUgPT09IFwiYnVmZmVyXCIgfHwgIWFwaS51cGxvYWRPcHRpb25zLmNvbnN1bWUpIHtcbiAgICAgICAgICAgICAgLy8gQnVmZmVyIOuqqOuTnDog66mU66qo66as7JeQIOuhnOuTnFxuICAgICAgICAgICAgICBmb3IgYXdhaXQgKGNvbnN0IHBhcnQgb2YgcGFydHMpIHtcbiAgICAgICAgICAgICAgICBpZiAocGFydC50eXBlID09PSBcImZpbGVcIikge1xuICAgICAgICAgICAgICAgICAgLy8gQ1JJVElDQUw6IO2MjOydvCDsiqTtirjrprzsnYQg7KaJ7IucIGNvbnN1bWXtlbTslbwg64uk7J2MIHBhcnTroZwg64SY7Ja06rCIIOyImCDsnojsnYxcbiAgICAgICAgICAgICAgICAgIC8vIOydtCDtmLjstpzsnbQg7JeG7Jy866m0IOyiheyihSBtdWx0aXBhcnQg7YyM7Iux7J20IHBlbmRpbmcg7IOB7YOc66GcIO2DgOyehOyVhOybgyDrsJzsg51cbiAgICAgICAgICAgICAgICAgIGNvbnN0IGJ1ZmZlciA9IGF3YWl0IHBhcnQudG9CdWZmZXIoKTtcbiAgICAgICAgICAgICAgICAgIGZpbGVzLmJ1ZmZlcmVkRmlsZXMucHVzaChuZXcgQnVmZmVyZWRGaWxlKHBhcnQsIGJ1ZmZlcikpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAocGFydC50eXBlID09PSBcImZpZWxkXCIpIHtcbiAgICAgICAgICAgICAgICAgIGZpZWxkc1twYXJ0LmZpZWxkbmFtZV0gPSBTdHJpbmcocGFydC52YWx1ZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGFwaS51cGxvYWRPcHRpb25zLmNvbnN1bWUgPT09IFwic3RyZWFtXCIpIHtcbiAgICAgICAgICAgICAgLy8gU3RyZWFtIOuqqOuTnDog7KaJ7IucIOyggOyepeyGjOuhnCDsiqTtirjrpqzrsI1cbiAgICAgICAgICAgICAgY29uc3QgZGlza05hbWUgPSBhcGkudXBsb2FkT3B0aW9ucy5kZXN0aW5hdGlvbjtcbiAgICAgICAgICAgICAgY29uc3QgZGlzayA9IHRoaXMuc3RvcmFnZS51c2UoZGlza05hbWUpO1xuXG4gICAgICAgICAgICAgIC8vIOyasOyEoOyInOychDog642w7L2U66CI7J207YSwID4g7KCE7JetIOyEpOyglSA+IOq4sOuzuOqwklxuICAgICAgICAgICAgICBjb25zdCBrZXlHZW5lcmF0b3I6IEtleUdlbmVyYXRvciA9XG4gICAgICAgICAgICAgICAgYXBpLnVwbG9hZE9wdGlvbnMua2V5R2VuZXJhdG9yID8/XG4gICAgICAgICAgICAgICAgdGhpcy5jb25maWcuc2VydmVyLnN0b3JhZ2U/LmtleUdlbmVyYXRvciA/P1xuICAgICAgICAgICAgICAgIGRlZmF1bHRLZXlHZW5lcmF0b3I7XG5cbiAgICAgICAgICAgICAgZm9yIGF3YWl0IChjb25zdCBwYXJ0IG9mIHBhcnRzKSB7XG4gICAgICAgICAgICAgICAgaWYgKHBhcnQudHlwZSA9PT0gXCJmaWxlXCIpIHtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IGtleSA9IGF3YWl0IGtleUdlbmVyYXRvcih7XG4gICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lOiBwYXJ0LmZpbGVuYW1lLFxuICAgICAgICAgICAgICAgICAgICBtaW1ldHlwZTogcGFydC5taW1ldHlwZSxcbiAgICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgICBhd2FpdCBkaXNrLnB1dFN0cmVhbShrZXksIHBhcnQuZmlsZSwge1xuICAgICAgICAgICAgICAgICAgICBjb250ZW50VHlwZTogcGFydC5taW1ldHlwZSxcbiAgICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgICBjb25zdCB1cmwgPSBhd2FpdCBkaXNrLmdldFVybChrZXkpO1xuICAgICAgICAgICAgICAgICAgY29uc3Qgc2lnbmVkVXJsID0gYXdhaXQgZGlzay5nZXRTaWduZWRVcmwoa2V5KTtcblxuICAgICAgICAgICAgICAgICAgZmlsZXMudXBsb2FkZWRGaWxlcy5wdXNoKFxuICAgICAgICAgICAgICAgICAgICBuZXcgVXBsb2FkZWRGaWxlKHtcbiAgICAgICAgICAgICAgICAgICAgICBmaWxlbmFtZTogcGFydC5maWxlbmFtZSxcbiAgICAgICAgICAgICAgICAgICAgICBtaW1ldHlwZTogcGFydC5taW1ldHlwZSxcbiAgICAgICAgICAgICAgICAgICAgICBzaXplOiBwYXJ0LmZpbGUuYnl0ZXNSZWFkLFxuICAgICAgICAgICAgICAgICAgICAgIHVybCxcbiAgICAgICAgICAgICAgICAgICAgICBzaWduZWRVcmwsXG4gICAgICAgICAgICAgICAgICAgICAga2V5LFxuICAgICAgICAgICAgICAgICAgICAgIGRpc2tOYW1lLFxuICAgICAgICAgICAgICAgICAgICB9KSxcbiAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChwYXJ0LnR5cGUgPT09IFwiZmllbGRcIikge1xuICAgICAgICAgICAgICAgICAgZmllbGRzW3BhcnQuZmllbGRuYW1lXSA9IFN0cmluZyhwYXJ0LnZhbHVlKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gcXProZwg7KSR7LKpIOq1rOyhsCDtjIzsi7E6IHBhcmFtc1tjYXRlZ29yeV0g4oaSIHsgcGFyYW1zOiB7IGNhdGVnb3J5OiBcInRlc3RcIiB9IH1cbiAgICAgICAgICAgIGNvbnN0IHFzID0gYXdhaXQgaW1wb3J0KFwicXNcIik7XG4gICAgICAgICAgICBjb25zdCBwYXJzZWQgPSBxcy5kZWZhdWx0LnBhcnNlKGZpZWxkcyk7XG4gICAgICAgICAgICBPYmplY3QuYXNzaWduKGJvZHksIHBhcnNlZCk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgY29uc3QgeyBmYXN0aWZ5Q2FzdGVyIH0gPSBhd2FpdCBpbXBvcnQoXCIuL2Nhc3RlclwiKTtcbiAgICAgICAgICByZXFCb2R5ID0gZmFzdGlmeUNhc3RlcihSZXFUeXBlKS5wYXJzZShib2R5KTtcbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgIGNvbnN0IHsgWm9kRXJyb3IgfSA9IGF3YWl0IGltcG9ydChcInpvZFwiKTtcbiAgICAgICAgICBpZiAoZSBpbnN0YW5jZW9mIFpvZEVycm9yKSB7XG4gICAgICAgICAgICBjb25zdCB7IGh1bWFuaXplWm9kRXJyb3IgfSA9IGF3YWl0IGltcG9ydChcIi4uL3V0aWxzL3pvZC1lcnJvclwiKTtcbiAgICAgICAgICAgIGNvbnN0IG1lc3NhZ2VzID0gaHVtYW5pemVab2RFcnJvcihlKVxuICAgICAgICAgICAgICAubWFwKChpc3N1ZSkgPT4gaXNzdWUubWVzc2FnZSlcbiAgICAgICAgICAgICAgLmpvaW4oXCIgXCIpO1xuICAgICAgICAgICAgY29uc3QgeyBCYWRSZXF1ZXN0RXhjZXB0aW9uIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9leGNlcHRpb25zL3NvLWV4Y2VwdGlvbnNcIik7XG4gICAgICAgICAgICB0aHJvdyBuZXcgQmFkUmVxdWVzdEV4Y2VwdGlvbihtZXNzYWdlcyBhcyBMb2NhbGl6ZWRTdHJpbmcsIHtcbiAgICAgICAgICAgICAgem9kRXJyb3I6IGUsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhyb3cgZTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBDb250ZW50LVR5cGVcbiAgICAgICAgcmVwbHkudHlwZShhcGkub3B0aW9ucy5jb250ZW50VHlwZSA/PyBcImFwcGxpY2F0aW9uL2pzb25cIik7XG5cbiAgICAgICAgLy8gQ2FjaGUtQ29udHJvbCDtl6TrjZQg7ISk7KCVXG4gICAgICAgIGNvbnN0IGFwaUNhY2hlQ29uZmlnID0gdGhpcy5nZXRBcGlDYWNoZUNvbnRyb2woYXBpLCByZXF1ZXN0LCBjb25maWcpO1xuICAgICAgICBpZiAoYXBpQ2FjaGVDb25maWcpIHtcbiAgICAgICAgICBhcHBseUNhY2hlSGVhZGVycyhyZXBseSwgYXBpQ2FjaGVDb25maWcpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8g7JeF66Gc65OcIOyYteyFmOydtCDsnojripQg6rK97JqwIO2MjOydvCDrjbDsnbTthLDrpbwgQ29udGV4dOyXkCDstpTqsIBcbiAgICAgICAgaWYgKGFwaS51cGxvYWRPcHRpb25zKSB7XG4gICAgICAgICAgY29uc3QgY29uc3VtZSA9IGFwaS51cGxvYWRPcHRpb25zLmNvbnN1bWUgPz8gXCJidWZmZXJcIjtcbiAgICAgICAgICBpZiAoY29uc3VtZSA9PT0gXCJidWZmZXJcIikge1xuICAgICAgICAgICAgY29udGV4dC5idWZmZXJlZEZpbGVzID0gZmlsZXMuYnVmZmVyZWRGaWxlcztcbiAgICAgICAgICB9IGVsc2UgaWYgKGNvbnN1bWUgPT09IFwic3RyZWFtXCIpIHtcbiAgICAgICAgICAgIGNvbnRleHQudXBsb2FkZWRGaWxlcyA9IGZpbGVzLnVwbG9hZGVkRmlsZXM7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8g66qo6424IOuplOyGjOuTnCBhcmdzIOyDneyEse2VmOyXrCDtmLjstpxcbiAgICAgICAgY29uc3QgeyBBcGlQYXJhbVR5cGUgfSA9IGF3YWl0IGltcG9ydChcIi4uL3R5cGVzL3R5cGVzXCIpO1xuICAgICAgICBjb25zdCBhcmdzID0gYXBpLnBhcmFtZXRlcnMubWFwKChwYXJhbSkgPT4ge1xuICAgICAgICAgIC8vIENvbnRleHQg7J247KCd7IWYXG4gICAgICAgICAgaWYgKEFwaVBhcmFtVHlwZS5pc0NvbnRleHQocGFyYW0udHlwZSkpIHtcbiAgICAgICAgICAgIHJldHVybiBjb250ZXh0O1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gcmVxQm9keVtwYXJhbS5uYW1lXTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHJldHVybiB0aGlzLmludm9rZU1vZGVsTWV0aG9kKGFwaSwgYXJncywgcmVwbHkpO1xuICAgICAgfSk7XG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBVUkzsl5DshJwgcGF0aCBwYXJhbXPrpbwg7LaU7Lac7ZWp64uI64ukLlxuICAgKiDsmIg6IHBhdHRlcm49XCIvYWRtaW4vY29tcGFuaWVzLzpjb21wYW55SWRcIiwgdXJsPVwiL2FkbWluL2NvbXBhbmllcy8xMjNcIiDihpIgeyBjb21wYW55SWQ6IFwiMTIzXCIgfVxuICAgKi9cbiAgcHJpdmF0ZSBleHRyYWN0UGF0aFBhcmFtcyhwYXR0ZXJuOiBzdHJpbmcsIHVybDogc3RyaW5nKTogUmVjb3JkPHN0cmluZywgc3RyaW5nPiB7XG4gICAgY29uc3QgcGF0dGVyblBhcnRzID0gcGF0dGVybi5zcGxpdChcIi9cIikuZmlsdGVyKEJvb2xlYW4pO1xuICAgIGNvbnN0IHVybFBhcnRzID0gdGhpcy5nZXRQYXRobmFtZUZyb21VcmwodXJsKS5zcGxpdChcIi9cIikuZmlsdGVyKEJvb2xlYW4pO1xuICAgIGNvbnN0IHBhcmFtczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9O1xuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBwYXR0ZXJuUGFydHMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmIChwYXR0ZXJuUGFydHNbaV0uc3RhcnRzV2l0aChcIjpcIikpIHtcbiAgICAgICAgcGFyYW1zW3BhdHRlcm5QYXJ0c1tpXS5zbGljZSgxKV0gPSB1cmxQYXJ0c1tpXTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHBhcmFtcztcbiAgfVxuXG4gIHByaXZhdGUgaXNQYXRoUGF0dGVybk1hdGNoKHBhdHRlcm46IHN0cmluZywgdXJsOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICBjb25zdCBwYXR0ZXJuUGFydHMgPSBwYXR0ZXJuLnNwbGl0KFwiL1wiKS5maWx0ZXIoQm9vbGVhbik7XG4gICAgY29uc3QgdXJsUGFydHMgPSB0aGlzLmdldFBhdGhuYW1lRnJvbVVybCh1cmwpLnNwbGl0KFwiL1wiKS5maWx0ZXIoQm9vbGVhbik7XG5cbiAgICBpZiAocGF0dGVyblBhcnRzLmxlbmd0aCAhPT0gdXJsUGFydHMubGVuZ3RoKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBwYXR0ZXJuUGFydHMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IHBhdHRlcm5QYXJ0ID0gcGF0dGVyblBhcnRzW2ldO1xuICAgICAgY29uc3QgdXJsUGFydCA9IHVybFBhcnRzW2ldO1xuICAgICAgaWYgKHBhdHRlcm5QYXJ0LnN0YXJ0c1dpdGgoXCI6XCIpKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgaWYgKHBhdHRlcm5QYXJ0ICE9PSB1cmxQYXJ0KSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0UGF0aG5hbWVGcm9tVXJsKHVybDogc3RyaW5nKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdXJsLnNwbGl0KFwiP1wiKVswXTtcbiAgfVxuXG4gIHByaXZhdGUgcmVzb2x2ZVBhdGhXaXRoaW5CYXNlRGlyKGJhc2VEaXI6IHN0cmluZywgaW5wdXRQYXRoOiBzdHJpbmcpOiBzdHJpbmcgfCBudWxsIHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgZGVjb2RlZCA9IGRlY29kZVVSSUNvbXBvbmVudChpbnB1dFBhdGgpLnJlcGxhY2UoL1xcXFwvZywgXCIvXCIpO1xuICAgICAgaWYgKGRlY29kZWQuaW5jbHVkZXMoXCJcXDBcIikpIHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG4gICAgICBjb25zdCByZWxhdGl2ZVBhdGggPSBkZWNvZGVkLnJlcGxhY2UoL15cXC8rLywgXCJcIik7XG4gICAgICBjb25zdCByZXNvbHZlZFBhdGggPSBwYXRoLnJlc29sdmUoYmFzZURpciwgcmVsYXRpdmVQYXRoKTtcbiAgICAgIGNvbnN0IHJlbGF0aXZlRnJvbUJhc2UgPSBwYXRoLnJlbGF0aXZlKGJhc2VEaXIsIHJlc29sdmVkUGF0aCk7XG4gICAgICBpZiAocmVsYXRpdmVGcm9tQmFzZS5zdGFydHNXaXRoKFwiLi5cIikgfHwgcGF0aC5pc0Fic29sdXRlKHJlbGF0aXZlRnJvbUJhc2UpKSB7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHJlc29sdmVkUGF0aDtcbiAgICB9IGNhdGNoIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBBUEkg7J2R64u17JeQIOyggeyaqe2VoCBDYWNoZS1Db250cm9sIOyEpOygleydhCDqsrDsoJXtlanri4jri6QuXG4gICAqIOyasOyEoOyInOychDog6rCc67OEIOyngOyglSA+IGNhY2hlQ29udHJvbEhhbmRsZXJcbiAgICovXG4gIHByaXZhdGUgZ2V0QXBpQ2FjaGVDb250cm9sKFxuICAgIGFwaTogRXh0ZW5kZWRBcGksXG4gICAgcmVxdWVzdDogRmFzdGlmeVJlcXVlc3QsXG4gICAgY29uZmlnOiBTb25hbXVGYXN0aWZ5Q29uZmlnLFxuICApIHtcbiAgICAvLyDrjbDsvZTroIjsnbTthLAg7ISk7KCVIOyasOyEoFxuICAgIGlmIChhcGkub3B0aW9ucy5jYWNoZUNvbnRyb2wpIHtcbiAgICAgIHJldHVybiBhcGkub3B0aW9ucy5jYWNoZUNvbnRyb2w7XG4gICAgfVxuXG4gICAgLy8g7KCE7JetIO2VuOuTpOufrFxuICAgIGlmIChjb25maWcuY2FjaGVDb250cm9sSGFuZGxlcikge1xuICAgICAgY29uc3QgY2FjaGVSZXE6IENhY2hlQ29udHJvbFJlcXVlc3QgPSB7XG4gICAgICAgIHR5cGU6IFwiYXBpXCIsXG4gICAgICAgIHVybDogcmVxdWVzdC51cmwsXG4gICAgICAgIHBhdGg6IHJlcXVlc3Qucm91dGVPcHRpb25zPy51cmwgPz8gcmVxdWVzdC51cmwuc3BsaXQoXCI/XCIpWzBdLFxuICAgICAgICBtZXRob2Q6IHJlcXVlc3QubWV0aG9kLFxuICAgICAgICBhcGksXG4gICAgICB9O1xuICAgICAgY29uc3QgcmVzdWx0ID0gY29uZmlnLmNhY2hlQ29udHJvbEhhbmRsZXIoY2FjaGVSZXEpO1xuICAgICAgaWYgKHJlc3VsdCkgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG5cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBTU1LsmqkgQVBJIO2YuOy2nCAoSFRUUCDsmKTrsoTtl6Trk5wg7JeG7J20IOyngeygkSDtmLjstpwpXG4gICAqIGNyZWF0ZUFwaUhhbmRsZXLsnZgg66Gc7KeB7J2EIOyerOyCrOyaqe2VmOuQmCwgcmVxdWVzdCDtjIzsi7Eg64yA7IugIHBhcmFtcyDsp4HsoJEg7IKs7JqpXG4gICAqL1xuICBhc3luYyBpbnZva2VBcGlGb3JTU1IoXG4gICAgYXBpOiBFeHRlbmRlZEFwaSxcbiAgICAvLyBveGxpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueSAtLSBTU1Lsl5DshJwg64uk7JaR7ZWcIO2DgOyeheydmCBwYXJhbXPrpbwg67Cb7JWE7JW8IO2VqFxuICAgIHBhcmFtczogYW55W10sXG4gICAgY29uZmlnOiBTb25hbXVGYXN0aWZ5Q29uZmlnLFxuICAgIHJlcXVlc3Q6IEZhc3RpZnlSZXF1ZXN0LFxuICAgIHJlcGx5OiBGYXN0aWZ5UmVwbHksXG4gICk6IFByb21pc2U8dW5rbm93bj4ge1xuICAgIC8vIENvbnRleHQg7IOd7ISxICjquLDsobQg66mU7IaM65OcIOyerOyCrOyaqSlcbiAgICBjb25zdCBjb250ZXh0ID0gYXdhaXQgdGhpcy5jcmVhdGVDb250ZXh0KGNvbmZpZywgcmVxdWVzdCwgcmVwbHkpO1xuXG4gICAgcmV0dXJuIHRoaXMuYXN5bmNMb2NhbFN0b3JhZ2UucnVuKHsgY29udGV4dCB9LCBhc3luYyAoKSA9PiB7XG4gICAgICAvLyBhcmdzIOyDneyEsTogQ29udGV4dCDtjIzrnbzrr7jthLDripQg7KO87J6FLCDrgpjrqLjsp4DripQgcGFyYW1z7JeQ7IScIOqwgOyguOyYpOq4sFxuICAgICAgY29uc3QgeyBBcGlQYXJhbVR5cGUgfSA9IGF3YWl0IGltcG9ydChcIi4uL3R5cGVzL3R5cGVzXCIpO1xuICAgICAgbGV0IHBhcmFtc0luZGV4ID0gMDtcbiAgICAgIGNvbnN0IGFyZ3MgPSBhcGkucGFyYW1ldGVycy5tYXAoKHBhcmFtKSA9PiB7XG4gICAgICAgIGlmIChBcGlQYXJhbVR5cGUuaXNDb250ZXh0KHBhcmFtLnR5cGUpKSB7XG4gICAgICAgICAgcmV0dXJuIGNvbnRleHQ7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHBhcmFtc1twYXJhbXNJbmRleCsrXTtcbiAgICAgIH0pO1xuXG4gICAgICAvLyDrqqjrjbgg66mU7ISc65OcIO2YuOy2nCAo6riw7KG0IOuplOyEnOuTnCDsnqzsgqzsmqkpXG4gICAgICByZXR1cm4gdGhpcy5pbnZva2VNb2RlbE1ldGhvZChhcGksIGFyZ3MsIHJlcGx5KTtcbiAgICB9KTtcbiAgfVxuXG4gIGFzeW5jIGludm9rZU1vZGVsTWV0aG9kKFxuICAgIGFwaTogRXh0ZW5kZWRBcGksXG4gICAgYXJnczogdW5rbm93bltdLFxuICAgIHJlcGx5OiBGYXN0aWZ5UmVwbHksXG4gICk6IFByb21pc2U8dW5rbm93bj4ge1xuICAgIGNvbnN0IG1vZGVsID0gdGhpcy5zeW5jZXIubW9kZWxzW2FwaS5tb2RlbE5hbWVdO1xuICAgIC8vIG94bGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55IC0tIG1vZGVs7J2AIOuqqOuNuCDsnbjsiqTthLTsiqTsnbTrr4DroZwg66mU7ISc65OcIO2YuOy2nCDqsIDriqVcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCAobW9kZWwgYXMgYW55KVthcGkubWV0aG9kTmFtZV0uYXBwbHkobW9kZWwsIGFyZ3MpO1xuICAgIHJlcGx5LnR5cGUoYXBpLm9wdGlvbnMuY29udGVudFR5cGUgPz8gXCJhcHBsaWNhdGlvbi9qc29uXCIpO1xuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGFzeW5jIGNyZWF0ZUNvbnRleHQoXG4gICAgY29uZmlnOiBTb25hbXVGYXN0aWZ5Q29uZmlnLFxuICAgIHJlcXVlc3Q6IEZhc3RpZnlSZXF1ZXN0LFxuICAgIHJlcGx5OiBGYXN0aWZ5UmVwbHksXG4gICk6IFByb21pc2U8Q29udGV4dD4ge1xuICAgIC8vIGNyZWF0ZVNTRUZhY3Rvcnkg7ZWo7IiY7JeQIOuvuOumrCByZXF1ZXN07J2YIHNvY2tldOqzvCByZXBseeulvCDrsJTsnbjrlKkuXG4gICAgY29uc3QgeyBjcmVhdGVTU0VGYWN0b3J5IH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9zdHJlYW0vc3NlXCIpO1xuICAgIGNvbnN0IGNyZWF0ZVNTRSA9ICg8VCBleHRlbmRzIFpvZE9iamVjdD4oXG4gICAgICBfcmVxdWVzdDogRmFzdGlmeVJlcXVlc3QsXG4gICAgICBfcmVwbHk6IEZhc3RpZnlSZXBseSxcbiAgICAgIF9ldmVudHM6IFQsXG4gICAgKSA9PiBjcmVhdGVTU0VGYWN0b3J5KF9yZXF1ZXN0LnNvY2tldCwgX3JlcGx5LCBfZXZlbnRzKSkuYmluZChudWxsLCByZXF1ZXN0LCByZXBseSk7XG5cbiAgICAvLyBsb2NhbGUg6rCQ7KeAXG4gICAgY29uc3QgbG9jYWxlID1cbiAgICAgIHRoaXMuZGV0ZWN0TG9jYWxlKHJlcXVlc3QuaGVhZGVyc1tcImFjY2VwdC1sYW5ndWFnZVwiXSwgdGhpcy5jb25maWcuaTE4bi5zdXBwb3J0ZWRMb2NhbGVzKSA/P1xuICAgICAgdGhpcy5jb25maWcuaTE4bi5kZWZhdWx0TG9jYWxlO1xuXG4gICAgLy8gYXV0aCBjb250ZXh0IOy2lOqwgFxuICAgIGNvbnN0IGhlYWRlcnMgPSBjb252ZXJ0RmFzdGlmeUhlYWRlcnNUb1N0YW5kYXJkKHJlcXVlc3QuaGVhZGVycyk7XG4gICAgY29uc3Qgc2Vzc2lvbiA9IChhd2FpdCB0aGlzLl9hdXRoPy5hcGkuZ2V0U2Vzc2lvbih7IGhlYWRlcnMgfSkpID8/IG51bGw7XG5cbiAgICBjb25zdCBjb250ZXh0OiBDb250ZXh0ID0ge1xuICAgICAgLi4uKGF3YWl0IFByb21pc2UucmVzb2x2ZShcbiAgICAgICAgY29uZmlnLmNvbnRleHRQcm92aWRlcihcbiAgICAgICAgICB7XG4gICAgICAgICAgICByZXF1ZXN0LFxuICAgICAgICAgICAgcmVwbHksXG4gICAgICAgICAgICBoZWFkZXJzOiByZXF1ZXN0LmhlYWRlcnMsXG4gICAgICAgICAgICBjcmVhdGVTU0UsXG4gICAgICAgICAgICBuYWl0ZVN0b3JlOiBuZXcgTWFwKCksXG4gICAgICAgICAgICBsb2NhbGUsXG4gICAgICAgICAgICAvLyBhdXRoXG4gICAgICAgICAgICB1c2VyOiBzZXNzaW9uPy51c2VyID8/IG51bGwsXG4gICAgICAgICAgICBzZXNzaW9uOiBzZXNzaW9uPy5zZXNzaW9uID8/IG51bGwsXG4gICAgICAgICAgfSxcbiAgICAgICAgICByZXF1ZXN0LFxuICAgICAgICAgIHJlcGx5LFxuICAgICAgICApLFxuICAgICAgKSksXG4gICAgfTtcbiAgICByZXR1cm4gY29udGV4dDtcbiAgfVxuXG4gIC8qKlxuICAgKiBBY2NlcHQtTGFuZ3VhZ2Ug7Zek642U7JeQ7IScIOyngOybkO2VmOuKlCBsb2NhbGXsnYQg7LC+7Iq164uI64ukLlxuICAgKiBAZXhhbXBsZSBcImtvLUtSLGtvO3E9MC45LGVuO3E9MC44XCIg4oaSIFwia29cIlxuICAgKi9cbiAgcHJpdmF0ZSBkZXRlY3RMb2NhbGUoXG4gICAgYWNjZXB0TGFuZ3VhZ2U6IHN0cmluZyB8IHVuZGVmaW5lZCxcbiAgICBzdXBwb3J0ZWQ6IHN0cmluZ1tdLFxuICApOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICAgIGlmICghYWNjZXB0TGFuZ3VhZ2UpIHJldHVybiB1bmRlZmluZWQ7XG5cbiAgICAvLyBBY2NlcHQtTGFuZ3VhZ2U6IGtvLUtSLGtvO3E9MC45LGVuO3E9MC44XG4gICAgY29uc3QgbGFuZ3MgPSBhY2NlcHRMYW5ndWFnZS5zcGxpdChcIixcIikubWFwKChsYW5nKSA9PiB7XG4gICAgICBjb25zdCBbY29kZV0gPSBsYW5nLnNwbGl0KFwiO1wiKTtcbiAgICAgIHJldHVybiBjb2RlLnRyaW0oKS5zcGxpdChcIi1cIilbMF07IC8vIGtvLUtSIOKGkiBrb1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIGxhbmdzLmZpbmQoKGxhbmcpID0+IHN1cHBvcnRlZC5pbmNsdWRlcyhsYW5nKSk7XG4gIH1cblxuICBhc3luYyBzdGFydFdhdGNoZXIoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3Qgd2F0Y2hQYXRoID0gW3BhdGguam9pbih0aGlzLmFwaVJvb3RQYXRoLCBcInNyY1wiKV07XG5cbiAgICBjb25zdCBjaG9raWRhciA9IChhd2FpdCBpbXBvcnQoXCJjaG9raWRhclwiKSkuZGVmYXVsdDtcbiAgICB0aGlzLndhdGNoZXIgPSBjaG9raWRhci53YXRjaCh3YXRjaFBhdGgsIHtcbiAgICAgIGlnbm9yZWQ6IChwYXRoLCBzdGF0cykgPT5cbiAgICAgICAgISFzdGF0cz8uaXNGaWxlKCkgJiYgIXBhdGguZW5kc1dpdGgoXCIudHNcIikgJiYgIXBhdGguZW5kc1dpdGgoXCIuanNvblwiKSxcbiAgICAgIHBlcnNpc3RlbnQ6IHRydWUsXG4gICAgICBpZ25vcmVJbml0aWFsOiB0cnVlLFxuICAgIH0pO1xuXG4gICAgdGhpcy53YXRjaGVyLm9uKFwiYWxsXCIsIGFzeW5jIChldmVudDogc3RyaW5nLCBmaWxlUGF0aDogc3RyaW5nKSA9PiB7XG4gICAgICBjb25zdCBhYnNvbHV0ZVBhdGggPSBmaWxlUGF0aCBhcyBBYnNvbHV0ZVBhdGg7XG4gICAgICBhc3NlcnQoXG4gICAgICAgIGFic29sdXRlUGF0aC5zdGFydHNXaXRoKHRoaXMuYXBpUm9vdFBhdGgpLFxuICAgICAgICBcIkZpbGUgcGF0aCBpcyBub3Qgd2l0aGluIHRoZSBBUEkgcm9vdCBwYXRoXCIsXG4gICAgICApO1xuXG4gICAgICBpZiAoZXZlbnQgIT09IFwiY2hhbmdlXCIgJiYgZXZlbnQgIT09IFwiYWRkXCIpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICB0cnkge1xuICAgICAgICAvLyBzb25hbXUuY29uZmlnLnRzIOuzgOqyvSDsi5wg7J6s7Iuc7J6RXG4gICAgICAgIGNvbnN0IGlzQ29uZmlnVHMgPSBmaWxlUGF0aCA9PT0gcGF0aC5qb2luKHRoaXMuYXBpUm9vdFBhdGgsIFwic3JjXCIsIFwic29uYW11LmNvbmZpZy50c1wiKTtcblxuICAgICAgICBpZiAoaXNDb25maWdUcykge1xuICAgICAgICAgIGNvbnN0IHJlbGF0aXZlUGF0aCA9IGZpbGVQYXRoLnJlcGxhY2UodGhpcy5hcGlSb290UGF0aCwgXCJhcGlcIik7XG4gICAgICAgICAgY29uc3QgY2hhbGsgPSAoYXdhaXQgaW1wb3J0KFwiY2hhbGtcIikpLmRlZmF1bHQ7XG4gICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICBjaGFsay5ib2xkKGBEZXRlY3RlZCgke2V2ZW50fSk6ICR7Y2hhbGsuYmx1ZShyZWxhdGl2ZVBhdGgpfSAtIFJlc3RhcnRpbmcuLi5gKSxcbiAgICAgICAgICApO1xuICAgICAgICAgIHByb2Nlc3Mua2lsbChwcm9jZXNzLnBpZCwgXCJTSUdVU1IyXCIpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGF3YWl0IHRoaXMuaGFuZGxlRmlsZUNoYW5nZShldmVudCwgYWJzb2x1dGVQYXRoKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihlKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIC8qXG4gICAgIEEgZnVuY3Rpb24gdGhhdCBhdXRvbWF0aWNhbGx5IGhhbmRsZXMgaW5pdCBhbmQgZGVzdHJveSB3aGVuIHVzaW5nIFNvbmFtdSB2aWEgc2NyaXB0cy5cbiAgKi9cbiAgYXN5bmMgcnVuU2NyaXB0KGZuOiAoKSA9PiBQcm9taXNlPHZvaWQ+KSB7XG4gICAgYXdhaXQgdGhpcy5pbml0KHRydWUsIGZhbHNlLCB1bmRlZmluZWQsIGZhbHNlKTtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgZm4oKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgYXdhaXQgdGhpcy5kZXN0cm95KCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyByZWdpc3RlclBsdWdpbnMoc2VydmVyOiBGYXN0aWZ5SW5zdGFuY2UsIHBsdWdpbnM6IFNvbmFtdVNlcnZlck9wdGlvbnNbXCJwbHVnaW5zXCJdKSB7XG4gICAgaWYgKCFwbHVnaW5zKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gY29tcHJlc3Mg7ZSM65+s6re47J247J2AIOuLpOuluCDtlIzrn6zqt7jsnbjrs7Tri6Qg66i87KCAIOuTseuhneuQmOyWtOyVvCDtlanri4jri6QuXG4gICAgaWYgKHBsdWdpbnMuY29tcHJlc3MpIHtcbiAgICAgIGNvbnN0IGNvbXByZXNzUGx1Z2luID0gKGF3YWl0IGltcG9ydChcIkBmYXN0aWZ5L2NvbXByZXNzXCIpKS5kZWZhdWx0O1xuICAgICAgY29uc3QgZGVmYXVsdE9wdGlvbnMgPSB7XG4gICAgICAgIHRocmVzaG9sZDogMTAyNCxcbiAgICAgICAgZW5jb2RpbmdzOiBbXCJiclwiLCBcImd6aXBcIiwgXCJkZWZsYXRlXCJdIGFzIChcImJyXCIgfCBcImd6aXBcIiB8IFwiZGVmbGF0ZVwiKVtdLFxuICAgICAgfTtcblxuICAgICAgaWYgKHBsdWdpbnMuY29tcHJlc3MgPT09IHRydWUpIHtcbiAgICAgICAgc2VydmVyLnJlZ2lzdGVyKGNvbXByZXNzUGx1Z2luLCBkZWZhdWx0T3B0aW9ucyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBzZXJ2ZXIucmVnaXN0ZXIoY29tcHJlc3NQbHVnaW4sIHtcbiAgICAgICAgICAuLi5kZWZhdWx0T3B0aW9ucyxcbiAgICAgICAgICAuLi5wbHVnaW5zLmNvbXByZXNzLFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCBwbHVnaW5zTW9kdWxlcyA9IHtcbiAgICAgIGNvcnM6IFwiQGZhc3RpZnkvY29yc1wiLFxuICAgICAgZm9ybWJvZHk6IFwiQGZhc3RpZnkvZm9ybWJvZHlcIixcbiAgICAgIG11bHRpcGFydDogXCJAZmFzdGlmeS9tdWx0aXBhcnRcIixcbiAgICAgIHFzOiBcImZhc3RpZnktcXNcIixcbiAgICAgIHNzZTogXCJmYXN0aWZ5LXNzZS12MlwiLFxuICAgICAgc3RhdGljOiBcIkBmYXN0aWZ5L3N0YXRpY1wiLFxuICAgIH0gYXMgY29uc3Q7XG5cbiAgICBjb25zdCByZWdpc3RlclBsdWdpbiA9IGFzeW5jIDxLIGV4dGVuZHMga2V5b2YgTm9uTnVsbGFibGU8dHlwZW9mIHBsdWdpbnM+PihcbiAgICAgIGtleTogSyxcbiAgICAgIHBsdWdpbk5hbWU6IHN0cmluZyxcbiAgICApID0+IHtcbiAgICAgIGNvbnN0IG9wdGlvbiA9IHBsdWdpbnNba2V5XTtcbiAgICAgIGlmICghb3B0aW9uKSByZXR1cm47XG5cbiAgICAgIGlmIChvcHRpb24gPT09IHRydWUpIHtcbiAgICAgICAgc2VydmVyLnJlZ2lzdGVyKChhd2FpdCBpbXBvcnQocGx1Z2luTmFtZSkpLmRlZmF1bHQpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgc2VydmVyLnJlZ2lzdGVyKChhd2FpdCBpbXBvcnQocGx1Z2luTmFtZSkpLmRlZmF1bHQsIG9wdGlvbik7XG4gICAgICB9XG4gICAgfTtcblxuICAgIGZvciAoY29uc3QgW2tleSwgcGx1Z2luTmFtZV0gb2YgT2JqZWN0LmVudHJpZXMocGx1Z2luc01vZHVsZXMpKSB7XG4gICAgICBhd2FpdCByZWdpc3RlclBsdWdpbihrZXkgYXMga2V5b2YgdHlwZW9mIHBsdWdpbnMsIHBsdWdpbk5hbWUpO1xuICAgIH1cblxuICAgIGlmIChwbHVnaW5zLmN1c3RvbSkge1xuICAgICAgcGx1Z2lucy5jdXN0b20oc2VydmVyKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogYmV0dGVyLWF1dGgg65287Jqw7Yq466W8IOuTseuhne2VqeuLiOuLpC5cbiAgICogL2FwaS9hdXRoLyog6rK966Gc66GcIOyduOymnSBBUEnqsIAg7J6Q64+ZIOuTseuhneuQqeuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcmVnaXN0ZXJCZXR0ZXJBdXRoKFxuICAgIHNlcnZlcjogRmFzdGlmeUluc3RhbmNlLFxuICAgIG9wdGlvbnM6IE5vbk51bGxhYmxlPFNvbmFtdVNlcnZlck9wdGlvbnNbXCJhdXRoXCJdPixcbiAgKSB7XG4gICAgaWYgKCFvcHRpb25zKSByZXR1cm47XG5cbiAgICBjb25zdCBiYXNlUGF0aCA9IG9wdGlvbnMuYmFzZVBhdGggPz8gXCIvYXBpL2F1dGhcIjtcblxuICAgIC8vIGJldHRlci1hdXRoIOudvOyasO2KuCDrk7HroZ1cbiAgICBzZXJ2ZXIucm91dGUoe1xuICAgICAgbWV0aG9kOiBbXCJHRVRcIiwgXCJQT1NUXCJdLFxuICAgICAgdXJsOiBgJHtiYXNlUGF0aH0vKmAsXG4gICAgICBoYW5kbGVyOiBhc3luYyAocmVxdWVzdCwgcmVwbHkpID0+IHtcbiAgICAgICAgY29uc3QgdXJsID0gbmV3IFVSTChyZXF1ZXN0LnVybCwgYGh0dHA6Ly8ke3JlcXVlc3QuaGVhZGVycy5ob3N0fWApO1xuICAgICAgICBjb25zdCBoZWFkZXJzID0gY29udmVydEZhc3RpZnlIZWFkZXJzVG9TdGFuZGFyZChyZXF1ZXN0LmhlYWRlcnMpO1xuICAgICAgICBjb25zdCByZXEgPSBuZXcgUmVxdWVzdCh1cmwudG9TdHJpbmcoKSwge1xuICAgICAgICAgIG1ldGhvZDogcmVxdWVzdC5tZXRob2QsXG4gICAgICAgICAgaGVhZGVycyxcbiAgICAgICAgICAuLi4ocmVxdWVzdC5ib2R5ID8geyBib2R5OiBKU09OLnN0cmluZ2lmeShyZXF1ZXN0LmJvZHkpIH0gOiB7fSksXG4gICAgICAgIH0pO1xuXG4gICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgdGhpcy5hdXRoLmhhbmRsZXIocmVxKTtcblxuICAgICAgICByZXBseS5zdGF0dXMocmVzcG9uc2Uuc3RhdHVzKTtcbiAgICAgICAgcmVzcG9uc2UuaGVhZGVycy5mb3JFYWNoKCh2YWx1ZTogc3RyaW5nLCBrZXk6IHN0cmluZykgPT4ge1xuICAgICAgICAgIHJlcGx5LmhlYWRlcihrZXksIHZhbHVlKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiByZXBseS5zZW5kKHJlc3BvbnNlLmJvZHkgPyBhd2FpdCByZXNwb25zZS50ZXh0KCkgOiBudWxsKTtcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBjb25zdCBjaGFsayA9IChhd2FpdCBpbXBvcnQoXCJjaGFsa1wiKSkuZGVmYXVsdDtcbiAgICBjb25zb2xlLmxvZyhjaGFsay5ncmVlbihg4pyTIGJldHRlci1hdXRoIHJlZ2lzdGVyZWQgYXQgJHtiYXNlUGF0aH0vKmApKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgaW5pdGlhbGl6ZUNhY2hlKGNvbmZpZzogQ2FjaGVDb25maWcgfCB1bmRlZmluZWQsIGZvclRlc3Rpbmc6IGJvb2xlYW4pIHtcbiAgICBjb25zdCB7IHNldENhY2hlTWFuYWdlclJlZiB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vY2FjaGUvZGVjb3JhdG9yXCIpO1xuXG4gICAgLy8g7YWM7Iqk7Yq4IO2ZmOqyveyXkOyEnCDrqZTrqqjrpqwg65Oc65287J2067KEIOyekOuPmSDsgqzsmqlcbiAgICBpZiAoZm9yVGVzdGluZykge1xuICAgICAgY29uc3QgeyBjcmVhdGVUZXN0Q2FjaGVNYW5hZ2VyIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9jYWNoZS9jYWNoZS1tYW5hZ2VyXCIpO1xuICAgICAgdGhpcy5fY2FjaGUgPSBjcmVhdGVUZXN0Q2FjaGVNYW5hZ2VyKCk7XG4gICAgICBzZXRDYWNoZU1hbmFnZXJSZWYodGhpcy5fY2FjaGUpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIOyEpOygleydtCDsl4bsnLzrqbQg7LqQ7IucIOu5hO2ZnOyEse2ZlFxuICAgIGlmICghY29uZmlnKSB7XG4gICAgICBzZXRDYWNoZU1hbmFnZXJSZWYobnVsbCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8g7ISk7KCV7JeQIOuUsOudvCBDYWNoZU1hbmFnZXIg7IOd7ISxXG4gICAgY29uc3QgeyBjcmVhdGVDYWNoZU1hbmFnZXIgfSA9IGF3YWl0IGltcG9ydChcIi4uL2NhY2hlL2NhY2hlLW1hbmFnZXJcIik7XG4gICAgdGhpcy5fY2FjaGUgPSBjcmVhdGVDYWNoZU1hbmFnZXIoY29uZmlnKTtcbiAgICBzZXRDYWNoZU1hbmFnZXJSZWYodGhpcy5fY2FjaGUpO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBpbml0aWFsaXplV29ya2Zsb3dzKG9wdGlvbnM6IFNvbmFtdVRhc2tPcHRpb25zIHwgdW5kZWZpbmVkKSB7XG4gICAgY29uc3QgeyBXb3JrZmxvd01hbmFnZXIgfSA9IGF3YWl0IGltcG9ydChcIi4uL3Rhc2tzL3dvcmtmbG93LW1hbmFnZXJcIik7XG4gICAgLy8gTk9URTogQHNvbmFtdS1raXQvdGFza3Mg7JWI7JeQ7ISgIGtuZXggY29uZmln66W8IOyImOygle2VmOq4sCDrlYzrrLjsl5AgY29ubmVjdGlvbuydtCDslYTri4wgY29uZmlnIOynuOuhnCDrs7Trg4Xri4jri6QuXG4gICAgdGhpcy5fd29ya2Zsb3dzID0gbmV3IFdvcmtmbG93TWFuYWdlcihEQi5nZXREQkNvbmZpZyhcIndcIikpO1xuICAgIGlmICghb3B0aW9ucykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IGVuYWJsZVdvcmtlciA9IG9wdGlvbnMuZW5hYmxlV29ya2VyID8/IGlzRGFlbW9uU2VydmVyKCk7XG4gICAgY29uc3QgZGVmYXVsdFdvcmtlck9wdGlvbnMgPSB7XG4gICAgICBjb25jdXJyZW5jeTogb3MuY3B1cygpLmxlbmd0aCAtIDEsXG4gICAgICB1c2VQdWJTdWI6IHRydWUsXG4gICAgICBsaXN0ZW5EZWxheTogNTAwLFxuICAgIH07XG5cbiAgICBpZiAoZW5hYmxlV29ya2VyKSB7XG4gICAgICB0aGlzLndvcmtmbG93cy5zZXR1cFdvcmtlcih7XG4gICAgICAgIC4uLmRlZmF1bHRXb3JrZXJPcHRpb25zLFxuICAgICAgICAuLi5vcHRpb25zLndvcmtlck9wdGlvbnMsXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGJvb3Qoc2VydmVyOiBGYXN0aWZ5SW5zdGFuY2UsIG9wdGlvbnM6IFNvbmFtdVNlcnZlck9wdGlvbnMpIHtcbiAgICBjb25zdCBwb3J0ID0gb3B0aW9ucy5saXN0ZW4/LnBvcnQgPz8gMzAwMDtcbiAgICBjb25zdCBob3N0ID0gb3B0aW9ucy5saXN0ZW4/Lmhvc3QgPz8gXCJsb2NhbGhvc3RcIjtcblxuICAgIHNlcnZlci5hZGRIb29rKFwib25DbG9zZVwiLCBhc3luYyAoKSA9PiB7XG4gICAgICBhd2FpdCBvcHRpb25zLmxpZmVjeWNsZT8ub25TaHV0ZG93bj8uKHNlcnZlcik7XG4gICAgICBhd2FpdCB0aGlzLndvcmtmbG93cy5kZXN0cm95KCk7XG4gICAgICBhd2FpdCB0aGlzLmRlc3Ryb3koKTtcbiAgICB9KTtcblxuICAgIGNvbnN0IHNodXRkb3duID0gYXN5bmMgKCkgPT4ge1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgc2VydmVyLmNsb3NlKCk7XG4gICAgICAgIHByb2Nlc3MuZXhpdCgwKTtcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBjb25zb2xlLmVycm9yKFwiRXJyb3IgZHVyaW5nIHNodXRkb3duOlwiLCBlcnIpO1xuICAgICAgICBwcm9jZXNzLmV4aXQoMSk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIHByb2Nlc3Mub24oXCJTSUdJTlRcIiwgc2h1dGRvd24pO1xuICAgIHByb2Nlc3Mub24oXCJTSUdURVJNXCIsIHNodXRkb3duKTtcblxuICAgIGlmIChvcHRpb25zLmxpZmVjeWNsZT8ub25FcnJvcikge1xuICAgICAgc2VydmVyLnNldEVycm9ySGFuZGxlcihvcHRpb25zLmxpZmVjeWNsZT8ub25FcnJvcik7XG4gICAgfVxuXG4gICAgc2VydmVyXG4gICAgICAubGlzdGVuKHsgcG9ydCwgaG9zdCB9KVxuICAgICAgLnRoZW4oYXN5bmMgKCkgPT4ge1xuICAgICAgICBhd2FpdCB0aGlzLndvcmtmbG93cy5zdGFydFdvcmtlcigpO1xuICAgICAgICBhd2FpdCBvcHRpb25zLmxpZmVjeWNsZT8ub25TdGFydD8uKHNlcnZlcik7XG4gICAgICB9KVxuICAgICAgLmNhdGNoKGFzeW5jIChlcnIpID0+IHtcbiAgICAgICAgY29uc3QgY2hhbGsgPSAoYXdhaXQgaW1wb3J0KFwiY2hhbGtcIikpLmRlZmF1bHQ7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoY2hhbGsucmVkKFwiRmFpbGVkIHRvIHN0YXJ0IHNlcnZlcjpcIiwgZXJyKSk7XG4gICAgICAgIGF3YWl0IHNodXRkb3duKCk7XG4gICAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgaGFuZGxlRmlsZUNoYW5nZShldmVudDogc3RyaW5nLCBmaWxlUGF0aDogQWJzb2x1dGVQYXRoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8g7LKrIOuyiOynuCDtjIzsnbzsnbTrqbQgSE1SIOyLnOyekSDsi5zqsIQg6riw66GdXG4gICAgaWYgKHRoaXMucGVuZGluZ0ZpbGVzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhpcy5obXJTdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuICAgIH1cbiAgICB0aGlzLnBlbmRpbmdGaWxlcy5wdXNoKGZpbGVQYXRoKTtcblxuICAgIGNvbnN0IHJlbGF0aXZlUGF0aCA9IHBhdGgucmVsYXRpdmUodGhpcy5hcGlSb290UGF0aCwgZmlsZVBhdGgpO1xuICAgIGNvbnN0IGNoYWxrID0gKGF3YWl0IGltcG9ydChcImNoYWxrXCIpKS5kZWZhdWx0O1xuICAgIGNvbnNvbGUubG9nKGNoYWxrLmJvbGQoYERldGVjdGVkKCR7ZXZlbnR9KTogJHtjaGFsay5ibHVlKHJlbGF0aXZlUGF0aCl9YCkpO1xuXG4gICAgYXdhaXQgdGhpcy5zeW5jZXIuc3luY0Zyb21XYXRjaGVyKGV2ZW50LCBmaWxlUGF0aCk7XG5cbiAgICAvLyDsspjrpqwg7JmE66OM65CcIO2MjOydvOydhCDrjIDquLAg66qp66Gd7JeQ7IScIOygnOqxsFxuICAgIHRoaXMucGVuZGluZ0ZpbGVzID0gdGhpcy5wZW5kaW5nRmlsZXMuc2xpY2UoMSk7XG5cbiAgICAvLyDrqqjrk6Ag7YyM7J28IOyymOumrOqwgCDsmYTro4zrkJjrqbQg7LWc7KKFIOuplOyLnOyngCDstpzroKVcbiAgICBpZiAodGhpcy5wZW5kaW5nRmlsZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICBhd2FpdCB0aGlzLmZpbmlzaEhNUigpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZmluaXNoSE1SKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMuc3luY2VyLnJlbmV3Q2hlY2tzdW1zKCk7XG5cbiAgICBjb25zdCBlbmRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICBjb25zdCB0b3RhbFRpbWUgPSBlbmRUaW1lIC0gdGhpcy5obXJTdGFydFRpbWU7XG4gICAgY29uc3QgW2NoYWxrLCB7IGNlbnRlclRleHQgfV0gPSBhd2FpdCBQcm9taXNlLmFsbChbXG4gICAgICAoYXdhaXQgaW1wb3J0KFwiY2hhbGtcIikpLmRlZmF1bHQsXG4gICAgICBpbXBvcnQoXCIuLi91dGlscy9jb25zb2xlLXV0aWxcIiksXG4gICAgXSk7XG4gICAgY29uc3QgbXNnID0gYEhNUiBEb25lISAke2NoYWxrLmJvbGQud2hpdGUoYCR7dG90YWxUaW1lfW1zYCl9YDtcblxuICAgIGNvbnNvbGUubG9nKGNoYWxrLmJsYWNrLmJnR3JlZW4oY2VudGVyVGV4dChtc2cpKSk7XG4gIH1cblxuICBhc3luYyBkZXN0cm95KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHsgQmFzZU1vZGVsIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9kYXRhYmFzZS9iYXNlLW1vZGVsXCIpO1xuICAgIC8vIOuovOyggCDsspjrpqztlbTslbztlaguXG4gICAgYXdhaXQgQmFzZU1vZGVsLmRlc3Ryb3koKTtcbiAgICBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoW1xuICAgICAgdGhpcy5fd29ya2Zsb3dzPy5kZXN0cm95KCkgPz8gUHJvbWlzZS5yZXNvbHZlKCksXG4gICAgICB0aGlzLl9jYWNoZT8uZGlzY29ubmVjdCgpID8/IFByb21pc2UucmVzb2x2ZSgpLFxuICAgICAgdGhpcy5fZGV2Vml0ZXN0TWFuYWdlcj8uc2h1dGRvd24oKSA/PyBQcm9taXNlLnJlc29sdmUoKSxcbiAgICAgIHRoaXMud2F0Y2hlcj8uY2xvc2UoKSA/PyBQcm9taXNlLnJlc29sdmUoKSxcbiAgICAgIGxvZ3RhcGVEaXNwb3NlKCksXG4gICAgXSk7XG4gIH1cbn1cblxuZXhwb3J0IGNvbnN0IFNvbmFtdSA9IG5ldyBTb25hbXVDbGFzcygpO1xuXG4vKipcbiAqIHN0cmVhbSDrqqjrk5zsl5DshJwg7YKkIOyDneyEsSDtlajsiJjqsIAg7KeA7KCV65CY7KeAIOyViuyVmOydhCDrlYwg7IKs7Jqp7ZWY64qUIOq4sOuzuCDtlajsiJjsnoXri4jri6QuXG4gKi9cbmZ1bmN0aW9uIGRlZmF1bHRLZXlHZW5lcmF0b3IoZmlsZTogeyBmaWxlbmFtZTogc3RyaW5nOyBtaW1ldHlwZTogc3RyaW5nIH0pOiBzdHJpbmcge1xuICBjb25zdCBleHQgPSBtaW1lLmV4dGVuc2lvbihmaWxlLm1pbWV0eXBlKSB8fCBcImJpblwiO1xuICBjb25zdCB0aW1lc3RhbXAgPSBEYXRlLm5vdygpO1xuICBjb25zdCByYW5kb20gPSBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zbGljZSgyLCA4KTtcbiAgcmV0dXJuIGB1cGxvYWRzLyR7dGltZXN0YW1wfS0ke3JhbmRvbX0uJHtleHR9YDtcbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUErNENBLFNBQVMsb0JBQW9CLE1BQXNEO0NBQ2pGLE1BQU0sTUFBTSxLQUFLLFVBQVUsS0FBSyxTQUFTLElBQUk7Q0FDN0MsTUFBTSxZQUFZLEtBQUssS0FBSztDQUM1QixNQUFNLFNBQVMsS0FBSyxRQUFRLENBQUMsU0FBUyxHQUFHLENBQUMsTUFBTSxHQUFHLEVBQUU7QUFDckQsUUFBTyxXQUFXLFVBQVUsR0FBRyxPQUFPLEdBQUc7Ozs7NEJBcjRDd0I7cUJBQ2M7Z0JBR2xCO1VBRTNCO1VBRVM7cUJBRW1CO3FCQUNSO3FCQUdBO1dBQ0g7a0JBS0E7Z0JBQ0U7YUFFaUI7Y0FJbEM7Q0FHaEMsY0FBTixNQUFrQjtFQUNoQixBQUFPLGdCQUF5QjtFQUNoQyxBQUFPLGFBQXNCO0VBQzdCLEFBQU8sb0JBRUYsSUFBSSxtQkFBbUI7RUFFNUIsQUFBTyxhQUFzQjtHQUMzQixNQUFNLFFBQVEsS0FBSyxrQkFBa0IsVUFBVTtBQUMvQyxPQUFJLE9BQU8sU0FBUztBQUNsQixXQUFPLE1BQU07O0FBR2YsT0FBSSxRQUFRLElBQUksYUFBYSxRQUFRO0FBRW5DLFdBQU87S0FDTCxTQUFTO0tBQ1QsT0FBTztLQUNQLFNB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
|