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/syncer/syncer.js
CHANGED
|
@@ -1,497 +1,431 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { __esmMin } from "../_virtual/rolldown_runtime.js";
|
|
2
|
+
import { AlreadyProcessedException, init_so_exceptions } from "../exceptions/so-exceptions.js";
|
|
3
|
+
import { init_controller, isTest } from "../utils/controller.js";
|
|
4
|
+
import { copyFileWithReplaceCoreToShared, exists, init_fs_utils } from "../utils/fs-utils.js";
|
|
5
|
+
import { Sonamu, init_sonamu } from "../api/sonamu.js";
|
|
6
|
+
import { TemplateKey, init_types } from "../types/types.js";
|
|
7
|
+
import { init_async_utils, mapAsync, reduceAsync } from "../utils/async-utils.js";
|
|
8
|
+
import { EntityManager, init_entity_manager } from "../entity/entity-manager.js";
|
|
9
|
+
import { Naite, init_naite } from "../naite/naite.js";
|
|
10
|
+
import { init_decorators, registeredApis } from "../api/decorators.js";
|
|
11
|
+
import { centerText, init_console_util } from "../utils/console-util.js";
|
|
12
|
+
import { TemplateManager, init_template_manager } from "../template/template-manager.js";
|
|
13
|
+
import { checksumPatternGroup, getChecksumPatternGroupInAbsolutePath, init_file_patterns } from "./file-patterns.js";
|
|
14
|
+
import { findChangedFilesUsingChecksums, init_checksum, renewChecksums } from "./checksum.js";
|
|
15
|
+
import { generateTemplate, init_code_generator, renderTemplate } from "./code-generator.js";
|
|
16
|
+
import { createEntity, delEntity, init_entity_operations } from "./entity-operations.js";
|
|
17
|
+
import { init_module_loader, loadApis, loadModels, loadTypes, loadWorkflows } from "./module-loader.js";
|
|
18
|
+
import { init_process_utils, runWithGracefulShutdown } from "../utils/process-utils.js";
|
|
19
|
+
import { actionGenerateHttps, actionGenerateSchemas, actionGenerateServices, actionGenerateSsr, actionSyncConfig, actionSyncFilesToTargets, init_syncer_actions } from "./syncer-actions.js";
|
|
20
|
+
import inflection from "inflection";
|
|
21
|
+
import { group, unique } from "radashi";
|
|
2
22
|
import assert from "assert";
|
|
23
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
24
|
+
import path from "path";
|
|
3
25
|
import chalk from "chalk";
|
|
4
26
|
import { EventEmitter } from "events";
|
|
5
|
-
import {
|
|
6
|
-
import inflection from "inflection";
|
|
27
|
+
import { hot } from "@sonamu-kit/hmr-hook";
|
|
7
28
|
import { minimatch } from "minimatch";
|
|
8
|
-
import path from "path";
|
|
9
|
-
import { group, unique } from "radashi";
|
|
10
|
-
import { registeredApis } from "../api/decorators.js";
|
|
11
|
-
import { Sonamu } from "../api/sonamu.js";
|
|
12
|
-
import { EntityManager } from "../entity/entity-manager.js";
|
|
13
|
-
import { AlreadyProcessedException } from "../exceptions/so-exceptions.js";
|
|
14
|
-
import { Naite } from "../naite/naite.js";
|
|
15
|
-
import { TemplateManager } from "../template/template-manager.js";
|
|
16
|
-
import { TemplateKey } from "../types/types.js";
|
|
17
|
-
import { mapAsync, reduceAsync } from "../utils/async-utils.js";
|
|
18
|
-
import { centerText } from "../utils/console-util.js";
|
|
19
|
-
import { isTest } from "../utils/controller.js";
|
|
20
|
-
import { copyFileWithReplaceCoreToShared, exists } from "../utils/fs-utils.js";
|
|
21
|
-
import { runWithGracefulShutdown } from "../utils/process-utils.js";
|
|
22
|
-
import { findChangedFilesUsingChecksums, renewChecksums } from "./checksum.js";
|
|
23
|
-
import { generateTemplate, renderTemplate } from "./code-generator.js";
|
|
24
|
-
import { createEntity, delEntity } from "./entity-operations.js";
|
|
25
|
-
import { checksumPatternGroup, getChecksumPatternGroupInAbsolutePath } from "./file-patterns.js";
|
|
26
|
-
import { loadApis, loadModels, loadTypes, loadWorkflows } from "./module-loader.js";
|
|
27
|
-
import * as SyncerActions from "./syncer-actions.js";
|
|
28
|
-
export class Syncer {
|
|
29
|
-
apis = [];
|
|
30
|
-
types = {};
|
|
31
|
-
models = {};
|
|
32
|
-
workflows = new Map();
|
|
33
|
-
eventEmitter = new EventEmitter();
|
|
34
|
-
/**
|
|
35
|
-
* 체크섬이 변경된 부분에 대해 싱크를 진행합니다.
|
|
36
|
-
* sonamu.shared.ts는 파일이 없을 때만 1회 생성하고, 이후에는 덮어쓰지 않습니다.
|
|
37
|
-
* @returns
|
|
38
|
-
*/ async sync() {
|
|
39
|
-
const { targets } = Sonamu.config.sync;
|
|
40
|
-
// sonamu.shared.ts는 파일이 없을 때만 1회 생성합니다.
|
|
41
|
-
await this.copySharedToTargets(targets);
|
|
42
|
-
// 그 다음부터는 변경된 파일을 찾아서 동기화 작업을 실행합니다.
|
|
43
|
-
const changedFiles = await findChangedFilesUsingChecksums();
|
|
44
|
-
if (changedFiles.length === 0) {
|
|
45
|
-
console.log(chalk.black.bgGreen(centerText("All files are synced!")));
|
|
46
|
-
// 변경사항이 없어도 SSR 템플릿은 생성 (초기 설정 시, 이미 존재하면 스킵)
|
|
47
|
-
try {
|
|
48
|
-
await generateTemplate("queries", {}, {
|
|
49
|
-
overwrite: false
|
|
50
|
-
});
|
|
51
|
-
await generateTemplate("entry_server", {}, {
|
|
52
|
-
overwrite: false
|
|
53
|
-
});
|
|
54
|
-
} catch (e) {
|
|
55
|
-
// 파일이 이미 존재하면 무시
|
|
56
|
-
if (!(e instanceof AlreadyProcessedException)) {
|
|
57
|
-
console.error("Failed to generate SSR templates:", e);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
// 만약 싱크 중에 프로세스가 죽으면 꼬여버리기 때문에,
|
|
63
|
-
// 시그널에도 잠시 버틸 수 있는 환경 속에서 싱크를 실행합니다.
|
|
64
|
-
await runWithGracefulShutdown(async ()=>{
|
|
65
|
-
// 얘가 싱크 작업 수행하는 본체입니다.
|
|
66
|
-
await this.doSyncActions(changedFiles);
|
|
67
|
-
// 싱크 액션이 끝나면 항상 체크섬을 다시 갱신합니다.
|
|
68
|
-
await renewChecksums();
|
|
69
|
-
}, {
|
|
70
|
-
whenThisHappens: "SIGUSR2",
|
|
71
|
-
waitForUpTo: 20000
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Watcher가 감지한 파일 변경 사항에 대해 싱크를 진행합니다.
|
|
76
|
-
* 주어진 변경 파일들 중 체크섬 관리 대상인 것들만 가져다가 싱크를 진행합니다.
|
|
77
|
-
* 체크섬 파일 업데이트는 여기에서 하지 않습니다. 호출자가 합니다.
|
|
78
|
-
* @param diffFilePath - 변경 파일들. 프로젝트 루트부터 "src/" 또는 "dist/"로 시작하는 상대 경로입니다. 예시: "src/application/user/user.model.ts"
|
|
79
|
-
*/ async syncFromWatcher(event, diffFilePath) {
|
|
80
|
-
if (event !== "change" && event !== "add" && event !== "unlink") {
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
// SSR 설정 파일 변경 감지
|
|
84
|
-
if (diffFilePath.includes("/src/ssr/")) {
|
|
85
|
-
console.log(chalk.bold.yellow("SSR config changed - reloading..."));
|
|
86
|
-
// SSR 파일도 invalidate 후 reload
|
|
87
|
-
if (!isTest()) {
|
|
88
|
-
await hot.invalidateFile(diffFilePath, event);
|
|
89
|
-
}
|
|
90
|
-
await this.autoloadSSRRoutes();
|
|
91
|
-
this.eventEmitter.emit("onHMRCompleted");
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
// 일단 변경된 파일과 dependent 파일들을 invalidate 합니다.
|
|
95
|
-
// 한 번 이상 import된 친구들에 대해서만 실제 작업이 일어납니다.
|
|
96
|
-
// 그러니 안심하고 invalidate 해도 됩니다.
|
|
97
|
-
// 테스트 환경에서는 hot.invalidateFile시 초기 에러가 발생하기 때문에 invalidate 하지 않습니다.
|
|
98
|
-
if (!isTest()) {
|
|
99
|
-
const invalidatedPaths = await hot.invalidateFile(diffFilePath, event);
|
|
100
|
-
if (invalidatedPaths.length > 0) {
|
|
101
|
-
console.log(chalk.bold(`🔄 Invalidated:`));
|
|
102
|
-
for (const invalidatedPath of invalidatedPaths){
|
|
103
|
-
try {
|
|
104
|
-
// 만약 model.ts 파일이 변경(invalidate)되었다? 그러면 registeredApis 중에서 이 모델에 해당하는 api들은 지워줘요.
|
|
105
|
-
// registeredApis는 통으로 다 날려버릴 수 없습니다. registeredApis에 올라오는 친구들은 초기 로드시 또는 HMR시에만 등록되기 때문입니다.
|
|
106
|
-
// 따라서 model.ts 파일의 변경으로 다음번 새로운 eval이 예상되는 이 시점에서만, 이 모델에서 나온 registeredApis들을 지워줄 수 있습니다.
|
|
107
|
-
const removedApis = this.removeInvalidatedRegisteredApis(invalidatedPath);
|
|
108
|
-
if (removedApis.length > 0) {
|
|
109
|
-
console.log(chalk.blue(`- ${path.relative(Sonamu.apiRootPath, invalidatedPath)}`), chalk.gray(`(with ${removedApis.length} APIs)`));
|
|
110
|
-
} else {
|
|
111
|
-
console.log(chalk.blue(`- ${path.relative(Sonamu.apiRootPath, invalidatedPath)}`));
|
|
112
|
-
}
|
|
113
|
-
} catch (e) {
|
|
114
|
-
console.error(e);
|
|
115
|
-
console.error(chalk.red(`Failed to remove invalidated registered APIs for ${invalidatedPath}`));
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
// devRunner 활성화 시, 변경된 소스 파일을 Vitest 모듈 그래프에서도 무효화합니다.
|
|
121
|
-
// Vite의 moduleGraph.invalidateModule()이 importer 방향으로 재귀적 cascade하므로,
|
|
122
|
-
// 소스 파일 하나만 무효화하면 이를 import하는 테스트 파일도 자동으로 무효화됩니다.
|
|
123
|
-
if (!isTest() && Sonamu.config.test?.devRunner?.enabled && Sonamu.devVitestManager) {
|
|
124
|
-
Sonamu.devVitestManager.invalidateFiles([
|
|
125
|
-
diffFilePath
|
|
126
|
-
]);
|
|
127
|
-
console.log(chalk.dim(`Test invalidated: ${path.relative(Sonamu.apiRootPath, diffFilePath)}`));
|
|
128
|
-
}
|
|
129
|
-
const isInCheckPatternGroup = Object.values(getChecksumPatternGroupInAbsolutePath()).some((pattern)=>minimatch(diffFilePath, pattern));
|
|
130
|
-
// 할 일(sync)이 있으면 합니다.
|
|
131
|
-
if (isInCheckPatternGroup) {
|
|
132
|
-
await this.doSyncActions([
|
|
133
|
-
diffFilePath
|
|
134
|
-
]);
|
|
135
|
-
}
|
|
136
|
-
// 싱크 작업이 끝나면 모든 모듈을 로드합니다.
|
|
137
|
-
// hmr-hook에 의해 invalidate된 부분들이 아니라면 캐시 그대로 유지합니다.
|
|
138
|
-
await this.autoloadTypes();
|
|
139
|
-
await this.autoloadModels();
|
|
140
|
-
await this.autoloadApis();
|
|
141
|
-
await this.autoloadWorkflows();
|
|
142
|
-
this.eventEmitter.emit("onHMRCompleted");
|
|
143
|
-
}
|
|
144
|
-
removeInvalidatedRegisteredApis(invalidatedPath) {
|
|
145
|
-
if (!invalidatedPath.endsWith(".model.ts" /*소스 코드를 다루는 상황이니 .ts 경로로 봅니다.*/ )) {
|
|
146
|
-
return [];
|
|
147
|
-
}
|
|
148
|
-
const entityId = EntityManager.getEntityIdFromPath(invalidatedPath);
|
|
149
|
-
const toRemove = registeredApis.filter((api)=>api.modelName === `${entityId}Model`);
|
|
150
|
-
for (const api of toRemove){
|
|
151
|
-
const idx = registeredApis.indexOf(api);
|
|
152
|
-
if (idx !== -1) {
|
|
153
|
-
registeredApis.splice(idx, 1);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
return toRemove;
|
|
157
|
-
}
|
|
158
|
-
async copySharedToTargets(targets) {
|
|
159
|
-
// plural.ts 내용을 읽어서 shared 파일에 삽입합니다.
|
|
160
|
-
const dictUtilsPath = path.join(import.meta.dirname.replace("/dist/", "/src/"), "../dict/utils.ts");
|
|
161
|
-
const dictUtilsCode = await exists(dictUtilsPath) ? await readFile(dictUtilsPath, "utf-8") : "";
|
|
162
|
-
// 특정 변수 치환을 위해서 사용합니다.
|
|
163
|
-
const convertMap = {
|
|
164
|
-
baseUrl: Sonamu.config.server.baseUrl ?? `http://${Sonamu.config.server.listen?.host ?? "localhost"}:${Sonamu.config.server.listen?.port ?? 3000}`,
|
|
165
|
-
dictUtils: dictUtilsCode
|
|
166
|
-
};
|
|
167
|
-
for (const target of targets){
|
|
168
|
-
// 지금 가져가려는 이 파일은 Sonamu 코드베이스의 일부입니다.
|
|
169
|
-
// 그런데 dist 속 빌드된 소스 코드 파일이 필요한 것이 아니고, src에만 있는 텍스트 파일이 필요합니다.
|
|
170
|
-
// 따라서 /src/에서 찾습니다.
|
|
171
|
-
const srcPath = path.join(import.meta.dirname.replace("/dist/", "/src/"), `../shared/${target}.shared.ts.txt`);
|
|
172
|
-
if (!await exists(srcPath)) {
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
if (!await exists(path.join(Sonamu.appRootPath, target))) {
|
|
176
|
-
throw new Error(`Tried to copy sonamu.shared.ts to target '${target}' but the target directory does not exist. Please check your project directory structure.`);
|
|
177
|
-
}
|
|
178
|
-
const fullText = await readFile(srcPath, "utf-8");
|
|
179
|
-
const convertedText = Object.entries(convertMap).reduce((acc, [key, value])=>acc.replace(`$[[${key}]]`, value), fullText);
|
|
180
|
-
// 이건 프로젝트에 .ts 소스 코드 파일을 생성하는 것이므로 src의 .ts 경로로 갑니다.
|
|
181
|
-
const destPath = path.join(Sonamu.appRootPath, target, "src/services/sonamu.shared.ts");
|
|
182
|
-
// 정말 혹시나지만 target 디렉토리는 있어도 src/services 디렉토리는 없을 수 있으므로 미리 생성해줍니다.
|
|
183
|
-
if (!await exists(path.dirname(destPath))) {
|
|
184
|
-
await mkdir(path.dirname(destPath), {
|
|
185
|
-
recursive: true
|
|
186
|
-
});
|
|
187
|
-
console.warn(`Created directory '${path.dirname(destPath)}' because it did not exist.`);
|
|
188
|
-
}
|
|
189
|
-
// 파일이 이미 존재하면 건너뜁니다.
|
|
190
|
-
// sonamu.shared.ts는 프로젝트에서 자유롭게 커스터마이징할 수 있어야 하므로,
|
|
191
|
-
// 최초 1회만 생성하고 이후에는 덮어쓰지 않습니다.
|
|
192
|
-
// 템플릿 내용($[[dictUtils]] 등)이 변경되었을 때 반영이 필요하면,
|
|
193
|
-
// 해당 파일을 삭제한 뒤 `pnpm sonamu sync`로 재생성하면 됩니다.
|
|
194
|
-
if (await exists(destPath)) {
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
await writeFile(destPath, convertedText);
|
|
198
|
-
!isTest() && console.log(chalk.bold("Copied: ") + chalk.blue(path.relative(Sonamu.appRootPath, destPath)));
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
async autoloadTypes() {
|
|
202
|
-
this.types = await loadTypes();
|
|
203
|
-
}
|
|
204
|
-
async autoloadModels() {
|
|
205
|
-
this.models = await loadModels();
|
|
206
|
-
}
|
|
207
|
-
async autoloadApis() {
|
|
208
|
-
this.apis = await loadApis();
|
|
209
|
-
}
|
|
210
|
-
async autoloadWorkflows() {
|
|
211
|
-
this.workflows = await loadWorkflows();
|
|
212
|
-
await Sonamu.workflows.synchronize(this.workflows);
|
|
213
|
-
}
|
|
214
|
-
async autoloadSSRRoutes() {
|
|
215
|
-
const ssrConfigPath = path.join(Sonamu.apiRootPath, "src/ssr");
|
|
216
|
-
// 기존 routes 초기화
|
|
217
|
-
const { clearSSRRoutes } = await import("../ssr/index.js");
|
|
218
|
-
clearSSRRoutes();
|
|
219
|
-
// ssr 폴더 없으면 스킵
|
|
220
|
-
if (!await exists(ssrConfigPath)) {
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
// ssr 폴더 안의 모든 .ts 파일 로드
|
|
224
|
-
const { globAsync } = await import("../utils/async-utils.js");
|
|
225
|
-
const { importMembers } = await import("../utils/esm-utils.js");
|
|
226
|
-
const { runtimePath } = await import("../utils/path-utils.js");
|
|
227
|
-
// runtimePath를 사용하여 개발/프로덕션 환경에 맞는 확장자 처리
|
|
228
|
-
const files = await globAsync(path.join(ssrConfigPath, runtimePath("**/*.ts")));
|
|
229
|
-
for (const file of files){
|
|
230
|
-
try {
|
|
231
|
-
// importMembers를 사용하면 파일의 side effect(registerSSR 호출)가 실행됨
|
|
232
|
-
await importMembers(file);
|
|
233
|
-
} catch (e) {
|
|
234
|
-
console.error(`Failed to load SSR route: ${file}`, e);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* 실제 싱크를 수행하는 본체입니다.
|
|
240
|
-
* 변경된 파일들을 타입별로 분류하고 각 타입에 맞는 액션을 실행합니다.
|
|
241
|
-
* @param diffFilePaths - 변경된 파일들의 절대 경로 목록
|
|
242
|
-
* @returns diffTypes - 변경된 파일의 타입 목록 (entity, types, model 등)
|
|
243
|
-
*/ async doSyncActions(diffFilePaths) {
|
|
244
|
-
const diffGroups = this.calculateDiffGroups(diffFilePaths);
|
|
245
|
-
const diffTypes = Object.keys(diffGroups);
|
|
246
|
-
// Single source of truth가 변경된 경우
|
|
247
|
-
if (diffTypes.includes("entity") || diffTypes.includes("types")) {
|
|
248
|
-
await this.handleTruthSourceChanges(diffGroups, diffTypes);
|
|
249
|
-
}
|
|
250
|
-
// 타겟으로 복사만 하면 되는 파일이 변경된 경우
|
|
251
|
-
if (diffTypes.includes("types") || diffTypes.includes("functions") || diffTypes.includes("generated")) {
|
|
252
|
-
await this.handleSyncableFileChanges(diffGroups);
|
|
253
|
-
}
|
|
254
|
-
// 모델/프레임 구현체가 변경된 경우
|
|
255
|
-
if (diffTypes.includes("model") || diffTypes.includes("frame")) {
|
|
256
|
-
await this.handleImplementationChanges(diffGroups);
|
|
257
|
-
}
|
|
258
|
-
// 설정 파일이 변경된 경우
|
|
259
|
-
if (diffTypes.includes("config")) {
|
|
260
|
-
await SyncerActions.actionSyncConfig();
|
|
261
|
-
}
|
|
262
|
-
// 워크플로우 파일이 변경된 경우
|
|
263
|
-
if (diffTypes.includes("workflow")) {
|
|
264
|
-
await this.autoloadWorkflows();
|
|
265
|
-
}
|
|
266
|
-
// i18n 관련 파일이 변경된 경우
|
|
267
|
-
// - i18n/*.ts: locale 번역 파일
|
|
268
|
-
// - entity.json: entity labels
|
|
269
|
-
// - sonamu.config.ts: i18n 설정 (defaultLocale, supportedLocales)
|
|
270
|
-
if (diffTypes.includes("i18n") || diffTypes.includes("entity") || diffTypes.includes("config")) {
|
|
271
|
-
await this.syncSD();
|
|
272
|
-
}
|
|
273
|
-
return {
|
|
274
|
-
diffTypes
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
calculateDiffGroups(diffFiles) {
|
|
278
|
-
const fileTypes = Object.keys(checksumPatternGroup);
|
|
279
|
-
return group(diffFiles, (filePath)=>{
|
|
280
|
-
// 절대 경로에서 src/로 시작하는 상대 경로 부분을 추출합니다.
|
|
281
|
-
const srcIndex = filePath.indexOf("/src/");
|
|
282
|
-
if (srcIndex === -1) return "unknown";
|
|
283
|
-
const relativePath = filePath.slice(srcIndex + 1); // "src/..." 형태
|
|
284
|
-
for (const fileType of fileTypes){
|
|
285
|
-
if (minimatch(relativePath, checksumPatternGroup[fileType])) {
|
|
286
|
-
return fileType;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
return "unknown";
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
async handleTruthSourceChanges(diffGroups, diffTypes) {
|
|
293
|
-
Naite.t("handleTruthSourceChanges", {
|
|
294
|
-
diffGroups,
|
|
295
|
-
diffTypes
|
|
296
|
-
});
|
|
297
|
-
await EntityManager.reload();
|
|
298
|
-
// types 생성(entity 새로 추가된 경우)
|
|
299
|
-
// parentId가 없고, types가 없는 경우에만 생성
|
|
300
|
-
const entityPath = diffGroups.entity?.at(0);
|
|
301
|
-
if (entityPath !== undefined) {
|
|
302
|
-
const entityId = EntityManager.getEntityIdFromPath(entityPath);
|
|
303
|
-
const entity = EntityManager.get(entityId);
|
|
304
|
-
// 프로젝트에 생성되어야 하는 .ts 파일의 경로입니다.
|
|
305
|
-
const typeFilePath = path.join(Sonamu.apiRootPath, `src/application/${entity.names.fs}/${entity.names.fs}.types.ts`);
|
|
306
|
-
if (entity.parentId === undefined && !await exists(typeFilePath)) {
|
|
307
|
-
await generateTemplate("init_types", {
|
|
308
|
-
entityId
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
await SyncerActions.actionGenerateSchemas();
|
|
313
|
-
diffGroups.generated = unique([
|
|
314
|
-
...diffGroups.generated ?? [],
|
|
315
|
-
path.join(Sonamu.apiRootPath, "src/application/sonamu.generated.ts")
|
|
316
|
-
]);
|
|
317
|
-
diffTypes.push("generated");
|
|
318
|
-
}
|
|
319
|
-
async handleSyncableFileChanges(diffGroups) {
|
|
320
|
-
const tsPaths = unique([
|
|
321
|
-
...diffGroups.types ?? [],
|
|
322
|
-
...diffGroups.functions ?? [],
|
|
323
|
-
...diffGroups.generated ?? []
|
|
324
|
-
]);
|
|
325
|
-
Naite.t("handleSyncableFileChanges", {
|
|
326
|
-
diffGroups
|
|
327
|
-
});
|
|
328
|
-
// console.log(
|
|
329
|
-
// chalk.gray(
|
|
330
|
-
// `[Processing] Handling types/functions/generated changes: ${tsPaths.map((p) => path.relative(Sonamu.apiRootPath, p)).join(", ")}`
|
|
331
|
-
// )
|
|
332
|
-
// );
|
|
333
|
-
await SyncerActions.actionSyncFilesToTargets(tsPaths);
|
|
334
|
-
return [];
|
|
335
|
-
}
|
|
336
|
-
async handleImplementationChanges(diffGroups) {
|
|
337
|
-
Naite.t("handleImplementationChanges", {
|
|
338
|
-
diffGroups
|
|
339
|
-
});
|
|
340
|
-
const mergedGroup = [
|
|
341
|
-
...diffGroups.model ?? [],
|
|
342
|
-
...diffGroups.frame ?? []
|
|
343
|
-
];
|
|
344
|
-
// console.log(
|
|
345
|
-
// chalk.gray(
|
|
346
|
-
// `[Processing] Handling model/frame changes: ${mergedGroup.map((p) => path.relative(Sonamu.apiRootPath, p)).join(", ")}`
|
|
347
|
-
// )
|
|
348
|
-
// );
|
|
349
|
-
// generated_http.template.ts에서 syncer.types를 씁니다.
|
|
350
|
-
// service.template.ts에서 syncer.apis를 씁니다.
|
|
351
|
-
await this.autoloadModels();
|
|
352
|
-
await this.autoloadTypes();
|
|
353
|
-
await this.autoloadApis();
|
|
354
|
-
const params = mergedGroup.map((modelPath)=>{
|
|
355
|
-
if (modelPath.endsWith(".model.ts")) {
|
|
356
|
-
const entityId = EntityManager.getEntityIdFromPath(modelPath);
|
|
357
|
-
assert(entityId);
|
|
358
|
-
return {
|
|
359
|
-
namesRecord: EntityManager.getNamesFromId(entityId)
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
if (modelPath.endsWith(".frame.ts")) {
|
|
363
|
-
const [, frameName] = modelPath.match(/.+\/(.+)\.frame\.ts$/) ?? [];
|
|
364
|
-
assert(frameName);
|
|
365
|
-
// frameName을 PascalCase로 변환 (dashboard -> Dashboard)
|
|
366
|
-
const frameId = inflection.camelize(frameName);
|
|
367
|
-
return {
|
|
368
|
-
namesRecord: EntityManager.getNamesFromId(frameId)
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
throw new Error("not reachable");
|
|
372
|
-
});
|
|
373
|
-
await SyncerActions.actionGenerateServices(params);
|
|
374
|
-
await SyncerActions.actionGenerateHttps();
|
|
375
|
-
await SyncerActions.actionGenerateSsr();
|
|
376
|
-
}
|
|
377
|
-
/**
|
|
378
|
-
* 주어진 엔티티와 템플릿 키에 대해, 생성된 코드가 존재하는지 확인합니다.
|
|
379
|
-
* @param entityId 엔티티 ID
|
|
380
|
-
* @param templateKey 템플릿 키
|
|
381
|
-
* @param enumId 열거형 ID
|
|
382
|
-
* @returns 생성된 코드가 존재하는지 여부
|
|
383
|
-
*/ async checkExistsGenCode(entityId, templateKey, enumId) {
|
|
384
|
-
const { target, path: genPath } = TemplateManager.get(templateKey).getTargetAndPath(EntityManager.getNamesFromId(entityId), enumId);
|
|
385
|
-
const subPath = path.join(target, genPath);
|
|
386
|
-
const fullPath = path.join(Sonamu.appRootPath, subPath);
|
|
387
|
-
return {
|
|
388
|
-
subPath,
|
|
389
|
-
fullPath,
|
|
390
|
-
isExists: await exists(fullPath)
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
/**
|
|
394
|
-
* 주어진 엔티티와 열거형에 대해, 생성된 코드가 존재하는지 확인합니다.
|
|
395
|
-
* @param entityId 엔티티 ID
|
|
396
|
-
* @param enums 열거형 레이블
|
|
397
|
-
* @returns 생성된 코드가 존재하는지 여부
|
|
398
|
-
*/ async checkExists(entityId, enums) {
|
|
399
|
-
const keys = TemplateKey.options;
|
|
400
|
-
const names = EntityManager.getNamesFromId(entityId);
|
|
401
|
-
const enumsKeys = Object.keys(enums).filter((name)=>name !== names.constant);
|
|
402
|
-
return await reduceAsync(keys, async (result, key)=>{
|
|
403
|
-
const tpl = TemplateManager.get(key);
|
|
404
|
-
if (key.startsWith("view_enums")) {
|
|
405
|
-
await mapAsync(enumsKeys, async (componentId)=>{
|
|
406
|
-
const { target, path: p } = tpl.getTargetAndPath(names, componentId);
|
|
407
|
-
result[`${key}__${componentId}`] = await exists(path.join(Sonamu.appRootPath, target, p));
|
|
408
|
-
});
|
|
409
|
-
return result;
|
|
410
|
-
}
|
|
411
|
-
const { target, path: p } = tpl.getTargetAndPath(names);
|
|
412
|
-
const { targets } = Sonamu.config.sync;
|
|
413
|
-
if (target.includes(":target")) {
|
|
414
|
-
await mapAsync(targets, async (t)=>{
|
|
415
|
-
result[`${key}__${t}`] = await exists(path.join(Sonamu.appRootPath, target.replace(":target", t), p));
|
|
416
|
-
});
|
|
417
|
-
} else {
|
|
418
|
-
result[key] = await exists(path.join(Sonamu.appRootPath, target, p));
|
|
419
|
-
}
|
|
420
|
-
return result;
|
|
421
|
-
}, {});
|
|
422
|
-
}
|
|
423
|
-
/**
|
|
424
|
-
* 하위호환용 프록시 메소드입니다.
|
|
425
|
-
*/ async createEntity(form) {
|
|
426
|
-
return await createEntity(form);
|
|
427
|
-
}
|
|
428
|
-
/**
|
|
429
|
-
* 하위호환용 프록시 메소드입니다.
|
|
430
|
-
*/ async delEntity(entityId) {
|
|
431
|
-
return await delEntity(entityId);
|
|
432
|
-
}
|
|
433
|
-
/**
|
|
434
|
-
* 하위호환용 프록시 메소드입니다.
|
|
435
|
-
*/ async generateTemplate(key, templateOptions, _generateOptions) {
|
|
436
|
-
return await generateTemplate(key, templateOptions, _generateOptions);
|
|
437
|
-
}
|
|
438
|
-
/**
|
|
439
|
-
* 하위호환용 프록시 메소드입니다.
|
|
440
|
-
*/ async renderTemplate(key, templateOptions) {
|
|
441
|
-
return await renderTemplate(key, templateOptions);
|
|
442
|
-
}
|
|
443
|
-
/**
|
|
444
|
-
* 하위호환용 프록시 메소드입니다.
|
|
445
|
-
*/ async renewChecksums() {
|
|
446
|
-
return await renewChecksums();
|
|
447
|
-
}
|
|
448
|
-
/**
|
|
449
|
-
* SD(Sonamu Dictionary) 템플릿을 생성합니다.
|
|
450
|
-
*/ async syncSD() {
|
|
451
|
-
const { targets } = Sonamu.config.sync;
|
|
452
|
-
const i18nConfig = Sonamu.config.i18n;
|
|
453
|
-
const targetList = [
|
|
454
|
-
"api",
|
|
455
|
-
...targets
|
|
456
|
-
];
|
|
457
|
-
const apiI18nDir = path.join(Sonamu.appRootPath, Sonamu.config.api.dir, "src/i18n");
|
|
458
|
-
for (const target of targetList){
|
|
459
|
-
try {
|
|
460
|
-
// web/app의 경우 locale 파일들을 api에서 복사
|
|
461
|
-
if (target !== "api") {
|
|
462
|
-
await this.syncLocaleFiles(target, apiI18nDir, i18nConfig.supportedLocales);
|
|
463
|
-
}
|
|
464
|
-
await generateTemplate("sd", {
|
|
465
|
-
target
|
|
466
|
-
}, {
|
|
467
|
-
overwrite: true
|
|
468
|
-
});
|
|
469
|
-
} catch (e) {
|
|
470
|
-
console.error(`Failed to generate SD template for ${target}:`, e);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
/**
|
|
475
|
-
* api의 locale 파일을 web/app으로 복사합니다.
|
|
476
|
-
*/ async syncLocaleFiles(target, apiI18nDir, locales) {
|
|
477
|
-
const targetI18nDir = path.join(Sonamu.appRootPath, target, "src/i18n");
|
|
478
|
-
// 디렉토리가 없으면 생성
|
|
479
|
-
await mkdir(targetI18nDir, {
|
|
480
|
-
recursive: true
|
|
481
|
-
});
|
|
482
|
-
for (const locale of locales){
|
|
483
|
-
const sourceFile = path.join(apiI18nDir, `${locale}.ts`);
|
|
484
|
-
const targetFile = path.join(targetI18nDir, `${locale}.ts`);
|
|
485
|
-
const syncHeader = [
|
|
486
|
-
"/**",
|
|
487
|
-
" * @generated",
|
|
488
|
-
" * API에서 동기화된 파일입니다. 직접 수정하지 마세요.",
|
|
489
|
-
" */"
|
|
490
|
-
].join("\n");
|
|
491
|
-
await copyFileWithReplaceCoreToShared(sourceFile, targetFile, syncHeader);
|
|
492
|
-
!isTest() && console.log(chalk.bold("Copied: ") + chalk.cyan(`${target}/src/i18n/${locale}.ts`));
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
29
|
|
|
497
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zeW5jZXIvc3luY2VyLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGhvdCB9IGZyb20gXCJAc29uYW11LWtpdC9obXItaG9va1wiO1xuaW1wb3J0IGFzc2VydCBmcm9tIFwiYXNzZXJ0XCI7XG5pbXBvcnQgY2hhbGsgZnJvbSBcImNoYWxrXCI7XG5pbXBvcnQgeyBFdmVudEVtaXR0ZXIgfSBmcm9tIFwiZXZlbnRzXCI7XG5pbXBvcnQgeyBta2RpciwgcmVhZEZpbGUsIHdyaXRlRmlsZSB9IGZyb20gXCJmcy9wcm9taXNlc1wiO1xuaW1wb3J0IGluZmxlY3Rpb24gZnJvbSBcImluZmxlY3Rpb25cIjtcbmltcG9ydCB7IG1pbmltYXRjaCB9IGZyb20gXCJtaW5pbWF0Y2hcIjtcbmltcG9ydCBwYXRoIGZyb20gXCJwYXRoXCI7XG5pbXBvcnQgeyBncm91cCwgdW5pcXVlIH0gZnJvbSBcInJhZGFzaGlcIjtcbmltcG9ydCB0eXBlIHsgeiB9IGZyb20gXCJ6b2RcIjtcbmltcG9ydCB0eXBlIHsgV29ya2Zsb3dNZXRhZGF0YSB9IGZyb20gXCIuLlwiO1xuaW1wb3J0IHsgcmVnaXN0ZXJlZEFwaXMgfSBmcm9tIFwiLi4vYXBpL2RlY29yYXRvcnNcIjtcbmltcG9ydCB7IFNvbmFtdSB9IGZyb20gXCIuLi9hcGkvc29uYW11XCI7XG5pbXBvcnQgeyBFbnRpdHlNYW5hZ2VyLCB0eXBlIEVudGl0eU5hbWVzUmVjb3JkIH0gZnJvbSBcIi4uL2VudGl0eS9lbnRpdHktbWFuYWdlclwiO1xuaW1wb3J0IHsgQWxyZWFkeVByb2Nlc3NlZEV4Y2VwdGlvbiB9IGZyb20gXCIuLi9leGNlcHRpb25zL3NvLWV4Y2VwdGlvbnNcIjtcbmltcG9ydCB7IE5haXRlIH0gZnJvbSBcIi4uL25haXRlL25haXRlXCI7XG5pbXBvcnQgeyBUZW1wbGF0ZU1hbmFnZXIgfSBmcm9tIFwiLi4vdGVtcGxhdGUvdGVtcGxhdGUtbWFuYWdlclwiO1xuaW1wb3J0IHR5cGUgeyBHZW5lcmF0ZU9wdGlvbnMsIFBhdGhBbmRDb2RlIH0gZnJvbSBcIi4uL3R5cGVzL3R5cGVzXCI7XG5pbXBvcnQgeyBUZW1wbGF0ZUtleSwgdHlwZSBUZW1wbGF0ZU9wdGlvbnMgfSBmcm9tIFwiLi4vdHlwZXMvdHlwZXNcIjtcbmltcG9ydCB7IG1hcEFzeW5jLCByZWR1Y2VBc3luYyB9IGZyb20gXCIuLi91dGlscy9hc3luYy11dGlsc1wiO1xuaW1wb3J0IHsgY2VudGVyVGV4dCB9IGZyb20gXCIuLi91dGlscy9jb25zb2xlLXV0aWxcIjtcbmltcG9ydCB7IGlzVGVzdCB9IGZyb20gXCIuLi91dGlscy9jb250cm9sbGVyXCI7XG5pbXBvcnQgeyBjb3B5RmlsZVdpdGhSZXBsYWNlQ29yZVRvU2hhcmVkLCBleGlzdHMgfSBmcm9tIFwiLi4vdXRpbHMvZnMtdXRpbHNcIjtcbmltcG9ydCB0eXBlIHsgQWJzb2x1dGVQYXRoIH0gZnJvbSBcIi4uL3V0aWxzL3BhdGgtdXRpbHNcIjtcbmltcG9ydCB7IHJ1bldpdGhHcmFjZWZ1bFNodXRkb3duIH0gZnJvbSBcIi4uL3V0aWxzL3Byb2Nlc3MtdXRpbHNcIjtcbmltcG9ydCB7IGZpbmRDaGFuZ2VkRmlsZXNVc2luZ0NoZWNrc3VtcywgcmVuZXdDaGVja3N1bXMgfSBmcm9tIFwiLi9jaGVja3N1bVwiO1xuaW1wb3J0IHsgZ2VuZXJhdGVUZW1wbGF0ZSwgcmVuZGVyVGVtcGxhdGUgfSBmcm9tIFwiLi9jb2RlLWdlbmVyYXRvclwiO1xuaW1wb3J0IHsgY3JlYXRlRW50aXR5LCBkZWxFbnRpdHkgfSBmcm9tIFwiLi9lbnRpdHktb3BlcmF0aW9uc1wiO1xuaW1wb3J0IHtcbiAgY2hlY2tzdW1QYXR0ZXJuR3JvdXAsXG4gIHR5cGUgRmlsZVR5cGUsXG4gIGdldENoZWNrc3VtUGF0dGVybkdyb3VwSW5BYnNvbHV0ZVBhdGgsXG59IGZyb20gXCIuL2ZpbGUtcGF0dGVybnNcIjtcbmltcG9ydCB7XG4gIHR5cGUgTG9hZGVkQXBpcyxcbiAgdHlwZSBMb2FkZWRNb2RlbHMsXG4gIHR5cGUgTG9hZGVkVHlwZXMsXG4gIGxvYWRBcGlzLFxuICBsb2FkTW9kZWxzLFxuICBsb2FkVHlwZXMsXG4gIGxvYWRXb3JrZmxvd3MsXG59IGZyb20gXCIuL21vZHVsZS1sb2FkZXJcIjtcbmltcG9ydCAqIGFzIFN5bmNlckFjdGlvbnMgZnJvbSBcIi4vc3luY2VyLWFjdGlvbnNcIjtcblxudHlwZSBEaWZmR3JvdXBzID0ge1xuICBba2V5IGluIEZpbGVUeXBlXTogQWJzb2x1dGVQYXRoW107XG59O1xuXG5leHBvcnQgY2xhc3MgU3luY2VyIHtcbiAgYXBpczogTG9hZGVkQXBpcyA9IFtdO1xuICB0eXBlczogTG9hZGVkVHlwZXMgPSB7fTtcbiAgbW9kZWxzOiBMb2FkZWRNb2RlbHMgPSB7fTtcbiAgd29ya2Zsb3dzOiBNYXA8c3RyaW5nLCBXb3JrZmxvd01ldGFkYXRhW10+ID0gbmV3IE1hcCgpO1xuICBldmVudEVtaXR0ZXI6IEV2ZW50RW1pdHRlciA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcblxuICAvKipcbiAgICog7LK07YGs7ISs7J20IOuzgOqyveuQnCDrtoDrtoTsl5Ag64yA7ZW0IOyLse2BrOulvCDsp4Ttlontlanri4jri6QuXG4gICAqIHNvbmFtdS5zaGFyZWQudHPripQg7YyM7J287J20IOyXhuydhCDrlYzrp4wgMe2ajCDsg53shLHtlZjqs6AsIOydtO2bhOyXkOuKlCDrja7slrTsk7Dsp4Ag7JWK7Iq164uI64ukLlxuICAgKiBAcmV0dXJuc1xuICAgKi9cbiAgYXN5bmMgc3luYygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB7IHRhcmdldHMgfSA9IFNvbmFtdS5jb25maWcuc3luYztcblxuICAgIC8vIHNvbmFtdS5zaGFyZWQudHPripQg7YyM7J287J20IOyXhuydhCDrlYzrp4wgMe2ajCDsg53shLHtlanri4jri6QuXG4gICAgYXdhaXQgdGhpcy5jb3B5U2hhcmVkVG9UYXJnZXRzKHRhcmdldHMpO1xuXG4gICAgLy8g6re4IOuLpOydjOu2gO2EsOuKlCDrs4Dqsr3rkJwg7YyM7J287J2EIOywvuyVhOyEnCDrj5nquLDtmZQg7J6R7JeF7J2EIOyLpO2Wie2VqeuLiOuLpC5cbiAgICBjb25zdCBjaGFuZ2VkRmlsZXMgPSBhd2FpdCBmaW5kQ2hhbmdlZEZpbGVzVXNpbmdDaGVja3N1bXMoKTtcbiAgICBpZiAoY2hhbmdlZEZpbGVzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgY29uc29sZS5sb2coY2hhbGsuYmxhY2suYmdHcmVlbihjZW50ZXJUZXh0KFwiQWxsIGZpbGVzIGFyZSBzeW5jZWQhXCIpKSk7XG5cbiAgICAgIC8vIOuzgOqyveyCrO2VreydtCDsl4bslrTrj4QgU1NSIO2FnO2UjOumv+ydgCDsg53shLEgKOy0iOq4sCDshKTsoJUg7IucLCDsnbTrr7gg7KG07J6s7ZWY66m0IOyKpO2CtSlcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IGdlbmVyYXRlVGVtcGxhdGUoXCJxdWVyaWVzXCIsIHt9LCB7IG92ZXJ3cml0ZTogZmFsc2UgfSk7XG4gICAgICAgIGF3YWl0IGdlbmVyYXRlVGVtcGxhdGUoXCJlbnRyeV9zZXJ2ZXJcIiwge30sIHsgb3ZlcndyaXRlOiBmYWxzZSB9KTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgLy8g7YyM7J287J20IOydtOuvuCDsobTsnqztlZjrqbQg66y07IucXG4gICAgICAgIGlmICghKGUgaW5zdGFuY2VvZiBBbHJlYWR5UHJvY2Vzc2VkRXhjZXB0aW9uKSkge1xuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJGYWlsZWQgdG8gZ2VuZXJhdGUgU1NSIHRlbXBsYXRlczpcIiwgZSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIOunjOyVvSDsi7Htgawg7KSR7JeQIO2UhOuhnOyEuOyKpOqwgCDso73snLzrqbQg6rys7Jes67KE66as6riwIOuVjOusuOyXkCxcbiAgICAvLyDsi5zqt7jrhJDsl5Drj4Qg7J6g7IucIOuyhO2LuCDsiJgg7J6I64qUIO2ZmOqyvSDsho3sl5DshJwg7Iux7YGs66W8IOyLpO2Wie2VqeuLiOuLpC5cbiAgICBhd2FpdCBydW5XaXRoR3JhY2VmdWxTaHV0ZG93bihcbiAgICAgIGFzeW5jICgpID0+IHtcbiAgICAgICAgLy8g7JaY6rCAIOyLse2BrCDsnpHsl4Ug7IiY7ZaJ7ZWY64qUIOuzuOyytOyeheuLiOuLpC5cbiAgICAgICAgYXdhaXQgdGhpcy5kb1N5bmNBY3Rpb25zKGNoYW5nZWRGaWxlcyk7XG5cbiAgICAgICAgLy8g7Iux7YGsIOyVoeyFmOydtCDrgZ3rgpjrqbQg7ZWt7IOBIOyytO2BrOyErOydhCDri6Tsi5wg6rCx7Iug7ZWp64uI64ukLlxuICAgICAgICBhd2FpdCByZW5ld0NoZWNrc3VtcygpO1xuICAgICAgfSxcbiAgICAgIHsgd2hlblRoaXNIYXBwZW5zOiBcIlNJR1VTUjJcIiwgd2FpdEZvclVwVG86IDIwMDAwIH0sXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBXYXRjaGVy6rCAIOqwkOyngO2VnCDtjIzsnbwg67OA6rK9IOyCrO2VreyXkCDrjIDtlbQg7Iux7YGs66W8IOynhO2Wie2VqeuLiOuLpC5cbiAgICog7KO87Ja07KeEIOuzgOqyvSDtjIzsnbzrk6Qg7KSRIOyytO2BrOyErCDqtIDrpqwg64yA7IOB7J24IOqyg+uTpOunjCDqsIDsoLjri6TqsIAg7Iux7YGs66W8IOynhO2Wie2VqeuLiOuLpC5cbiAgICog7LK07YGs7ISsIO2MjOydvCDsl4XrjbDsnbTtirjripQg7Jes6riw7JeQ7IScIO2VmOyngCDslYrsirXri4jri6QuIO2YuOy2nOyekOqwgCDtlanri4jri6QuXG4gICAqIEBwYXJhbSBkaWZmRmlsZVBhdGggLSDrs4Dqsr0g7YyM7J2865OkLiDtlITroZzsoJ3tirgg66Oo7Yq467aA7YSwIFwic3JjL1wiIOuYkOuKlCBcImRpc3QvXCLroZwg7Iuc7J6R7ZWY64qUIOyDgeuMgCDqsr3roZzsnoXri4jri6QuIOyYiOyLnDogXCJzcmMvYXBwbGljYXRpb24vdXNlci91c2VyLm1vZGVsLnRzXCJcbiAgICovXG4gIGFzeW5jIHN5bmNGcm9tV2F0Y2hlcihldmVudDogc3RyaW5nLCBkaWZmRmlsZVBhdGg6IEFic29sdXRlUGF0aCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmIChldmVudCAhPT0gXCJjaGFuZ2VcIiAmJiBldmVudCAhPT0gXCJhZGRcIiAmJiBldmVudCAhPT0gXCJ1bmxpbmtcIikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIFNTUiDshKTsoJUg7YyM7J28IOuzgOqyvSDqsJDsp4BcbiAgICBpZiAoZGlmZkZpbGVQYXRoLmluY2x1ZGVzKFwiL3NyYy9zc3IvXCIpKSB7XG4gICAgICBjb25zb2xlLmxvZyhjaGFsay5ib2xkLnllbGxvdyhcIlNTUiBjb25maWcgY2hhbmdlZCAtIHJlbG9hZGluZy4uLlwiKSk7XG4gICAgICAvLyBTU1Ig7YyM7J2864+EIGludmFsaWRhdGUg7ZuEIHJlbG9hZFxuICAgICAgaWYgKCFpc1Rlc3QoKSkge1xuICAgICAgICBhd2FpdCBob3QuaW52YWxpZGF0ZUZpbGUoZGlmZkZpbGVQYXRoLCBldmVudCk7XG4gICAgICB9XG4gICAgICBhd2FpdCB0aGlzLmF1dG9sb2FkU1NSUm91dGVzKCk7XG4gICAgICB0aGlzLmV2ZW50RW1pdHRlci5lbWl0KFwib25ITVJDb21wbGV0ZWRcIik7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8g7J2864uoIOuzgOqyveuQnCDtjIzsnbzqs7wgZGVwZW5kZW50IO2MjOydvOuTpOydhCBpbnZhbGlkYXRlIO2VqeuLiOuLpC5cbiAgICAvLyDtlZwg67KIIOydtOyDgSBpbXBvcnTrkJwg7Lmc6rWs65Ok7JeQIOuMgO2VtOyEnOunjCDsi6TsoJwg7J6R7JeF7J20IOydvOyWtOuCqeuLiOuLpC5cbiAgICAvLyDqt7jrn6zri4gg7JWI7Ius7ZWY6rOgIGludmFsaWRhdGUg7ZW064+EIOuQqeuLiOuLpC5cbiAgICAvLyDthYzsiqTtirgg7ZmY6rK97JeQ7ISc64qUIGhvdC5pbnZhbGlkYXRlRmlsZeyLnCDstIjquLAg7JeQ65+s6rCAIOuwnOyDne2VmOq4sCDrlYzrrLjsl5AgaW52YWxpZGF0ZSDtlZjsp4Ag7JWK7Iq164uI64ukLlxuICAgIGlmICghaXNUZXN0KCkpIHtcbiAgICAgIGNvbnN0IGludmFsaWRhdGVkUGF0aHMgPSAoYXdhaXQgaG90LmludmFsaWRhdGVGaWxlKGRpZmZGaWxlUGF0aCwgZXZlbnQpKSBhcyBBYnNvbHV0ZVBhdGhbXTtcblxuICAgICAgaWYgKGludmFsaWRhdGVkUGF0aHMubGVuZ3RoID4gMCkge1xuICAgICAgICBjb25zb2xlLmxvZyhjaGFsay5ib2xkKGDwn5SEIEludmFsaWRhdGVkOmApKTtcblxuICAgICAgICBmb3IgKGNvbnN0IGludmFsaWRhdGVkUGF0aCBvZiBpbnZhbGlkYXRlZFBhdGhzKSB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIC8vIOunjOyVvSBtb2RlbC50cyDtjIzsnbzsnbQg67OA6rK9KGludmFsaWRhdGUp65CY7JeI64ukPyDqt7jrn6zrqbQgcmVnaXN0ZXJlZEFwaXMg7KSR7JeQ7IScIOydtCDrqqjrjbjsl5Ag7ZW064u57ZWY64qUIGFwaeuTpOydgCDsp4Dsm4zspJjsmpQuXG4gICAgICAgICAgICAvLyByZWdpc3RlcmVkQXBpc+uKlCDthrXsnLzroZwg64ukIOuCoOugpOuyhOumtCDsiJgg7JeG7Iq164uI64ukLiByZWdpc3RlcmVkQXBpc+yXkCDsmKzrnbzsmKTripQg7Lmc6rWs65Ok7J2AIOy0iOq4sCDroZzrk5zsi5wg65iQ64qUIEhNUuyLnOyXkOunjCDrk7HroZ3rkJjquLAg65WM66y47J6F64uI64ukLlxuICAgICAgICAgICAgLy8g65Sw65287IScIG1vZGVsLnRzIO2MjOydvOydmCDrs4Dqsr3snLzroZwg64uk7J2M67KIIOyDiOuhnOyatCBldmFs7J20IOyYiOyDgeuQmOuKlCDsnbQg7Iuc7KCQ7JeQ7ISc66eMLCDsnbQg66qo64247JeQ7IScIOuCmOyYqCByZWdpc3RlcmVkQXBpc+uTpOydhCDsp4Dsm4zspIQg7IiYIOyeiOyKteuLiOuLpC5cbiAgICAgICAgICAgIGNvbnN0IHJlbW92ZWRBcGlzID0gdGhpcy5yZW1vdmVJbnZhbGlkYXRlZFJlZ2lzdGVyZWRBcGlzKGludmFsaWRhdGVkUGF0aCk7XG4gICAgICAgICAgICBpZiAocmVtb3ZlZEFwaXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICBjaGFsay5ibHVlKGAtICR7cGF0aC5yZWxhdGl2ZShTb25hbXUuYXBpUm9vdFBhdGgsIGludmFsaWRhdGVkUGF0aCl9YCksXG4gICAgICAgICAgICAgICAgY2hhbGsuZ3JheShgKHdpdGggJHtyZW1vdmVkQXBpcy5sZW5ndGh9IEFQSXMpYCksXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBjb25zb2xlLmxvZyhjaGFsay5ibHVlKGAtICR7cGF0aC5yZWxhdGl2ZShTb25hbXUuYXBpUm9vdFBhdGgsIGludmFsaWRhdGVkUGF0aCl9YCkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZSk7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKFxuICAgICAgICAgICAgICBjaGFsay5yZWQoYEZhaWxlZCB0byByZW1vdmUgaW52YWxpZGF0ZWQgcmVnaXN0ZXJlZCBBUElzIGZvciAke2ludmFsaWRhdGVkUGF0aH1gKSxcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gZGV2UnVubmVyIO2ZnOyEse2ZlCDsi5wsIOuzgOqyveuQnCDshozsiqQg7YyM7J287J2EIFZpdGVzdCDrqqjrk4gg6re4656Y7ZSE7JeQ7ISc64+EIOustO2aqO2ZlO2VqeuLiOuLpC5cbiAgICAvLyBWaXRl7J2YIG1vZHVsZUdyYXBoLmludmFsaWRhdGVNb2R1bGUoKeydtCBpbXBvcnRlciDrsKntlqXsnLzroZwg7J6s6reA7KCBIGNhc2NhZGXtlZjrr4DroZwsXG4gICAgLy8g7IaM7IqkIO2MjOydvCDtlZjrgpjrp4wg66y07Zqo7ZmU7ZWY66m0IOydtOulvCBpbXBvcnTtlZjripQg7YWM7Iqk7Yq4IO2MjOydvOuPhCDsnpDrj5nsnLzroZwg66y07Zqo7ZmU65Cp64uI64ukLlxuICAgIGlmICghaXNUZXN0KCkgJiYgU29uYW11LmNvbmZpZy50ZXN0Py5kZXZSdW5uZXI/LmVuYWJsZWQgJiYgU29uYW11LmRldlZpdGVzdE1hbmFnZXIpIHtcbiAgICAgIFNvbmFtdS5kZXZWaXRlc3RNYW5hZ2VyLmludmFsaWRhdGVGaWxlcyhbZGlmZkZpbGVQYXRoXSk7XG4gICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgY2hhbGsuZGltKGBUZXN0IGludmFsaWRhdGVkOiAke3BhdGgucmVsYXRpdmUoU29uYW11LmFwaVJvb3RQYXRoLCBkaWZmRmlsZVBhdGgpfWApLFxuICAgICAgKTtcbiAgICB9XG5cbiAgICBjb25zdCBpc0luQ2hlY2tQYXR0ZXJuR3JvdXAgPSBPYmplY3QudmFsdWVzKGdldENoZWNrc3VtUGF0dGVybkdyb3VwSW5BYnNvbHV0ZVBhdGgoKSkuc29tZShcbiAgICAgIChwYXR0ZXJuKSA9PiBtaW5pbWF0Y2goZGlmZkZpbGVQYXRoLCBwYXR0ZXJuKSxcbiAgICApO1xuXG4gICAgLy8g7ZWgIOydvChzeW5jKeydtCDsnojsnLzrqbQg7ZWp64uI64ukLlxuICAgIGlmIChpc0luQ2hlY2tQYXR0ZXJuR3JvdXApIHtcbiAgICAgIGF3YWl0IHRoaXMuZG9TeW5jQWN0aW9ucyhbZGlmZkZpbGVQYXRoXSk7XG4gICAgfVxuXG4gICAgLy8g7Iux7YGsIOyekeyXheydtCDrgZ3rgpjrqbQg66qo65OgIOuqqOuTiOydhCDroZzrk5ztlanri4jri6QuXG4gICAgLy8gaG1yLWhvb2vsl5Ag7J2Y7ZW0IGludmFsaWRhdGXrkJwg67aA67aE65Ok7J20IOyVhOuLiOudvOuptCDsupDsi5wg6re464yA66GcIOycoOyngO2VqeuLiOuLpC5cbiAgICBhd2FpdCB0aGlzLmF1dG9sb2FkVHlwZXMoKTtcbiAgICBhd2FpdCB0aGlzLmF1dG9sb2FkTW9kZWxzKCk7XG4gICAgYXdhaXQgdGhpcy5hdXRvbG9hZEFwaXMoKTtcbiAgICBhd2FpdCB0aGlzLmF1dG9sb2FkV29ya2Zsb3dzKCk7XG5cbiAgICB0aGlzLmV2ZW50RW1pdHRlci5lbWl0KFwib25ITVJDb21wbGV0ZWRcIik7XG4gIH1cblxuICByZW1vdmVJbnZhbGlkYXRlZFJlZ2lzdGVyZWRBcGlzKFxuICAgIGludmFsaWRhdGVkUGF0aDogQWJzb2x1dGVQYXRoLFxuICApOiAodHlwZW9mIHJlZ2lzdGVyZWRBcGlzKVtudW1iZXJdW10ge1xuICAgIGlmICghaW52YWxpZGF0ZWRQYXRoLmVuZHNXaXRoKFwiLm1vZGVsLnRzXCIgLyrshozsiqQg7L2U65Oc66W8IOuLpOujqOuKlCDsg4HtmansnbTri4ggLnRzIOqyveuhnOuhnCDrtIXri4jri6QuKi8pKSB7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuXG4gICAgY29uc3QgZW50aXR5SWQgPSBFbnRpdHlNYW5hZ2VyLmdldEVudGl0eUlkRnJvbVBhdGgoaW52YWxpZGF0ZWRQYXRoKTtcbiAgICBjb25zdCB0b1JlbW92ZSA9IHJlZ2lzdGVyZWRBcGlzLmZpbHRlcigoYXBpKSA9PiBhcGkubW9kZWxOYW1lID09PSBgJHtlbnRpdHlJZH1Nb2RlbGApO1xuICAgIGZvciAoY29uc3QgYXBpIG9mIHRvUmVtb3ZlKSB7XG4gICAgICBjb25zdCBpZHggPSByZWdpc3RlcmVkQXBpcy5pbmRleE9mKGFwaSk7XG4gICAgICBpZiAoaWR4ICE9PSAtMSkge1xuICAgICAgICByZWdpc3RlcmVkQXBpcy5zcGxpY2UoaWR4LCAxKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdG9SZW1vdmU7XG4gIH1cblxuICBhc3luYyBjb3B5U2hhcmVkVG9UYXJnZXRzKHRhcmdldHM6IHN0cmluZ1tdKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gcGx1cmFsLnRzIOuCtOyaqeydhCDsnb3slrTshJwgc2hhcmVkIO2MjOydvOyXkCDsgr3snoXtlanri4jri6QuXG4gICAgY29uc3QgZGljdFV0aWxzUGF0aCA9IHBhdGguam9pbihcbiAgICAgIGltcG9ydC5tZXRhLmRpcm5hbWUucmVwbGFjZShcIi9kaXN0L1wiLCBcIi9zcmMvXCIpLFxuICAgICAgXCIuLi9kaWN0L3V0aWxzLnRzXCIsXG4gICAgKTtcbiAgICBjb25zdCBkaWN0VXRpbHNDb2RlID0gKGF3YWl0IGV4aXN0cyhkaWN0VXRpbHNQYXRoKSlcbiAgICAgID8gYXdhaXQgcmVhZEZpbGUoZGljdFV0aWxzUGF0aCwgXCJ1dGYtOFwiKVxuICAgICAgOiBcIlwiO1xuXG4gICAgLy8g7Yq57KCVIOuzgOyImCDsuZjtmZjsnYQg7JyE7ZW07IScIOyCrOyaqe2VqeuLiOuLpC5cbiAgICBjb25zdCBjb252ZXJ0TWFwID0ge1xuICAgICAgYmFzZVVybDpcbiAgICAgICAgU29uYW11LmNvbmZpZy5zZXJ2ZXIuYmFzZVVybCA/P1xuICAgICAgICBgaHR0cDovLyR7U29uYW11LmNvbmZpZy5zZXJ2ZXIubGlzdGVuPy5ob3N0ID8/IFwibG9jYWxob3N0XCJ9OiR7U29uYW11LmNvbmZpZy5zZXJ2ZXIubGlzdGVuPy5wb3J0ID8/IDMwMDB9YCxcbiAgICAgIGRpY3RVdGlsczogZGljdFV0aWxzQ29kZSxcbiAgICB9O1xuXG4gICAgZm9yIChjb25zdCB0YXJnZXQgb2YgdGFyZ2V0cykge1xuICAgICAgLy8g7KeA6riIIOqwgOyguOqwgOugpOuKlCDsnbQg7YyM7J287J2AIFNvbmFtdSDsvZTrk5zrsqDsnbTsiqTsnZgg7J2867aA7J6F64uI64ukLlxuICAgICAgLy8g6re465+w642wIGRpc3Qg7IaNIOu5jOuTnOuQnCDshozsiqQg7L2U65OcIO2MjOydvOydtCDtlYTsmpTtlZwg6rKD7J20IOyVhOuLiOqzoCwgc3Jj7JeQ66eMIOyeiOuKlCDthY3siqTtirgg7YyM7J287J20IO2VhOyalO2VqeuLiOuLpC5cbiAgICAgIC8vIOuUsOudvOyEnCAvc3JjL+yXkOyEnCDssL7sirXri4jri6QuXG4gICAgICBjb25zdCBzcmNQYXRoID0gcGF0aC5qb2luKFxuICAgICAgICBpbXBvcnQubWV0YS5kaXJuYW1lLnJlcGxhY2UoXCIvZGlzdC9cIiwgXCIvc3JjL1wiKSxcbiAgICAgICAgYC4uL3NoYXJlZC8ke3RhcmdldH0uc2hhcmVkLnRzLnR4dGAsXG4gICAgICApO1xuICAgICAgaWYgKCEoYXdhaXQgZXhpc3RzKHNyY1BhdGgpKSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIGlmICghKGF3YWl0IGV4aXN0cyhwYXRoLmpvaW4oU29uYW11LmFwcFJvb3RQYXRoLCB0YXJnZXQpKSkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIGBUcmllZCB0byBjb3B5IHNvbmFtdS5zaGFyZWQudHMgdG8gdGFyZ2V0ICcke3RhcmdldH0nIGJ1dCB0aGUgdGFyZ2V0IGRpcmVjdG9yeSBkb2VzIG5vdCBleGlzdC4gUGxlYXNlIGNoZWNrIHlvdXIgcHJvamVjdCBkaXJlY3Rvcnkgc3RydWN0dXJlLmAsXG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGZ1bGxUZXh0ID0gYXdhaXQgcmVhZEZpbGUoc3JjUGF0aCwgXCJ1dGYtOFwiKTtcbiAgICAgIGNvbnN0IGNvbnZlcnRlZFRleHQgPSBPYmplY3QuZW50cmllcyhjb252ZXJ0TWFwKS5yZWR1Y2UoXG4gICAgICAgIChhY2MsIFtrZXksIHZhbHVlXSkgPT4gYWNjLnJlcGxhY2UoYCRbWyR7a2V5fV1dYCwgdmFsdWUpLFxuICAgICAgICBmdWxsVGV4dCxcbiAgICAgICk7XG5cbiAgICAgIC8vIOydtOqxtCDtlITroZzsoJ3tirjsl5AgLnRzIOyGjOyKpCDsvZTrk5wg7YyM7J287J2EIOyDneyEse2VmOuKlCDqsoPsnbTrr4DroZwgc3Jj7J2YIC50cyDqsr3roZzroZwg6rCR64uI64ukLlxuICAgICAgY29uc3QgZGVzdFBhdGggPSBwYXRoLmpvaW4oU29uYW11LmFwcFJvb3RQYXRoLCB0YXJnZXQsIFwic3JjL3NlcnZpY2VzL3NvbmFtdS5zaGFyZWQudHNcIik7XG5cbiAgICAgIC8vIOygleunkCDtmLnsi5zrgpjsp4Drp4wgdGFyZ2V0IOuUlOugie2GoOumrOuKlCDsnojslrTrj4Qgc3JjL3NlcnZpY2VzIOuUlOugie2GoOumrOuKlCDsl4bsnYQg7IiYIOyeiOycvOuvgOuhnCDrr7jrpqwg7IOd7ISx7ZW07KSN64uI64ukLlxuICAgICAgaWYgKCEoYXdhaXQgZXhpc3RzKHBhdGguZGlybmFtZShkZXN0UGF0aCkpKSkge1xuICAgICAgICBhd2FpdCBta2RpcihwYXRoLmRpcm5hbWUoZGVzdFBhdGgpLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICAgICAgY29uc29sZS53YXJuKGBDcmVhdGVkIGRpcmVjdG9yeSAnJHtwYXRoLmRpcm5hbWUoZGVzdFBhdGgpfScgYmVjYXVzZSBpdCBkaWQgbm90IGV4aXN0LmApO1xuICAgICAgfVxuXG4gICAgICAvLyDtjIzsnbzsnbQg7J2066+4IOyhtOyerO2VmOuptCDqsbTrhIjrnIHri4jri6QuXG4gICAgICAvLyBzb25hbXUuc2hhcmVkLnRz64qUIO2UhOuhnOygne2KuOyXkOyEnCDsnpDsnKDroa3qsowg7Luk7Iqk7YSw66eI7J207KeV7ZWgIOyImCDsnojslrTslbwg7ZWY66+A66GcLFxuICAgICAgLy8g7LWc7LSIIDHtmozrp4wg7IOd7ISx7ZWY6rOgIOydtO2bhOyXkOuKlCDrja7slrTsk7Dsp4Ag7JWK7Iq164uI64ukLlxuICAgICAgLy8g7YWc7ZSM66a/IOuCtOyaqSgkW1tkaWN0VXRpbHNdXSDrk7Ep7J20IOuzgOqyveuQmOyXiOydhCDrlYwg67CY7JiB7J20IO2VhOyalO2VmOuptCxcbiAgICAgIC8vIO2VtOuLuSDtjIzsnbzsnYQg7IKt7KCc7ZWcIOuSpCBgcG5wbSBzb25hbXUgc3luY2DroZwg7J6s7IOd7ISx7ZWY66m0IOuQqeuLiOuLpC5cbiAgICAgIGlmIChhd2FpdCBleGlzdHMoZGVzdFBhdGgpKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICBhd2FpdCB3cml0ZUZpbGUoZGVzdFBhdGgsIGNvbnZlcnRlZFRleHQpO1xuICAgICAgIWlzVGVzdCgpICYmXG4gICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgIGNoYWxrLmJvbGQoXCJDb3BpZWQ6IFwiKSArIGNoYWxrLmJsdWUocGF0aC5yZWxhdGl2ZShTb25hbXUuYXBwUm9vdFBhdGgsIGRlc3RQYXRoKSksXG4gICAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgYXV0b2xvYWRUeXBlcygpIHtcbiAgICB0aGlzLnR5cGVzID0gYXdhaXQgbG9hZFR5cGVzKCk7XG4gIH1cblxuICBhc3luYyBhdXRvbG9hZE1vZGVscygpIHtcbiAgICB0aGlzLm1vZGVscyA9IGF3YWl0IGxvYWRNb2RlbHMoKTtcbiAgfVxuXG4gIGFzeW5jIGF1dG9sb2FkQXBpcygpIHtcbiAgICB0aGlzLmFwaXMgPSBhd2FpdCBsb2FkQXBpcygpO1xuICB9XG5cbiAgYXN5bmMgYXV0b2xvYWRXb3JrZmxvd3MoKSB7XG4gICAgdGhpcy53b3JrZmxvd3MgPSBhd2FpdCBsb2FkV29ya2Zsb3dzKCk7XG4gICAgYXdhaXQgU29uYW11LndvcmtmbG93cy5zeW5jaHJvbml6ZSh0aGlzLndvcmtmbG93cyk7XG4gIH1cblxuICBhc3luYyBhdXRvbG9hZFNTUlJvdXRlcygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBzc3JDb25maWdQYXRoID0gcGF0aC5qb2luKFNvbmFtdS5hcGlSb290UGF0aCwgXCJzcmMvc3NyXCIpO1xuXG4gICAgLy8g6riw7KG0IHJvdXRlcyDstIjquLDtmZRcbiAgICBjb25zdCB7IGNsZWFyU1NSUm91dGVzIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9zc3JcIik7XG4gICAgY2xlYXJTU1JSb3V0ZXMoKTtcblxuICAgIC8vIHNzciDtj7TrjZQg7JeG7Jy866m0IOyKpO2CtVxuICAgIGlmICghKGF3YWl0IGV4aXN0cyhzc3JDb25maWdQYXRoKSkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBzc3Ig7Y+0642UIOyViOydmCDrqqjrk6AgLnRzIO2MjOydvCDroZzrk5xcbiAgICBjb25zdCB7IGdsb2JBc3luYyB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vdXRpbHMvYXN5bmMtdXRpbHNcIik7XG4gICAgY29uc3QgeyBpbXBvcnRNZW1iZXJzIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi91dGlscy9lc20tdXRpbHNcIik7XG4gICAgY29uc3QgeyBydW50aW1lUGF0aCB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vdXRpbHMvcGF0aC11dGlsc1wiKTtcblxuICAgIC8vIHJ1bnRpbWVQYXRo66W8IOyCrOyaqe2VmOyXrCDqsJzrsJwv7ZSE66Gc642V7IWYIO2ZmOqyveyXkCDrp57ripQg7ZmV7J6l7J6QIOyymOumrFxuICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgZ2xvYkFzeW5jKHBhdGguam9pbihzc3JDb25maWdQYXRoLCBydW50aW1lUGF0aChcIioqLyoudHNcIikpKTtcblxuICAgIGZvciAoY29uc3QgZmlsZSBvZiBmaWxlcykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgLy8gaW1wb3J0TWVtYmVyc+ulvCDsgqzsmqntlZjrqbQg7YyM7J287J2YIHNpZGUgZWZmZWN0KHJlZ2lzdGVyU1NSIO2YuOy2nCnqsIAg7Iuk7ZaJ65CoXG4gICAgICAgIGF3YWl0IGltcG9ydE1lbWJlcnMoZmlsZSk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYEZhaWxlZCB0byBsb2FkIFNTUiByb3V0ZTogJHtmaWxlfWAsIGUpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiDsi6TsoJwg7Iux7YGs66W8IOyImO2Wie2VmOuKlCDrs7jssrTsnoXri4jri6QuXG4gICAqIOuzgOqyveuQnCDtjIzsnbzrk6TsnYQg7YOA7J6F67OE66GcIOu2hOulmO2VmOqzoCDqsIEg7YOA7J6F7JeQIOunnuuKlCDslaHshZjsnYQg7Iuk7ZaJ7ZWp64uI64ukLlxuICAgKiBAcGFyYW0gZGlmZkZpbGVQYXRocyAtIOuzgOqyveuQnCDtjIzsnbzrk6TsnZgg7KCI64yAIOqyveuhnCDrqqnroZ1cbiAgICogQHJldHVybnMgZGlmZlR5cGVzIC0g67OA6rK965CcIO2MjOydvOydmCDtg4DsnoUg66qp66GdIChlbnRpdHksIHR5cGVzLCBtb2RlbCDrk7EpXG4gICAqL1xuICBhc3luYyBkb1N5bmNBY3Rpb25zKGRpZmZGaWxlUGF0aHM6IEFic29sdXRlUGF0aFtdKTogUHJvbWlzZTx7IGRpZmZUeXBlczogc3RyaW5nW10gfT4ge1xuICAgIGNvbnN0IGRpZmZHcm91cHMgPSB0aGlzLmNhbGN1bGF0ZURpZmZHcm91cHMoZGlmZkZpbGVQYXRocyk7XG4gICAgY29uc3QgZGlmZlR5cGVzID0gT2JqZWN0LmtleXMoZGlmZkdyb3VwcykgYXMgRmlsZVR5cGVbXTtcblxuICAgIC8vIFNpbmdsZSBzb3VyY2Ugb2YgdHJ1dGjqsIAg67OA6rK965CcIOqyveyasFxuICAgIGlmIChkaWZmVHlwZXMuaW5jbHVkZXMoXCJlbnRpdHlcIikgfHwgZGlmZlR5cGVzLmluY2x1ZGVzKFwidHlwZXNcIikpIHtcbiAgICAgIGF3YWl0IHRoaXMuaGFuZGxlVHJ1dGhTb3VyY2VDaGFuZ2VzKGRpZmZHcm91cHMsIGRpZmZUeXBlcyk7XG4gICAgfVxuXG4gICAgLy8g7YOA6rKf7Jy866GcIOuzteyCrOunjCDtlZjrqbQg65CY64qUIO2MjOydvOydtCDrs4Dqsr3rkJwg6rK97JqwXG4gICAgaWYgKFxuICAgICAgZGlmZlR5cGVzLmluY2x1ZGVzKFwidHlwZXNcIikgfHxcbiAgICAgIGRpZmZUeXBlcy5pbmNsdWRlcyhcImZ1bmN0aW9uc1wiKSB8fFxuICAgICAgZGlmZlR5cGVzLmluY2x1ZGVzKFwiZ2VuZXJhdGVkXCIpXG4gICAgKSB7XG4gICAgICBhd2FpdCB0aGlzLmhhbmRsZVN5bmNhYmxlRmlsZUNoYW5nZXMoZGlmZkdyb3Vwcyk7XG4gICAgfVxuXG4gICAgLy8g66qo6424L+2UhOugiOyehCDqtaztmITssrTqsIAg67OA6rK965CcIOqyveyasFxuICAgIGlmIChkaWZmVHlwZXMuaW5jbHVkZXMoXCJtb2RlbFwiKSB8fCBkaWZmVHlwZXMuaW5jbHVkZXMoXCJmcmFtZVwiKSkge1xuICAgICAgYXdhaXQgdGhpcy5oYW5kbGVJbXBsZW1lbnRhdGlvbkNoYW5nZXMoZGlmZkdyb3Vwcyk7XG4gICAgfVxuXG4gICAgLy8g7ISk7KCVIO2MjOydvOydtCDrs4Dqsr3rkJwg6rK97JqwXG4gICAgaWYgKGRpZmZUeXBlcy5pbmNsdWRlcyhcImNvbmZpZ1wiKSkge1xuICAgICAgYXdhaXQgU3luY2VyQWN0aW9ucy5hY3Rpb25TeW5jQ29uZmlnKCk7XG4gICAgfVxuXG4gICAgLy8g7JuM7YGs7ZSM66Gc7JqwIO2MjOydvOydtCDrs4Dqsr3rkJwg6rK97JqwXG4gICAgaWYgKGRpZmZUeXBlcy5pbmNsdWRlcyhcIndvcmtmbG93XCIpKSB7XG4gICAgICBhd2FpdCB0aGlzLmF1dG9sb2FkV29ya2Zsb3dzKCk7XG4gICAgfVxuXG4gICAgLy8gaTE4biDqtIDroKgg7YyM7J287J20IOuzgOqyveuQnCDqsr3smrBcbiAgICAvLyAtIGkxOG4vKi50czogbG9jYWxlIOuyiOyXrSDtjIzsnbxcbiAgICAvLyAtIGVudGl0eS5qc29uOiBlbnRpdHkgbGFiZWxzXG4gICAgLy8gLSBzb25hbXUuY29uZmlnLnRzOiBpMThuIOyEpOyglSAoZGVmYXVsdExvY2FsZSwgc3VwcG9ydGVkTG9jYWxlcylcbiAgICBpZiAoXG4gICAgICBkaWZmVHlwZXMuaW5jbHVkZXMoXCJpMThuXCIpIHx8XG4gICAgICBkaWZmVHlwZXMuaW5jbHVkZXMoXCJlbnRpdHlcIikgfHxcbiAgICAgIGRpZmZUeXBlcy5pbmNsdWRlcyhcImNvbmZpZ1wiKVxuICAgICkge1xuICAgICAgYXdhaXQgdGhpcy5zeW5jU0QoKTtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgZGlmZlR5cGVzLFxuICAgIH07XG4gIH1cblxuICBjYWxjdWxhdGVEaWZmR3JvdXBzKGRpZmZGaWxlczogQWJzb2x1dGVQYXRoW10pOiBEaWZmR3JvdXBzIHtcbiAgICBjb25zdCBmaWxlVHlwZXMgPSBPYmplY3Qua2V5cyhjaGVja3N1bVBhdHRlcm5Hcm91cCkgYXMgRmlsZVR5cGVbXTtcblxuICAgIHJldHVybiBncm91cChkaWZmRmlsZXMsIChmaWxlUGF0aCkgPT4ge1xuICAgICAgLy8g7KCI64yAIOqyveuhnOyXkOyEnCBzcmMv66GcIOyLnOyeke2VmOuKlCDsg4HrjIAg6rK966GcIOu2gOu2hOydhCDstpTstpztlanri4jri6QuXG4gICAgICBjb25zdCBzcmNJbmRleCA9IGZpbGVQYXRoLmluZGV4T2YoXCIvc3JjL1wiKTtcbiAgICAgIGlmIChzcmNJbmRleCA9PT0gLTEpIHJldHVybiBcInVua25vd25cIjtcbiAgICAgIGNvbnN0IHJlbGF0aXZlUGF0aCA9IGZpbGVQYXRoLnNsaWNlKHNyY0luZGV4ICsgMSk7IC8vIFwic3JjLy4uLlwiIO2Yle2DnFxuXG4gICAgICBmb3IgKGNvbnN0IGZpbGVUeXBlIG9mIGZpbGVUeXBlcykge1xuICAgICAgICBpZiAobWluaW1hdGNoKHJlbGF0aXZlUGF0aCwgY2hlY2tzdW1QYXR0ZXJuR3JvdXBbZmlsZVR5cGVdKSkge1xuICAgICAgICAgIHJldHVybiBmaWxlVHlwZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIFwidW5rbm93blwiO1xuICAgIH0pIGFzIHVua25vd24gYXMgRGlmZkdyb3VwcztcbiAgfVxuXG4gIGFzeW5jIGhhbmRsZVRydXRoU291cmNlQ2hhbmdlcyhkaWZmR3JvdXBzOiBEaWZmR3JvdXBzLCBkaWZmVHlwZXM6IHN0cmluZ1tdKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgTmFpdGUudChcImhhbmRsZVRydXRoU291cmNlQ2hhbmdlc1wiLCB7IGRpZmZHcm91cHMsIGRpZmZUeXBlcyB9KTtcblxuICAgIGF3YWl0IEVudGl0eU1hbmFnZXIucmVsb2FkKCk7XG5cbiAgICAvLyB0eXBlcyDsg53shLEoZW50aXR5IOyDiOuhnCDstpTqsIDrkJwg6rK97JqwKVxuICAgIC8vIHBhcmVudElk6rCAIOyXhuqzoCwgdHlwZXPqsIAg7JeG64qUIOqyveyasOyXkOunjCDsg53shLFcbiAgICBjb25zdCBlbnRpdHlQYXRoID0gZGlmZkdyb3Vwcy5lbnRpdHk/LmF0KDApO1xuICAgIGlmIChlbnRpdHlQYXRoICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbnN0IGVudGl0eUlkID0gRW50aXR5TWFuYWdlci5nZXRFbnRpdHlJZEZyb21QYXRoKGVudGl0eVBhdGgpO1xuICAgICAgY29uc3QgZW50aXR5ID0gRW50aXR5TWFuYWdlci5nZXQoZW50aXR5SWQpO1xuICAgICAgLy8g7ZSE66Gc7KCd7Yq47JeQIOyDneyEseuQmOyWtOyVvCDtlZjripQgLnRzIO2MjOydvOydmCDqsr3roZzsnoXri4jri6QuXG4gICAgICBjb25zdCB0eXBlRmlsZVBhdGggPSBwYXRoLmpvaW4oXG4gICAgICAgIFNvbmFtdS5hcGlSb290UGF0aCxcbiAgICAgICAgYHNyYy9hcHBsaWNhdGlvbi8ke2VudGl0eS5uYW1lcy5mc30vJHtlbnRpdHkubmFtZXMuZnN9LnR5cGVzLnRzYCxcbiAgICAgICk7XG4gICAgICBpZiAoZW50aXR5LnBhcmVudElkID09PSB1bmRlZmluZWQgJiYgIShhd2FpdCBleGlzdHModHlwZUZpbGVQYXRoKSkpIHtcbiAgICAgICAgYXdhaXQgZ2VuZXJhdGVUZW1wbGF0ZShcImluaXRfdHlwZXNcIiwgeyBlbnRpdHlJZCB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBhd2FpdCBTeW5jZXJBY3Rpb25zLmFjdGlvbkdlbmVyYXRlU2NoZW1hcygpO1xuXG4gICAgZGlmZkdyb3Vwcy5nZW5lcmF0ZWQgPSB1bmlxdWUoW1xuICAgICAgLi4uKGRpZmZHcm91cHMuZ2VuZXJhdGVkID8/IFtdKSxcbiAgICAgIHBhdGguam9pbihTb25hbXUuYXBpUm9vdFBhdGgsIFwic3JjL2FwcGxpY2F0aW9uL3NvbmFtdS5nZW5lcmF0ZWQudHNcIikgYXMgQWJzb2x1dGVQYXRoLFxuICAgIF0pO1xuICAgIGRpZmZUeXBlcy5wdXNoKFwiZ2VuZXJhdGVkXCIpO1xuICB9XG5cbiAgYXN5bmMgaGFuZGxlU3luY2FibGVGaWxlQ2hhbmdlcyhkaWZmR3JvdXBzOiBEaWZmR3JvdXBzKTogUHJvbWlzZTxGaWxlVHlwZVtdPiB7XG4gICAgY29uc3QgdHNQYXRocyA9IHVuaXF1ZShbXG4gICAgICAuLi4oZGlmZkdyb3Vwcy50eXBlcyA/PyBbXSksXG4gICAgICAuLi4oZGlmZkdyb3Vwcy5mdW5jdGlvbnMgPz8gW10pLFxuICAgICAgLi4uKGRpZmZHcm91cHMuZ2VuZXJhdGVkID8/IFtdKSxcbiAgICBdKTtcbiAgICBOYWl0ZS50KFwiaGFuZGxlU3luY2FibGVGaWxlQ2hhbmdlc1wiLCB7IGRpZmZHcm91cHMgfSk7XG5cbiAgICAvLyBjb25zb2xlLmxvZyhcbiAgICAvLyAgIGNoYWxrLmdyYXkoXG4gICAgLy8gICAgIGBbUHJvY2Vzc2luZ10gSGFuZGxpbmcgdHlwZXMvZnVuY3Rpb25zL2dlbmVyYXRlZCBjaGFuZ2VzOiAke3RzUGF0aHMubWFwKChwKSA9PiBwYXRoLnJlbGF0aXZlKFNvbmFtdS5hcGlSb290UGF0aCwgcCkpLmpvaW4oXCIsIFwiKX1gXG4gICAgLy8gICApXG4gICAgLy8gKTtcblxuICAgIGF3YWl0IFN5bmNlckFjdGlvbnMuYWN0aW9uU3luY0ZpbGVzVG9UYXJnZXRzKHRzUGF0aHMpO1xuXG4gICAgcmV0dXJuIFtdO1xuICB9XG5cbiAgYXN5bmMgaGFuZGxlSW1wbGVtZW50YXRpb25DaGFuZ2VzKGRpZmZHcm91cHM6IERpZmZHcm91cHMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBOYWl0ZS50KFwiaGFuZGxlSW1wbGVtZW50YXRpb25DaGFuZ2VzXCIsIHsgZGlmZkdyb3VwcyB9KTtcbiAgICBjb25zdCBtZXJnZWRHcm91cCA9IFsuLi4oZGlmZkdyb3Vwcy5tb2RlbCA/PyBbXSksIC4uLihkaWZmR3JvdXBzLmZyYW1lID8/IFtdKV07XG5cbiAgICAvLyBjb25zb2xlLmxvZyhcbiAgICAvLyAgIGNoYWxrLmdyYXkoXG4gICAgLy8gICAgIGBbUHJvY2Vzc2luZ10gSGFuZGxpbmcgbW9kZWwvZnJhbWUgY2hhbmdlczogJHttZXJnZWRHcm91cC5tYXAoKHApID0+IHBhdGgucmVsYXRpdmUoU29uYW11LmFwaVJvb3RQYXRoLCBwKSkuam9pbihcIiwgXCIpfWBcbiAgICAvLyAgIClcbiAgICAvLyApO1xuXG4gICAgLy8gZ2VuZXJhdGVkX2h0dHAudGVtcGxhdGUudHPsl5DshJwgc3luY2VyLnR5cGVz66W8IOyUgeuLiOuLpC5cbiAgICAvLyBzZXJ2aWNlLnRlbXBsYXRlLnRz7JeQ7IScIHN5bmNlci5hcGlz66W8IOyUgeuLiOuLpC5cbiAgICBhd2FpdCB0aGlzLmF1dG9sb2FkTW9kZWxzKCk7XG4gICAgYXdhaXQgdGhpcy5hdXRvbG9hZFR5cGVzKCk7XG4gICAgYXdhaXQgdGhpcy5hdXRvbG9hZEFwaXMoKTtcblxuICAgIGNvbnN0IHBhcmFtczoge1xuICAgICAgbmFtZXNSZWNvcmQ6IEVudGl0eU5hbWVzUmVjb3JkO1xuICAgIH1bXSA9IG1lcmdlZEdyb3VwLm1hcCgobW9kZWxQYXRoKSA9PiB7XG4gICAgICBpZiAobW9kZWxQYXRoLmVuZHNXaXRoKFwiLm1vZGVsLnRzXCIpKSB7XG4gICAgICAgIGNvbnN0IGVudGl0eUlkID0gRW50aXR5TWFuYWdlci5nZXRFbnRpdHlJZEZyb21QYXRoKG1vZGVsUGF0aCk7XG4gICAgICAgIGFzc2VydChlbnRpdHlJZCk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgbmFtZXNSZWNvcmQ6IEVudGl0eU1hbmFnZXIuZ2V0TmFtZXNGcm9tSWQoZW50aXR5SWQpLFxuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgaWYgKG1vZGVsUGF0aC5lbmRzV2l0aChcIi5mcmFtZS50c1wiKSkge1xuICAgICAgICBjb25zdCBbLCBmcmFtZU5hbWVdID0gbW9kZWxQYXRoLm1hdGNoKC8uK1xcLyguKylcXC5mcmFtZVxcLnRzJC8pID8/IFtdO1xuICAgICAgICBhc3NlcnQoZnJhbWVOYW1lKTtcbiAgICAgICAgLy8gZnJhbWVOYW1l7J2EIFBhc2NhbENhc2XroZwg67OA7ZmYIChkYXNoYm9hcmQgLT4gRGFzaGJvYXJkKVxuICAgICAgICBjb25zdCBmcmFtZUlkID0gaW5mbGVjdGlvbi5jYW1lbGl6ZShmcmFtZU5hbWUpO1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIG5hbWVzUmVjb3JkOiBFbnRpdHlNYW5hZ2VyLmdldE5hbWVzRnJvbUlkKGZyYW1lSWQpLFxuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgdGhyb3cgbmV3IEVycm9yKFwibm90IHJlYWNoYWJsZVwiKTtcbiAgICB9KTtcblxuICAgIGF3YWl0IFN5bmNlckFjdGlvbnMuYWN0aW9uR2VuZXJhdGVTZXJ2aWNlcyhwYXJhbXMpO1xuICAgIGF3YWl0IFN5bmNlckFjdGlvbnMuYWN0aW9uR2VuZXJhdGVIdHRwcygpO1xuICAgIGF3YWl0IFN5bmNlckFjdGlvbnMuYWN0aW9uR2VuZXJhdGVTc3IoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiDso7zslrTsp4Qg7JeU7Yuw7Yuw7JmAIO2FnO2UjOumvyDtgqTsl5Ag64yA7ZW0LCDsg53shLHrkJwg7L2U65Oc6rCAIOyhtOyerO2VmOuKlOyngCDtmZXsnbjtlanri4jri6QuXG4gICAqIEBwYXJhbSBlbnRpdHlJZCDsl5Tti7Dti7AgSURcbiAgICogQHBhcmFtIHRlbXBsYXRlS2V5IO2FnO2UjOumvyDtgqRcbiAgICogQHBhcmFtIGVudW1JZCDsl7TqsbDtmJUgSURcbiAgICogQHJldHVybnMg7IOd7ISx65CcIOy9lOuTnOqwgCDsobTsnqztlZjripTsp4Ag7Jes67aAXG4gICAqL1xuICBhc3luYyBjaGVja0V4aXN0c0dlbkNvZGUoXG4gICAgZW50aXR5SWQ6IHN0cmluZyxcbiAgICB0ZW1wbGF0ZUtleTogVGVtcGxhdGVLZXksXG4gICAgZW51bUlkPzogc3RyaW5nLFxuICApOiBQcm9taXNlPHsgc3ViUGF0aDogc3RyaW5nOyBmdWxsUGF0aDogc3RyaW5nOyBpc0V4aXN0czogYm9vbGVhbiB9PiB7XG4gICAgY29uc3QgeyB0YXJnZXQsIHBhdGg6IGdlblBhdGggfSA9IFRlbXBsYXRlTWFuYWdlci5nZXQodGVtcGxhdGVLZXkpLmdldFRhcmdldEFuZFBhdGgoXG4gICAgICBFbnRpdHlNYW5hZ2VyLmdldE5hbWVzRnJvbUlkKGVudGl0eUlkKSxcbiAgICAgIGVudW1JZCxcbiAgICApO1xuXG4gICAgY29uc3Qgc3ViUGF0aCA9IHBhdGguam9pbih0YXJnZXQsIGdlblBhdGgpO1xuICAgIGNvbnN0IGZ1bGxQYXRoID0gcGF0aC5qb2luKFNvbmFtdS5hcHBSb290UGF0aCwgc3ViUGF0aCk7XG4gICAgcmV0dXJuIHtcbiAgICAgIHN1YlBhdGgsXG4gICAgICBmdWxsUGF0aCxcbiAgICAgIGlzRXhpc3RzOiBhd2FpdCBleGlzdHMoZnVsbFBhdGgpLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICog7KO87Ja07KeEIOyXlO2LsO2LsOyZgCDsl7TqsbDtmJXsl5Ag64yA7ZW0LCDsg53shLHrkJwg7L2U65Oc6rCAIOyhtOyerO2VmOuKlOyngCDtmZXsnbjtlanri4jri6QuXG4gICAqIEBwYXJhbSBlbnRpdHlJZCDsl5Tti7Dti7AgSURcbiAgICogQHBhcmFtIGVudW1zIOyXtOqxsO2YlSDroIjsnbTruJRcbiAgICogQHJldHVybnMg7IOd7ISx65CcIOy9lOuTnOqwgCDsobTsnqztlZjripTsp4Ag7Jes67aAXG4gICAqL1xuICBhc3luYyBjaGVja0V4aXN0cyhcbiAgICBlbnRpdHlJZDogc3RyaW5nLFxuICAgIGVudW1zOiB7XG4gICAgICBbbmFtZTogc3RyaW5nXTogei5ab2RFbnVtPFJlYWRvbmx5PFJlY29yZDxzdHJpbmcsIHN0cmluZyB8IG51bWJlcj4+PjtcbiAgICB9LFxuICApOiBQcm9taXNlPFJlY29yZDxgJHtUZW1wbGF0ZUtleX0ke3N0cmluZ31gLCBib29sZWFuPj4ge1xuICAgIGNvbnN0IGtleXM6IFRlbXBsYXRlS2V5W10gPSBUZW1wbGF0ZUtleS5vcHRpb25zO1xuICAgIGNvbnN0IG5hbWVzID0gRW50aXR5TWFuYWdlci5nZXROYW1lc0Zyb21JZChlbnRpdHlJZCk7XG4gICAgY29uc3QgZW51bXNLZXlzID0gT2JqZWN0LmtleXMoZW51bXMpLmZpbHRlcigobmFtZSkgPT4gbmFtZSAhPT0gbmFtZXMuY29uc3RhbnQpO1xuXG4gICAgcmV0dXJuIGF3YWl0IHJlZHVjZUFzeW5jKFxuICAgICAga2V5cyxcbiAgICAgIGFzeW5jIChyZXN1bHQsIGtleSkgPT4ge1xuICAgICAgICBjb25zdCB0cGwgPSBUZW1wbGF0ZU1hbmFnZXIuZ2V0KGtleSk7XG4gICAgICAgIGlmIChrZXkuc3RhcnRzV2l0aChcInZpZXdfZW51bXNcIikpIHtcbiAgICAgICAgICBhd2FpdCBtYXBBc3luYyhlbnVtc0tleXMsIGFzeW5jIChjb21wb25lbnRJZCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgeyB0YXJnZXQsIHBhdGg6IHAgfSA9IHRwbC5nZXRUYXJnZXRBbmRQYXRoKG5hbWVzLCBjb21wb25lbnRJZCk7XG4gICAgICAgICAgICByZXN1bHRbYCR7a2V5fV9fJHtjb21wb25lbnRJZH1gXSA9IGF3YWl0IGV4aXN0cyhcbiAgICAgICAgICAgICAgcGF0aC5qb2luKFNvbmFtdS5hcHBSb290UGF0aCwgdGFyZ2V0LCBwKSxcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHsgdGFyZ2V0LCBwYXRoOiBwIH0gPSB0cGwuZ2V0VGFyZ2V0QW5kUGF0aChuYW1lcyk7XG4gICAgICAgIGNvbnN0IHsgdGFyZ2V0cyB9ID0gU29uYW11LmNvbmZpZy5zeW5jO1xuICAgICAgICBpZiAodGFyZ2V0LmluY2x1ZGVzKFwiOnRhcmdldFwiKSkge1xuICAgICAgICAgIGF3YWl0IG1hcEFzeW5jKHRhcmdldHMsIGFzeW5jICh0KSA9PiB7XG4gICAgICAgICAgICByZXN1bHRbYCR7a2V5fV9fJHt0fWBdID0gYXdhaXQgZXhpc3RzKFxuICAgICAgICAgICAgICBwYXRoLmpvaW4oU29uYW11LmFwcFJvb3RQYXRoLCB0YXJnZXQucmVwbGFjZShcIjp0YXJnZXRcIiwgdCksIHApLFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXN1bHRba2V5XSA9IGF3YWl0IGV4aXN0cyhwYXRoLmpvaW4oU29uYW11LmFwcFJvb3RQYXRoLCB0YXJnZXQsIHApKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgICB9LFxuICAgICAge30gYXMgUmVjb3JkPGAke1RlbXBsYXRlS2V5fSR7c3RyaW5nfWAsIGJvb2xlYW4+LFxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICog7ZWY7JyE7Zi47ZmY7JqpIO2UhOuhneyLnCDrqZTshozrk5zsnoXri4jri6QuXG4gICAqL1xuICBhc3luYyBjcmVhdGVFbnRpdHkoZm9ybTogVGVtcGxhdGVPcHRpb25zW1wiZW50aXR5XCJdKSB7XG4gICAgcmV0dXJuIGF3YWl0IGNyZWF0ZUVudGl0eShmb3JtKTtcbiAgfVxuXG4gIC8qKlxuICAgKiDtlZjsnITtmLjtmZjsmqkg7ZSE66Gd7IucIOuplOyGjOuTnOyeheuLiOuLpC5cbiAgICovXG4gIGFzeW5jIGRlbEVudGl0eShlbnRpdHlJZDogc3RyaW5nKTogUHJvbWlzZTx7IGRlbFBhdGhzOiBzdHJpbmdbXSB9PiB7XG4gICAgcmV0dXJuIGF3YWl0IGRlbEVudGl0eShlbnRpdHlJZCk7XG4gIH1cblxuICAvKipcbiAgICog7ZWY7JyE7Zi47ZmY7JqpIO2UhOuhneyLnCDrqZTshozrk5zsnoXri4jri6QuXG4gICAqL1xuICBhc3luYyBnZW5lcmF0ZVRlbXBsYXRlPFQgZXh0ZW5kcyBUZW1wbGF0ZUtleT4oXG4gICAga2V5OiBULFxuICAgIHRlbXBsYXRlT3B0aW9uczogVGVtcGxhdGVPcHRpb25zW1RdLFxuICAgIF9nZW5lcmF0ZU9wdGlvbnM/OiBHZW5lcmF0ZU9wdGlvbnMsXG4gICk6IFByb21pc2U8QWJzb2x1dGVQYXRoW10+IHtcbiAgICByZXR1cm4gYXdhaXQgZ2VuZXJhdGVUZW1wbGF0ZShrZXksIHRlbXBsYXRlT3B0aW9ucywgX2dlbmVyYXRlT3B0aW9ucyk7XG4gIH1cblxuICAvKipcbiAgICog7ZWY7JyE7Zi47ZmY7JqpIO2UhOuhneyLnCDrqZTshozrk5zsnoXri4jri6QuXG4gICAqL1xuICBhc3luYyByZW5kZXJUZW1wbGF0ZTxUIGV4dGVuZHMga2V5b2YgVGVtcGxhdGVPcHRpb25zPihcbiAgICBrZXk6IFQsXG4gICAgdGVtcGxhdGVPcHRpb25zOiBUZW1wbGF0ZU9wdGlvbnNbVF0sXG4gICk6IFByb21pc2U8UGF0aEFuZENvZGVbXT4ge1xuICAgIHJldHVybiBhd2FpdCByZW5kZXJUZW1wbGF0ZShrZXksIHRlbXBsYXRlT3B0aW9ucyk7XG4gIH1cblxuICAvKipcbiAgICog7ZWY7JyE7Zi47ZmY7JqpIO2UhOuhneyLnCDrqZTshozrk5zsnoXri4jri6QuXG4gICAqL1xuICBhc3luYyByZW5ld0NoZWNrc3VtcygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gYXdhaXQgcmVuZXdDaGVja3N1bXMoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTRChTb25hbXUgRGljdGlvbmFyeSkg7YWc7ZSM66a/7J2EIOyDneyEse2VqeuLiOuLpC5cbiAgICovXG4gIGFzeW5jIHN5bmNTRCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB7IHRhcmdldHMgfSA9IFNvbmFtdS5jb25maWcuc3luYztcbiAgICBjb25zdCBpMThuQ29uZmlnID0gU29uYW11LmNvbmZpZy5pMThuO1xuXG4gICAgY29uc3QgdGFyZ2V0TGlzdCA9IFtcImFwaVwiLCAuLi50YXJnZXRzXSBhcyAoXCJhcGlcIiB8IFwid2ViXCIgfCBcImFwcFwiKVtdO1xuXG4gICAgY29uc3QgYXBpSTE4bkRpciA9IHBhdGguam9pbihTb25hbXUuYXBwUm9vdFBhdGgsIFNvbmFtdS5jb25maWcuYXBpLmRpciwgXCJzcmMvaTE4blwiKTtcblxuICAgIGZvciAoY29uc3QgdGFyZ2V0IG9mIHRhcmdldExpc3QpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIC8vIHdlYi9hcHDsnZgg6rK97JqwIGxvY2FsZSDtjIzsnbzrk6TsnYQgYXBp7JeQ7IScIOuzteyCrFxuICAgICAgICBpZiAodGFyZ2V0ICE9PSBcImFwaVwiKSB7XG4gICAgICAgICAgYXdhaXQgdGhpcy5zeW5jTG9jYWxlRmlsZXModGFyZ2V0LCBhcGlJMThuRGlyLCBpMThuQ29uZmlnLnN1cHBvcnRlZExvY2FsZXMpO1xuICAgICAgICB9XG5cbiAgICAgICAgYXdhaXQgZ2VuZXJhdGVUZW1wbGF0ZShcInNkXCIsIHsgdGFyZ2V0IH0sIHsgb3ZlcndyaXRlOiB0cnVlIH0pO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBjb25zb2xlLmVycm9yKGBGYWlsZWQgdG8gZ2VuZXJhdGUgU0QgdGVtcGxhdGUgZm9yICR7dGFyZ2V0fTpgLCBlKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogYXBp7J2YIGxvY2FsZSDtjIzsnbzsnYQgd2ViL2FwcOycvOuhnCDrs7Xsgqztlanri4jri6QuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHN5bmNMb2NhbGVGaWxlcyhcbiAgICB0YXJnZXQ6IHN0cmluZyxcbiAgICBhcGlJMThuRGlyOiBzdHJpbmcsXG4gICAgbG9jYWxlczogc3RyaW5nW10sXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHRhcmdldEkxOG5EaXIgPSBwYXRoLmpvaW4oU29uYW11LmFwcFJvb3RQYXRoLCB0YXJnZXQsIFwic3JjL2kxOG5cIik7XG5cbiAgICAvLyDrlJTroInthqDrpqzqsIAg7JeG7Jy866m0IOyDneyEsVxuICAgIGF3YWl0IG1rZGlyKHRhcmdldEkxOG5EaXIsIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuXG4gICAgZm9yIChjb25zdCBsb2NhbGUgb2YgbG9jYWxlcykge1xuICAgICAgY29uc3Qgc291cmNlRmlsZSA9IHBhdGguam9pbihhcGlJMThuRGlyLCBgJHtsb2NhbGV9LnRzYCk7XG4gICAgICBjb25zdCB0YXJnZXRGaWxlID0gcGF0aC5qb2luKHRhcmdldEkxOG5EaXIsIGAke2xvY2FsZX0udHNgKTtcblxuICAgICAgY29uc3Qgc3luY0hlYWRlciA9IFtcbiAgICAgICAgXCIvKipcIixcbiAgICAgICAgXCIgKiBAZ2VuZXJhdGVkXCIsXG4gICAgICAgIFwiICogQVBJ7JeQ7IScIOuPmeq4sO2ZlOuQnCDtjIzsnbzsnoXri4jri6QuIOyngeygkSDsiJjsoJXtlZjsp4Ag66eI7IS47JqULlwiLFxuICAgICAgICBcIiAqL1wiLFxuICAgICAgXS5qb2luKFwiXFxuXCIpO1xuICAgICAgYXdhaXQgY29weUZpbGVXaXRoUmVwbGFjZUNvcmVUb1NoYXJlZChzb3VyY2VGaWxlLCB0YXJnZXRGaWxlLCBzeW5jSGVhZGVyKTtcbiAgICAgICFpc1Rlc3QoKSAmJlxuICAgICAgICBjb25zb2xlLmxvZyhjaGFsay5ib2xkKFwiQ29waWVkOiBcIikgKyBjaGFsay5jeWFuKGAke3RhcmdldH0vc3JjL2kxOG4vJHtsb2NhbGV9LnRzYCkpO1xuICAgIH1cbiAgfVxufVxuIl0sIm5hbWVzIjpbImhvdCIsImFzc2VydCIsImNoYWxrIiwiRXZlbnRFbWl0dGVyIiwibWtkaXIiLCJyZWFkRmlsZSIsIndyaXRlRmlsZSIsImluZmxlY3Rpb24iLCJtaW5pbWF0Y2giLCJwYXRoIiwiZ3JvdXAiLCJ1bmlxdWUiLCJyZWdpc3RlcmVkQXBpcyIsIlNvbmFtdSIsIkVudGl0eU1hbmFnZXIiLCJBbHJlYWR5UHJvY2Vzc2VkRXhjZXB0aW9uIiwiTmFpdGUiLCJUZW1wbGF0ZU1hbmFnZXIiLCJUZW1wbGF0ZUtleSIsIm1hcEFzeW5jIiwicmVkdWNlQXN5bmMiLCJjZW50ZXJUZXh0IiwiaXNUZXN0IiwiY29weUZpbGVXaXRoUmVwbGFjZUNvcmVUb1NoYXJlZCIsImV4aXN0cyIsInJ1bldpdGhHcmFjZWZ1bFNodXRkb3duIiwiZmluZENoYW5nZWRGaWxlc1VzaW5nQ2hlY2tzdW1zIiwicmVuZXdDaGVja3N1bXMiLCJnZW5lcmF0ZVRlbXBsYXRlIiwicmVuZGVyVGVtcGxhdGUiLCJjcmVhdGVFbnRpdHkiLCJkZWxFbnRpdHkiLCJjaGVja3N1bVBhdHRlcm5Hcm91cCIsImdldENoZWNrc3VtUGF0dGVybkdyb3VwSW5BYnNvbHV0ZVBhdGgiLCJsb2FkQXBpcyIsImxvYWRNb2RlbHMiLCJsb2FkVHlwZXMiLCJsb2FkV29ya2Zsb3dzIiwiU3luY2VyQWN0aW9ucyIsIlN5bmNlciIsImFwaXMiLCJ0eXBlcyIsIm1vZGVscyIsIndvcmtmbG93cyIsIk1hcCIsImV2ZW50RW1pdHRlciIsInN5bmMiLCJ0YXJnZXRzIiwiY29uZmlnIiwiY29weVNoYXJlZFRvVGFyZ2V0cyIsImNoYW5nZWRGaWxlcyIsImxlbmd0aCIsImNvbnNvbGUiLCJsb2ciLCJibGFjayIsImJnR3JlZW4iLCJvdmVyd3JpdGUiLCJlIiwiZXJyb3IiLCJkb1N5bmNBY3Rpb25zIiwid2hlblRoaXNIYXBwZW5zIiwid2FpdEZvclVwVG8iLCJzeW5jRnJvbVdhdGNoZXIiLCJldmVudCIsImRpZmZGaWxlUGF0aCIsImluY2x1ZGVzIiwiYm9sZCIsInllbGxvdyIsImludmFsaWRhdGVGaWxlIiwiYXV0b2xvYWRTU1JSb3V0ZXMiLCJlbWl0IiwiaW52YWxpZGF0ZWRQYXRocyIsImludmFsaWRhdGVkUGF0aCIsInJlbW92ZWRBcGlzIiwicmVtb3ZlSW52YWxpZGF0ZWRSZWdpc3RlcmVkQXBpcyIsImJsdWUiLCJyZWxhdGl2ZSIsImFwaVJvb3RQYXRoIiwiZ3JheSIsInJlZCIsInRlc3QiLCJkZXZSdW5uZXIiLCJlbmFibGVkIiwiZGV2Vml0ZXN0TWFuYWdlciIsImludmFsaWRhdGVGaWxlcyIsImRpbSIsImlzSW5DaGVja1BhdHRlcm5Hcm91cCIsIk9iamVjdCIsInZhbHVlcyIsInNvbWUiLCJwYXR0ZXJuIiwiYXV0b2xvYWRUeXBlcyIsImF1dG9sb2FkTW9kZWxzIiwiYXV0b2xvYWRBcGlzIiwiYXV0b2xvYWRXb3JrZmxvd3MiLCJlbmRzV2l0aCIsImVudGl0eUlkIiwiZ2V0RW50aXR5SWRGcm9tUGF0aCIsInRvUmVtb3ZlIiwiZmlsdGVyIiwiYXBpIiwibW9kZWxOYW1lIiwiaWR4IiwiaW5kZXhPZiIsInNwbGljZSIsImRpY3RVdGlsc1BhdGgiLCJqb2luIiwiZGlybmFtZSIsInJlcGxhY2UiLCJkaWN0VXRpbHNDb2RlIiwiY29udmVydE1hcCIsImJhc2VVcmwiLCJzZXJ2ZXIiLCJsaXN0ZW4iLCJob3N0IiwicG9ydCIsImRpY3RVdGlscyIsInRhcmdldCIsInNyY1BhdGgiLCJhcHBSb290UGF0aCIsIkVycm9yIiwiZnVsbFRleHQiLCJjb252ZXJ0ZWRUZXh0IiwiZW50cmllcyIsInJlZHVjZSIsImFjYyIsImtleSIsInZhbHVlIiwiZGVzdFBhdGgiLCJyZWN1cnNpdmUiLCJ3YXJuIiwic3luY2hyb25pemUiLCJzc3JDb25maWdQYXRoIiwiY2xlYXJTU1JSb3V0ZXMiLCJnbG9iQXN5bmMiLCJpbXBvcnRNZW1iZXJzIiwicnVudGltZVBhdGgiLCJmaWxlcyIsImZpbGUiLCJkaWZmRmlsZVBhdGhzIiwiZGlmZkdyb3VwcyIsImNhbGN1bGF0ZURpZmZHcm91cHMiLCJkaWZmVHlwZXMiLCJrZXlzIiwiaGFuZGxlVHJ1dGhTb3VyY2VDaGFuZ2VzIiwiaGFuZGxlU3luY2FibGVGaWxlQ2hhbmdlcyIsImhhbmRsZUltcGxlbWVudGF0aW9uQ2hhbmdlcyIsImFjdGlvblN5bmNDb25maWciLCJzeW5jU0QiLCJkaWZmRmlsZXMiLCJmaWxlVHlwZXMiLCJmaWxlUGF0aCIsInNyY0luZGV4IiwicmVsYXRpdmVQYXRoIiwic2xpY2UiLCJmaWxlVHlwZSIsInQiLCJyZWxvYWQiLCJlbnRpdHlQYXRoIiwiZW50aXR5IiwiYXQiLCJ1bmRlZmluZWQiLCJnZXQiLCJ0eXBlRmlsZVBhdGgiLCJuYW1lcyIsImZzIiwicGFyZW50SWQiLCJhY3Rpb25HZW5lcmF0ZVNjaGVtYXMiLCJnZW5lcmF0ZWQiLCJwdXNoIiwidHNQYXRocyIsImZ1bmN0aW9ucyIsImFjdGlvblN5bmNGaWxlc1RvVGFyZ2V0cyIsIm1lcmdlZEdyb3VwIiwibW9kZWwiLCJmcmFtZSIsInBhcmFtcyIsIm1hcCIsIm1vZGVsUGF0aCIsIm5hbWVzUmVjb3JkIiwiZ2V0TmFtZXNGcm9tSWQiLCJmcmFtZU5hbWUiLCJtYXRjaCIsImZyYW1lSWQiLCJjYW1lbGl6ZSIsImFjdGlvbkdlbmVyYXRlU2VydmljZXMiLCJhY3Rpb25HZW5lcmF0ZUh0dHBzIiwiYWN0aW9uR2VuZXJhdGVTc3IiLCJjaGVja0V4aXN0c0dlbkNvZGUiLCJ0ZW1wbGF0ZUtleSIsImVudW1JZCIsImdlblBhdGgiLCJnZXRUYXJnZXRBbmRQYXRoIiwic3ViUGF0aCIsImZ1bGxQYXRoIiwiaXNFeGlzdHMiLCJjaGVja0V4aXN0cyIsImVudW1zIiwib3B0aW9ucyIsImVudW1zS2V5cyIsIm5hbWUiLCJjb25zdGFudCIsInJlc3VsdCIsInRwbCIsInN0YXJ0c1dpdGgiLCJjb21wb25lbnRJZCIsInAiLCJmb3JtIiwidGVtcGxhdGVPcHRpb25zIiwiX2dlbmVyYXRlT3B0aW9ucyIsImkxOG5Db25maWciLCJpMThuIiwidGFyZ2V0TGlzdCIsImFwaUkxOG5EaXIiLCJkaXIiLCJzeW5jTG9jYWxlRmlsZXMiLCJzdXBwb3J0ZWRMb2NhbGVzIiwibG9jYWxlcyIsInRhcmdldEkxOG5EaXIiLCJsb2NhbGUiLCJzb3VyY2VGaWxlIiwidGFyZ2V0RmlsZSIsInN5bmNIZWFkZXIiLCJjeWFuIl0sIm1hcHBpbmdzIjoiQUFBQSxTQUFTQSxHQUFHLFFBQVEsdUJBQXVCO0FBQzNDLE9BQU9DLFlBQVksU0FBUztBQUM1QixPQUFPQyxXQUFXLFFBQVE7QUFDMUIsU0FBU0MsWUFBWSxRQUFRLFNBQVM7QUFDdEMsU0FBU0MsS0FBSyxFQUFFQyxRQUFRLEVBQUVDLFNBQVMsUUFBUSxtQkFBYztBQUN6RCxPQUFPQyxnQkFB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
|
|
30
|
+
//#region src/syncer/syncer.ts
|
|
31
|
+
var Syncer;
|
|
32
|
+
var init_syncer = __esmMin((() => {
|
|
33
|
+
init_decorators();
|
|
34
|
+
init_sonamu();
|
|
35
|
+
init_entity_manager();
|
|
36
|
+
init_so_exceptions();
|
|
37
|
+
init_naite();
|
|
38
|
+
init_template_manager();
|
|
39
|
+
init_types();
|
|
40
|
+
init_async_utils();
|
|
41
|
+
init_console_util();
|
|
42
|
+
init_controller();
|
|
43
|
+
init_fs_utils();
|
|
44
|
+
init_process_utils();
|
|
45
|
+
init_checksum();
|
|
46
|
+
init_code_generator();
|
|
47
|
+
init_entity_operations();
|
|
48
|
+
init_file_patterns();
|
|
49
|
+
init_module_loader();
|
|
50
|
+
init_syncer_actions();
|
|
51
|
+
Syncer = class {
|
|
52
|
+
apis = [];
|
|
53
|
+
types = {};
|
|
54
|
+
models = {};
|
|
55
|
+
workflows = new Map();
|
|
56
|
+
eventEmitter = new EventEmitter();
|
|
57
|
+
/**
|
|
58
|
+
* 체크섬이 변경된 부분에 대해 싱크를 진행합니다.
|
|
59
|
+
* sonamu.shared.ts는 파일이 없을 때만 1회 생성하고, 이후에는 덮어쓰지 않습니다.
|
|
60
|
+
* @returns
|
|
61
|
+
*/
|
|
62
|
+
async sync() {
|
|
63
|
+
const { targets } = Sonamu.config.sync;
|
|
64
|
+
await this.copySharedToTargets(targets);
|
|
65
|
+
const changedFiles = await findChangedFilesUsingChecksums();
|
|
66
|
+
if (changedFiles.length === 0) {
|
|
67
|
+
console.log(chalk.black.bgGreen(centerText("All files are synced!")));
|
|
68
|
+
try {
|
|
69
|
+
await generateTemplate("queries", {}, { overwrite: false });
|
|
70
|
+
await generateTemplate("entry_server", {}, { overwrite: false });
|
|
71
|
+
} catch (e) {
|
|
72
|
+
if (!(e instanceof AlreadyProcessedException)) {
|
|
73
|
+
console.error("Failed to generate SSR templates:", e);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
await runWithGracefulShutdown(async () => {
|
|
79
|
+
await this.doSyncActions(changedFiles);
|
|
80
|
+
await renewChecksums();
|
|
81
|
+
}, {
|
|
82
|
+
whenThisHappens: "SIGUSR2",
|
|
83
|
+
waitForUpTo: 2e4
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Watcher가 감지한 파일 변경 사항에 대해 싱크를 진행합니다.
|
|
88
|
+
* 주어진 변경 파일들 중 체크섬 관리 대상인 것들만 가져다가 싱크를 진행합니다.
|
|
89
|
+
* 체크섬 파일 업데이트는 여기에서 하지 않습니다. 호출자가 합니다.
|
|
90
|
+
* @param diffFilePath - 변경 파일들. 프로젝트 루트부터 "src/" 또는 "dist/"로 시작하는 상대 경로입니다. 예시: "src/application/user/user.model.ts"
|
|
91
|
+
*/
|
|
92
|
+
async syncFromWatcher(event, diffFilePath) {
|
|
93
|
+
if (event !== "change" && event !== "add" && event !== "unlink") {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (diffFilePath.includes("/src/ssr/")) {
|
|
97
|
+
console.log(chalk.bold.yellow("SSR config changed - reloading..."));
|
|
98
|
+
if (!isTest()) {
|
|
99
|
+
await hot.invalidateFile(diffFilePath, event);
|
|
100
|
+
}
|
|
101
|
+
await this.autoloadSSRRoutes();
|
|
102
|
+
this.eventEmitter.emit("onHMRCompleted");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (!isTest()) {
|
|
106
|
+
const invalidatedPaths = await hot.invalidateFile(diffFilePath, event);
|
|
107
|
+
if (invalidatedPaths.length > 0) {
|
|
108
|
+
console.log(chalk.bold(`🔄 Invalidated:`));
|
|
109
|
+
for (const invalidatedPath of invalidatedPaths) {
|
|
110
|
+
try {
|
|
111
|
+
const removedApis = this.removeInvalidatedRegisteredApis(invalidatedPath);
|
|
112
|
+
if (removedApis.length > 0) {
|
|
113
|
+
console.log(chalk.blue(`- ${path.relative(Sonamu.apiRootPath, invalidatedPath)}`), chalk.gray(`(with ${removedApis.length} APIs)`));
|
|
114
|
+
} else {
|
|
115
|
+
console.log(chalk.blue(`- ${path.relative(Sonamu.apiRootPath, invalidatedPath)}`));
|
|
116
|
+
}
|
|
117
|
+
} catch (e) {
|
|
118
|
+
console.error(e);
|
|
119
|
+
console.error(chalk.red(`Failed to remove invalidated registered APIs for ${invalidatedPath}`));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (!isTest() && Sonamu.config.test?.devRunner?.enabled && Sonamu.devVitestManager) {
|
|
125
|
+
Sonamu.devVitestManager.invalidateFiles([diffFilePath]);
|
|
126
|
+
console.log(chalk.dim(`Test invalidated: ${path.relative(Sonamu.apiRootPath, diffFilePath)}`));
|
|
127
|
+
}
|
|
128
|
+
const isInCheckPatternGroup = Object.values(getChecksumPatternGroupInAbsolutePath()).some((pattern) => minimatch(diffFilePath, pattern));
|
|
129
|
+
if (isInCheckPatternGroup) {
|
|
130
|
+
await this.doSyncActions([diffFilePath]);
|
|
131
|
+
}
|
|
132
|
+
await this.autoloadTypes();
|
|
133
|
+
await this.autoloadModels();
|
|
134
|
+
await this.autoloadApis();
|
|
135
|
+
await this.autoloadWorkflows();
|
|
136
|
+
this.eventEmitter.emit("onHMRCompleted");
|
|
137
|
+
}
|
|
138
|
+
removeInvalidatedRegisteredApis(invalidatedPath) {
|
|
139
|
+
if (!invalidatedPath.endsWith(".model.ts")) {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
const entityId = EntityManager.getEntityIdFromPath(invalidatedPath);
|
|
143
|
+
const toRemove = registeredApis.filter((api) => api.modelName === `${entityId}Model`);
|
|
144
|
+
for (const api of toRemove) {
|
|
145
|
+
const idx = registeredApis.indexOf(api);
|
|
146
|
+
if (idx !== -1) {
|
|
147
|
+
registeredApis.splice(idx, 1);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return toRemove;
|
|
151
|
+
}
|
|
152
|
+
async copySharedToTargets(targets) {
|
|
153
|
+
const dictUtilsPath = path.join(import.meta.dirname.replace("/dist/", "/src/"), "../dict/utils.ts");
|
|
154
|
+
const dictUtilsCode = await exists(dictUtilsPath) ? await readFile(dictUtilsPath, "utf-8") : "";
|
|
155
|
+
const convertMap = {
|
|
156
|
+
baseUrl: Sonamu.config.server.baseUrl ?? `http://${Sonamu.config.server.listen?.host ?? "localhost"}:${Sonamu.config.server.listen?.port ?? 3e3}`,
|
|
157
|
+
dictUtils: dictUtilsCode
|
|
158
|
+
};
|
|
159
|
+
for (const target of targets) {
|
|
160
|
+
const srcPath = path.join(import.meta.dirname.replace("/dist/", "/src/"), `../shared/${target}.shared.ts.txt`);
|
|
161
|
+
if (!await exists(srcPath)) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (!await exists(path.join(Sonamu.appRootPath, target))) {
|
|
165
|
+
throw new Error(`Tried to copy sonamu.shared.ts to target '${target}' but the target directory does not exist. Please check your project directory structure.`);
|
|
166
|
+
}
|
|
167
|
+
const fullText = await readFile(srcPath, "utf-8");
|
|
168
|
+
const convertedText = Object.entries(convertMap).reduce((acc, [key, value]) => acc.replace(`$[[${key}]]`, value), fullText);
|
|
169
|
+
const destPath = path.join(Sonamu.appRootPath, target, "src/services/sonamu.shared.ts");
|
|
170
|
+
if (!await exists(path.dirname(destPath))) {
|
|
171
|
+
await mkdir(path.dirname(destPath), { recursive: true });
|
|
172
|
+
console.warn(`Created directory '${path.dirname(destPath)}' because it did not exist.`);
|
|
173
|
+
}
|
|
174
|
+
if (await exists(destPath)) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
await writeFile(destPath, convertedText);
|
|
178
|
+
!isTest() && console.log(chalk.bold("Copied: ") + chalk.blue(path.relative(Sonamu.appRootPath, destPath)));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
async autoloadTypes() {
|
|
182
|
+
this.types = await loadTypes();
|
|
183
|
+
}
|
|
184
|
+
async autoloadModels() {
|
|
185
|
+
this.models = await loadModels();
|
|
186
|
+
}
|
|
187
|
+
async autoloadApis() {
|
|
188
|
+
this.apis = await loadApis();
|
|
189
|
+
}
|
|
190
|
+
async autoloadWorkflows() {
|
|
191
|
+
this.workflows = await loadWorkflows();
|
|
192
|
+
await Sonamu.workflows.synchronize(this.workflows);
|
|
193
|
+
}
|
|
194
|
+
async autoloadSSRRoutes() {
|
|
195
|
+
const ssrConfigPath = path.join(Sonamu.apiRootPath, "src/ssr");
|
|
196
|
+
const { clearSSRRoutes } = await import("../ssr/index.js");
|
|
197
|
+
clearSSRRoutes();
|
|
198
|
+
if (!await exists(ssrConfigPath)) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const { globAsync } = await import("../utils/async-utils.js");
|
|
202
|
+
const { importMembers } = await import("../utils/esm-utils.js");
|
|
203
|
+
const { runtimePath } = await import("../utils/path-utils.js");
|
|
204
|
+
const files = await globAsync(path.join(ssrConfigPath, runtimePath("**/*.ts")));
|
|
205
|
+
for (const file of files) {
|
|
206
|
+
try {
|
|
207
|
+
await importMembers(file);
|
|
208
|
+
} catch (e) {
|
|
209
|
+
console.error(`Failed to load SSR route: ${file}`, e);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* 실제 싱크를 수행하는 본체입니다.
|
|
215
|
+
* 변경된 파일들을 타입별로 분류하고 각 타입에 맞는 액션을 실행합니다.
|
|
216
|
+
* @param diffFilePaths - 변경된 파일들의 절대 경로 목록
|
|
217
|
+
* @returns diffTypes - 변경된 파일의 타입 목록 (entity, types, model 등)
|
|
218
|
+
*/
|
|
219
|
+
async doSyncActions(diffFilePaths) {
|
|
220
|
+
const diffGroups = this.calculateDiffGroups(diffFilePaths);
|
|
221
|
+
const diffTypes = Object.keys(diffGroups);
|
|
222
|
+
if (diffTypes.includes("entity") || diffTypes.includes("types")) {
|
|
223
|
+
await this.handleTruthSourceChanges(diffGroups, diffTypes);
|
|
224
|
+
}
|
|
225
|
+
if (diffTypes.includes("types") || diffTypes.includes("functions") || diffTypes.includes("generated")) {
|
|
226
|
+
await this.handleSyncableFileChanges(diffGroups);
|
|
227
|
+
}
|
|
228
|
+
if (diffTypes.includes("model") || diffTypes.includes("frame")) {
|
|
229
|
+
await this.handleImplementationChanges(diffGroups);
|
|
230
|
+
}
|
|
231
|
+
if (diffTypes.includes("config")) {
|
|
232
|
+
await actionSyncConfig();
|
|
233
|
+
}
|
|
234
|
+
if (diffTypes.includes("workflow")) {
|
|
235
|
+
await this.autoloadWorkflows();
|
|
236
|
+
}
|
|
237
|
+
if (diffTypes.includes("i18n") || diffTypes.includes("entity") || diffTypes.includes("config")) {
|
|
238
|
+
await this.syncSD();
|
|
239
|
+
}
|
|
240
|
+
return { diffTypes };
|
|
241
|
+
}
|
|
242
|
+
calculateDiffGroups(diffFiles) {
|
|
243
|
+
const fileTypes = Object.keys(checksumPatternGroup);
|
|
244
|
+
return group(diffFiles, (filePath) => {
|
|
245
|
+
const srcIndex = filePath.indexOf("/src/");
|
|
246
|
+
if (srcIndex === -1) return "unknown";
|
|
247
|
+
const relativePath = filePath.slice(srcIndex + 1);
|
|
248
|
+
for (const fileType of fileTypes) {
|
|
249
|
+
if (minimatch(relativePath, checksumPatternGroup[fileType])) {
|
|
250
|
+
return fileType;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return "unknown";
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
async handleTruthSourceChanges(diffGroups, diffTypes) {
|
|
257
|
+
Naite.t("handleTruthSourceChanges", {
|
|
258
|
+
diffGroups,
|
|
259
|
+
diffTypes
|
|
260
|
+
});
|
|
261
|
+
await EntityManager.reload();
|
|
262
|
+
const entityPath = diffGroups.entity?.at(0);
|
|
263
|
+
if (entityPath !== undefined) {
|
|
264
|
+
const entityId = EntityManager.getEntityIdFromPath(entityPath);
|
|
265
|
+
const entity = EntityManager.get(entityId);
|
|
266
|
+
const typeFilePath = path.join(Sonamu.apiRootPath, `src/application/${entity.names.fs}/${entity.names.fs}.types.ts`);
|
|
267
|
+
if (entity.parentId === undefined && !await exists(typeFilePath)) {
|
|
268
|
+
await generateTemplate("init_types", { entityId });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
await actionGenerateSchemas();
|
|
272
|
+
diffGroups.generated = unique([...diffGroups.generated ?? [], path.join(Sonamu.apiRootPath, "src/application/sonamu.generated.ts")]);
|
|
273
|
+
diffTypes.push("generated");
|
|
274
|
+
}
|
|
275
|
+
async handleSyncableFileChanges(diffGroups) {
|
|
276
|
+
const tsPaths = unique([
|
|
277
|
+
...diffGroups.types ?? [],
|
|
278
|
+
...diffGroups.functions ?? [],
|
|
279
|
+
...diffGroups.generated ?? []
|
|
280
|
+
]);
|
|
281
|
+
Naite.t("handleSyncableFileChanges", { diffGroups });
|
|
282
|
+
await actionSyncFilesToTargets(tsPaths);
|
|
283
|
+
return [];
|
|
284
|
+
}
|
|
285
|
+
async handleImplementationChanges(diffGroups) {
|
|
286
|
+
Naite.t("handleImplementationChanges", { diffGroups });
|
|
287
|
+
const mergedGroup = [...diffGroups.model ?? [], ...diffGroups.frame ?? []];
|
|
288
|
+
await this.autoloadModels();
|
|
289
|
+
await this.autoloadTypes();
|
|
290
|
+
await this.autoloadApis();
|
|
291
|
+
const params = mergedGroup.map((modelPath) => {
|
|
292
|
+
if (modelPath.endsWith(".model.ts")) {
|
|
293
|
+
const entityId = EntityManager.getEntityIdFromPath(modelPath);
|
|
294
|
+
assert(entityId);
|
|
295
|
+
return { namesRecord: EntityManager.getNamesFromId(entityId) };
|
|
296
|
+
}
|
|
297
|
+
if (modelPath.endsWith(".frame.ts")) {
|
|
298
|
+
const [, frameName] = modelPath.match(/.+\/(.+)\.frame\.ts$/) ?? [];
|
|
299
|
+
assert(frameName);
|
|
300
|
+
const frameId = inflection.camelize(frameName);
|
|
301
|
+
return { namesRecord: EntityManager.getNamesFromId(frameId) };
|
|
302
|
+
}
|
|
303
|
+
throw new Error("not reachable");
|
|
304
|
+
});
|
|
305
|
+
await actionGenerateServices(params);
|
|
306
|
+
await actionGenerateHttps();
|
|
307
|
+
await actionGenerateSsr();
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* 주어진 엔티티와 템플릿 키에 대해, 생성된 코드가 존재하는지 확인합니다.
|
|
311
|
+
* @param entityId 엔티티 ID
|
|
312
|
+
* @param templateKey 템플릿 키
|
|
313
|
+
* @param enumId 열거형 ID
|
|
314
|
+
* @returns 생성된 코드가 존재하는지 여부
|
|
315
|
+
*/
|
|
316
|
+
async checkExistsGenCode(entityId, templateKey, enumId) {
|
|
317
|
+
const { target, path: genPath } = TemplateManager.get(templateKey).getTargetAndPath(EntityManager.getNamesFromId(entityId), enumId);
|
|
318
|
+
const subPath = path.join(target, genPath);
|
|
319
|
+
const fullPath = path.join(Sonamu.appRootPath, subPath);
|
|
320
|
+
return {
|
|
321
|
+
subPath,
|
|
322
|
+
fullPath,
|
|
323
|
+
isExists: await exists(fullPath)
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* 주어진 엔티티와 열거형에 대해, 생성된 코드가 존재하는지 확인합니다.
|
|
328
|
+
* @param entityId 엔티티 ID
|
|
329
|
+
* @param enums 열거형 레이블
|
|
330
|
+
* @returns 생성된 코드가 존재하는지 여부
|
|
331
|
+
*/
|
|
332
|
+
async checkExists(entityId, enums) {
|
|
333
|
+
const keys = TemplateKey.options;
|
|
334
|
+
const names = EntityManager.getNamesFromId(entityId);
|
|
335
|
+
const enumsKeys = Object.keys(enums).filter((name) => name !== names.constant);
|
|
336
|
+
return await reduceAsync(keys, async (result, key) => {
|
|
337
|
+
const tpl = TemplateManager.get(key);
|
|
338
|
+
if (key.startsWith("view_enums")) {
|
|
339
|
+
await mapAsync(enumsKeys, async (componentId) => {
|
|
340
|
+
const { target: target$1, path: p$1 } = tpl.getTargetAndPath(names, componentId);
|
|
341
|
+
result[`${key}__${componentId}`] = await exists(path.join(Sonamu.appRootPath, target$1, p$1));
|
|
342
|
+
});
|
|
343
|
+
return result;
|
|
344
|
+
}
|
|
345
|
+
const { target, path: p } = tpl.getTargetAndPath(names);
|
|
346
|
+
const { targets } = Sonamu.config.sync;
|
|
347
|
+
if (target.includes(":target")) {
|
|
348
|
+
await mapAsync(targets, async (t) => {
|
|
349
|
+
result[`${key}__${t}`] = await exists(path.join(Sonamu.appRootPath, target.replace(":target", t), p));
|
|
350
|
+
});
|
|
351
|
+
} else {
|
|
352
|
+
result[key] = await exists(path.join(Sonamu.appRootPath, target, p));
|
|
353
|
+
}
|
|
354
|
+
return result;
|
|
355
|
+
}, {});
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* 하위호환용 프록시 메소드입니다.
|
|
359
|
+
*/
|
|
360
|
+
async createEntity(form) {
|
|
361
|
+
return await createEntity(form);
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* 하위호환용 프록시 메소드입니다.
|
|
365
|
+
*/
|
|
366
|
+
async delEntity(entityId) {
|
|
367
|
+
return await delEntity(entityId);
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* 하위호환용 프록시 메소드입니다.
|
|
371
|
+
*/
|
|
372
|
+
async generateTemplate(key, templateOptions, _generateOptions) {
|
|
373
|
+
return await generateTemplate(key, templateOptions, _generateOptions);
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* 하위호환용 프록시 메소드입니다.
|
|
377
|
+
*/
|
|
378
|
+
async renderTemplate(key, templateOptions) {
|
|
379
|
+
return await renderTemplate(key, templateOptions);
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* 하위호환용 프록시 메소드입니다.
|
|
383
|
+
*/
|
|
384
|
+
async renewChecksums() {
|
|
385
|
+
return await renewChecksums();
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* SD(Sonamu Dictionary) 템플릿을 생성합니다.
|
|
389
|
+
*/
|
|
390
|
+
async syncSD() {
|
|
391
|
+
const { targets } = Sonamu.config.sync;
|
|
392
|
+
const i18nConfig = Sonamu.config.i18n;
|
|
393
|
+
const targetList = ["api", ...targets];
|
|
394
|
+
const apiI18nDir = path.join(Sonamu.appRootPath, Sonamu.config.api.dir, "src/i18n");
|
|
395
|
+
for (const target of targetList) {
|
|
396
|
+
try {
|
|
397
|
+
if (target !== "api") {
|
|
398
|
+
await this.syncLocaleFiles(target, apiI18nDir, i18nConfig.supportedLocales);
|
|
399
|
+
}
|
|
400
|
+
await generateTemplate("sd", { target }, { overwrite: true });
|
|
401
|
+
} catch (e) {
|
|
402
|
+
console.error(`Failed to generate SD template for ${target}:`, e);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* api의 locale 파일을 web/app으로 복사합니다.
|
|
408
|
+
*/
|
|
409
|
+
async syncLocaleFiles(target, apiI18nDir, locales) {
|
|
410
|
+
const targetI18nDir = path.join(Sonamu.appRootPath, target, "src/i18n");
|
|
411
|
+
await mkdir(targetI18nDir, { recursive: true });
|
|
412
|
+
for (const locale of locales) {
|
|
413
|
+
const sourceFile = path.join(apiI18nDir, `${locale}.ts`);
|
|
414
|
+
const targetFile = path.join(targetI18nDir, `${locale}.ts`);
|
|
415
|
+
const syncHeader = [
|
|
416
|
+
"/**",
|
|
417
|
+
" * @generated",
|
|
418
|
+
" * API에서 동기화된 파일입니다. 직접 수정하지 마세요.",
|
|
419
|
+
" */"
|
|
420
|
+
].join("\n");
|
|
421
|
+
await copyFileWithReplaceCoreToShared(sourceFile, targetFile, syncHeader);
|
|
422
|
+
!isTest() && console.log(chalk.bold("Copied: ") + chalk.cyan(`${target}/src/i18n/${locale}.ts`));
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
}));
|
|
427
|
+
|
|
428
|
+
//#endregion
|
|
429
|
+
init_syncer();
|
|
430
|
+
export { Syncer, init_syncer };
|
|
431
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3luY2VyLmpzIiwibmFtZXMiOlsiU3luY2VyQWN0aW9ucy5hY3Rpb25TeW5jQ29uZmlnIiwiU3luY2VyQWN0aW9ucy5hY3Rpb25HZW5lcmF0ZVNjaGVtYXMiLCJTeW5jZXJBY3Rpb25zLmFjdGlvblN5bmNGaWxlc1RvVGFyZ2V0cyIsInBhcmFtczoge1xuICAgICAgbmFtZXNSZWNvcmQ6IEVudGl0eU5hbWVzUmVjb3JkO1xuICAgIH1bXSIsIlN5bmNlckFjdGlvbnMuYWN0aW9uR2VuZXJhdGVTZXJ2aWNlcyIsIlN5bmNlckFjdGlvbnMuYWN0aW9uR2VuZXJhdGVIdHRwcyIsIlN5bmNlckFjdGlvbnMuYWN0aW9uR2VuZXJhdGVTc3IiLCJrZXlzOiBUZW1wbGF0ZUtleVtdIiwicCIsInRhcmdldCJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zeW5jZXIvc3luY2VyLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBhc3NlcnQgZnJvbSBcImFzc2VydFwiO1xuaW1wb3J0IHsgRXZlbnRFbWl0dGVyIH0gZnJvbSBcImV2ZW50c1wiO1xuaW1wb3J0IHsgbWtkaXIsIHJlYWRGaWxlLCB3cml0ZUZpbGUgfSBmcm9tIFwiZnMvcHJvbWlzZXNcIjtcbmltcG9ydCBwYXRoIGZyb20gXCJwYXRoXCI7XG5cbmltcG9ydCB7IGhvdCB9IGZyb20gXCJAc29uYW11LWtpdC9obXItaG9va1wiO1xuaW1wb3J0IGNoYWxrIGZyb20gXCJjaGFsa1wiO1xuaW1wb3J0IGluZmxlY3Rpb24gZnJvbSBcImluZmxlY3Rpb25cIjtcbmltcG9ydCB7IG1pbmltYXRjaCB9IGZyb20gXCJtaW5pbWF0Y2hcIjtcbmltcG9ydCB7IGdyb3VwLCB1bmlxdWUgfSBmcm9tIFwicmFkYXNoaVwiO1xuaW1wb3J0IHsgdHlwZSB6IH0gZnJvbSBcInpvZFwiO1xuXG5pbXBvcnQgeyByZWdpc3RlcmVkQXBpcyB9IGZyb20gXCIuLi9hcGkvZGVjb3JhdG9yc1wiO1xuaW1wb3J0IHsgU29uYW11IH0gZnJvbSBcIi4uL2FwaS9zb25hbXVcIjtcbmltcG9ydCB7IEVudGl0eU1hbmFnZXIgfSBmcm9tIFwiLi4vZW50aXR5L2VudGl0eS1tYW5hZ2VyXCI7XG5pbXBvcnQgeyB0eXBlIEVudGl0eU5hbWVzUmVjb3JkIH0gZnJvbSBcIi4uL2VudGl0eS9lbnRpdHktbWFuYWdlclwiO1xuaW1wb3J0IHsgQWxyZWFkeVByb2Nlc3NlZEV4Y2VwdGlvbiB9IGZyb20gXCIuLi9leGNlcHRpb25zL3NvLWV4Y2VwdGlvbnNcIjtcbmltcG9ydCB7IE5haXRlIH0gZnJvbSBcIi4uL25haXRlL25haXRlXCI7XG5pbXBvcnQgeyB0eXBlIFdvcmtmbG93TWV0YWRhdGEgfSBmcm9tIFwiLi4vdGFza3MvZGVjb3JhdG9yXCI7XG5pbXBvcnQgeyBUZW1wbGF0ZU1hbmFnZXIgfSBmcm9tIFwiLi4vdGVtcGxhdGUvdGVtcGxhdGUtbWFuYWdlclwiO1xuaW1wb3J0IHsgdHlwZSBHZW5lcmF0ZU9wdGlvbnMsIHR5cGUgUGF0aEFuZENvZGUgfSBmcm9tIFwiLi4vdHlwZXMvdHlwZXNcIjtcbmltcG9ydCB7IFRlbXBsYXRlS2V5IH0gZnJvbSBcIi4uL3R5cGVzL3R5cGVzXCI7XG5pbXBvcnQgeyB0eXBlIFRlbXBsYXRlT3B0aW9ucyB9IGZyb20gXCIuLi90eXBlcy90eXBlc1wiO1xuaW1wb3J0IHsgbWFwQXN5bmMsIHJlZHVjZUFzeW5jIH0gZnJvbSBcIi4uL3V0aWxzL2FzeW5jLXV0aWxzXCI7XG5pbXBvcnQgeyBjZW50ZXJUZXh0IH0gZnJvbSBcIi4uL3V0aWxzL2NvbnNvbGUtdXRpbFwiO1xuaW1wb3J0IHsgaXNUZXN0IH0gZnJvbSBcIi4uL3V0aWxzL2NvbnRyb2xsZXJcIjtcbmltcG9ydCB7IGNvcHlGaWxlV2l0aFJlcGxhY2VDb3JlVG9TaGFyZWQsIGV4aXN0cyB9IGZyb20gXCIuLi91dGlscy9mcy11dGlsc1wiO1xuaW1wb3J0IHsgdHlwZSBBYnNvbHV0ZVBhdGggfSBmcm9tIFwiLi4vdXRpbHMvcGF0aC11dGlsc1wiO1xuaW1wb3J0IHsgcnVuV2l0aEdyYWNlZnVsU2h1dGRvd24gfSBmcm9tIFwiLi4vdXRpbHMvcHJvY2Vzcy11dGlsc1wiO1xuaW1wb3J0IHsgZmluZENoYW5nZWRGaWxlc1VzaW5nQ2hlY2tzdW1zLCByZW5ld0NoZWNrc3VtcyB9IGZyb20gXCIuL2NoZWNrc3VtXCI7XG5pbXBvcnQgeyBnZW5lcmF0ZVRlbXBsYXRlLCByZW5kZXJUZW1wbGF0ZSB9IGZyb20gXCIuL2NvZGUtZ2VuZXJhdG9yXCI7XG5pbXBvcnQgeyBjcmVhdGVFbnRpdHksIGRlbEVudGl0eSB9IGZyb20gXCIuL2VudGl0eS1vcGVyYXRpb25zXCI7XG5pbXBvcnQgeyBjaGVja3N1bVBhdHRlcm5Hcm91cCwgZ2V0Q2hlY2tzdW1QYXR0ZXJuR3JvdXBJbkFic29sdXRlUGF0aCB9IGZyb20gXCIuL2ZpbGUtcGF0dGVybnNcIjtcbmltcG9ydCB7IHR5cGUgRmlsZVR5cGUgfSBmcm9tIFwiLi9maWxlLXBhdHRlcm5zXCI7XG5pbXBvcnQgeyBsb2FkQXBpcywgbG9hZE1vZGVscywgbG9hZFR5cGVzLCBsb2FkV29ya2Zsb3dzIH0gZnJvbSBcIi4vbW9kdWxlLWxvYWRlclwiO1xuaW1wb3J0IHsgdHlwZSBMb2FkZWRBcGlzLCB0eXBlIExvYWRlZE1vZGVscywgdHlwZSBMb2FkZWRUeXBlcyB9IGZyb20gXCIuL21vZHVsZS1sb2FkZXJcIjtcbmltcG9ydCAqIGFzIFN5bmNlckFjdGlvbnMgZnJvbSBcIi4vc3luY2VyLWFjdGlvbnNcIjtcblxudHlwZSBEaWZmR3JvdXBzID0ge1xuICBba2V5IGluIEZpbGVUeXBlXTogQWJzb2x1dGVQYXRoW107XG59O1xuXG5leHBvcnQgY2xhc3MgU3luY2VyIHtcbiAgYXBpczogTG9hZGVkQXBpcyA9IFtdO1xuICB0eXBlczogTG9hZGVkVHlwZXMgPSB7fTtcbiAgbW9kZWxzOiBMb2FkZWRNb2RlbHMgPSB7fTtcbiAgd29ya2Zsb3dzOiBNYXA8c3RyaW5nLCBXb3JrZmxvd01ldGFkYXRhW10+ID0gbmV3IE1hcCgpO1xuICBldmVudEVtaXR0ZXI6IEV2ZW50RW1pdHRlciA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcblxuICAvKipcbiAgICog7LK07YGs7ISs7J20IOuzgOqyveuQnCDrtoDrtoTsl5Ag64yA7ZW0IOyLse2BrOulvCDsp4Ttlontlanri4jri6QuXG4gICAqIHNvbmFtdS5zaGFyZWQudHPripQg7YyM7J287J20IOyXhuydhCDrlYzrp4wgMe2ajCDsg53shLHtlZjqs6AsIOydtO2bhOyXkOuKlCDrja7slrTsk7Dsp4Ag7JWK7Iq164uI64ukLlxuICAgKiBAcmV0dXJuc1xuICAgKi9cbiAgYXN5bmMgc3luYygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB7IHRhcmdldHMgfSA9IFNvbmFtdS5jb25maWcuc3luYztcblxuICAgIC8vIHNvbmFtdS5zaGFyZWQudHPripQg7YyM7J287J20IOyXhuydhCDrlYzrp4wgMe2ajCDsg53shLHtlanri4jri6QuXG4gICAgYXdhaXQgdGhpcy5jb3B5U2hhcmVkVG9UYXJnZXRzKHRhcmdldHMpO1xuXG4gICAgLy8g6re4IOuLpOydjOu2gO2EsOuKlCDrs4Dqsr3rkJwg7YyM7J287J2EIOywvuyVhOyEnCDrj5nquLDtmZQg7J6R7JeF7J2EIOyLpO2Wie2VqeuLiOuLpC5cbiAgICBjb25zdCBjaGFuZ2VkRmlsZXMgPSBhd2FpdCBmaW5kQ2hhbmdlZEZpbGVzVXNpbmdDaGVja3N1bXMoKTtcbiAgICBpZiAoY2hhbmdlZEZpbGVzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgY29uc29sZS5sb2coY2hhbGsuYmxhY2suYmdHcmVlbihjZW50ZXJUZXh0KFwiQWxsIGZpbGVzIGFyZSBzeW5jZWQhXCIpKSk7XG5cbiAgICAgIC8vIOuzgOqyveyCrO2VreydtCDsl4bslrTrj4QgU1NSIO2FnO2UjOumv+ydgCDsg53shLEgKOy0iOq4sCDshKTsoJUg7IucLCDsnbTrr7gg7KG07J6s7ZWY66m0IOyKpO2CtSlcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IGdlbmVyYXRlVGVtcGxhdGUoXCJxdWVyaWVzXCIsIHt9LCB7IG92ZXJ3cml0ZTogZmFsc2UgfSk7XG4gICAgICAgIGF3YWl0IGdlbmVyYXRlVGVtcGxhdGUoXCJlbnRyeV9zZXJ2ZXJcIiwge30sIHsgb3ZlcndyaXRlOiBmYWxzZSB9KTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgLy8g7YyM7J287J20IOydtOuvuCDsobTsnqztlZjrqbQg66y07IucXG4gICAgICAgIGlmICghKGUgaW5zdGFuY2VvZiBBbHJlYWR5UHJvY2Vzc2VkRXhjZXB0aW9uKSkge1xuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXCJGYWlsZWQgdG8gZ2VuZXJhdGUgU1NSIHRlbXBsYXRlczpcIiwgZSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIOunjOyVvSDsi7Htgawg7KSR7JeQIO2UhOuhnOyEuOyKpOqwgCDso73snLzrqbQg6rys7Jes67KE66as6riwIOuVjOusuOyXkCxcbiAgICAvLyDsi5zqt7jrhJDsl5Drj4Qg7J6g7IucIOuyhO2LuCDsiJgg7J6I64qUIO2ZmOqyvSDsho3sl5DshJwg7Iux7YGs66W8IOyLpO2Wie2VqeuLiOuLpC5cbiAgICBhd2FpdCBydW5XaXRoR3JhY2VmdWxTaHV0ZG93bihcbiAgICAgIGFzeW5jICgpID0+IHtcbiAgICAgICAgLy8g7JaY6rCAIOyLse2BrCDsnpHsl4Ug7IiY7ZaJ7ZWY64qUIOuzuOyytOyeheuLiOuLpC5cbiAgICAgICAgYXdhaXQgdGhpcy5kb1N5bmNBY3Rpb25zKGNoYW5nZWRGaWxlcyk7XG5cbiAgICAgICAgLy8g7Iux7YGsIOyVoeyFmOydtCDrgZ3rgpjrqbQg7ZWt7IOBIOyytO2BrOyErOydhCDri6Tsi5wg6rCx7Iug7ZWp64uI64ukLlxuICAgICAgICBhd2FpdCByZW5ld0NoZWNrc3VtcygpO1xuICAgICAgfSxcbiAgICAgIHsgd2hlblRoaXNIYXBwZW5zOiBcIlNJR1VTUjJcIiwgd2FpdEZvclVwVG86IDIwMDAwIH0sXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBXYXRjaGVy6rCAIOqwkOyngO2VnCDtjIzsnbwg67OA6rK9IOyCrO2VreyXkCDrjIDtlbQg7Iux7YGs66W8IOynhO2Wie2VqeuLiOuLpC5cbiAgICog7KO87Ja07KeEIOuzgOqyvSDtjIzsnbzrk6Qg7KSRIOyytO2BrOyErCDqtIDrpqwg64yA7IOB7J24IOqyg+uTpOunjCDqsIDsoLjri6TqsIAg7Iux7YGs66W8IOynhO2Wie2VqeuLiOuLpC5cbiAgICog7LK07YGs7ISsIO2MjOydvCDsl4XrjbDsnbTtirjripQg7Jes6riw7JeQ7IScIO2VmOyngCDslYrsirXri4jri6QuIO2YuOy2nOyekOqwgCDtlanri4jri6QuXG4gICAqIEBwYXJhbSBkaWZmRmlsZVBhdGggLSDrs4Dqsr0g7YyM7J2865OkLiDtlITroZzsoJ3tirgg66Oo7Yq467aA7YSwIFwic3JjL1wiIOuYkOuKlCBcImRpc3QvXCLroZwg7Iuc7J6R7ZWY64qUIOyDgeuMgCDqsr3roZzsnoXri4jri6QuIOyYiOyLnDogXCJzcmMvYXBwbGljYXRpb24vdXNlci91c2VyLm1vZGVsLnRzXCJcbiAgICovXG4gIGFzeW5jIHN5bmNGcm9tV2F0Y2hlcihldmVudDogc3RyaW5nLCBkaWZmRmlsZVBhdGg6IEFic29sdXRlUGF0aCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmIChldmVudCAhPT0gXCJjaGFuZ2VcIiAmJiBldmVudCAhPT0gXCJhZGRcIiAmJiBldmVudCAhPT0gXCJ1bmxpbmtcIikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIFNTUiDshKTsoJUg7YyM7J28IOuzgOqyvSDqsJDsp4BcbiAgICBpZiAoZGlmZkZpbGVQYXRoLmluY2x1ZGVzKFwiL3NyYy9zc3IvXCIpKSB7XG4gICAgICBjb25zb2xlLmxvZyhjaGFsay5ib2xkLnllbGxvdyhcIlNTUiBjb25maWcgY2hhbmdlZCAtIHJlbG9hZGluZy4uLlwiKSk7XG4gICAgICAvLyBTU1Ig7YyM7J2864+EIGludmFsaWRhdGUg7ZuEIHJlbG9hZFxuICAgICAgaWYgKCFpc1Rlc3QoKSkge1xuICAgICAgICBhd2FpdCBob3QuaW52YWxpZGF0ZUZpbGUoZGlmZkZpbGVQYXRoLCBldmVudCk7XG4gICAgICB9XG4gICAgICBhd2FpdCB0aGlzLmF1dG9sb2FkU1NSUm91dGVzKCk7XG4gICAgICB0aGlzLmV2ZW50RW1pdHRlci5lbWl0KFwib25ITVJDb21wbGV0ZWRcIik7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8g7J2864uoIOuzgOqyveuQnCDtjIzsnbzqs7wgZGVwZW5kZW50IO2MjOydvOuTpOydhCBpbnZhbGlkYXRlIO2VqeuLiOuLpC5cbiAgICAvLyDtlZwg67KIIOydtOyDgSBpbXBvcnTrkJwg7Lmc6rWs65Ok7JeQIOuMgO2VtOyEnOunjCDsi6TsoJwg7J6R7JeF7J20IOydvOyWtOuCqeuLiOuLpC5cbiAgICAvLyDqt7jrn6zri4gg7JWI7Ius7ZWY6rOgIGludmFsaWRhdGUg7ZW064+EIOuQqeuLiOuLpC5cbiAgICAvLyDthYzsiqTtirgg7ZmY6rK97JeQ7ISc64qUIGhvdC5pbnZhbGlkYXRlRmlsZeyLnCDstIjquLAg7JeQ65+s6rCAIOuwnOyDne2VmOq4sCDrlYzrrLjsl5AgaW52YWxpZGF0ZSDtlZjsp4Ag7JWK7Iq164uI64ukLlxuICAgIGlmICghaXNUZXN0KCkpIHtcbiAgICAgIGNvbnN0IGludmFsaWRhdGVkUGF0aHMgPSAoYXdhaXQgaG90LmludmFsaWRhdGVGaWxlKGRpZmZGaWxlUGF0aCwgZXZlbnQpKSBhcyBBYnNvbHV0ZVBhdGhbXTtcblxuICAgICAgaWYgKGludmFsaWRhdGVkUGF0aHMubGVuZ3RoID4gMCkge1xuICAgICAgICBjb25zb2xlLmxvZyhjaGFsay5ib2xkKGDwn5SEIEludmFsaWRhdGVkOmApKTtcblxuICAgICAgICBmb3IgKGNvbnN0IGludmFsaWRhdGVkUGF0aCBvZiBpbnZhbGlkYXRlZFBhdGhzKSB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIC8vIOunjOyVvSBtb2RlbC50cyDtjIzsnbzsnbQg67OA6rK9KGludmFsaWRhdGUp65CY7JeI64ukPyDqt7jrn6zrqbQgcmVnaXN0ZXJlZEFwaXMg7KSR7JeQ7IScIOydtCDrqqjrjbjsl5Ag7ZW064u57ZWY64qUIGFwaeuTpOydgCDsp4Dsm4zspJjsmpQuXG4gICAgICAgICAgICAvLyByZWdpc3RlcmVkQXBpc+uKlCDthrXsnLzroZwg64ukIOuCoOugpOuyhOumtCDsiJgg7JeG7Iq164uI64ukLiByZWdpc3RlcmVkQXBpc+yXkCDsmKzrnbzsmKTripQg7Lmc6rWs65Ok7J2AIOy0iOq4sCDroZzrk5zsi5wg65iQ64qUIEhNUuyLnOyXkOunjCDrk7HroZ3rkJjquLAg65WM66y47J6F64uI64ukLlxuICAgICAgICAgICAgLy8g65Sw65287IScIG1vZGVsLnRzIO2MjOydvOydmCDrs4Dqsr3snLzroZwg64uk7J2M67KIIOyDiOuhnOyatCBldmFs7J20IOyYiOyDgeuQmOuKlCDsnbQg7Iuc7KCQ7JeQ7ISc66eMLCDsnbQg66qo64247JeQ7IScIOuCmOyYqCByZWdpc3RlcmVkQXBpc+uTpOydhCDsp4Dsm4zspIQg7IiYIOyeiOyKteuLiOuLpC5cbiAgICAgICAgICAgIGNvbnN0IHJlbW92ZWRBcGlzID0gdGhpcy5yZW1vdmVJbnZhbGlkYXRlZFJlZ2lzdGVyZWRBcGlzKGludmFsaWRhdGVkUGF0aCk7XG4gICAgICAgICAgICBpZiAocmVtb3ZlZEFwaXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICBjaGFsay5ibHVlKGAtICR7cGF0aC5yZWxhdGl2ZShTb25hbXUuYXBpUm9vdFBhdGgsIGludmFsaWRhdGVkUGF0aCl9YCksXG4gICAgICAgICAgICAgICAgY2hhbGsuZ3JheShgKHdpdGggJHtyZW1vdmVkQXBpcy5sZW5ndGh9IEFQSXMpYCksXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBjb25zb2xlLmxvZyhjaGFsay5ibHVlKGAtICR7cGF0aC5yZWxhdGl2ZShTb25hbXUuYXBpUm9vdFBhdGgsIGludmFsaWRhdGVkUGF0aCl9YCkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZSk7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKFxuICAgICAgICAgICAgICBjaGFsay5yZWQoYEZhaWxlZCB0byByZW1vdmUgaW52YWxpZGF0ZWQgcmVnaXN0ZXJlZCBBUElzIGZvciAke2ludmFsaWRhdGVkUGF0aH1gKSxcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gZGV2UnVubmVyIO2ZnOyEse2ZlCDsi5wsIOuzgOqyveuQnCDshozsiqQg7YyM7J287J2EIFZpdGVzdCDrqqjrk4gg6re4656Y7ZSE7JeQ7ISc64+EIOustO2aqO2ZlO2VqeuLiOuLpC5cbiAgICAvLyBWaXRl7J2YIG1vZHVsZUdyYXBoLmludmFsaWRhdGVNb2R1bGUoKeydtCBpbXBvcnRlciDrsKntlqXsnLzroZwg7J6s6reA7KCBIGNhc2NhZGXtlZjrr4DroZwsXG4gICAgLy8g7IaM7IqkIO2MjOydvCDtlZjrgpjrp4wg66y07Zqo7ZmU7ZWY66m0IOydtOulvCBpbXBvcnTtlZjripQg7YWM7Iqk7Yq4IO2MjOydvOuPhCDsnpDrj5nsnLzroZwg66y07Zqo7ZmU65Cp64uI64ukLlxuICAgIGlmICghaXNUZXN0KCkgJiYgU29uYW11LmNvbmZpZy50ZXN0Py5kZXZSdW5uZXI/LmVuYWJsZWQgJiYgU29uYW11LmRldlZpdGVzdE1hbmFnZXIpIHtcbiAgICAgIFNvbmFtdS5kZXZWaXRlc3RNYW5hZ2VyLmludmFsaWRhdGVGaWxlcyhbZGlmZkZpbGVQYXRoXSk7XG4gICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgY2hhbGsuZGltKGBUZXN0IGludmFsaWRhdGVkOiAke3BhdGgucmVsYXRpdmUoU29uYW11LmFwaVJvb3RQYXRoLCBkaWZmRmlsZVBhdGgpfWApLFxuICAgICAgKTtcbiAgICB9XG5cbiAgICBjb25zdCBpc0luQ2hlY2tQYXR0ZXJuR3JvdXAgPSBPYmplY3QudmFsdWVzKGdldENoZWNrc3VtUGF0dGVybkdyb3VwSW5BYnNvbHV0ZVBhdGgoKSkuc29tZShcbiAgICAgIChwYXR0ZXJuKSA9PiBtaW5pbWF0Y2goZGlmZkZpbGVQYXRoLCBwYXR0ZXJuKSxcbiAgICApO1xuXG4gICAgLy8g7ZWgIOydvChzeW5jKeydtCDsnojsnLzrqbQg7ZWp64uI64ukLlxuICAgIGlmIChpc0luQ2hlY2tQYXR0ZXJuR3JvdXApIHtcbiAgICAgIGF3YWl0IHRoaXMuZG9TeW5jQWN0aW9ucyhbZGlmZkZpbGVQYXRoXSk7XG4gICAgfVxuXG4gICAgLy8g7Iux7YGsIOyekeyXheydtCDrgZ3rgpjrqbQg66qo65OgIOuqqOuTiOydhCDroZzrk5ztlanri4jri6QuXG4gICAgLy8gaG1yLWhvb2vsl5Ag7J2Y7ZW0IGludmFsaWRhdGXrkJwg67aA67aE65Ok7J20IOyVhOuLiOudvOuptCDsupDsi5wg6re464yA66GcIOycoOyngO2VqeuLiOuLpC5cbiAgICBhd2FpdCB0aGlzLmF1dG9sb2FkVHlwZXMoKTtcbiAgICBhd2FpdCB0aGlzLmF1dG9sb2FkTW9kZWxzKCk7XG4gICAgYXdhaXQgdGhpcy5hdXRvbG9hZEFwaXMoKTtcbiAgICBhd2FpdCB0aGlzLmF1dG9sb2FkV29ya2Zsb3dzKCk7XG5cbiAgICB0aGlzLmV2ZW50RW1pdHRlci5lbWl0KFwib25ITVJDb21wbGV0ZWRcIik7XG4gIH1cblxuICByZW1vdmVJbnZhbGlkYXRlZFJlZ2lzdGVyZWRBcGlzKFxuICAgIGludmFsaWRhdGVkUGF0aDogQWJzb2x1dGVQYXRoLFxuICApOiAodHlwZW9mIHJlZ2lzdGVyZWRBcGlzKVtudW1iZXJdW10ge1xuICAgIGlmICghaW52YWxpZGF0ZWRQYXRoLmVuZHNXaXRoKFwiLm1vZGVsLnRzXCIgLyrshozsiqQg7L2U65Oc66W8IOuLpOujqOuKlCDsg4HtmansnbTri4ggLnRzIOqyveuhnOuhnCDrtIXri4jri6QuKi8pKSB7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuXG4gICAgY29uc3QgZW50aXR5SWQgPSBFbnRpdHlNYW5hZ2VyLmdldEVudGl0eUlkRnJvbVBhdGgoaW52YWxpZGF0ZWRQYXRoKTtcbiAgICBjb25zdCB0b1JlbW92ZSA9IHJlZ2lzdGVyZWRBcGlzLmZpbHRlcigoYXBpKSA9PiBhcGkubW9kZWxOYW1lID09PSBgJHtlbnRpdHlJZH1Nb2RlbGApO1xuICAgIGZvciAoY29uc3QgYXBpIG9mIHRvUmVtb3ZlKSB7XG4gICAgICBjb25zdCBpZHggPSByZWdpc3RlcmVkQXBpcy5pbmRleE9mKGFwaSk7XG4gICAgICBpZiAoaWR4ICE9PSAtMSkge1xuICAgICAgICByZWdpc3RlcmVkQXBpcy5zcGxpY2UoaWR4LCAxKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdG9SZW1vdmU7XG4gIH1cblxuICBhc3luYyBjb3B5U2hhcmVkVG9UYXJnZXRzKHRhcmdldHM6IHN0cmluZ1tdKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gcGx1cmFsLnRzIOuCtOyaqeydhCDsnb3slrTshJwgc2hhcmVkIO2MjOydvOyXkCDsgr3snoXtlanri4jri6QuXG4gICAgY29uc3QgZGljdFV0aWxzUGF0aCA9IHBhdGguam9pbihcbiAgICAgIGltcG9ydC5tZXRhLmRpcm5hbWUucmVwbGFjZShcIi9kaXN0L1wiLCBcIi9zcmMvXCIpLFxuICAgICAgXCIuLi9kaWN0L3V0aWxzLnRzXCIsXG4gICAgKTtcbiAgICBjb25zdCBkaWN0VXRpbHNDb2RlID0gKGF3YWl0IGV4aXN0cyhkaWN0VXRpbHNQYXRoKSlcbiAgICAgID8gYXdhaXQgcmVhZEZpbGUoZGljdFV0aWxzUGF0aCwgXCJ1dGYtOFwiKVxuICAgICAgOiBcIlwiO1xuXG4gICAgLy8g7Yq57KCVIOuzgOyImCDsuZjtmZjsnYQg7JyE7ZW07IScIOyCrOyaqe2VqeuLiOuLpC5cbiAgICBjb25zdCBjb252ZXJ0TWFwID0ge1xuICAgICAgYmFzZVVybDpcbiAgICAgICAgU29uYW11LmNvbmZpZy5zZXJ2ZXIuYmFzZVVybCA/P1xuICAgICAgICBgaHR0cDovLyR7U29uYW11LmNvbmZpZy5zZXJ2ZXIubGlzdGVuPy5ob3N0ID8/IFwibG9jYWxob3N0XCJ9OiR7U29uYW11LmNvbmZpZy5zZXJ2ZXIubGlzdGVuPy5wb3J0ID8/IDMwMDB9YCxcbiAgICAgIGRpY3RVdGlsczogZGljdFV0aWxzQ29kZSxcbiAgICB9O1xuXG4gICAgZm9yIChjb25zdCB0YXJnZXQgb2YgdGFyZ2V0cykge1xuICAgICAgLy8g7KeA6riIIOqwgOyguOqwgOugpOuKlCDsnbQg7YyM7J287J2AIFNvbmFtdSDsvZTrk5zrsqDsnbTsiqTsnZgg7J2867aA7J6F64uI64ukLlxuICAgICAgLy8g6re465+w642wIGRpc3Qg7IaNIOu5jOuTnOuQnCDshozsiqQg7L2U65OcIO2MjOydvOydtCDtlYTsmpTtlZwg6rKD7J20IOyVhOuLiOqzoCwgc3Jj7JeQ66eMIOyeiOuKlCDthY3siqTtirgg7YyM7J287J20IO2VhOyalO2VqeuLiOuLpC5cbiAgICAgIC8vIOuUsOudvOyEnCAvc3JjL+yXkOyEnCDssL7sirXri4jri6QuXG4gICAgICBjb25zdCBzcmNQYXRoID0gcGF0aC5qb2luKFxuICAgICAgICBpbXBvcnQubWV0YS5kaXJuYW1lLnJlcGxhY2UoXCIvZGlzdC9cIiwgXCIvc3JjL1wiKSxcbiAgICAgICAgYC4uL3NoYXJlZC8ke3RhcmdldH0uc2hhcmVkLnRzLnR4dGAsXG4gICAgICApO1xuICAgICAgaWYgKCEoYXdhaXQgZXhpc3RzKHNyY1BhdGgpKSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIGlmICghKGF3YWl0IGV4aXN0cyhwYXRoLmpvaW4oU29uYW11LmFwcFJvb3RQYXRoLCB0YXJnZXQpKSkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIGBUcmllZCB0byBjb3B5IHNvbmFtdS5zaGFyZWQudHMgdG8gdGFyZ2V0ICcke3RhcmdldH0nIGJ1dCB0aGUgdGFyZ2V0IGRpcmVjdG9yeSBkb2VzIG5vdCBleGlzdC4gUGxlYXNlIGNoZWNrIHlvdXIgcHJvamVjdCBkaXJlY3Rvcnkgc3RydWN0dXJlLmAsXG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGZ1bGxUZXh0ID0gYXdhaXQgcmVhZEZpbGUoc3JjUGF0aCwgXCJ1dGYtOFwiKTtcbiAgICAgIGNvbnN0IGNvbnZlcnRlZFRleHQgPSBPYmplY3QuZW50cmllcyhjb252ZXJ0TWFwKS5yZWR1Y2UoXG4gICAgICAgIChhY2MsIFtrZXksIHZhbHVlXSkgPT4gYWNjLnJlcGxhY2UoYCRbWyR7a2V5fV1dYCwgdmFsdWUpLFxuICAgICAgICBmdWxsVGV4dCxcbiAgICAgICk7XG5cbiAgICAgIC8vIOydtOqxtCDtlITroZzsoJ3tirjsl5AgLnRzIOyGjOyKpCDsvZTrk5wg7YyM7J287J2EIOyDneyEse2VmOuKlCDqsoPsnbTrr4DroZwgc3Jj7J2YIC50cyDqsr3roZzroZwg6rCR64uI64ukLlxuICAgICAgY29uc3QgZGVzdFBhdGggPSBwYXRoLmpvaW4oU29uYW11LmFwcFJvb3RQYXRoLCB0YXJnZXQsIFwic3JjL3NlcnZpY2VzL3NvbmFtdS5zaGFyZWQudHNcIik7XG5cbiAgICAgIC8vIOygleunkCDtmLnsi5zrgpjsp4Drp4wgdGFyZ2V0IOuUlOugie2GoOumrOuKlCDsnojslrTrj4Qgc3JjL3NlcnZpY2VzIOuUlOugie2GoOumrOuKlCDsl4bsnYQg7IiYIOyeiOycvOuvgOuhnCDrr7jrpqwg7IOd7ISx7ZW07KSN64uI64ukLlxuICAgICAgaWYgKCEoYXdhaXQgZXhpc3RzKHBhdGguZGlybmFtZShkZXN0UGF0aCkpKSkge1xuICAgICAgICBhd2FpdCBta2RpcihwYXRoLmRpcm5hbWUoZGVzdFBhdGgpLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICAgICAgY29uc29sZS53YXJuKGBDcmVhdGVkIGRpcmVjdG9yeSAnJHtwYXRoLmRpcm5hbWUoZGVzdFBhdGgpfScgYmVjYXVzZSBpdCBkaWQgbm90IGV4aXN0LmApO1xuICAgICAgfVxuXG4gICAgICAvLyDtjIzsnbzsnbQg7J2066+4IOyhtOyerO2VmOuptCDqsbTrhIjrnIHri4jri6QuXG4gICAgICAvLyBzb25hbXUuc2hhcmVkLnRz64qUIO2UhOuhnOygne2KuOyXkOyEnCDsnpDsnKDroa3qsowg7Luk7Iqk7YSw66eI7J207KeV7ZWgIOyImCDsnojslrTslbwg7ZWY66+A66GcLFxuICAgICAgLy8g7LWc7LSIIDHtmozrp4wg7IOd7ISx7ZWY6rOgIOydtO2bhOyXkOuKlCDrja7slrTsk7Dsp4Ag7JWK7Iq164uI64ukLlxuICAgICAgLy8g7YWc7ZSM66a/IOuCtOyaqSgkW1tkaWN0VXRpbHNdXSDrk7Ep7J20IOuzgOqyveuQmOyXiOydhCDrlYwg67CY7JiB7J20IO2VhOyalO2VmOuptCxcbiAgICAgIC8vIO2VtOuLuSDtjIzsnbzsnYQg7IKt7KCc7ZWcIOuSpCBgcG5wbSBzb25hbXUgc3luY2DroZwg7J6s7IOd7ISx7ZWY66m0IOuQqeuLiOuLpC5cbiAgICAgIGlmIChhd2FpdCBleGlzdHMoZGVzdFBhdGgpKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICBhd2FpdCB3cml0ZUZpbGUoZGVzdFBhdGgsIGNvbnZlcnRlZFRleHQpO1xuICAgICAgIWlzVGVzdCgpICYmXG4gICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgIGNoYWxrLmJvbGQoXCJDb3BpZWQ6IFwiKSArIGNoYWxrLmJsdWUocGF0aC5yZWxhdGl2ZShTb25hbXUuYXBwUm9vdFBhdGgsIGRlc3RQYXRoKSksXG4gICAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgYXV0b2xvYWRUeXBlcygpIHtcbiAgICB0aGlzLnR5cGVzID0gYXdhaXQgbG9hZFR5cGVzKCk7XG4gIH1cblxuICBhc3luYyBhdXRvbG9hZE1vZGVscygpIHtcbiAgICB0aGlzLm1vZGVscyA9IGF3YWl0IGxvYWRNb2RlbHMoKTtcbiAgfVxuXG4gIGFzeW5jIGF1dG9sb2FkQXBpcygpIHtcbiAgICB0aGlzLmFwaXMgPSBhd2FpdCBsb2FkQXBpcygpO1xuICB9XG5cbiAgYXN5bmMgYXV0b2xvYWRXb3JrZmxvd3MoKSB7XG4gICAgdGhpcy53b3JrZmxvd3MgPSBhd2FpdCBsb2FkV29ya2Zsb3dzKCk7XG4gICAgYXdhaXQgU29uYW11LndvcmtmbG93cy5zeW5jaHJvbml6ZSh0aGlzLndvcmtmbG93cyk7XG4gIH1cblxuICBhc3luYyBhdXRvbG9hZFNTUlJvdXRlcygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBzc3JDb25maWdQYXRoID0gcGF0aC5qb2luKFNvbmFtdS5hcGlSb290UGF0aCwgXCJzcmMvc3NyXCIpO1xuXG4gICAgLy8g6riw7KG0IHJvdXRlcyDstIjquLDtmZRcbiAgICBjb25zdCB7IGNsZWFyU1NSUm91dGVzIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi9zc3JcIik7XG4gICAgY2xlYXJTU1JSb3V0ZXMoKTtcblxuICAgIC8vIHNzciDtj7TrjZQg7JeG7Jy866m0IOyKpO2CtVxuICAgIGlmICghKGF3YWl0IGV4aXN0cyhzc3JDb25maWdQYXRoKSkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBzc3Ig7Y+0642UIOyViOydmCDrqqjrk6AgLnRzIO2MjOydvCDroZzrk5xcbiAgICBjb25zdCB7IGdsb2JBc3luYyB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vdXRpbHMvYXN5bmMtdXRpbHNcIik7XG4gICAgY29uc3QgeyBpbXBvcnRNZW1iZXJzIH0gPSBhd2FpdCBpbXBvcnQoXCIuLi91dGlscy9lc20tdXRpbHNcIik7XG4gICAgY29uc3QgeyBydW50aW1lUGF0aCB9ID0gYXdhaXQgaW1wb3J0KFwiLi4vdXRpbHMvcGF0aC11dGlsc1wiKTtcblxuICAgIC8vIHJ1bnRpbWVQYXRo66W8IOyCrOyaqe2VmOyXrCDqsJzrsJwv7ZSE66Gc642V7IWYIO2ZmOqyveyXkCDrp57ripQg7ZmV7J6l7J6QIOyymOumrFxuICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgZ2xvYkFzeW5jKHBhdGguam9pbihzc3JDb25maWdQYXRoLCBydW50aW1lUGF0aChcIioqLyoudHNcIikpKTtcblxuICAgIGZvciAoY29uc3QgZmlsZSBvZiBmaWxlcykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgLy8gaW1wb3J0TWVtYmVyc+ulvCDsgqzsmqntlZjrqbQg7YyM7J287J2YIHNpZGUgZWZmZWN0KHJlZ2lzdGVyU1NSIO2YuOy2nCnqsIAg7Iuk7ZaJ65CoXG4gICAgICAgIGF3YWl0IGltcG9ydE1lbWJlcnMoZmlsZSk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYEZhaWxlZCB0byBsb2FkIFNTUiByb3V0ZTogJHtmaWxlfWAsIGUpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiDsi6TsoJwg7Iux7YGs66W8IOyImO2Wie2VmOuKlCDrs7jssrTsnoXri4jri6QuXG4gICAqIOuzgOqyveuQnCDtjIzsnbzrk6TsnYQg7YOA7J6F67OE66GcIOu2hOulmO2VmOqzoCDqsIEg7YOA7J6F7JeQIOunnuuKlCDslaHshZjsnYQg7Iuk7ZaJ7ZWp64uI64ukLlxuICAgKiBAcGFyYW0gZGlmZkZpbGVQYXRocyAtIOuzgOqyveuQnCDtjIzsnbzrk6TsnZgg7KCI64yAIOqyveuhnCDrqqnroZ1cbiAgICogQHJldHVybnMgZGlmZlR5cGVzIC0g67OA6rK965CcIO2MjOydvOydmCDtg4DsnoUg66qp66GdIChlbnRpdHksIHR5cGVzLCBtb2RlbCDrk7EpXG4gICAqL1xuICBhc3luYyBkb1N5bmNBY3Rpb25zKGRpZmZGaWxlUGF0aHM6IEFic29sdXRlUGF0aFtdKTogUHJvbWlzZTx7IGRpZmZUeXBlczogc3RyaW5nW10gfT4ge1xuICAgIGNvbnN0IGRpZmZHcm91cHMgPSB0aGlzLmNhbGN1bGF0ZURpZmZHcm91cHMoZGlmZkZpbGVQYXRocyk7XG4gICAgY29uc3QgZGlmZlR5cGVzID0gT2JqZWN0LmtleXMoZGlmZkdyb3VwcykgYXMgRmlsZVR5cGVbXTtcblxuICAgIC8vIFNpbmdsZSBzb3VyY2Ugb2YgdHJ1dGjqsIAg67OA6rK965CcIOqyveyasFxuICAgIGlmIChkaWZmVHlwZXMuaW5jbHVkZXMoXCJlbnRpdHlcIikgfHwgZGlmZlR5cGVzLmluY2x1ZGVzKFwidHlwZXNcIikpIHtcbiAgICAgIGF3YWl0IHRoaXMuaGFuZGxlVHJ1dGhTb3VyY2VDaGFuZ2VzKGRpZmZHcm91cHMsIGRpZmZUeXBlcyk7XG4gICAgfVxuXG4gICAgLy8g7YOA6rKf7Jy866GcIOuzteyCrOunjCDtlZjrqbQg65CY64qUIO2MjOydvOydtCDrs4Dqsr3rkJwg6rK97JqwXG4gICAgaWYgKFxuICAgICAgZGlmZlR5cGVzLmluY2x1ZGVzKFwidHlwZXNcIikgfHxcbiAgICAgIGRpZmZUeXBlcy5pbmNsdWRlcyhcImZ1bmN0aW9uc1wiKSB8fFxuICAgICAgZGlmZlR5cGVzLmluY2x1ZGVzKFwiZ2VuZXJhdGVkXCIpXG4gICAgKSB7XG4gICAgICBhd2FpdCB0aGlzLmhhbmRsZVN5bmNhYmxlRmlsZUNoYW5nZXMoZGlmZkdyb3Vwcyk7XG4gICAgfVxuXG4gICAgLy8g66qo6424L+2UhOugiOyehCDqtaztmITssrTqsIAg67OA6rK965CcIOqyveyasFxuICAgIGlmIChkaWZmVHlwZXMuaW5jbHVkZXMoXCJtb2RlbFwiKSB8fCBkaWZmVHlwZXMuaW5jbHVkZXMoXCJmcmFtZVwiKSkge1xuICAgICAgYXdhaXQgdGhpcy5oYW5kbGVJbXBsZW1lbnRhdGlvbkNoYW5nZXMoZGlmZkdyb3Vwcyk7XG4gICAgfVxuXG4gICAgLy8g7ISk7KCVIO2MjOydvOydtCDrs4Dqsr3rkJwg6rK97JqwXG4gICAgaWYgKGRpZmZUeXBlcy5pbmNsdWRlcyhcImNvbmZpZ1wiKSkge1xuICAgICAgYXdhaXQgU3luY2VyQWN0aW9ucy5hY3Rpb25TeW5jQ29uZmlnKCk7XG4gICAgfVxuXG4gICAgLy8g7JuM7YGs7ZSM66Gc7JqwIO2MjOydvOydtCDrs4Dqsr3rkJwg6rK97JqwXG4gICAgaWYgKGRpZmZUeXBlcy5pbmNsdWRlcyhcIndvcmtmbG93XCIpKSB7XG4gICAgICBhd2FpdCB0aGlzLmF1dG9sb2FkV29ya2Zsb3dzKCk7XG4gICAgfVxuXG4gICAgLy8gaTE4biDqtIDroKgg7YyM7J287J20IOuzgOqyveuQnCDqsr3smrBcbiAgICAvLyAtIGkxOG4vKi50czogbG9jYWxlIOuyiOyXrSDtjIzsnbxcbiAgICAvLyAtIGVudGl0eS5qc29uOiBlbnRpdHkgbGFiZWxzXG4gICAgLy8gLSBzb25hbXUuY29uZmlnLnRzOiBpMThuIOyEpOyglSAoZGVmYXVsdExvY2FsZSwgc3VwcG9ydGVkTG9jYWxlcylcbiAgICBpZiAoXG4gICAgICBkaWZmVHlwZXMuaW5jbHVkZXMoXCJpMThuXCIpIHx8XG4gICAgICBkaWZmVHlwZXMuaW5jbHVkZXMoXCJlbnRpdHlcIikgfHxcbiAgICAgIGRpZmZUeXBlcy5pbmNsdWRlcyhcImNvbmZpZ1wiKVxuICAgICkge1xuICAgICAgYXdhaXQgdGhpcy5zeW5jU0QoKTtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgZGlmZlR5cGVzLFxuICAgIH07XG4gIH1cblxuICBjYWxjdWxhdGVEaWZmR3JvdXBzKGRpZmZGaWxlczogQWJzb2x1dGVQYXRoW10pOiBEaWZmR3JvdXBzIHtcbiAgICBjb25zdCBmaWxlVHlwZXMgPSBPYmplY3Qua2V5cyhjaGVja3N1bVBhdHRlcm5Hcm91cCkgYXMgRmlsZVR5cGVbXTtcblxuICAgIHJldHVybiBncm91cChkaWZmRmlsZXMsIChmaWxlUGF0aCkgPT4ge1xuICAgICAgLy8g7KCI64yAIOqyveuhnOyXkOyEnCBzcmMv66GcIOyLnOyeke2VmOuKlCDsg4HrjIAg6rK966GcIOu2gOu2hOydhCDstpTstpztlanri4jri6QuXG4gICAgICBjb25zdCBzcmNJbmRleCA9IGZpbGVQYXRoLmluZGV4T2YoXCIvc3JjL1wiKTtcbiAgICAgIGlmIChzcmNJbmRleCA9PT0gLTEpIHJldHVybiBcInVua25vd25cIjtcbiAgICAgIGNvbnN0IHJlbGF0aXZlUGF0aCA9IGZpbGVQYXRoLnNsaWNlKHNyY0luZGV4ICsgMSk7IC8vIFwic3JjLy4uLlwiIO2Yle2DnFxuXG4gICAgICBmb3IgKGNvbnN0IGZpbGVUeXBlIG9mIGZpbGVUeXBlcykge1xuICAgICAgICBpZiAobWluaW1hdGNoKHJlbGF0aXZlUGF0aCwgY2hlY2tzdW1QYXR0ZXJuR3JvdXBbZmlsZVR5cGVdKSkge1xuICAgICAgICAgIHJldHVybiBmaWxlVHlwZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIFwidW5rbm93blwiO1xuICAgIH0pIGFzIHVua25vd24gYXMgRGlmZkdyb3VwcztcbiAgfVxuXG4gIGFzeW5jIGhhbmRsZVRydXRoU291cmNlQ2hhbmdlcyhkaWZmR3JvdXBzOiBEaWZmR3JvdXBzLCBkaWZmVHlwZXM6IHN0cmluZ1tdKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgTmFpdGUudChcImhhbmRsZVRydXRoU291cmNlQ2hhbmdlc1wiLCB7IGRpZmZHcm91cHMsIGRpZmZUeXBlcyB9KTtcblxuICAgIGF3YWl0IEVudGl0eU1hbmFnZXIucmVsb2FkKCk7XG5cbiAgICAvLyB0eXBlcyDsg53shLEoZW50aXR5IOyDiOuhnCDstpTqsIDrkJwg6rK97JqwKVxuICAgIC8vIHBhcmVudElk6rCAIOyXhuqzoCwgdHlwZXPqsIAg7JeG64qUIOqyveyasOyXkOunjCDsg53shLFcbiAgICBjb25zdCBlbnRpdHlQYXRoID0gZGlmZkdyb3Vwcy5lbnRpdHk/LmF0KDApO1xuICAgIGlmIChlbnRpdHlQYXRoICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbnN0IGVudGl0eUlkID0gRW50aXR5TWFuYWdlci5nZXRFbnRpdHlJZEZyb21QYXRoKGVudGl0eVBhdGgpO1xuICAgICAgY29uc3QgZW50aXR5ID0gRW50aXR5TWFuYWdlci5nZXQoZW50aXR5SWQpO1xuICAgICAgLy8g7ZSE66Gc7KCd7Yq47JeQIOyDneyEseuQmOyWtOyVvCDtlZjripQgLnRzIO2MjOydvOydmCDqsr3roZzsnoXri4jri6QuXG4gICAgICBjb25zdCB0eXBlRmlsZVBhdGggPSBwYXRoLmpvaW4oXG4gICAgICAgIFNvbmFtdS5hcGlSb290UGF0aCxcbiAgICAgICAgYHNyYy9hcHBsaWNhdGlvbi8ke2VudGl0eS5uYW1lcy5mc30vJHtlbnRpdHkubmFtZXMuZnN9LnR5cGVzLnRzYCxcbiAgICAgICk7XG4gICAgICBpZiAoZW50aXR5LnBhcmVudElkID09PSB1bmRlZmluZWQgJiYgIShhd2FpdCBleGlzdHModHlwZUZpbGVQYXRoKSkpIHtcbiAgICAgICAgYXdhaXQgZ2VuZXJhdGVUZW1wbGF0ZShcImluaXRfdHlwZXNcIiwgeyBlbnRpdHlJZCB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBhd2FpdCBTeW5jZXJBY3Rpb25zLmFjdGlvbkdlbmVyYXRlU2NoZW1hcygpO1xuXG4gICAgZGlmZkdyb3Vwcy5nZW5lcmF0ZWQgPSB1bmlxdWUoW1xuICAgICAgLi4uKGRpZmZHcm91cHMuZ2VuZXJhdGVkID8/IFtdKSxcbiAgICAgIHBhdGguam9pbihTb25hbXUuYXBpUm9vdFBhdGgsIFwic3JjL2FwcGxpY2F0aW9uL3NvbmFtdS5nZW5lcmF0ZWQudHNcIikgYXMgQWJzb2x1dGVQYXRoLFxuICAgIF0pO1xuICAgIGRpZmZUeXBlcy5wdXNoKFwiZ2VuZXJhdGVkXCIpO1xuICB9XG5cbiAgYXN5bmMgaGFuZGxlU3luY2FibGVGaWxlQ2hhbmdlcyhkaWZmR3JvdXBzOiBEaWZmR3JvdXBzKTogUHJvbWlzZTxGaWxlVHlwZVtdPiB7XG4gICAgY29uc3QgdHNQYXRocyA9IHVuaXF1ZShbXG4gICAgICAuLi4oZGlmZkdyb3Vwcy50eXBlcyA/PyBbXSksXG4gICAgICAuLi4oZGlmZkdyb3Vwcy5mdW5jdGlvbnMgPz8gW10pLFxuICAgICAgLi4uKGRpZmZHcm91cHMuZ2VuZXJhdGVkID8/IFtdKSxcbiAgICBdKTtcbiAgICBOYWl0ZS50KFwiaGFuZGxlU3luY2FibGVGaWxlQ2hhbmdlc1wiLCB7IGRpZmZHcm91cHMgfSk7XG5cbiAgICAvLyBjb25zb2xlLmxvZyhcbiAgICAvLyAgIGNoYWxrLmdyYXkoXG4gICAgLy8gICAgIGBbUHJvY2Vzc2luZ10gSGFuZGxpbmcgdHlwZXMvZnVuY3Rpb25zL2dlbmVyYXRlZCBjaGFuZ2VzOiAke3RzUGF0aHMubWFwKChwKSA9PiBwYXRoLnJlbGF0aXZlKFNvbmFtdS5hcGlSb290UGF0aCwgcCkpLmpvaW4oXCIsIFwiKX1gXG4gICAgLy8gICApXG4gICAgLy8gKTtcblxuICAgIGF3YWl0IFN5bmNlckFjdGlvbnMuYWN0aW9uU3luY0ZpbGVzVG9UYXJnZXRzKHRzUGF0aHMpO1xuXG4gICAgcmV0dXJuIFtdO1xuICB9XG5cbiAgYXN5bmMgaGFuZGxlSW1wbGVtZW50YXRpb25DaGFuZ2VzKGRpZmZHcm91cHM6IERpZmZHcm91cHMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBOYWl0ZS50KFwiaGFuZGxlSW1wbGVtZW50YXRpb25DaGFuZ2VzXCIsIHsgZGlmZkdyb3VwcyB9KTtcbiAgICBjb25zdCBtZXJnZWRHcm91cCA9IFsuLi4oZGlmZkdyb3Vwcy5tb2RlbCA/PyBbXSksIC4uLihkaWZmR3JvdXBzLmZyYW1lID8/IFtdKV07XG5cbiAgICAvLyBjb25zb2xlLmxvZyhcbiAgICAvLyAgIGNoYWxrLmdyYXkoXG4gICAgLy8gICAgIGBbUHJvY2Vzc2luZ10gSGFuZGxpbmcgbW9kZWwvZnJhbWUgY2hhbmdlczogJHttZXJnZWRHcm91cC5tYXAoKHApID0+IHBhdGgucmVsYXRpdmUoU29uYW11LmFwaVJvb3RQYXRoLCBwKSkuam9pbihcIiwgXCIpfWBcbiAgICAvLyAgIClcbiAgICAvLyApO1xuXG4gICAgLy8gZ2VuZXJhdGVkX2h0dHAudGVtcGxhdGUudHPsl5DshJwgc3luY2VyLnR5cGVz66W8IOyUgeuLiOuLpC5cbiAgICAvLyBzZXJ2aWNlLnRlbXBsYXRlLnRz7JeQ7IScIHN5bmNlci5hcGlz66W8IOyUgeuLiOuLpC5cbiAgICBhd2FpdCB0aGlzLmF1dG9sb2FkTW9kZWxzKCk7XG4gICAgYXdhaXQgdGhpcy5hdXRvbG9hZFR5cGVzKCk7XG4gICAgYXdhaXQgdGhpcy5hdXRvbG9hZEFwaXMoKTtcblxuICAgIGNvbnN0IHBhcmFtczoge1xuICAgICAgbmFtZXNSZWNvcmQ6IEVudGl0eU5hbWVzUmVjb3JkO1xuICAgIH1bXSA9IG1lcmdlZEdyb3VwLm1hcCgobW9kZWxQYXRoKSA9PiB7XG4gICAgICBpZiAobW9kZWxQYXRoLmVuZHNXaXRoKFwiLm1vZGVsLnRzXCIpKSB7XG4gICAgICAgIGNvbnN0IGVudGl0eUlkID0gRW50aXR5TWFuYWdlci5nZXRFbnRpdHlJZEZyb21QYXRoKG1vZGVsUGF0aCk7XG4gICAgICAgIGFzc2VydChlbnRpdHlJZCk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgbmFtZXNSZWNvcmQ6IEVudGl0eU1hbmFnZXIuZ2V0TmFtZXNGcm9tSWQoZW50aXR5SWQpLFxuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgaWYgKG1vZGVsUGF0aC5lbmRzV2l0aChcIi5mcmFtZS50c1wiKSkge1xuICAgICAgICBjb25zdCBbLCBmcmFtZU5hbWVdID0gbW9kZWxQYXRoLm1hdGNoKC8uK1xcLyguKylcXC5mcmFtZVxcLnRzJC8pID8/IFtdO1xuICAgICAgICBhc3NlcnQoZnJhbWVOYW1lKTtcbiAgICAgICAgLy8gZnJhbWVOYW1l7J2EIFBhc2NhbENhc2XroZwg67OA7ZmYIChkYXNoYm9hcmQgLT4gRGFzaGJvYXJkKVxuICAgICAgICBjb25zdCBmcmFtZUlkID0gaW5mbGVjdGlvbi5jYW1lbGl6ZShmcmFtZU5hbWUpO1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIG5hbWVzUmVjb3JkOiBFbnRpdHlNYW5hZ2VyLmdldE5hbWVzRnJvbUlkKGZyYW1lSWQpLFxuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgdGhyb3cgbmV3IEVycm9yKFwibm90IHJlYWNoYWJsZVwiKTtcbiAgICB9KTtcblxuICAgIGF3YWl0IFN5bmNlckFjdGlvbnMuYWN0aW9uR2VuZXJhdGVTZXJ2aWNlcyhwYXJhbXMpO1xuICAgIGF3YWl0IFN5bmNlckFjdGlvbnMuYWN0aW9uR2VuZXJhdGVIdHRwcygpO1xuICAgIGF3YWl0IFN5bmNlckFjdGlvbnMuYWN0aW9uR2VuZXJhdGVTc3IoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiDso7zslrTsp4Qg7JeU7Yuw7Yuw7JmAIO2FnO2UjOumvyDtgqTsl5Ag64yA7ZW0LCDsg53shLHrkJwg7L2U65Oc6rCAIOyhtOyerO2VmOuKlOyngCDtmZXsnbjtlanri4jri6QuXG4gICAqIEBwYXJhbSBlbnRpdHlJZCDsl5Tti7Dti7AgSURcbiAgICogQHBhcmFtIHRlbXBsYXRlS2V5IO2FnO2UjOumvyDtgqRcbiAgICogQHBhcmFtIGVudW1JZCDsl7TqsbDtmJUgSURcbiAgICogQHJldHVybnMg7IOd7ISx65CcIOy9lOuTnOqwgCDsobTsnqztlZjripTsp4Ag7Jes67aAXG4gICAqL1xuICBhc3luYyBjaGVja0V4aXN0c0dlbkNvZGUoXG4gICAgZW50aXR5SWQ6IHN0cmluZyxcbiAgICB0ZW1wbGF0ZUtleTogVGVtcGxhdGVLZXksXG4gICAgZW51bUlkPzogc3RyaW5nLFxuICApOiBQcm9taXNlPHsgc3ViUGF0aDogc3RyaW5nOyBmdWxsUGF0aDogc3RyaW5nOyBpc0V4aXN0czogYm9vbGVhbiB9PiB7XG4gICAgY29uc3QgeyB0YXJnZXQsIHBhdGg6IGdlblBhdGggfSA9IFRlbXBsYXRlTWFuYWdlci5nZXQodGVtcGxhdGVLZXkpLmdldFRhcmdldEFuZFBhdGgoXG4gICAgICBFbnRpdHlNYW5hZ2VyLmdldE5hbWVzRnJvbUlkKGVudGl0eUlkKSxcbiAgICAgIGVudW1JZCxcbiAgICApO1xuXG4gICAgY29uc3Qgc3ViUGF0aCA9IHBhdGguam9pbih0YXJnZXQsIGdlblBhdGgpO1xuICAgIGNvbnN0IGZ1bGxQYXRoID0gcGF0aC5qb2luKFNvbmFtdS5hcHBSb290UGF0aCwgc3ViUGF0aCk7XG4gICAgcmV0dXJuIHtcbiAgICAgIHN1YlBhdGgsXG4gICAgICBmdWxsUGF0aCxcbiAgICAgIGlzRXhpc3RzOiBhd2FpdCBleGlzdHMoZnVsbFBhdGgpLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICog7KO87Ja07KeEIOyXlO2LsO2LsOyZgCDsl7TqsbDtmJXsl5Ag64yA7ZW0LCDsg53shLHrkJwg7L2U65Oc6rCAIOyhtOyerO2VmOuKlOyngCDtmZXsnbjtlanri4jri6QuXG4gICAqIEBwYXJhbSBlbnRpdHlJZCDsl5Tti7Dti7AgSURcbiAgICogQHBhcmFtIGVudW1zIOyXtOqxsO2YlSDroIjsnbTruJRcbiAgICogQHJldHVybnMg7IOd7ISx65CcIOy9lOuTnOqwgCDsobTsnqztlZjripTsp4Ag7Jes67aAXG4gICAqL1xuICBhc3luYyBjaGVja0V4aXN0cyhcbiAgICBlbnRpdHlJZDogc3RyaW5nLFxuICAgIGVudW1zOiB7XG4gICAgICBbbmFtZTogc3RyaW5nXTogei5ab2RFbnVtO1xuICAgIH0sXG4gICk6IFByb21pc2U8UmVjb3JkPGAke1RlbXBsYXRlS2V5fSR7c3RyaW5nfWAsIGJvb2xlYW4+PiB7XG4gICAgY29uc3Qga2V5czogVGVtcGxhdGVLZXlbXSA9IFRlbXBsYXRlS2V5Lm9wdGlvbnM7XG4gICAgY29uc3QgbmFtZXMgPSBFbnRpdHlNYW5hZ2VyLmdldE5hbWVzRnJvbUlkKGVudGl0eUlkKTtcbiAgICBjb25zdCBlbnVtc0tleXMgPSBPYmplY3Qua2V5cyhlbnVtcykuZmlsdGVyKChuYW1lKSA9PiBuYW1lICE9PSBuYW1lcy5jb25zdGFudCk7XG5cbiAgICByZXR1cm4gYXdhaXQgcmVkdWNlQXN5bmMoXG4gICAgICBrZXlzLFxuICAgICAgYXN5bmMgKHJlc3VsdCwga2V5KSA9PiB7XG4gICAgICAgIGNvbnN0IHRwbCA9IFRlbXBsYXRlTWFuYWdlci5nZXQoa2V5KTtcbiAgICAgICAgaWYgKGtleS5zdGFydHNXaXRoKFwidmlld19lbnVtc1wiKSkge1xuICAgICAgICAgIGF3YWl0IG1hcEFzeW5jKGVudW1zS2V5cywgYXN5bmMgKGNvbXBvbmVudElkKSA9PiB7XG4gICAgICAgICAgICBjb25zdCB7IHRhcmdldCwgcGF0aDogcCB9ID0gdHBsLmdldFRhcmdldEFuZFBhdGgobmFtZXMsIGNvbXBvbmVudElkKTtcbiAgICAgICAgICAgIHJlc3VsdFtgJHtrZXl9X18ke2NvbXBvbmVudElkfWBdID0gYXdhaXQgZXhpc3RzKFxuICAgICAgICAgICAgICBwYXRoLmpvaW4oU29uYW11LmFwcFJvb3RQYXRoLCB0YXJnZXQsIHApLFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgeyB0YXJnZXQsIHBhdGg6IHAgfSA9IHRwbC5nZXRUYXJnZXRBbmRQYXRoKG5hbWVzKTtcbiAgICAgICAgY29uc3QgeyB0YXJnZXRzIH0gPSBTb25hbXUuY29uZmlnLnN5bmM7XG4gICAgICAgIGlmICh0YXJnZXQuaW5jbHVkZXMoXCI6dGFyZ2V0XCIpKSB7XG4gICAgICAgICAgYXdhaXQgbWFwQXN5bmModGFyZ2V0cywgYXN5bmMgKHQpID0+IHtcbiAgICAgICAgICAgIHJlc3VsdFtgJHtrZXl9X18ke3R9YF0gPSBhd2FpdCBleGlzdHMoXG4gICAgICAgICAgICAgIHBhdGguam9pbihTb25hbXUuYXBwUm9vdFBhdGgsIHRhcmdldC5yZXBsYWNlKFwiOnRhcmdldFwiLCB0KSwgcCksXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJlc3VsdFtrZXldID0gYXdhaXQgZXhpc3RzKHBhdGguam9pbihTb25hbXUuYXBwUm9vdFBhdGgsIHRhcmdldCwgcCkpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgIH0sXG4gICAgICB7fSBhcyBSZWNvcmQ8YCR7VGVtcGxhdGVLZXl9JHtzdHJpbmd9YCwgYm9vbGVhbj4sXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiDtlZjsnITtmLjtmZjsmqkg7ZSE66Gd7IucIOuplOyGjOuTnOyeheuLiOuLpC5cbiAgICovXG4gIGFzeW5jIGNyZWF0ZUVudGl0eShmb3JtOiBUZW1wbGF0ZU9wdGlvbnNbXCJlbnRpdHlcIl0pIHtcbiAgICByZXR1cm4gYXdhaXQgY3JlYXRlRW50aXR5KGZvcm0pO1xuICB9XG5cbiAgLyoqXG4gICAqIO2VmOychO2YuO2ZmOyaqSDtlITroZ3si5wg66mU7IaM65Oc7J6F64uI64ukLlxuICAgKi9cbiAgYXN5bmMgZGVsRW50aXR5KGVudGl0eUlkOiBzdHJpbmcpOiBQcm9taXNlPHsgZGVsUGF0aHM6IHN0cmluZ1tdIH0+IHtcbiAgICByZXR1cm4gYXdhaXQgZGVsRW50aXR5KGVudGl0eUlkKTtcbiAgfVxuXG4gIC8qKlxuICAgKiDtlZjsnITtmLjtmZjsmqkg7ZSE66Gd7IucIOuplOyGjOuTnOyeheuLiOuLpC5cbiAgICovXG4gIGFzeW5jIGdlbmVyYXRlVGVtcGxhdGU8VCBleHRlbmRzIFRlbXBsYXRlS2V5PihcbiAgICBrZXk6IFQsXG4gICAgdGVtcGxhdGVPcHRpb25zOiBUZW1wbGF0ZU9wdGlvbnNbVF0sXG4gICAgX2dlbmVyYXRlT3B0aW9ucz86IEdlbmVyYXRlT3B0aW9ucyxcbiAgKTogUHJvbWlzZTxBYnNvbHV0ZVBhdGhbXT4ge1xuICAgIHJldHVybiBhd2FpdCBnZW5lcmF0ZVRlbXBsYXRlKGtleSwgdGVtcGxhdGVPcHRpb25zLCBfZ2VuZXJhdGVPcHRpb25zKTtcbiAgfVxuXG4gIC8qKlxuICAgKiDtlZjsnITtmLjtmZjsmqkg7ZSE66Gd7IucIOuplOyGjOuTnOyeheuLiOuLpC5cbiAgICovXG4gIGFzeW5jIHJlbmRlclRlbXBsYXRlPFQgZXh0ZW5kcyBrZXlvZiBUZW1wbGF0ZU9wdGlvbnM+KFxuICAgIGtleTogVCxcbiAgICB0ZW1wbGF0ZU9wdGlvbnM6IFRlbXBsYXRlT3B0aW9uc1tUXSxcbiAgKTogUHJvbWlzZTxQYXRoQW5kQ29kZVtdPiB7XG4gICAgcmV0dXJuIGF3YWl0IHJlbmRlclRlbXBsYXRlKGtleSwgdGVtcGxhdGVPcHRpb25zKTtcbiAgfVxuXG4gIC8qKlxuICAgKiDtlZjsnITtmLjtmZjsmqkg7ZSE66Gd7IucIOuplOyGjOuTnOyeheuLiOuLpC5cbiAgICovXG4gIGFzeW5jIHJlbmV3Q2hlY2tzdW1zKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiBhd2FpdCByZW5ld0NoZWNrc3VtcygpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNEKFNvbmFtdSBEaWN0aW9uYXJ5KSDthZztlIzrpr/snYQg7IOd7ISx7ZWp64uI64ukLlxuICAgKi9cbiAgYXN5bmMgc3luY1NEKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHsgdGFyZ2V0cyB9ID0gU29uYW11LmNvbmZpZy5zeW5jO1xuICAgIGNvbnN0IGkxOG5Db25maWcgPSBTb25hbXUuY29uZmlnLmkxOG47XG5cbiAgICBjb25zdCB0YXJnZXRMaXN0ID0gW1wiYXBpXCIsIC4uLnRhcmdldHNdIGFzIChcImFwaVwiIHwgXCJ3ZWJcIiB8IFwiYXBwXCIpW107XG5cbiAgICBjb25zdCBhcGlJMThuRGlyID0gcGF0aC5qb2luKFNvbmFtdS5hcHBSb290UGF0aCwgU29uYW11LmNvbmZpZy5hcGkuZGlyLCBcInNyYy9pMThuXCIpO1xuXG4gICAgZm9yIChjb25zdCB0YXJnZXQgb2YgdGFyZ2V0TGlzdCkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgLy8gd2ViL2FwcOydmCDqsr3smrAgbG9jYWxlIO2MjOydvOuTpOydhCBhcGnsl5DshJwg67O17IKsXG4gICAgICAgIGlmICh0YXJnZXQgIT09IFwiYXBpXCIpIHtcbiAgICAgICAgICBhd2FpdCB0aGlzLnN5bmNMb2NhbGVGaWxlcyh0YXJnZXQsIGFwaUkxOG5EaXIsIGkxOG5Db25maWcuc3VwcG9ydGVkTG9jYWxlcyk7XG4gICAgICAgIH1cblxuICAgICAgICBhd2FpdCBnZW5lcmF0ZVRlbXBsYXRlKFwic2RcIiwgeyB0YXJnZXQgfSwgeyBvdmVyd3JpdGU6IHRydWUgfSk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYEZhaWxlZCB0byBnZW5lcmF0ZSBTRCB0ZW1wbGF0ZSBmb3IgJHt0YXJnZXR9OmAsIGUpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBhcGnsnZggbG9jYWxlIO2MjOydvOydhCB3ZWIvYXBw7Jy866GcIOuzteyCrO2VqeuLiOuLpC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgc3luY0xvY2FsZUZpbGVzKFxuICAgIHRhcmdldDogc3RyaW5nLFxuICAgIGFwaUkxOG5EaXI6IHN0cmluZyxcbiAgICBsb2NhbGVzOiBzdHJpbmdbXSxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgdGFyZ2V0STE4bkRpciA9IHBhdGguam9pbihTb25hbXUuYXBwUm9vdFBhdGgsIHRhcmdldCwgXCJzcmMvaTE4blwiKTtcblxuICAgIC8vIOuUlOugie2GoOumrOqwgCDsl4bsnLzrqbQg7IOd7ISxXG4gICAgYXdhaXQgbWtkaXIodGFyZ2V0STE4bkRpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG5cbiAgICBmb3IgKGNvbnN0IGxvY2FsZSBvZiBsb2NhbGVzKSB7XG4gICAgICBjb25zdCBzb3VyY2VGaWxlID0gcGF0aC5qb2luKGFwaUkxOG5EaXIsIGAke2xvY2FsZX0udHNgKTtcbiAgICAgIGNvbnN0IHRhcmdldEZpbGUgPSBwYXRoLmpvaW4odGFyZ2V0STE4bkRpciwgYCR7bG9jYWxlfS50c2ApO1xuXG4gICAgICBjb25zdCBzeW5jSGVhZGVyID0gW1xuICAgICAgICBcIi8qKlwiLFxuICAgICAgICBcIiAqIEBnZW5lcmF0ZWRcIixcbiAgICAgICAgXCIgKiBBUEnsl5DshJwg64+Z6riw7ZmU65CcIO2MjOydvOyeheuLiOuLpC4g7KeB7KCRIOyImOygle2VmOyngCDrp4jshLjsmpQuXCIsXG4gICAgICAgIFwiICovXCIsXG4gICAgICBdLmpvaW4oXCJcXG5cIik7XG4gICAgICBhd2FpdCBjb3B5RmlsZVdpdGhSZXBsYWNlQ29yZVRvU2hhcmVkKHNvdXJjZUZpbGUsIHRhcmdldEZpbGUsIHN5bmNIZWFkZXIpO1xuICAgICAgIWlzVGVzdCgpICYmXG4gICAgICAgIGNvbnNvbGUubG9nKGNoYWxrLmJvbGQoXCJDb3BpZWQ6IFwiKSArIGNoYWxrLmN5YW4oYCR7dGFyZ2V0fS9zcmMvaTE4bi8ke2xvY2FsZX0udHNgKSk7XG4gICAgfVxuICB9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O2tCQVltRDtjQUNaO3NCQUNrQjtxQkFFZTthQUNqQzt3QkFFd0I7YUFFbEI7bUJBRWdCO29CQUNWO2tCQUNOO2dCQUMrQjtxQkFFWDtnQkFDVztzQkFDUjt5QkFDTjtxQkFDZ0M7cUJBRWI7c0JBRS9CO0NBTXJDLFNBQWIsTUFBb0I7RUFDbEIsT0FBbUIsRUFBRTtFQUNyQixRQUFxQixFQUFFO0VBQ3ZCLFNBQXVCLEVBQUU7RUFDekIsWUFBNkMsSUFBSSxLQUFLO0VBQ3RELGVBQTZCLElBQUksY0FBYzs7Ozs7O0VBTy9DLE1BQU0sT0FBc0I7R0FDMUIsTUFBTSxFQUFFLFlBQVksT0FBTyxPQUFPO0FBR2xDLFNBQU0sS0FBSyxvQkFBb0IsUUFBUTtHQUd2QyxNQUFNLGVBQWUsTUFBTSxnQ0FBZ0M7QUFDM0QsT0FBSSxhQUFhLFdBQVcsR0FBRztBQUM3QixZQUFRLElBQUksTUFBTSxNQUFNLFFBQVEsV0FBVyx3QkFBd0IsQ0FBQyxDQUFDO0FBR3JFLFFBQUk7QUFDRixXQUFNLGlCQUFpQixXQUFXLEVBQUUsRUFBRSxFQUFFLFdBQVcsT0FBTyxDQUFDO0FBQzNELFdBQU0saUJBQWlCLGdCQUFnQixFQUFFLEVBQUUsRUFBRSxXQUFXLE9BQU8sQ0FBQzthQUN6RCxHQUFHO0FBRVYsU0FBSSxFQUFFLGFBQWEsNEJBQTRCO0FBQzdDLGNBQVEsTUFBTSxxQ0FBcUMsRUFBRTs7O0FBSXpEOztBQUtGLFNBQU0sd0JBQ0osWUFBWTtBQUVWLFVBQU0sS0FBSyxjQUFjLGFBQWE7QUFHdEMsVUFBTSxnQkFBZ0I7TUFFeEI7SUFBRSxpQkFBaUI7SUFBVyxhQUFhO0lBQU8sQ0FDbkQ7Ozs7Ozs7O0VBU0gsTUFBTSxnQkFBZ0IsT0FBZSxjQUEyQztBQUM5RSxPQUFJLFVBQVUsWUFBWSxVQUFVLFNBQVMsVUFBVSxVQUFVO0FBQy9EOztBQUlGLE9BQUksYUFBYSxTQUFTLFlBQVksRUFBRTtBQUN0QyxZQUFRLElBQUksTUFBTSxLQUFLLE9BQU8sb0NBQW9DLENBQUM7QUFFbkUsUUFBSSxDQUFDLFFBQVEsRUFBRTtBQUNiLFdBQU0sSUFBSSxlQUFlLGNBQWMsTUFBTTs7QUFFL0MsVUFBTSxLQUFLLG1CQUFtQjtBQUM5QixTQUFLLGFBQWEsS0FBSyxpQkFBaUI7QUFDeEM7O0FBT0YsT0FBSSxDQUFDLFFBQVEsRUFBRTtJQUNiLE1BQU0sbUJBQW9CLE1BQU0sSUFBSSxlQUFlLGNBQWMsTUFBTTtBQUV2RSxRQUFJLGlCQUFpQixTQUFTLEdBQUc7QUFDL0IsYUFBUSxJQUFJLE1BQU0sS0FBSyxrQkFBa0IsQ0FBQztBQUUxQyxVQUFLLE1BQU0sbUJBQW1CLGtCQUFrQjtBQUM5QyxVQUFJO09BSUYsTUFBTSxjQUFjLEtBQUssZ0NBQWdDLGdCQUFnQjtBQUN6RSxXQUFJLFlBQVksU0FBUyxHQUFHO0FBQzFCLGdCQUFRLElBQ04sTUFBTSxLQUFLLEtBQUssS0FBSyxTQUFTLE9BQU8sYUFBYSxnQkFBZ0IsR0FBRyxFQUNyRSxNQUFNLEtBQUssU0FBUyxZQUFZLE9BQU8sUUFBUSxDQUNoRDtjQUNJO0FBQ0wsZ0JBQVEsSUFBSSxNQUFNLEtBQUssS0FBSyxLQUFLLFNBQVMsT0FBTyxhQUFhLGdCQUFnQixHQUFHLENBQUM7O2VBRTdFLEdBQUc7QUFDVixlQUFRLE1BQU0sRUFBRTtBQUNoQixlQUFRLE1BQ04sTUFBTSxJQUFJLG9EQUFvRCxrQkFBa0IsQ0FDakY7Ozs7O0FBU1QsT0FBSSxDQUFDLFFBQVEsSUFBSSxPQUFPLE9BQU8sTUFBTSxXQUFXLFdBQVcsT0FBTyxrQkFBa0I7QUFDbEYsV0FBTyxpQkFBaUIsZ0JBQWdCLENBQUMsYUFBYSxDQUFDO0FBQ3ZELFlBQVEsSUFDTixNQUFNLElBQUkscUJBQXFCLEtBQUssU0FBUyxPQUFPLGFBQWEsYUFBYSxHQUFHLENBQ2xGOztHQUdILE1BQU0sd0JBQXdCLE9BQU8sT0FBTyx1Q0FBdUMsQ0FBQyxDQUFDLE1BQ2xGLFlBQVksVUFBVSxjQUFjLFFBQVEsQ0FDOUM7QUFHRCxPQUFJLHVCQUF1QjtBQUN6QixVQUFNLEtBQUssY0FBYyxDQUFDLGFBQWEsQ0FBQzs7QUFLMUMsU0FBTSxLQUFLLGVBQWU7QUFDMUIsU0FBTSxLQUFLLGdCQUFnQjtBQUMzQixTQUFNLEtBQUssY0FBYztBQUN6QixTQUFNLEtBQUssbUJBQW1CO0FBRTlCLFFBQUssYUFBYSxLQUFLLGlCQUFpQjs7RUFHMUMsZ0NBQ0UsaUJBQ21DO0FBQ25DLE9BQUksQ0FBQyxnQkFBZ0IsU0FBUyxZQUE2QyxFQUFFO0FBQzNFLFdBQU8sRUFBRTs7R0FHWCxNQUFNLFdBQVcsY0FBYyxvQkFBb0IsZ0JBQWdCO0dBQ25FLE1BQU0sV0FBVyxlQUFlLFFBQVEsUUFBUSxJQUFJLGNBQWMsR0FBRyxTQUFTLE9BQU87QUFDckYsUUFBSyxNQUFNLE9BQU8sVUFBVTtJQUMxQixNQUFNLE1BQU0sZUFBZSxRQUFRLElBQUk7QUFDdkMsUUFBSSxRQUFRLENBQUMsR0FBRztBQUNkLG9CQUFlLE9BQU8sS0FBSyxFQUFFOzs7QUFJakMsVUFBTzs7RUFHVCxNQUFNLG9CQUFvQixTQUFrQztHQUUxRCxNQUFNLGdCQUFnQixLQUFLLEtBQ3pCLE9BQU8sS0FBSyxRQUFRLFFBQVEsVUFBVSxRQUFRLEVBQzlDLG1CQUNEO0dBQ0QsTUFBTSxnQkFBaUIsTUFBTSxPQUFPLGNBQWMsR0FDOUMsTUFBTSxTQUFTLGVBQWUsUUFBUSxHQUN0QztHQUdKLE1BQU0sYUFBYTtJQUNqQixTQUNFLE9BQU8sT0FBTyxPQUFPLFdBQ3JCLFVBQVUsT0FBTyxPQUFPLE9BQU8sUUFBUSxRQUFRLFlBQVksR0FBRyxPQUFPLE9BQU8sT0FBTyxRQUFRLFFBQVE7SUFDckcsV0FBVztJQUNaO0FBRUQsUUFBSyxNQUFNLFVBQVUsU0FBUztJQUk1QixNQUFNLFVBQVUsS0FBSyxLQUNuQixPQUFPLEtBQUssUUFBUSxRQUFRLFVBQVUsUUFBUSxFQUM5QyxhQUFhLE9BQU8sZ0JBQ3JCO0FBQ0QsUUFBSSxDQUFFLE1BQU0sT0FBTyxRQUFRLEVBQUc7QUFDNUI7O0FBRUYsUUFBSSxDQUFFLE1BQU0sT0FBTyxLQUFLLEtBQUssT0FBTyxhQUFhLE9BQU8sQ0FBQyxFQUFHO0FBQzFELFdBQU0sSUFBSSxNQUNSLDZDQUE2QyxPQUFPLDJGQUNyRDs7SUFHSCxNQUFNLFdBQVcsTUFBTSxTQUFTLFNBQVMsUUFBUTtJQUNqRCxNQUFNLGdCQUFnQixPQUFPLFFBQVEsV0FBVyxDQUFDLFFBQzlDLEtBQUssQ0FBQyxLQUFLLFdBQVcsSUFBSSxRQUFRLE1BQU0sSUFBSSxLQUFLLE1BQU0sRUFDeEQsU0FDRDtJQUdELE1BQU0sV0FBVyxLQUFLLEtBQUssT0FBTyxhQUFhLFFBQVEsZ0NBQWdDO0FBR3ZGLFFBQUksQ0FBRSxNQUFNLE9BQU8sS0FBSyxRQUFRLFNBQVMsQ0FBQyxFQUFHO0FBQzNDLFdBQU0sTUFBTSxLQUFLLFFBQVEsU0FBUyxFQUFFLEVBQUUsV0FBVyxNQUFNLENBQUM7QUFDeEQsYUFBUSxLQUFLLHNCQUFzQixLQUFLLFFBQVEsU0FBUyxDQUFDLDZCQUE2Qjs7QUFRekYsUUFBSSxNQUFNLE9BQU8sU0FBUyxFQUFFO0FBQzFCOztBQUdGLFVBQU0sVUFBVSxVQUFVLGNBQWM7QUFDeEMsS0FBQyxRQUFRLElBQ1AsUUFBUSxJQUNOLE1BQU0sS0FBSyxXQUFXLEdBQUcsTUFBTSxLQUFLLEtBQUssU0FBUyxPQUFPLGFBQWEsU0FBUyxDQUFDLENBQ2pGOzs7RUFJUCxNQUFNLGdCQUFnQjtBQUNwQixRQUFLLFFBQVEsTUFBTSxXQUFXOztFQUdoQyxNQUFNLGlCQUFpQjtBQUNyQixRQUFLLFNBQVMsTUFBTSxZQUFZOztFQUdsQyxNQUFNLGVBQWU7QUFDbkIsUUFBSyxPQUFPLE1BQU0sVUFBVTs7RUFHOUIsTUFBTSxvQkFBb0I7QUFDeEIsUUFBSyxZQUFZLE1BQU0sZUFBZTtBQUN0QyxTQUFNLE9BQU8sVUFBVSxZQUFZLEtBQUssVUFBVTs7RUFHcEQsTUFBTSxvQkFBbUM7R0FDdkMsTUFBTSxnQkFBZ0IsS0FBSyxLQUFLLE9BQU8sYUFBYSxVQUFVO0dBRzlELE1BQU0sRUFBRSxtQkFBbUIsTUFBTSxPQUFPO0FBQ3hDLG1CQUFnQjtBQUdoQixPQUFJLENBQUUsTUFBTSxPQUFPLGNBQWMsRUFBRztBQUNsQzs7R0FJRixNQUFNLEVBQUUsY0FBYyxNQUFNLE9BQU87R0FDbkMsTUFBTSxFQUFFLGtCQUFrQixNQUFNLE9BQU87R0FDdkMsTUFBTSxFQUFFLGdCQUFnQixNQUFNLE9BQU87R0FHckMsTUFBTSxRQUFRLE1BQU0sVUFBVSxLQUFLLEtBQUssZUFBZSxZQUFZLFVBQVUsQ0FBQyxDQUFDO0FBRS9FLFFBQUssTUFBTSxRQUFRLE9BQU87QUFDeEIsUUFBSTtBQUVGLFdBQU0sY0FBYyxLQUFLO2FBQ2xCLEdBQUc7QUFDVixhQUFRLE1BQU0sNkJBQTZCLFFBQVEsRUFBRTs7Ozs7Ozs7OztFQVczRCxNQUFNLGNBQWMsZUFBaUU7R0FDbkYsTUFBTSxhQUFhLEtBQUssb0JBQW9CLGNBQWM7R0FDMUQsTUFBTSxZQUFZLE9BQU8sS0FBSyxXQUFXO0FBR3pDLE9BQUksVUFBVSxTQUFTLFNBQVMsSUFBSSxVQUFVLFNBQVMsUUFBUSxFQUFFO0FBQy9ELFVBQU0sS0FBSyx5QkFBeUIsWUFBWSxVQUFVOztBQUk1RCxPQUNFLFVBQVUsU0FBUyxRQUFRLElBQzNCLFVBQVUsU0FBUyxZQUFZLElBQy9CLFVBQVUsU0FBUyxZQUFZLEVBQy9CO0FBQ0EsVUFBTSxLQUFLLDBCQUEwQixXQUFXOztBQUlsRCxPQUFJLFVBQVUsU0FBUyxRQUFRLElBQUksVUFBVSxTQUFTLFFBQVEsRUFBRTtBQUM5RCxVQUFNLEtBQUssNEJBQTRCLFdBQVc7O0FBSXBELE9BQUksVUFBVSxTQUFTLFNBQVMsRUFBRTtBQUNoQyxVQUFNQSxrQkFBZ0M7O0FBSXhDLE9BQUksVUFBVSxTQUFTLFdBQVcsRUFBRTtBQUNsQyxVQUFNLEtBQUssbUJBQW1COztBQU9oQyxPQUNFLFVBQVUsU0FBUyxPQUFPLElBQzFCLFVBQVUsU0FBUyxTQUFTLElBQzVCLFVBQVUsU0FBUyxTQUFTLEVBQzVCO0FBQ0EsVUFBTSxLQUFLLFFBQVE7O0FBR3JCLFVBQU8sRUFDTCxXQUNEOztFQUdILG9CQUFvQixXQUF1QztHQUN6RCxNQUFNLFlBQVksT0FBTyxLQUFLLHFCQUFxQjtBQUVuRCxVQUFPLE1BQU0sWUFBWSxhQUFhO0lBRXBDLE1BQU0sV0FBVyxTQUFTLFFBQVEsUUFBUTtBQUMxQyxRQUFJLGFBQWEsQ0FBQyxFQUFHLFFBQU87SUFDNUIsTUFBTSxlQUFlLFNBQVMsTUFBTSxXQUFXLEVBQUU7QUFFakQsU0FBSyxNQUFNLFlBQVksV0FBVztBQUNoQyxTQUFJLFVBQVUsY0FBYyxxQkFBcUIsVUFBVSxFQUFFO0FBQzNELGFBQU87OztBQUdYLFdBQU87S0FDUDs7RUFHSixNQUFNLHlCQUF5QixZQUF3QixXQUFvQztBQUN6RixTQUFNLEVBQUUsNEJBQTRCO0lBQUU7SUFBWTtJQUFXLENBQUM7QUFFOUQsU0FBTSxjQUFjLFFBQVE7R0FJNUIsTUFBTSxhQUFhLFdBQVcsUUFBUSxHQUFHLEVBQUU7QUFDM0MsT0FBSSxlQUFlLFdBQVc7SUFDNUIsTUFBTSxXQUFXLGNBQWMsb0JBQW9CLFdBQVc7SUFDOUQsTUFBTSxTQUFTLGNBQWMsSUFBSSxTQUFTO0lBRTFDLE1BQU0sZUFBZSxLQUFLLEtBQ3hCLE9BQU8sYUFDUCxtQkFBbUIsT0FBTyxNQUFNLEdBQUcsR0FBRyxPQUFPLE1BQU0sR0FBRyxXQUN2RDtBQUNELFFBQUksT0FBTyxhQUFhLGFBQWEsQ0FBRSxNQUFNLE9BQU8sYUFBYSxFQUFHO0FBQ2xFLFdBQU0saUJBQWlCLGNBQWMsRUFBRSxVQUFVLENBQUM7OztBQUl0RCxTQUFNQyx1QkFBcUM7QUFFM0MsY0FBVyxZQUFZLE9BQU8sQ0FDNUIsR0FBSSxXQUFXLGFBQWEsRUFBRSxFQUM5QixLQUFLLEtBQUssT0FBTyxhQUFhLHNDQUFzQyxDQUNyRSxDQUFDO0FBQ0YsYUFBVSxLQUFLLFlBQVk7O0VBRzdCLE1BQU0sMEJBQTBCLFlBQTZDO0dBQzNFLE1BQU0sVUFBVSxPQUFPO0lBQ3JCLEdBQUksV0FBVyxTQUFTLEVBQUU7SUFDMUIsR0FBSSxXQUFXLGFBQWEsRUFBRTtJQUM5QixHQUFJLFdBQVcsYUFBYSxFQUFFO0lBQy9CLENBQUM7QUFDRixTQUFNLEVBQUUsNkJBQTZCLEVBQUUsWUFBWSxDQUFDO0FBUXBELFNBQU1DLHlCQUF1QyxRQUFRO0FBRXJELFVBQU8sRUFBRTs7RUFHWCxNQUFNLDRCQUE0QixZQUF1QztBQUN2RSxTQUFNLEVBQUUsK0JBQStCLEVBQUUsWUFBWSxDQUFDO0dBQ3RELE1BQU0sY0FBYyxDQUFDLEdBQUksV0FBVyxTQUFTLEVBQUUsRUFBRyxHQUFJLFdBQVcsU0FBUyxFQUFFLENBQUU7QUFVOUUsU0FBTSxLQUFLLGdCQUFnQjtBQUMzQixTQUFNLEtBQUssZUFBZTtBQUMxQixTQUFNLEtBQUssY0FBYztHQUV6QixNQUFNQyxTQUVBLFlBQVksS0FBSyxjQUFjO0FBQ25DLFFBQUksVUFBVSxTQUFTLFlBQVksRUFBRTtLQUNuQyxNQUFNLFdBQVcsY0FBYyxvQkFBb0IsVUFBVTtBQUM3RCxZQUFPLFNBQVM7QUFDaEIsWUFBTyxFQUNMLGFBQWEsY0FBYyxlQUFlLFNBQVMsRUFDcEQ7O0FBRUgsUUFBSSxVQUFVLFNBQVMsWUFBWSxFQUFFO0tBQ25DLE1BQU0sR0FBRyxhQUFhLFVBQVUsTUFBTSx1QkFBdUIsSUFBSSxFQUFFO0FBQ25FLFlBQU8sVUFBVTtLQUVqQixNQUFNLFVBQVUsV0FBVyxTQUFTLFVBQVU7QUFDOUMsWUFBTyxFQUNMLGFBQWEsY0FBYyxlQUFlLFFBQVEsRUFDbkQ7O0FBRUgsVUFBTSxJQUFJLE1BQU0sZ0JBQWdCO0tBQ2hDO0FBRUYsU0FBTUMsdUJBQXFDLE9BQU87QUFDbEQsU0FBTUMscUJBQW1DO0FBQ3pDLFNBQU1DLG1CQUFpQzs7Ozs7Ozs7O0VBVXpDLE1BQU0sbUJBQ0osVUFDQSxhQUNBLFFBQ21FO0dBQ25FLE1BQU0sRUFBRSxRQUFRLE1BQU0sWUFBWSxnQkFBZ0IsSUFBSSxZQUFZLENBQUMsaUJBQ2pFLGNBQWMsZUFBZSxTQUFTLEVBQ3RDLE9BQ0Q7R0FFRCxNQUFNLFVBQVUsS0FBSyxLQUFLLFFBQVEsUUFBUTtHQUMxQyxNQUFNLFdBQVcsS0FBSyxLQUFLLE9BQU8sYUFBYSxRQUFRO0FBQ3ZELFVBQU87SUFDTDtJQUNBO0lBQ0EsVUFBVSxNQUFNLE9BQU8sU0FBUztJQUNqQzs7Ozs7Ozs7RUFTSCxNQUFNLFlBQ0osVUFDQSxPQUdxRDtHQUNyRCxNQUFNQyxPQUFzQixZQUFZO0dBQ3hDLE1BQU0sUUFBUSxjQUFjLGVBQWUsU0FBUztHQUNwRCxNQUFNLFlBQVksT0FBTyxLQUFLLE1BQU0sQ0FBQyxRQUFRLFNBQVMsU0FBUyxNQUFNLFNBQVM7QUFFOUUsVUFBTyxNQUFNLFlBQ1gsTUFDQSxPQUFPLFFBQVEsUUFBUTtJQUNyQixNQUFNLE1BQU0sZ0JBQWdCLElBQUksSUFBSTtBQUNwQyxRQUFJLElBQUksV0FBVyxhQUFhLEVBQUU7QUFDaEMsV0FBTSxTQUFTLFdBQVcsT0FBTyxnQkFBZ0I7TUFDL0MsTUFBTSxFQUFFLGtCQUFRLE1BQU1DLFFBQU0sSUFBSSxpQkFBaUIsT0FBTyxZQUFZO0FBQ3BFLGFBQU8sR0FBRyxJQUFJLElBQUksaUJBQWlCLE1BQU0sT0FDdkMsS0FBSyxLQUFLLE9BQU8sYUFBYUMsVUFBUUQsSUFBRSxDQUN6QztPQUNEO0FBQ0YsWUFBTzs7SUFHVCxNQUFNLEVBQUUsUUFBUSxNQUFNLE1BQU0sSUFBSSxpQkFBaUIsTUFBTTtJQUN2RCxNQUFNLEVBQUUsWUFBWSxPQUFPLE9BQU87QUFDbEMsUUFBSSxPQUFPLFNBQVMsVUFBVSxFQUFFO0FBQzlCLFdBQU0sU0FBUyxTQUFTLE9BQU8sTUFBTTtBQUNuQyxhQUFPLEdBQUcsSUFBSSxJQUFJLE9BQU8sTUFBTSxPQUM3QixLQUFLLEtBQUssT0FBTyxhQUFhLE9BQU8sUUFBUSxXQUFXLEVBQUUsRUFBRSxFQUFFLENBQy9EO09BQ0Q7V0FDRztBQUNMLFlBQU8sT0FBTyxNQUFNLE9BQU8sS0FBSyxLQUFLLE9BQU8sYUFBYSxRQUFRLEVBQUUsQ0FBQzs7QUFHdEUsV0FBTztNQUVULEVBQUUsQ0FDSDs7Ozs7RUFNSCxNQUFNLGFBQWEsTUFBaUM7QUFDbEQsVUFBTyxNQUFNLGFBQWEsS0FBSzs7Ozs7RUFNakMsTUFBTSxVQUFVLFVBQW1EO0FBQ2pFLFVBQU8sTUFBTSxVQUFVLFNBQVM7Ozs7O0VBTWxDLE1BQU0saUJBQ0osS0FDQSxpQkFDQSxrQkFDeUI7QUFDekIsVUFBTyxNQUFNLGlCQUFpQixLQUFLLGlCQUFpQixpQkFBaUI7Ozs7O0VBTXZFLE1BQU0sZUFDSixLQUNBLGlCQUN3QjtBQUN4QixVQUFPLE1BQU0sZUFBZSxLQUFLLGdCQUFnQjs7Ozs7RUFNbkQsTUFBTSxpQkFBZ0M7QUFDcEMsVUFBTyxNQUFNLGdCQUFnQjs7Ozs7RUFNL0IsTUFBTSxTQUF3QjtHQUM1QixNQUFNLEVBQUUsWUFBWSxPQUFPLE9BQU87R0FDbEMsTUFBTSxhQUFhLE9BQU8sT0FBTztHQUVqQyxNQUFNLGFBQWEsQ0FBQyxPQUFPLEdBQUcsUUFBUTtHQUV0QyxNQUFNLGFBQWEsS0FBSyxLQUFLLE9BQU8sYUFBYSxPQUFPLE9BQU8sSUFBSSxLQUFLLFdBQVc7QUFFbkYsUUFBSyxNQUFNLFVBQVUsWUFBWTtBQUMvQixRQUFJO0FBRUYsU0FBSSxXQUFXLE9BQU87QUFDcEIsWUFBTSxLQUFLLGdCQUFnQixRQUFRLFlBQVksV0FBVyxpQkFBaUI7O0FBRzdFLFdBQU0saUJBQWlCLE1BQU0sRUFBRSxRQUFRLEVBQUUsRUFBRSxXQUFXLE1BQU0sQ0FBQzthQUN0RCxHQUFHO0FBQ1YsYUFBUSxNQUFNLHNDQUFzQyxPQUFPLElBQUksRUFBRTs7Ozs7OztFQVF2RSxNQUFjLGdCQUNaLFFBQ0EsWUFDQSxTQUNlO0dBQ2YsTUFBTSxnQkFBZ0IsS0FBSyxLQUFLLE9BQU8sYUFBYSxRQUFRLFdBQVc7QUFHdkUsU0FBTSxNQUFNLGVBQWUsRUFBRSxXQUFXLE1BQU0sQ0FBQztBQUUvQyxRQUFLLE1BQU0sVUFBVSxTQUFTO0lBQzVCLE1BQU0sYUFBYSxLQUFLLEtBQUssWUFBWSxHQUFHLE9BQU8sS0FBSztJQUN4RCxNQUFNLGFBQWEsS0FBSyxLQUFLLGVBQWUsR0FBRyxPQUFPLEtBQUs7SUFFM0QsTUFBTSxhQUFhO0tBQ2pCO0tBQ0E7S0FDQTtLQUNBO0tBQ0QsQ0FBQyxLQUFLLEtBQUs7QUFDWixVQUFNLGdDQUFnQyxZQUFZLFlBQVksV0FBVztBQUN6RSxLQUFDLFFBQVEsSUFDUCxRQUFRLElBQUksTUFBTSxLQUFLLFdBQVcsR0FBRyxNQUFNLEtBQUssR0FBRyxPQUFPLFlBQVksT0FBTyxLQUFLLENBQUMifQ==
|