From 0f17d5ea0bd30b5d362ff4a13cd41f63f5a9e36c Mon Sep 17 00:00:00 2001 From: Ajay Kumar Date: Thu, 3 Sep 2026 19:15:01 -0500 Subject: [PATCH 1/7] build: exclude brick templates from dart format The mono-repo brick is about to gain ~170 Dart files under __brick__. They are mustache templates, not parseable Dart, and `dart format` has no exclude of its own, so the lint script now filters the paths before handing them over. Co-Authored-By: Claude Opus 5 (1M context) --- melos.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/melos.yaml b/melos.yaml index e4822fc..0e84cb6 100644 --- a/melos.yaml +++ b/melos.yaml @@ -23,8 +23,11 @@ command: scripts: lint: description: "Run format and analyze all packages" + # Brick sources are mustache templates, not valid Dart, so the formatter + # has to skip them - `dart format` has no exclude of its own. run: | - dart format --set-exit-if-changed . && + find . -name '*.dart' -not -path '*/__brick__/*' -not -path '*/.dart_tool/*' \ + -exec dart format --set-exit-if-changed {} + && dart analyze --fatal-infos . exec: concurrency: 5 From a599726af8220b685d61de8a88e0f3b9cf903b99 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Date: Thu, 3 Sep 2026 19:15:21 -0500 Subject: [PATCH 2/7] feat(nonstop_cli): scaffold a production-shaped app in the mono-repo brick The brick created four empty directories and a melos.yaml. Everything a real app needs was still a day of copy-paste from the last project, so this ports that groundwork in, adapted from an app that had already grown it. Always generated: core (logger, DI bootstrap, route observers, error types, BLoC observer), di, design_system (Material 3 theme, components, toasts, loaders), localization (type-safe strings via i69n), utils, and the app shell itself - bootstrap, router and splash, with no business logic. Optional, each gated on a brick variable: network, auth, firestore, notifications, analytics, crashlytics, feature_flags, developer, dashboard. A directory named `{{_dir}}` renders to an empty path segment when its module is off, and mason skips every file underneath it - section tags cannot be used for this because `{{/x}}` contains a path separator. pre_gen derives those variables, resolves the two dependencies between modules and runs `flutter create` so the brick's own lib/ and pubspec.yaml overlay it. post_gen formats the result, because mustache line breaks depend on the answers and a fresh project has to pass its own `melos lint`. Every process step now checks its exit code. cli_core's trackOperation reports success as soon as the future completes, so a failed `flutter create` or `melos bootstrap` showed a green tick and generation carried on to produce a half-built workspace. Non-Firebase projects ship a boot test that runs the real bootstrap and pumps the app; Firebase ones cannot until `flutterfire configure` has been run, so the file name renders empty and mason drops it. Verified across ten module combinations: melos bootstrap, melos lint and melos test all pass, `flutter build web` succeeds, and the app reaches its first screen at runtime. Co-Authored-By: Claude Opus 5 (1M context) --- .../{{ name.snakeCase() }}/.gitignore | 2 + .../{{ name.snakeCase() }}/README.md | 95 ++- .../analysis_options.yaml | 13 +- .../{{ name.snakeCase() }}/apps/README.md | 12 +- .../apps/{{ name.snakeCase() }}/lib/app.dart | 66 ++ .../{{ name.snakeCase() }}/lib/bootstrap.dart | 59 ++ .../lib/firebase_options.dart | 17 + .../apps/{{ name.snakeCase() }}/lib/main.dart | 8 + .../lib/router/router.dart | 114 ++++ .../lib/ui/splash_screen.dart | 61 ++ .../apps/{{ name.snakeCase() }}/pubspec.yaml | 61 ++ .../test/{{smoke_test_file}} | 17 + .../{{ name.snakeCase() }}/features/README.md | 16 +- .../features/{{auth_dir}}/README.md | 18 + .../{{auth_dir}}/lib/analytics/analytics.dart | 101 +++ .../features/{{auth_dir}}/lib/auth.dart | 40 ++ .../{{auth_dir}}/lib/config/config.dart | 3 + .../{{auth_dir}}/lib/config/config_imp.dart | 8 + .../{{auth_dir}}/lib/config/index.dart | 2 + .../{{auth_dir}}/lib/constants/index.dart | 1 + .../{{auth_dir}}/lib/constants/routes.dart | 10 + .../features/{{auth_dir}}/lib/data/index.dart | 3 + .../lib/data/services/auth_service.dart | 7 + .../lib/data/services/auth_service_imp.dart | 35 ++ .../{{auth_dir}}/lib/data/services/index.dart | 3 + .../token/firebase_auth_token_provider.dart | 163 +++++ .../{{auth_dir}}/lib/router/router.dart | 53 ++ .../lib/ui/components/footer_builder.dart | 70 +++ .../lib/ui/components/header_builder.dart | 15 + .../ui/screens/forgot_password_screen.dart | 19 + .../{{auth_dir}}/lib/ui/screens/index.dart | 4 + .../lib/ui/screens/register_screen.dart | 83 +++ .../lib/ui/screens/sign_in_screen.dart | 94 +++ .../features/{{auth_dir}}/pubspec.yaml | 40 ++ .../features/{{dashboard_dir}}/README.md | 18 + .../{{dashboard_dir}}/lib/dashboard.dart | 5 + .../lib/router/dashboard_router.dart | 51 ++ .../ui/screens/dashboard_shell_screen.dart | 54 ++ .../lib/ui/screens/explore_tab_screen.dart | 21 + .../lib/ui/screens/home_tab_screen.dart | 22 + .../lib/ui/screens/index.dart | 4 + .../lib/ui/screens/profile_tab_screen.dart | 87 +++ .../lib/ui/widgets/index.dart | 1 + .../lib/ui/widgets/placeholder_tab.dart | 63 ++ .../features/{{dashboard_dir}}/pubspec.yaml | 32 + .../{{ name.snakeCase() }}/melos.yaml | 35 +- .../{{ name.snakeCase() }}/packages/README.md | 14 +- .../packages/core/README.md | 18 + .../packages/core/lib/bloc/global_event.dart | 27 + .../lib/bloc/global_event_channel_bloc.dart | 41 ++ .../bloc/global_event_channel_provider.dart | 92 +++ .../lib/bloc/global_event_channel_state.dart | 33 + .../packages/core/lib/bloc/index.dart | 19 + .../packages/core/lib/constants/defaults.dart | 6 + .../core/lib/constants/environment.dart | 18 + .../packages/core/lib/constants/index.dart | 4 + .../packages/core/lib/constants/keys.dart | 11 + .../packages/core/lib/constants/routes.dart | 13 + .../lib/converters/datetime_converter.dart | 35 ++ .../packages/core/lib/converters/index.dart | 3 + .../lib/converters/timestamp_converter.dart | 44 ++ .../packages/core/lib/core.dart | 19 + .../core/lib/developer/emulators.dart | 25 + .../packages/core/lib/errors/exceptions.dart | 11 + .../packages/core/lib/errors/index.dart | 2 + .../packages/core/lib/extensions/basic.dart | 7 + .../packages/core/lib/extensions/index.dart | 1 + .../packages/core/lib/logger/index.dart | 3 + .../packages/core/lib/logger/logger.dart | 22 + .../core/lib/logger/talker_logger_impl.dart | 31 + .../core/lib/observer/core_bloc_observer.dart | 34 + .../lib/observer/core_route_observer.dart | 89 +++ .../packages/core/lib/observer/index.dart | 3 + .../packages/core/lib/router/core_router.dart | 5 + .../packages/core/pubspec.yaml | 39 ++ .../packages/design_system/README.md | 18 + .../lib/components/asset_image.dart | 55 ++ .../lib/components/auth_headers_builder.dart | 116 ++++ .../lib/components/date_filter_chips.dart | 68 ++ .../lib/components/default_error_view.dart | 127 ++++ .../design_system/lib/components/header.dart | 47 ++ .../design_system/lib/components/index.dart | 8 + .../design_system/lib/components/loaders.dart | 18 + .../lib/components/network_url_image.dart | 45 ++ .../lib/constants/navigation_icons.dart | 26 + .../design_system/lib/design_system.dart | 138 +++++ .../lib/dialogs/file_info_dialog.dart | 264 ++++++++ .../design_system/lib/dialogs/index.dart | 1 + .../design_system/lib/generated/theme.dart | 386 ++++++++++++ .../design_system/lib/generated/util.dart | 27 + .../design_system/lib/loader/loader.dart | 15 + .../lib/screens/error_screen.dart | 152 +++++ .../design_system/lib/screens/index.dart | 1 + .../design_system/lib/toast/toasts.dart | 89 +++ .../lib/utils/extensions/index.dart | 2 + .../lib/utils/extensions/string.dart | 33 + .../design_system/lib/utils/index.dart | 2 + .../design_system/lib/wrapper/wrappers.dart | 54 ++ .../packages/design_system/pubspec.yaml | 33 + .../test/design_system_test.dart | 25 + .../packages/di/README.md | 6 + .../packages/di/lib/di.dart | 14 + .../di/lib/src/dependency_injection.dart | 22 + .../di/lib/src/dependency_injection_imp.dart | 115 ++++ .../packages/di/pubspec.yaml | 20 + .../packages/di/test/di_test.dart | 253 ++++++++ .../packages/localization/README.md | 169 +++++ .../packages/localization/build.yaml | 13 + .../localization/lib/localization.dart | 14 + .../localization/lib/messages.i69n.dart | 579 ++++++++++++++++++ .../localization/lib/messages.i69n.yaml | 151 +++++ .../lib/src/localization_provider.dart | 43 ++ .../localization/lib/src/strings.dart | 14 + .../packages/localization/pubspec.yaml | 21 + .../localization/test/localization_test.dart | 26 + .../utils/lib/src/number_formatter.dart | 79 +++ .../packages/utils/lib/utils.dart | 3 + .../packages/utils/pubspec.yaml | 20 + .../packages/{{analytics_dir}}/README.md | 267 ++++++++ .../{{analytics_dir}}/lib/analytics.dart | 37 ++ .../lib/src/client/analytics_client.dart | 44 ++ .../src/client/firebase_analytics_client.dart | 204 ++++++ .../lib/src/client/index.dart | 2 + .../lib/src/config/analytics_config.dart | 42 ++ .../lib/src/config/index.dart | 1 + .../lib/src/models/analytics_event.dart | 63 ++ .../lib/src/models/analytics_events.dart | 217 +++++++ .../lib/src/models/index.dart | 2 + .../observer/analytics_route_observer.dart | 65 ++ .../lib/src/observer/index.dart | 1 + .../lib/src/utils/analytics_helper.dart | 203 ++++++ .../lib/src/utils/index.dart | 1 + .../packages/{{analytics_dir}}/pubspec.yaml | 24 + .../test/analytics_test.dart | 26 + .../packages/{{crashlytics_dir}}/README.md | 274 +++++++++ .../{{crashlytics_dir}}/lib/crashlytics.dart | 56 ++ .../lib/src/client/crashlytics_client.dart | 54 ++ .../client/firebase_crashlytics_client.dart | 286 +++++++++ .../lib/src/client/index.dart | 2 + .../lib/src/config/crashlytics_config.dart | 104 ++++ .../lib/src/config/index.dart | 1 + .../lib/src/models/crash_report.dart | 73 +++ .../lib/src/models/index.dart | 2 + .../lib/src/models/user_metadata.dart | 66 ++ .../packages/{{crashlytics_dir}}/pubspec.yaml | 25 + .../test/crashlytics_test.dart | 133 ++++ .../{{developer_dir}}/lib/developer.dart | 3 + .../lib/src/routes/developer_routes.dart | 21 + .../lib/src/routes/index.dart | 1 + .../lib/src/screens/developer_screen.dart | 13 + .../lib/src/screens/index.dart | 1 + .../lib/src/widgets/index.dart | 1 + .../src/widgets/open_dev_tools_wrapper.dart | 187 ++++++ .../packages/{{developer_dir}}/pubspec.yaml | 29 + .../test/developer_test.dart | 38 ++ .../packages/{{feature_flags_dir}}/README.md | 18 + .../lib/feature_flags.dart | 29 + .../lib/src/feature_flag_provider.dart | 23 + .../lib/src/feature_flag_service.dart | 95 +++ .../lib/src/feature_flag_wrapper.dart | 56 ++ .../src/firebase_remote_config_provider.dart | 144 +++++ .../{{feature_flags_dir}}/lib/src/index.dart | 5 + .../lib/src/register.dart | 19 + .../{{feature_flags_dir}}/pubspec.yaml | 25 + .../packages/{{network_dir}}/README.md | 18 + .../packages/{{network_dir}}/lib/network.dart | 78 +++ .../{{network_dir}}/lib/src/api/index.dart | 2 + .../lib/src/api/network_api.dart | 13 + .../lib/src/auth/auth_token_provider.dart | 27 + .../{{network_dir}}/lib/src/auth/index.dart | 1 + .../lib/src/client/dio_network_client.dart | 339 ++++++++++ .../{{network_dir}}/lib/src/client/index.dart | 2 + .../lib/src/client/network_client.dart | 50 ++ .../{{network_dir}}/lib/src/config/index.dart | 1 + .../lib/src/config/network_config.dart | 44 ++ .../lib/src/exceptions/index.dart | 1 + .../src/exceptions/network_exceptions.dart | 107 ++++ .../src/interceptors/auth_interceptor.dart | 73 +++ .../lib/src/interceptors/index.dart | 2 + .../src/interceptors/logging_interceptor.dart | 44 ++ .../{{network_dir}}/lib/src/models/index.dart | 1 + .../lib/src/models/network_response.dart | 102 +++ .../lib/src/models/network_response.g.dart | 93 +++ .../extensions/error_message_extension.dart | 97 +++ .../lib/src/utils/extensions/index.dart | 1 + .../{{network_dir}}/lib/src/utils/index.dart | 1 + .../packages/{{network_dir}}/pubspec.yaml | 31 + .../{{network_dir}}/test/network_test.dart | 15 + .../lib/notifications.dart | 28 + .../client/firebase_notification_client.dart | 258 ++++++++ .../lib/src/client/index.dart | 2 + .../lib/src/client/notification_client.dart | 27 + .../lib/src/config/index.dart | 1 + .../lib/src/config/notification_config.dart | 7 + .../lib/src/device_info/device_info.dart | 5 + .../lib/src/device_info/device_info_impl.dart | 210 +++++++ .../lib/src/device_info/index.dart | 2 + .../lib/src/di/index.dart | 1 + .../lib/src/di/notification_di.dart | 27 + .../lib/src/exceptions/index.dart | 1 + .../exceptions/notification_exceptions.dart | 5 + .../lib/src/models/device_token_models.dart | 35 ++ .../lib/src/models/device_token_models.g.dart | 31 + .../lib/src/models/index.dart | 1 + .../lib/src/services/index.dart | 2 + .../notification_permission_manager.dart | 45 ++ .../services/notification_token_manager.dart | 103 ++++ .../{{notifications_dir}}/pubspec.yaml | 41 ++ .../{{ name.snakeCase() }}/plugins/README.md | 8 +- .../{{ name.snakeCase() }}/pubspec.yaml | 5 +- .../flutter_project_with_mono_repo/brick.yaml | 51 +- .../hooks/commands/checked_process.dart | 51 ++ .../commands/flutter_create_command.dart | 53 +- .../hooks/commands/format_command.dart | 25 + .../hooks/commands/melos_command.dart | 27 +- .../hooks/commands/module_vars.dart | 54 ++ .../hooks/commands/next_steps_command.dart | 28 + .../hooks/post_gen.dart | 6 +- .../hooks/pre_gen.dart | 12 +- 219 files changed, 10909 insertions(+), 56 deletions(-) create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/app.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/bootstrap.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/firebase_options.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/main.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/router/router.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/ui/splash_screen.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/pubspec.yaml create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/test/{{smoke_test_file}} create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/README.md create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/analytics/analytics.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/auth.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/config/config.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/config/config_imp.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/config/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/constants/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/constants/routes.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/services/auth_service.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/services/auth_service_imp.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/services/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/token/firebase_auth_token_provider.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/router/router.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/components/footer_builder.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/components/header_builder.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/forgot_password_screen.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/register_screen.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/sign_in_screen.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/pubspec.yaml create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/README.md create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/dashboard.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/router/dashboard_router.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/dashboard_shell_screen.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/explore_tab_screen.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/home_tab_screen.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/profile_tab_screen.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/widgets/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/widgets/placeholder_tab.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/pubspec.yaml create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/README.md create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event_channel_bloc.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event_channel_provider.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event_channel_state.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/defaults.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/environment.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/keys.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/routes.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/converters/datetime_converter.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/converters/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/converters/timestamp_converter.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/core.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/developer/emulators.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/errors/exceptions.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/errors/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/extensions/basic.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/extensions/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/logger/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/logger/logger.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/logger/talker_logger_impl.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/observer/core_bloc_observer.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/observer/core_route_observer.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/observer/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/router/core_router.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/pubspec.yaml create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/README.md create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/asset_image.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/auth_headers_builder.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/date_filter_chips.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/default_error_view.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/header.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/loaders.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/network_url_image.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/constants/navigation_icons.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/design_system.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/dialogs/file_info_dialog.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/dialogs/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/generated/theme.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/generated/util.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/loader/loader.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/screens/error_screen.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/screens/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/toast/toasts.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/utils/extensions/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/utils/extensions/string.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/utils/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/wrapper/wrappers.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/pubspec.yaml create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/test/design_system_test.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/README.md create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/lib/di.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/lib/src/dependency_injection.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/lib/src/dependency_injection_imp.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/pubspec.yaml create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/test/di_test.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/README.md create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/build.yaml create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/localization.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/messages.i69n.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/messages.i69n.yaml create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/src/localization_provider.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/src/strings.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/pubspec.yaml create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/test/localization_test.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/utils/lib/src/number_formatter.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/utils/lib/utils.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/utils/pubspec.yaml create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/README.md create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/analytics.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/client/analytics_client.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/client/firebase_analytics_client.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/client/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/config/analytics_config.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/config/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/analytics_event.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/analytics_events.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/observer/analytics_route_observer.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/observer/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/utils/analytics_helper.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/utils/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/pubspec.yaml create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/test/analytics_test.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/README.md create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/crashlytics.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/client/crashlytics_client.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/client/firebase_crashlytics_client.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/client/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/config/crashlytics_config.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/config/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/models/crash_report.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/models/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/models/user_metadata.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/pubspec.yaml create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/test/crashlytics_test.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/developer.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/routes/developer_routes.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/routes/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/screens/developer_screen.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/screens/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/widgets/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/widgets/open_dev_tools_wrapper.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/pubspec.yaml create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/test/developer_test.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/README.md create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/feature_flags.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/feature_flag_provider.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/feature_flag_service.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/feature_flag_wrapper.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/firebase_remote_config_provider.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/register.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/pubspec.yaml create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/README.md create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/network.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/api/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/api/network_api.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/auth/auth_token_provider.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/auth/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/client/dio_network_client.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/client/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/client/network_client.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/config/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/config/network_config.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/exceptions/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/exceptions/network_exceptions.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/interceptors/auth_interceptor.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/interceptors/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/interceptors/logging_interceptor.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/models/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/models/network_response.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/models/network_response.g.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/utils/extensions/error_message_extension.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/utils/extensions/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/utils/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/pubspec.yaml create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/test/network_test.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/notifications.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/firebase_notification_client.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/notification_client.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/config/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/config/notification_config.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/device_info/device_info.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/device_info/device_info_impl.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/device_info/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/di/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/di/notification_di.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/exceptions/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/exceptions/notification_exceptions.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/models/device_token_models.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/models/device_token_models.g.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/models/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/services/index.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/services/notification_permission_manager.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/services/notification_token_manager.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/pubspec.yaml create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/checked_process.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/format_command.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/module_vars.dart create mode 100644 packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/next_steps_command.dart diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/.gitignore b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/.gitignore index 34a1c19..1f60ad4 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/.gitignore +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/.gitignore @@ -12,5 +12,7 @@ pubspec_overrides.yaml .flutter-plugins-dependencies *.iml *.lock +# Apps ship a binary; pin their resolution. +!apps/*/pubspec.lock mason-lock.json .mason diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/README.md b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/README.md index 1225a1b..4c9021d 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/README.md +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/README.md @@ -1,20 +1,95 @@ # {{ name.titleCase() }} -{{ description }} +{{{description}}} [![nonstop_cli](https://img.shields.io/badge/started%20with-nonstop_cli-166C4E.svg?style=flat-square)](https://pub.dev/packages/nonstop_cli) [![melos](https://img.shields.io/badge/maintained%20with-melos-f700ff.svg?style=flat-square)](https://github.com/invertase/melos) +## Layout -## Technologies at play +``` +apps/{{name.snakeCase()}} the application shell: bootstrap, router, splash +features/ vertical slices (ui + state + data) +packages/ shared capabilities +plugins/ native integrations +``` -- Dart -- Flutter -- Melos +Dependencies point inward: apps depend on features, features depend on +packages, packages depend on nothing above them. -## Getting Started +### What is already wired -- All apps are in the `/apps` directory. -- All features are in the `/features` directory -- All shared packages are in the `/packages` directory -- All plugins are in the `/plugins` directory \ No newline at end of file +| Module | Role | +| --- | --- | +| `packages/core` | logger, DI bootstrap, route observers, error types, BLoC observer | +| `packages/di` | service locator (GetIt) behind a small interface | +| `packages/design_system` | Material 3 theme, shared components, toasts, loaders | +| `packages/localization` | type-safe strings generated from YAML (i69n) | +| `packages/utils` | dependency-light helpers | +{{#network}}| `packages/network` | Dio client, auth + logging interceptors, typed errors | +{{/network}}{{#analytics}}| `packages/analytics` | event tracking behind a swappable client | +{{/analytics}}{{#crashlytics}}| `packages/crashlytics` | crash and non-fatal reporting | +{{/crashlytics}}{{#notifications}}| `packages/notifications` | FCM, permissions, device-token registration | +{{/notifications}}{{#feature_flags}}| `packages/feature_flags` | Remote Config flags with a widget wrapper | +{{/feature_flags}}{{#developer}}| `packages/developer` | in-app dev tools, reachable by a hidden 5-tap gesture | +{{/developer}}{{#auth}}| `features/auth` | Firebase Auth (email, Google, Apple) + route guards | +{{/auth}}{{#dashboard}}| `features/dashboard` | bottom-navigation shell with starter tabs | +{{/dashboard}} + +## Getting started + +```sh +dart pub global activate melos +melos bootstrap +``` +{{#firebase}} +### Firebase + +This project uses Firebase, so configure it before the first run - the +generated `lib/firebase_options.dart` deliberately throws until you do: + +```sh +dart pub global activate flutterfire_cli +cd apps/{{name.snakeCase()}} +flutterfire configure +``` +{{/firebase}} +### Run + +```sh +cd apps/{{name.snakeCase()}} +flutter run --dart-define=BASE_URL=https://api.example.com +``` + +Every environment value is a `--dart-define` read in +`packages/core/lib/constants/environment.dart`. Add new ones there rather than +scattering `String.fromEnvironment` around the codebase. +{{#firebase}} +To run against the Firebase emulator suite, add +`--dart-define=USE_EMULATORS=true`. +{{/firebase}} +## Day-to-day + +```sh +melos lint # format + analyze everything +melos test # run every package's tests +melos run generate # build_runner where it is needed +melos run generate:i69n # regenerate strings after editing messages.i69n.yaml +``` + +## Adding to the monorepo + +```sh +nonstop create package my_feature -o features +nonstop create package my_package -o packages +nonstop create app my_second_app -o apps +nonstop create plugin my_plugin -o plugins +``` + +## Where to start + +1. Replace the placeholder tabs in `features/dashboard`. +2. Put your product strings in `packages/localization/lib/messages.i69n.yaml`. +3. Set the palette in `packages/design_system/lib/generated/theme.dart`. +4. Add feature modules and spread their routes into + `apps/{{name.snakeCase()}}/lib/router/router.dart`. diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/analysis_options.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/analysis_options.yaml index d09b221..4100bd8 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/analysis_options.yaml +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/analysis_options.yaml @@ -1,5 +1,16 @@ include: package:flutter_lints/flutter.yaml +analyzer: + exclude: + # Generated code - regenerate it, do not lint it. + - "**/*.g.dart" + - "**/*.freezed.dart" + - "**/*.i69n.dart" + # Build output and the plugin sources CocoaPods symlinks into the app. + - "**/build/**" + - "**/ios/.symlinks/**" + - "**/macos/.symlinks/**" + linter: rules: - require_trailing_commas: true \ No newline at end of file + require_trailing_commas: true diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/README.md b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/README.md index 6673234..b323ea6 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/README.md +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/README.md @@ -1 +1,11 @@ -# Put all your apps in this folder +# Apps + +Runnable Flutter applications. An app wires modules together in +`lib/bootstrap.dart` and composes feature routes in `lib/router/router.dart` - +it should hold almost no business logic of its own. + +Add another (an admin build, a white-label variant) with: + +```sh +nonstop create app my_other_app -o apps +``` diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/app.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/app.dart new file mode 100644 index 0000000..12b4796 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/app.dart @@ -0,0 +1,66 @@ +import 'package:core/core.dart'; +import 'package:design_system/design_system.dart'; +{{#notifications}}import 'package:di/di.dart'; +{{/notifications}}import 'package:flutter/material.dart'; +import 'package:localization/localization.dart'; +{{#notifications}}import 'package:notifications/notifications.dart'; +{{/notifications}}import 'package:{{name.snakeCase()}}/router/router.dart'; + +class App extends StatefulWidget { + const App({super.key}); + + @override + State createState() => _AppState(); +} + +class _AppState extends State { +{{#notifications}} late final AppLifecycleListener _lifecycleListener; + +{{/notifications}} @override + void initState() { + super.initState(); +{{#notifications}} // Asking for notification permission needs a foreground surface, so this + // runs from the first frame rather than from bootstrap. Move it behind + // sign-in once you would rather ask a user who has committed to the app. + _lifecycleListener = AppLifecycleListener(onResume: _clearBadge); + WidgetsBinding.instance.addPostFrameCallback((_) async { + final logger = di.get(); + try { + await di.get().init(); + } catch (e, s) { + logger.e('Failed to initialize NotificationClient', e, s); + } + }); +{{/notifications}} } +{{#notifications}} + void _clearBadge() { + final logger = di.get(); + try { + di.get().clearBadge(); + } catch (e, s) { + logger.e('Failed to clear badge on app resume', e, s); + } + } + + @override + void dispose() { + _lifecycleListener.dispose(); + super.dispose(); + } +{{/notifications}} + @override + Widget build(BuildContext context) { + return GlobalEventChannelProvider( + child: DesignSystemWrapper( + builder: (context, theme) => MaterialApp.router( + debugShowCheckedModeBanner: false, + title: strings.app.name, + theme: theme, + routeInformationParser: AppRouter.routeInformationParser, + routeInformationProvider: AppRouter.routeInformationProvider, + routerDelegate: AppRouter.routerDelegate, + ), + ), + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/bootstrap.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/bootstrap.dart new file mode 100644 index 0000000..1fe9563 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/bootstrap.dart @@ -0,0 +1,59 @@ +{{#analytics}}import 'package:analytics/analytics.dart' as analytics; +{{/analytics}}{{#auth}}import 'package:auth/auth.dart' as auth; +{{/auth}}import 'package:bloc/bloc.dart'; +import 'package:core/core.dart' as core; +{{#firebase}}import 'package:core/developer/emulators.dart' as emulators; +{{/firebase}}import 'package:core/logger/logger.dart'; +{{#crashlytics}}import 'package:crashlytics/crashlytics.dart' as crashlytics; +{{/crashlytics}}import 'package:di/di.dart'; +{{#feature_flags}}import 'package:feature_flags/feature_flags.dart' as feature_flags; +{{/feature_flags}}{{#firebase}}import 'package:firebase_core/firebase_core.dart'; +{{/firebase}}import 'package:flutter/widgets.dart'; +{{#network}}import 'package:network/network.dart' as network; +{{/network}}{{#notifications}}import 'package:notifications/notifications.dart' as notifications; +{{/notifications}}{{#firebase}}import 'package:{{name.snakeCase()}}/firebase_options.dart'; +{{/firebase}} +/// Brings every module up, in dependency order, before the first frame. +/// +/// Order matters: the logger is registered first so everything after it can +/// report failures, and Firebase must be live before any Firebase-backed +/// module initialises. +Future init() async { + WidgetsFlutterBinding.ensureInitialized(); + final stopwatch = Stopwatch()..start(); + + await core.init(); + final logger = di.get(); + logger.i('Core ready in ${stopwatch.elapsedMilliseconds} ms'); + + Bloc.observer = core.CoreBlocObserver(); +{{#firebase}} + final options = DefaultFirebaseOptions.currentPlatform; + await Firebase.initializeApp(options: options); + + if (core.Environment.useEmulators) { + await emulators.init(); + } +{{/firebase}} +{{#crashlytics}} await crashlytics.init(); +{{/crashlytics}}{{#analytics}} await analytics.init(); +{{/analytics}}{{#feature_flags}} await feature_flags.init(); +{{/feature_flags}}{{#auth}} + final clientId = options.iosClientId ?? options.androidClientId ?? ''; + await auth.init(auth.DefaultAuthConfig(clientId: clientId)); +{{/auth}}{{#network}} + await network.init( + config: network.DefaultNetworkConfig(baseUrl: core.Environment.baseUrl), + ); +{{/network}}{{#notifications}} + await notifications.init(); +{{/notifications}} + // TODO: initialise your own feature modules here. + + stopwatch.stop(); + logger.i('Bootstrap completed in ${stopwatch.elapsedMilliseconds} ms'); +{{#analytics}} + await analytics.AnalyticsHelper.logAppOpen( + parameters: {'bootstrap_duration': stopwatch.elapsedMilliseconds}, + ); +{{/analytics}}} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/firebase_options.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/firebase_options.dart new file mode 100644 index 0000000..9a50531 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/firebase_options.dart @@ -0,0 +1,17 @@ +{{#firebase}}// This file is a placeholder. Generate the real one with: +// +// cd apps/{{name.snakeCase()}} && flutterfire configure +// +// FlutterFire overwrites this file with your project's options and also drops +// google-services.json / GoogleService-Info.plist into the native folders. +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; + +class DefaultFirebaseOptions { + const DefaultFirebaseOptions._(); + + static FirebaseOptions get currentPlatform => throw UnsupportedError( + 'Firebase has not been configured for {{name.titleCase()}}.\n' + 'Run `flutterfire configure` inside apps/{{name.snakeCase()}} ' + 'to replace lib/firebase_options.dart.', + ); +}{{/firebase}} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/main.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/main.dart new file mode 100644 index 0000000..7178b59 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/main.dart @@ -0,0 +1,8 @@ +import 'package:flutter/material.dart'; +import 'package:{{name.snakeCase()}}/app.dart'; +import 'package:{{name.snakeCase()}}/bootstrap.dart' as bootstrap; + +void main() async { + await bootstrap.init(); + runApp(const App()); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/router/router.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/router/router.dart new file mode 100644 index 0000000..69f3586 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/router/router.dart @@ -0,0 +1,114 @@ +{{#analytics}}import 'package:analytics/analytics.dart'; +{{/analytics}}{{#auth}}import 'package:auth/auth.dart' as auth; +{{/auth}}import 'package:core/core.dart' as core; +{{#dashboard}}import 'package:dashboard/dashboard.dart'; +{{/dashboard}}import 'package:design_system/design_system.dart'; +{{#developer}}import 'package:developer/developer.dart' as developer; +{{/developer}}import 'package:di/di.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:localization/localization.dart'; +import 'package:navigation_history_observer/navigation_history_observer.dart'; +import 'package:{{name.snakeCase()}}/ui/splash_screen.dart'; + +/// The app's single [GoRouter]. +/// +/// Feature packages expose their own `routes` list and are spliced in here, so +/// adding a feature is one import plus one spread - no route table to merge. +class AppRouter { + const AppRouter._(); + + static final GoRouter _router = createRouter(); + + static RouterDelegate get routerDelegate => _router.routerDelegate; + + static RouteInformationParser get routeInformationParser => + _router.routeInformationParser; + + static RouteInformationProvider? get routeInformationProvider => + _router.routeInformationProvider; + + static GoRouter createRouter() { + final router = GoRouter( + debugLogDiagnostics: kDebugMode, + initialLocation: core.CoreRoutes.root, + observers: [ + NavigationHistoryObserver(), + core.CoreRouteObserver(), +{{#analytics}} AnalyticsRouteObserver(), +{{/analytics}} ], + errorBuilder: (context, state) => ErrorScreen( + title: strings.errors.page_not_found, + message: strings.errors.page_not_found_description, + onGoHome: () => context.go(core.CoreRoutes.root), + error: state.uri.toString(), + ), + routes: [ + GoRoute( + path: core.CoreRoutes.root, + builder: (context, state) => const SplashScreen(), + ), +{{#dashboard}} GoRoute( + path: core.CoreRoutes.home, + redirect: (context, state) => DashboardRouter.home, + ), + DashboardRouter.createShellRoute(), +{{/dashboard}}{{^dashboard}} GoRoute( + path: core.CoreRoutes.home, + builder: (context, state) => const _HomeScreen(), + ), +{{/dashboard}}{{#auth}} ...auth.AuthRouter( + onSignedIn: _onAuthenticated, + onSignedUp: _onAuthenticated, + ).routes, +{{/auth}}{{#developer}} ...developer.DeveloperRouter().routes, +{{/developer}} GoRoute( + path: core.CoreRoutes.error, + builder: (context, state) => ErrorScreen( + title: + state.uri.queryParameters[core.Keys.title] ?? + strings.generic.error, + message: + state.uri.queryParameters[core.Keys.message] ?? + strings.errors.unexpected_error, + error: state.uri.queryParameters[core.Keys.error], + onGoHome: () => context.go(core.CoreRoutes.root), + ), + ), + ], + ); + + di.register(router); + return router; + } +{{#auth}} + /// Where a user lands once sign-in or sign-up succeeds. + /// + /// Load the profile / entitlements you need here before routing on. + static Future _onAuthenticated(BuildContext context) async { +{{#analytics}} await AnalyticsHelper.logEvent(AnalyticsEvents.user.authenticatedRedirect); +{{/analytics}} if (context.mounted) { + context.go({{#dashboard}}DashboardRouter.home{{/dashboard}}{{^dashboard}}core.CoreRoutes.home{{/dashboard}}); + } + } +{{/auth}}} +{{^dashboard}} +/// Placeholder landing screen - replace it with your first feature. +class _HomeScreen extends StatelessWidget { + const _HomeScreen(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text(strings.app.name)), + body: Center( + child: {{#developer}}developer.OpenDevToolsWrapper( + // Tap 5x to reach the developer tools. + child: Text(strings.app.welcome_to_app), + ){{/developer}}{{^developer}}Text(strings.app.welcome_to_app){{/developer}}, + ), + ); + } +} +{{/dashboard}} \ No newline at end of file diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/ui/splash_screen.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/ui/splash_screen.dart new file mode 100644 index 0000000..11997e2 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/ui/splash_screen.dart @@ -0,0 +1,61 @@ +{{#auth}}import 'package:auth/auth.dart' as auth; +{{/auth}}{{^dashboard}}import 'package:core/core.dart' as core; +{{/dashboard}}{{#dashboard}}import 'package:dashboard/dashboard.dart'; +{{/dashboard}}import 'package:design_system/design_system.dart'; +{{#auth}}import 'package:di/di.dart'; +{{/auth}}import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:localization/localization.dart'; + +/// First frame the user sees. +/// +/// It exists to give async startup work (session restore, profile fetch) a +/// place to happen before the app commits to a destination. +class SplashScreen extends StatefulWidget { + const SplashScreen({super.key}); + + @override + State createState() => _SplashScreenState(); +} + +class _SplashScreenState extends State { + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) => _redirect()); + } + + Future _redirect() async { + if (!mounted) return; +{{#auth}} + if (!di.get().isSignedIn) { + context.go(auth.AuthRoutes.signIn); + return; + } +{{/auth}} + // TODO: load whatever the first screen needs before navigating. + context.go({{#dashboard}}DashboardRouter.home{{/dashboard}}{{^dashboard}}core.CoreRoutes.home{{/dashboard}}); + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + return Scaffold( + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + strings.app.name, + style: theme.textTheme.headlineMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 32), + const DefaultLoader(), + ], + ), + ), + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/pubspec.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/pubspec.yaml new file mode 100644 index 0000000..b60cd50 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/pubspec.yaml @@ -0,0 +1,61 @@ +name: {{name.snakeCase()}} +description: "{{{description}}}" +publish_to: 'none' +version: 0.1.0+1 + +environment: + sdk: ^3.8.0 + flutter: ">=3.29.0" + +dependencies: + flutter: + sdk: flutter + cupertino_icons: ^1.0.8 + go_router: ^15.1.3 + bloc: ^9.0.0 + navigation_history_observer: ^1.1.1 + core: + path: ../../packages/core + design_system: + path: ../../packages/design_system + di: + path: ../../packages/di + localization: + path: ../../packages/localization + utils: + path: ../../packages/utils +{{#firebase}} firebase_core: ^3.14.0 +{{/firebase}}{{#network}} network: + path: ../../packages/network +{{/network}}{{#analytics}} analytics: + path: ../../packages/analytics +{{/analytics}}{{#crashlytics}} crashlytics: + path: ../../packages/crashlytics +{{/crashlytics}}{{#feature_flags}} feature_flags: + path: ../../packages/feature_flags +{{/feature_flags}}{{#notifications}} notifications: + path: ../../packages/notifications +{{/notifications}}{{#developer}} developer: + path: ../../packages/developer +{{/developer}}{{#auth}} auth: + path: ../../features/auth +{{/auth}}{{#dashboard}} dashboard: + path: ../../features/dashboard +{{/dashboard}} +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +dependency_overrides: + intl: ^0.20.2 + +flutter: + uses-material-design: true +{{#auth}} fonts: + # firebase_ui_auth ships this font but never registers it, so the Google + # and Apple buttons render as tofu unless the app declares it here. + - family: SocialIcons + fonts: + - asset: packages/firebase_ui_auth/fonts/SocialIcons.ttf +{{/auth}} \ No newline at end of file diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/test/{{smoke_test_file}} b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/test/{{smoke_test_file}} new file mode 100644 index 0000000..a87dd87 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/test/{{smoke_test_file}} @@ -0,0 +1,17 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:{{name.snakeCase()}}/app.dart'; +import 'package:{{name.snakeCase()}}/bootstrap.dart' as bootstrap; + +void main() { + testWidgets('the app boots and reaches its first screen', (tester) async { + // Exercises the whole graph: module init order, DI registration and the + // splash redirect. If a module forgets to register something, this fails + // here rather than on a device. + await bootstrap.init(); + await tester.pumpWidget(const App()); + await tester.pumpAndSettle(); + + expect(tester.takeException(), isNull); +{{#dashboard}} expect(find.text('Explore'), findsWidgets); +{{/dashboard}} }); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/README.md b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/README.md index 66a1979..2f493c7 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/README.md +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/README.md @@ -1 +1,15 @@ -# Put all your features in this folder \ No newline at end of file +# Features + +Vertical slices of the product. A feature owns its UI, state and data access, +and exposes two things to the app: + +- an `init()` that registers its services with the locator +- a `Router` exposing the routes the app splices into its route table + +Features may depend on `packages/`, and should avoid depending on each other. + +Add one with: + +```sh +nonstop create package my_feature -o features +``` diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/README.md b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/README.md new file mode 100644 index 0000000..de195f3 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/README.md @@ -0,0 +1,18 @@ +# Auth + +A Melos-managed project for mono-repo, created using NonStop CLI. + +[![nonstop_cli](https://img.shields.io/badge/started%20with-nonstop_cli-166C4E.svg?style=flat-square)](https://pub.dev/packages/nonstop_cli) +[![melos](https://img.shields.io/badge/maintained%20with-melos-f700ff.svg?style=flat-square)](https://github.com/invertase/melos) + +## Installation 💻 + +**❗ In order to start using Auth you must have the [Flutter SDK][flutter_install_link] installed on your machine.** + +Install via `dart pub add`: + +```sh +dart pub add auth +``` + +[flutter_install_link]: https://docs.flutter.dev/get-started/install \ No newline at end of file diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/analytics/analytics.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/analytics/analytics.dart new file mode 100644 index 0000000..e19d0ba --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/analytics/analytics.dart @@ -0,0 +1,101 @@ +{{#analytics}}import 'package:analytics/analytics.dart'; +{{/analytics}}import 'package:firebase_auth/firebase_auth.dart'; + +/// Every auth-related analytics event in one place. +/// +/// Screens call these instead of `AnalyticsHelper` directly so the event names +/// and parameter shapes stay consistent across sign-in, sign-up and recovery. +{{^analytics}}/// +/// Analytics is not enabled in this project, so the log methods are no-ops. +/// Wire them up by adding the `analytics` package back to `pubspec.yaml`. +{{/analytics}}class AuthAnalytics { + const AuthAnalytics._(); + + /// 'google', 'apple' or 'email', derived from the signed-in provider. + static String getAuthMethod(List? providerData) { + if (providerData?.isNotEmpty ?? false) { + final providerId = providerData!.first.providerId; + if (providerId.contains('google')) return 'google'; + if (providerId.contains('apple')) return 'apple'; + } + return 'email'; + } + + static Future logSignInSuccess({ + required String method, + String? userEmail, + }) async { +{{#analytics}} await AnalyticsHelper.logSignIn( + method: method, + parameters: _successParams(userEmail), + ); +{{/analytics}} } + + static Future logSignUpSuccess({ + required String method, + String? userEmail, + }) async { +{{#analytics}} await AnalyticsHelper.logSignUp( + method: method, + parameters: _successParams(userEmail), + ); +{{/analytics}} } + + static Future logUserCreationSuccess({ + required String method, + String? userEmail, + }) async { +{{#analytics}} await AnalyticsHelper.logEvent( + AnalyticsEvents.user.authenticatedRedirect, + parameters: { + 'auth_method': method, + 'flow_type': 'registration', + ..._successParams(userEmail), + }, + ); +{{/analytics}} } + + static Future logSignOutSuccess() async { +{{#analytics}} await AnalyticsHelper.logEvent( + AnalyticsEvents.auth.signOut, + parameters: _successParams(null), + ); +{{/analytics}} } + + /// [flowType] is one of 'sign_in', 'sign_up', 'forgot_password'. + static Future logAuthError({ + required String errorType, + required String flowType, + String? errorMessage, + String? method, + }) async { +{{#analytics}} await AnalyticsHelper.logEvent( + AnalyticsEvents.auth.failed, + parameters: { + 'error_type': errorType, + 'flow_type': flowType, + 'auth_method': method, + 'error_message': errorMessage, + 'timestamp': DateTime.now().toIso8601String(), + }, + ); +{{/analytics}} } + + static Future logSignOutError({required String errorMessage}) async { +{{#analytics}} await AnalyticsHelper.logEvent( + AnalyticsEvents.error.appError, + parameters: { + 'error_type': 'sign_out_failed', + 'error_message': errorMessage, + 'feature': 'auth', + 'timestamp': DateTime.now().toIso8601String(), + }, + ); +{{/analytics}} } +{{#analytics}} + static Map _successParams(String? userEmail) => { + 'success': 'true', + 'user_email_domain': userEmail?.split('@').last, + 'timestamp': DateTime.now().toIso8601String(), + }; +{{/analytics}}} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/auth.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/auth.dart new file mode 100644 index 0000000..520383f --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/auth.dart @@ -0,0 +1,40 @@ +import 'dart:io'; + +import 'package:auth/config/config.dart'; +import 'package:auth/data/index.dart'; +import 'package:core/logger/logger.dart'; +import 'package:di/di.dart'; +import 'package:firebase_ui_auth/firebase_ui_auth.dart'; +import 'package:firebase_ui_oauth_apple/firebase_ui_oauth_apple.dart'; +import 'package:firebase_ui_oauth_google/firebase_ui_oauth_google.dart'; +{{#network}}import 'package:network/network.dart'; +{{/network}} +export 'analytics/analytics.dart'; +export 'config/index.dart'; +export 'constants/index.dart'; +export 'data/index.dart'; +export 'router/router.dart'; +export 'ui/screens/index.dart'; + +/// Registers the auth providers and services. +/// +/// Call this after `Firebase.initializeApp` and before the router is built - +/// route guards resolve [AuthService] from the locator on the first frame. +Future init(AuthConfig config) async { + final logger = di.get(); + logger.i('Initializing Auth...'); + + FirebaseUIAuth.configureProviders([ + EmailAuthProvider(), + GoogleProvider(clientId: config.clientId, iOSPreferPlist: true), + if (Platform.isIOS) AppleProvider(), + ]); + + di.register(AuthServiceImp()); +{{#network}} + // The network layer reads bearer tokens through this provider, so auth has + // to own it - registering it anywhere else risks a stale token on refresh. + di.register(FirebaseAuthTokenProvider()); +{{/network}} + logger.i('Auth initialized'); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/config/config.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/config/config.dart new file mode 100644 index 0000000..a6a04a0 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/config/config.dart @@ -0,0 +1,3 @@ +abstract class AuthConfig { + String get clientId; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/config/config_imp.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/config/config_imp.dart new file mode 100644 index 0000000..693191f --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/config/config_imp.dart @@ -0,0 +1,8 @@ +import 'package:auth/config/config.dart'; + +class DefaultAuthConfig implements AuthConfig { + @override + String clientId; + + DefaultAuthConfig({required this.clientId}); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/config/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/config/index.dart new file mode 100644 index 0000000..5a25c0b --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/config/index.dart @@ -0,0 +1,2 @@ +export 'config.dart'; +export 'config_imp.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/constants/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/constants/index.dart new file mode 100644 index 0000000..a935bb9 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/constants/index.dart @@ -0,0 +1 @@ +export 'routes.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/constants/routes.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/constants/routes.dart new file mode 100644 index 0000000..9289a7a --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/constants/routes.dart @@ -0,0 +1,10 @@ +class AuthRoutes { + AuthRoutes._(); + + static const String auth = '/auth'; + + static const String signIn = '$auth/sign-in'; + static const String signUp = '$auth/sign-up'; + static const String forgotPassword = '$auth/forgot-password'; + static const String resetPassword = '$auth/reset-password'; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/index.dart new file mode 100644 index 0000000..cfb42d9 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/index.dart @@ -0,0 +1,3 @@ +export './services/index.dart'; +{{#network}}export './token/firebase_auth_token_provider.dart'; +{{/network}} \ No newline at end of file diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/services/auth_service.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/services/auth_service.dart new file mode 100644 index 0000000..1c351b4 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/services/auth_service.dart @@ -0,0 +1,7 @@ +abstract class AuthService { + String get uid; + + bool get isSignedIn; + + Future signOut(); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/services/auth_service_imp.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/services/auth_service_imp.dart new file mode 100644 index 0000000..9c407ff --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/services/auth_service_imp.dart @@ -0,0 +1,35 @@ +import 'package:auth/analytics/analytics.dart'; +import 'package:auth/data/services/auth_service.dart'; +import 'package:core/core.dart'; +import 'package:di/di.dart'; +import 'package:firebase_auth/firebase_auth.dart'; + +class AuthServiceImp implements AuthService { + final _logger = di.get(); + final FirebaseAuth _firebaseAuth = FirebaseAuth.instance; + + @override + String get uid => _firebaseAuth.currentUser?.uid ?? ''; + + @override + bool get isSignedIn => _firebaseAuth.currentUser != null; + + @override + Future signOut() async { + try { + _logger.d('Attempting to sign out user'); + await _firebaseAuth.signOut(); + _logger.d('User signed out successfully'); + + // Log successful sign out + AuthAnalytics.logSignOutSuccess(); + } catch (e, s) { + _logger.e('Error signing out user: $e', e, s); + + // Log sign out error + AuthAnalytics.logSignOutError(errorMessage: e.toString()); + + rethrow; + } + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/services/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/services/index.dart new file mode 100644 index 0000000..67ce986 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/services/index.dart @@ -0,0 +1,3 @@ +//GENERATED BARREL FILE +export 'auth_service.dart'; +export 'auth_service_imp.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/token/firebase_auth_token_provider.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/token/firebase_auth_token_provider.dart new file mode 100644 index 0000000..321743f --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/token/firebase_auth_token_provider.dart @@ -0,0 +1,163 @@ +{{#network}}import 'dart:async'; + +import 'package:core/core.dart'; +import 'package:di/di.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:network/network.dart'; + +final _tag = 'FirebaseAuthTokenProvider'; + +class FirebaseAuthTokenProvider implements AuthTokenProvider { + FirebaseAuthTokenProvider() : _logger = di.get() { + _initializeTokenStream(); + } + + final FirebaseAuth _firebaseAuth = FirebaseAuth.instance; + final Logger _logger; + + late final StreamController _tokenController; + StreamSubscription? _authStateSubscription; + String? _cachedToken; + DateTime? _tokenExpiry; + + void _initializeTokenStream() { + _tokenController = StreamController.broadcast(); + + // Listen to auth state changes + _authStateSubscription = _firebaseAuth.authStateChanges().listen( + (User? user) async { + if (user != null) { + try { + final token = await user.getIdToken(); + _cachedToken = token; + _setTokenExpiry(token!); + _tokenController.add(token); + _logger.d('🔐$_tag: Auth token updated for user: ${user.uid}'); + } catch (e, s) { + _logger.e( + '🚨$_tag: Error getting token for user: ${user.uid}', + e, + s, + ); + _cachedToken = null; + _tokenExpiry = null; + _tokenController.add(null); + } + } else { + _cachedToken = null; + _tokenExpiry = null; + _tokenController.add(null); + _logger.d('🔓$_tag: User signed out, token cleared'); + } + }, + onError: (e, s) { + _logger.e('🚨 $_tag: Auth state change error', e, s); + _tokenController.add(null); + }, + ); + } + + void _setTokenExpiry(String token) { + try { + // Firebase ID tokens are typically valid for 1 hour + // We'll refresh 5 minutes before expiry + _tokenExpiry = DateTime.now().add(const Duration(minutes: 55)); + } catch (e) { + _logger.w('⚠️$_tag: Could not determine token expiry, using default $e'); + _tokenExpiry = DateTime.now().add(const Duration(minutes: 55)); + } + } + + @override + Future getValidToken() async { + final user = _firebaseAuth.currentUser; + if (user == null) { + _logger.d('🔓$_tag: No authenticated user available'); + return null; + } + + // Check if cached token is still valid + if (_cachedToken != null && !isTokenExpired) { + _logger.d('🔐$_tag: Using cached token'); + return _cachedToken; + } + + // Get fresh token + try { + _logger.d('🔄$_tag: Fetching fresh token for user: ${user.uid}'); + final token = await user.getIdToken(true); // Force refresh + _cachedToken = token; + _setTokenExpiry(token!); + _tokenController.add(token); + return token; + } catch (e, s) { + _logger.e('🚨$_tag: Error getting fresh token', e, s); + _cachedToken = null; + _tokenExpiry = null; + return null; + } + } + + @override + String? getCurrentToken() { + return _cachedToken; + } + + @override + Stream get tokenStream => _tokenController.stream; + + @override + Future refreshToken() async { + final user = _firebaseAuth.currentUser; + if (user == null) { + _logger.w('🔓$_tag: Cannot refresh token - no authenticated user'); + return null; + } + + try { + _logger.d('🔄$_tag: Refreshing token for user: ${user.uid}'); + final token = await user.getIdToken(true); // Force refresh + _cachedToken = token; + _setTokenExpiry(token!); + _tokenController.add(token); + _logger.i('✅$_tag: Token refreshed successfully'); + return token; + } catch (e, s) { + _logger.e('🚨$_tag: Token refresh failed', e, s); + _cachedToken = null; + _tokenExpiry = null; + _tokenController.add(null); + return null; + } + } + + @override + bool get isAuthenticated => _firebaseAuth.currentUser != null; + + @override + bool get isTokenExpired { + if (_tokenExpiry == null) return true; + return DateTime.now().isAfter(_tokenExpiry!); + } + + @override + Future signOut() async { + try { + _logger.d('🔓$_tag: Signing out user'); + await _firebaseAuth.signOut(); + _cachedToken = null; + _tokenExpiry = null; + _tokenController.add(null); + _logger.i('✅$_tag: User signed out successfully'); + } catch (e, s) { + _logger.e('🚨$_tag: Error signing out user', e, s); + rethrow; + } + } + + void dispose() { + _authStateSubscription?.cancel(); + _tokenController.close(); + _logger.d('🧹$_tag: disposed'); + } +}{{/network}} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/router/router.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/router/router.dart new file mode 100644 index 0000000..0b4c742 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/router/router.dart @@ -0,0 +1,53 @@ +import 'package:auth/constants/index.dart'; +import 'package:auth/data/services/auth_service.dart'; +import 'package:auth/ui/screens/index.dart'; +import 'package:core/core.dart' as core; +import 'package:di/di.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +class AuthRouter extends core.CoreRouter { + final Function(BuildContext context) onSignedIn; + final Function(BuildContext context) onSignedUp; + + AuthRouter({required this.onSignedIn, required this.onSignedUp}); + + @override + List get routes => [ + GoRoute( + path: AuthRoutes.signIn, + builder: (context, state) => SignInScreen(onSignedIn: onSignedIn), + ), + GoRoute( + path: AuthRoutes.signUp, + builder: (context, state) => RegisterScreen(onSignedUp: onSignedUp), + ), + GoRoute( + path: AuthRoutes.forgotPassword, + builder: (context, state) { + final email = state.uri.queryParameters[core.Keys.email]; + return ForgotPasswordScreen(email: email); + }, + ), + ]; +} + +final class GoAuthRoute extends GoRoute { + /// Create a secure route that requires authentication + GoAuthRoute({ + required super.path, + required super.builder, + List super.routes = const [], + super.pageBuilder, + }) : super( + redirect: (context, state) async { + final authService = di.get(); + + if (!authService.isSignedIn) { + return AuthRoutes.signIn; + } + + return null; + }, + ); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/components/footer_builder.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/components/footer_builder.dart new file mode 100644 index 0000000..234ede7 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/components/footer_builder.dart @@ -0,0 +1,70 @@ +import 'package:auth/constants/index.dart'; +import 'package:core/logger/logger.dart'; +import 'package:di/di.dart'; +import 'package:firebase_ui_auth/firebase_ui_auth.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:localization/localization.dart'; + +enum FooterType { signIn, register } + +Widget footerBuilder(BuildContext context, AuthAction action, FooterType type) { + final logger = di.get(); + switch (type) { + case FooterType.signIn: + return _AuthFooter( + title: strings.auth.dont_have_account, + linkText: strings.auth.register, + onTap: () { + logger.d('Navigating to register screen'); + + context.go(AuthRoutes.signUp); + }, + ); + case FooterType.register: + return _AuthFooter( + title: strings.auth.already_have_account, + linkText: strings.auth.sign_in, + onTap: () { + logger.d('Navigating to sign in screen'); + + context.go(AuthRoutes.signIn); + }, + ); + } +} + +class _AuthFooter extends StatelessWidget { + final String title; + final String linkText; + final VoidCallback onTap; + + const _AuthFooter({ + required this.title, + required this.linkText, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(title), + GestureDetector( + onTap: onTap, + child: Text( + linkText, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/components/header_builder.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/components/header_builder.dart new file mode 100644 index 0000000..08b94e5 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/components/header_builder.dart @@ -0,0 +1,15 @@ +import 'package:design_system/design_system.dart' as ds; +import 'package:flutter/widgets.dart'; + +/// Banner shown above the sign-in and register forms. +/// +/// Swap this for your own artwork: add it under the app's `assets:` in +/// pubspec.yaml and switch to `ds.HeaderType.asset` with an `assetPath`. +Widget headerBuilder(BuildContext context) { + return const Padding( + padding: EdgeInsets.all(8), + child: ds.Header( + imageUrl: 'https://picsum.photos/1200/300', + ), + ); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/forgot_password_screen.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/forgot_password_screen.dart new file mode 100644 index 0000000..265c87f --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/forgot_password_screen.dart @@ -0,0 +1,19 @@ +import 'package:auth/ui/components/header_builder.dart'; +import 'package:firebase_ui_auth/firebase_ui_auth.dart' as ui_auth; +import 'package:flutter/material.dart'; + +class ForgotPasswordScreen extends StatelessWidget { + final String? email; + + const ForgotPasswordScreen({super.key, this.email}); + + @override + Widget build(BuildContext context) { + return ui_auth.ForgotPasswordScreen( + email: email, + headerBuilder: (context, constraints, shrinkOffset) { + return headerBuilder(context); + }, + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/index.dart new file mode 100644 index 0000000..2fe2af8 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/index.dart @@ -0,0 +1,4 @@ +//GENERATED BARREL FILE +export 'forgot_password_screen.dart'; +export 'register_screen.dart'; +export 'sign_in_screen.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/register_screen.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/register_screen.dart new file mode 100644 index 0000000..67688ea --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/register_screen.dart @@ -0,0 +1,83 @@ +import 'package:auth/analytics/analytics.dart'; +import 'package:auth/ui/components/footer_builder.dart'; +import 'package:auth/ui/components/header_builder.dart'; +import 'package:core/logger/logger.dart'; +import 'package:di/di.dart'; +import 'package:firebase_ui_auth/firebase_ui_auth.dart' as ui_auth; +import 'package:flutter/material.dart'; + +class RegisterScreen extends StatelessWidget { + const RegisterScreen({super.key, required this.onSignedUp}); + + final void Function(BuildContext context) onSignedUp; + + @override + Widget build(BuildContext context) { + final logger = di.get(); + + return ui_auth.RegisterScreen( + showAuthActionSwitch: false, + actions: [ + ui_auth.AuthStateChangeAction((context, state) async { + logger.d('User registered successfully: ${state.user?.email}'); + + final method = AuthAnalytics.getAuthMethod(state.user?.providerData); + + // Log successful sign in (when user already exists) + AuthAnalytics.logSignInSuccess( + method: method, + userEmail: state.user?.email, + ); + + onSignedUp(context); + }), + ui_auth.AuthStateChangeAction(( + context, + state, + ) async { + logger.d( + 'User created successfully: ${state.credential.user?.email}', + ); + + final method = AuthAnalytics.getAuthMethod( + state.credential.user?.providerData, + ); + + // Log successful sign up + AuthAnalytics.logSignUpSuccess( + method: method, + userEmail: state.credential.user?.email, + ); + + // Log user creation success + AuthAnalytics.logUserCreationSuccess( + method: method, + userEmail: state.credential.user?.email, + ); + + onSignedUp(context); + }), + ui_auth.AuthStateChangeAction(( + context, + state, + ) async { + logger.e('Registration failed: ${state.exception}'); + + // Log auth error + AuthAnalytics.logAuthError( + errorType: state.exception.runtimeType.toString(), + flowType: 'sign_up', + errorMessage: state.exception.toString(), + method: 'email', + ); + }), + ], + footerBuilder: (context, action) { + return footerBuilder(context, action, FooterType.register); + }, + headerBuilder: (context, constraints, shrinkOffset) { + return headerBuilder(context); + }, + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/sign_in_screen.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/sign_in_screen.dart new file mode 100644 index 0000000..13f00f3 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/sign_in_screen.dart @@ -0,0 +1,94 @@ +import 'package:auth/analytics/analytics.dart'; +import 'package:auth/constants/index.dart'; +import 'package:auth/ui/components/footer_builder.dart'; +import 'package:auth/ui/components/header_builder.dart'; +import 'package:core/core.dart' as core; +import 'package:core/logger/logger.dart'; +import 'package:design_system/toast/toasts.dart'; +import 'package:di/di.dart'; +import 'package:firebase_ui_auth/firebase_ui_auth.dart' as ui_auth; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:localization/localization.dart'; + +class SignInScreen extends StatelessWidget { + const SignInScreen({super.key, required this.onSignedIn}); + + final Function(BuildContext context) onSignedIn; + + @override + Widget build(BuildContext context) { + final logger = di.get(); + + return ui_auth.SignInScreen( + showAuthActionSwitch: false, + oauthButtonVariant: ui_auth.OAuthButtonVariant.icon_and_text, + actions: [ + ui_auth.ForgotPasswordAction((context, email) { + logger.d('Navigating to forgot password screen for email: $email'); + + final uri = Uri( + path: AuthRoutes.forgotPassword, + queryParameters: {core.Keys.email: email}, + ); + context.push(uri.toString()); + }), + ui_auth.AuthStateChangeAction((context, state) async { + logger.d('User signed in successfully: ${state.user?.email}'); + + final method = AuthAnalytics.getAuthMethod(state.user?.providerData); + + // Log successful sign in + AuthAnalytics.logSignInSuccess( + method: method, + userEmail: state.user?.email, + ); + + onSignedIn(context); + }), + ui_auth.AuthStateChangeAction(( + context, + state, + ) async { + logger.d( + 'User signed in successfully: ${state.credential.user?.email}', + ); + + final method = AuthAnalytics.getAuthMethod( + state.credential.user?.providerData, + ); + + // Log successful user creation (from sign in screen) + AuthAnalytics.logUserCreationSuccess( + method: method, + userEmail: state.credential.user?.email, + ); + + onSignedIn(context); + }), + ui_auth.AuthStateChangeAction(( + context, + state, + ) async { + logger.e('Sign-in failed: ${state.exception}'); + + // Log auth error + AuthAnalytics.logAuthError( + errorType: state.exception.runtimeType.toString(), + flowType: 'sign_in', + errorMessage: state.exception.toString(), + ); + + Toast.error(context, message: strings.errors.default_error_message); + }), + //TODO: Handle other AuthState + ], + footerBuilder: (context, action) { + return footerBuilder(context, action, FooterType.signIn); + }, + headerBuilder: (context, constraints, shrinkOffset) { + return headerBuilder(context); + }, + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/pubspec.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/pubspec.yaml new file mode 100644 index 0000000..8c82322 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/pubspec.yaml @@ -0,0 +1,40 @@ +name: auth +description: "Firebase Authentication (email, Google, Apple) with go_router integration for {{name.titleCase()}}." +version: 0.1.0 +publish_to: 'none' + +environment: + sdk: ^3.8.0 + flutter: ">=3.29.0" + +dependencies: + flutter: + sdk: flutter + equatable: ^2.0.7 + firebase_auth: ^5.6.0 + firebase_core: ^3.14.0 + firebase_ui_auth: ^1.17.0 + firebase_ui_oauth_apple: ^1.3.2 + firebase_ui_oauth_google: ^1.4.2 + go_router: ^15.1.3 + json_annotation: ^4.9.0 + core: + path: ../../packages/core + design_system: + path: ../../packages/design_system + di: + path: ../../packages/di + localization: + path: ../../packages/localization +{{#firestore}} cloud_firestore: ^5.6.0 +{{/firestore}}{{#network}} network: + path: ../../packages/network +{{/network}}{{#analytics}} analytics: + path: ../../packages/analytics +{{/analytics}} +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/README.md b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/README.md new file mode 100644 index 0000000..4f80f3e --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/README.md @@ -0,0 +1,18 @@ +# Dashboard + +Dashboard feature with bottom navigation + +[![nonstop_cli](https://img.shields.io/badge/started%20with-nonstop_cli-166C4E.svg?style=flat-square)](https://pub.dev/packages/nonstop_cli) +[![melos](https://img.shields.io/badge/maintained%20with-melos-f700ff.svg?style=flat-square)](https://github.com/invertase/melos) + +## Installation 💻 + +**❗ In order to start using Dashboard you must have the [Flutter SDK][flutter_install_link] installed on your machine.** + +Install via `dart pub add`: + +```sh +dart pub add dashboard +``` + +[flutter_install_link]: https://docs.flutter.dev/get-started/install \ No newline at end of file diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/dashboard.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/dashboard.dart new file mode 100644 index 0000000..ffb156e --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/dashboard.dart @@ -0,0 +1,5 @@ +library; + +export 'router/dashboard_router.dart'; +export 'ui/screens/index.dart'; +export 'ui/widgets/index.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/router/dashboard_router.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/router/dashboard_router.dart new file mode 100644 index 0000000..1c08705 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/router/dashboard_router.dart @@ -0,0 +1,51 @@ +{{#auth}}import 'package:auth/auth.dart' as auth; +{{/auth}}import 'package:dashboard/ui/screens/index.dart'; +import 'package:go_router/go_router.dart'; + +{{#auth}}/// Every shell branch is guarded - unauthenticated users bounce to sign-in. +typedef _ShellRoute = auth.GoAuthRoute;{{/auth}}{{^auth}}typedef _ShellRoute = GoRoute;{{/auth}} + +/// Bottom-navigation shell for the signed-in part of the app. +/// +/// Add a tab by adding a [StatefulShellBranch] here and a destination in +/// [DashboardShellScreen]. +class DashboardRouter { + const DashboardRouter._(); + + static const String home = '/home/dashboard'; + static const String explore = '/home/explore'; + static const String profile = '/home/profile'; + + static StatefulShellRoute createShellRoute() { + return StatefulShellRoute.indexedStack( + builder: (context, state, navigationShell) => + DashboardShellScreen(navigationShell: navigationShell), + branches: [ + StatefulShellBranch( + routes: [ + _ShellRoute( + path: home, + builder: (context, state) => const HomeTabScreen(), + ), + ], + ), + StatefulShellBranch( + routes: [ + _ShellRoute( + path: explore, + builder: (context, state) => const ExploreTabScreen(), + ), + ], + ), + StatefulShellBranch( + routes: [ + _ShellRoute( + path: profile, + builder: (context, state) => const ProfileTabScreen(), + ), + ], + ), + ], + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/dashboard_shell_screen.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/dashboard_shell_screen.dart new file mode 100644 index 0000000..6c890f2 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/dashboard_shell_screen.dart @@ -0,0 +1,54 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:localization/localization.dart'; + +/// Hosts the bottom navigation bar and keeps each tab's navigation stack alive. +/// +/// Add a tab by adding a [StatefulShellBranch] in `DashboardRouter` and a +/// matching destination here - the two lists are positional. +class DashboardShellScreen extends StatefulWidget { + const DashboardShellScreen({super.key, required this.navigationShell}); + + final StatefulNavigationShell navigationShell; + + @override + State createState() => _DashboardShellScreenState(); +} + +class _DashboardShellScreenState extends State { + void _onDestinationSelected(int index) { + widget.navigationShell.goBranch( + index, + initialLocation: index == widget.navigationShell.currentIndex, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: widget.navigationShell, + bottomNavigationBar: NavigationBar( + selectedIndex: widget.navigationShell.currentIndex, + onDestinationSelected: _onDestinationSelected, + destinations: [ + NavigationDestination( + icon: const Icon(NavigationIcons.home), + selectedIcon: const Icon(NavigationIcons.homeSelected), + label: strings.nav.home, + ), + const NavigationDestination( + icon: Icon(NavigationIcons.explore), + selectedIcon: Icon(NavigationIcons.exploreSelected), + label: 'Explore', + ), + NavigationDestination( + icon: const Icon(NavigationIcons.profile), + selectedIcon: const Icon(NavigationIcons.profileSelected), + label: strings.nav.profile, + ), + ], + ), + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/explore_tab_screen.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/explore_tab_screen.dart new file mode 100644 index 0000000..1dfa7b9 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/explore_tab_screen.dart @@ -0,0 +1,21 @@ +import 'package:dashboard/ui/widgets/index.dart'; +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; + +class ExploreTabScreen extends StatelessWidget { + const ExploreTabScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Explore')), + body: const SafeArea( + child: PlaceholderTab( + icon: NavigationIcons.exploreSelected, + title: 'Explore', + subtitle: 'Your second tab. Replace this with a real feature.', + ), + ), + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/home_tab_screen.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/home_tab_screen.dart new file mode 100644 index 0000000..340cac9 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/home_tab_screen.dart @@ -0,0 +1,22 @@ +import 'package:dashboard/ui/widgets/index.dart'; +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:localization/localization.dart'; + +class HomeTabScreen extends StatelessWidget { + const HomeTabScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text(strings.nav.dashboard)), + body: SafeArea( + child: PlaceholderTab( + icon: NavigationIcons.homeSelected, + title: strings.app.welcome_to_app, + subtitle: strings.app.description, + ), + ), + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/index.dart new file mode 100644 index 0000000..8bebb72 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/index.dart @@ -0,0 +1,4 @@ +export 'dashboard_shell_screen.dart'; +export 'explore_tab_screen.dart'; +export 'home_tab_screen.dart'; +export 'profile_tab_screen.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/profile_tab_screen.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/profile_tab_screen.dart new file mode 100644 index 0000000..977abad --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/profile_tab_screen.dart @@ -0,0 +1,87 @@ +{{#auth}}import 'package:auth/auth.dart' as auth; +{{/auth}}import 'package:design_system/design_system.dart'; +{{#developer}}import 'package:developer/developer.dart'; +{{/developer}}{{#auth}}import 'package:di/di.dart'; +{{/auth}}import 'package:flutter/material.dart'; +{{#auth}}import 'package:go_router/go_router.dart'; +{{/auth}}import 'package:localization/localization.dart'; + +/// Account tab: identity, settings entry points, sign out. +/// +/// This is deliberately thin - hang your real profile feature off it. +class ProfileTabScreen extends StatelessWidget { + const ProfileTabScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text(strings.profile.profile)), + body: SafeArea( + child: ListView( + children: [ + const SizedBox(height: 16), + {{#developer}}// Tap the avatar 5x to open developer tools. + OpenDevToolsWrapper(child: const _ProfileAvatar()),{{/developer}}{{^developer}}const _ProfileAvatar(),{{/developer}} + const SizedBox(height: 24), + ListTile( + leading: const Icon(NavigationIcons.settings), + title: Text(strings.profile.settings), + trailing: const Icon(Icons.chevron_right), + onTap: () {}, + ), + {{#auth}}ListTile( + leading: const Icon(Icons.logout), + title: Text(strings.auth.sign_out), + onTap: () => _confirmSignOut(context), + ),{{/auth}} + ], + ), + ), + ); + } +{{#auth}} + Future _confirmSignOut(BuildContext context) async { + final confirmed = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(strings.auth.sign_out), + content: Text(strings.auth.sign_out_confirmation), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: Text(strings.generic.cancel), + ), + FilledButton( + onPressed: () => Navigator.of(context).pop(true), + child: Text(strings.auth.sign_out), + ), + ], + ), + ); + + if (confirmed != true || !context.mounted) return; + + await di.get().signOut(); + if (context.mounted) context.go(auth.AuthRoutes.signIn); + } +{{/auth}}} + +class _ProfileAvatar extends StatelessWidget { + const _ProfileAvatar(); + + @override + Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Center( + child: CircleAvatar( + radius: 44, + backgroundColor: colorScheme.primaryContainer, + child: Icon( + NavigationIcons.profileSelected, + size: 44, + color: colorScheme.onPrimaryContainer, + ), + ), + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/widgets/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/widgets/index.dart new file mode 100644 index 0000000..7abc376 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/widgets/index.dart @@ -0,0 +1 @@ +export 'placeholder_tab.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/widgets/placeholder_tab.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/widgets/placeholder_tab.dart new file mode 100644 index 0000000..e367fed --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/widgets/placeholder_tab.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; + +/// Drop-in "nothing here yet" tab body. +/// +/// Delete this the moment a tab grows real content. +class PlaceholderTab extends StatelessWidget { + const PlaceholderTab({ + super.key, + required this.icon, + required this.title, + required this.subtitle, + this.action, + }); + + final IconData icon; + final String title; + final String subtitle; + final Widget? action; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final colorScheme = theme.colorScheme; + final textTheme = theme.textTheme; + + return Center( + child: Padding( + padding: const EdgeInsets.all(32), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + padding: const EdgeInsets.all(24), + decoration: BoxDecoration( + color: colorScheme.primary.withValues(alpha: 0.1), + shape: BoxShape.circle, + ), + child: Icon(icon, size: 72, color: colorScheme.primary), + ), + const SizedBox(height: 32), + Text( + title, + style: textTheme.headlineMedium?.copyWith( + color: colorScheme.onSurface, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 16), + Text( + subtitle, + style: textTheme.bodyLarge?.copyWith( + color: colorScheme.onSurfaceVariant, + height: 1.5, + ), + textAlign: TextAlign.center, + ), + if (action != null) ...[const SizedBox(height: 32), action!], + ], + ), + ), + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/pubspec.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/pubspec.yaml new file mode 100644 index 0000000..aa88eaf --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{dashboard_dir}}/pubspec.yaml @@ -0,0 +1,32 @@ +name: dashboard +description: "Bottom-navigation shell and starter tabs for {{name.titleCase()}}." +version: 0.1.0 +publish_to: 'none' + +environment: + sdk: ^3.8.0 + flutter: ">=3.29.0" + +dependencies: + flutter: + sdk: flutter + go_router: ^15.1.3 + core: + path: ../../packages/core + design_system: + path: ../../packages/design_system + di: + path: ../../packages/di + localization: + path: ../../packages/localization +{{#auth}} auth: + path: ../auth +{{/auth}}{{#developer}} developer: + path: ../../packages/developer +{{/developer}} +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/melos.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/melos.yaml index 7b645ab..338f220 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/melos.yaml +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/melos.yaml @@ -3,14 +3,25 @@ name: {{ name.snakeCase() }}_workspace ############################################################################### # MONOREPO CONFIGURATION ############################################################################### -# Define the packages that make up your monorepo -# Customize these patterns to match your project structure +# apps/ - runnable Flutter applications +# features/ - vertical slices (ui + state + data) that an app composes +# packages/ - horizontal capabilities shared by features (network, design, ...) +# plugins/ - platform channels and native integrations packages: - apps/* - features/* - packages/* - plugins/* +command: + bootstrap: + dependencies: + json_annotation: ^4.9.0 + dev_dependencies: + build_runner: ^2.4.15 + flutter_lints: ^5.0.0 + json_serializable: ^6.9.5 + ############################################################################### # SCRIPTS ############################################################################### @@ -25,8 +36,11 @@ scripts: lint: description: "Run format and analyze all packages" + # `dart format` has no exclude of its own, so build output and the plugin + # sources CocoaPods symlinks into the app are filtered out here. run: | - dart format --set-exit-if-changed . && + find . -name '*.dart' -not -path '*/build/*' -not -path '*/.dart_tool/*' \ + -not -path '*/.symlinks/*' -exec dart format --set-exit-if-changed {} + && dart analyze --fatal-infos . exec: concurrency: 5 @@ -49,13 +63,23 @@ scripts: dirExists: lib generate: - description: "Generate code for Dart packages using build_runner" + description: "Run build_runner in every package that needs codegen" run: dart run build_runner build --delete-conflicting-outputs exec: concurrency: 1 packageFilters: dependsOn: build_runner + generate:i69n: + description: "Regenerate localization strings from messages.i69n.yaml" + run: | + dart run build_runner build --delete-conflicting-outputs && + dart format lib/messages.i69n.dart + exec: + concurrency: 1 + packageFilters: + scope: "localization" + test: description: "Run all tests (Dart and Flutter)" run: melos run test:select --no-select @@ -72,8 +96,7 @@ scripts: ############################################################################### # IDE CONFIGURATION ############################################################################### -# Optional: Configure IDE integration ide: intellij: enabled: true - moduleNamePrefix: '' \ No newline at end of file + moduleNamePrefix: '' diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/README.md b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/README.md index e880883..528e3dc 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/README.md +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/README.md @@ -1 +1,13 @@ -# Put all your shared packages in this folder +# Packages + +Horizontal capabilities shared across features: networking, design system, +logging, analytics, dependency injection. + +A package here must not depend on a `features/` package - dependencies point +inward, from features to packages. + +Add one with: + +```sh +nonstop create package my_package -o packages +``` diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/README.md b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/README.md new file mode 100644 index 0000000..cb34437 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/README.md @@ -0,0 +1,18 @@ +# Core + +A Melos-managed project for mono-repo, created using NonStop CLI. + +[![nonstop_cli](https://img.shields.io/badge/started%20with-nonstop_cli-166C4E.svg?style=flat-square)](https://pub.dev/packages/nonstop_cli) +[![melos](https://img.shields.io/badge/maintained%20with-melos-f700ff.svg?style=flat-square)](https://github.com/invertase/melos) + +## Installation 💻 + +**❗ In order to start using Core you must have the [Flutter SDK][flutter_install_link] installed on your machine.** + +Install via `dart pub add`: + +```sh +dart pub add core +``` + +[flutter_install_link]: https://docs.flutter.dev/get-started/install \ No newline at end of file diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event.dart new file mode 100644 index 0000000..0e27e43 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event.dart @@ -0,0 +1,27 @@ +import 'package:equatable/equatable.dart'; + +abstract class GlobalEventType extends Equatable { + const GlobalEventType(); + + @override + List get props => []; +} + +class RefreshSurveyAssignment extends GlobalEventType { + final String? id; + + const RefreshSurveyAssignment(this.id); +} + +class RefreshProfile extends GlobalEventType { + const RefreshProfile(); +} + +class FireGlobalEvent extends Equatable { + final GlobalEventType eventType; + + const FireGlobalEvent({required this.eventType}); + + @override + List get props => [eventType]; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event_channel_bloc.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event_channel_bloc.dart new file mode 100644 index 0000000..128e05e --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event_channel_bloc.dart @@ -0,0 +1,41 @@ +import 'package:bloc/bloc.dart'; +import 'package:core/bloc/global_event.dart'; +import 'package:core/bloc/global_event_channel_state.dart'; +import 'package:core/logger/logger.dart'; +import 'package:di/di.dart'; + +class GlobalEventChannel extends Bloc { + final Logger _logger = di.get(); + + GlobalEventChannel({required int maxRecentEvents}) + : super(GlobalEventState(maxRecentEvents: maxRecentEvents)) { + on(_onFireGlobalEvent); + } + + void _onFireGlobalEvent( + FireGlobalEvent event, + Emitter emit, + ) { + _logger.i('GlobalEventChannel: Processing ${event.eventType.runtimeType}'); + + final eventType = event.eventType; + final newHistory = List.from(state.history); + newHistory.insert(0, eventType); + + if (newHistory.length > state.maxRecentEvents) { + newHistory.removeLast(); + } + + final newEventCounts = Map.from(state.eventCounts); + final eventTypeKey = eventType.runtimeType; + newEventCounts[eventTypeKey] = (newEventCounts[eventTypeKey] ?? 0) + 1; + + emit( + state.copyWith( + history: newHistory, + eventCounts: newEventCounts, + current: eventType, + ), + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event_channel_provider.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event_channel_provider.dart new file mode 100644 index 0000000..eb84614 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event_channel_provider.dart @@ -0,0 +1,92 @@ +import 'package:core/bloc/global_event_channel_bloc.dart'; +import 'package:core/bloc/global_event_channel_state.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class GlobalEventChannelProvider extends StatelessWidget { + final Widget child; + final int maxRecentEvents; + final bool lazy; + + const GlobalEventChannelProvider({ + super.key, + required this.child, + this.maxRecentEvents = 50, + this.lazy = true, + }); + + @override + Widget build(BuildContext context) { + return BlocProvider( + lazy: lazy, + create: (context) => GlobalEventChannel(maxRecentEvents: maxRecentEvents), + child: child, + ); + } +} + +class GlobalEventChannelBuilder extends StatelessWidget { + final BlocWidgetBuilder builder; + final BlocBuilderCondition? buildWhen; + + const GlobalEventChannelBuilder({ + super.key, + required this.builder, + this.buildWhen, + }); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + buildWhen: buildWhen, + builder: builder, + ); + } +} + +class GlobalEventChannelListener extends StatelessWidget { + final Widget child; + final BlocWidgetListener listener; + final BlocListenerCondition? listenWhen; + + const GlobalEventChannelListener({ + super.key, + required this.child, + required this.listener, + this.listenWhen, + }); + + @override + Widget build(BuildContext context) { + return BlocListener( + listenWhen: listenWhen, + listener: listener, + child: child, + ); + } +} + +class GlobalEventChannelConsumer extends StatelessWidget { + final BlocWidgetBuilder builder; + final BlocWidgetListener listener; + final BlocBuilderCondition? buildWhen; + final BlocListenerCondition? listenWhen; + + const GlobalEventChannelConsumer({ + super.key, + required this.builder, + required this.listener, + this.buildWhen, + this.listenWhen, + }); + + @override + Widget build(BuildContext context) { + return BlocConsumer( + buildWhen: buildWhen, + listenWhen: listenWhen, + builder: builder, + listener: listener, + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event_channel_state.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event_channel_state.dart new file mode 100644 index 0000000..50cf330 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event_channel_state.dart @@ -0,0 +1,33 @@ +import 'package:core/bloc/global_event.dart'; +import 'package:equatable/equatable.dart'; + +class GlobalEventState extends Equatable { + final List history; + final Map eventCounts; + final GlobalEventType? current; + final int maxRecentEvents; + + const GlobalEventState({ + this.history = const [], + this.eventCounts = const {}, + this.current, + this.maxRecentEvents = 50, + }); + + GlobalEventState copyWith({ + List? history, + Map? eventCounts, + GlobalEventType? current, + int? maxRecentEvents, + }) { + return GlobalEventState( + history: history ?? this.history, + eventCounts: eventCounts ?? this.eventCounts, + current: current ?? this.current, + maxRecentEvents: maxRecentEvents ?? this.maxRecentEvents, + ); + } + + @override + List get props => [history, eventCounts, current, maxRecentEvents]; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/index.dart new file mode 100644 index 0000000..8d8d5d4 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/index.dart @@ -0,0 +1,19 @@ +//GENERATED BARREL FILE + +import 'package:core/bloc/global_event.dart'; +import 'package:core/bloc/global_event_channel_bloc.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +export 'global_event.dart'; +export 'global_event_channel_bloc.dart'; +export 'global_event_channel_provider.dart'; +export 'global_event_channel_state.dart'; + +extension GlobalEventChannelExtension on BuildContext { + /// Fires a global event with the given [eventType]. + void fire(GlobalEventType eventType) { + final bloc = read(); + bloc.add(FireGlobalEvent(eventType: eventType)); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/defaults.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/defaults.dart new file mode 100644 index 0000000..9ca3832 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/defaults.dart @@ -0,0 +1,6 @@ +/// App-wide default values. +class Defaults { + Defaults._(); + + static const int defaultPageSize = 20; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/environment.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/environment.dart new file mode 100644 index 0000000..205f857 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/environment.dart @@ -0,0 +1,18 @@ +/// Build-time configuration. +/// +/// Every value is a `--dart-define`, so the same binary can be pointed at a +/// different backend without a code change: +/// +/// ```sh +/// flutter run --dart-define=BASE_URL=https://staging.example.com +/// ``` +class Environment { + Environment._(); + + static const bool useEmulators = bool.fromEnvironment('USE_EMULATORS'); + + static const String baseUrl = String.fromEnvironment( + 'BASE_URL', + defaultValue: 'https://api.example.com', + ); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/index.dart new file mode 100644 index 0000000..4db272b --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/index.dart @@ -0,0 +1,4 @@ +export 'defaults.dart'; +export 'environment.dart'; +export 'keys.dart'; +export 'routes.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/keys.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/keys.dart new file mode 100644 index 0000000..2faa261 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/keys.dart @@ -0,0 +1,11 @@ +/// Query-parameter and `extra` keys shared between routers and screens. +/// +/// Keep these here rather than inline so a rename is a single edit. +class Keys { + Keys._(); + + static const String email = 'email'; + static const String title = 'title'; + static const String message = 'message'; + static const String error = 'error'; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/routes.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/routes.dart new file mode 100644 index 0000000..51921d5 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/constants/routes.dart @@ -0,0 +1,13 @@ +/// Paths owned by the app shell itself. +/// +/// Feature packages declare their own route constants; these are the ones +/// every app has. +class CoreRoutes { + CoreRoutes._(); + + static const String root = '/'; + static const String home = '/home'; + static const String dashboard = '/home/dashboard'; + static const String profile = '/home/profile'; + static const String error = '/error'; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/converters/datetime_converter.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/converters/datetime_converter.dart new file mode 100644 index 0000000..7c477d7 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/converters/datetime_converter.dart @@ -0,0 +1,35 @@ +import 'package:intl/intl.dart'; + +final class DateTimeConverter { + const DateTimeConverter(); + + /// Converts a [DateTime] to a [String] in the format 'MMMM d, y'. + /// If the [date] is null, it returns an empty string. + /// + /// Example: + /// ```dart + /// DateTime date = DateTime.now(); + /// String formattedDate = DateTimeConverter.toViewFormat(date); + /// print(formattedDate); // Output: 'October 1, 2023' + /// ``` + static String toViewFormat(DateTime? date) { + if (date == null) return ''; + final dateFormate = DateFormat('MMMM d, y'); + + return dateFormate.format(date.toLocal()); + } + + static String toViewFormatWithTime(DateTime? date) { + if (date == null) return ''; + final dateFormate = DateFormat('MMMM d, y, h:mm a'); + + return dateFormate.format(date); + } + + static String toViewFormatTime(DateTime? date) { + if (date == null) return ''; + final dateFormate = DateFormat('h:mm:ss a'); + + return dateFormate.format(date); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/converters/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/converters/index.dart new file mode 100644 index 0000000..bac47b7 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/converters/index.dart @@ -0,0 +1,3 @@ +//GENERATED BARREL FILE +export 'datetime_converter.dart'; +export 'timestamp_converter.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/converters/timestamp_converter.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/converters/timestamp_converter.dart new file mode 100644 index 0000000..3691e46 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/converters/timestamp_converter.dart @@ -0,0 +1,44 @@ +{{#firestore}}import 'package:cloud_firestore/cloud_firestore.dart'; +{{/firestore}}import 'package:json_annotation/json_annotation.dart'; + +/// Accepts the three shapes a timestamp arrives in - ISO string,{{#firestore}} +/// Firestore [Timestamp],{{/firestore}} or the `{_seconds, _nanoseconds}` map that +/// Cloud Functions emit - and always writes back UTC ISO-8601. +class TimestampConverter implements JsonConverter { + const TimestampConverter(); + + @override + DateTime fromJson(dynamic json) { + final parsed = _tryParse(json); + if (parsed == null) { + throw ArgumentError('Invalid timestamp format: $json'); + } + return parsed; + } + + @override + String? toJson(DateTime? date) => date?.toUtc().toIso8601String(); +} + +class TimestampConverterNullable implements JsonConverter { + const TimestampConverterNullable(); + + @override + DateTime? fromJson(dynamic json) => _tryParse(json); + + @override + String? toJson(DateTime? date) => date?.toUtc().toIso8601String(); +} + +DateTime? _tryParse(dynamic json) { + if (json is String && json.isNotEmpty) return DateTime.parse(json).toLocal(); +{{#firestore}} if (json is Timestamp) return json.toDate(); +{{/firestore}} if (json is Map) { + final seconds = json['_seconds'] as int; + final nanoseconds = json['_nanoseconds'] as int? ?? 0; + return DateTime.fromMillisecondsSinceEpoch( + seconds * 1000 + nanoseconds ~/ 1000000, + ); + } + return null; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/core.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/core.dart new file mode 100644 index 0000000..06b8df3 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/core.dart @@ -0,0 +1,19 @@ +library; + +import 'package:core/logger/logger.dart'; +import 'package:di/di.dart'; + +export 'bloc/index.dart'; +export 'constants/index.dart'; +export 'converters/index.dart'; +export 'errors/index.dart'; +export 'extensions/index.dart'; +export 'logger/logger.dart'; +export 'observer/index.dart'; +export 'router/core_router.dart'; + +Future init() async { + registerLoggerWithDI(); + final logger = di.get(); + logger.i('Core module initialized'); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/developer/emulators.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/developer/emulators.dart new file mode 100644 index 0000000..9586507 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/developer/emulators.dart @@ -0,0 +1,25 @@ +{{#firebase}}{{#auth}}import 'package:firebase_auth/firebase_auth.dart'; +{{/auth}}{{#firestore}}import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:cloud_functions/cloud_functions.dart'; +{{/firestore}}import 'package:core/logger/logger.dart'; +import 'package:di/di.dart'; + +/// Points every Firebase SDK at the local emulator suite. +/// +/// Ports match the defaults in `firebase.json`; change both together. +Future init() async { + final logger = di.get(); + logger.i('Using Firebase emulators'); + try { +{{#auth}} await FirebaseAuth.instance.useAuthEmulator('localhost', 9099); +{{/auth}}{{#firestore}} FirebaseFirestore.instance.useFirestoreEmulator('localhost', 8080); + FirebaseFunctions.instance.useFunctionsEmulator('localhost', 5001); +{{/firestore}} logger.i('Successfully initialised the Firebase emulators'); + } catch (error, stackTrace) { + logger.e('Failed to initialise the Firebase emulators:', error, stackTrace); + } +} +{{/firebase}}{{^firebase}}/// No Firebase services are enabled in this project, so there is nothing to +/// point at an emulator. Kept as a no-op so bootstrap stays uniform. +Future init() async {} +{{/firebase}} \ No newline at end of file diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/errors/exceptions.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/errors/exceptions.dart new file mode 100644 index 0000000..704f0bb --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/errors/exceptions.dart @@ -0,0 +1,11 @@ +import 'package:localization/localization.dart'; + +class CoreException implements Exception { + final String message; + + CoreException({required this.message}); +} + +class UserNotFoundException extends CoreException { + UserNotFoundException() : super(message: strings.errors.user_not_found); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/errors/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/errors/index.dart new file mode 100644 index 0000000..c6bf010 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/errors/index.dart @@ -0,0 +1,2 @@ +//GENERATED BARREL FILE +export 'exceptions.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/extensions/basic.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/extensions/basic.dart new file mode 100644 index 0000000..5d04989 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/extensions/basic.dart @@ -0,0 +1,7 @@ +extension BasicStringExtensions on String? { + bool get asBool { + if (this == null) return false; + final lower = this!.toLowerCase(); + return lower == 'true' || lower == '1'; + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/extensions/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/extensions/index.dart new file mode 100644 index 0000000..0f1b76c --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/extensions/index.dart @@ -0,0 +1 @@ +export 'basic.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/logger/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/logger/index.dart new file mode 100644 index 0000000..48419fd --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/logger/index.dart @@ -0,0 +1,3 @@ +//GENERATED BARREL FILE +export 'logger.dart'; +export 'talker_logger_impl.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/logger/logger.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/logger/logger.dart new file mode 100644 index 0000000..b87be14 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/logger/logger.dart @@ -0,0 +1,22 @@ +import 'package:core/logger/talker_logger_impl.dart'; +import 'package:di/di.dart'; +import 'package:talker_flutter/talker_flutter.dart'; + +void registerLoggerWithDI() { + final talker = TalkerFlutter.init(); + final logger = TalkerLoggerImpl(talker); + + di.register(logger); +} + +abstract class Logger { + Object get logger; + + void d(String message); + + void i(String message); + + void w(String message); + + void e(String message, [Object? error, StackTrace? stackTrace]); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/logger/talker_logger_impl.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/logger/talker_logger_impl.dart new file mode 100644 index 0000000..2db6aee --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/logger/talker_logger_impl.dart @@ -0,0 +1,31 @@ +import 'package:core/logger/logger.dart'; +import 'package:talker_flutter/talker_flutter.dart'; + +class TalkerLoggerImpl implements Logger { + final Talker _talker; + + TalkerLoggerImpl(this._talker); + + @override + void d(String message) { + _talker.debug(message); + } + + @override + void i(String message) { + _talker.info(message); + } + + @override + void w(String message) { + _talker.warning(message); + } + + @override + void e(String message, [Object? error, StackTrace? stackTrace]) { + _talker.error(message, error, stackTrace); + } + + @override + Object get logger => _talker; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/observer/core_bloc_observer.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/observer/core_bloc_observer.dart new file mode 100644 index 0000000..1de8928 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/observer/core_bloc_observer.dart @@ -0,0 +1,34 @@ +import 'package:bloc/bloc.dart'; +import 'package:core/core.dart'; +import 'package:di/di.dart'; +import 'package:talker_bloc_logger/talker_bloc_logger_observer.dart'; +import 'package:talker_bloc_logger/talker_bloc_logger_settings.dart'; +import 'package:talker_flutter/talker_flutter.dart'; + +class CoreBlocObserver extends BlocObserver { + final TalkerBlocObserver _observer; + + CoreBlocObserver() + : _observer = TalkerBlocObserver( + talker: di.get().logger as Talker, + settings: TalkerBlocLoggerSettings(printEventFullData: true), + ); + + @override + void onEvent(Bloc bloc, Object? event) { + super.onEvent(bloc, event); + _observer.onEvent(bloc, event); + } + + @override + void onTransition(Bloc bloc, Transition transition) { + super.onTransition(bloc, transition); + _observer.onTransition(bloc, transition); + } + + @override + void onError(BlocBase bloc, Object error, StackTrace stackTrace) { + super.onError(bloc, error, stackTrace); + _observer.onError(bloc, error, stackTrace); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/observer/core_route_observer.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/observer/core_route_observer.dart new file mode 100644 index 0000000..776e95c --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/observer/core_route_observer.dart @@ -0,0 +1,89 @@ +import 'package:core/core.dart'; +import 'package:di/di.dart'; +import 'package:flutter/material.dart'; +import 'package:navigation_history_observer/navigation_history_observer.dart'; +import 'package:talker_flutter/talker_flutter.dart'; + +class CoreRouteObserver extends NavigatorObserver { + CoreRouteObserver() : _observer = di.get().logger as Talker; + + final Talker _observer; + + @override + void didPush(Route route, Route? previousRoute) { + super.didPush(route, previousRoute); + if (route.settings.name == null) { + return; + } + _observer.logCustom(TalkerRouteLog(route: route, type: RouteLogType.push)); + } + + @override + void didPop(Route route, Route? previousRoute) { + super.didPop(route, previousRoute); + if (route.settings.name == null) { + return; + } + _observer.logCustom(TalkerRouteLog(route: route, type: RouteLogType.pop)); + } + + @override + void didRemove(Route route, Route? previousRoute) { + super.didRemove(route, previousRoute); + if (route.settings.name == null) { + return; + } + _observer.logCustom( + TalkerRouteLog(route: route, type: RouteLogType.remove), + ); + } + + @override + void didReplace({Route? newRoute, Route? oldRoute}) { + super.didReplace(newRoute: newRoute, oldRoute: oldRoute); + if (newRoute?.settings.name == null) { + return; + } + _observer.logCustom( + TalkerRouteLog(route: newRoute!, type: RouteLogType.replace), + ); + } +} + +enum RouteLogType { push, pop, remove, replace } + +class TalkerRouteLog extends TalkerLog { + TalkerRouteLog({required Route route, required RouteLogType type}) + : super(_createMessage(route, type)); + + @override + AnsiPen get pen => AnsiPen()..xterm(135); + + @override + String get key => TalkerLogType.route.key; + + static String _createMessage(Route route, RouteLogType type) { + final buffer = StringBuffer(); + buffer.write(type.name); + buffer.write(' route named '); + buffer.write(route.settings.name ?? 'null'); + + final args = route.settings.arguments; + if (args != null) { + buffer.write('\nArguments: $args'); + } + + final historyObserver = NavigationHistoryObserver(); + final history = historyObserver.history; + if (history.isEmpty) { + buffer.write('\nHistory: na'); + } else { + final historyAsString = historyObserver.history + .map((r) => r.settings.name) + .join(' -> '); + buffer.write('\nHistory: $historyAsString'); + } + + return buffer.toString(); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/observer/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/observer/index.dart new file mode 100644 index 0000000..1f24d43 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/observer/index.dart @@ -0,0 +1,3 @@ +//GENERATED BARREL FILE +export 'core_bloc_observer.dart'; +export 'core_route_observer.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/router/core_router.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/router/core_router.dart new file mode 100644 index 0000000..3cd154c --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/router/core_router.dart @@ -0,0 +1,5 @@ +import 'package:go_router/go_router.dart'; + +abstract class CoreRouter { + List get routes; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/pubspec.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/pubspec.yaml new file mode 100644 index 0000000..2076f23 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/pubspec.yaml @@ -0,0 +1,39 @@ +name: core +description: "Cross-cutting foundation for {{name.titleCase()}}: logging, DI wiring, routing contracts, error types and BLoC plumbing." +version: 0.1.0 +publish_to: 'none' + +environment: + sdk: ^3.8.0 + flutter: ">=3.29.0" + +dependencies: + flutter: + sdk: flutter + bloc: ^9.0.0 + equatable: ^2.0.5 + flutter_bloc: ^9.1.1 + go_router: ^15.1.3 + intl: ^0.20.2 + json_annotation: ^4.9.0 + navigation_history_observer: ^1.1.1 + talker_bloc_logger: ^4.9.1 + talker_flutter: ^4.4.1 + di: + path: ../di + localization: + path: ../localization +{{#firebase}} firebase_core: ^3.14.0 +{{/firebase}}{{#auth}} firebase_auth: ^5.3.4 +{{/auth}}{{#firestore}} cloud_firestore: ^5.5.3 + cloud_functions: ^5.2.3 +{{/firestore}} +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +dependency_overrides: + intl: ^0.20.2 + +flutter: diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/README.md b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/README.md new file mode 100644 index 0000000..47e41eb --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/README.md @@ -0,0 +1,18 @@ +# Design System + +A Melos-managed project for mono-repo, created using NonStop CLI. + +[![nonstop_cli](https://img.shields.io/badge/started%20with-nonstop_cli-166C4E.svg?style=flat-square)](https://pub.dev/packages/nonstop_cli) +[![melos](https://img.shields.io/badge/maintained%20with-melos-f700ff.svg?style=flat-square)](https://github.com/invertase/melos) + +## Installation 💻 + +**❗ In order to start using Design System you must have the [Flutter SDK][flutter_install_link] installed on your machine.** + +Install via `dart pub add`: + +```sh +dart pub add design_system +``` + +[flutter_install_link]: https://docs.flutter.dev/get-started/install \ No newline at end of file diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/asset_image.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/asset_image.dart new file mode 100644 index 0000000..d321f7c --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/asset_image.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; + +class AssetImage extends StatelessWidget { + final String assetPath; + final double? width; + final double? height; + final BoxFit boxFit; + final String? package; + + const AssetImage({ + super.key, + required this.assetPath, + this.width, + this.height, + this.boxFit = BoxFit.cover, + this.package, + }); + @override + Widget build(BuildContext context) { + // if asset is svg, use SvgPicture.asset + if (assetPath.endsWith('.svg')) { + return SvgPicture.asset( + assetPath, + width: width, + height: height, + fit: boxFit, + package: package, + errorBuilder: (context, error, stackTrace) { + return Icon( + Icons.image, + size: 32, + color: Theme.of(context).colorScheme.onSurface, + ); + }, + ); + } + + // otherwise, use Image.asset + return Image.asset( + assetPath, + width: width, + height: height, + fit: boxFit, + package: package, + errorBuilder: (context, error, stackTrace) { + return Icon( + Icons.image, + size: 32, + color: Theme.of(context).colorScheme.onSurface, + ); + }, + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/auth_headers_builder.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/auth_headers_builder.dart new file mode 100644 index 0000000..09d09e4 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/auth_headers_builder.dart @@ -0,0 +1,116 @@ +{{#network}}import 'package:core/core.dart'; +import 'package:di/di.dart'; +import 'package:flutter/material.dart'; +import 'package:network/network.dart'; + +/// A widget that provides authentication headers to its child builder. +/// +/// This widget automatically fetches the current authentication token +/// using the [AuthTokenProvider] from dependency injection and constructs +/// HTTP headers with the Bearer token. +/// +/// Example usage: +/// ```dart +/// // Without token validation (default - faster, uses cached token) +/// AuthHeadersBuilder( +/// builder: (context, headers) { +/// return CachedNetworkImage( +/// imageUrl: imageUrl, +/// httpHeaders: headers, +/// ); +/// }, +/// ) +/// +/// // With token validation (ensures fresh token) +/// AuthHeadersBuilder(d +/// validateToken: true, +/// builder: (context, headers) { +/// return CachedNetworkImage( +/// imageUrl: imageUrl, +/// httpHeaders: headers, +/// ); +/// }, +/// ) +/// ``` +class AuthHeadersBuilder extends StatelessWidget { + static final _logger = di.get(); + + /// Creates an [AuthHeadersBuilder]. + /// + /// The [builder] function is called with the current [BuildContext] and + /// a map of HTTP headers. If authentication fails or no token is available, + /// an empty map is provided. + /// + /// If [validateToken] is true, the token will be validated and + /// refreshed if needed. If false (default), the current cached token will be used + /// without validation. + const AuthHeadersBuilder({ + super.key, + required this.builder, + this.validateToken = false, + }); + + /// The builder function that receives the authentication headers. + /// + /// The headers map will contain: + /// - 'Authorization': 'Bearer {token}' (if authenticated) + /// - 'Content-Type': 'application/json' + /// + /// If no authentication token is available, an empty map {} is provided. + final Widget Function(BuildContext context, Map headers) + builder; + + /// Whether to validate and potentially refresh the token. + /// + /// If true, uses [AuthTokenProvider.getValidToken] which validates + /// the token and refreshes it if needed. + /// If false (default), uses [AuthTokenProvider.getCurrentToken] which returns the + /// cached token without validation. + final bool validateToken; + + Map _getSyncAuthHeaders() { + try { + final authTokenProvider = di.get(); + final token = authTokenProvider.getCurrentToken(); + + if (token != null) { + return {'Authorization': 'Bearer $token'}; + } + } catch (e) { + _logger.e('Error getting auth headers: $e'); + } + return {}; + } + + Future> _getAsyncAuthHeaders() async { + try { + final authTokenProvider = di.get(); + final token = await authTokenProvider.getValidToken(); + + if (token != null) { + return {'Authorization': 'Bearer $token'}; + } + } catch (e) { + _logger.e('Error getting auth headers: $e'); + } + return {}; + } + + @override + Widget build(BuildContext context) { + // If validation is not needed, return synchronously without FutureBuilder + if (!validateToken) { + final headers = _getSyncAuthHeaders(); + return builder(context, headers); + } + + // If validation is needed, use FutureBuilder + return FutureBuilder>( + future: _getAsyncAuthHeaders(), + builder: (context, snapshot) { + final headers = snapshot.data ?? {}; + return builder(context, headers); + }, + ); + } +}{{/network}} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/date_filter_chips.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/date_filter_chips.dart new file mode 100644 index 0000000..0932cc1 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/date_filter_chips.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; +import 'package:localization/localization.dart'; + +enum DateFilterType { + week, + month; + + String get displayName { + switch (this) { + case DateFilterType.week: + return strings.common.week; + case DateFilterType.month: + return strings.common.month; + } + } +} + +class DateFilterChips extends StatelessWidget { + final DateFilterType selectedFilter; + final ValueChanged onFilterChanged; + + const DateFilterChips({ + super.key, + required this.selectedFilter, + required this.onFilterChanged, + }); + + @override + Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: DateFilterType.values.map((filter) { + final isSelected = filter == selectedFilter; + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: FilterChip( + label: Text(filter.displayName), + selected: isSelected, + onSelected: (selected) { + if (selected) { + onFilterChanged(filter); + } + }, + backgroundColor: colorScheme.surface, + selectedColor: colorScheme.primary, + checkmarkColor: colorScheme.onPrimary, + labelStyle: TextStyle( + color: isSelected ? colorScheme.onPrimary : colorScheme.onSurface, + fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide( + color: isSelected + ? colorScheme.primary + : colorScheme.outline.withValues(alpha: 0.5), + width: isSelected ? 1.5 : 1, + ), + ), + ), + ); + }).toList(), + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/default_error_view.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/default_error_view.dart new file mode 100644 index 0000000..718e1b6 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/default_error_view.dart @@ -0,0 +1,127 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:localization/localization.dart'; + +class DefaultErrorView extends StatelessWidget { + const DefaultErrorView({ + super.key, + this.error, + this.stackTrace, + this.onRetry, + this.descriptionBuilder, + this.padding, + }); + + final Object? error; + final StackTrace? stackTrace; + final VoidCallback? onRetry; + final String? Function(Object?)? descriptionBuilder; + final EdgeInsetsGeometry? padding; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final colorScheme = theme.colorScheme; + final textTheme = theme.textTheme; + + final title = strings.errors.default_error_message; + final description = descriptionBuilder?.call(error); + + return SafeArea( + child: Padding( + padding: padding ?? const EdgeInsets.all(40.0), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 80, + height: 80, + decoration: BoxDecoration( + color: colorScheme.errorContainer.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: colorScheme.error.withValues(alpha: 0.2), + width: 1, + ), + ), + child: Icon( + Icons.error_outline_rounded, + size: 40, + color: colorScheme.error, + ), + ), + const SizedBox(height: 24), + Text( + title, + style: textTheme.titleMedium?.copyWith( + color: colorScheme.onSurface, + ), + textAlign: TextAlign.center, + maxLines: 5, + overflow: TextOverflow.ellipsis, + ), + if (description != null) ...[ + const SizedBox(height: 8), + Text( + description, + style: textTheme.labelMedium?.copyWith( + color: colorScheme.onSurfaceVariant, + ), + textAlign: TextAlign.center, + maxLines: 20, + overflow: TextOverflow.ellipsis, + ), + ], + if (error != null && kDebugMode) ...[ + const SizedBox(height: 16), + Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: colorScheme.surfaceContainerHigh, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: colorScheme.outline.withValues(alpha: 0.3), + width: 1, + ), + ), + child: SelectableText( + 'type:${error.runtimeType}:\n$error\n${stackTrace?.toString() ?? ''}', + style: textTheme.bodySmall?.copyWith( + color: colorScheme.onSurface, + fontFamily: 'monospace', + fontSize: 12, + ), + textAlign: TextAlign.left, + maxLines: 10, + ), + ), + ], + const SizedBox(height: 32), + Wrap( + spacing: 12, + runSpacing: 8, + alignment: WrapAlignment.center, + children: [ + if (onRetry != null) + OutlinedButton( + onPressed: onRetry, + style: FilledButton.styleFrom( + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 12, + ), + minimumSize: const Size(88, 44), + ), + child: Text(strings.generic.try_again), + ), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/header.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/header.dart new file mode 100644 index 0000000..415c857 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/header.dart @@ -0,0 +1,47 @@ +import 'package:design_system/components/network_url_image.dart'; +import 'package:design_system/utils/index.dart'; +import 'package:flutter/material.dart'; + +enum HeaderType { + image, + asset; + + double get ratio => switch (this) { + HeaderType.image => 4 / 1, + HeaderType.asset => 1 / 1, + }; +} + +class Header extends StatelessWidget { + const Header({ + super.key, + this.type = HeaderType.image, + this.assetPath, + this.imageUrl, + }); + + final HeaderType type; + final String? assetPath; + final String? imageUrl; + + @override + Widget build(BuildContext context) { + return AspectRatio(aspectRatio: type.ratio, child: _buildImage(context)); + } + + Widget _buildImage(BuildContext context) { + switch (type) { + case HeaderType.asset: + return Hero( + tag: assetPath!, + child: Image.asset( + assetPath!, + fit: BoxFit.contain, + alignment: Alignment.center, + ), + ); + case HeaderType.image: + return NetworkUrlImage(url: imageUrl.asEmptyIfNull); + } + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/index.dart new file mode 100644 index 0000000..9363949 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/index.dart @@ -0,0 +1,8 @@ +//GENERATED BARREL FILE +export 'asset_image.dart'; +export 'auth_headers_builder.dart'; +export 'date_filter_chips.dart'; +export 'default_error_view.dart'; +export 'header.dart'; +export 'loaders.dart'; +export 'network_url_image.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/loaders.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/loaders.dart new file mode 100644 index 0000000..b215488 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/loaders.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +class DefaultLoader extends StatelessWidget { + const DefaultLoader({super.key, this.size = 20}); + + final double size; + + @override + Widget build(BuildContext context) { + return Center( + child: SizedBox( + width: size, + height: size, + child: CircularProgressIndicator.adaptive(strokeWidth: 4), + ), + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/network_url_image.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/network_url_image.dart new file mode 100644 index 0000000..a80c2d0 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/components/network_url_image.dart @@ -0,0 +1,45 @@ +import 'dart:math'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; + +class NetworkUrlImage extends StatelessWidget { + final String url; + final double? width; + final double? height; + final BoxFit? boxFit; + + const NetworkUrlImage({ + super.key, + required this.url, + this.width, + this.height, + this.boxFit = BoxFit.cover, + }); + + @override + Widget build(final BuildContext context) => CachedNetworkImage( + imageUrl: url, + placeholder: (final context, final __) { + final theme = Theme.of(context); + + return Container( + color: theme.colorScheme.surface, + width: width, + height: height, + child: Icon( + Icons.image, + size: min(width ?? 32, height ?? 32), + color: theme.cardColor, + ), + ); + }, + width: width, + height: height, + fit: boxFit, + fadeOutDuration: const Duration(), + fadeInDuration: const Duration(), + errorWidget: (final context, final url, final error) => + Icon(Icons.image, size: 32), + ); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/constants/navigation_icons.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/constants/navigation_icons.dart new file mode 100644 index 0000000..5697b47 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/constants/navigation_icons.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; + +/// Icons used by the app's primary navigation. +/// +/// Swap these for your product's icon set - every navigation surface reads +/// from here so a single edit re-skins the whole shell. +class NavigationIcons { + const NavigationIcons._(); + + static const IconData home = Icons.home_outlined; + static const IconData homeSelected = Icons.home; + + static const IconData explore = Icons.explore_outlined; + static const IconData exploreSelected = Icons.explore; + + static const IconData dashboard = Icons.dashboard_outlined; + static const IconData dashboardSelected = Icons.dashboard; + + static const IconData insights = Icons.trending_up_outlined; + static const IconData insightsSelected = Icons.trending_up; + + static const IconData profile = Icons.person_outline; + static const IconData profileSelected = Icons.person; + + static const IconData settings = Icons.settings_outlined; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/design_system.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/design_system.dart new file mode 100644 index 0000000..ad047a9 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/design_system.dart @@ -0,0 +1,138 @@ +import 'package:design_system/generated/theme.dart'; +import 'package:design_system/generated/util.dart'; +import 'package:flutter/material.dart'; + +export 'components/index.dart'; +export 'constants/navigation_icons.dart'; +export 'dialogs/index.dart'; +export 'generated/theme.dart'; +export 'generated/util.dart'; +export 'loader/loader.dart'; +export 'screens/index.dart'; +export 'wrapper/wrappers.dart'; + +class DesignSystem { + final MaterialTheme theme; + + DesignSystem( + BuildContext context, { + String bodyFont = "Open Sans", + String displayFont = "Open Sans", + }) : theme = buildTheme( + context, + bodyFont: bodyFont, + displayFont: displayFont, + ); + + static MaterialTheme buildTheme( + BuildContext context, { + String bodyFont = "Open Sans", + String displayFont = "Open Sans", + }) { + final textTheme = createTextTheme(context, bodyFont, displayFont); + return MaterialTheme(textTheme); + } + + ThemeData _baseTheme({required bool isDark}) { + final base = isDark ? theme.dark() : theme.light(); + return base.copyWith( + inputDecorationTheme: base.inputDecorationTheme.copyWith( + border: UnderlineInputBorder( + borderSide: BorderSide(color: Colors.grey.shade300), + ), + hintStyle: base.textTheme.labelSmall?.copyWith( + color: base.colorScheme.onSurface.withValues(alpha: 0.6), + ), + labelStyle: base.textTheme.labelMedium?.copyWith( + color: base.colorScheme.onSurface.withValues(alpha: 0.6), + ), + ), + appBarTheme: AppBarTheme( + backgroundColor: base.colorScheme.surface, + surfaceTintColor: base.colorScheme.surfaceTint, + foregroundColor: base.colorScheme.onSurface, + elevation: 0, + centerTitle: true, + titleTextStyle: base.textTheme.titleLarge?.copyWith( + color: base.colorScheme.onSurface, + fontWeight: FontWeight.w600, + ), + iconTheme: IconThemeData(color: base.colorScheme.onSurface), + actionsIconTheme: IconThemeData(color: base.colorScheme.onSurface), + ), + navigationBarTheme: NavigationBarThemeData( + backgroundColor: base.colorScheme.surface, + surfaceTintColor: Colors.transparent, + elevation: 0, + height: 70, + labelBehavior: NavigationDestinationLabelBehavior.alwaysShow, + labelTextStyle: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return base.textTheme.labelSmall?.copyWith( + color: base.colorScheme.onSurface, + fontWeight: FontWeight.w600, + fontSize: 12, + ); + } + return base.textTheme.labelSmall?.copyWith( + color: base.colorScheme.onSurfaceVariant, + fontWeight: FontWeight.w500, + fontSize: 12, + ); + }), + iconTheme: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return IconThemeData(color: base.colorScheme.onPrimary, size: 20); + } + return IconThemeData( + color: base.colorScheme.onSurfaceVariant, + size: 20, + ); + }), + indicatorColor: base.colorScheme.primary, + indicatorShape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(4)), + ), + ), + filledButtonTheme: FilledButtonThemeData( + style: FilledButton.styleFrom( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)), + textStyle: base.textTheme.labelLarge?.copyWith( + fontWeight: FontWeight.w600, + ), + ), + ), + textButtonTheme: TextButtonThemeData( + style: TextButton.styleFrom( + foregroundColor: base.colorScheme.primary, + textStyle: base.textTheme.labelLarge?.copyWith( + fontWeight: FontWeight.w600, + ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)), + ), + ), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)), + textStyle: base.textTheme.labelLarge?.copyWith( + fontWeight: FontWeight.w600, + ), + ), + ), + outlinedButtonTheme: OutlinedButtonThemeData( + style: OutlinedButton.styleFrom( + foregroundColor: base.colorScheme.primary, + textStyle: base.textTheme.labelLarge?.copyWith( + fontWeight: FontWeight.w600, + ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)), + side: BorderSide(color: base.colorScheme.primary, width: 1.5), + ), + ), + ); + } + + ThemeData light() => _baseTheme(isDark: false); + + ThemeData dark() => _baseTheme(isDark: true); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/dialogs/file_info_dialog.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/dialogs/file_info_dialog.dart new file mode 100644 index 0000000..a2b456f --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/dialogs/file_info_dialog.dart @@ -0,0 +1,264 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class FileInfoDialog extends StatelessWidget { + final File file; + + const FileInfoDialog({super.key, required this.file}); + + static Future show(BuildContext context, File file) { + return showDialog( + context: context, + builder: (context) => FileInfoDialog(file: file), + ); + } + + String _getFileType(String extension) { + switch (extension.toLowerCase()) { + case 'jpg': + case 'jpeg': + return 'JPEG Image'; + case 'png': + return 'PNG Image'; + case 'gif': + return 'GIF Image'; + case 'webp': + return 'WebP Image'; + case 'bmp': + return 'BMP Image'; + case 'tiff': + case 'tif': + return 'TIFF Image'; + case 'heic': + case 'heif': + return 'HEIF Image'; + default: + return 'Image File'; + } + } + + String _formatFileSize(int bytes) { + if (bytes < 1024) return '$bytes B'; + if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB'; + return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB'; + } + + Widget _buildInfoRow({ + required IconData icon, + required String label, + required String value, + required ColorScheme colorScheme, + required TextTheme textTheme, + bool isPath = false, + VoidCallback? onTap, + }) { + return InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(8), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(icon, size: 18, color: colorScheme.onSurfaceVariant), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: textTheme.bodySmall?.copyWith( + color: colorScheme.onSurfaceVariant, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 4), + SelectableText( + value, + style: textTheme.bodyMedium?.copyWith( + color: colorScheme.onSurface, + fontFamily: isPath ? 'monospace' : null, + ), + ), + ], + ), + ), + if (onTap != null) + Icon( + Icons.copy, + size: 16, + color: colorScheme.onSurfaceVariant.withValues(alpha: 0.7), + ), + ], + ), + ), + ); + } + + void _copyToClipboard(BuildContext context, String value, String label) { + Clipboard.setData(ClipboardData(text: value)); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('$label copied to clipboard'), + duration: const Duration(seconds: 2), + behavior: SnackBarBehavior.floating, + ), + ); + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final colorScheme = theme.colorScheme; + final textTheme = theme.textTheme; + + // Get file info + final fileStat = file.statSync(); + final fileSize = fileStat.size; + final fileName = file.path.split('/').last; + final fileExtension = fileName.split('.').last.toLowerCase(); + final fileType = _getFileType(fileExtension); + + return Dialog( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + child: Container( + constraints: const BoxConstraints(maxWidth: 400, maxHeight: 600), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Header + Container( + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: colorScheme.primaryContainer, + borderRadius: const BorderRadius.vertical( + top: Radius.circular(16), + ), + ), + child: Row( + children: [ + Icon( + Icons.info_outline, + color: colorScheme.onPrimaryContainer, + ), + const SizedBox(width: 12), + Expanded( + child: Text( + 'File Information', + style: textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.w600, + color: colorScheme.onPrimaryContainer, + ), + ), + ), + IconButton( + onPressed: () => Navigator.of(context).pop(), + icon: Icon( + Icons.close, + color: colorScheme.onPrimaryContainer, + ), + ), + ], + ), + ), + + // Content + Flexible( + child: SingleChildScrollView( + padding: const EdgeInsets.all(20), + child: Column( + children: [ + _buildInfoRow( + icon: Icons.image_outlined, + label: 'File Name', + value: fileName, + colorScheme: colorScheme, + textTheme: textTheme, + onTap: () => + _copyToClipboard(context, fileName, 'File name'), + ), + _buildInfoRow( + icon: Icons.category_outlined, + label: 'File Type', + value: fileType, + colorScheme: colorScheme, + textTheme: textTheme, + onTap: () => + _copyToClipboard(context, fileType, 'File type'), + ), + _buildInfoRow( + icon: Icons.extension_outlined, + label: 'File Extension', + value: '.$fileExtension', + colorScheme: colorScheme, + textTheme: textTheme, + onTap: () => _copyToClipboard( + context, + '.$fileExtension', + 'File extension', + ), + ), + _buildInfoRow( + icon: Icons.storage_outlined, + label: 'File Size', + value: _formatFileSize(fileSize), + colorScheme: colorScheme, + textTheme: textTheme, + onTap: () => _copyToClipboard( + context, + _formatFileSize(fileSize), + 'File size', + ), + ), + _buildInfoRow( + icon: Icons.folder_outlined, + label: 'File Path', + value: file.path, + colorScheme: colorScheme, + textTheme: textTheme, + isPath: true, + onTap: () => + _copyToClipboard(context, file.path, 'File path'), + ), + const SizedBox(height: 8), + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: colorScheme.surfaceContainerHighest.withValues( + alpha: 0.5, + ), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Icon( + Icons.touch_app, + size: 16, + color: colorScheme.onSurfaceVariant, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + 'Tap any field to copy to clipboard', + style: textTheme.bodySmall?.copyWith( + color: colorScheme.onSurfaceVariant, + fontStyle: FontStyle.italic, + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/dialogs/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/dialogs/index.dart new file mode 100644 index 0000000..df0904f --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/dialogs/index.dart @@ -0,0 +1 @@ +export 'file_info_dialog.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/generated/theme.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/generated/theme.dart new file mode 100644 index 0000000..33d3a0f --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/generated/theme.dart @@ -0,0 +1,386 @@ +import "package:flutter/material.dart"; + +class MaterialTheme { + final TextTheme textTheme; + + const MaterialTheme(this.textTheme); + + static ColorScheme lightScheme() { + return const ColorScheme( + brightness: Brightness.light, + primary: Color(0xff446732), + surfaceTint: Color(0xff446732), + onPrimary: Color(0xffffffff), + primaryContainer: Color(0xffc5efab), + onPrimaryContainer: Color(0xff2d4f1c), + secondary: Color(0xff55624c), + onSecondary: Color(0xffffffff), + secondaryContainer: Color(0xffd9e7cb), + onSecondaryContainer: Color(0xff3e4a35), + tertiary: Color(0xff386666), + onTertiary: Color(0xffffffff), + tertiaryContainer: Color(0xffbbebec), + onTertiaryContainer: Color(0xff1e4e4e), + error: Color(0xffba1a1a), + onError: Color(0xffffffff), + errorContainer: Color(0xffffdad6), + onErrorContainer: Color(0xff93000a), + surface: Color(0xfff8faf0), + onSurface: Color(0xff191d16), + onSurfaceVariant: Color(0xff43483e), + outline: Color(0xff74796d), + outlineVariant: Color(0xffc3c8bb), + shadow: Color(0xff000000), + scrim: Color(0xff000000), + inverseSurface: Color(0xff2e312b), + inversePrimary: Color(0xffaad291), + primaryFixed: Color(0xffc5efab), + onPrimaryFixed: Color(0xff072100), + primaryFixedDim: Color(0xffaad291), + onPrimaryFixedVariant: Color(0xff2d4f1c), + secondaryFixed: Color(0xffd9e7cb), + onSecondaryFixed: Color(0xff131f0d), + secondaryFixedDim: Color(0xffbdcbb0), + onSecondaryFixedVariant: Color(0xff3e4a35), + tertiaryFixed: Color(0xffbbebec), + onTertiaryFixed: Color(0xff002020), + tertiaryFixedDim: Color(0xffa0cfcf), + onTertiaryFixedVariant: Color(0xff1e4e4e), + surfaceDim: Color(0xffd9dbd1), + surfaceBright: Color(0xfff8faf0), + surfaceContainerLowest: Color(0xffffffff), + surfaceContainerLow: Color(0xfff2f5ea), + surfaceContainer: Color(0xffedefe5), + surfaceContainerHigh: Color(0xffe7e9df), + surfaceContainerHighest: Color(0xffe1e4d9), + ); + } + + ThemeData light() { + return theme(lightScheme()); + } + + static ColorScheme lightMediumContrastScheme() { + return const ColorScheme( + brightness: Brightness.light, + primary: Color(0xff1d3e0c), + surfaceTint: Color(0xff446732), + onPrimary: Color(0xffffffff), + primaryContainer: Color(0xff53773f), + onPrimaryContainer: Color(0xffffffff), + secondary: Color(0xff2d3a26), + onSecondary: Color(0xffffffff), + secondaryContainer: Color(0xff64715a), + onSecondaryContainer: Color(0xffffffff), + tertiary: Color(0xff073d3e), + onTertiary: Color(0xffffffff), + tertiaryContainer: Color(0xff477575), + onTertiaryContainer: Color(0xffffffff), + error: Color(0xff740006), + onError: Color(0xffffffff), + errorContainer: Color(0xffcf2c27), + onErrorContainer: Color(0xffffffff), + surface: Color(0xfff8faf0), + onSurface: Color(0xff0f120c), + onSurfaceVariant: Color(0xff33382e), + outline: Color(0xff4f544a), + outlineVariant: Color(0xff6a6f64), + shadow: Color(0xff000000), + scrim: Color(0xff000000), + inverseSurface: Color(0xff2e312b), + inversePrimary: Color(0xffaad291), + primaryFixed: Color(0xff53773f), + onPrimaryFixed: Color(0xffffffff), + primaryFixedDim: Color(0xff3b5d29), + onPrimaryFixedVariant: Color(0xffffffff), + secondaryFixed: Color(0xff64715a), + onSecondaryFixed: Color(0xffffffff), + secondaryFixedDim: Color(0xff4c5943), + onSecondaryFixedVariant: Color(0xffffffff), + tertiaryFixed: Color(0xff477575), + onTertiaryFixed: Color(0xffffffff), + tertiaryFixedDim: Color(0xff2e5c5d), + onTertiaryFixedVariant: Color(0xffffffff), + surfaceDim: Color(0xffc5c8be), + surfaceBright: Color(0xfff8faf0), + surfaceContainerLowest: Color(0xffffffff), + surfaceContainerLow: Color(0xfff2f5ea), + surfaceContainer: Color(0xffe7e9df), + surfaceContainerHigh: Color(0xffdbded4), + surfaceContainerHighest: Color(0xffd0d3c9), + ); + } + + ThemeData lightMediumContrast() { + return theme(lightMediumContrastScheme()); + } + + static ColorScheme lightHighContrastScheme() { + return const ColorScheme( + brightness: Brightness.light, + primary: Color(0xff133304), + surfaceTint: Color(0xff446732), + onPrimary: Color(0xffffffff), + primaryContainer: Color(0xff30511e), + onPrimaryContainer: Color(0xffffffff), + secondary: Color(0xff242f1c), + onSecondary: Color(0xffffffff), + secondaryContainer: Color(0xff404d38), + onSecondaryContainer: Color(0xffffffff), + tertiary: Color(0xff003233), + onTertiary: Color(0xffffffff), + tertiaryContainer: Color(0xff215051), + onTertiaryContainer: Color(0xffffffff), + error: Color(0xff600004), + onError: Color(0xffffffff), + errorContainer: Color(0xff98000a), + onErrorContainer: Color(0xffffffff), + surface: Color(0xfff8faf0), + onSurface: Color(0xff000000), + onSurfaceVariant: Color(0xff000000), + outline: Color(0xff292e25), + outlineVariant: Color(0xff464b41), + shadow: Color(0xff000000), + scrim: Color(0xff000000), + inverseSurface: Color(0xff2e312b), + inversePrimary: Color(0xffaad291), + primaryFixed: Color(0xff30511e), + onPrimaryFixed: Color(0xffffffff), + primaryFixedDim: Color(0xff1a3a09), + onPrimaryFixedVariant: Color(0xffffffff), + secondaryFixed: Color(0xff404d38), + onSecondaryFixed: Color(0xffffffff), + secondaryFixedDim: Color(0xff2a3622), + onSecondaryFixedVariant: Color(0xffffffff), + tertiaryFixed: Color(0xff215051), + onTertiaryFixed: Color(0xffffffff), + tertiaryFixedDim: Color(0xff02393a), + onTertiaryFixedVariant: Color(0xffffffff), + surfaceDim: Color(0xffb7bab0), + surfaceBright: Color(0xfff8faf0), + surfaceContainerLowest: Color(0xffffffff), + surfaceContainerLow: Color(0xfff0f2e7), + surfaceContainer: Color(0xffe1e4d9), + surfaceContainerHigh: Color(0xffd3d5cb), + surfaceContainerHighest: Color(0xffc5c8be), + ); + } + + ThemeData lightHighContrast() { + return theme(lightHighContrastScheme()); + } + + static ColorScheme darkScheme() { + return const ColorScheme( + brightness: Brightness.dark, + primary: Color(0xffaad291), + surfaceTint: Color(0xffaad291), + onPrimary: Color(0xff173807), + primaryContainer: Color(0xff2d4f1c), + onPrimaryContainer: Color(0xffc5efab), + secondary: Color(0xffbdcbb0), + onSecondary: Color(0xff283420), + secondaryContainer: Color(0xff3e4a35), + onSecondaryContainer: Color(0xffd9e7cb), + tertiary: Color(0xffa0cfcf), + onTertiary: Color(0xff003738), + tertiaryContainer: Color(0xff1e4e4e), + onTertiaryContainer: Color(0xffbbebec), + error: Color(0xffffb4ab), + onError: Color(0xff690005), + errorContainer: Color(0xff93000a), + onErrorContainer: Color(0xffffdad6), + surface: Color(0xff11140e), + onSurface: Color(0xffe1e4d9), + onSurfaceVariant: Color(0xffc3c8bb), + outline: Color(0xff8d9286), + outlineVariant: Color(0xff43483e), + shadow: Color(0xff000000), + scrim: Color(0xff000000), + inverseSurface: Color(0xffe1e4d9), + inversePrimary: Color(0xff446732), + primaryFixed: Color(0xffc5efab), + onPrimaryFixed: Color(0xff072100), + primaryFixedDim: Color(0xffaad291), + onPrimaryFixedVariant: Color(0xff2d4f1c), + secondaryFixed: Color(0xffd9e7cb), + onSecondaryFixed: Color(0xff131f0d), + secondaryFixedDim: Color(0xffbdcbb0), + onSecondaryFixedVariant: Color(0xff3e4a35), + tertiaryFixed: Color(0xffbbebec), + onTertiaryFixed: Color(0xff002020), + tertiaryFixedDim: Color(0xffa0cfcf), + onTertiaryFixedVariant: Color(0xff1e4e4e), + surfaceDim: Color(0xff11140e), + surfaceBright: Color(0xff373a33), + surfaceContainerLowest: Color(0xff0c0f09), + surfaceContainerLow: Color(0xff191d16), + surfaceContainer: Color(0xff1d211a), + surfaceContainerHigh: Color(0xff282b24), + surfaceContainerHighest: Color(0xff32362f), + ); + } + + ThemeData dark() { + return theme(darkScheme()); + } + + static ColorScheme darkMediumContrastScheme() { + return const ColorScheme( + brightness: Brightness.dark, + primary: Color(0xffbfe8a5), + surfaceTint: Color(0xffaad291), + onPrimary: Color(0xff0c2c00), + primaryContainer: Color(0xff759b5f), + onPrimaryContainer: Color(0xff000000), + secondary: Color(0xffd2e1c5), + onSecondary: Color(0xff1d2916), + secondaryContainer: Color(0xff87957c), + onSecondaryContainer: Color(0xff000000), + tertiary: Color(0xffb5e5e5), + onTertiary: Color(0xff002b2c), + tertiaryContainer: Color(0xff6b9999), + onTertiaryContainer: Color(0xff000000), + error: Color(0xffffd2cc), + onError: Color(0xff540003), + errorContainer: Color(0xffff5449), + onErrorContainer: Color(0xff000000), + surface: Color(0xff11140e), + onSurface: Color(0xffffffff), + onSurfaceVariant: Color(0xffd9ded0), + outline: Color(0xffafb4a7), + outlineVariant: Color(0xff8d9286), + shadow: Color(0xff000000), + scrim: Color(0xff000000), + inverseSurface: Color(0xffe1e4d9), + inversePrimary: Color(0xff2f501d), + primaryFixed: Color(0xffc5efab), + onPrimaryFixed: Color(0xff041500), + primaryFixedDim: Color(0xffaad291), + onPrimaryFixedVariant: Color(0xff1d3e0c), + secondaryFixed: Color(0xffd9e7cb), + onSecondaryFixed: Color(0xff091405), + secondaryFixedDim: Color(0xffbdcbb0), + onSecondaryFixedVariant: Color(0xff2d3a26), + tertiaryFixed: Color(0xffbbebec), + onTertiaryFixed: Color(0xff001415), + tertiaryFixedDim: Color(0xffa0cfcf), + onTertiaryFixedVariant: Color(0xff073d3e), + surfaceDim: Color(0xff11140e), + surfaceBright: Color(0xff42463e), + surfaceContainerLowest: Color(0xff060804), + surfaceContainerLow: Color(0xff1b1f18), + surfaceContainer: Color(0xff262922), + surfaceContainerHigh: Color(0xff30342d), + surfaceContainerHighest: Color(0xff3b3f38), + ); + } + + ThemeData darkMediumContrast() { + return theme(darkMediumContrastScheme()); + } + + static ColorScheme darkHighContrastScheme() { + return const ColorScheme( + brightness: Brightness.dark, + primary: Color(0xffd2fcb7), + surfaceTint: Color(0xffaad291), + onPrimary: Color(0xff000000), + primaryContainer: Color(0xffa6ce8d), + onPrimaryContainer: Color(0xff020e00), + secondary: Color(0xffe6f5d8), + onSecondary: Color(0xff000000), + secondaryContainer: Color(0xffb9c7ac), + onSecondaryContainer: Color(0xff040e02), + tertiary: Color(0xffc9f9f9), + onTertiary: Color(0xff000000), + tertiaryContainer: Color(0xff9ccbcb), + onTertiaryContainer: Color(0xff000e0e), + error: Color(0xffffece9), + onError: Color(0xff000000), + errorContainer: Color(0xffffaea4), + onErrorContainer: Color(0xff220001), + surface: Color(0xff11140e), + onSurface: Color(0xffffffff), + onSurfaceVariant: Color(0xffffffff), + outline: Color(0xffedf2e4), + outlineVariant: Color(0xffbfc4b7), + shadow: Color(0xff000000), + scrim: Color(0xff000000), + inverseSurface: Color(0xffe1e4d9), + inversePrimary: Color(0xff2f501d), + primaryFixed: Color(0xffc5efab), + onPrimaryFixed: Color(0xff000000), + primaryFixedDim: Color(0xffaad291), + onPrimaryFixedVariant: Color(0xff041500), + secondaryFixed: Color(0xffd9e7cb), + onSecondaryFixed: Color(0xff000000), + secondaryFixedDim: Color(0xffbdcbb0), + onSecondaryFixedVariant: Color(0xff091405), + tertiaryFixed: Color(0xffbbebec), + onTertiaryFixed: Color(0xff000000), + tertiaryFixedDim: Color(0xffa0cfcf), + onTertiaryFixedVariant: Color(0xff001415), + surfaceDim: Color(0xff11140e), + surfaceBright: Color(0xff4e514a), + surfaceContainerLowest: Color(0xff000000), + surfaceContainerLow: Color(0xff1d211a), + surfaceContainer: Color(0xff2e312b), + surfaceContainerHigh: Color(0xff393c35), + surfaceContainerHighest: Color(0xff444840), + ); + } + + ThemeData darkHighContrast() { + return theme(darkHighContrastScheme()); + } + + ThemeData theme(ColorScheme colorScheme) => ThemeData( + useMaterial3: true, + brightness: colorScheme.brightness, + colorScheme: colorScheme, + textTheme: textTheme.apply( + bodyColor: colorScheme.onSurface, + displayColor: colorScheme.onSurface, + ), + scaffoldBackgroundColor: colorScheme.surface, + canvasColor: colorScheme.surface, + ); + + List get extendedColors => []; +} + +class ExtendedColor { + final Color seed, value; + final ColorFamily light; + final ColorFamily lightHighContrast; + final ColorFamily lightMediumContrast; + final ColorFamily dark; + final ColorFamily darkHighContrast; + final ColorFamily darkMediumContrast; + + const ExtendedColor({ + required this.seed, + required this.value, + required this.light, + required this.lightHighContrast, + required this.lightMediumContrast, + required this.dark, + required this.darkHighContrast, + required this.darkMediumContrast, + }); +} + +class ColorFamily { + const ColorFamily({ + required this.color, + required this.onColor, + required this.colorContainer, + required this.onColorContainer, + }); + + final Color color; + final Color onColor; + final Color colorContainer; + final Color onColorContainer; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/generated/util.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/generated/util.dart new file mode 100644 index 0000000..10a2d84 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/generated/util.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +TextTheme createTextTheme( + BuildContext context, + String bodyFontString, + String displayFontString, +) { + TextTheme baseTextTheme = Theme.of(context).textTheme; + TextTheme bodyTextTheme = GoogleFonts.getTextTheme( + bodyFontString, + baseTextTheme, + ); + TextTheme displayTextTheme = GoogleFonts.getTextTheme( + displayFontString, + baseTextTheme, + ); + TextTheme textTheme = displayTextTheme.copyWith( + bodyLarge: bodyTextTheme.bodyLarge, + bodyMedium: bodyTextTheme.bodyMedium, + bodySmall: bodyTextTheme.bodySmall, + labelLarge: bodyTextTheme.labelLarge, + labelMedium: bodyTextTheme.labelMedium, + labelSmall: bodyTextTheme.labelSmall, + ); + return textTheme; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/loader/loader.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/loader/loader.dart new file mode 100644 index 0000000..3e75782 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/loader/loader.dart @@ -0,0 +1,15 @@ +import 'package:design_system/components/index.dart'; +import 'package:flutter/material.dart'; +import 'package:loader_overlay/loader_overlay.dart'; + +class Loader { + static void show(BuildContext context) { + context.loaderOverlay.show( + widgetBuilder: (_) => const Center(child: DefaultLoader()), + ); + } + + static void hide(BuildContext context) { + context.loaderOverlay.hide(); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/screens/error_screen.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/screens/error_screen.dart new file mode 100644 index 0000000..25c9f79 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/screens/error_screen.dart @@ -0,0 +1,152 @@ +import 'package:flutter/material.dart'; +import 'package:localization/localization.dart'; + +class ErrorScreen extends StatelessWidget { + const ErrorScreen({ + super.key, + this.title, + this.message, + this.onRetry, + this.onGoHome, + this.error, + }); + + final String? title; + final String? message; + final VoidCallback? onRetry; + final VoidCallback? onGoHome; + final String? error; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final colorScheme = theme.colorScheme; + final textTheme = theme.textTheme; + + final resolvedTitle = title ?? strings.errors.default_error_message; + final resolvedMessage = message ?? strings.errors.default_error_description; + + return Scaffold( + backgroundColor: colorScheme.surface, + appBar: AppBar( + backgroundColor: colorScheme.surface, + foregroundColor: colorScheme.onSurface, + elevation: 0, + surfaceTintColor: colorScheme.surfaceTint, + ), + body: SafeArea( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 80, + height: 80, + decoration: BoxDecoration( + color: colorScheme.errorContainer.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: colorScheme.error.withValues(alpha: 0.2), + width: 1, + ), + ), + child: Icon( + Icons.error_outline_rounded, + size: 40, + color: colorScheme.error, + ), + ), + const SizedBox(height: 24), + Text( + resolvedTitle, + style: textTheme.titleLarge?.copyWith( + color: colorScheme.onSurface, + fontWeight: FontWeight.w600, + ), + textAlign: TextAlign.center, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 8), + Text( + resolvedMessage, + style: textTheme.bodyMedium?.copyWith( + color: colorScheme.onSurfaceVariant, + ), + textAlign: TextAlign.center, + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + if (error != null) ...[ + const SizedBox(height: 16), + Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: colorScheme.surfaceContainerHigh, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: colorScheme.outline.withValues(alpha: 0.3), + width: 1, + ), + ), + child: SelectableText( + error!, + style: textTheme.bodySmall?.copyWith( + color: colorScheme.onSurface, + fontFamily: 'monospace', + fontSize: 12, + ), + textAlign: TextAlign.left, + maxLines: 4, + ), + ), + ], + const SizedBox(height: 32), + Wrap( + spacing: 12, + runSpacing: 8, + alignment: WrapAlignment.center, + children: [ + if (onRetry != null) + FilledButton( + onPressed: onRetry, + style: FilledButton.styleFrom( + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 12, + ), + minimumSize: const Size(88, 44), + ), + child: Text(strings.generic.try_again), + ), + if (onGoHome != null) + OutlinedButton( + onPressed: onGoHome, + style: OutlinedButton.styleFrom( + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 12, + ), + minimumSize: const Size(88, 44), + side: BorderSide( + color: colorScheme.outline, + width: 1, + ), + ), + child: Text(strings.generic.retry), + ), + ], + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/screens/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/screens/index.dart new file mode 100644 index 0000000..807a6de --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/screens/index.dart @@ -0,0 +1 @@ +export 'error_screen.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/toast/toasts.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/toast/toasts.dart new file mode 100644 index 0000000..3e06134 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/toast/toasts.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:iconsax_flutter/iconsax_flutter.dart'; +import 'package:toastification/toastification.dart'; + +class Toast { + static void notification({required String title, required String body}) => + toastification.show( + type: ToastificationType.info, + style: ToastificationStyle.fillColored, + title: Text(title), + description: Text(body), + alignment: Alignment.topCenter, + autoCloseDuration: const Duration(seconds: 3, milliseconds: 500), + animationBuilder: (context, animation, alignment, child) { + return ScaleTransition(scale: animation, child: child); + }, + icon: Icon(Iconsax.notification), + borderRadius: BorderRadius.circular(12.0), + showProgressBar: true, + dragToClose: true, + applyBlurEffect: true, + ); + + static void error( + BuildContext context, { + required String message, + String? description, + }) => toastification.show( + context: context, + type: ToastificationType.error, + style: ToastificationStyle.fillColored, + title: Text(message), + description: description != null ? Text(description) : null, + alignment: Alignment.topCenter, + autoCloseDuration: const Duration(seconds: 3, milliseconds: 500), + animationBuilder: (context, animation, alignment, child) { + return ScaleTransition(scale: animation, child: child); + }, + icon: Icon(Iconsax.warning_2), + borderRadius: BorderRadius.circular(12.0), + showProgressBar: true, + dragToClose: true, + applyBlurEffect: true, + ); + + static void success( + BuildContext context, { + required String message, + String? description, + }) => toastification.show( + context: context, + type: ToastificationType.success, + style: ToastificationStyle.fillColored, + title: Text(message), + description: description != null ? Text(description) : null, + alignment: Alignment.topCenter, + autoCloseDuration: const Duration(seconds: 3, milliseconds: 500), + animationBuilder: (context, animation, alignment, child) { + return ScaleTransition(scale: animation, child: child); + }, + icon: Icon(Iconsax.tick_circle), + borderRadius: BorderRadius.circular(12.0), + showProgressBar: true, + dragToClose: true, + applyBlurEffect: true, + ); + + static void warning( + BuildContext context, { + required String message, + String? description, + }) => toastification.show( + context: context, + type: ToastificationType.warning, + style: ToastificationStyle.fillColored, + title: Text(message), + description: description != null ? Text(description) : null, + alignment: Alignment.topCenter, + autoCloseDuration: const Duration(seconds: 3, milliseconds: 500), + animationBuilder: (context, animation, alignment, child) { + return ScaleTransition(scale: animation, child: child); + }, + icon: Icon(Iconsax.danger), + borderRadius: BorderRadius.circular(12.0), + showProgressBar: true, + dragToClose: true, + applyBlurEffect: true, + ); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/utils/extensions/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/utils/extensions/index.dart new file mode 100644 index 0000000..526a048 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/utils/extensions/index.dart @@ -0,0 +1,2 @@ +//GENERATED BARREL FILE +export 'string.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/utils/extensions/string.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/utils/extensions/string.dart new file mode 100644 index 0000000..c68c177 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/utils/extensions/string.dart @@ -0,0 +1,33 @@ +/// String extensions for common utility operations +extension StringExtensions on String? { + /// Returns true if the string is not null and not empty + /// + /// Example: + /// ```dart + /// String? text = "Hello"; + /// print(text.isNotNullAndNotEmpty); // true + /// + /// String? emptyText = ""; + /// print(emptyText.isNotNullAndNotEmpty); // false + /// + /// String? nullText = null; + /// print(nullText.isNotNullAndNotEmpty); // false + /// ``` + bool get isNotNullAndNotEmpty { + return this != null && this!.isNotEmpty; + } + + /// Returns an empty string if the string is null, otherwise returns the string itself + /// + /// Example: + /// ```dart + /// String? text = "Hello"; + /// print(text.asEmptyIfNull); // "Hello" + /// + /// String? nullText = null; + /// print(nullText.asEmptyIfNull); // "" + /// ``` + String get asEmptyIfNull { + return this ?? ''; + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/utils/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/utils/index.dart new file mode 100644 index 0000000..fd6ab25 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/utils/index.dart @@ -0,0 +1,2 @@ +//GENERATED BARREL FILE +export 'extensions/index.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/wrapper/wrappers.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/wrapper/wrappers.dart new file mode 100644 index 0000000..1a08c02 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/lib/wrapper/wrappers.dart @@ -0,0 +1,54 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:loader_overlay/loader_overlay.dart'; +import 'package:toastification/toastification.dart'; + +class DesignSystemWrapper extends StatefulWidget { + const DesignSystemWrapper({super.key, required this.builder}); + + final Widget Function(BuildContext context, ThemeData theme) builder; + + @override + State createState() => _DesignSystemWrapperState(); +} + +class _DesignSystemWrapperState extends State + with WidgetsBindingObserver { + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + void didChangePlatformBrightness() { + setState(() {}); + } + + @override + Widget build(BuildContext context) { + final brightness = MediaQuery.platformBrightnessOf(context); + final materialTheme = DesignSystem(context); + final theme = brightness != Brightness.dark + ? materialTheme.light() + : materialTheme.dark(); + + return Theme( + data: theme, + child: GlobalLoaderOverlay( + overlayColor: theme.colorScheme.surface.withValues(alpha: 0.8), + duration: const Duration(milliseconds: 300), + reverseDuration: const Duration(milliseconds: 200), + switchInCurve: Curves.easeInOut, + switchOutCurve: Curves.easeInOut, + child: ToastificationWrapper(child: widget.builder(context, theme)), + ), + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/pubspec.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/pubspec.yaml new file mode 100644 index 0000000..71dfdf9 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/pubspec.yaml @@ -0,0 +1,33 @@ +name: design_system +description: "Theme, components and dialogs shared across {{name.titleCase()}}." +version: 0.1.0 +publish_to: 'none' + +environment: + sdk: ^3.8.0 + flutter: ">=3.29.0" + +dependencies: + flutter: + sdk: flutter + cached_network_image: ^3.4.1 + flutter_svg: ^2.2.0 + google_fonts: ^6.2.1 + iconsax_flutter: ^1.0.1 + loader_overlay: ^5.0.0 + toastification: ^3.0.3 + core: + path: ../core + di: + path: ../di + localization: + path: ../localization +{{#network}} network: + path: ../network +{{/network}} +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/test/design_system_test.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/test/design_system_test.dart new file mode 100644 index 0000000..8cd1ae7 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/design_system/test/design_system_test.dart @@ -0,0 +1,25 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('DesignSystemWrapper hands the builder a themed context', ( + tester, + ) async { + late ThemeData built; + + await tester.pumpWidget( + MaterialApp( + home: DesignSystemWrapper( + builder: (context, theme) { + built = theme; + return const SizedBox.shrink(); + }, + ), + ), + ); + + expect(built.useMaterial3, isTrue); + expect(built.colorScheme.primary, isNot(Colors.transparent)); + }); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/README.md b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/README.md new file mode 100644 index 0000000..e641145 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/README.md @@ -0,0 +1,6 @@ +# Dependency Injection (DI) Package + +A clean dependency injection abstraction for the {{name.titleCase()}} monorepo, built on top of GetIt. + +[![nonstop_cli](https://img.shields.io/badge/started%20with-nonstop_cli-166C4E.svg?style=flat-square)](https://pub.dev/packages/nonstop_cli) +[![melos](https://img.shields.io/badge/maintained%20with-melos-f700ff.svg?style=flat-square)](https://github.com/invertase/melos) diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/lib/di.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/lib/di.dart new file mode 100644 index 0000000..8136bb6 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/lib/di.dart @@ -0,0 +1,14 @@ +/// Dependency Injection package for {{name.titleCase()}} +/// +/// This package provides a clean abstraction over dependency injection +/// using GetIt as the underlying implementation. +library; + +import 'package:di/di.dart'; + +// Core interfaces +export 'src/dependency_injection.dart'; +// GetIt implementation +export 'src/dependency_injection_imp.dart'; + +final di = GetItDependencyInjection(); diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/lib/src/dependency_injection.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/lib/src/dependency_injection.dart new file mode 100644 index 0000000..1e5b2f0 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/lib/src/dependency_injection.dart @@ -0,0 +1,22 @@ +import 'dart:async'; + +typedef DisposeFunc = FutureOr Function(T param); + +abstract class DependencyInjection { + Future init(); + + Future dispose(); + + void register( + final T instance, { + final DisposeFunc? dispose, + }); + + Future unregister(final T? instance); + + T get(); + + bool has(); + + Future reset(); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/lib/src/dependency_injection_imp.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/lib/src/dependency_injection_imp.dart new file mode 100644 index 0000000..7fb9bfe --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/lib/src/dependency_injection_imp.dart @@ -0,0 +1,115 @@ +import 'dart:async'; + +import 'dart:developer' as dev; + +import 'package:di/src/dependency_injection.dart'; +import 'package:get_it/get_it.dart'; + +/// Implementation of DependencyInjection using GetIt +class GetItDependencyInjection implements DependencyInjection { + final GetIt _getIt; + final Map _disposeFunctions = {}; + final Map _registeredInstances = {}; + + /// Creates a new instance with optional GetIt instance + /// If no instance is provided, uses GetIt.instance + GetItDependencyInjection({GetIt? getIt}) : _getIt = getIt ?? GetIt.instance; + + @override + Future init() async { + // GetIt doesn't require explicit initialization + // but we can use this to set up any global configurations + } + + @override + Future dispose() async { + // Call dispose functions for all registered instances + for (final entry in _disposeFunctions.entries) { + final type = entry.key; + final disposeFunc = entry.value; + final instance = _registeredInstances[type]; + + if (instance != null) { + try { + await disposeFunc(instance); + } catch (e) { + // Log error but continue disposing other instances + dev.log( + 'Error disposing instance of type $type', + name: 'di', + error: e, + ); + } + } + } + + // Clear dispose functions and registered instances maps + _disposeFunctions.clear(); + _registeredInstances.clear(); + + // Reset GetIt instance + await _getIt.reset(); + } + + @override + void register( + final T instance, { + final DisposeFunc? dispose, + }) { + // Register the instance as a singleton + _getIt.registerSingleton(instance); + + // Store the instance and dispose function if provided + _registeredInstances[T] = instance; + if (dispose != null) { + _disposeFunctions[T] = dispose; + } + } + + @override + Future unregister(final T? instance) async { + if (!_getIt.isRegistered()) { + return; + } + + // Call dispose function if it exists + final disposeFunc = _disposeFunctions[T]; + if (disposeFunc != null) { + try { + final registeredInstance = instance ?? _registeredInstances[T]; + if (registeredInstance != null) { + await disposeFunc(registeredInstance); + } + } catch (e) { + // Log error but continue with unregistration + dev.log('Error disposing instance of type $T', name: 'di', error: e); + } + _disposeFunctions.remove(T); + } + + // Remove from our tracking + _registeredInstances.remove(T); + + // Unregister from GetIt + await _getIt.unregister(instance: instance); + } + + @override + T get() { + return _getIt.get(); + } + + @override + bool has() { + return _getIt.isRegistered(); + } + + @override + Future reset() async { + await dispose(); + await init(); + } + + /// Get the underlying GetIt instance (for advanced usage) + GetIt get getIt => _getIt; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/pubspec.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/pubspec.yaml new file mode 100644 index 0000000..381b1d0 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/pubspec.yaml @@ -0,0 +1,20 @@ +name: di +description: "Service locator abstraction for {{name.titleCase()}}, backed by GetIt." +version: 0.1.0 +publish_to: 'none' + +environment: + sdk: ^3.8.0 + flutter: ">=3.29.0" + +dependencies: + flutter: + sdk: flutter + get_it: ^8.0.3 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/test/di_test.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/test/di_test.dart new file mode 100644 index 0000000..6772212 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/di/test/di_test.dart @@ -0,0 +1,253 @@ +import 'package:di/di.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:get_it/get_it.dart'; + +// Test classes for dependency injection +class TestService { + final String name; + bool isDisposed = false; + + TestService(this.name); + + void dispose() { + isDisposed = true; + } +} + +class TestRepository { + final TestService service; + bool isDisposed = false; + + TestRepository(this.service); + + void dispose() { + isDisposed = true; + } +} + +void main() { + group('GetItDependencyInjection', () { + late GetItDependencyInjection di; + late GetIt testGetIt; + + setUp(() { + // Create a separate GetIt instance for testing + testGetIt = GetIt.asNewInstance(); + di = GetItDependencyInjection(getIt: testGetIt); + }); + + tearDown(() async { + await di.dispose(); + }); + + group('Registration', () { + setUp(() async { + await di.init(); + }); + + test('should register and retrieve instances', () async { + final service = TestService('test'); + + di.register(service); + + expect(di.has(), true); + final retrieved = di.get(); + expect(retrieved, same(service)); + expect(retrieved.name, 'test'); + }); + + test('should register with dispose function', () async { + final service = TestService('test'); + bool disposeCalled = false; + + di.register( + service, + dispose: (instance) { + disposeCalled = true; + instance.dispose(); + }, + ); + + expect(di.has(), true); + await di.unregister(null); + + expect(disposeCalled, true); + expect(service.isDisposed, true); + expect(di.has(), false); + }); + }); + + group('Error handling', () { + test('registering before init() is allowed', () { + // init() exists for symmetry with dispose(); GetIt needs no setup, so + // registration works straight away. + final service = TestService('test'); + + di.register(service); + + expect(di.get(), same(service)); + }); + + test('should throw when getting before initialization', () { + expect(() => di.get(), throwsA(isA())); + }); + + test('should return false when not initialized', () { + expect(di.has(), false); + }); + }); + + group('Retrieval', () { + setUp(() async { + await di.init(); + }); + + test('should get registered instances', () { + final service = TestService('test'); + di.register(service); + + final retrieved = di.get(); + expect(retrieved, same(service)); + }); + + test('should throw when getting unregistered type', () { + expect(() => di.get(), throwsA(isA())); + }); + }); + + group('Has/Contains', () { + setUp(() async { + await di.init(); + }); + + test('should return true for registered types', () { + final service = TestService('test'); + di.register(service); + + expect(di.has(), true); + }); + + test('should return false for unregistered types', () { + expect(di.has(), false); + }); + }); + + group('Unregistration', () { + setUp(() async { + await di.init(); + }); + + test('should unregister instances', () async { + final service = TestService('test'); + di.register(service); + + expect(di.has(), true); + await di.unregister(null); + expect(di.has(), false); + }); + + test('should call dispose function when unregistering', () async { + final service = TestService('test'); + bool disposeCalled = false; + + di.register( + service, + dispose: (instance) { + disposeCalled = true; + instance.dispose(); + }, + ); + + await di.unregister(null); + + expect(disposeCalled, true); + expect(service.isDisposed, true); + }); + + test( + 'should handle unregistering non-existent types gracefully', + () async { + // Should not throw + await di.unregister(null); + expect(di.has(), false); + }, + ); + + test('should handle unregistering when not initialized', () async { + // Should not throw + await di.unregister(null); + }); + }); + + group('Disposal', () { + setUp(() async { + await di.init(); + }); + + test('should dispose all registered instances', () async { + final service = TestService('service'); + final repository = TestRepository(service); + + di.register( + service, + dispose: (instance) => instance.dispose(), + ); + + di.register( + repository, + dispose: (instance) => instance.dispose(), + ); + + await di.dispose(); + + expect(service.isDisposed, true); + expect(repository.isDisposed, true); + expect(di.has(), false); + expect(di.has(), false); + }); + }); + + group('Reset', () { + test('should reset and reinitialize', () async { + await di.init(); + + final service = TestService('test'); + di.register( + service, + dispose: (instance) => instance.dispose(), + ); + + expect(di.has(), true); + + await di.reset(); + + expect(di.has(), false); + expect(service.isDisposed, true); + }); + }); + + group('Complex scenarios', () { + setUp(() async { + await di.init(); + }); + + test('should handle multiple dependencies', () { + final service = TestService('service'); + final repository = TestRepository(service); + + di.register(service); + di.register(repository); + + expect(di.has(), true); + expect(di.has(), true); + + final retrievedService = di.get(); + final retrievedRepository = di.get(); + + expect(retrievedService, same(service)); + expect(retrievedRepository, same(repository)); + expect(retrievedRepository.service, same(service)); + }); + }); + }); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/README.md b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/README.md new file mode 100644 index 0000000..0090626 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/README.md @@ -0,0 +1,169 @@ +# Localization + +Localization package for {{name.titleCase()}} using i69n for type-safe internationalization. + +[![nonstop_cli](https://img.shields.io/badge/started%20with-nonstop_cli-166C4E.svg?style=flat-square)](https://pub.dev/packages/nonstop_cli) +[![melos](https://img.shields.io/badge/maintained%20with-melos-f700ff.svg?style=flat-square)](https://github.com/invertase/melos) + +## Features ✨ + +- **Type-safe translations** - Generated classes with compile-time string validation +- **Easy integration** - Simple API with global `strings` access +- **Organized structure** - Categorized messages (auth, profile, generic, etc.) +- **Future-ready** - Built for multi-language support +- **Melos integration** - Seamless monorepo workflow + +## Installation 💻 + +Add to your `pubspec.yaml`: + +```yaml +dependencies: + localization: + path: ../packages/localization +``` + +## Usage 🚀 + +### Basic Usage + +```dart +import 'package:localization/localization.dart'; + +// Simple access to any string +Text(strings.auth.login); // "Login" +Text(strings.generic.ok); // "OK" +Text(strings.profile.name); // "Name" +Text(strings.validation.required_field); // "This field is required" +``` + +### In Widgets + +```dart +class LoginButton extends StatelessWidget { + @override + Widget build(BuildContext context) { + return ElevatedButton( + onPressed: () => _login(), + child: Text(strings.auth.login), + ); + } +} +``` + +### Error Handling + +```dart +void showError() { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(strings.errors.network_error)), + ); +} +``` + +### Form Validation + +```dart +String? validateEmail(String? value) { + if (value?.isEmpty ?? true) { + return strings.validation.required_field; + } + if (!value!.contains('@')) { + return strings.validation.invalid_email; + } + return null; +} +``` + +## Development 🛠️ + +### Adding New Strings + +1. Edit `lib/messages.i69n.yaml`: +```yaml +new_category: + new_message: "New Message Text" + another_message: "Another Message" +``` + +2. Generate code: +```bash +# From the monorepo root +melos run generate:i69n + +# Or from this package +dart run build_runner build --delete-conflicting-outputs +``` + +3. Use the new strings: +```dart +Text(strings.new_category.new_message); // "New Message Text" +``` + +### Testing + +```bash +# Run tests +flutter test + +# Run with coverage +flutter test --coverage +``` + +### Linting + +```bash +# From monorepo root +melos run lint + +# Or from this package +dart format . && dart analyze --fatal-infos . +``` + +## Advanced Usage 🔧 + +### Using LocalizationProvider + +```dart +import 'package:localization/localization.dart'; + +// Initialize (currently only supports English) +LocalizationProvider.initialize(); + +// Check supported locales +if (LocalizationProvider.isLocaleSupported('en')) { + // Handle supported locale +} + +// Simple access to any string +Text(strings.auth.login); // "Login" + +``` + +### Future Multi-language Support + +This package is designed to support multiple languages in the future. The current implementation supports English only, but the structure is ready for: + +- Spanish (`es`) +- French (`fr`) +- German (`de`) +- Additional languages as needed + +## Package Structure 📁 + +``` +packages/localization/ +├── lib/ +│ ├── localization.dart # Main export file +│ ├── messages.i69n.dart # Generated i69n classes +│ ├── messages.i69n.yaml # Translation source +│ └── src/ +│ ├── localization_provider.dart # Advanced utilities +│ └── strings.dart # Global strings instance +├── test/ +│ └── localization_test.dart # Comprehensive tests +├── build.yaml # i69n build configuration +└── pubspec.yaml # Package dependencies +``` + +[flutter_install_link]: https://docs.flutter.dev/get-started/install diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/build.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/build.yaml new file mode 100644 index 0000000..f1b199a --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/build.yaml @@ -0,0 +1,13 @@ +targets: + $default: + builders: + i69n: + options: + default_locale: en + locales: + - en + - es + - fr + - de + generate_for: + - lib/messages.i69n.yaml \ No newline at end of file diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/localization.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/localization.dart new file mode 100644 index 0000000..1691dfb --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/localization.dart @@ -0,0 +1,14 @@ +/// {{name.titleCase()}} Localization Package +/// +/// This package provides internationalization support using i69n. +/// It contains generated message classes and utilities for managing translations. +library; + +// Export generated i69n messages +export 'messages.i69n.dart'; + +// Export localization utilities +export 'src/localization_provider.dart'; + +// Export global strings instance for easy access +export 'src/strings.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/messages.i69n.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/messages.i69n.dart new file mode 100644 index 0000000..52f436d --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/messages.i69n.dart @@ -0,0 +1,579 @@ +// ignore_for_file: unused_element, unused_field, camel_case_types, annotate_overrides, prefer_single_quotes +// GENERATED FILE, do not edit! +// dart format off +import 'package:i69n/i69n.dart' as i69n; + +String get _languageCode => 'en'; +String get _localeName => 'en'; + +String _plural(int count, + {String? zero, + String? one, + String? two, + String? few, + String? many, + String? other}) => + i69n.plural(count, _languageCode, + zero: zero, one: one, two: two, few: few, many: many, other: other); +String _ordinal(int count, + {String? zero, + String? one, + String? two, + String? few, + String? many, + String? other}) => + i69n.ordinal(count, _languageCode, + zero: zero, one: one, two: two, few: few, many: many, other: other); +String _cardinal(int count, + {String? zero, + String? one, + String? two, + String? few, + String? many, + String? other}) => + i69n.cardinal(count, _languageCode, + zero: zero, one: one, two: two, few: few, many: many, other: other); + +class Messages implements i69n.I69nMessageBundle { + const Messages(); + AppMessages get app => AppMessages(this); + GenericMessages get generic => GenericMessages(this); + CommonMessages get common => CommonMessages(this); + AuthMessages get auth => AuthMessages(this); + ProfileMessages get profile => ProfileMessages(this); + NavMessages get nav => NavMessages(this); + NotificationsMessages get notifications => NotificationsMessages(this); + ErrorsMessages get errors => ErrorsMessages(this); + ValidationMessages get validation => ValidationMessages(this); + Object operator [](String key) { + var index = key.indexOf('.'); + if (index > 0) { + return (this[key.substring(0, index)] + as i69n.I69nMessageBundle)[key.substring(index + 1)]; + } + switch (key) { + case 'app': + return app; + case 'generic': + return generic; + case 'common': + return common; + case 'auth': + return auth; + case 'profile': + return profile; + case 'nav': + return nav; + case 'notifications': + return notifications; + case 'errors': + return errors; + case 'validation': + return validation; + default: + return key; + } + } +} + +class AppMessages implements i69n.I69nMessageBundle { + final Messages _parent; + const AppMessages(this._parent); + String get name => "{{name.titleCase()}}"; + String get description => "{{{description}}}"; + String get welcome_to_app => "Welcome to {{name.titleCase()}}!"; + Object operator [](String key) { + var index = key.indexOf('.'); + if (index > 0) { + return (this[key.substring(0, index)] + as i69n.I69nMessageBundle)[key.substring(index + 1)]; + } + switch (key) { + case 'name': + return name; + case 'description': + return description; + case 'welcome_to_app': + return welcome_to_app; + default: + return key; + } + } +} + +class GenericMessages implements i69n.I69nMessageBundle { + final Messages _parent; + const GenericMessages(this._parent); + String get ok => "OK"; + String get cancel => "Cancel"; + String get save => "Save"; + String get delete => "Delete"; + String get edit => "Edit"; + String get update => "Update"; + String get submit => "Submit"; + String get close => "Close"; + String get back => "Back"; + String get next => "Next"; + String get previous => "Previous"; + String get done => "Done"; + String get loading => "Loading..."; + String get error => "Error"; + String get success => "Success"; + String get warning => "Warning"; + String get info => "Info"; + String get retry => "Retry"; + String get refresh => "Refresh"; + String get yes => "Yes"; + String get no => "No"; + String get add => "+ Add"; + String get try_again => "Try Again"; + Object operator [](String key) { + var index = key.indexOf('.'); + if (index > 0) { + return (this[key.substring(0, index)] + as i69n.I69nMessageBundle)[key.substring(index + 1)]; + } + switch (key) { + case 'ok': + return ok; + case 'cancel': + return cancel; + case 'save': + return save; + case 'delete': + return delete; + case 'edit': + return edit; + case 'update': + return update; + case 'submit': + return submit; + case 'close': + return close; + case 'back': + return back; + case 'next': + return next; + case 'previous': + return previous; + case 'done': + return done; + case 'loading': + return loading; + case 'error': + return error; + case 'success': + return success; + case 'warning': + return warning; + case 'info': + return info; + case 'retry': + return retry; + case 'refresh': + return refresh; + case 'yes': + return yes; + case 'no': + return no; + case 'add': + return add; + case 'try_again': + return try_again; + default: + return key; + } + } +} + +class CommonMessages implements i69n.I69nMessageBundle { + final Messages _parent; + const CommonMessages(this._parent); + String get cancel => "Cancel"; + String get ok => "OK"; + String get save => "Save"; + String get delete => "Delete"; + String get edit => "Edit"; + String get try_again => "Try Again"; + String get week => "Week"; + String get month => "Month"; + Object operator [](String key) { + var index = key.indexOf('.'); + if (index > 0) { + return (this[key.substring(0, index)] + as i69n.I69nMessageBundle)[key.substring(index + 1)]; + } + switch (key) { + case 'cancel': + return cancel; + case 'ok': + return ok; + case 'save': + return save; + case 'delete': + return delete; + case 'edit': + return edit; + case 'try_again': + return try_again; + case 'week': + return week; + case 'month': + return month; + default: + return key; + } + } +} + +class AuthMessages implements i69n.I69nMessageBundle { + final Messages _parent; + const AuthMessages(this._parent); + String get register => "Register"; + String get sign_in => "Sign In"; + String get sign_out => "Sign Out"; + String get dont_have_account => "Don't have an account? "; + String get already_have_account => "Already have an account? "; + String get sign_out_confirmation => "Are you sure you want to sign out?"; + Object operator [](String key) { + var index = key.indexOf('.'); + if (index > 0) { + return (this[key.substring(0, index)] + as i69n.I69nMessageBundle)[key.substring(index + 1)]; + } + switch (key) { + case 'register': + return register; + case 'sign_in': + return sign_in; + case 'sign_out': + return sign_out; + case 'dont_have_account': + return dont_have_account; + case 'already_have_account': + return already_have_account; + case 'sign_out_confirmation': + return sign_out_confirmation; + default: + return key; + } + } +} + +class ProfileMessages implements i69n.I69nMessageBundle { + final Messages _parent; + const ProfileMessages(this._parent); + String get profile => "Profile"; + String get settings => "Settings"; + String get account => "Account"; + String get personal_info => "Personal Information"; + String get privacy_settings => "Privacy Settings"; + String get name => "Name"; + String get email_address => "Email Address"; + String get phone_number => "Phone Number"; + String get date_of_birth => "Date of Birth"; + String get delete_confirmation => "Delete Confirmation"; + String get delete_confirmation_message => + "Are you sure you want to delete this item? This action cannot be undone."; + Object operator [](String key) { + var index = key.indexOf('.'); + if (index > 0) { + return (this[key.substring(0, index)] + as i69n.I69nMessageBundle)[key.substring(index + 1)]; + } + switch (key) { + case 'profile': + return profile; + case 'settings': + return settings; + case 'account': + return account; + case 'personal_info': + return personal_info; + case 'privacy_settings': + return privacy_settings; + case 'name': + return name; + case 'email_address': + return email_address; + case 'phone_number': + return phone_number; + case 'date_of_birth': + return date_of_birth; + case 'delete_confirmation': + return delete_confirmation; + case 'delete_confirmation_message': + return delete_confirmation_message; + default: + return key; + } + } +} + +class NavMessages implements i69n.I69nMessageBundle { + final Messages _parent; + const NavMessages(this._parent); + String get home => "Home"; + String get dashboard => "Dashboard"; + String get profile => "Profile"; + String get settings => "Settings"; + String get notifications => "Notifications"; + Object operator [](String key) { + var index = key.indexOf('.'); + if (index > 0) { + return (this[key.substring(0, index)] + as i69n.I69nMessageBundle)[key.substring(index + 1)]; + } + switch (key) { + case 'home': + return home; + case 'dashboard': + return dashboard; + case 'profile': + return profile; + case 'settings': + return settings; + case 'notifications': + return notifications; + default: + return key; + } + } +} + +class NotificationsMessages implements i69n.I69nMessageBundle { + final Messages _parent; + const NotificationsMessages(this._parent); + String get title => "Notifications"; + String get mark_as_read => "Mark as read"; + String get mark_all_read => "Mark all as read"; + String get delete => "Delete"; + String get filter_all => "All"; + String get filter_unread => "Unread"; + String get filter_read => "Read"; + String get type_reminder => "Reminder"; + String get type_alert => "Alert"; + String get type_promotion => "Promotion"; + String get type_system => "System"; + String get type_custom => "Custom"; + String get empty_title => "No notifications"; + String get empty_description => + "You're all caught up! New notifications will appear here."; + String get delete_confirmation_title => "Delete notification?"; + String get delete_confirmation_message => + "This notification will be permanently removed from your list."; + Object operator [](String key) { + var index = key.indexOf('.'); + if (index > 0) { + return (this[key.substring(0, index)] + as i69n.I69nMessageBundle)[key.substring(index + 1)]; + } + switch (key) { + case 'title': + return title; + case 'mark_as_read': + return mark_as_read; + case 'mark_all_read': + return mark_all_read; + case 'delete': + return delete; + case 'filter_all': + return filter_all; + case 'filter_unread': + return filter_unread; + case 'filter_read': + return filter_read; + case 'type_reminder': + return type_reminder; + case 'type_alert': + return type_alert; + case 'type_promotion': + return type_promotion; + case 'type_system': + return type_system; + case 'type_custom': + return type_custom; + case 'empty_title': + return empty_title; + case 'empty_description': + return empty_description; + case 'delete_confirmation_title': + return delete_confirmation_title; + case 'delete_confirmation_message': + return delete_confirmation_message; + default: + return key; + } + } +} + +class ErrorsMessages implements i69n.I69nMessageBundle { + final Messages _parent; + const ErrorsMessages(this._parent); + String get network_error => "Network error. Please check your connection."; + String get unknown_error => "An unknown error occurred."; + String get validation_error => "Please check your input and try again."; + String get server_error => "Server error. Please try again later."; + String get default_error_message => + "Oops! Something went wrong. Please try again."; + String get user_not_found => "User not found. Please check your credentials."; + String get default_error_description => + "We encountered an error while processing your request. We apologize for the inconvenience. Please try again later or contact support if the issue persists."; + String get page_not_found => "Page Not Found"; + String get page_not_found_description => + "The page you are looking for does not exist."; + String get unexpected_error => "An unexpected error occurred."; + String get redirect_error => "Redirect Error"; + String get bad_request => "Invalid request. Please check your input."; + String get unauthorized => "Authentication required. Please sign in again."; + String get forbidden => "Access denied. You don't have permission."; + String get not_found => "Requested resource not found."; + String get conflict => "Data conflict. Please refresh and try again."; + String get unprocessable_entity => + "Invalid data format. Please check your input."; + String get internal_server_error => "Server error. Please try again later."; + String get connection_timeout => + "Connection timeout. Please check your internet."; + String get receive_timeout => "Request timeout. Please try again."; + String get send_timeout => "Upload timeout. Please try again."; + String get no_internet => + "No internet connection. Please check your network."; + String get unknown_network => "Network error occurred. Please try again."; + String format_exception_message(String code, String postfix) => + "This data is wearing the wrong costume, I don't recognize it [$code] $postfix"; + String type_error_message(String code, String postfix) => + "This data is not what I expected, I can't process it [$code] $postfix"; + String index_error_message(String code, String postfix) => + "Hmm, I can't seem to find that item in the list [$code] $postfix"; + String range_error_message(String code, String postfix) => + "Oops! That number is way out of my comfort zone [$code] $postfix"; + String argument_error_message(String code, String postfix) => + "Hey! Something's not right with what you gave me [$code] $postfix"; + String state_error_message(String code, String postfix) => + "I'm a bit confused about what I should be doing right now [$code] $postfix"; + String unimplemented_error_message(String code, String postfix) => + "This feature is still under construction [$code] $postfix"; + String unsupported_error_message(String code, String postfix) => + "Sorry, I don't know how to do that yet [$code] $postfix"; + String concurrent_modification_error_message(String code, String postfix) => + "Whoa! Too many things happening at once [$code] $postfix"; + String out_of_memory_error_message(String code, String postfix) => + "My brain is full! Need to clear some space [$code] $postfix"; + String stack_overflow_error_message(String code, String postfix) => + "I'm stuck in a loop and getting dizzy [$code] $postfix"; + String unknown_error_message(String code, String postfix) => + "Something unexpected happened, but don't worry [$code] $postfix"; + Object operator [](String key) { + var index = key.indexOf('.'); + if (index > 0) { + return (this[key.substring(0, index)] + as i69n.I69nMessageBundle)[key.substring(index + 1)]; + } + switch (key) { + case 'network_error': + return network_error; + case 'unknown_error': + return unknown_error; + case 'validation_error': + return validation_error; + case 'server_error': + return server_error; + case 'default_error_message': + return default_error_message; + case 'user_not_found': + return user_not_found; + case 'default_error_description': + return default_error_description; + case 'page_not_found': + return page_not_found; + case 'page_not_found_description': + return page_not_found_description; + case 'unexpected_error': + return unexpected_error; + case 'redirect_error': + return redirect_error; + case 'bad_request': + return bad_request; + case 'unauthorized': + return unauthorized; + case 'forbidden': + return forbidden; + case 'not_found': + return not_found; + case 'conflict': + return conflict; + case 'unprocessable_entity': + return unprocessable_entity; + case 'internal_server_error': + return internal_server_error; + case 'connection_timeout': + return connection_timeout; + case 'receive_timeout': + return receive_timeout; + case 'send_timeout': + return send_timeout; + case 'no_internet': + return no_internet; + case 'unknown_network': + return unknown_network; + case 'format_exception_message': + return format_exception_message; + case 'type_error_message': + return type_error_message; + case 'index_error_message': + return index_error_message; + case 'range_error_message': + return range_error_message; + case 'argument_error_message': + return argument_error_message; + case 'state_error_message': + return state_error_message; + case 'unimplemented_error_message': + return unimplemented_error_message; + case 'unsupported_error_message': + return unsupported_error_message; + case 'concurrent_modification_error_message': + return concurrent_modification_error_message; + case 'out_of_memory_error_message': + return out_of_memory_error_message; + case 'stack_overflow_error_message': + return stack_overflow_error_message; + case 'unknown_error_message': + return unknown_error_message; + default: + return key; + } + } +} + +class ValidationMessages implements i69n.I69nMessageBundle { + final Messages _parent; + const ValidationMessages(this._parent); + String get required_field => "This field is required"; + String get invalid_email => "Please enter a valid email address"; + String get password_too_short => "Password must be at least 8 characters"; + String get passwords_dont_match => "Passwords do not match"; + String invalid_key_config(String of, String key) => + "Invalid configuration for $key in $of. Please check your settings."; + Object operator [](String key) { + var index = key.indexOf('.'); + if (index > 0) { + return (this[key.substring(0, index)] + as i69n.I69nMessageBundle)[key.substring(index + 1)]; + } + switch (key) { + case 'required_field': + return required_field; + case 'invalid_email': + return invalid_email; + case 'password_too_short': + return password_too_short; + case 'passwords_dont_match': + return passwords_dont_match; + case 'invalid_key_config': + return invalid_key_config; + default: + return key; + } + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/messages.i69n.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/messages.i69n.yaml new file mode 100644 index 0000000..15beb7f --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/messages.i69n.yaml @@ -0,0 +1,151 @@ +# {{name.titleCase()}} - Internationalization Messages +# Base translations for the application + +# application metadata +app: + name: "{{name.titleCase()}}" + description: "{{{description}}}" + welcome_to_app: "Welcome to {{name.titleCase()}}!" + +# Common/Generic messages +generic: + ok: "OK" + cancel: "Cancel" + save: "Save" + delete: "Delete" + edit: "Edit" + update: "Update" + submit: "Submit" + close: "Close" + back: "Back" + next: "Next" + previous: "Previous" + done: "Done" + loading: "Loading..." + error: "Error" + success: "Success" + warning: "Warning" + info: "Info" + retry: "Retry" + refresh: "Refresh" + yes: "Yes" + no: "No" + add: "+ Add" + try_again: "Try Again" + +# Common messages (used for consistency across modules) +common: + cancel: "Cancel" + ok: "OK" + save: "Save" + delete: "Delete" + edit: "Edit" + try_again: "Try Again" + week: "Week" + month: "Month" + +# Authentication +auth: + register: "Register" + sign_in: "Sign In" + sign_out: "Sign Out" + dont_have_account: "Don't have an account? " + already_have_account: "Already have an account? " + sign_out_confirmation: "Are you sure you want to sign out?" + +# Profile +# Profile +profile: + profile: "Profile" + settings: "Settings" + account: "Account" + personal_info: "Personal Information" + privacy_settings: "Privacy Settings" + name: "Name" + email_address: "Email Address" + phone_number: "Phone Number" + date_of_birth: "Date of Birth" + delete_confirmation: "Delete Confirmation" + delete_confirmation_message: "Are you sure you want to delete this item? This action cannot be undone." + +nav: + home: "Home" + dashboard: "Dashboard" + profile: "Profile" + settings: "Settings" + notifications: "Notifications" + +# Notifications +notifications: + title: "Notifications" + mark_as_read: "Mark as read" + mark_all_read: "Mark all as read" + delete: "Delete" + + # Filter tabs + filter_all: "All" + filter_unread: "Unread" + filter_read: "Read" + + # Notification types + type_reminder: "Reminder" + type_alert: "Alert" + type_promotion: "Promotion" + type_system: "System" + type_custom: "Custom" + + # Empty states + empty_title: "No notifications" + empty_description: "You're all caught up! New notifications will appear here." + + # Delete confirmation + delete_confirmation_title: "Delete notification?" + delete_confirmation_message: "This notification will be permanently removed from your list." + +# Errors +errors: + network_error: "Network error. Please check your connection." + unknown_error: "An unknown error occurred." + validation_error: "Please check your input and try again." + server_error: "Server error. Please try again later." + default_error_message: "Oops! Something went wrong. Please try again." + user_not_found: "User not found. Please check your credentials." + default_error_description: "We encountered an error while processing your request. We apologize for the inconvenience. Please try again later or contact support if the issue persists." + page_not_found: "Page Not Found" + page_not_found_description: "The page you are looking for does not exist." + unexpected_error: "An unexpected error occurred." + redirect_error: "Redirect Error" + bad_request: "Invalid request. Please check your input." + unauthorized: "Authentication required. Please sign in again." + forbidden: "Access denied. You don't have permission." + not_found: "Requested resource not found." + conflict: "Data conflict. Please refresh and try again." + unprocessable_entity: "Invalid data format. Please check your input." + internal_server_error: "Server error. Please try again later." + connection_timeout: "Connection timeout. Please check your internet." + receive_timeout: "Request timeout. Please try again." + send_timeout: "Upload timeout. Please try again." + no_internet: "No internet connection. Please check your network." + unknown_network: "Network error occurred. Please try again." + format_exception_message(String code, String postfix): "This data is wearing the wrong costume, I don't recognize it [$code] $postfix" + type_error_message(String code, String postfix): "This data is not what I expected, I can't process it [$code] $postfix" + index_error_message(String code, String postfix): "Hmm, I can't seem to find that item in the list [$code] $postfix" + range_error_message(String code, String postfix): "Oops! That number is way out of my comfort zone [$code] $postfix" + argument_error_message(String code, String postfix): "Hey! Something's not right with what you gave me [$code] $postfix" + state_error_message(String code, String postfix): "I'm a bit confused about what I should be doing right now [$code] $postfix" + unimplemented_error_message(String code, String postfix): "This feature is still under construction [$code] $postfix" + unsupported_error_message(String code, String postfix): "Sorry, I don't know how to do that yet [$code] $postfix" + concurrent_modification_error_message(String code, String postfix): "Whoa! Too many things happening at once [$code] $postfix" + out_of_memory_error_message(String code, String postfix): "My brain is full! Need to clear some space [$code] $postfix" + stack_overflow_error_message(String code, String postfix): "I'm stuck in a loop and getting dizzy [$code] $postfix" + unknown_error_message(String code, String postfix): "Something unexpected happened, but don't worry [$code] $postfix" +# Validation messages +validation: + required_field: "This field is required" + invalid_email: "Please enter a valid email address" + password_too_short: "Password must be at least 8 characters" + passwords_dont_match: "Passwords do not match" + invalid_key_config(String of, String key): "Invalid configuration for $key in $of. Please check your settings." + +# BioKit + diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/src/localization_provider.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/src/localization_provider.dart new file mode 100644 index 0000000..1598ccb --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/src/localization_provider.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:localization/messages.i69n.dart'; + +/// Provides localization utilities for the {{name.titleCase()}} application +class LocalizationProvider { + static Messages _messages = const Messages(); + + /// Get the current messages instance + static Messages get messages => _messages; + + /// Initialize localization (currently only supports English) + static void initialize({String locale = 'en'}) { + // Currently only supports English, future versions will support multiple locales + _messages = const Messages(); + } + + /// Get messages (currently only supports English) + static Messages getMessages(String locale) { + // Currently only supports English, future versions will support multiple locales + return const Messages(); + } + + /// Get the current locale + static String get currentLocale => 'en'; + + /// Get supported locales (currently only English) + static List get supportedLocales => ['en']; + + /// Check if a locale is supported + static bool isLocaleSupported(String locale) { + return supportedLocales.contains(locale); + } + + /// Get the best matching locale from a list of preferred locales + static String getBestMatchingLocale(List preferredLocales) { + for (final locale in preferredLocales) { + if (isLocaleSupported(locale.languageCode)) { + return locale.languageCode; + } + } + return 'en'; // Default to English + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/src/strings.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/src/strings.dart new file mode 100644 index 0000000..30e8e64 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/src/strings.dart @@ -0,0 +1,14 @@ +import 'package:localization/localization.dart'; + +/// Global strings instance for easy access throughout the application +/// +/// Usage: +/// ```dart +/// import 'package:localization/localization.dart'; +/// +/// // Access strings easily +/// Text(strings.auth.login); +/// Text(strings.generic.ok); +/// Text(strings.profile.name); +/// ``` +Messages get strings => LocalizationProvider.messages; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/pubspec.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/pubspec.yaml new file mode 100644 index 0000000..76ebee1 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/pubspec.yaml @@ -0,0 +1,21 @@ +name: localization +description: "Type-safe strings for {{name.titleCase()}}, generated from YAML with i69n." +version: 0.1.0 +publish_to: 'none' + +environment: + sdk: ^3.8.0 + flutter: ">=3.29.0" + +dependencies: + flutter: + sdk: flutter + i69n: ^3.1.0 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + build_runner: ^2.4.15 + +flutter: diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/test/localization_test.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/test/localization_test.dart new file mode 100644 index 0000000..52f0988 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/test/localization_test.dart @@ -0,0 +1,26 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:localization/localization.dart'; + +void main() { + group('LocalizationProvider Tests', () { + test('should initialize with default locale', () { + LocalizationProvider.initialize(); + expect(LocalizationProvider.currentLocale, 'en'); + }); + + test('should return Messages instance', () { + final messages = LocalizationProvider.messages; + expect(messages, isA()); + }); + + test('should return supported locales', () { + final locales = LocalizationProvider.supportedLocales; + expect(locales, contains('en')); + }); + + test('should check if locale is supported', () { + expect(LocalizationProvider.isLocaleSupported('en'), true); + expect(LocalizationProvider.isLocaleSupported('es'), false); + }); + }); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/utils/lib/src/number_formatter.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/utils/lib/src/number_formatter.dart new file mode 100644 index 0000000..06e58a4 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/utils/lib/src/number_formatter.dart @@ -0,0 +1,79 @@ +import 'package:intl/intl.dart'; + +/// Extension for formatting decimal numbers with clean, professional display +extension DoubleFormatting on double { + /// Formats decimal values, removing unnecessary .0 for whole numbers + String format({int decimalPlaces = 1}) { + final formatter = NumberFormat('#.${'#' * decimalPlaces}'); + return formatter.format(this); + } + + /// Formats score values with clean decimal handling + String asScore() => format(decimalPlaces: 1); + + /// Formats HRV values with smart decimal handling + String asHrv() => format(decimalPlaces: 1); + + /// Formats sleep hours with smart decimal handling + String asSleepHours() => format(decimalPlaces: 1); + + /// Formats respiratory rate with smart decimal handling + String asRespiratoryRate() => format(decimalPlaces: 1); +} + +/// Extension for formatting any numeric value +extension NumFormatting on num { + /// Formats large numbers with K, M, B suffixes and smart decimal handling + String compact() { + final NumberFormat formatter = NumberFormat.compact(locale: 'en_US'); + return formatter.format(this); + } + + /// Formats heart rate values (always whole numbers) + String asHeartRate() => round().toString(); +} + +/// Extension for formatting time duration from seconds +extension IntTimeFormatting on int { + /// Format time from seconds to 1 sec or 1min 1sec or 1hr 1min + String asTime() { + if (this < 60) { + return '$this sec'; + } else if (this < 3600) { + final minutes = this ~/ 60; + final remainingSeconds = this % 60; + if (remainingSeconds == 0) { + return '$minutes min'; + } + return '$minutes min $remainingSeconds sec'; + } else { + final hours = this ~/ 3600; + final remainingMinutes = (this % 3600) ~/ 60; + if (remainingMinutes == 0) { + return '$hours hr'; + } + return '$hours hr $remainingMinutes min'; + } + } +} + +extension DateTimeFormatting on DateTime { + String formatRelative() { + final now = DateTime.now(); + final difference = now.difference(this); + + if (difference.inMinutes < 1) { + return 'Just now'; + } else if (difference.inMinutes < 60) { + return '${difference.inMinutes}m ago'; + } else if (difference.inHours < 24) { + return '${difference.inHours}h ago'; + } else if (difference.inDays == 1) { + return 'Yesterday'; + } else if (difference.inDays < 7) { + return '${difference.inDays}d ago'; + } else { + return '$day/$month/$year'; + } + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/utils/lib/utils.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/utils/lib/utils.dart new file mode 100644 index 0000000..036f0c1 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/utils/lib/utils.dart @@ -0,0 +1,3 @@ +library; + +export 'src/number_formatter.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/utils/pubspec.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/utils/pubspec.yaml new file mode 100644 index 0000000..ba5d039 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/utils/pubspec.yaml @@ -0,0 +1,20 @@ +name: utils +description: "Shared, dependency-light helpers for {{name.titleCase()}}." +version: 0.1.0 +publish_to: 'none' + +environment: + sdk: ^3.8.0 + flutter: ">=3.29.0" + +dependencies: + flutter: + sdk: flutter + intl: ^0.20.2 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/README.md b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/README.md new file mode 100644 index 0000000..3591060 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/README.md @@ -0,0 +1,267 @@ +# Analytics Package + +A structured analytics package for {{name.titleCase()}} with Firebase Analytics integration and pluggable provider architecture. + +## Features + +- ✅ **Interface-based design** - Easy to switch analytics providers +- ✅ **Automatic screen tracking** - Route observer tracks navigation automatically +- ✅ **Structured event names** - Type-safe, organized event constants +- ✅ **Dependency injection** - Integrated with DI container +- ✅ **Error handling** - Graceful failures don't crash the app +- ✅ **Debug logging** - Configurable logging for development + +## Quick Start + +### 1. Initialize Analytics + +```dart +import 'package:analytics/analytics.dart'; + +final analyticsConfig = DefaultAnalyticsConfig( + enableAnalytics: true, + enableDebugLogging: true, // Set to false in production +); +await init(config: analyticsConfig); +``` + +### 2. Automatic Screen Tracking + +Add the `AnalyticsRouteObserver` to your router: + +```dart +GoRouter( + observers: [ + AnalyticsRouteObserver(), // Uses FirebaseAnalyticsObserver internally + ], + // ... routes +); +``` + +The `AnalyticsRouteObserver` internally uses Firebase's native `FirebaseAnalyticsObserver` for proper screen tracking while adding additional logging and error handling. + +### 3. Manual Event Tracking + +#### Using Structured Event Names (Recommended) + +```dart +// User events +await AnalyticsHelper.logEvent(AnalyticsEvents.user.authenticatedRedirect); + +// Authentication events +await AnalyticsHelper.logEvent( + AnalyticsEvents.auth.signIn, + parameters: {'method': 'google'}, +); + +// Feature usage +await AnalyticsHelper.logEvent( + AnalyticsEvents.feature.used, + parameters: {'feature_name': 'health_sync'}, +); + +// Survey events +await AnalyticsHelper.logEvent( + AnalyticsEvents.survey.completed, + parameters: { + 'survey_id': 'wellness_assessment', + 'duration_seconds': 120, + }, +); +``` + +#### Using Helper Methods + +```dart +// Feature tracking +await AnalyticsHelper.logFeatureUsed('health_dashboard'); + +// Survey tracking +await AnalyticsHelper.logSurveyStarted('mood_tracker'); +await AnalyticsHelper.logSurveyCompleted('mood_tracker', duration: 45); + +// Error tracking +await AnalyticsHelper.logAppError('network_timeout'); + +// Button interactions +await AnalyticsHelper.logButtonPressed('start_assessment'); + +// Authentication events +await AnalyticsHelper.logSignIn(method: 'google'); +await AnalyticsHelper.logSignUp(method: 'email'); + +// User management +await AnalyticsHelper.setUserId('user123'); +await AnalyticsHelper.setUserProperty(name: 'plan', value: 'premium'); + +// App lifecycle +await AnalyticsHelper.logAppOpen(); +``` + +#### Direct Client Usage + +```dart +final analyticsClient = di.get(); + +await analyticsClient.logEvent( + name: 'custom_event', + parameters: {'key': 'value'}, +); + +await analyticsClient.setUserId('user123'); +await analyticsClient.setUserProperty(name: 'plan', value: 'premium'); +``` + +## Structured Event Names + +The package provides organized, type-safe event names through `AnalyticsEvents`: + +### Available Event Categories + +```dart +// User-related events +AnalyticsEvents.user.authenticatedRedirect +AnalyticsEvents.user.profileUpdated +AnalyticsEvents.user.onboardingCompleted + +// Authentication events +AnalyticsEvents.auth.signIn +AnalyticsEvents.auth.signUp +AnalyticsEvents.auth.signOut + +// Navigation events +AnalyticsEvents.navigation.screenView +AnalyticsEvents.navigation.tabSwitched + +// Feature usage +AnalyticsEvents.feature.used +AnalyticsEvents.feature.tutorialCompleted + +// App lifecycle +AnalyticsEvents.app.open +AnalyticsEvents.app.background +AnalyticsEvents.app.foreground + +// Survey events +AnalyticsEvents.survey.started +AnalyticsEvents.survey.completed +AnalyticsEvents.survey.abandoned + +// Health data events +AnalyticsEvents.health.dataSynced +AnalyticsEvents.health.deviceConnected +AnalyticsEvents.health.goalAchieved + +// Error events +AnalyticsEvents.error.appError +AnalyticsEvents.error.networkError +``` + +## Configuration + +### AnalyticsConfig Options + +```dart +final config = DefaultAnalyticsConfig( + enableAnalytics: true, // Enable/disable analytics collection + enableDebugLogging: false, // Debug logging (development only) + userId: 'user123', // Optional: Set initial user ID + defaultUserProperties: { // Optional: Default user properties + 'user_type': 'premium', + 'app_version': '1.0.0', + }, +); +``` + +### Environment-based Configuration + +```dart +import 'package:flutter/foundation.dart'; + +final config = DefaultAnalyticsConfig( + enableAnalytics: !kDebugMode, // Disable in debug builds + enableDebugLogging: kDebugMode, // Debug logging in development +); +``` + +## Architecture + +### Components + +- **AnalyticsClient** - Abstract interface for analytics operations +- **FirebaseAnalyticsClient** - Firebase Analytics implementation +- **AnalyticsRouteObserver** - Automatic screen tracking +- **AnalyticsHelper** - Convenience methods for common events +- **AnalyticsEvents** - Structured event name constants + +### Pluggable Design + +Switch analytics providers by implementing `AnalyticsClient`: + +```dart +class MixpanelAnalyticsClient implements AnalyticsClient { + @override + Future logEvent({required String name, Map? parameters}) { + // Mixpanel implementation + } + // ... other methods +} + +// Register with DI +di.register(MixpanelAnalyticsClient(config)); +``` + +## Best Practices + +1. **Use Structured Events**: Always prefer `AnalyticsEvents.category.eventName` over string literals +2. **Include Context**: Add relevant parameters like `screen_name`, `user_id`, etc. +3. **Consistent Naming**: Use snake_case for event names and parameters +4. **Error Handling**: Analytics failures should never crash the app +5. **Privacy**: Respect user privacy settings and data regulations + +## Examples + +### Track User Journey + +```dart +// App launch +await AnalyticsHelper.logAppOpen(); + +// User signs in +await AnalyticsHelper.logSignIn(method: 'google'); + +// User completes onboarding +await AnalyticsHelper.logEvent( + AnalyticsEvents.user.onboardingCompleted, + parameters: {'steps_completed': 5}, +); + +// User uses a feature +await AnalyticsHelper.logFeatureUsed('health_sync'); + +// User completes a survey +await AnalyticsHelper.logSurveyCompleted( + 'wellness_assessment', + duration: 180, +); +``` + +### Error Tracking + +```dart +try { + // Some operation +} catch (error) { + await AnalyticsHelper.logAppError( + 'api_call_failed', + errorMessage: error.toString(), + parameters: { + 'endpoint': '/api/health/sync', + 'user_id': userId, + }, + ); + rethrow; +} +``` + +This structured approach ensures consistent, discoverable, and maintainable analytics throughout the {{name.titleCase()}} app. \ No newline at end of file diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/analytics.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/analytics.dart new file mode 100644 index 0000000..e1cc4bf --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/analytics.dart @@ -0,0 +1,37 @@ +library; + +import 'package:analytics/src/client/index.dart'; +import 'package:analytics/src/config/index.dart'; +import 'package:core/core.dart'; +import 'package:di/di.dart'; + +export 'src/client/index.dart'; +export 'src/config/index.dart'; +export 'src/models/index.dart'; +export 'src/observer/index.dart'; +export 'src/utils/index.dart'; + +Future init({ + AnalyticsConfig config = const DefaultAnalyticsConfig(), +}) async { + await registerAnalyticsWithDI(config); + final logger = di.get(); + logger.i('Analytics module initialized with Firebase Analytics'); +} + +Future registerAnalyticsWithDI(AnalyticsConfig config) async { + final logger = di.get(); + + final analyticsClient = FirebaseAnalyticsClient(config); + di.register( + analyticsClient, + dispose: (client) => client.dispose(), + ); + di.register(config); + + if (config.enableAnalytics) { + logger.i('🔍 Analytics client registered with collection enabled'); + } else { + logger.i('🔍 Analytics client registered with collection disabled'); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/client/analytics_client.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/client/analytics_client.dart new file mode 100644 index 0000000..40f738a --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/client/analytics_client.dart @@ -0,0 +1,44 @@ +import 'package:analytics/src/models/analytics_event.dart'; + +abstract class AnalyticsClient { + Future logEvent({ + required String name, + Map? parameters, + }); + + Future logCustomEvent(AnalyticsEvent event); + + Future setUserId(String? userId); + + Future setUserProperty({required String name, required String? value}); + + Future resetAnalyticsData(); + + Future setAnalyticsCollectionEnabled(bool enabled); + + Future logScreenView({ + required String screenName, + String? screenClass, + Map? parameters, + }); + + Future logAppOpen({Map? parameters}); + + Future logLogin({ + String? loginMethod, + Map? parameters, + }); + + Future logSignUp({ + String? signUpMethod, + Map? parameters, + }); + + Future logPurchase({ + required String currency, + required double value, + Map? parameters, + }); + + void dispose(); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/client/firebase_analytics_client.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/client/firebase_analytics_client.dart new file mode 100644 index 0000000..d9f7206 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/client/firebase_analytics_client.dart @@ -0,0 +1,204 @@ +import 'package:analytics/src/client/analytics_client.dart'; +import 'package:analytics/src/config/analytics_config.dart'; +import 'package:analytics/src/models/analytics_event.dart'; +import 'package:core/core.dart'; +import 'package:di/di.dart'; +import 'package:firebase_analytics/firebase_analytics.dart'; + +class FirebaseAnalyticsClient implements AnalyticsClient { + FirebaseAnalyticsClient(this._config) + : _analytics = FirebaseAnalytics.instance { + _setupAnalytics(); + } + + final AnalyticsConfig _config; + final FirebaseAnalytics _analytics; + final Logger _logger = di.get(); + + void _setupAnalytics() { + _analytics.setAnalyticsCollectionEnabled(_config.enableAnalytics); + + if (_config.userId != null) { + _analytics.setUserId(id: _config.userId); + _logger.d('🔍 Analytics user ID set: ${_config.userId}'); + } + + if (_config.defaultUserProperties != null) { + for (final entry in _config.defaultUserProperties!.entries) { + _analytics.setUserProperty(name: entry.key, value: entry.value); + } + _logger.d('🔍 Analytics default user properties set'); + } + + if (_config.enableDebugLogging) { + _logger.d('🔍 Firebase Analytics initialized with debug logging enabled'); + } else { + _logger.i('🔍 Firebase Analytics initialized'); + } + } + + @override + Future logEvent({ + required String name, + Map? parameters, + }) async { + try { + if (!_config.enableAnalytics) { + if (_config.enableDebugLogging) { + _logger.d('🔍 Analytics disabled, skipping event: $name'); + } + return; + } + + await _analytics.logEvent( + name: name, + parameters: parameters?.cast(), + ); + + if (_config.enableDebugLogging) { + _logger.d( + '🔍 Analytics event logged: $name with parameters: $parameters', + ); + } + } catch (e, s) { + _logger.e('Failed to log analytics event: $name', e, s); + } + } + + @override + Future logCustomEvent(AnalyticsEvent event) async { + await logEvent(name: event.name, parameters: event.parameters); + } + + @override + Future setUserId(String? userId) async { + try { + await _analytics.setUserId(id: userId); + + if (_config.enableDebugLogging) { + _logger.d('🔍 Analytics user ID set: $userId'); + } + } catch (e, s) { + _logger.e('Failed to set analytics user ID: $userId', e, s); + } + } + + @override + Future setUserProperty({ + required String name, + required String? value, + }) async { + try { + await _analytics.setUserProperty(name: name, value: value); + + if (_config.enableDebugLogging) { + _logger.d('🔍 Analytics user property set: $name = $value'); + } + } catch (e, s) { + _logger.e('Failed to set analytics user property: $name', e, s); + } + } + + @override + Future resetAnalyticsData() async { + try { + await _analytics.resetAnalyticsData(); + + if (_config.enableDebugLogging) { + _logger.d('🔍 Analytics data reset'); + } + } catch (e, s) { + _logger.e('Failed to reset analytics data', e, s); + } + } + + @override + Future setAnalyticsCollectionEnabled(bool enabled) async { + try { + await _analytics.setAnalyticsCollectionEnabled(enabled); + + if (_config.enableDebugLogging) { + _logger.d( + '🔍 Analytics collection ${enabled ? 'enabled' : 'disabled'}', + ); + } + } catch (e, s) { + _logger.e('Failed to set analytics collection enabled: $enabled', e, s); + } + } + + @override + Future logScreenView({ + required String screenName, + String? screenClass, + Map? parameters, + }) async { + final eventParameters = { + PredefinedParameters.screenName: screenName, + if (screenClass != null) PredefinedParameters.screenClass: screenClass, + ...?parameters, + }; + + await logEvent( + name: PredefinedEvents.screenView, + parameters: eventParameters, + ); + } + + @override + Future logAppOpen({Map? parameters}) async { + await logEvent(name: PredefinedEvents.appOpen, parameters: parameters); + } + + @override + Future logLogin({ + String? loginMethod, + Map? parameters, + }) async { + final eventParameters = { + if (loginMethod != null) PredefinedParameters.method: loginMethod, + ...?parameters, + }; + + await logEvent(name: PredefinedEvents.login, parameters: eventParameters); + } + + @override + Future logSignUp({ + String? signUpMethod, + Map? parameters, + }) async { + final eventParameters = { + if (signUpMethod != null) PredefinedParameters.method: signUpMethod, + ...?parameters, + }; + + await logEvent(name: PredefinedEvents.signUp, parameters: eventParameters); + } + + @override + Future logPurchase({ + required String currency, + required double value, + Map? parameters, + }) async { + final eventParameters = { + PredefinedParameters.currency: currency, + PredefinedParameters.value: value, + ...?parameters, + }; + + await logEvent( + name: PredefinedEvents.purchase, + parameters: eventParameters, + ); + } + + @override + void dispose() { + // Firebase Analytics doesn't need explicit disposal + if (_config.enableDebugLogging) { + _logger.d('🔍 Firebase Analytics client disposed'); + } + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/client/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/client/index.dart new file mode 100644 index 0000000..959ed69 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/client/index.dart @@ -0,0 +1,2 @@ +export 'analytics_client.dart'; +export 'firebase_analytics_client.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/config/analytics_config.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/config/analytics_config.dart new file mode 100644 index 0000000..d4b6d02 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/config/analytics_config.dart @@ -0,0 +1,42 @@ +abstract class AnalyticsConfig { + bool get enableAnalytics; + bool get enableDebugLogging; + String? get userId; + Map? get defaultUserProperties; +} + +class DefaultAnalyticsConfig implements AnalyticsConfig { + const DefaultAnalyticsConfig({ + this.enableAnalytics = true, + this.enableDebugLogging = false, + this.userId, + this.defaultUserProperties, + }); + + @override + final bool enableAnalytics; + + @override + final bool enableDebugLogging; + + @override + final String? userId; + + @override + final Map? defaultUserProperties; + + DefaultAnalyticsConfig copyWith({ + bool? enableAnalytics, + bool? enableDebugLogging, + String? userId, + Map? defaultUserProperties, + }) { + return DefaultAnalyticsConfig( + enableAnalytics: enableAnalytics ?? this.enableAnalytics, + enableDebugLogging: enableDebugLogging ?? this.enableDebugLogging, + userId: userId ?? this.userId, + defaultUserProperties: + defaultUserProperties ?? this.defaultUserProperties, + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/config/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/config/index.dart new file mode 100644 index 0000000..cfd2ef0 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/config/index.dart @@ -0,0 +1 @@ +export 'analytics_config.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/analytics_event.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/analytics_event.dart new file mode 100644 index 0000000..97ab101 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/analytics_event.dart @@ -0,0 +1,63 @@ +class AnalyticsEvent { + const AnalyticsEvent({required this.name, this.parameters}); + + final String name; + final Map? parameters; + + Map toMap() { + return {'name': name, if (parameters != null) 'parameters': parameters}; + } + + @override + String toString() => 'AnalyticsEvent(name: $name, parameters: $parameters)'; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + return other is AnalyticsEvent && + other.name == name && + _mapEquals(other.parameters, parameters); + } + + @override + int get hashCode => name.hashCode ^ parameters.hashCode; + + bool _mapEquals(Map? a, Map? b) { + if (a == null) return b == null; + if (b == null || a.length != b.length) return false; + for (final key in a.keys) { + if (!b.containsKey(key) || a[key] != b[key]) return false; + } + return true; + } +} + +class PredefinedEvents { + static const String appOpen = 'app_open'; + static const String login = 'login'; + static const String signUp = 'sign_up'; + static const String screenView = 'screen_view'; + static const String purchase = 'purchase'; + static const String selectContent = 'select_content'; + static const String share = 'share'; + static const String search = 'search'; + static const String beginCheckout = 'begin_checkout'; + static const String addToCart = 'add_to_cart'; + static const String removeFromCart = 'remove_from_cart'; + static const String viewItem = 'view_item'; + static const String viewItemList = 'view_item_list'; +} + +class PredefinedParameters { + static const String screenName = 'screen_name'; + static const String screenClass = 'screen_class'; + static const String method = 'method'; + static const String currency = 'currency'; + static const String value = 'value'; + static const String itemId = 'item_id'; + static const String itemName = 'item_name'; + static const String itemCategory = 'item_category'; + static const String contentType = 'content_type'; + static const String searchTerm = 'search_term'; + static const String userId = 'user_id'; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/analytics_events.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/analytics_events.dart new file mode 100644 index 0000000..231b32e --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/analytics_events.dart @@ -0,0 +1,217 @@ +/// Structured analytics event names organized by category +/// +/// Usage: AnalyticsEvents.user.authenticatedRedirect +/// This provides type-safe, discoverable event names and prevents typos +class AnalyticsEvents { + AnalyticsEvents._(); + + /// User-related events + static const user = _UserEvents(); + + /// Authentication-related events + static const auth = _AuthEvents(); + + /// Navigation-related events + static const navigation = _NavigationEvents(); + + /// Feature usage events + static const feature = _FeatureEvents(); + + /// Error and debugging events + static const error = _ErrorEvents(); + + /// App lifecycle events + static const app = _AppEvents(); + + /// Survey and assessment events + static const survey = _SurveyEvents(); + + /// Health data and biokit events + static const health = _HealthEvents(); + + /// Profile-related events + static const profile = _ProfileEvents(); +} + +class _UserEvents { + const _UserEvents(); + + /// User successfully authenticated and redirected + String get authenticatedRedirect => 'user_authenticated_redirect'; + + /// User profile updated + String get profileUpdated => 'user_profile_updated'; + + /// User preferences changed + String get preferencesChanged => 'user_preferences_changed'; + + /// User completed onboarding + String get onboardingCompleted => 'user_onboarding_completed'; + + /// User account deleted + String get accountDeleted => 'user_account_deleted'; +} + +class _AuthEvents { + const _AuthEvents(); + + /// User signed in + String get signIn => 'auth_sign_in'; + + /// User signed up + String get signUp => 'auth_sign_up'; + + /// User signed out + String get signOut => 'auth_sign_out'; + + /// Password reset requested + String get passwordReset => 'auth_password_reset'; + + /// Authentication failed + String get failed => 'auth_failed'; + + /// Social authentication (Google, Apple, etc.) + String get socialLogin => 'auth_social_login'; +} + +class _NavigationEvents { + const _NavigationEvents(); + + /// Screen viewed (automatically tracked) + String get screenView => 'screen_view'; + + /// Tab switched in bottom navigation + String get tabSwitched => 'navigation_tab_switched'; + + /// Back button pressed + String get backPressed => 'navigation_back_pressed'; + + /// Deep link opened + String get deepLinkOpened => 'navigation_deep_link_opened'; +} + +class _FeatureEvents { + const _FeatureEvents(); + + /// Feature was used/accessed + String get used => 'feature_used'; + + /// Feature tutorial started + String get tutorialStarted => 'feature_tutorial_started'; + + /// Feature tutorial completed + String get tutorialCompleted => 'feature_tutorial_completed'; + + /// Feature feedback given + String get feedbackGiven => 'feature_feedback_given'; + + /// Button pressed + String get buttonPressed => 'button_pressed'; +} + +class _ErrorEvents { + const _ErrorEvents(); + + /// General app error occurred + String get appError => 'app_error'; + + /// Network error occurred + String get networkError => 'network_error'; + + /// Crash occurred + String get crash => 'app_crash'; + + /// API error occurred + String get apiError => 'api_error'; + + /// Validation error + String get validationError => 'validation_error'; +} + +class _AppEvents { + const _AppEvents(); + + /// App opened/launched + String get open => 'app_open'; + + /// App went to background + String get background => 'app_background'; + + /// App came to foreground + String get foreground => 'app_foreground'; + + /// App updated to new version + String get updated => 'app_updated'; + + /// App installed for first time + String get installed => 'app_installed'; + + /// App uninstalled + String get uninstalled => 'app_uninstalled'; +} + +class _SurveyEvents { + const _SurveyEvents(); + + /// Survey started + String get started => 'survey_started'; + + /// Survey completed + String get completed => 'survey_completed'; + + /// Survey abandoned/cancelled + String get abandoned => 'survey_abandoned'; + + /// Survey question answered + String get questionAnswered => 'survey_question_answered'; + + /// Survey progress saved + String get progressSaved => 'survey_progress_saved'; + + /// Survey results viewed + String get resultsViewed => 'survey_results_viewed'; +} + +class _HealthEvents { + const _HealthEvents(); + + /// Health data synced + String get dataSynced => 'health_data_synced'; + + /// Wearable device connected + String get deviceConnected => 'health_device_connected'; + + /// Wearable device disconnected + String get deviceDisconnected => 'health_device_disconnected'; + + /// Health metric recorded + String get metricRecorded => 'health_metric_recorded'; + + /// Health goal set + String get goalSet => 'health_goal_set'; + + /// Health goal achieved + String get goalAchieved => 'health_goal_achieved'; + + /// Health report generated + String get reportGenerated => 'health_report_generated'; +} + +class _ProfileEvents { + const _ProfileEvents(); + + /// Profile viewed + String get viewed => 'profile_viewed'; + + /// Profile edited + String get edited => 'profile_edited'; + + /// Profile photo updated + String get photoUpdated => 'profile_photo_updated'; + + /// Profile settings changed + String get settingsChanged => 'profile_settings_changed'; + + /// Profile privacy settings updated + String get privacyUpdated => 'profile_privacy_updated'; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/index.dart new file mode 100644 index 0000000..17f5809 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/index.dart @@ -0,0 +1,2 @@ +export 'analytics_event.dart'; +export 'analytics_events.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/observer/analytics_route_observer.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/observer/analytics_route_observer.dart new file mode 100644 index 0000000..d30f281 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/observer/analytics_route_observer.dart @@ -0,0 +1,65 @@ +import 'package:core/core.dart'; +import 'package:di/di.dart'; +import 'package:firebase_analytics/firebase_analytics.dart'; +import 'package:flutter/material.dart'; + +/// Analytics route observer that uses Firebase Analytics Observer internally +/// while providing additional logging and error handling capabilities +class AnalyticsRouteObserver extends NavigatorObserver { + AnalyticsRouteObserver() + : _logger = di.get(), + _firebaseObserver = FirebaseAnalyticsObserver( + analytics: FirebaseAnalytics.instance, + ); + + final Logger _logger; + final FirebaseAnalyticsObserver _firebaseObserver; + + @override + void didPush(Route route, Route? previousRoute) { + super.didPush(route, previousRoute); + + try { + // Delegate to Firebase Analytics Observer for native screen tracking + _firebaseObserver.didPush(route, previousRoute); + } catch (e, s) { + _logger.e('Failed to track screen push event', e, s); + } + } + + @override + void didPop(Route route, Route? previousRoute) { + super.didPop(route, previousRoute); + + try { + // Delegate to Firebase Analytics Observer + _firebaseObserver.didPop(route, previousRoute); + } catch (e, s) { + _logger.e('Failed to track screen pop event', e, s); + } + } + + @override + void didReplace({Route? newRoute, Route? oldRoute}) { + super.didReplace(newRoute: newRoute, oldRoute: oldRoute); + + try { + // Delegate to Firebase Analytics Observer + _firebaseObserver.didReplace(newRoute: newRoute, oldRoute: oldRoute); + } catch (e, s) { + _logger.e('Failed to track screen replace event', e, s); + } + } + + @override + void didRemove(Route route, Route? previousRoute) { + super.didRemove(route, previousRoute); + + try { + // Delegate to Firebase Analytics Observer + _firebaseObserver.didRemove(route, previousRoute); + } catch (e, s) { + _logger.e('Failed to track screen remove event', e, s); + } + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/observer/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/observer/index.dart new file mode 100644 index 0000000..e4f9659 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/observer/index.dart @@ -0,0 +1 @@ +export 'analytics_route_observer.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/utils/analytics_helper.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/utils/analytics_helper.dart new file mode 100644 index 0000000..bd9ab4e --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/utils/analytics_helper.dart @@ -0,0 +1,203 @@ +import 'package:analytics/src/client/analytics_client.dart'; +import 'package:analytics/src/models/analytics_event.dart'; +import 'package:analytics/src/models/analytics_events.dart'; +import 'package:core/core.dart'; +import 'package:di/di.dart'; + +class AnalyticsHelper { + static AnalyticsClient? _client; + static Logger? _logger; + + static AnalyticsClient? get _analyticsClient { + _client ??= _getAnalyticsClient(); + return _client; + } + + static Logger get _log { + _logger ??= di.get(); + return _logger!; + } + + static AnalyticsClient? _getAnalyticsClient() { + try { + return di.get(); + } catch (e) { + _log.w('Analytics client not available: $e'); + return null; + } + } + + /// Log a custom event with optional parameters + static Future logEvent( + String name, { + Map? parameters, + }) async { + final client = _analyticsClient; + if (client == null) return; + + try { + await client.logEvent(name: name, parameters: parameters); + } catch (e, s) { + _log.e('Failed to log analytics event: $name', e, s); + } + } + + /// Log user sign in + static Future logSignIn({ + String? method, + Map? parameters, + }) async { + final client = _analyticsClient; + if (client == null) return; + + try { + await client.logLogin(loginMethod: method, parameters: parameters); + } catch (e, s) { + _log.e('Failed to log sign in event', e, s); + } + } + + /// Log user sign up + static Future logSignUp({ + String? method, + Map? parameters, + }) async { + final client = _analyticsClient; + if (client == null) return; + + try { + await client.logSignUp(signUpMethod: method, parameters: parameters); + } catch (e, s) { + _log.e('Failed to log sign up event', e, s); + } + } + + /// Set user ID for analytics + static Future setUserId(String? userId) async { + final client = _analyticsClient; + if (client == null) return; + + try { + await client.setUserId(userId); + } catch (e, s) { + _log.e('Failed to set analytics user ID', e, s); + } + } + + /// Set user property + static Future setUserProperty({ + required String name, + required String? value, + }) async { + final client = _analyticsClient; + if (client == null) return; + + try { + await client.setUserProperty(name: name, value: value); + } catch (e, s) { + _log.e('Failed to set analytics user property: $name', e, s); + } + } + + /// Log app open event + static Future logAppOpen({Map? parameters}) async { + final client = _analyticsClient; + if (client == null) return; + + try { + await client.logAppOpen(parameters: parameters); + } catch (e, s) { + _log.e('Failed to log app open event', e, s); + } + } + + /// Log custom analytics event + static Future logCustomEvent(AnalyticsEvent event) async { + final client = _analyticsClient; + if (client == null) return; + + try { + await client.logCustomEvent(event); + } catch (e, s) { + _log.e('Failed to log custom analytics event: ${event.name}', e, s); + } + } + + /// Common business events + static Future logFeatureUsed( + String featureName, { + Map? parameters, + }) async { + await logEvent( + AnalyticsEvents.feature.used, + parameters: { + 'feature_name': featureName, + 'timestamp': DateTime.now().toIso8601String(), + ...?parameters, + }, + ); + } + + static Future logButtonPressed( + String buttonName, { + String? screenName, + Map? parameters, + }) async { + await logEvent( + AnalyticsEvents.feature.buttonPressed, + parameters: { + 'button_name': buttonName, + if (screenName != null) 'screen_name': screenName, + 'timestamp': DateTime.now().toIso8601String(), + ...?parameters, + }, + ); + } + + static Future logAppError( + String errorType, { + String? errorMessage, + Map? parameters, + }) async { + await logEvent( + AnalyticsEvents.error.appError, + parameters: { + 'error_type': errorType, + if (errorMessage != null) 'error_message': errorMessage, + 'timestamp': DateTime.now().toIso8601String(), + ...?parameters, + }, + ); + } + + /// Additional structured event helpers + static Future logSurveyStarted( + String surveyId, { + Map? parameters, + }) async { + await logEvent( + AnalyticsEvents.survey.started, + parameters: { + 'survey_id': surveyId, + 'timestamp': DateTime.now().toIso8601String(), + ...?parameters, + }, + ); + } + + static Future logSurveyCompleted( + String surveyId, { + int? duration, + Map? parameters, + }) async { + await logEvent( + AnalyticsEvents.survey.completed, + parameters: { + 'survey_id': surveyId, + if (duration != null) 'duration_seconds': duration, + 'timestamp': DateTime.now().toIso8601String(), + ...?parameters, + }, + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/utils/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/utils/index.dart new file mode 100644 index 0000000..7195697 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/utils/index.dart @@ -0,0 +1 @@ +export 'analytics_helper.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/pubspec.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/pubspec.yaml new file mode 100644 index 0000000..8253dde --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/pubspec.yaml @@ -0,0 +1,24 @@ +name: analytics +description: "Event tracking for {{name.titleCase()}} behind a swappable client interface." +version: 0.1.0 +publish_to: 'none' + +environment: + sdk: ^3.8.0 + flutter: ">=3.29.0" + +dependencies: + flutter: + sdk: flutter + firebase_analytics: ^11.3.5 + core: + path: ../core + di: + path: ../di + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/test/analytics_test.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/test/analytics_test.dart new file mode 100644 index 0000000..4414841 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/test/analytics_test.dart @@ -0,0 +1,26 @@ +import 'package:analytics/analytics.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('Analytics Package', () { + test('should create AnalyticsEvent correctly', () { + const event = AnalyticsEvent( + name: 'test_event', + parameters: {'key': 'value'}, + ); + + expect(event.name, 'test_event'); + expect(event.parameters, {'key': 'value'}); + }); + + test('should create DefaultAnalyticsConfig correctly', () { + const config = DefaultAnalyticsConfig( + enableAnalytics: true, + enableDebugLogging: false, + ); + + expect(config.enableAnalytics, isTrue); + expect(config.enableDebugLogging, isFalse); + }); + }); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/README.md b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/README.md new file mode 100644 index 0000000..df673db --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/README.md @@ -0,0 +1,274 @@ +# Crashlytics + +Firebase Crashlytics integration package for {{name.titleCase()}} - providing comprehensive crash reporting and error tracking capabilities. + +[![nonstop_cli](https://img.shields.io/badge/started%20with-nonstop_cli-166C4E.svg?style=flat-square)](https://pub.dev/packages/nonstop_cli) +[![melos](https://img.shields.io/badge/maintained%20with-melos-f700ff.svg?style=flat-square)](https://github.com/invertase/melos) + +## Features ✨ + +- 🔥 **Firebase Crashlytics Integration** - Complete Firebase Crashlytics implementation +- 🚀 **Automatic Error Handling** - Catches Flutter framework errors automatically +- 👤 **User Context** - Track crashes with user information and metadata +- 🔑 **Custom Attributes** - Add custom key-value pairs for debugging +- 📊 **Custom Logging** - Log custom messages for crash analysis +- ⚙️ **Configurable** - Flexible configuration for different environments +- 🧩 **DI Integration** - Seamless integration with dependency injection +- 🛡️ **Type Safe** - Full TypeScript-style type safety with Dart + +## Installation 💻 + +Add to your `pubspec.yaml`: + +```yaml +dependencies: + crashlytics: + path: ../packages/crashlytics +``` + +## Quick Start 🚀 + +### 1. Initialize in your app + +```dart +import 'package:crashlytics/crashlytics.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + // Initialize crashlytics with default config + await crashlytics.init(); + + runApp(MyApp()); +} +``` + +### 2. Use the service + +```dart +import 'package:crashlytics/crashlytics.dart'; +import 'package:di/di.dart'; + +class MyWidget extends StatefulWidget { + @override + _MyWidgetState createState() => _MyWidgetState(); +} + +class _MyWidgetState extends State { + late final CrashlyticsService _crashlytics; + + @override + void initState() { + super.initState(); + _crashlytics = di.get(); + } + + void _reportError() async { + try { + // Some risky operation + throw Exception('Something went wrong!'); + } catch (error, stackTrace) { + // Report the error + await _crashlytics.reportError( + error, + stackTrace: stackTrace, + context: {'feature': 'user_action', 'screen': 'home'}, + ); + } + } +} +``` + +## Configuration ⚙️ + +### Basic Configuration + +```dart +import 'package:crashlytics/crashlytics.dart'; + +await crashlytics.init( + config: CrashlyticsConfig( + enableInDebugMode: false, + enableAutomaticDataCollection: true, + enableCustomLogs: true, + logBufferSize: 100, + enableUserMetadata: true, + customKeys: { + 'app_version': '1.0.0', + 'environment': 'production', + }, + ), +); +``` + +### Advanced Configuration + +```dart +// Custom configuration for different environments +final config = kDebugMode + ? CrashlyticsConfig( + enableInDebugMode: true, + enableAutomaticDataCollection: false, + customKeys: {'environment': 'debug'}, + ) + : CrashlyticsConfig( + enableAutomaticDataCollection: true, + logBufferSize: 200, + customKeys: {'environment': 'production'}, + ); + +await crashlytics.init(config: config); +``` + +## Usage Examples 📝 + +### Error Reporting + +```dart +final crashlytics = di.get(); + +// Report a non-fatal error +await crashlytics.reportError( + Exception('Network timeout'), + stackTrace: StackTrace.current, + context: {'endpoint': '/api/users', 'timeout': '30s'}, +); + +// Report a fatal error +await crashlytics.reportError( + error, + stackTrace: stackTrace, + fatal: true, + userId: 'user123', +); +``` + +### User Context + +```dart +// Set user information +await crashlytics.setUser( + userId: 'user123', + email: 'user@example.com', + name: 'John Doe', + customAttributes: { + 'subscription': 'premium', + 'last_login': DateTime.now().toIso8601String(), + }, +); + +// Clear user information (e.g., on logout) +await crashlytics.clearUser(); +``` + +### Custom Logging + +```dart +// Log custom messages +await crashlytics.logMessage('User completed onboarding'); +await crashlytics.logMessage('API call started: /users/profile'); + +// Set custom attributes +await crashlytics.setCustomAttributes({ + 'feature_flag_a': true, + 'api_version': 'v2', + 'device_type': 'mobile', +}); + +// Set individual custom attribute +await crashlytics.setCustomAttribute('current_screen', 'profile'); +``` + +### Manual Crash Reporting + +```dart +// Use the client directly for advanced scenarios +final client = di.get(); + +await client.recordError( + CustomException('Database connection failed'), + StackTrace.current, + fatal: false, + information: ['Database host: db.example.com', 'Connection timeout: 30s'], +); +``` + +## API Reference 📚 + +### CrashlyticsService + +Main service class for crash reporting: + +| Method | Description | +|--------|-------------| +| `reportError()` | Report an error or exception | +| `reportFlutterError()` | Report Flutter framework errors | +| `logMessage()` | Log custom messages | +| `setUser()` | Set user information | +| `clearUser()` | Clear user information | +| `setCustomAttributes()` | Set multiple custom attributes | +| `setCustomAttribute()` | Set single custom attribute | +| `setCrashReportingEnabled()` | Enable/disable crash reporting | +| `sendPendingReports()` | Send unsent crash reports | +| `deletePendingReports()` | Delete unsent crash reports | + +### CrashlyticsConfig + +Configuration options: + +| Property | Default | Description | +|----------|---------|-------------| +| `enableInDebugMode` | `false` | Enable crashlytics in debug mode | +| `enableAutomaticDataCollection` | `true` | Enable automatic error collection | +| `enableCustomLogs` | `true` | Enable custom logging | +| `logBufferSize` | `100` | Maximum log buffer size | +| `enableUserMetadata` | `true` | Enable user metadata collection | +| `customKeys` | `{}` | Default custom keys | + +## Integration with DI 🔧 + +The package integrates seamlessly with the DI container: + +```dart +// Get services from DI +final crashlytics = di.get(); +final client = di.get(); +final config = di.get(); + +// Services are automatically disposed when DI container is disposed +``` + +## Testing 🧪 + +```dart +// Update test file +import 'package:flutter_test/flutter_test.dart'; +import 'package:crashlytics/crashlytics.dart'; + +void main() { + group('Crashlytics', () { + test('can be initialized', () async { + // Test initialization + expect(() => crashlytics.init(), isNot(throwsException)); + }); + }); +} +``` + +## Best Practices 💡 + +1. **Initialize Early**: Initialize crashlytics as early as possible in your app +2. **Environment Configuration**: Use different configs for debug/release builds +3. **User Privacy**: Be mindful of user privacy when setting user metadata +4. **Context is Key**: Always provide context when reporting errors +5. **Test in Debug**: Enable debug mode during development to test integration +6. **Monitor Usage**: Regularly check Firebase console for crash reports + +## Requirements 📋 + +- Flutter SDK >=3.19.0 +- Dart SDK ^3.3.0 +- Firebase project with Crashlytics enabled +- `core` and `di` packages from {{name.titleCase()}} monorepo + +[flutter_install_link]: https://docs.flutter.dev/get-started/install \ No newline at end of file diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/crashlytics.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/crashlytics.dart new file mode 100644 index 0000000..ca23fce --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/crashlytics.dart @@ -0,0 +1,56 @@ +library; + +import 'dart:async'; + +import 'package:core/core.dart'; +import 'package:crashlytics/src/client/index.dart'; +import 'package:crashlytics/src/config/index.dart'; +import 'package:di/di.dart'; + +export 'src/client/index.dart'; +export 'src/config/index.dart'; +export 'src/models/index.dart'; + +/// Initialize the crashlytics module with dependency injection +Future init({CrashlyticsConfig? config}) async { + await registerCrashlyticsWithDI(config ?? const DefaultCrashlyticsConfig()); + final logger = di.get(); + logger.i('🔥 Crashlytics module initialized'); +} + +/// Register crashlytics components with DI container +Future registerCrashlyticsWithDI(CrashlyticsConfig config) async { + final logger = di.get(); + + try { + // Register config + di.register(config); + + // Create and register crashlytics client + final client = FirebaseCrashlyticsClient(config: config, logger: logger); + di.register( + client, + dispose: (client) => client.dispose(), + ); + + // Initialize the client + await client.initialize(); + + // Check if there are any unsent reports + unawaited( + client.checkForUnsentReports().then((hasReports) { + if (hasReports) { + logger.w('⚠️ There are unsent crash reports.'); + unawaited(client.sendUnsentReports()); + } else { + logger.i('✅ No unsent crash reports found.'); + } + }), + ); + + logger.i('🔥 Crashlytics registered with DI container'); + } catch (e, stackTrace) { + logger.e('❌ Failed to register crashlytics with DI', e, stackTrace); + rethrow; + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/client/crashlytics_client.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/client/crashlytics_client.dart new file mode 100644 index 0000000..717d855 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/client/crashlytics_client.dart @@ -0,0 +1,54 @@ +import 'package:crashlytics/src/models/user_metadata.dart'; + +abstract class CrashlyticsClient { + /// Initialize the crashlytics client + Future initialize(); + + /// Record a fatal error/exception + Future recordError( + dynamic exception, + StackTrace? stackTrace, { + bool fatal = false, + Iterable information = const [], + }); + + /// Record a non-fatal error with custom context + Future recordFlutterFatalError( + dynamic exception, + StackTrace stackTrace, { + Map? context, + }); + + /// Log a custom message for debugging + Future log(String message); + + /// Set user identifier for crash reports + Future setUserIdentifier(String identifier); + + /// Set user metadata + Future setUserMetadata(UserMetadata metadata); + + /// Set custom key-value pairs for debugging + Future setCustomKey(String key, dynamic value); + + /// Set custom keys from a map + Future setCustomKeys(Map keys); + + /// Check if crashlytics collection is enabled + bool get isCrashlyticsCollectionEnabled; + + /// Enable or disable crashlytics collection + Future setCrashlyticsCollectionEnabled(bool enabled); + + /// Send any unsent crash reports + Future sendUnsentReports(); + + /// Delete any unsent crash reports + Future deleteUnsentReports(); + + /// Check if there are any unsent crash reports + Future checkForUnsentReports(); + + /// Dispose of resources + void dispose(); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/client/firebase_crashlytics_client.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/client/firebase_crashlytics_client.dart new file mode 100644 index 0000000..5317dba --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/client/firebase_crashlytics_client.dart @@ -0,0 +1,286 @@ +import 'dart:async'; +import 'dart:collection'; + +import 'package:core/core.dart'; +import 'package:crashlytics/src/client/crashlytics_client.dart'; +import 'package:crashlytics/src/config/crashlytics_config.dart'; +import 'package:crashlytics/src/models/user_metadata.dart'; +import 'package:di/di.dart'; +import 'package:firebase_crashlytics/firebase_crashlytics.dart'; +import 'package:flutter/foundation.dart'; + +class FirebaseCrashlyticsClient implements CrashlyticsClient { + FirebaseCrashlyticsClient({required this.config, Logger? logger}) + : _logger = logger ?? di.get(); + + final CrashlyticsConfig config; + final Logger _logger; + late final FirebaseCrashlytics _crashlytics; + final Queue _logBuffer = Queue(); + bool _isInitialized = false; + + @override + Future initialize() async { + try { + _crashlytics = FirebaseCrashlytics.instance; + + // Set crashlytics collection enabled based on config + await _crashlytics.setCrashlyticsCollectionEnabled( + config.enableInDebugMode || kReleaseMode, + ); + + // Set up automatic Flutter error handling + if (config.enableAutomaticDataCollection) { + FlutterError.onError = (errorDetails) { + _crashlytics.recordFlutterFatalError(errorDetails); + }; + + // Catch errors from the Flutter framework that are not handled by FlutterError + PlatformDispatcher.instance.onError = (error, stack) { + _crashlytics.recordError(error, stack, fatal: true); + return true; + }; + } + + // Set default custom keys + if (config.customKeys.isNotEmpty) { + await setCustomKeys(config.customKeys); + } + + _isInitialized = true; + _logger.i('🔥 Firebase Crashlytics initialized successfully'); + } catch (e, stackTrace) { + _logger.e('❌ Failed to initialize Firebase Crashlytics: $e', stackTrace); + rethrow; + } + } + + @override + Future recordError( + dynamic exception, + StackTrace? stackTrace, { + bool fatal = false, + Iterable information = const [], + }) async { + if (!_isInitialized) { + _logger.w('⚠️ Crashlytics not initialized, skipping error recording'); + return; + } + + try { + await _crashlytics.recordError( + exception, + stackTrace, + fatal: fatal, + information: information, + ); + + _logger.d( + '📝 Recorded ${fatal ? 'fatal' : 'non-fatal'} error: $exception', + ); + } catch (e) { + _logger.e('❌ Failed to record error: $e'); + } + } + + @override + Future recordFlutterFatalError( + dynamic exception, + StackTrace stackTrace, { + Map? context, + }) async { + if (!_isInitialized) { + _logger.w( + '⚠️ Crashlytics not initialized, skipping Flutter fatal error recording', + ); + return; + } + + try { + // Set context as custom keys if provided + if (context != null && context.isNotEmpty) { + await setCustomKeys(context); + } + + await _crashlytics.recordError(exception, stackTrace, fatal: true); + + _logger.d('📝 Recorded Flutter fatal error: $exception'); + } catch (e) { + _logger.e('❌ Failed to record Flutter fatal error: $e'); + } + } + + @override + Future log(String message) async { + if (!_isInitialized || !config.enableCustomLogs) { + return; + } + + try { + await _crashlytics.log(message); + + // Add to local buffer for debugging + _logBuffer.add('[${DateTime.now().toIso8601String()}] $message'); + + // Maintain buffer size + while (_logBuffer.length > config.logBufferSize) { + _logBuffer.removeFirst(); + } + + _logger.d('📊 Logged message: $message'); + } catch (e) { + _logger.e('❌ Failed to log message: $e'); + } + } + + @override + Future setUserIdentifier(String identifier) async { + if (!_isInitialized || !config.enableUserMetadata) { + return; + } + + try { + await _crashlytics.setUserIdentifier(identifier); + _logger.d('👤 Set user identifier: $identifier'); + } catch (e) { + _logger.e('❌ Failed to set user identifier: $e'); + } + } + + @override + Future setUserMetadata(UserMetadata metadata) async { + if (!_isInitialized || !config.enableUserMetadata) { + return; + } + + try { + if (metadata.userId != null) { + await setUserIdentifier(metadata.userId!); + } + + // Set custom attributes + final allAttributes = { + if (metadata.email != null) 'email': metadata.email!, + if (metadata.name != null) 'name': metadata.name!, + ...metadata.customAttributes, + }; + + await setCustomKeys(allAttributes); + _logger.d('👤 Set user metadata: $metadata'); + } catch (e) { + _logger.e('❌ Failed to set user metadata: $e'); + } + } + + @override + Future setCustomKey(String key, dynamic value) async { + if (!_isInitialized) { + return; + } + + try { + await _crashlytics.setCustomKey(key, value); + _logger.d('🔑 Set custom key: $key = $value'); + } catch (e) { + _logger.e('❌ Failed to set custom key: $e'); + } + } + + @override + Future setCustomKeys(Map keys) async { + if (!_isInitialized || keys.isEmpty) { + return; + } + + try { + for (final entry in keys.entries) { + await _crashlytics.setCustomKey(entry.key, entry.value); + } + _logger.d('🔑 Set ${keys.length} custom keys'); + } catch (e) { + _logger.e('❌ Failed to set custom keys: $e'); + } + } + + @override + bool get isCrashlyticsCollectionEnabled { + if (!_isInitialized) return false; + return _crashlytics.isCrashlyticsCollectionEnabled; + } + + @override + Future setCrashlyticsCollectionEnabled(bool enabled) async { + if (!_isInitialized) { + return; + } + + try { + await _crashlytics.setCrashlyticsCollectionEnabled(enabled); + _logger.d( + '⚙️ Crashlytics collection ${enabled ? 'enabled' : 'disabled'}', + ); + } catch (e) { + _logger.e('❌ Failed to set crashlytics collection enabled: $e'); + } + } + + @override + Future sendUnsentReports() async { + if (!_isInitialized) { + return; + } + + try { + await _crashlytics.sendUnsentReports(); + _logger.d('📤 Sent unsent crash reports'); + } catch (e) { + _logger.e('❌ Failed to send unsent reports: $e'); + } + } + + @override + Future deleteUnsentReports() async { + if (!_isInitialized) { + return; + } + + try { + await _crashlytics.deleteUnsentReports(); + _logger.d('🗑️ Deleted unsent crash reports'); + } catch (e) { + _logger.e('❌ Failed to delete unsent reports: $e'); + } + } + + @override + Future checkForUnsentReports() async { + if (!_isInitialized) { + return false; + } + + try { + final hasUnsent = await _crashlytics.checkForUnsentReports(); + _logger.d('📋 Has unsent reports: $hasUnsent'); + return hasUnsent; + } catch (e) { + _logger.e('❌ Failed to check for unsent reports: $e'); + return false; + } + } + + /// Get buffered log messages for debugging + List get bufferedLogs => _logBuffer.toList(); + + /// Clear the log buffer + void clearLogBuffer() { + _logBuffer.clear(); + _logger.d('🧹 Cleared log buffer'); + } + + @override + void dispose() { + _logBuffer.clear(); + _isInitialized = false; + _logger.d('🧹 Firebase Crashlytics client disposed'); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/client/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/client/index.dart new file mode 100644 index 0000000..5d20960 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/client/index.dart @@ -0,0 +1,2 @@ +export 'crashlytics_client.dart'; +export 'firebase_crashlytics_client.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/config/crashlytics_config.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/config/crashlytics_config.dart new file mode 100644 index 0000000..5a52e29 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/config/crashlytics_config.dart @@ -0,0 +1,104 @@ +class CrashlyticsConfig { + const CrashlyticsConfig({ + this.enableInDebugMode = false, + this.enableAutomaticDataCollection = true, + this.enableCustomLogs = true, + this.logBufferSize = 100, + this.enableUserMetadata = true, + this.customKeys = const {}, + }); + + /// Enable crashlytics in debug mode (typically disabled for development) + final bool enableInDebugMode; + + /// Enable automatic data collection + final bool enableAutomaticDataCollection; + + /// Enable custom logging + final bool enableCustomLogs; + + /// Maximum number of log messages to buffer + final int logBufferSize; + + /// Enable user metadata collection + final bool enableUserMetadata; + + /// Default custom keys to set on initialization + final Map customKeys; + + CrashlyticsConfig copyWith({ + bool? enableInDebugMode, + bool? enableAutomaticDataCollection, + bool? enableCustomLogs, + int? logBufferSize, + bool? enableUserMetadata, + Map? customKeys, + }) { + return CrashlyticsConfig( + enableInDebugMode: enableInDebugMode ?? this.enableInDebugMode, + enableAutomaticDataCollection: + enableAutomaticDataCollection ?? this.enableAutomaticDataCollection, + enableCustomLogs: enableCustomLogs ?? this.enableCustomLogs, + logBufferSize: logBufferSize ?? this.logBufferSize, + enableUserMetadata: enableUserMetadata ?? this.enableUserMetadata, + customKeys: customKeys ?? this.customKeys, + ); + } + + Map toMap() { + return { + 'enableInDebugMode': enableInDebugMode, + 'enableAutomaticDataCollection': enableAutomaticDataCollection, + 'enableCustomLogs': enableCustomLogs, + 'logBufferSize': logBufferSize, + 'enableUserMetadata': enableUserMetadata, + 'customKeys': customKeys, + }; + } + + @override + String toString() { + return 'CrashlyticsConfig{enableInDebugMode: $enableInDebugMode, enableAutomaticDataCollection: $enableAutomaticDataCollection, enableCustomLogs: $enableCustomLogs, logBufferSize: $logBufferSize, enableUserMetadata: $enableUserMetadata, customKeys: $customKeys}'; + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is CrashlyticsConfig && + runtimeType == other.runtimeType && + enableInDebugMode == other.enableInDebugMode && + enableAutomaticDataCollection == + other.enableAutomaticDataCollection && + enableCustomLogs == other.enableCustomLogs && + logBufferSize == other.logBufferSize && + enableUserMetadata == other.enableUserMetadata && + _mapEquals(customKeys, other.customKeys); + + @override + int get hashCode => + enableInDebugMode.hashCode ^ + enableAutomaticDataCollection.hashCode ^ + enableCustomLogs.hashCode ^ + logBufferSize.hashCode ^ + enableUserMetadata.hashCode ^ + customKeys.hashCode; + + bool _mapEquals(Map a, Map b) { + if (a.length != b.length) return false; + for (final key in a.keys) { + if (!b.containsKey(key) || a[key] != b[key]) return false; + } + return true; + } +} + +class DefaultCrashlyticsConfig extends CrashlyticsConfig { + const DefaultCrashlyticsConfig({ + super.enableInDebugMode = false, + super.enableAutomaticDataCollection = true, + super.enableCustomLogs = true, + super.logBufferSize = 100, + super.enableUserMetadata = true, + super.customKeys = const {}, + }); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/config/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/config/index.dart new file mode 100644 index 0000000..2caf5ad --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/config/index.dart @@ -0,0 +1 @@ +export 'crashlytics_config.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/models/crash_report.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/models/crash_report.dart new file mode 100644 index 0000000..e86c428 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/models/crash_report.dart @@ -0,0 +1,73 @@ +class CrashReport { + const CrashReport({ + required this.exception, + this.stackTrace, + this.fatal = false, + this.timestamp, + this.customKeys = const {}, + this.logs = const [], + this.userMetadata, + }); + + final dynamic exception; + final StackTrace? stackTrace; + final bool fatal; + final DateTime? timestamp; + final Map customKeys; + final List logs; + final Map? userMetadata; + + CrashReport copyWith({ + dynamic exception, + StackTrace? stackTrace, + bool? fatal, + DateTime? timestamp, + Map? customKeys, + List? logs, + Map? userMetadata, + }) { + return CrashReport( + exception: exception ?? this.exception, + stackTrace: stackTrace ?? this.stackTrace, + fatal: fatal ?? this.fatal, + timestamp: timestamp ?? this.timestamp, + customKeys: customKeys ?? this.customKeys, + logs: logs ?? this.logs, + userMetadata: userMetadata ?? this.userMetadata, + ); + } + + Map toMap() { + return { + 'exception': exception.toString(), + 'stackTrace': stackTrace?.toString(), + 'fatal': fatal, + 'timestamp': timestamp?.toIso8601String(), + 'customKeys': customKeys, + 'logs': logs, + 'userMetadata': userMetadata, + }; + } + + @override + String toString() { + return 'CrashReport{exception: $exception, fatal: $fatal, timestamp: $timestamp}'; + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is CrashReport && + runtimeType == other.runtimeType && + exception.toString() == other.exception.toString() && + stackTrace.toString() == other.stackTrace.toString() && + fatal == other.fatal && + timestamp == other.timestamp; + + @override + int get hashCode => + exception.hashCode ^ + stackTrace.hashCode ^ + fatal.hashCode ^ + timestamp.hashCode; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/models/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/models/index.dart new file mode 100644 index 0000000..8a9f2b6 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/models/index.dart @@ -0,0 +1,2 @@ +export 'crash_report.dart'; +export 'user_metadata.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/models/user_metadata.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/models/user_metadata.dart new file mode 100644 index 0000000..fc8633c --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/models/user_metadata.dart @@ -0,0 +1,66 @@ +class UserMetadata { + const UserMetadata({ + this.userId, + this.email, + this.name, + this.customAttributes = const {}, + }); + + final String? userId; + final String? email; + final String? name; + final Map customAttributes; + + UserMetadata copyWith({ + String? userId, + String? email, + String? name, + Map? customAttributes, + }) { + return UserMetadata( + userId: userId ?? this.userId, + email: email ?? this.email, + name: name ?? this.name, + customAttributes: customAttributes ?? this.customAttributes, + ); + } + + Map toMap() { + return { + 'userId': userId, + 'email': email, + 'name': name, + 'customAttributes': customAttributes, + }; + } + + @override + String toString() { + return 'UserMetadata{userId: $userId, email: $email, name: $name, customAttributes: $customAttributes}'; + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is UserMetadata && + runtimeType == other.runtimeType && + userId == other.userId && + email == other.email && + name == other.name && + _mapEquals(customAttributes, other.customAttributes); + + @override + int get hashCode => + userId.hashCode ^ + email.hashCode ^ + name.hashCode ^ + customAttributes.hashCode; + + bool _mapEquals(Map a, Map b) { + if (a.length != b.length) return false; + for (final key in a.keys) { + if (!b.containsKey(key) || a[key] != b[key]) return false; + } + return true; + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/pubspec.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/pubspec.yaml new file mode 100644 index 0000000..7a0ae42 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/pubspec.yaml @@ -0,0 +1,25 @@ +name: crashlytics +description: "Crash and non-fatal error reporting for {{name.titleCase()}}." +version: 0.1.0 +publish_to: 'none' + +environment: + sdk: ^3.8.0 + flutter: ">=3.29.0" + +dependencies: + flutter: + sdk: flutter + firebase_core: ^3.14.0 + firebase_crashlytics: ^4.1.4 + core: + path: ../core + di: + path: ../di + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/test/crashlytics_test.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/test/crashlytics_test.dart new file mode 100644 index 0000000..a0cad7d --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/test/crashlytics_test.dart @@ -0,0 +1,133 @@ +import 'package:crashlytics/crashlytics.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('Crashlytics', () { + group('CrashlyticsConfig', () { + test('creates default config correctly', () { + const config = DefaultCrashlyticsConfig(); + + expect(config.enableInDebugMode, false); + expect(config.enableAutomaticDataCollection, true); + expect(config.enableCustomLogs, true); + expect(config.logBufferSize, 100); + expect(config.enableUserMetadata, true); + expect(config.customKeys, isEmpty); + }); + + test('creates custom config correctly', () { + const config = CrashlyticsConfig( + enableInDebugMode: true, + enableAutomaticDataCollection: false, + logBufferSize: 50, + customKeys: {'test': 'value'}, + ); + + expect(config.enableInDebugMode, true); + expect(config.enableAutomaticDataCollection, false); + expect(config.logBufferSize, 50); + expect(config.customKeys, {'test': 'value'}); + }); + + test('copyWith works correctly', () { + const config = CrashlyticsConfig(); + final updated = config.copyWith(enableInDebugMode: true); + + expect(updated.enableInDebugMode, true); + expect( + updated.enableAutomaticDataCollection, + config.enableAutomaticDataCollection, + ); + }); + }); + + group('UserMetadata', () { + test('creates user metadata correctly', () { + const metadata = UserMetadata( + userId: 'user123', + email: 'test@example.com', + name: 'Test User', + customAttributes: {'role': 'admin'}, + ); + + expect(metadata.userId, 'user123'); + expect(metadata.email, 'test@example.com'); + expect(metadata.name, 'Test User'); + expect(metadata.customAttributes, {'role': 'admin'}); + }); + + test('toMap works correctly', () { + const metadata = UserMetadata( + userId: 'user123', + email: 'test@example.com', + ); + + final map = metadata.toMap(); + expect(map['userId'], 'user123'); + expect(map['email'], 'test@example.com'); + expect(map['name'], null); + expect(map['customAttributes'], isEmpty); + }); + + test('copyWith works correctly', () { + const metadata = UserMetadata(userId: 'user123'); + final updated = metadata.copyWith(email: 'new@example.com'); + + expect(updated.userId, 'user123'); + expect(updated.email, 'new@example.com'); + expect(updated.name, null); + }); + + test('equality works correctly', () { + const metadata1 = UserMetadata(userId: 'user123'); + const metadata2 = UserMetadata(userId: 'user123'); + const metadata3 = UserMetadata(userId: 'user456'); + + expect(metadata1, equals(metadata2)); + expect(metadata1, isNot(equals(metadata3))); + }); + }); + + group('CrashReport', () { + test('creates crash report correctly', () { + final timestamp = DateTime.now(); + final report = CrashReport( + exception: Exception('Test error'), + stackTrace: StackTrace.current, + fatal: true, + timestamp: timestamp, + customKeys: {'key': 'value'}, + ); + + expect(report.exception.toString(), contains('Test error')); + expect(report.fatal, true); + expect(report.timestamp, timestamp); + expect(report.customKeys, {'key': 'value'}); + }); + + test('toMap works correctly', () { + final report = CrashReport( + exception: Exception('Test error'), + fatal: false, + customKeys: {'test': 'data'}, + ); + + final map = report.toMap(); + expect(map['fatal'], false); + expect(map['customKeys'], {'test': 'data'}); + expect(map['exception'], contains('Test error')); + }); + + test('copyWith works correctly', () { + final report = CrashReport( + exception: Exception('Test error'), + fatal: false, + ); + + final updated = report.copyWith(fatal: true); + expect(updated.fatal, true); + expect(updated.exception.toString(), report.exception.toString()); + }); + }); + }); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/developer.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/developer.dart new file mode 100644 index 0000000..546a189 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/developer.dart @@ -0,0 +1,3 @@ +export 'src/routes/index.dart'; +export 'src/screens/index.dart'; +export 'src/widgets/index.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/routes/developer_routes.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/routes/developer_routes.dart new file mode 100644 index 0000000..2992c41 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/routes/developer_routes.dart @@ -0,0 +1,21 @@ +import 'package:core/core.dart' as core; +import 'package:developer/src/screens/index.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +class DeveloperRoutes { + static const String developer = '/developer'; +} + +class DeveloperRouter extends core.CoreRouter { + @override + List get routes => [ + GoRoute( + path: DeveloperRoutes.developer, + name: 'developer-screen', + builder: (BuildContext context, GoRouterState state) { + return const DeveloperScreen(); + }, + ), + ]; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/routes/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/routes/index.dart new file mode 100644 index 0000000..34e52a8 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/routes/index.dart @@ -0,0 +1 @@ +export 'developer_routes.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/screens/developer_screen.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/screens/developer_screen.dart new file mode 100644 index 0000000..9c4cba5 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/screens/developer_screen.dart @@ -0,0 +1,13 @@ +import 'package:core/core.dart'; +import 'package:di/di.dart'; +import 'package:flutter/material.dart'; +import 'package:talker_flutter/talker_flutter.dart'; + +class DeveloperScreen extends StatelessWidget { + const DeveloperScreen({super.key}); + + @override + Widget build(BuildContext context) { + return TalkerScreen(talker: di.get().logger as Talker); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/screens/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/screens/index.dart new file mode 100644 index 0000000..1f2dffb --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/screens/index.dart @@ -0,0 +1 @@ +export 'developer_screen.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/widgets/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/widgets/index.dart new file mode 100644 index 0000000..dfcb372 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/widgets/index.dart @@ -0,0 +1 @@ +export 'open_dev_tools_wrapper.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/widgets/open_dev_tools_wrapper.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/widgets/open_dev_tools_wrapper.dart new file mode 100644 index 0000000..f65abee --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/widgets/open_dev_tools_wrapper.dart @@ -0,0 +1,187 @@ +import 'dart:async'; + +import 'package:developer/src/routes/index.dart'; +import 'package:feature_flags/feature_flags.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:go_router/go_router.dart'; + +class OpenDevToolsWrapper extends StatefulWidget { + const OpenDevToolsWrapper({ + super.key, + required this.child, + this.tapCount = 5, + this.tapWindow = const Duration(seconds: 2), + this.enableHaptics = true, + this.enableVisualFeedback = false, + }); + + final Widget child; + final int tapCount; + final Duration tapWindow; + final bool enableHaptics; + final bool enableVisualFeedback; + + @override + State createState() => _OpenDevToolsWrapperState(); +} + +class _OpenDevToolsWrapperState extends State + with SingleTickerProviderStateMixin { + int _currentTapCount = 0; + Timer? _resetTimer; + late AnimationController _animationController; + late Animation _scaleAnimation; + bool _isAnimating = false; + + @override + void initState() { + super.initState(); + _animationController = AnimationController( + duration: const Duration(milliseconds: 200), + vsync: this, + ); + _scaleAnimation = Tween(begin: 1.0, end: 0.95).animate( + CurvedAnimation(parent: _animationController, curve: Curves.easeInOut), + ); + } + + @override + void dispose() { + _resetTimer?.cancel(); + _animationController.dispose(); + super.dispose(); + } + + void _handleTap() { + setState(() { + _currentTapCount++; + }); + + // Provide haptic feedback for progress + if (widget.enableHaptics) { + HapticFeedback.selectionClick(); + } + + // Visual feedback animation + if (widget.enableVisualFeedback && !_isAnimating) { + _isAnimating = true; + _animationController.forward().then((_) { + _animationController.reverse().then((_) { + if (mounted) { + setState(() { + _isAnimating = false; + }); + } + }); + }); + } + + // Check if we've reached the required tap count + if (_currentTapCount >= widget.tapCount) { + _navigateToDeveloperScreen(); + return; + } + + // Reset the timer + _resetTimer?.cancel(); + _resetTimer = Timer(widget.tapWindow, _resetTapCount); + } + + void _navigateToDeveloperScreen() { + // Provide success haptic feedback + if (widget.enableHaptics) { + HapticFeedback.mediumImpact(); + } + + // Reset state + _resetTapCount(); + + // Navigate to developer screen + if (mounted) { + context.push(DeveloperRoutes.developer); + } + } + + void _resetTapCount() { + _resetTimer?.cancel(); + if (mounted) { + setState(() { + _currentTapCount = 0; + }); + } + } + + Widget _buildChild() { + if (widget.enableVisualFeedback) { + return AnimatedBuilder( + animation: _scaleAnimation, + builder: (context, child) { + return Transform.scale( + scale: _scaleAnimation.value, + child: widget.child, + ); + }, + ); + } + return widget.child; + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return FeatureFlagWrapper( + flagKey: 'developer_screen_enabled', + builder: (context, isEnabled) { + if (!isEnabled) { + return widget.child; // Just return the child if feature is disabled + } + + return GestureDetector( + onTap: _handleTap, + behavior: HitTestBehavior.translucent, + child: Stack( + children: [ + _buildChild(), + // Optional debug indicator (only visible in debug and with visual feedback enabled) + if (widget.enableVisualFeedback && _currentTapCount > 0) ...[ + Positioned.fill( + child: IgnorePointer( + child: Container( + decoration: BoxDecoration( + border: Border.all( + color: theme.colorScheme.error.withValues(alpha: 0.3), + width: 2, + ), + ), + child: Center( + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 4, + ), + decoration: BoxDecoration( + color: Colors.red.withValues(alpha: 0.8), + borderRadius: BorderRadius.circular(4), + ), + child: Text( + '$_currentTapCount/${widget.tapCount}', + style: theme.textTheme.labelSmall?.copyWith( + color: theme.colorScheme.onError, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + ), + ], + ], + ), + ); + }, + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/pubspec.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/pubspec.yaml new file mode 100644 index 0000000..f2f8c42 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/pubspec.yaml @@ -0,0 +1,29 @@ +name: developer +description: "In-app developer tools for {{name.titleCase()}}: logs, flags and environment inspection." +version: 0.1.0 +publish_to: 'none' + +environment: + sdk: ^3.8.0 + flutter: ">=3.29.0" + +dependencies: + flutter: + sdk: flutter + go_router: ^15.1.3 + talker_flutter: ^4.4.1 + core: + path: ../core + design_system: + path: ../design_system + di: + path: ../di + feature_flags: + path: ../feature_flags + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/test/developer_test.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/test/developer_test.dart new file mode 100644 index 0000000..f8dde7b --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{developer_dir}}/test/developer_test.dart @@ -0,0 +1,38 @@ +import 'package:developer/developer.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('Developer Package', () { + test('package exports are available', () { + expect(DeveloperRoutes.developer, '/developer'); + }); + + testWidgets('DebugGestureDetector exports are available', (tester) async { + // Test that the widget can be instantiated + const widget = OpenDevToolsWrapper(child: Text('Test')); + + expect(widget, isNotNull); + expect(widget.tapCount, 5); // Default value + expect(widget.tapWindow, const Duration(seconds: 2)); // Default value + expect(widget.enableHaptics, true); // Default value + expect(widget.enableVisualFeedback, false); // Default value + }); + + testWidgets('DebugGestureDetector custom values work', (tester) async { + // Test custom configuration + const widget = OpenDevToolsWrapper( + tapCount: 7, + tapWindow: Duration(seconds: 2), + enableHaptics: false, + enableVisualFeedback: true, + child: Text('Test'), + ); + + expect(widget.tapCount, 7); + expect(widget.tapWindow, const Duration(seconds: 2)); + expect(widget.enableHaptics, false); + expect(widget.enableVisualFeedback, true); + }); + }); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/README.md b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/README.md new file mode 100644 index 0000000..eb82669 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/README.md @@ -0,0 +1,18 @@ +# Feature Flags + +Feature flags and remote configuration management for {{name.titleCase()}} + +[![nonstop_cli](https://img.shields.io/badge/started%20with-nonstop_cli-166C4E.svg?style=flat-square)](https://pub.dev/packages/nonstop_cli) +[![melos](https://img.shields.io/badge/maintained%20with-melos-f700ff.svg?style=flat-square)](https://github.com/invertase/melos) + +## Installation 💻 + +**❗ In order to start using Feature Flags you must have the [Flutter SDK][flutter_install_link] installed on your machine.** + +Install via `dart pub add`: + +```sh +dart pub add feature_flags +``` + +[flutter_install_link]: https://docs.flutter.dev/get-started/install \ No newline at end of file diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/feature_flags.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/feature_flags.dart new file mode 100644 index 0000000..e683947 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/feature_flags.dart @@ -0,0 +1,29 @@ +library; + +import 'package:core/logger/logger.dart'; +import 'package:di/di.dart'; +import 'package:feature_flags/src/feature_flag_service.dart'; +import 'package:feature_flags/src/register.dart'; + +export 'src/index.dart'; + +/// Initialize the feature flags module and register with DI +Future init({ + FeatureFlagsConfig config = const FeatureFlagsConfig( + fetchTimeout: Duration(seconds: 10), + minimumFetchInterval: Duration(minutes: 30), + defaultParameters: {}, + ), +}) async { + final logger = di.get(); + try { + logger.i('init feature flags module with Firebase Remote Config'); + await registerFeatureFlagsWithDI(config: config); + final service = di.get(); + await service.init(); + logger.i('Feature flags module initialized with Firebase Remote Config'); + } catch (e, stackTrace) { + logger.e('Failed to initialize feature flags module', e, stackTrace); + rethrow; + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/feature_flag_provider.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/feature_flag_provider.dart new file mode 100644 index 0000000..23c1788 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/feature_flag_provider.dart @@ -0,0 +1,23 @@ +/// Abstract interface for feature flag providers +abstract class FeatureFlagProvider { + /// Initialize the feature flag provider + Future init(); + + /// Get a boolean feature flag value + Future getBool(String key, {bool defaultValue = false}); + + /// Get a string feature flag value + Future getString(String key, {String defaultValue = ''}); + + /// Get an integer feature flag value + Future getInt(String key, {int defaultValue = 0}); + + /// Get a double feature flag value + Future getDouble(String key, {double defaultValue = 0.0}); + + /// Check if a feature flag exists + Future hasFlag(String key); + + /// Dispose resources + void dispose(); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/feature_flag_service.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/feature_flag_service.dart new file mode 100644 index 0000000..400d945 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/feature_flag_service.dart @@ -0,0 +1,95 @@ +import 'package:core/logger/logger.dart'; +import 'package:feature_flags/src/feature_flag_provider.dart'; + +/// Configuration class for feature flags initialization +class FeatureFlagsConfig { + const FeatureFlagsConfig({ + this.fetchTimeout, + this.minimumFetchInterval, + required this.defaultParameters, + }); + + final Duration? fetchTimeout; + final Duration? minimumFetchInterval; + final Map defaultParameters; +} + +/// Service for managing feature flags across the application +class FeatureFlag { + final FeatureFlagProvider _provider; + final Logger _logger; + bool _initialized = false; + + FeatureFlag({required FeatureFlagProvider provider, required Logger logger}) + : _provider = provider, + _logger = logger; + + /// Initialize the feature flag service + Future init() async { + if (_initialized) { + _logger.w('FeatureFlag already initialized'); + return; + } + + try { + await _provider.init(); + _initialized = true; + _logger.i('FeatureFlag initialized successfully'); + } catch (e, stackTrace) { + _logger.e('Failed to initialize FeatureFlag', e, stackTrace); + rethrow; + } + } + + /// Check if service is initialized + bool get isInitialized => _initialized; + + /// Get a boolean feature flag + Future isEnabled(String key, {bool defaultValue = false}) async { + _ensureInitialized(); + final value = await _provider.getBool(key, defaultValue: defaultValue); + _logger.i('isEnabled($key): Provider returned = $value'); + return value; + } + + /// Get a string configuration value + Future getConfig(String key, {String defaultValue = ''}) async { + _ensureInitialized(); + + final value = await _provider.getString(key, defaultValue: defaultValue); + return value; + } + + /// Get an integer configuration value + Future getIntConfig(String key, {int defaultValue = 0}) async { + _ensureInitialized(); + + final value = await _provider.getInt(key, defaultValue: defaultValue); + return value; + } + + /// Get a double configuration value + Future getDoubleConfig( + String key, { + double defaultValue = 0.0, + }) async { + _ensureInitialized(); + + final value = await _provider.getDouble(key, defaultValue: defaultValue); + return value; + } + + /// Dispose the service + void dispose() { + _provider.dispose(); + _initialized = false; + _logger.i('FeatureFlag disposed'); + } + + /// Ensure the service is initialized + void _ensureInitialized() { + if (!_initialized) { + throw StateError('FeatureFlag not initialized. Call initialize() first.'); + } + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/feature_flag_wrapper.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/feature_flag_wrapper.dart new file mode 100644 index 0000000..56d22a6 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/feature_flag_wrapper.dart @@ -0,0 +1,56 @@ +import 'package:di/di.dart'; +import 'package:feature_flags/src/feature_flag_service.dart'; +import 'package:flutter/material.dart'; + +/// A wrapper widget that conditionally renders content based on feature flags +class FeatureFlagWrapper extends StatelessWidget { + const FeatureFlagWrapper({ + super.key, + required this.flagKey, + required this.builder, + this.defaultValue = false, + this.loading, + }); + + /// The feature flag key to check + final String flagKey; + + /// Builder function that receives the context and feature flag value + final Widget Function(BuildContext context, bool value) builder; + + /// The default value to use if the feature flag service is unavailable + final bool defaultValue; + + /// Widget to show while loading the feature flag value + final Widget? loading; + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: _checkFeatureFlag(), + builder: (context, snapshot) { + // Show loading widget while waiting for feature flag + if (snapshot.connectionState == ConnectionState.waiting) { + return loading ?? const SizedBox.shrink(); + } + + // Get the feature flag value, defaulting to false if there's an error + final isEnabled = snapshot.data ?? defaultValue; + + // Return the widget built by the builder function + return builder(context, isEnabled); + }, + ); + } + + Future _checkFeatureFlag() async { + try { + final featureFlag = di.get(); + return await featureFlag.isEnabled(flagKey, defaultValue: defaultValue); + } catch (e) { + // If there's an error accessing the feature flag service, + // return the default value + return defaultValue; + } + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/firebase_remote_config_provider.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/firebase_remote_config_provider.dart new file mode 100644 index 0000000..7116a8c --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/firebase_remote_config_provider.dart @@ -0,0 +1,144 @@ +import 'package:core/logger/logger.dart'; +import 'package:di/di.dart'; +import 'package:feature_flags/feature_flags.dart'; +import 'package:firebase_remote_config/firebase_remote_config.dart'; + +/// Firebase Remote Config implementation of FeatureFlagProvider +class FirebaseRemoteConfigProvider implements FeatureFlagProvider { + late final FirebaseRemoteConfig _remoteConfig; + final FeatureFlagsConfig _config; + late final Logger logger; + + FirebaseRemoteConfigProvider({FeatureFlagsConfig? config}) + : _config = + config ?? + const FeatureFlagsConfig( + defaultParameters: {}, + fetchTimeout: Duration(seconds: 10), + minimumFetchInterval: Duration(hours: 1), + ) { + if (di.has()) { + logger = di.get(); + } + } + + @override + Future init() async { + try { + _remoteConfig = FirebaseRemoteConfig.instance; + + // Configure settings + await _remoteConfig.setConfigSettings( + RemoteConfigSettings( + fetchTimeout: _config.fetchTimeout ?? const Duration(seconds: 10), + minimumFetchInterval: + _config.minimumFetchInterval ?? const Duration(hours: 1), + ), + ); + + // Set default values + await _setDefaults(); + + // Fetch and activate + final activated = await _remoteConfig.fetchAndActivate(); + + logger.i('Firebase Remote Config initialized successfully'); + logger.i('Remote Config activated: $activated'); + logger.i('Last fetch status: ${_remoteConfig.lastFetchStatus}'); + logger.i('Last fetch time: ${_remoteConfig.lastFetchTime}'); + + // Log all current values for debugging + final allValues = _remoteConfig.getAll(); + for (final entry in allValues.entries) { + logger.i('Remote Config - ${entry.key}: ${entry.value.asString()}'); + } + } catch (e, stackTrace) { + logger.e('Failed to initialize Firebase Remote Config', e, stackTrace); + rethrow; + } + } + + Future _setDefaults() async { + await _remoteConfig.setDefaults(_config.defaultParameters); + } + + @override + Future getBool(String key, {bool defaultValue = false}) async { + try { + final value = _remoteConfig.getBool(key); + final configValue = _remoteConfig.getValue(key); + logger.i( + 'Getting bool flag "$key": value=$value,' + ' source=${configValue.source}', + ); + return value; + } catch (e) { + logger.e( + 'Failed to get bool flag: $key, using default: $defaultValue', + e, + ); + return defaultValue; + } + } + + @override + Future getString(String key, {String defaultValue = ''}) async { + try { + final value = _remoteConfig.getString(key); + return value.isEmpty ? defaultValue : value; + } catch (e) { + logger.w('Failed to get string flag: $key, using default: $defaultValue'); + return defaultValue; + } + } + + @override + Future getInt(String key, {int defaultValue = 0}) async { + try { + return _remoteConfig.getInt(key); + } catch (e) { + logger.w('Failed to get int flag: $key, using default: $defaultValue'); + return defaultValue; + } + } + + @override + Future getDouble(String key, {double defaultValue = 0.0}) async { + try { + return _remoteConfig.getDouble(key); + } catch (e) { + logger.w('Failed to get double flag: $key, using default: $defaultValue'); + return defaultValue; + } + } + + @override + Future hasFlag(String key) async { + try { + final value = _remoteConfig.getValue(key); + return value.source != ValueSource.valueStatic; + } catch (e) { + logger.w('Failed to check if flag exists: $key'); + return false; + } + } + + @override + void dispose() { + // Firebase Remote Config doesn't require explicit disposal + logger.i('Firebase Remote Config provider disposed'); + } + + /// Get the last fetch time + DateTime get lastFetchTime => _remoteConfig.lastFetchTime; + + /// Get the last fetch status + RemoteConfigFetchStatus get lastFetchStatus => _remoteConfig.lastFetchStatus; + + /// Add a listener for config updates + Stream get onConfigUpdated => + _remoteConfig.onConfigUpdated; + + /// Get the current configuration + FeatureFlagsConfig get config => _config; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/index.dart new file mode 100644 index 0000000..dc262f4 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/index.dart @@ -0,0 +1,5 @@ +export 'feature_flag_provider.dart'; +export 'feature_flag_service.dart'; +export 'feature_flag_wrapper.dart'; +export 'firebase_remote_config_provider.dart'; +export 'register.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/register.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/register.dart new file mode 100644 index 0000000..0cc8c04 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/register.dart @@ -0,0 +1,19 @@ +import 'package:core/logger/logger.dart'; +import 'package:di/di.dart'; +import 'package:feature_flags/src/feature_flag_provider.dart'; +import 'package:feature_flags/src/feature_flag_service.dart'; +import 'package:feature_flags/src/firebase_remote_config_provider.dart'; + +/// Register feature flags services with dependency injection +/// +Future registerFeatureFlagsWithDI({FeatureFlagsConfig? config}) async { + final logger = di.get(); + + // Register the provider + final provider = FirebaseRemoteConfigProvider(config: config); + di.register(provider, dispose: (p) => p.dispose()); + + // Register the service + final service = FeatureFlag(provider: provider, logger: logger); + di.register(service, dispose: (s) => s.dispose()); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/pubspec.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/pubspec.yaml new file mode 100644 index 0000000..60d0a14 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/pubspec.yaml @@ -0,0 +1,25 @@ +name: feature_flags +description: "Remote-config backed feature flags for {{name.titleCase()}}." +version: 0.1.0 +publish_to: 'none' + +environment: + sdk: ^3.8.0 + flutter: ">=3.29.0" + +dependencies: + flutter: + sdk: flutter + firebase_core: ^3.14.0 + firebase_remote_config: ^5.1.6 + core: + path: ../core + di: + path: ../di + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/README.md b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/README.md new file mode 100644 index 0000000..f88d62b --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/README.md @@ -0,0 +1,18 @@ +# Network + +A Melos-managed project for mono-repo, created using NonStop CLI. + +[![nonstop_cli](https://img.shields.io/badge/started%20with-nonstop_cli-166C4E.svg?style=flat-square)](https://pub.dev/packages/nonstop_cli) +[![melos](https://img.shields.io/badge/maintained%20with-melos-f700ff.svg?style=flat-square)](https://github.com/invertase/melos) + +## Installation 💻 + +**❗ In order to start using Network you must have the [Flutter SDK][flutter_install_link] installed on your machine.** + +Install via `dart pub add`: + +```sh +dart pub add network +``` + +[flutter_install_link]: https://docs.flutter.dev/get-started/install \ No newline at end of file diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/network.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/network.dart new file mode 100644 index 0000000..4cabe41 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/network.dart @@ -0,0 +1,78 @@ +library; + +import 'package:core/core.dart'; +import 'package:di/di.dart'; +import 'package:network/src/auth/index.dart'; +import 'package:network/src/client/index.dart'; +import 'package:network/src/config/index.dart'; + +export 'src/api/index.dart'; +export 'src/auth/index.dart'; +export 'src/client/index.dart'; +export 'src/config/index.dart'; +export 'src/exceptions/index.dart'; +export 'src/interceptors/index.dart'; +export 'src/models/index.dart'; +export 'src/utils/index.dart'; + +Future init({ + required NetworkConfig config, + bool useAuthentication = true, +}) async { + await registerNetworkWithDI(config, useAuthentication: useAuthentication); + final logger = di.get(); + logger.i('Network module initialized with base URL: ${config.baseUrl}'); +} + +Future registerNetworkWithDI( + NetworkConfig config, { + bool useAuthentication = true, +}) async { + final logger = di.get(); + + // Try to get AuthTokenProvider from DI if authentication is enabled + AuthTokenProvider? authTokenProvider; + if (useAuthentication && config.authTokenProvider == null) { + try { + authTokenProvider = di.get(); + logger.d('🔐 Found AuthTokenProvider in DI, using for authentication'); + } catch (e) { + logger.d( + '🔓 No AuthTokenProvider found in DI, creating unauthenticated client', + ); + } + } else if (config.authTokenProvider != null) { + authTokenProvider = config.authTokenProvider; + logger.d('🔐 Using provided AuthTokenProvider'); + } + + // Create network config with auth provider if available + late final NetworkConfig finalConfig; + if (authTokenProvider != null && config.authTokenProvider == null) { + // Create a new config with the auth provider + finalConfig = DefaultNetworkConfig( + baseUrl: config.baseUrl, + connectTimeout: config.connectTimeout, + receiveTimeout: config.receiveTimeout, + sendTimeout: config.sendTimeout, + defaultHeaders: config.defaultHeaders, + enableLogging: config.enableLogging, + authTokenProvider: authTokenProvider, + ); + } else { + finalConfig = config; + } + + final networkClient = DioNetworkClient(finalConfig); + di.register( + networkClient, + dispose: (client) => client.dispose(), + ); + di.register(finalConfig); + + if (finalConfig.authTokenProvider != null) { + logger.i('🔐 Network client registered with authentication support'); + } else { + logger.i('🔓 Network client registered without authentication'); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/api/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/api/index.dart new file mode 100644 index 0000000..e3427eb --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/api/index.dart @@ -0,0 +1,2 @@ +//GENERATED BARREL FILE +export 'network_api.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/api/network_api.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/api/network_api.dart new file mode 100644 index 0000000..66a684c --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/api/network_api.dart @@ -0,0 +1,13 @@ +abstract class ApiUrlConfig { + final String pathPrefix; + + const ApiUrlConfig({required this.pathPrefix}); +} + +abstract class NetworkApi { + final T urlConfig; + + const NetworkApi({required this.urlConfig}); + + Map get headers; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/auth/auth_token_provider.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/auth/auth_token_provider.dart new file mode 100644 index 0000000..eb3e08c --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/auth/auth_token_provider.dart @@ -0,0 +1,27 @@ +abstract class AuthTokenProvider { + /// Gets a valid authentication token + /// Returns null if user is not authenticated + Future getValidToken(); + + /// Gets the current token without validation + /// Returns null if no token is available + String? getCurrentToken(); + + /// Stream of authentication token changes + /// Emits new tokens when authentication state changes + Stream get tokenStream; + + /// Forces a token refresh + /// Returns the new token or null if refresh fails + Future refreshToken(); + + /// Checks if the user is currently authenticated + bool get isAuthenticated; + + /// Checks if the current token is expired + /// Returns true if token is null or expired + bool get isTokenExpired; + + /// Sign out the current user + Future signOut(); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/auth/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/auth/index.dart new file mode 100644 index 0000000..5f2f025 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/auth/index.dart @@ -0,0 +1 @@ +export 'auth_token_provider.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/client/dio_network_client.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/client/dio_network_client.dart new file mode 100644 index 0000000..5c5712c --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/client/dio_network_client.dart @@ -0,0 +1,339 @@ +import 'dart:io'; + +import 'package:core/core.dart'; +import 'package:di/di.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; +import 'package:localization/localization.dart'; +import 'package:network/src/client/network_client.dart'; +import 'package:network/src/config/network_config.dart'; +import 'package:network/src/exceptions/network_exceptions.dart'; +import 'package:network/src/interceptors/auth_interceptor.dart'; +import 'package:network/src/models/network_response.dart'; +import 'package:talker_dio_logger/talker_dio_logger.dart'; +import 'package:talker_flutter/talker_flutter.dart'; + +class DioNetworkClient implements NetworkClient { + DioNetworkClient(this._config) : _dio = Dio() { + _setupDio(); + } + + final NetworkConfig _config; + final Dio _dio; + final Logger _logger = di.get(); + + void _setupDio() { + _dio.options = BaseOptions( + baseUrl: _config.baseUrl, + connectTimeout: _config.connectTimeout, + receiveTimeout: _config.receiveTimeout, + //sendTimeout cannot be used without a request body to send on Web + sendTimeout: kIsWeb ? null : _config.sendTimeout, + headers: _config.defaultHeaders, + ); + + // Add authentication interceptor if token provider is available + if (_config.authTokenProvider != null) { + _dio.interceptors.add(AuthInterceptor(_config.authTokenProvider!)); + _logger.d('🔐 Auth interceptor added to network client'); + } + + if (_config.enableLogging) { + _dio.interceptors.add( + TalkerDioLogger( + talker: di.get().logger as Talker, + settings: TalkerDioLoggerSettings( + printResponseData: true, + printResponseTime: true, + ), + ), + ); + } + } + + @override + Future> get( + String path, { + Map? queryParameters, + Map? headers, + required T Function(Object? json) fromJsonT, + }) async { + try { + final response = await _dio.get( + path, + queryParameters: queryParameters, + options: Options(headers: headers), + ); + return _handleResponse(response, fromJsonT: fromJsonT); + } catch (e) { + return _handleError(e); + } + } + + @override + Future> post( + String path, { + dynamic data, + Map? queryParameters, + Map? headers, + required T Function(Object? json) fromJsonT, + }) async { + try { + final response = await _dio.post( + path, + data: data, + queryParameters: queryParameters, + options: Options(headers: headers), + ); + return _handleResponse(response, fromJsonT: fromJsonT); + } catch (e) { + return _handleError(e); + } + } + + @override + Future> put( + String path, { + dynamic data, + Map? queryParameters, + Map? headers, + required T Function(Object? json) fromJsonT, + }) async { + try { + final response = await _dio.put( + path, + data: data, + queryParameters: queryParameters, + options: Options(headers: headers), + ); + return _handleResponse(response, fromJsonT: fromJsonT); + } catch (e) { + return _handleError(e); + } + } + + @override + Future> patch( + String path, { + dynamic data, + Map? queryParameters, + Map? headers, + required T Function(Object? json) fromJsonT, + }) async { + try { + final response = await _dio.patch( + path, + data: data, + queryParameters: queryParameters, + options: Options(headers: headers), + ); + return _handleResponse(response, fromJsonT: fromJsonT); + } catch (e) { + return _handleError(e); + } + } + + @override + Future> delete( + String path, { + dynamic data, + Map? queryParameters, + Map? headers, + required T Function(Object? json) fromJsonT, + }) async { + try { + final response = await _dio.delete( + path, + data: data, + queryParameters: queryParameters, + options: Options(headers: headers), + ); + return _handleResponse(response, fromJsonT: fromJsonT); + } catch (e) { + return _handleError(e); + } + } + + NetworkResponse _handleResponse( + Response response, { + required T Function(Object? json) fromJsonT, + }) { + try { + if (response.data is Map) { + final responseData = response.data as Map; + + if (responseData.containsKey('success')) { + if (responseData['success'] == true) { + return SuccessResponse.fromJson({ + 'statusCode': response.statusCode ?? 200, + 'headers': response.headers.map, + ...responseData, + }, fromJsonT); + } else { + ErrorResponse.fromJson({ + 'statusCode': response.statusCode ?? 500, + 'headers': response.headers.map, + ...responseData, + }); + } + } + } + + // Ideally, should not reach here if the API follows the + // standard response format + _logger.w('Invalid response format: ${response.data}'); + + // Fallback to a generic success response + return SuccessResponse.fromJson({ + 'statusCode': response.statusCode ?? 200, + 'headers': response.headers.map, + 'data': response.data, + }, (value) => value as T); + } catch (e, s) { + _logger.e('Error parsing response', e); + return ErrorResponse( + statusCode: response.statusCode ?? 500, + success: false, + message: 'Network Client failed to parse response!', + error: ErrorDetails( + code: 'PARSE_ERROR', + message: e.toString(), + details: {'stackTrace': s.toString()}, + ), + timestamp: DateTime.now().toIso8601String(), + ); + } + } + + NetworkResponse _handleError(dynamic error) { + NetworkException networkException; + ErrorResponse? apiError; + + if (error is DioException) { + apiError = _parseApiError(error.response); + networkException = _mapDioException(error, apiError); + } else { + networkException = UnknownNetworkException(message: error.toString()); + } + + _logger.e('Network error occurred', networkException); + + throw networkException; + } + + ErrorResponse? _parseApiError(Response? response) { + try { + final responseData = response?.data; + if (responseData is Map && + responseData.containsKey('success') && + responseData['success'] == false) { + return ErrorResponse.fromJson({ + 'statusCode': response?.statusCode ?? 500, + 'headers': response?.headers.map, + ...responseData, + }); + } + } catch (e) { + _logger.w('Failed to parse API error: $e'); + } + return null; + } + + NetworkException _mapDioException( + DioException dioException, + ErrorResponse? apiError, + ) { + final message = apiError?.error.message; + + switch (dioException.type) { + case DioExceptionType.connectionTimeout: + return ConnectionTimeoutException(message: message); + case DioExceptionType.receiveTimeout: + return ReceiveTimeoutException(message: message); + case DioExceptionType.sendTimeout: + return SendTimeoutException(message: message); + case DioExceptionType.connectionError: + if (dioException.error is SocketException) { + return NoInternetConnectionException(message: message); + } + return UnknownNetworkException(message: message); + case DioExceptionType.badResponse: + final statusCode = dioException.response?.statusCode; + switch (statusCode) { + case 400: + return BadRequestException(message: message, error: apiError); + case 401: + return UnauthorizedException(message: message, error: apiError); + case 403: + return ForbiddenException(message: message, error: apiError); + case 404: + return NotFoundException(message: message, error: apiError); + case 409: + return ConflictException(message: message, error: apiError); + case 422: + return UnprocessableEntityException( + message: message, + error: apiError, + ); + case 500: + case 502: + case 503: + case 504: + return InternalServerException(message: message, error: apiError); + default: + return UnknownNetworkException( + message: message, + statusCode: statusCode, + error: apiError, + ); + } + default: + return UnknownNetworkException(message: message, error: apiError); + } + } + + @override + T handleResponse(final NetworkResponse response) { + switch (response) { + case SuccessResponse(): + return response.data!; + case ErrorResponse(): + throw UnknownNetworkException( + message: response.message ?? strings.errors.network_error, + statusCode: response.statusCode, + error: response, + ); + default: + throw UnknownNetworkException( + message: strings.errors.unknown_error, + statusCode: response.statusCode, + ); + } + } + + @override + SuccessResponse handleSuccessResponse( + final NetworkResponse response, + ) { + switch (response) { + case SuccessResponse(): + return response; + case ErrorResponse(): + throw UnknownNetworkException( + message: response.message ?? strings.errors.network_error, + statusCode: response.statusCode, + error: response, + ); + default: + throw UnknownNetworkException( + message: strings.errors.unknown_error, + statusCode: response.statusCode, + ); + } + } + + @override + void dispose() { + _dio.close(); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/client/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/client/index.dart new file mode 100644 index 0000000..23a5e8f --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/client/index.dart @@ -0,0 +1,2 @@ +export 'dio_network_client.dart'; +export 'network_client.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/client/network_client.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/client/network_client.dart new file mode 100644 index 0000000..4d87172 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/client/network_client.dart @@ -0,0 +1,50 @@ +import 'package:network/src/models/network_response.dart'; + +abstract class NetworkClient { + Future> get( + String path, { + Map? queryParameters, + Map? headers, + required T Function(Object? json) fromJsonT, + }); + + Future> post( + String path, { + dynamic data, + Map? queryParameters, + Map? headers, + required T Function(Object? json) fromJsonT, + }); + + Future> put( + String path, { + dynamic data, + Map? queryParameters, + Map? headers, + required T Function(Object? json) fromJsonT, + }); + + Future> patch( + String path, { + dynamic data, + Map? queryParameters, + Map? headers, + required T Function(Object? json) fromJsonT, + }); + + Future> delete( + String path, { + dynamic data, + Map? queryParameters, + Map? headers, + required T Function(Object? json) fromJsonT, + }); + + T handleResponse(final NetworkResponse response); + + SuccessResponse handleSuccessResponse( + final NetworkResponse response, + ); + + void dispose(); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/config/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/config/index.dart new file mode 100644 index 0000000..81201c9 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/config/index.dart @@ -0,0 +1 @@ +export 'network_config.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/config/network_config.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/config/network_config.dart new file mode 100644 index 0000000..9b31a45 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/config/network_config.dart @@ -0,0 +1,44 @@ +import 'package:network/src/auth/auth_token_provider.dart'; + +abstract class NetworkConfig { + String get baseUrl; + Duration get connectTimeout; + Duration get receiveTimeout; + Duration get sendTimeout; + Map get defaultHeaders; + bool get enableLogging; + AuthTokenProvider? get authTokenProvider; +} + +class DefaultNetworkConfig implements NetworkConfig { + const DefaultNetworkConfig({ + required this.baseUrl, + this.connectTimeout = const Duration(seconds: 30), + this.receiveTimeout = const Duration(seconds: 30), + this.sendTimeout = const Duration(seconds: 30), + this.defaultHeaders = const {}, + this.enableLogging = true, + this.authTokenProvider, + }); + + @override + final String baseUrl; + + @override + final Duration connectTimeout; + + @override + final Duration receiveTimeout; + + @override + final Duration sendTimeout; + + @override + final Map defaultHeaders; + + @override + final bool enableLogging; + + @override + final AuthTokenProvider? authTokenProvider; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/exceptions/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/exceptions/index.dart new file mode 100644 index 0000000..8df1c24 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/exceptions/index.dart @@ -0,0 +1 @@ +export 'network_exceptions.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/exceptions/network_exceptions.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/exceptions/network_exceptions.dart new file mode 100644 index 0000000..5464888 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/exceptions/network_exceptions.dart @@ -0,0 +1,107 @@ +import 'package:localization/localization.dart'; +import 'package:network/network.dart'; + +abstract class NetworkException implements Exception { + const NetworkException({this.message, this.statusCode, this.error}); + + final String? message; + final int? statusCode; + final ErrorResponse? error; + + String get displayMessage => message ?? strings.errors.unknown_network; + + @override + String toString() => '$runtimeType: $displayMessage'; +} + +class BadRequestException extends NetworkException { + const BadRequestException({super.message, super.error}) + : super(statusCode: 400); + + @override + String get displayMessage => message ?? strings.errors.bad_request; +} + +class UnauthorizedException extends NetworkException { + const UnauthorizedException({super.message, super.error}) + : super(statusCode: 401); + + @override + String get displayMessage => message ?? strings.errors.unauthorized; +} + +class ForbiddenException extends NetworkException { + const ForbiddenException({super.message, super.error}) + : super(statusCode: 403); + + @override + String get displayMessage => message ?? strings.errors.forbidden; +} + +class NotFoundException extends NetworkException { + const NotFoundException({super.message, super.error}) + : super(statusCode: 404); + + @override + String get displayMessage => message ?? strings.errors.not_found; +} + +class ConflictException extends NetworkException { + const ConflictException({super.message, super.error}) + : super(statusCode: 409); + + @override + String get displayMessage => message ?? strings.errors.conflict; +} + +class UnprocessableEntityException extends NetworkException { + const UnprocessableEntityException({super.message, super.error}) + : super(statusCode: 422); + + @override + String get displayMessage => message ?? strings.errors.unprocessable_entity; +} + +class InternalServerException extends NetworkException { + const InternalServerException({super.message, super.error}) + : super(statusCode: 500); + + @override + String get displayMessage => message ?? strings.errors.internal_server_error; +} + +class ConnectionTimeoutException extends NetworkException { + const ConnectionTimeoutException({super.message}) : super(statusCode: 599); + + @override + String get displayMessage => message ?? strings.errors.connection_timeout; +} + +class ReceiveTimeoutException extends NetworkException { + const ReceiveTimeoutException({super.message}) : super(statusCode: null); + + @override + String get displayMessage => message ?? strings.errors.receive_timeout; +} + +class SendTimeoutException extends NetworkException { + const SendTimeoutException({super.message}) : super(statusCode: null); + + @override + String get displayMessage => message ?? strings.errors.send_timeout; +} + +class NoInternetConnectionException extends NetworkException { + const NoInternetConnectionException({super.message}) + : super(statusCode: null); + + @override + String get displayMessage => message ?? strings.errors.no_internet; +} + +class UnknownNetworkException extends NetworkException { + const UnknownNetworkException({super.message, super.statusCode, super.error}); + + @override + String get displayMessage => message ?? strings.errors.unknown_network; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/interceptors/auth_interceptor.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/interceptors/auth_interceptor.dart new file mode 100644 index 0000000..8aed94b --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/interceptors/auth_interceptor.dart @@ -0,0 +1,73 @@ +import 'package:core/core.dart'; +import 'package:di/di.dart'; +import 'package:dio/dio.dart'; +import 'package:network/src/auth/auth_token_provider.dart'; + +class AuthInterceptor extends Interceptor { + AuthInterceptor(this._authTokenProvider) : _logger = di.get(); + + final AuthTokenProvider _authTokenProvider; + final Logger _logger; + + @override + void onRequest( + RequestOptions options, + RequestInterceptorHandler handler, + ) async { + try { + // Skip authentication for requests that already have authorization header + if (options.headers.containsKey('Authorization')) { + handler.next(options); + return; + } + + final token = await _authTokenProvider.getValidToken(); + if (token != null) { + options.headers['Authorization'] = 'Bearer $token'; + _logger.d('🔐 Added Bearer token to request: ${options.uri}'); + } else { + _logger.d('🔓 No auth token available for request: ${options.uri}'); + } + + handler.next(options); + } catch (e) { + _logger.e('🚨 Error adding auth token to request', e); + handler.next(options); + } + } + + @override + void onError(DioException err, ErrorInterceptorHandler handler) async { + // Handle 401 Unauthorized - token might be expired + if (err.response?.statusCode == 401) { + _logger.w('🔐 Received 401 Unauthorized, attempting token refresh'); + + try { + final newToken = await _authTokenProvider.refreshToken(); + if (newToken != null) { + _logger.i('🔄 Token refreshed successfully, retrying request'); + + // Clone the request with new token + final requestOptions = err.requestOptions; + requestOptions.headers['Authorization'] = 'Bearer $newToken'; + + // Retry the request + final response = await Dio().fetch(requestOptions); + handler.resolve(response); + return; + } else { + _logger.w('❌ Token refresh failed, user needs to re-authenticate'); + } + } catch (refreshError) { + _logger.e('🚨 Error during token refresh', refreshError); + } + } + + // Handle 403 Forbidden - user doesn't have permission + if (err.response?.statusCode == 403) { + _logger.w('🚫 Received 403 Forbidden, insufficient permissions'); + } + + handler.next(err); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/interceptors/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/interceptors/index.dart new file mode 100644 index 0000000..075b202 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/interceptors/index.dart @@ -0,0 +1,2 @@ +export 'auth_interceptor.dart'; +export 'logging_interceptor.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/interceptors/logging_interceptor.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/interceptors/logging_interceptor.dart new file mode 100644 index 0000000..897650e --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/interceptors/logging_interceptor.dart @@ -0,0 +1,44 @@ +import 'package:core/core.dart'; +import 'package:di/di.dart'; +import 'package:dio/dio.dart'; + +class LoggingInterceptor extends Interceptor { + LoggingInterceptor() : _logger = di.get(); + + final Logger _logger; + + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + _logger.d('🚀 Request: ${options.method} ${options.uri}'); + _logger.d('Headers: ${options.headers}'); + if (options.data != null) { + _logger.d('Data: ${options.data}'); + } + if (options.queryParameters.isNotEmpty) { + _logger.d('Query Parameters: ${options.queryParameters}'); + } + super.onRequest(options, handler); + } + + @override + void onResponse(Response response, ResponseInterceptorHandler handler) { + _logger.d( + '✅ Response: ${response.statusCode} ${response.requestOptions.uri}', + ); + _logger.d('Response Data: ${response.data}'); + super.onResponse(response, handler); + } + + @override + void onError(DioException err, ErrorInterceptorHandler handler) { + _logger.e( + '❌ Error: ${err.response?.statusCode} ${err.requestOptions.uri}', + err, + err.stackTrace, + ); + if (err.response?.data != null) { + _logger.e('Error Data: ${err.response?.data}'); + } + super.onError(err, handler); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/models/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/models/index.dart new file mode 100644 index 0000000..87c17f7 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/models/index.dart @@ -0,0 +1 @@ +export 'network_response.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/models/network_response.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/models/network_response.dart new file mode 100644 index 0000000..cb65ffd --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/models/network_response.dart @@ -0,0 +1,102 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'network_response.g.dart'; + +abstract class NetworkResponse { + const NetworkResponse({ + required this.statusCode, + required this.success, + this.message, + required this.timestamp, + this.headers, + }); + + final int statusCode; + final bool success; + final String? message; + final String timestamp; + final Map>? headers; + + bool get isSuccess => success && statusCode >= 200 && statusCode < 300; +} + +@JsonSerializable(genericArgumentFactories: true) +class SuccessResponse extends NetworkResponse { + final T data; + final Pagination? pagination; + + const SuccessResponse({ + required super.statusCode, + required super.success, + required this.data, + super.message, + required super.timestamp, + this.pagination, + super.headers, + }); + + factory SuccessResponse.fromJson( + Map json, + T Function(Object? json) fromJsonT, + ) => _$SuccessResponseFromJson(json, fromJsonT); + + Map toJson(Object Function(T value) toJsonT) => + _$SuccessResponseToJson(this, toJsonT); +} + +@JsonSerializable() +class ErrorResponse extends NetworkResponse { + final ErrorDetails error; + + const ErrorResponse({ + required super.statusCode, + required super.success, + required this.error, + required super.timestamp, + super.message, + super.headers, + }); + + factory ErrorResponse.fromJson(Map json) => + _$ErrorResponseFromJson(json); + + Map toJson() => _$ErrorResponseToJson(this); +} + +@JsonSerializable() +class ErrorDetails { + const ErrorDetails({required this.code, required this.message, this.details}); + + final String code; + final String message; + final Map? details; + + factory ErrorDetails.fromJson(Map json) => + _$ErrorDetailsFromJson(json); + + Map toJson() => _$ErrorDetailsToJson(this); +} + +@JsonSerializable() +class Pagination { + const Pagination({ + required this.page, + required this.limit, + required this.total, + required this.totalPages, + required this.hasNextPage, + required this.hasPreviousPage, + }); + + final int page; + final int limit; + final int total; + final int totalPages; + final bool hasNextPage; + final bool hasPreviousPage; + + factory Pagination.fromJson(Map json) => + _$PaginationFromJson(json); + + Map toJson() => _$PaginationToJson(this); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/models/network_response.g.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/models/network_response.g.dart new file mode 100644 index 0000000..7445c78 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/models/network_response.g.dart @@ -0,0 +1,93 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'network_response.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +SuccessResponse _$SuccessResponseFromJson( + Map json, + T Function(Object? json) fromJsonT, +) => SuccessResponse( + statusCode: (json['statusCode'] as num).toInt(), + success: json['success'] as bool, + data: fromJsonT(json['data']), + message: json['message'] as String?, + timestamp: json['timestamp'] as String, + pagination: json['pagination'] == null + ? null + : Pagination.fromJson(json['pagination'] as Map), + headers: (json['headers'] as Map?)?.map( + (k, e) => + MapEntry(k, (e as List).map((e) => e as String).toList()), + ), +); + +Map _$SuccessResponseToJson( + SuccessResponse instance, + Object? Function(T value) toJsonT, +) => { + 'statusCode': instance.statusCode, + 'success': instance.success, + 'message': instance.message, + 'timestamp': instance.timestamp, + 'headers': instance.headers, + 'data': toJsonT(instance.data), + 'pagination': instance.pagination, +}; + +ErrorResponse _$ErrorResponseFromJson(Map json) => + ErrorResponse( + statusCode: (json['statusCode'] as num).toInt(), + success: json['success'] as bool, + error: ErrorDetails.fromJson(json['error'] as Map), + timestamp: json['timestamp'] as String, + message: json['message'] as String?, + headers: (json['headers'] as Map?)?.map( + (k, e) => + MapEntry(k, (e as List).map((e) => e as String).toList()), + ), + ); + +Map _$ErrorResponseToJson(ErrorResponse instance) => + { + 'statusCode': instance.statusCode, + 'success': instance.success, + 'message': instance.message, + 'timestamp': instance.timestamp, + 'headers': instance.headers, + 'error': instance.error, + }; + +ErrorDetails _$ErrorDetailsFromJson(Map json) => ErrorDetails( + code: json['code'] as String, + message: json['message'] as String, + details: json['details'] as Map?, +); + +Map _$ErrorDetailsToJson(ErrorDetails instance) => + { + 'code': instance.code, + 'message': instance.message, + 'details': instance.details, + }; + +Pagination _$PaginationFromJson(Map json) => Pagination( + page: (json['page'] as num).toInt(), + limit: (json['limit'] as num).toInt(), + total: (json['total'] as num).toInt(), + totalPages: (json['totalPages'] as num).toInt(), + hasNextPage: json['hasNextPage'] as bool, + hasPreviousPage: json['hasPreviousPage'] as bool, +); + +Map _$PaginationToJson(Pagination instance) => + { + 'page': instance.page, + 'limit': instance.limit, + 'total': instance.total, + 'totalPages': instance.totalPages, + 'hasNextPage': instance.hasNextPage, + 'hasPreviousPage': instance.hasPreviousPage, + }; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/utils/extensions/error_message_extension.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/utils/extensions/error_message_extension.dart new file mode 100644 index 0000000..a715c62 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/utils/extensions/error_message_extension.dart @@ -0,0 +1,97 @@ +import 'package:core/core.dart'; +import 'package:flutter/foundation.dart'; +import 'package:localization/localization.dart'; +import 'package:network/network.dart'; + +extension ErrorMessageExtension on Object? { + String message() { + return switch (this) { + NetworkException error => error.displayMessage, + CoreException error => error.message, + FormatException _ => strings.errors.format_exception_message( + ErrorCodes.formatException, + debugMessage, + ), + Error error => error.message(), + _ => strings.errors.default_error_message, + }; + } + + String get debugMessage { + if (kDebugMode) { + return '\n${toString()}\ntype: $runtimeType'; + } + return ''; + } +} + +// Simple creative error messages with codes +extension ErrorMessageOnError on Error { + String message() { + return switch (this) { + TypeError _ => strings.errors.type_error_message( + ErrorCodes.typeError, + debugMessage, + ), + IndexError _ => strings.errors.index_error_message( + ErrorCodes.indexError, + debugMessage, + ), + RangeError _ => strings.errors.range_error_message( + ErrorCodes.rangeError, + debugMessage, + ), + ArgumentError _ => strings.errors.argument_error_message( + ErrorCodes.argumentError, + debugMessage, + ), + StateError _ => strings.errors.state_error_message( + ErrorCodes.stateError, + debugMessage, + ), + UnimplementedError _ => strings.errors.unimplemented_error_message( + ErrorCodes.unimplementedError, + debugMessage, + ), + UnsupportedError _ => strings.errors.unsupported_error_message( + ErrorCodes.unsupportedError, + debugMessage, + ), + ConcurrentModificationError _ => + strings.errors.concurrent_modification_error_message( + ErrorCodes.concurrentModificationError, + debugMessage, + ), + OutOfMemoryError _ => strings.errors.out_of_memory_error_message( + ErrorCodes.outOfMemoryError, + debugMessage, + ), + StackOverflowError _ => strings.errors.stack_overflow_error_message( + ErrorCodes.stackOverflowError, + debugMessage, + ), + _ => strings.errors.unknown_error_message( + ErrorCodes.unknownError, + debugMessage, + ), + }; + } +} + +sealed class ErrorCodes { + static const String typeError = "E001"; + static const String argumentError = "E002"; + static const String rangeError = "E003"; + static const String indexError = "E004"; + static const String stateError = "E005"; + static const String unsupportedError = "E006"; + static const String unimplementedError = "E007"; + static const String concurrentModificationError = "E008"; + static const String outOfMemoryError = "E009"; + static const String stackOverflowError = "E010"; + static const String formatException = "E011"; + static const String unknownError = "E999"; + static const String networkException = "E100"; + static const String coreException = "E101"; + static const String defaultError = "E000"; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/utils/extensions/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/utils/extensions/index.dart new file mode 100644 index 0000000..76768c9 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/utils/extensions/index.dart @@ -0,0 +1 @@ +export 'error_message_extension.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/utils/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/utils/index.dart new file mode 100644 index 0000000..49893a1 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/utils/index.dart @@ -0,0 +1 @@ +export 'extensions/index.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/pubspec.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/pubspec.yaml new file mode 100644 index 0000000..6fe7754 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/pubspec.yaml @@ -0,0 +1,31 @@ +name: network +description: "HTTP layer for {{name.titleCase()}}: Dio client, interceptors, typed responses and error mapping." +version: 0.1.0 +publish_to: 'none' + +environment: + sdk: ^3.8.0 + flutter: ">=3.29.0" + +dependencies: + flutter: + sdk: flutter + dio: ^5.7.0 + json_annotation: ^4.9.0 + talker_dio_logger: ^4.9.1 + talker_flutter: ^4.4.1 + core: + path: ../core + localization: + path: ../localization + di: + path: ../di + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + build_runner: ^2.4.15 + json_serializable: ^6.9.5 + +flutter: diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/test/network_test.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/test/network_test.dart new file mode 100644 index 0000000..b7e0d4e --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{network_dir}}/test/network_test.dart @@ -0,0 +1,15 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:network/network.dart'; + +void main() { + test('DefaultNetworkConfig creates instance with provided values', () { + const config = DefaultNetworkConfig( + baseUrl: 'https://api.example.com', + enableLogging: true, + ); + + expect(config.baseUrl, 'https://api.example.com'); + expect(config.enableLogging, true); + expect(config.connectTimeout, const Duration(seconds: 30)); + }); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/notifications.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/notifications.dart new file mode 100644 index 0000000..21ecd44 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/notifications.dart @@ -0,0 +1,28 @@ +library; + +import 'dart:async'; + +import 'package:core/core.dart'; +import 'package:di/di.dart'; +import 'package:notifications/src/config/index.dart'; +import 'package:notifications/src/di/index.dart'; + +export 'src/client/index.dart'; +export 'src/config/index.dart'; +export 'src/device_info/index.dart'; +export 'src/exceptions/index.dart'; +export 'src/models/index.dart'; +export 'src/services/index.dart'; + +/// Initialize the notifications module with dependency injection +Future init({ + NotificationConfig config = const DefaultNotificationConfig(), +}) async { + final logger = di.get(); + try { + await registerNotificationWithDI(config); + logger.i('🔔 Notifications module initialized'); + } catch (e, s) { + logger.e('🔔 Failed to initialize notifications module', e, s); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/firebase_notification_client.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/firebase_notification_client.dart new file mode 100644 index 0000000..babe212 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/firebase_notification_client.dart @@ -0,0 +1,258 @@ +import 'dart:async'; + +import 'package:app_badge_plus/app_badge_plus.dart'; +import 'package:core/logger/logger.dart'; +import 'package:design_system/toast/toasts.dart'; +import 'package:di/di.dart'; +import 'package:firebase_messaging/firebase_messaging.dart' + hide NotificationSettings; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:network/network.dart'; +import 'package:notifications/notifications.dart'; + +const String _tag = 'FirebaseNotificationClient'; + +/// Firebase implementation of the notification client +class FirebaseNotificationClient implements NotificationClient { + FirebaseNotificationClient({ + required this.config, + Logger? logger, + NotificationTokenManager? tokenManager, + NotificationPermissionManager? permissionManager, + }) : _logger = logger ?? di.get(), + _tokenManager = tokenManager ?? di.get(), + _permissionManager = + permissionManager ?? di.get(); + + final NotificationConfig config; + final Logger _logger; + final NotificationTokenManager _tokenManager; + final NotificationPermissionManager _permissionManager; + bool _isGranted = false; + + FirebaseMessaging? _firebaseMessaging; + + String? _fcmToken; + String? _deviceId; + + // Store subscriptions for cleanup + StreamSubscription? _foregroundMessageSubscription; + StreamSubscription? _messageOpenedAppSubscription; + StreamSubscription? _tokenRefreshSubscription; + + @override + Future init() async { + try { + _logger.i('$_tag: Initializing Firebase notification client'); + + // Initialize Firebase Messaging + _firebaseMessaging = FirebaseMessaging.instance; + + final isGranted = await requestPermissions(); + if (!isGranted) { + _logger.w('$_tag: Notification permissions not granted'); + return; + } + _isGranted = isGranted; + + // Setup FCM handlers if background notifications are enabled + await _setupFCMHandlers(); + + // Get FCM token and register if backend registration is enabled + await getFCMToken(); + + // Clear badge count as the last step of initialization + await clearBadge(); + + _logger.i('$_tag: Firebase notification client initialized successfully'); + } catch (e, s) { + _logger.e('$_tag: Error initializing Firebase notification client', e, s); + throw NotificationException(message: e.message()); + } + } + + @override + Future requestPermissions({bool provisional = false}) async { + return await _permissionManager.requestPermissions(); + } + + @override + Future clearBadge() async { + try { + _logger.i('🔔 Clearing badge count'); + if (!_isGranted) { + _logger.w( + '🔔 Notification permissions ' + 'not granted, cannot clear badge', + ); + return; + } + if (!await AppBadgePlus.isSupported()) { + _logger.w('🔔 AppBadgePlus is not supported on this platform'); + return; + } + await AppBadgePlus.updateBadge(0); + _logger.i('🔔 Badge count cleared'); + } catch (e) { + _logger.w('🔔 Failed to clear badge count: $e'); + } + } + + Future _setupFCMHandlers() async { + // Handle background messages (when app is terminated) + FirebaseMessaging.onBackgroundMessage(_handleBackgroundNotification); + + // Handle foreground messages + _foregroundMessageSubscription = FirebaseMessaging.onMessage.listen( + (RemoteMessage message) { + _logger.i('$_tag: Received foreground message: ${message.messageId}'); + handleForegroundNotification(message); + }, + onError: (error) { + _logger.e('$_tag: Error in foreground message handler', error); + }, + ); + + // Handle notification opened (from background/terminated) + _messageOpenedAppSubscription = FirebaseMessaging.onMessageOpenedApp.listen( + (RemoteMessage message) { + _logger.i('$_tag: Notification opened app: ${message.messageId}'); + handleNotificationOpened(message, source: 'background'); + }, + onError: (error) { + _logger.e('$_tag: Error in notification opened handler', error); + }, + ); + + // Handle initial message (if app was opened from terminated state) + if (_firebaseMessaging != null) { + final initialMessage = await _firebaseMessaging!.getInitialMessage(); + if (initialMessage != null) { + _logger.i( + '$_tag: App opened from terminated state: ${initialMessage.messageId}', + ); + handleNotificationOpened(initialMessage, source: 'terminated'); + } + + // Handle token refresh + _tokenRefreshSubscription = _firebaseMessaging!.onTokenRefresh.listen( + (String newToken) { + _fcmToken = newToken; + _tokenManager.handleTokenRefresh(newToken); + }, + onError: (error) { + _logger.e('$_tag: Error in token refresh handler', error); + }, + ); + } + + _logger.i('$_tag: FCM handlers setup complete'); + } + + @override + Future getFCMToken() async { + try { + _fcmToken = await _tokenManager.getFCMToken(); + + if (_fcmToken != null) { + _deviceId = await _tokenManager.registerToken(_fcmToken!); + _logger.i('$_tag: Token registered with device ID: $_deviceId'); + } + + return _fcmToken; + } catch (e, s) { + _logger.e('$_tag: Error getting FCM token', e, s); + return null; + } + } + + /// Handle background notification (when app is not running) + @pragma('vm:entry-point') + static Future _handleBackgroundNotification( + RemoteMessage message, + ) async { + try { + debugPrint( + '$_tag: Handling background notification: ${message.messageId}', + ); + } catch (e, s) { + debugPrint( + '$_tag: Error handling background notification:' + ' ${message.messageId} - $e $s', + ); + } + } + + @override + Future handleForegroundNotification(RemoteMessage message) async { + try { + _logger.i( + '$_tag: Handling foreground notification: ${message.messageId}', + ); + + final notification = message.notification; + if (notification != null && + notification.title != null && + notification.body != null) { + Toast.notification( + title: notification.title!, + body: notification.body!, + ); + } + } catch (e, s) { + _logger.e('$_tag: Error handling foreground notification', e, s); + } + } + + @override + Future handleNotificationOpened( + RemoteMessage message, { + String? source, + }) async { + try { + _logger.i('$_tag: Handling notification opened: ${message.messageId}'); + + final notification = message.notification; + + _logger.i( + '$_tag: Notification opened from $source: ${notification?.title} - ${notification?.body}', + ); + + final data = message.data; + final actionType = data['type']; + if (actionType == null) return; + + if (actionType == 'surveyAssignments') { + final id = data['id']; + if (id == null) return; + final router = di.get(); + router.push('/home/survey/assignment/$id'); + } + } catch (e, s) { + _logger.e('$_tag: Error handling notification opened', e, s); + } + } + + @override + String? get fcmToken => _fcmToken; + + @override + String? get deviceId => _deviceId; + + @override + Future dispose() async { + _logger.i('$_tag: Disposing Firebase notification client'); + + // Cancel all subscriptions to prevent memory leaks + _foregroundMessageSubscription?.cancel(); + _messageOpenedAppSubscription?.cancel(); + _tokenRefreshSubscription?.cancel(); + + _foregroundMessageSubscription = null; + _messageOpenedAppSubscription = null; + _tokenRefreshSubscription = null; + + _firebaseMessaging = null; + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/index.dart new file mode 100644 index 0000000..d7fab81 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/index.dart @@ -0,0 +1,2 @@ +export 'firebase_notification_client.dart'; +export 'notification_client.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/notification_client.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/notification_client.dart new file mode 100644 index 0000000..38007ce --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/notification_client.dart @@ -0,0 +1,27 @@ +import 'package:firebase_messaging/firebase_messaging.dart' + hide NotificationSettings; + +/// Abstract notification client interface +/// Simplified interface following network package patterns +abstract class NotificationClient { + Future init(); + + Future requestPermissions({bool provisional = false}); + + Future getFCMToken(); + + Future handleForegroundNotification(RemoteMessage message); + + Future handleNotificationOpened( + RemoteMessage message, { + String? source, + }); + + Future clearBadge(); + + String? get fcmToken; + + String? get deviceId; + + Future dispose(); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/config/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/config/index.dart new file mode 100644 index 0000000..8ca3673 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/config/index.dart @@ -0,0 +1 @@ +export 'notification_config.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/config/notification_config.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/config/notification_config.dart new file mode 100644 index 0000000..54d3860 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/config/notification_config.dart @@ -0,0 +1,7 @@ +class NotificationConfig { + const NotificationConfig(); +} + +class DefaultNotificationConfig extends NotificationConfig { + const DefaultNotificationConfig() : super(); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/device_info/device_info.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/device_info/device_info.dart new file mode 100644 index 0000000..7e9166c --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/device_info/device_info.dart @@ -0,0 +1,5 @@ +abstract class DeviceInfo { + Future generateDeviceId(); + + Future getDeviceName(); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/device_info/device_info_impl.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/device_info/device_info_impl.dart new file mode 100644 index 0000000..9f11e5a --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/device_info/device_info_impl.dart @@ -0,0 +1,210 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:core/logger/logger.dart'; +import 'package:crypto/crypto.dart'; +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:di/di.dart'; +import 'package:notifications/src/device_info/device_info.dart'; + +/// Concrete implementation of DeviceInfo for Android and iOS only +/// Generates consistent device identifiers using stable device characteristics +class DeviceInfoImpl implements DeviceInfo { + static const String _tag = 'DeviceInfoImpl'; + + final Logger _logger; + final DeviceInfoPlugin _deviceInfoPlugin; + + DeviceInfoImpl({Logger? logger, DeviceInfoPlugin? deviceInfoPlugin}) + : _logger = logger ?? di.get(), + _deviceInfoPlugin = deviceInfoPlugin ?? DeviceInfoPlugin(); + + @override + Future generateDeviceId() async { + try { + if (Platform.isAndroid) { + return await _generateAndroidDeviceId(); + } else if (Platform.isIOS) { + return await _generateIOSDeviceId(); + } else { + _logger.w('$_tag: Unsupported platform: ${Platform.operatingSystem}'); + throw UnsupportedError('Only Android and iOS platforms are supported'); + } + } catch (e) { + _logger.w('$_tag: Error generating device ID: $e'); + return _generateFallbackId(); + } + } + + @override + Future getDeviceName() async { + try { + if (Platform.isAndroid) { + return await _getAndroidDeviceName(); + } else if (Platform.isIOS) { + return await _getIOSDeviceName(); + } else { + _logger.w('$_tag: Unsupported platform: ${Platform.operatingSystem}'); + throw UnsupportedError('Only Android and iOS platforms are supported'); + } + } catch (e) { + _logger.w('$_tag: Error getting device name: $e'); + return _getFallbackDeviceName(); + } + } + + /// Generate Android device ID using stable hardware characteristics + Future _generateAndroidDeviceId() async { + try { + final androidInfo = await _deviceInfoPlugin.androidInfo; + + // Use only the most stable hardware characteristics that don't change + final stableCharacteristics = [ + androidInfo.brand, + androidInfo.model, + androidInfo.device, + androidInfo.hardware, + androidInfo.board, + androidInfo.bootloader, + androidInfo.product, + // fingerprint contains build info but is generally stable for same device + androidInfo.fingerprint, + // Physical device indicator + androidInfo.isPhysicalDevice.toString(), + // Hardware features that are stable + androidInfo.supportedAbis.join(','), + androidInfo.supported32BitAbis.join(','), + androidInfo.supported64BitAbis.join(','), + ]; + + // Remove empty values and create a deterministic string + final cleanCharacteristics = stableCharacteristics + .where((char) => char.isNotEmpty && char != 'null') + .toList(); + + if (cleanCharacteristics.isEmpty) { + throw Exception('No stable characteristics found for Android device'); + } + + // Sort to ensure consistent ordering + cleanCharacteristics.sort(); + + final deviceString = cleanCharacteristics.join('|'); + final deviceHash = _generateDeterministicHash(deviceString); + + _logger.d( + '$_tag: Generated Android device ID from ' + '${cleanCharacteristics.length} characteristics', + ); + return 'android_$deviceHash'; + } catch (e) { + _logger.w('$_tag: Android device ID generation failed: $e'); + rethrow; + } + } + + /// Generate iOS device ID using identifierForVendor or stable characteristics + Future _generateIOSDeviceId() async { + try { + final iosInfo = await _deviceInfoPlugin.iosInfo; + + // First preference: use identifierForVendor as it's designed to be stable + final identifier = iosInfo.identifierForVendor; + if (identifier != null && identifier.isNotEmpty && identifier != 'null') { + _logger.d('$_tag: Using iOS identifierForVendor'); + return 'ios_$identifier'; + } + + // Fallback: use stable device characteristics + _logger.i( + '$_tag: iOS identifierForVendor not available, ' + 'using device characteristics', + ); + + final stableCharacteristics = [ + iosInfo.model, + iosInfo.localizedModel, + iosInfo.systemName, + iosInfo.isPhysicalDevice.toString(), + // Machine identifier from utsname (hardware model) + iosInfo.utsname.machine, + iosInfo.utsname.sysname, + // These are generally stable for the same device model + iosInfo.utsname.nodename, + ]; + + // Remove empty values and create a deterministic string + final cleanCharacteristics = stableCharacteristics + .where((char) => char.isNotEmpty && char != 'null') + .toList(); + + if (cleanCharacteristics.isEmpty) { + throw Exception('No stable characteristics found for iOS device'); + } + + // Sort to ensure consistent ordering + cleanCharacteristics.sort(); + + final deviceString = cleanCharacteristics.join('|'); + final deviceHash = _generateDeterministicHash(deviceString); + + _logger.d( + '$_tag: Generated iOS device ID from ' + '${cleanCharacteristics.length} characteristics', + ); + return 'ios_$deviceHash'; + } catch (e) { + _logger.w('$_tag: iOS device ID generation failed: $e'); + rethrow; + } + } + + /// Get Android device name in format "Brand Model" + Future _getAndroidDeviceName() async { + final androidInfo = await _deviceInfoPlugin.androidInfo; + final brand = androidInfo.brand; + final model = androidInfo.model; + + return '$brand $model'; + } + + /// Get iOS device name in format "Name (Model)" + Future _getIOSDeviceName() async { + final iosInfo = await _deviceInfoPlugin.iosInfo; + final name = iosInfo.name; + final model = iosInfo.model; + + return '$name ($model)'; + } + + /// Generate a deterministic hash from device characteristics + /// Uses SHA-256 to create a consistent, stable hash + String _generateDeterministicHash(String input) { + // Add a salt to make the hash more unique while keeping it deterministic + const salt = 'device_identifier_salt_2024'; + final saltedInput = '$salt|$input'; + + final bytes = utf8.encode(saltedInput); + final digest = sha256.convert(bytes); + + // Return first 16 characters for reasonable length + return digest.toString().substring(0, 16); + } + + /// Generate a fallback device ID when all other methods fail + String _generateFallbackId() { + final platform = Platform.isAndroid ? 'android' : 'ios'; + + // Use a deterministic fallback based on platform + final fallbackString = '${platform}_fallback_device'; + final fallbackHash = _generateDeterministicHash(fallbackString); + + _logger.w('$_tag: Using deterministic fallback ID'); + return '${platform}_$fallbackHash'; + } + + /// Get fallback device name when detection fails + String _getFallbackDeviceName() { + return Platform.isAndroid ? 'Android Device' : 'iOS Device'; + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/device_info/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/device_info/index.dart new file mode 100644 index 0000000..2bdcc90 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/device_info/index.dart @@ -0,0 +1,2 @@ +export 'device_info.dart'; +export 'device_info_impl.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/di/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/di/index.dart new file mode 100644 index 0000000..44acbaa --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/di/index.dart @@ -0,0 +1 @@ +export 'notification_di.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/di/notification_di.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/di/notification_di.dart new file mode 100644 index 0000000..160c0a7 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/di/notification_di.dart @@ -0,0 +1,27 @@ +import 'package:di/di.dart'; +import 'package:notifications/src/client/index.dart'; +import 'package:notifications/src/config/index.dart'; +import 'package:notifications/src/device_info/index.dart'; +import 'package:notifications/src/services/notification_permission_manager.dart'; +import 'package:notifications/src/services/notification_token_manager.dart'; + +/// Register notification services with dependency injection +Future registerNotificationWithDI(NotificationConfig config) async { + // Register config + di.register(config); + + // Register device info service + di.register(DeviceInfoImpl()); + + // Register token manager + di.register(FirebaseTokenManager()); + + // Register permission manager + di.register(FirebasePermissionManager()); + + // Register notification client with proper disposal + di.register( + FirebaseNotificationClient(config: config), + dispose: (client) => client.dispose(), + ); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/exceptions/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/exceptions/index.dart new file mode 100644 index 0000000..27be259 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/exceptions/index.dart @@ -0,0 +1 @@ +export 'notification_exceptions.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/exceptions/notification_exceptions.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/exceptions/notification_exceptions.dart new file mode 100644 index 0000000..603caf4 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/exceptions/notification_exceptions.dart @@ -0,0 +1,5 @@ +import 'package:core/core.dart'; + +class NotificationException extends CoreException { + NotificationException({required super.message}); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/models/device_token_models.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/models/device_token_models.dart new file mode 100644 index 0000000..c6236a7 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/models/device_token_models.dart @@ -0,0 +1,35 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'device_token_models.g.dart'; + +@JsonSerializable() +class DeviceTokenRequest { + final String fcmToken; + final String deviceId; + final String deviceName; + final String deviceType; + + const DeviceTokenRequest({ + required this.fcmToken, + required this.deviceId, + required this.deviceName, + required this.deviceType, + }); + + factory DeviceTokenRequest.fromJson(Map json) => + _$DeviceTokenRequestFromJson(json); + + Map toJson() => _$DeviceTokenRequestToJson(this); +} + +@JsonSerializable() +class DeviceTokenUpdateRequest { + final String fcmToken; + + const DeviceTokenUpdateRequest({required this.fcmToken}); + + factory DeviceTokenUpdateRequest.fromJson(Map json) => + _$DeviceTokenUpdateRequestFromJson(json); + + Map toJson() => _$DeviceTokenUpdateRequestToJson(this); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/models/device_token_models.g.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/models/device_token_models.g.dart new file mode 100644 index 0000000..bfadaaa --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/models/device_token_models.g.dart @@ -0,0 +1,31 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'device_token_models.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +DeviceTokenRequest _$DeviceTokenRequestFromJson(Map json) => + DeviceTokenRequest( + fcmToken: json['fcmToken'] as String, + deviceId: json['deviceId'] as String, + deviceName: json['deviceName'] as String, + deviceType: json['deviceType'] as String, + ); + +Map _$DeviceTokenRequestToJson(DeviceTokenRequest instance) => + { + 'fcmToken': instance.fcmToken, + 'deviceId': instance.deviceId, + 'deviceName': instance.deviceName, + 'deviceType': instance.deviceType, + }; + +DeviceTokenUpdateRequest _$DeviceTokenUpdateRequestFromJson( + Map json, +) => DeviceTokenUpdateRequest(fcmToken: json['fcmToken'] as String); + +Map _$DeviceTokenUpdateRequestToJson( + DeviceTokenUpdateRequest instance, +) => {'fcmToken': instance.fcmToken}; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/models/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/models/index.dart new file mode 100644 index 0000000..c8ac690 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/models/index.dart @@ -0,0 +1 @@ +export 'device_token_models.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/services/index.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/services/index.dart new file mode 100644 index 0000000..78c2c15 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/services/index.dart @@ -0,0 +1,2 @@ +export 'notification_permission_manager.dart'; +export 'notification_token_manager.dart'; diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/services/notification_permission_manager.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/services/notification_permission_manager.dart new file mode 100644 index 0000000..3c5cd20 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/services/notification_permission_manager.dart @@ -0,0 +1,45 @@ +import 'package:core/logger/logger.dart'; +import 'package:di/di.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:network/network.dart'; +import 'package:notifications/src/exceptions/notification_exceptions.dart'; + +abstract class NotificationPermissionManager { + Future requestPermissions(); +} + +class FirebasePermissionManager implements NotificationPermissionManager { + static const String _tag = 'FirebasePermissionManager'; + + final Logger _logger; + final FirebaseMessaging _firebaseMessaging; + + FirebasePermissionManager({ + Logger? logger, + FirebaseMessaging? firebaseMessaging, + }) : _logger = logger ?? di.get(), + _firebaseMessaging = firebaseMessaging ?? FirebaseMessaging.instance; + + @override + Future requestPermissions() async { + try { + _logger.i('$_tag: Requesting notification permissions'); + final authSettings = await _firebaseMessaging.requestPermission(); + + final isGranted = + authSettings.authorizationStatus == AuthorizationStatus.authorized || + authSettings.authorizationStatus == AuthorizationStatus.provisional; + + _logger.i( + '$_tag: Notification permissions requested ' + 'with status: ${authSettings.authorizationStatus}, ' + 'granted: $isGranted', + ); + + return isGranted; + } catch (e, s) { + _logger.e('$_tag: Error requesting permissions', e, s); + throw NotificationException(message: e.message()); + } + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/services/notification_token_manager.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/services/notification_token_manager.dart new file mode 100644 index 0000000..fc10913 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/services/notification_token_manager.dart @@ -0,0 +1,103 @@ +import 'dart:io'; + +import 'package:core/logger/logger.dart'; +import 'package:di/di.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:network/network.dart'; +import 'package:notifications/notifications.dart'; + +abstract class NotificationTokenManager { + Future getFCMToken(); + + Future registerToken(String fcmToken); + + Future unRegisterToken(String deviceId); + + Future handleTokenRefresh(String newToken); +} + +class FirebaseTokenManager implements NotificationTokenManager { + static const String _tag = 'FirebaseTokenManager'; + + final Logger _logger; + final NetworkClient _networkClient; + final DeviceInfo _deviceInfo; + final FirebaseMessaging _firebaseMessaging; + + FirebaseTokenManager({ + Logger? logger, + NetworkClient? networkClient, + DeviceInfo? deviceInfo, + FirebaseMessaging? firebaseMessaging, + }) : _logger = logger ?? di.get(), + _networkClient = networkClient ?? di.get(), + _deviceInfo = deviceInfo ?? di.get(), + _firebaseMessaging = firebaseMessaging ?? FirebaseMessaging.instance; + + @override + Future getFCMToken() async { + try { + final token = await _firebaseMessaging.getToken(); + if (token != null) { + _logger.i('$_tag: FCM Token obtained successfully'); + } else { + _logger.w('$_tag: Failed to obtain FCM token'); + } + return token; + } catch (e, s) { + _logger.e('$_tag: Error getting FCM token', e, s); + rethrow; + } + } + + @override + Future registerToken(String fcmToken) async { + try { + _logger.i('$_tag: Registering FCM token with backend'); + + final deviceId = await _deviceInfo.generateDeviceId(); + final deviceName = await _deviceInfo.getDeviceName(); + final deviceType = Platform.isIOS ? 'ios' : 'android'; + + final request = DeviceTokenRequest( + fcmToken: fcmToken, + deviceId: deviceId, + deviceName: deviceName, + deviceType: deviceType, + ); + + await _networkClient.post>( + '/device-tokens/me', + data: request.toJson(), + fromJsonT: (json) => json as Map, + ); + _logger.i('$_tag: FCM token registered successfully'); + return deviceId; + } catch (e, s) { + _logger.e('$_tag: Error registering FCM token', e, s); + rethrow; + } + } + + @override + Future unRegisterToken(String deviceId) async { + try { + _logger.i('$_tag: Removing FCM token from backend'); + + await _networkClient.delete>( + '/device-tokens/me/$deviceId', + fromJsonT: (json) => json as Map, + ); + _logger.i('$_tag: FCM token removed successfully'); + } catch (e, s) { + _logger.e('$_tag: Error removing FCM token', e, s); + rethrow; + } + } + + @override + Future handleTokenRefresh(String newToken) async { + _logger.i('$_tag: FCM token refreshed'); + await registerToken(newToken); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/pubspec.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/pubspec.yaml new file mode 100644 index 0000000..e555076 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/pubspec.yaml @@ -0,0 +1,41 @@ +name: notifications +description: "Firebase Cloud Messaging, permissions and device-token management for {{name.titleCase()}}." +version: 0.1.0 +publish_to: 'none' + +environment: + sdk: ^3.8.0 + flutter: ">=3.29.0" + +dependencies: + flutter: + sdk: flutter + app_badge_plus: ^1.1.2 + crypto: ^3.0.6 + device_info_plus: ^11.1.1 + firebase_core: ^3.14.0 + firebase_messaging: ^15.1.6 + go_router: ^15.1.3 + json_annotation: ^4.9.0 + permission_handler: ^11.1.0 + timezone: ^0.9.4 + core: + path: ../core + design_system: + path: ../design_system + di: + path: ../di +{{#network}} network: + path: ../network +{{/network}}{{#analytics}} analytics: + path: ../analytics +{{/analytics}} +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + build_runner: ^2.4.15 + json_serializable: ^6.9.5 + +flutter: + uses-material-design: true diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/plugins/README.md b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/plugins/README.md index d1f52ac..38747c9 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/plugins/README.md +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/plugins/README.md @@ -1 +1,7 @@ -# Put all your plugins in this folder +# Plugins + +Platform channels and native integrations. + +```sh +nonstop create plugin my_plugin -o plugins +``` diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/pubspec.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/pubspec.yaml index 6211a92..07334b7 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/pubspec.yaml +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/pubspec.yaml @@ -1,8 +1,9 @@ name: {{ name.snakeCase() }}_workspace -description: {{ description }} +description: "{{{description}}}" +publish_to: 'none' environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.8.0 <4.0.0' dev_dependencies: flutter_lints: ^5.0.0 diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/brick.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/brick.yaml index 6b5f65d..439affd 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/brick.yaml +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/brick.yaml @@ -1,9 +1,9 @@ name: flutter_project_with_mono_repo -description: A Flutter project within a Melos-managed mono-repo. +description: A production-shaped Flutter mono-repo - app shell, design system, localization and the platform modules you pick. -repository: https://github.com/nonstopio/flutter_forge/tree/main/plugins/nonstop_cli +repository: https://github.com/nonstopio/flutter_forge/tree/main/packages/nonstop_cli -version: 1.0.0 +version: 2.0.0 environment: mason: ">=0.1.0-dev.52 <0.1.0" @@ -22,3 +22,48 @@ vars: description: Organization name prompt: What is your organization name? default: com.example + network: + type: boolean + description: Dio-based HTTP client with auth and logging interceptors + prompt: Include the network package? + default: true + auth: + type: boolean + description: Firebase Authentication with email, Google and Apple sign-in + prompt: Include authentication? + default: true + firestore: + type: boolean + description: Cloud Firestore and Cloud Functions wiring + prompt: Include Cloud Firestore? + default: false + notifications: + type: boolean + description: Firebase Cloud Messaging, permissions and token registration + prompt: Include push notifications? + default: true + analytics: + type: boolean + description: Event tracking behind a swappable client + prompt: Include analytics? + default: true + crashlytics: + type: boolean + description: Crash and non-fatal error reporting + prompt: Include crash reporting? + default: true + feature_flags: + type: boolean + description: Firebase Remote Config feature flags + prompt: Include feature flags? + default: true + developer: + type: boolean + description: In-app developer tools behind a hidden gesture + prompt: Include developer tools? + default: true + dashboard: + type: boolean + description: Bottom-navigation shell with starter tabs + prompt: Include the dashboard shell? + default: true diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/checked_process.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/checked_process.dart new file mode 100644 index 0000000..d787477 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/checked_process.dart @@ -0,0 +1,51 @@ +import 'dart:io'; + +import 'package:mason/mason.dart'; + +/// Runs [executable] and throws when it fails. +/// +/// `cli_core`'s `trackOperation` reports success as soon as the future +/// completes, so a non-zero exit code shows a green tick and generation +/// carries on to produce a half-built project. Every step here is load-bearing +/// - if `flutter create` or `melos bootstrap` fails there is nothing worth +/// continuing for - so the exit code is checked and the child's stderr is +/// surfaced. +Future runChecked( + HookContext context, { + required String startMessage, + required String endMessage, + required String executable, + required List arguments, + String? workingDirectory, +}) async { + final progress = context.logger.progress(startMessage); + + final ProcessResult result; + try { + result = await Process.run( + executable, + arguments, + workingDirectory: workingDirectory, + runInShell: true, + ); + } catch (error) { + progress.fail('$startMessage failed: $error'); + rethrow; + } + + if (result.exitCode != 0) { + progress.fail('$startMessage failed (exit ${result.exitCode})'); + final details = '${result.stderr}'.trim().isNotEmpty + ? '${result.stderr}'.trim() + : '${result.stdout}'.trim(); + if (details.isNotEmpty) context.logger.err(details); + throw ProcessException( + executable, + arguments, + 'exited with ${result.exitCode}', + result.exitCode, + ); + } + + progress.complete(endMessage); +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/flutter_create_command.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/flutter_create_command.dart index 267a00b..7219430 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/flutter_create_command.dart +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/flutter_create_command.dart @@ -1,29 +1,54 @@ import 'dart:async'; +import 'dart:io'; -import 'package:cli_core/cli_core.dart' show BaseFlutterCommand; +import 'package:cli_core/cli_core.dart' show CliCommand, FileUtils; import 'package:mason/mason.dart'; import 'package:path/path.dart' as p; -final class FlutterCreateCommand extends BaseFlutterCommand { +import 'checked_process.dart'; + +/// Scaffolds the application with `flutter create`. +/// +/// This runs in pre-gen, so the brick's own `lib/` and `pubspec.yaml` are +/// written over the counter-app that `flutter create` produces. +final class FlutterCreateCommand extends CliCommand { @override Future run(HookContext context) async { final String name = context.vars['name']; final String description = context.vars['description']; final String orgName = context.vars['org_name'] ?? 'com.example'; final appName = name.snakeCase; - final outputPath = p.normalize('$appName/apps'); - - await createFlutterProject( - context: context, - name: appName, - description: description, - outputPath: outputPath, - orgName: orgName, - ); + final appsPath = p.normalize('$appName/apps'); + + await FileUtils.ensureDirectory(appsPath); - await removeAnalysisOptions( - context: context, - projectPath: p.join(outputPath, appName), + await runChecked( + context, + startMessage: 'Creating Flutter app: $appName', + endMessage: 'Flutter app created', + executable: 'flutter', + arguments: [ + 'create', + appName, + '--template=app', + '--platforms=ios,android,web', + '--description=$description', + '--org=$orgName', + ], + workingDirectory: appsPath, ); + + final appPath = p.join(appsPath, appName); + + // The workspace owns lint configuration. + await FileUtils.deleteFile(p.join(appPath, 'analysis_options.yaml')); + + // `flutter create` leaves a widget test for the counter app it just wrote; + // the brick replaces that app, so the test would not compile. + await FileUtils.deleteFile(p.join(appPath, 'test', 'widget_test.dart')); + final testDir = Directory(p.join(appPath, 'test')); + if (testDir.existsSync() && testDir.listSync().isEmpty) { + await testDir.delete(); + } } } diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/format_command.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/format_command.dart new file mode 100644 index 0000000..ad82202 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/format_command.dart @@ -0,0 +1,25 @@ +import 'package:cli_core/cli_core.dart' show CliCommand; +import 'package:mason/mason.dart'; + +import 'checked_process.dart'; + +/// Formats the generated workspace. +/// +/// Templates are rendered from mustache, so the exact line breaks depend on +/// which modules were selected. Running the formatter once here means a fresh +/// project passes its own `melos lint` whatever the answers were. +final class FormatCommand extends CliCommand { + @override + Future run(HookContext context) async { + final String name = context.vars['name']; + + await runChecked( + context, + startMessage: 'Formatting generated code', + endMessage: 'Code formatted', + executable: 'dart', + arguments: ['format', '.'], + workingDirectory: name.snakeCase, + ); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/melos_command.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/melos_command.dart index 90bfb21..15ad78a 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/melos_command.dart +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/melos_command.dart @@ -1,28 +1,29 @@ -import 'dart:io'; - -import 'package:cli_core/cli_core.dart' show BaseMelosCommand; +import 'package:cli_core/cli_core.dart' show CliCommand; import 'package:mason/mason.dart'; -final class MelosCommand extends BaseMelosCommand { +import 'checked_process.dart'; + +final class MelosCommand extends CliCommand { @override Future run(HookContext context) async { final String name = context.vars['name']; final appName = name.snakeCase; - await trackOperation( + await runChecked( context, startMessage: 'Activating Melos globally', endMessage: 'Melos activated globally', - operation: () => Process.run( - 'dart', - ['pub', 'global', 'activate', 'melos'], - runInShell: true, - ), + executable: 'dart', + arguments: ['pub', 'global', 'activate', 'melos'], ); - await bootstrap( - context: context, - workspacePath: appName, + await runChecked( + context, + startMessage: 'Running melos bootstrap', + endMessage: 'Dependencies installed', + executable: 'melos', + arguments: ['bootstrap'], + workingDirectory: appName, ); } } diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/module_vars.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/module_vars.dart new file mode 100644 index 0000000..b152038 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/module_vars.dart @@ -0,0 +1,54 @@ +import 'package:mason/mason.dart'; + +/// Optional modules, each of which maps to a directory in the brick. +/// +/// A directory named `{{_dir}}` renders to an empty path segment when +/// the module is off, and mason skips every file underneath it. +const optionalModules = [ + 'network', + 'notifications', + 'analytics', + 'crashlytics', + 'feature_flags', + 'developer', + 'auth', + 'dashboard', +]; + +/// Modules that cannot work without `Firebase.initializeApp`. +const _firebaseBackedModules = [ + 'notifications', + 'analytics', + 'crashlytics', + 'feature_flags', + 'auth', + 'firestore', +]; + +bool _flag(HookContext context, String key) => context.vars[key] == true; + +/// Normalises the module answers and derives everything the templates read. +void resolveModuleVars(HookContext context) { + // A module can be implied by another (dev tools read feature flags), so + // resolve implications before deriving anything from them. `nonstop create` + // already does this in lib/commands/create/modules.dart; it is repeated here + // so `mason make` on its own still produces a project that compiles. + if (_flag(context, 'developer')) context.vars['feature_flags'] = true; + if (_flag(context, 'notifications')) context.vars['network'] = true; + + for (final module in optionalModules) { + final enabled = _flag(context, module); + context.vars[module] = enabled; + context.vars['${module}_dir'] = enabled ? module : ''; + } + + context.vars['firestore'] = _flag(context, 'firestore'); + final usesFirebase = + _firebaseBackedModules.any((module) => _flag(context, module)); + context.vars['firebase'] = usesFirebase; + + // The boot test runs the real bootstrap, which a Firebase project cannot do + // until `flutterfire configure` has been run. An empty file name makes mason + // skip the file, so those projects simply ship without it. + context.vars['smoke_test_file'] = usesFirebase ? '' : 'smoke_test.dart'; +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/next_steps_command.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/next_steps_command.dart new file mode 100644 index 0000000..5fe5092 --- /dev/null +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/commands/next_steps_command.dart @@ -0,0 +1,28 @@ +import 'package:cli_core/cli_core.dart' show CliCommand; +import 'package:mason/mason.dart'; + +/// Prints the handful of things the generator cannot do for you. +final class NextStepsCommand extends CliCommand { + @override + Future run(HookContext context) async { + final String name = context.vars['name']; + final appName = name.snakeCase; + final logger = context.logger; + + if (context.vars['firebase'] == true) { + logger + ..info('') + ..warn('Firebase modules are enabled but not configured yet.') + ..info(' dart pub global activate flutterfire_cli') + ..info(' cd $appName/apps/$appName && flutterfire configure') + ..info( + ' (until then lib/firebase_options.dart throws on purpose)', + ); + } + + logger + ..info('') + ..info('Run the app:') + ..info(' cd $appName/apps/$appName && flutter run'); + } +} diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/post_gen.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/post_gen.dart index 8008827..2db5689 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/post_gen.dart +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/post_gen.dart @@ -2,13 +2,15 @@ import 'dart:async'; import 'package:mason/mason.dart'; -import 'commands/flutter_create_command.dart'; +import 'commands/format_command.dart'; import 'commands/melos_command.dart'; +import 'commands/next_steps_command.dart'; Future run(HookContext context) async { final commands = [ - FlutterCreateCommand(), + FormatCommand(), MelosCommand(), + NextStepsCommand(), ]; for (final command in commands) { diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/pre_gen.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/pre_gen.dart index 85edb08..a85eaba 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/pre_gen.dart +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/hooks/pre_gen.dart @@ -3,7 +3,17 @@ import 'dart:async'; import 'package:cli_core/cli_core.dart'; import 'package:mason/mason.dart'; +import 'commands/flutter_create_command.dart'; +import 'commands/module_vars.dart'; + Future run(HookContext context) async { - // Check if we're in a mono repo using the shared utility context.vars['is_mono_repo'] = await FileUtils.isMonoRepo(); + + // Turn the module answers into the `*_dir` path variables the brick uses to + // include or skip whole directories, and derive `firebase`. + resolveModuleVars(context); + + // The app is scaffolded before generation so the brick's `lib/` and + // `pubspec.yaml` land on top of what `flutter create` produced. + await FlutterCreateCommand().run(context); } From 42269f7656e02cf05db3bbda5f9c80ca0ae880e6 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Date: Thu, 3 Sep 2026 19:15:30 -0500 Subject: [PATCH 3/7] feat(nonstop_cli): ask which modules a new project needs `create` took a name, a description and an org. The brick now has nine optional modules behind it, so it asks - one multi-select, defaults pre-ticked - and prompts for the description and org name when they were not passed. Explicit `--network` / `--no-network` style flags always win, `-y` takes the recommended set, and a missing or undrivable terminal falls back to defaults so scripts and CI keep working. Both ends have to be a terminal: mason_logger reads stdin but draws on stdout, and piping either one breaks it. Two module dependencies are applied and reported rather than left to fail at compile time: notifications needs network, developer tools need feature flags. The brick's hooks apply the same rules, so `mason make` on its own still produces something that builds. The description is validated like the name and org already were. It is written into YAML, Dart string literals and Markdown, and a quote or a dollar sign silently produced a project that does not parse. Co-Authored-By: Claude Opus 5 (1M context) --- packages/nonstop_cli/README.md | 55 ++++++- .../lib/commands/create/command.dart | 146 +++++++++++++++++- .../lib/commands/create/modules.dart | 111 +++++++++++++ .../src/commands/create_command_test.dart | 73 +++++++++ 4 files changed, 380 insertions(+), 5 deletions(-) create mode 100644 packages/nonstop_cli/lib/commands/create/modules.dart create mode 100644 packages/nonstop_cli/test/src/commands/create_command_test.dart diff --git a/packages/nonstop_cli/README.md b/packages/nonstop_cli/README.md index 5eb87ef..b66e0b6 100644 --- a/packages/nonstop_cli/README.md +++ b/packages/nonstop_cli/README.md @@ -68,6 +68,7 @@ nonstop create [arguments] | `--output-directory` | `-o` | Output directory for the new project | Current directory | | `--description` | `--desc` | Description for the new project | "A Melos-managed project for mono-repo, created using NonStop CLI." | | `--org-name` | `--org` | Organization name for the new project | `com.example` | +| `--defaults` | `-y` | Skip the module picker and take the recommended set | Prompt | #### 📦 Template Options @@ -75,7 +76,7 @@ nonstop create [arguments] | Template | 🎯 Description | 📁 Structure Created | |-----------|------------------------------------------------------------------|-------------------------------------------------------------------------| -| `mono` | 🏢 Generate a Flutter application along with mono-repo (default) | Complete mono-repo structure with apps, features, packages, and plugins | +| `mono` | 🏢 Generate a full app skeleton in a mono-repo (default) | App shell, design system, localization and the modules you pick | | `package` | 📦 Generate a Flutter package for a Melos-managed mono-repo | Flutter package compatible with mono-repo structure | | `app` | 📱 Generate a Flutter application for a Melos-managed mono-repo | Flutter application configured for mono-repo structure | | `plugin` | 🔌 Generate a Flutter plugin for a Melos-managed mono-repo | Flutter plugin compatible with mono-repo structure | @@ -84,6 +85,38 @@ nonstop create [arguments] #### 💡 Examples +#### 🧩 Modules + +`mono` does not stop at empty folders. It asks which platform modules the app +needs and generates them wired together - bootstrap order, DI registration, +route table and all. + +``` +? Which modules should youtube start with? +❯ ◉ Network - Dio client, auth + logging interceptors, typed errors + ◉ Authentication - Firebase Auth (email, Google, Apple) with route guards + ◯ Cloud Firestore - Firestore + Cloud Functions wiring and timestamp converters + ◉ Push notifications + ◉ Analytics + ◉ Crash reporting + ◉ Feature flags + ◉ Developer tools + ◉ Dashboard shell +``` + +Answer non-interactively with `-y` (recommended set) or per module: + +```bash +nonstop create youtube -y --no-notifications --firestore +``` + +Two rules are applied for you, because the code will not compile otherwise: +push notifications pull in `network`, and developer tools pull in +`feature_flags`. + +Every project also gets `core`, `di`, `design_system`, `localization` and +`utils` - the pieces every app needs. +
🏢 Create a mono-repo project @@ -96,9 +129,22 @@ This creates the following structure: ``` youtube/ ├── 📱 apps/ -│ └── youtube/ +│ └── youtube/ # bootstrap, router, splash - no business logic ├── 🧩 features/ +│ ├── auth/ # Firebase Auth + route guards +│ └── dashboard/ # bottom-navigation shell ├── 📦 packages/ +│ ├── core/ # logger, DI bootstrap, observers, error types +│ ├── design_system/ # Material 3 theme, components, toasts +│ ├── di/ # service locator +│ ├── localization/ # type-safe strings (i69n) +│ ├── network/ # Dio client and interceptors +│ ├── analytics/ +│ ├── crashlytics/ +│ ├── feature_flags/ +│ ├── notifications/ +│ ├── developer/ +│ └── utils/ ├── 🔌 plugins/ ├── 📋 analysis_options.yaml ├── 📖 README.md @@ -106,6 +152,11 @@ youtube/ └── 📄 pubspec.yaml ``` +The generated workspace is bootstrapped, formatted and passes its own +`melos lint` and `melos test` before you touch it. If you picked any Firebase +module, run `flutterfire configure` in the app directory - the generated +`firebase_options.dart` throws until you do. +
diff --git a/packages/nonstop_cli/lib/commands/create/command.dart b/packages/nonstop_cli/lib/commands/create/command.dart index df63d75..d175c8d 100644 --- a/packages/nonstop_cli/lib/commands/create/command.dart +++ b/packages/nonstop_cli/lib/commands/create/command.dart @@ -5,12 +5,17 @@ import 'package:args/args.dart'; import 'package:args/command_runner.dart'; import 'package:mason/mason.dart'; import 'package:meta/meta.dart'; +import 'package:nonstop_cli/commands/create/modules.dart'; import 'package:nonstop_cli/commands/create/templates.dart'; import 'package:nonstop_cli/template.dart'; import 'package:nonstop_cli/utils/utils.dart'; import 'package:path/path.dart' as path; const _defaultOrgName = 'com.example'; + +/// Characters that cannot survive being written into a double-quoted YAML +/// scalar and a Dart string literal unchanged. +const _unsafeDescriptionCharacters = ['"', r'\', r'$']; const _defaultDescription = 'A Melos-managed project for mono-repo, created using NonStop CLI.'; @@ -54,7 +59,22 @@ class CreateCommand extends Command { 'Generate a Flutter application for a Melos-managed mono-repo.', 'plugin': 'Generate a Flutter plugin for a Melos-managed mono-repo.', }, + ) + ..addFlag( + 'defaults', + abbr: 'y', + negatable: false, + help: 'Skip the module picker and accept the recommended selection.', ); + + for (final module in templateModules) { + argParser.addFlag( + module.flag, + help: '${module.description}.', + defaultsTo: null, + hide: true, + ); + } } final Logger logger; @@ -104,7 +124,53 @@ class CreateCommand extends Command { return args.first; } - String get projectDescription => argResults['description'] as String? ?? ''; + String get projectDescription { + final description = _askIfUnset( + 'description', + 'Describe ${lightCyan.wrap(projectName)}:', + ); + _validateDescription(description); + return description; + } + + /// The description is interpolated into YAML, Dart string literals and + /// Markdown, so a few characters would produce a project that does not + /// parse. Rejecting them beats silently mangling the text. + void _validateDescription(String description) { + final offending = + _unsafeDescriptionCharacters.where(description.contains).toList(); + if (offending.isEmpty) return; + + usageException( + 'The description cannot contain ${offending.map((c) => '"$c"').join(', ')}.\n\n' + 'It is written into pubspec.yaml, Dart string literals and README.md, ' + 'and these characters break at least one of them.', + ); + } + + /// Whether this process can hold an interactive conversation. + /// + /// Both ends have to be a terminal: mason_logger reads from stdin but draws + /// the prompt on stdout, and piping either one is enough to break it. + bool get _canPrompt => + argResults['defaults'] != true && stdin.hasTerminal && stdout.hasTerminal; + + /// Returns [option], prompting for it when the user did not pass it and + /// there is a terminal to ask on. + /// + /// Falling back to the option's default keeps scripted and CI runs working. + String _askIfUnset(String option, String question) { + final fallback = argResults[option] as String? ?? ''; + if (argResults.wasParsed(option) || !_canPrompt) return fallback; + try { + final answer = logger.prompt(question, defaultValue: fallback).trim(); + return answer.isEmpty ? fallback : answer; + } catch (error) { + // Some CI runners advertise a terminal they cannot actually drive. + logger.detail('Prompt unavailable, using default: $error'); + return fallback; + } + } @override String get invocation => 'nonstop $name [arguments]'; @@ -179,7 +245,14 @@ class CreateCommand extends Command { logger: logger, ); - final _ = await generator.generate(target, vars: vars, logger: logger); + // The app skeleton is scaffolded by `flutter create` in the pre-gen hook, + // so the brick's own `lib/` and `pubspec.yaml` are expected to land on top. + final _ = await generator.generate( + target, + vars: vars, + logger: logger, + fileConflictResolution: FileConflictResolution.overwrite, + ); await generator.hooks.postGen( vars: vars, @@ -204,13 +277,80 @@ class CreateCommand extends Command { 'name': projectName, 'description': projectDescription, 'org_name': orgName, + if (argResults['template'] == 'mono') ...selectedModules(), + }; + } + + /// Resolves which optional modules the generated mono-repo should contain. + /// + /// Explicit `--network` / `--no-network` style flags always win. Whatever is + /// left is asked for once, as a single multi-select, unless `--defaults` was + /// passed or there is no terminal to ask on. + @visibleForTesting + Map selectedModules() { + final answered = { + for (final module in templateModules) + if (argResults.wasParsed(module.flag)) + module.key: argResults[module.flag] as bool, + }; + + final shouldPrompt = answered.length < templateModules.length && _canPrompt; + + final selection = shouldPrompt ? _promptForModules(answered) : answered; + + final requested = { + for (final module in templateModules) + module.key: selection[module.key] ?? module.defaultValue, + }; + final resolved = resolveModuleImplications(requested); + + for (final module in templateModules) { + if (resolved[module.key]! && !requested[module.key]!) { + logger.info( + '${lightYellow.wrap('+')} ${module.label} is required by ' + '${module.impliedBy.where((o) => resolved[o]!).join(', ')}, ' + 'adding it.', + ); + } + } + + return resolved; + } + + Map _promptForModules(Map answered) { + final pending = templateModules + .where((module) => !answered.containsKey(module.key)) + .toList(); + + logger.info(''); + final List chosen; + try { + chosen = logger.chooseAny( + 'Which modules should ${lightCyan.wrap(projectName)} start with?', + choices: pending, + defaultValues: pending.where((module) => module.defaultValue).toList(), + display: (module) => + '${module.label} ${darkGray.wrap('- ${module.description}')}', + ); + } catch (error) { + // Some CI runners advertise a terminal they cannot actually drive. + logger.detail('Module picker unavailable, using defaults: $error'); + return answered; + } + + return { + ...answered, + for (final module in pending) module.key: chosen.contains(module), }; } } extension on CreateCommand { String get orgName { - final orgName = argResults['org-name'] as String? ?? _defaultOrgName; + final orgName = _askIfUnset( + 'org-name', + 'What is the organization for ${lightCyan.wrap(projectName)}?', + ); _validateOrgName(orgName); return orgName; } diff --git a/packages/nonstop_cli/lib/commands/create/modules.dart b/packages/nonstop_cli/lib/commands/create/modules.dart new file mode 100644 index 0000000..7290f08 --- /dev/null +++ b/packages/nonstop_cli/lib/commands/create/modules.dart @@ -0,0 +1,111 @@ +/// An optional module the mono-repo template can generate. +/// +/// Each one maps to a boolean brick variable of the same [key]; the brick uses +/// it to include or skip a whole package directory and the code that wires it +/// into the app shell. +class TemplateModule { + const TemplateModule({ + required this.key, + required this.label, + required this.description, + required this.defaultValue, + this.impliedBy = const [], + }); + + /// Brick variable name, e.g. `network`. + final String key; + + /// Short name shown in the picker. + final String label; + + /// One line explaining what the user gets. + final String description; + + /// Whether the module is pre-selected. + final bool defaultValue; + + /// Modules that cannot compile without this one. + final List impliedBy; + + /// CLI flag: `--network` / `--no-network`. + String get flag => key.replaceAll('_', '-'); + + @override + String toString() => '$label — $description'; +} + +/// The modules offered when creating a mono-repo. +/// +/// Order is the order they are presented in. +const templateModules = [ + TemplateModule( + key: 'network', + label: 'Network', + description: 'Dio client, auth + logging interceptors, typed errors', + defaultValue: true, + impliedBy: ['notifications'], + ), + TemplateModule( + key: 'auth', + label: 'Authentication', + description: 'Firebase Auth (email, Google, Apple) with route guards', + defaultValue: true, + ), + TemplateModule( + key: 'firestore', + label: 'Cloud Firestore', + description: 'Firestore + Cloud Functions wiring and timestamp converters', + defaultValue: false, + ), + TemplateModule( + key: 'notifications', + label: 'Push notifications', + description: 'FCM, permission handling, device-token registration', + defaultValue: true, + ), + TemplateModule( + key: 'analytics', + label: 'Analytics', + description: 'Event tracking behind a swappable client', + defaultValue: true, + ), + TemplateModule( + key: 'crashlytics', + label: 'Crash reporting', + description: 'Crashlytics for fatals and handled errors', + defaultValue: true, + ), + TemplateModule( + key: 'feature_flags', + label: 'Feature flags', + description: 'Remote Config flags with a widget wrapper', + defaultValue: true, + impliedBy: ['developer'], + ), + TemplateModule( + key: 'developer', + label: 'Developer tools', + description: 'In-app logs and flag inspector behind a hidden gesture', + defaultValue: true, + ), + TemplateModule( + key: 'dashboard', + label: 'Dashboard shell', + description: 'Bottom navigation with starter tabs and a profile tab', + defaultValue: true, + ), +]; + +/// Applies the "module A needs module B" rules to a selection. +/// +/// The brick's `hooks/commands/module_vars.dart` applies the same rules, so a +/// project generated with plain `mason make` still compiles. Change both. +Map resolveModuleImplications(Map selection) { + final resolved = Map.from(selection); + for (final module in templateModules) { + if (module.impliedBy.any((other) => resolved[other] ?? false)) { + resolved[module.key] = true; + } + } + return resolved; +} diff --git a/packages/nonstop_cli/test/src/commands/create_command_test.dart b/packages/nonstop_cli/test/src/commands/create_command_test.dart new file mode 100644 index 0000000..ef0ace2 --- /dev/null +++ b/packages/nonstop_cli/test/src/commands/create_command_test.dart @@ -0,0 +1,73 @@ +import 'package:mason_logger/mason_logger.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:nonstop_cli/commands/commands.dart'; +import 'package:nonstop_cli/commands/create/modules.dart'; +import 'package:test/test.dart'; + +class _MockLogger extends Mock implements Logger {} + +/// Parses [args] through the real `create` parser so the tests exercise the +/// same flag definitions users type. +Map modulesFor(List args) { + final command = CreateCommand(logger: _MockLogger()) + ..argResultOverrides = null; + command.argResultOverrides = command.argParser.parse(args); + return command.selectedModules(); +} + +void main() { + group('create module selection', () { + test('falls back to the recommended set with --defaults', () { + final modules = modulesFor(['--defaults', 'my_app']); + + for (final module in templateModules) { + expect( + modules[module.key], + module.defaultValue, + reason: module.key, + ); + } + }); + + test('explicit flags win over the defaults', () { + final modules = modulesFor([ + '--defaults', + '--no-network', + '--no-notifications', + '--firestore', + 'my_app', + ]); + + expect(modules['network'], isFalse); + expect(modules['firestore'], isTrue); + }); + + test('notifications pull in the network package they post tokens to', () { + final modules = modulesFor(['--defaults', '--no-network', 'my_app']); + + expect(modules['notifications'], isTrue); + // notifications imports package:network, so it cannot be dropped. + expect(modules['network'], isTrue); + }); + + test('developer tools pull in feature flags', () { + final modules = modulesFor([ + '--defaults', + '--developer', + '--no-feature-flags', + 'my_app', + ]); + + expect(modules['feature_flags'], isTrue); + }); + + test('every module can be switched off together', () { + final modules = modulesFor([ + for (final module in templateModules) '--no-${module.flag}', + 'my_app', + ]); + + expect(modules.values, everyElement(isFalse)); + }); + }); +} From dff35188fcc78a95ed227f4f5165da6f198ab4fd Mon Sep 17 00:00:00 2001 From: Ajay Kumar Date: Thu, 3 Sep 2026 19:15:39 -0500 Subject: [PATCH 4/7] chore(nonstop_cli): update flutter_project_with_mono_repo bundle Regenerated with `mason bundle` so the CLI ships the new template. The registry lookup for the brick is version-pinned and the brick is now 2.0.0, so this bundle is what `nonstop create` actually uses. Co-Authored-By: Claude Opus 5 (1M context) --- ...flutter_project_with_mono_repo_bundle.dart | 1466 ++++++++++++++++- 1 file changed, 1447 insertions(+), 19 deletions(-) diff --git a/packages/nonstop_cli/lib/commands/create/flutter_project_with_mono_repo_bundle.dart b/packages/nonstop_cli/lib/commands/create/flutter_project_with_mono_repo_bundle.dart index 758898c..23d7c76 100644 --- a/packages/nonstop_cli/lib/commands/create/flutter_project_with_mono_repo_bundle.dart +++ b/packages/nonstop_cli/lib/commands/create/flutter_project_with_mono_repo_bundle.dart @@ -8,78 +8,1449 @@ final flutterProjectWithMonoRepoBundle = MasonBundle.fromJson({ { "path": "{{ name.snakeCase() }}/.gitignore", "data": - "Kiovbm9kZV9tb2R1bGVzCioubG9nCmJ1aWxkCmRpc3QKLnNhbml0eQouRFNfU3RvcmUKLmlkZWEKY292ZXJhZ2UKLmRhcnRfdG9vbApwdWJzcGVjX292ZXJyaWRlcy55YW1sCi5mbHV0dGVyLXBsdWdpbnMKLmZsdXR0ZXItcGx1Z2lucy1kZXBlbmRlbmNpZXMKKi5pbWwKKi5sb2NrCm1hc29uLWxvY2suanNvbgoubWFzb24K", + "Kiovbm9kZV9tb2R1bGVzCioubG9nCmJ1aWxkCmRpc3QKLnNhbml0eQouRFNfU3RvcmUKLmlkZWEKY292ZXJhZ2UKLmRhcnRfdG9vbApwdWJzcGVjX292ZXJyaWRlcy55YW1sCi5mbHV0dGVyLXBsdWdpbnMKLmZsdXR0ZXItcGx1Z2lucy1kZXBlbmRlbmNpZXMKKi5pbWwKKi5sb2NrCiMgQXBwcyBzaGlwIGEgYmluYXJ5OyBwaW4gdGhlaXIgcmVzb2x1dGlvbi4KIWFwcHMvKi9wdWJzcGVjLmxvY2sKbWFzb24tbG9jay5qc29uCi5tYXNvbgo=", "type": "text" }, { "path": "{{ name.snakeCase() }}/analysis_options.yaml", "data": - "aW5jbHVkZTogcGFja2FnZTpmbHV0dGVyX2xpbnRzL2ZsdXR0ZXIueWFtbAoKbGludGVyOgogIHJ1bGVzOgogICAgcmVxdWlyZV90cmFpbGluZ19jb21tYXM6IHRydWU=", + "aW5jbHVkZTogcGFja2FnZTpmbHV0dGVyX2xpbnRzL2ZsdXR0ZXIueWFtbAoKYW5hbHl6ZXI6CiAgZXhjbHVkZToKICAgICMgR2VuZXJhdGVkIGNvZGUgLSByZWdlbmVyYXRlIGl0LCBkbyBub3QgbGludCBpdC4KICAgIC0gIioqLyouZy5kYXJ0IgogICAgLSAiKiovKi5mcmVlemVkLmRhcnQiCiAgICAtICIqKi8qLmk2OW4uZGFydCIKICAgICMgQnVpbGQgb3V0cHV0IGFuZCB0aGUgcGx1Z2luIHNvdXJjZXMgQ29jb2FQb2RzIHN5bWxpbmtzIGludG8gdGhlIGFwcC4KICAgIC0gIioqL2J1aWxkLyoqIgogICAgLSAiKiovaW9zLy5zeW1saW5rcy8qKiIKICAgIC0gIioqL21hY29zLy5zeW1saW5rcy8qKiIKCmxpbnRlcjoKICBydWxlczoKICAgIHJlcXVpcmVfdHJhaWxpbmdfY29tbWFzOiB0cnVlCg==", "type": "text" }, { "path": "{{ name.snakeCase() }}/apps/README.md", - "data": "IyBQdXQgYWxsIHlvdXIgYXBwcyBpbiB0aGlzIGZvbGRlcgo=", + "data": + "IyBBcHBzCgpSdW5uYWJsZSBGbHV0dGVyIGFwcGxpY2F0aW9ucy4gQW4gYXBwIHdpcmVzIG1vZHVsZXMgdG9nZXRoZXIgaW4KYGxpYi9ib290c3RyYXAuZGFydGAgYW5kIGNvbXBvc2VzIGZlYXR1cmUgcm91dGVzIGluIGBsaWIvcm91dGVyL3JvdXRlci5kYXJ0YCAtCml0IHNob3VsZCBob2xkIGFsbW9zdCBubyBidXNpbmVzcyBsb2dpYyBvZiBpdHMgb3duLgoKQWRkIGFub3RoZXIgKGFuIGFkbWluIGJ1aWxkLCBhIHdoaXRlLWxhYmVsIHZhcmlhbnQpIHdpdGg6CgpgYGBzaApub25zdG9wIGNyZWF0ZSBhcHAgbXlfb3RoZXJfYXBwIC1vIGFwcHMKYGBgCg==", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/app.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNvcmUvY29yZS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRlc2lnbl9zeXN0ZW0vZGVzaWduX3N5c3RlbS5kYXJ0JzsKe3sjbm90aWZpY2F0aW9uc319aW1wb3J0ICdwYWNrYWdlOmRpL2RpLmRhcnQnOwp7ey9ub3RpZmljYXRpb25zfX1pbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlci9tYXRlcmlhbC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmxvY2FsaXphdGlvbi9sb2NhbGl6YXRpb24uZGFydCc7Cnt7I25vdGlmaWNhdGlvbnN9fWltcG9ydCAncGFja2FnZTpub3RpZmljYXRpb25zL25vdGlmaWNhdGlvbnMuZGFydCc7Cnt7L25vdGlmaWNhdGlvbnN9fWltcG9ydCAncGFja2FnZTp7e25hbWUuc25ha2VDYXNlKCl9fS9yb3V0ZXIvcm91dGVyLmRhcnQnOwoKY2xhc3MgQXBwIGV4dGVuZHMgU3RhdGVmdWxXaWRnZXQgewogIGNvbnN0IEFwcCh7c3VwZXIua2V5fSk7CgogIEBvdmVycmlkZQogIFN0YXRlPEFwcD4gY3JlYXRlU3RhdGUoKSA9PiBfQXBwU3RhdGUoKTsKfQoKY2xhc3MgX0FwcFN0YXRlIGV4dGVuZHMgU3RhdGU8QXBwPiB7Cnt7I25vdGlmaWNhdGlvbnN9fSAgbGF0ZSBmaW5hbCBBcHBMaWZlY3ljbGVMaXN0ZW5lciBfbGlmZWN5Y2xlTGlzdGVuZXI7Cgp7ey9ub3RpZmljYXRpb25zfX0gIEBvdmVycmlkZQogIHZvaWQgaW5pdFN0YXRlKCkgewogICAgc3VwZXIuaW5pdFN0YXRlKCk7Cnt7I25vdGlmaWNhdGlvbnN9fSAgICAvLyBBc2tpbmcgZm9yIG5vdGlmaWNhdGlvbiBwZXJtaXNzaW9uIG5lZWRzIGEgZm9yZWdyb3VuZCBzdXJmYWNlLCBzbyB0aGlzCiAgICAvLyBydW5zIGZyb20gdGhlIGZpcnN0IGZyYW1lIHJhdGhlciB0aGFuIGZyb20gYm9vdHN0cmFwLiBNb3ZlIGl0IGJlaGluZAogICAgLy8gc2lnbi1pbiBvbmNlIHlvdSB3b3VsZCByYXRoZXIgYXNrIGEgdXNlciB3aG8gaGFzIGNvbW1pdHRlZCB0byB0aGUgYXBwLgogICAgX2xpZmVjeWNsZUxpc3RlbmVyID0gQXBwTGlmZWN5Y2xlTGlzdGVuZXIob25SZXN1bWU6IF9jbGVhckJhZGdlKTsKICAgIFdpZGdldHNCaW5kaW5nLmluc3RhbmNlLmFkZFBvc3RGcmFtZUNhbGxiYWNrKChfKSBhc3luYyB7CiAgICAgIGZpbmFsIGxvZ2dlciA9IGRpLmdldDxMb2dnZXI+KCk7CiAgICAgIHRyeSB7CiAgICAgICAgYXdhaXQgZGkuZ2V0PE5vdGlmaWNhdGlvbkNsaWVudD4oKS5pbml0KCk7CiAgICAgIH0gY2F0Y2ggKGUsIHMpIHsKICAgICAgICBsb2dnZXIuZSgnRmFpbGVkIHRvIGluaXRpYWxpemUgTm90aWZpY2F0aW9uQ2xpZW50JywgZSwgcyk7CiAgICAgIH0KICAgIH0pOwp7ey9ub3RpZmljYXRpb25zfX0gIH0Ke3sjbm90aWZpY2F0aW9uc319CiAgdm9pZCBfY2xlYXJCYWRnZSgpIHsKICAgIGZpbmFsIGxvZ2dlciA9IGRpLmdldDxMb2dnZXI+KCk7CiAgICB0cnkgewogICAgICBkaS5nZXQ8Tm90aWZpY2F0aW9uQ2xpZW50PigpLmNsZWFyQmFkZ2UoKTsKICAgIH0gY2F0Y2ggKGUsIHMpIHsKICAgICAgbG9nZ2VyLmUoJ0ZhaWxlZCB0byBjbGVhciBiYWRnZSBvbiBhcHAgcmVzdW1lJywgZSwgcyk7CiAgICB9CiAgfQoKICBAb3ZlcnJpZGUKICB2b2lkIGRpc3Bvc2UoKSB7CiAgICBfbGlmZWN5Y2xlTGlzdGVuZXIuZGlzcG9zZSgpOwogICAgc3VwZXIuZGlzcG9zZSgpOwogIH0Ke3svbm90aWZpY2F0aW9uc319CiAgQG92ZXJyaWRlCiAgV2lkZ2V0IGJ1aWxkKEJ1aWxkQ29udGV4dCBjb250ZXh0KSB7CiAgICByZXR1cm4gR2xvYmFsRXZlbnRDaGFubmVsUHJvdmlkZXIoCiAgICAgIGNoaWxkOiBEZXNpZ25TeXN0ZW1XcmFwcGVyKAogICAgICAgIGJ1aWxkZXI6IChjb250ZXh0LCB0aGVtZSkgPT4gTWF0ZXJpYWxBcHAucm91dGVyKAogICAgICAgICAgZGVidWdTaG93Q2hlY2tlZE1vZGVCYW5uZXI6IGZhbHNlLAogICAgICAgICAgdGl0bGU6IHN0cmluZ3MuYXBwLm5hbWUsCiAgICAgICAgICB0aGVtZTogdGhlbWUsCiAgICAgICAgICByb3V0ZUluZm9ybWF0aW9uUGFyc2VyOiBBcHBSb3V0ZXIucm91dGVJbmZvcm1hdGlvblBhcnNlciwKICAgICAgICAgIHJvdXRlSW5mb3JtYXRpb25Qcm92aWRlcjogQXBwUm91dGVyLnJvdXRlSW5mb3JtYXRpb25Qcm92aWRlciwKICAgICAgICAgIHJvdXRlckRlbGVnYXRlOiBBcHBSb3V0ZXIucm91dGVyRGVsZWdhdGUsCiAgICAgICAgKSwKICAgICAgKSwKICAgICk7CiAgfQp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/bootstrap.dart", + "data": + "e3sjYW5hbHl0aWNzfX1pbXBvcnQgJ3BhY2thZ2U6YW5hbHl0aWNzL2FuYWx5dGljcy5kYXJ0JyBhcyBhbmFseXRpY3M7Cnt7L2FuYWx5dGljc319e3sjYXV0aH19aW1wb3J0ICdwYWNrYWdlOmF1dGgvYXV0aC5kYXJ0JyBhcyBhdXRoOwp7ey9hdXRofX1pbXBvcnQgJ3BhY2thZ2U6YmxvYy9ibG9jLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Y29yZS9jb3JlLmRhcnQnIGFzIGNvcmU7Cnt7I2ZpcmViYXNlfX1pbXBvcnQgJ3BhY2thZ2U6Y29yZS9kZXZlbG9wZXIvZW11bGF0b3JzLmRhcnQnIGFzIGVtdWxhdG9yczsKe3svZmlyZWJhc2V9fWltcG9ydCAncGFja2FnZTpjb3JlL2xvZ2dlci9sb2dnZXIuZGFydCc7Cnt7I2NyYXNobHl0aWNzfX1pbXBvcnQgJ3BhY2thZ2U6Y3Jhc2hseXRpY3MvY3Jhc2hseXRpY3MuZGFydCcgYXMgY3Jhc2hseXRpY3M7Cnt7L2NyYXNobHl0aWNzfX1pbXBvcnQgJ3BhY2thZ2U6ZGkvZGkuZGFydCc7Cnt7I2ZlYXR1cmVfZmxhZ3N9fWltcG9ydCAncGFja2FnZTpmZWF0dXJlX2ZsYWdzL2ZlYXR1cmVfZmxhZ3MuZGFydCcgYXMgZmVhdHVyZV9mbGFnczsKe3svZmVhdHVyZV9mbGFnc319e3sjZmlyZWJhc2V9fWltcG9ydCAncGFja2FnZTpmaXJlYmFzZV9jb3JlL2ZpcmViYXNlX2NvcmUuZGFydCc7Cnt7L2ZpcmViYXNlfX1pbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlci93aWRnZXRzLmRhcnQnOwp7eyNuZXR3b3JrfX1pbXBvcnQgJ3BhY2thZ2U6bmV0d29yay9uZXR3b3JrLmRhcnQnIGFzIG5ldHdvcms7Cnt7L25ldHdvcmt9fXt7I25vdGlmaWNhdGlvbnN9fWltcG9ydCAncGFja2FnZTpub3RpZmljYXRpb25zL25vdGlmaWNhdGlvbnMuZGFydCcgYXMgbm90aWZpY2F0aW9uczsKe3svbm90aWZpY2F0aW9uc319e3sjZmlyZWJhc2V9fWltcG9ydCAncGFja2FnZTp7e25hbWUuc25ha2VDYXNlKCl9fS9maXJlYmFzZV9vcHRpb25zLmRhcnQnOwp7ey9maXJlYmFzZX19Ci8vLyBCcmluZ3MgZXZlcnkgbW9kdWxlIHVwLCBpbiBkZXBlbmRlbmN5IG9yZGVyLCBiZWZvcmUgdGhlIGZpcnN0IGZyYW1lLgovLy8KLy8vIE9yZGVyIG1hdHRlcnM6IHRoZSBsb2dnZXIgaXMgcmVnaXN0ZXJlZCBmaXJzdCBzbyBldmVyeXRoaW5nIGFmdGVyIGl0IGNhbgovLy8gcmVwb3J0IGZhaWx1cmVzLCBhbmQgRmlyZWJhc2UgbXVzdCBiZSBsaXZlIGJlZm9yZSBhbnkgRmlyZWJhc2UtYmFja2VkCi8vLyBtb2R1bGUgaW5pdGlhbGlzZXMuCkZ1dHVyZTx2b2lkPiBpbml0KCkgYXN5bmMgewogIFdpZGdldHNGbHV0dGVyQmluZGluZy5lbnN1cmVJbml0aWFsaXplZCgpOwogIGZpbmFsIHN0b3B3YXRjaCA9IFN0b3B3YXRjaCgpLi5zdGFydCgpOwoKICBhd2FpdCBjb3JlLmluaXQoKTsKICBmaW5hbCBsb2dnZXIgPSBkaS5nZXQ8TG9nZ2VyPigpOwogIGxvZ2dlci5pKCdDb3JlIHJlYWR5IGluICR7c3RvcHdhdGNoLmVsYXBzZWRNaWxsaXNlY29uZHN9IG1zJyk7CgogIEJsb2Mub2JzZXJ2ZXIgPSBjb3JlLkNvcmVCbG9jT2JzZXJ2ZXIoKTsKe3sjZmlyZWJhc2V9fQogIGZpbmFsIG9wdGlvbnMgPSBEZWZhdWx0RmlyZWJhc2VPcHRpb25zLmN1cnJlbnRQbGF0Zm9ybTsKICBhd2FpdCBGaXJlYmFzZS5pbml0aWFsaXplQXBwKG9wdGlvbnM6IG9wdGlvbnMpOwoKICBpZiAoY29yZS5FbnZpcm9ubWVudC51c2VFbXVsYXRvcnMpIHsKICAgIGF3YWl0IGVtdWxhdG9ycy5pbml0KCk7CiAgfQp7ey9maXJlYmFzZX19Cnt7I2NyYXNobHl0aWNzfX0gIGF3YWl0IGNyYXNobHl0aWNzLmluaXQoKTsKe3svY3Jhc2hseXRpY3N9fXt7I2FuYWx5dGljc319ICBhd2FpdCBhbmFseXRpY3MuaW5pdCgpOwp7ey9hbmFseXRpY3N9fXt7I2ZlYXR1cmVfZmxhZ3N9fSAgYXdhaXQgZmVhdHVyZV9mbGFncy5pbml0KCk7Cnt7L2ZlYXR1cmVfZmxhZ3N9fXt7I2F1dGh9fQogIGZpbmFsIGNsaWVudElkID0gb3B0aW9ucy5pb3NDbGllbnRJZCA/PyBvcHRpb25zLmFuZHJvaWRDbGllbnRJZCA/PyAnJzsKICBhd2FpdCBhdXRoLmluaXQoYXV0aC5EZWZhdWx0QXV0aENvbmZpZyhjbGllbnRJZDogY2xpZW50SWQpKTsKe3svYXV0aH19e3sjbmV0d29ya319CiAgYXdhaXQgbmV0d29yay5pbml0KAogICAgY29uZmlnOiBuZXR3b3JrLkRlZmF1bHROZXR3b3JrQ29uZmlnKGJhc2VVcmw6IGNvcmUuRW52aXJvbm1lbnQuYmFzZVVybCksCiAgKTsKe3svbmV0d29ya319e3sjbm90aWZpY2F0aW9uc319CiAgYXdhaXQgbm90aWZpY2F0aW9ucy5pbml0KCk7Cnt7L25vdGlmaWNhdGlvbnN9fQogIC8vIFRPRE86IGluaXRpYWxpc2UgeW91ciBvd24gZmVhdHVyZSBtb2R1bGVzIGhlcmUuCgogIHN0b3B3YXRjaC5zdG9wKCk7CiAgbG9nZ2VyLmkoJ0Jvb3RzdHJhcCBjb21wbGV0ZWQgaW4gJHtzdG9wd2F0Y2guZWxhcHNlZE1pbGxpc2Vjb25kc30gbXMnKTsKe3sjYW5hbHl0aWNzfX0KICBhd2FpdCBhbmFseXRpY3MuQW5hbHl0aWNzSGVscGVyLmxvZ0FwcE9wZW4oCiAgICBwYXJhbWV0ZXJzOiB7J2Jvb3RzdHJhcF9kdXJhdGlvbic6IHN0b3B3YXRjaC5lbGFwc2VkTWlsbGlzZWNvbmRzfSwKICApOwp7ey9hbmFseXRpY3N9fX0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/firebase_options.dart", + "data": + "e3sjZmlyZWJhc2V9fS8vIFRoaXMgZmlsZSBpcyBhIHBsYWNlaG9sZGVyLiBHZW5lcmF0ZSB0aGUgcmVhbCBvbmUgd2l0aDoKLy8KLy8gICBjZCBhcHBzL3t7bmFtZS5zbmFrZUNhc2UoKX19ICYmIGZsdXR0ZXJmaXJlIGNvbmZpZ3VyZQovLwovLyBGbHV0dGVyRmlyZSBvdmVyd3JpdGVzIHRoaXMgZmlsZSB3aXRoIHlvdXIgcHJvamVjdCdzIG9wdGlvbnMgYW5kIGFsc28gZHJvcHMKLy8gZ29vZ2xlLXNlcnZpY2VzLmpzb24gLyBHb29nbGVTZXJ2aWNlLUluZm8ucGxpc3QgaW50byB0aGUgbmF0aXZlIGZvbGRlcnMuCmltcG9ydCAncGFja2FnZTpmaXJlYmFzZV9jb3JlL2ZpcmViYXNlX2NvcmUuZGFydCcgc2hvdyBGaXJlYmFzZU9wdGlvbnM7CgpjbGFzcyBEZWZhdWx0RmlyZWJhc2VPcHRpb25zIHsKICBjb25zdCBEZWZhdWx0RmlyZWJhc2VPcHRpb25zLl8oKTsKCiAgc3RhdGljIEZpcmViYXNlT3B0aW9ucyBnZXQgY3VycmVudFBsYXRmb3JtID0+IHRocm93IFVuc3VwcG9ydGVkRXJyb3IoCiAgICAnRmlyZWJhc2UgaGFzIG5vdCBiZWVuIGNvbmZpZ3VyZWQgZm9yIHt7bmFtZS50aXRsZUNhc2UoKX19LlxuJwogICAgJ1J1biBgZmx1dHRlcmZpcmUgY29uZmlndXJlYCBpbnNpZGUgYXBwcy97e25hbWUuc25ha2VDYXNlKCl9fSAnCiAgICAndG8gcmVwbGFjZSBsaWIvZmlyZWJhc2Vfb3B0aW9ucy5kYXJ0LicsCiAgKTsKfXt7L2ZpcmViYXNlfX0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/main.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CmltcG9ydCAncGFja2FnZTp7e25hbWUuc25ha2VDYXNlKCl9fS9hcHAuZGFydCc7CmltcG9ydCAncGFja2FnZTp7e25hbWUuc25ha2VDYXNlKCl9fS9ib290c3RyYXAuZGFydCcgYXMgYm9vdHN0cmFwOwoKdm9pZCBtYWluKCkgYXN5bmMgewogIGF3YWl0IGJvb3RzdHJhcC5pbml0KCk7CiAgcnVuQXBwKGNvbnN0IEFwcCgpKTsKfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/router/router.dart", + "data": + "e3sjYW5hbHl0aWNzfX1pbXBvcnQgJ3BhY2thZ2U6YW5hbHl0aWNzL2FuYWx5dGljcy5kYXJ0JzsKe3svYW5hbHl0aWNzfX17eyNhdXRofX1pbXBvcnQgJ3BhY2thZ2U6YXV0aC9hdXRoLmRhcnQnIGFzIGF1dGg7Cnt7L2F1dGh9fWltcG9ydCAncGFja2FnZTpjb3JlL2NvcmUuZGFydCcgYXMgY29yZTsKe3sjZGFzaGJvYXJkfX1pbXBvcnQgJ3BhY2thZ2U6ZGFzaGJvYXJkL2Rhc2hib2FyZC5kYXJ0JzsKe3svZGFzaGJvYXJkfX1pbXBvcnQgJ3BhY2thZ2U6ZGVzaWduX3N5c3RlbS9kZXNpZ25fc3lzdGVtLmRhcnQnOwp7eyNkZXZlbG9wZXJ9fWltcG9ydCAncGFja2FnZTpkZXZlbG9wZXIvZGV2ZWxvcGVyLmRhcnQnIGFzIGRldmVsb3BlcjsKe3svZGV2ZWxvcGVyfX1pbXBvcnQgJ3BhY2thZ2U6ZGkvZGkuZGFydCc7CmltcG9ydCAncGFja2FnZTpmbHV0dGVyL2ZvdW5kYXRpb24uZGFydCc7CmltcG9ydCAncGFja2FnZTpmbHV0dGVyL21hdGVyaWFsLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Z29fcm91dGVyL2dvX3JvdXRlci5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmxvY2FsaXphdGlvbi9sb2NhbGl6YXRpb24uZGFydCc7CmltcG9ydCAncGFja2FnZTpuYXZpZ2F0aW9uX2hpc3Rvcnlfb2JzZXJ2ZXIvbmF2aWdhdGlvbl9oaXN0b3J5X29ic2VydmVyLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6e3tuYW1lLnNuYWtlQ2FzZSgpfX0vdWkvc3BsYXNoX3NjcmVlbi5kYXJ0JzsKCi8vLyBUaGUgYXBwJ3Mgc2luZ2xlIFtHb1JvdXRlcl0uCi8vLwovLy8gRmVhdHVyZSBwYWNrYWdlcyBleHBvc2UgdGhlaXIgb3duIGByb3V0ZXNgIGxpc3QgYW5kIGFyZSBzcGxpY2VkIGluIGhlcmUsIHNvCi8vLyBhZGRpbmcgYSBmZWF0dXJlIGlzIG9uZSBpbXBvcnQgcGx1cyBvbmUgc3ByZWFkIC0gbm8gcm91dGUgdGFibGUgdG8gbWVyZ2UuCmNsYXNzIEFwcFJvdXRlciB7CiAgY29uc3QgQXBwUm91dGVyLl8oKTsKCiAgc3RhdGljIGZpbmFsIEdvUm91dGVyIF9yb3V0ZXIgPSBjcmVhdGVSb3V0ZXIoKTsKCiAgc3RhdGljIFJvdXRlckRlbGVnYXRlPE9iamVjdD4gZ2V0IHJvdXRlckRlbGVnYXRlID0+IF9yb3V0ZXIucm91dGVyRGVsZWdhdGU7CgogIHN0YXRpYyBSb3V0ZUluZm9ybWF0aW9uUGFyc2VyPE9iamVjdD4gZ2V0IHJvdXRlSW5mb3JtYXRpb25QYXJzZXIgPT4KICAgICAgX3JvdXRlci5yb3V0ZUluZm9ybWF0aW9uUGFyc2VyOwoKICBzdGF0aWMgUm91dGVJbmZvcm1hdGlvblByb3ZpZGVyPyBnZXQgcm91dGVJbmZvcm1hdGlvblByb3ZpZGVyID0+CiAgICAgIF9yb3V0ZXIucm91dGVJbmZvcm1hdGlvblByb3ZpZGVyOwoKICBzdGF0aWMgR29Sb3V0ZXIgY3JlYXRlUm91dGVyKCkgewogICAgZmluYWwgcm91dGVyID0gR29Sb3V0ZXIoCiAgICAgIGRlYnVnTG9nRGlhZ25vc3RpY3M6IGtEZWJ1Z01vZGUsCiAgICAgIGluaXRpYWxMb2NhdGlvbjogY29yZS5Db3JlUm91dGVzLnJvb3QsCiAgICAgIG9ic2VydmVyczogWwogICAgICAgIE5hdmlnYXRpb25IaXN0b3J5T2JzZXJ2ZXIoKSwKICAgICAgICBjb3JlLkNvcmVSb3V0ZU9ic2VydmVyKCksCnt7I2FuYWx5dGljc319ICAgICAgICBBbmFseXRpY3NSb3V0ZU9ic2VydmVyKCksCnt7L2FuYWx5dGljc319ICAgICAgXSwKICAgICAgZXJyb3JCdWlsZGVyOiAoY29udGV4dCwgc3RhdGUpID0+IEVycm9yU2NyZWVuKAogICAgICAgIHRpdGxlOiBzdHJpbmdzLmVycm9ycy5wYWdlX25vdF9mb3VuZCwKICAgICAgICBtZXNzYWdlOiBzdHJpbmdzLmVycm9ycy5wYWdlX25vdF9mb3VuZF9kZXNjcmlwdGlvbiwKICAgICAgICBvbkdvSG9tZTogKCkgPT4gY29udGV4dC5nbyhjb3JlLkNvcmVSb3V0ZXMucm9vdCksCiAgICAgICAgZXJyb3I6IHN0YXRlLnVyaS50b1N0cmluZygpLAogICAgICApLAogICAgICByb3V0ZXM6IFsKICAgICAgICBHb1JvdXRlKAogICAgICAgICAgcGF0aDogY29yZS5Db3JlUm91dGVzLnJvb3QsCiAgICAgICAgICBidWlsZGVyOiAoY29udGV4dCwgc3RhdGUpID0+IGNvbnN0IFNwbGFzaFNjcmVlbigpLAogICAgICAgICksCnt7I2Rhc2hib2FyZH19ICAgICAgICBHb1JvdXRlKAogICAgICAgICAgcGF0aDogY29yZS5Db3JlUm91dGVzLmhvbWUsCiAgICAgICAgICByZWRpcmVjdDogKGNvbnRleHQsIHN0YXRlKSA9PiBEYXNoYm9hcmRSb3V0ZXIuaG9tZSwKICAgICAgICApLAogICAgICAgIERhc2hib2FyZFJvdXRlci5jcmVhdGVTaGVsbFJvdXRlKCksCnt7L2Rhc2hib2FyZH19e3teZGFzaGJvYXJkfX0gICAgICAgIEdvUm91dGUoCiAgICAgICAgICBwYXRoOiBjb3JlLkNvcmVSb3V0ZXMuaG9tZSwKICAgICAgICAgIGJ1aWxkZXI6IChjb250ZXh0LCBzdGF0ZSkgPT4gY29uc3QgX0hvbWVTY3JlZW4oKSwKICAgICAgICApLAp7ey9kYXNoYm9hcmR9fXt7I2F1dGh9fSAgICAgICAgLi4uYXV0aC5BdXRoUm91dGVyKAogICAgICAgICAgb25TaWduZWRJbjogX29uQXV0aGVudGljYXRlZCwKICAgICAgICAgIG9uU2lnbmVkVXA6IF9vbkF1dGhlbnRpY2F0ZWQsCiAgICAgICAgKS5yb3V0ZXMsCnt7L2F1dGh9fXt7I2RldmVsb3Blcn19ICAgICAgICAuLi5kZXZlbG9wZXIuRGV2ZWxvcGVyUm91dGVyKCkucm91dGVzLAp7ey9kZXZlbG9wZXJ9fSAgICAgICAgR29Sb3V0ZSgKICAgICAgICAgIHBhdGg6IGNvcmUuQ29yZVJvdXRlcy5lcnJvciwKICAgICAgICAgIGJ1aWxkZXI6IChjb250ZXh0LCBzdGF0ZSkgPT4gRXJyb3JTY3JlZW4oCiAgICAgICAgICAgIHRpdGxlOgogICAgICAgICAgICAgICAgc3RhdGUudXJpLnF1ZXJ5UGFyYW1ldGVyc1tjb3JlLktleXMudGl0bGVdID8/CiAgICAgICAgICAgICAgICBzdHJpbmdzLmdlbmVyaWMuZXJyb3IsCiAgICAgICAgICAgIG1lc3NhZ2U6CiAgICAgICAgICAgICAgICBzdGF0ZS51cmkucXVlcnlQYXJhbWV0ZXJzW2NvcmUuS2V5cy5tZXNzYWdlXSA/PwogICAgICAgICAgICAgICAgc3RyaW5ncy5lcnJvcnMudW5leHBlY3RlZF9lcnJvciwKICAgICAgICAgICAgZXJyb3I6IHN0YXRlLnVyaS5xdWVyeVBhcmFtZXRlcnNbY29yZS5LZXlzLmVycm9yXSwKICAgICAgICAgICAgb25Hb0hvbWU6ICgpID0+IGNvbnRleHQuZ28oY29yZS5Db3JlUm91dGVzLnJvb3QpLAogICAgICAgICAgKSwKICAgICAgICApLAogICAgICBdLAogICAgKTsKCiAgICBkaS5yZWdpc3RlcjxHb1JvdXRlcj4ocm91dGVyKTsKICAgIHJldHVybiByb3V0ZXI7CiAgfQp7eyNhdXRofX0KICAvLy8gV2hlcmUgYSB1c2VyIGxhbmRzIG9uY2Ugc2lnbi1pbiBvciBzaWduLXVwIHN1Y2NlZWRzLgogIC8vLwogIC8vLyBMb2FkIHRoZSBwcm9maWxlIC8gZW50aXRsZW1lbnRzIHlvdSBuZWVkIGhlcmUgYmVmb3JlIHJvdXRpbmcgb24uCiAgc3RhdGljIEZ1dHVyZTx2b2lkPiBfb25BdXRoZW50aWNhdGVkKEJ1aWxkQ29udGV4dCBjb250ZXh0KSBhc3luYyB7Cnt7I2FuYWx5dGljc319ICAgIGF3YWl0IEFuYWx5dGljc0hlbHBlci5sb2dFdmVudChBbmFseXRpY3NFdmVudHMudXNlci5hdXRoZW50aWNhdGVkUmVkaXJlY3QpOwp7ey9hbmFseXRpY3N9fSAgICBpZiAoY29udGV4dC5tb3VudGVkKSB7CiAgICAgIGNvbnRleHQuZ28oe3sjZGFzaGJvYXJkfX1EYXNoYm9hcmRSb3V0ZXIuaG9tZXt7L2Rhc2hib2FyZH19e3teZGFzaGJvYXJkfX1jb3JlLkNvcmVSb3V0ZXMuaG9tZXt7L2Rhc2hib2FyZH19KTsKICAgIH0KICB9Cnt7L2F1dGh9fX0Ke3teZGFzaGJvYXJkfX0KLy8vIFBsYWNlaG9sZGVyIGxhbmRpbmcgc2NyZWVuIC0gcmVwbGFjZSBpdCB3aXRoIHlvdXIgZmlyc3QgZmVhdHVyZS4KY2xhc3MgX0hvbWVTY3JlZW4gZXh0ZW5kcyBTdGF0ZWxlc3NXaWRnZXQgewogIGNvbnN0IF9Ib21lU2NyZWVuKCk7CgogIEBvdmVycmlkZQogIFdpZGdldCBidWlsZChCdWlsZENvbnRleHQgY29udGV4dCkgewogICAgcmV0dXJuIFNjYWZmb2xkKAogICAgICBhcHBCYXI6IEFwcEJhcih0aXRsZTogVGV4dChzdHJpbmdzLmFwcC5uYW1lKSksCiAgICAgIGJvZHk6IENlbnRlcigKICAgICAgICBjaGlsZDoge3sjZGV2ZWxvcGVyfX1kZXZlbG9wZXIuT3BlbkRldlRvb2xzV3JhcHBlcigKICAgICAgICAgIC8vIFRhcCA1eCB0byByZWFjaCB0aGUgZGV2ZWxvcGVyIHRvb2xzLgogICAgICAgICAgY2hpbGQ6IFRleHQoc3RyaW5ncy5hcHAud2VsY29tZV90b19hcHApLAogICAgICAgICl7ey9kZXZlbG9wZXJ9fXt7XmRldmVsb3Blcn19VGV4dChzdHJpbmdzLmFwcC53ZWxjb21lX3RvX2FwcCl7ey9kZXZlbG9wZXJ9fSwKICAgICAgKSwKICAgICk7CiAgfQp9Cnt7L2Rhc2hib2FyZH19", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/lib/ui/splash_screen.dart", + "data": + "e3sjYXV0aH19aW1wb3J0ICdwYWNrYWdlOmF1dGgvYXV0aC5kYXJ0JyBhcyBhdXRoOwp7ey9hdXRofX17e15kYXNoYm9hcmR9fWltcG9ydCAncGFja2FnZTpjb3JlL2NvcmUuZGFydCcgYXMgY29yZTsKe3svZGFzaGJvYXJkfX17eyNkYXNoYm9hcmR9fWltcG9ydCAncGFja2FnZTpkYXNoYm9hcmQvZGFzaGJvYXJkLmRhcnQnOwp7ey9kYXNoYm9hcmR9fWltcG9ydCAncGFja2FnZTpkZXNpZ25fc3lzdGVtL2Rlc2lnbl9zeXN0ZW0uZGFydCc7Cnt7I2F1dGh9fWltcG9ydCAncGFja2FnZTpkaS9kaS5kYXJ0JzsKe3svYXV0aH19aW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CmltcG9ydCAncGFja2FnZTpnb19yb3V0ZXIvZ29fcm91dGVyLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6bG9jYWxpemF0aW9uL2xvY2FsaXphdGlvbi5kYXJ0JzsKCi8vLyBGaXJzdCBmcmFtZSB0aGUgdXNlciBzZWVzLgovLy8KLy8vIEl0IGV4aXN0cyB0byBnaXZlIGFzeW5jIHN0YXJ0dXAgd29yayAoc2Vzc2lvbiByZXN0b3JlLCBwcm9maWxlIGZldGNoKSBhCi8vLyBwbGFjZSB0byBoYXBwZW4gYmVmb3JlIHRoZSBhcHAgY29tbWl0cyB0byBhIGRlc3RpbmF0aW9uLgpjbGFzcyBTcGxhc2hTY3JlZW4gZXh0ZW5kcyBTdGF0ZWZ1bFdpZGdldCB7CiAgY29uc3QgU3BsYXNoU2NyZWVuKHtzdXBlci5rZXl9KTsKCiAgQG92ZXJyaWRlCiAgU3RhdGU8U3BsYXNoU2NyZWVuPiBjcmVhdGVTdGF0ZSgpID0+IF9TcGxhc2hTY3JlZW5TdGF0ZSgpOwp9CgpjbGFzcyBfU3BsYXNoU2NyZWVuU3RhdGUgZXh0ZW5kcyBTdGF0ZTxTcGxhc2hTY3JlZW4+IHsKICBAb3ZlcnJpZGUKICB2b2lkIGluaXRTdGF0ZSgpIHsKICAgIHN1cGVyLmluaXRTdGF0ZSgpOwogICAgV2lkZ2V0c0JpbmRpbmcuaW5zdGFuY2UuYWRkUG9zdEZyYW1lQ2FsbGJhY2soKF8pID0+IF9yZWRpcmVjdCgpKTsKICB9CgogIEZ1dHVyZTx2b2lkPiBfcmVkaXJlY3QoKSBhc3luYyB7CiAgICBpZiAoIW1vdW50ZWQpIHJldHVybjsKe3sjYXV0aH19CiAgICBpZiAoIWRpLmdldDxhdXRoLkF1dGhTZXJ2aWNlPigpLmlzU2lnbmVkSW4pIHsKICAgICAgY29udGV4dC5nbyhhdXRoLkF1dGhSb3V0ZXMuc2lnbkluKTsKICAgICAgcmV0dXJuOwogICAgfQp7ey9hdXRofX0KICAgIC8vIFRPRE86IGxvYWQgd2hhdGV2ZXIgdGhlIGZpcnN0IHNjcmVlbiBuZWVkcyBiZWZvcmUgbmF2aWdhdGluZy4KICAgIGNvbnRleHQuZ28oe3sjZGFzaGJvYXJkfX1EYXNoYm9hcmRSb3V0ZXIuaG9tZXt7L2Rhc2hib2FyZH19e3teZGFzaGJvYXJkfX1jb3JlLkNvcmVSb3V0ZXMuaG9tZXt7L2Rhc2hib2FyZH19KTsKICB9CgogIEBvdmVycmlkZQogIFdpZGdldCBidWlsZChCdWlsZENvbnRleHQgY29udGV4dCkgewogICAgZmluYWwgdGhlbWUgPSBUaGVtZS5vZihjb250ZXh0KTsKICAgIHJldHVybiBTY2FmZm9sZCgKICAgICAgYm9keTogQ2VudGVyKAogICAgICAgIGNoaWxkOiBDb2x1bW4oCiAgICAgICAgICBtYWluQXhpc0FsaWdubWVudDogTWFpbkF4aXNBbGlnbm1lbnQuY2VudGVyLAogICAgICAgICAgY2hpbGRyZW46IFsKICAgICAgICAgICAgVGV4dCgKICAgICAgICAgICAgICBzdHJpbmdzLmFwcC5uYW1lLAogICAgICAgICAgICAgIHN0eWxlOiB0aGVtZS50ZXh0VGhlbWUuaGVhZGxpbmVNZWRpdW0/LmNvcHlXaXRoKAogICAgICAgICAgICAgICAgZm9udFdlaWdodDogRm9udFdlaWdodC5ib2xkLAogICAgICAgICAgICAgICksCiAgICAgICAgICAgICksCiAgICAgICAgICAgIGNvbnN0IFNpemVkQm94KGhlaWdodDogMzIpLAogICAgICAgICAgICBjb25zdCBEZWZhdWx0TG9hZGVyKCksCiAgICAgICAgICBdLAogICAgICAgICksCiAgICAgICksCiAgICApOwogIH0KfQo=", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/pubspec.yaml", + "data": + "bmFtZToge3tuYW1lLnNuYWtlQ2FzZSgpfX0KZGVzY3JpcHRpb246ICJ7e3tkZXNjcmlwdGlvbn19fSIKcHVibGlzaF90bzogJ25vbmUnCnZlcnNpb246IDAuMS4wKzEKCmVudmlyb25tZW50OgogIHNkazogXjMuOC4wCiAgZmx1dHRlcjogIj49My4yOS4wIgoKZGVwZW5kZW5jaWVzOgogIGZsdXR0ZXI6CiAgICBzZGs6IGZsdXR0ZXIKICBjdXBlcnRpbm9faWNvbnM6IF4xLjAuOAogIGdvX3JvdXRlcjogXjE1LjEuMwogIGJsb2M6IF45LjAuMAogIG5hdmlnYXRpb25faGlzdG9yeV9vYnNlcnZlcjogXjEuMS4xCiAgY29yZToKICAgIHBhdGg6IC4uLy4uL3BhY2thZ2VzL2NvcmUKICBkZXNpZ25fc3lzdGVtOgogICAgcGF0aDogLi4vLi4vcGFja2FnZXMvZGVzaWduX3N5c3RlbQogIGRpOgogICAgcGF0aDogLi4vLi4vcGFja2FnZXMvZGkKICBsb2NhbGl6YXRpb246CiAgICBwYXRoOiAuLi8uLi9wYWNrYWdlcy9sb2NhbGl6YXRpb24KICB1dGlsczoKICAgIHBhdGg6IC4uLy4uL3BhY2thZ2VzL3V0aWxzCnt7I2ZpcmViYXNlfX0gIGZpcmViYXNlX2NvcmU6IF4zLjE0LjAKe3svZmlyZWJhc2V9fXt7I25ldHdvcmt9fSAgbmV0d29yazoKICAgIHBhdGg6IC4uLy4uL3BhY2thZ2VzL25ldHdvcmsKe3svbmV0d29ya319e3sjYW5hbHl0aWNzfX0gIGFuYWx5dGljczoKICAgIHBhdGg6IC4uLy4uL3BhY2thZ2VzL2FuYWx5dGljcwp7ey9hbmFseXRpY3N9fXt7I2NyYXNobHl0aWNzfX0gIGNyYXNobHl0aWNzOgogICAgcGF0aDogLi4vLi4vcGFja2FnZXMvY3Jhc2hseXRpY3MKe3svY3Jhc2hseXRpY3N9fXt7I2ZlYXR1cmVfZmxhZ3N9fSAgZmVhdHVyZV9mbGFnczoKICAgIHBhdGg6IC4uLy4uL3BhY2thZ2VzL2ZlYXR1cmVfZmxhZ3MKe3svZmVhdHVyZV9mbGFnc319e3sjbm90aWZpY2F0aW9uc319ICBub3RpZmljYXRpb25zOgogICAgcGF0aDogLi4vLi4vcGFja2FnZXMvbm90aWZpY2F0aW9ucwp7ey9ub3RpZmljYXRpb25zfX17eyNkZXZlbG9wZXJ9fSAgZGV2ZWxvcGVyOgogICAgcGF0aDogLi4vLi4vcGFja2FnZXMvZGV2ZWxvcGVyCnt7L2RldmVsb3Blcn19e3sjYXV0aH19ICBhdXRoOgogICAgcGF0aDogLi4vLi4vZmVhdHVyZXMvYXV0aAp7ey9hdXRofX17eyNkYXNoYm9hcmR9fSAgZGFzaGJvYXJkOgogICAgcGF0aDogLi4vLi4vZmVhdHVyZXMvZGFzaGJvYXJkCnt7L2Rhc2hib2FyZH19CmRldl9kZXBlbmRlbmNpZXM6CiAgZmx1dHRlcl90ZXN0OgogICAgc2RrOiBmbHV0dGVyCiAgZmx1dHRlcl9saW50czogXjUuMC4wCgpkZXBlbmRlbmN5X292ZXJyaWRlczoKICBpbnRsOiBeMC4yMC4yCgpmbHV0dGVyOgogIHVzZXMtbWF0ZXJpYWwtZGVzaWduOiB0cnVlCnt7I2F1dGh9fSAgZm9udHM6CiAgICAjIGZpcmViYXNlX3VpX2F1dGggc2hpcHMgdGhpcyBmb250IGJ1dCBuZXZlciByZWdpc3RlcnMgaXQsIHNvIHRoZSBHb29nbGUKICAgICMgYW5kIEFwcGxlIGJ1dHRvbnMgcmVuZGVyIGFzIHRvZnUgdW5sZXNzIHRoZSBhcHAgZGVjbGFyZXMgaXQgaGVyZS4KICAgIC0gZmFtaWx5OiBTb2NpYWxJY29ucwogICAgICBmb250czoKICAgICAgICAtIGFzc2V0OiBwYWNrYWdlcy9maXJlYmFzZV91aV9hdXRoL2ZvbnRzL1NvY2lhbEljb25zLnR0Zgp7ey9hdXRofX0=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/apps/{{ name.snakeCase() }}/test/{{smoke_test_file}}", + "data": + "aW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXJfdGVzdC9mbHV0dGVyX3Rlc3QuZGFydCc7CmltcG9ydCAncGFja2FnZTp7e25hbWUuc25ha2VDYXNlKCl9fS9hcHAuZGFydCc7CmltcG9ydCAncGFja2FnZTp7e25hbWUuc25ha2VDYXNlKCl9fS9ib290c3RyYXAuZGFydCcgYXMgYm9vdHN0cmFwOwoKdm9pZCBtYWluKCkgewogIHRlc3RXaWRnZXRzKCd0aGUgYXBwIGJvb3RzIGFuZCByZWFjaGVzIGl0cyBmaXJzdCBzY3JlZW4nLCAodGVzdGVyKSBhc3luYyB7CiAgICAvLyBFeGVyY2lzZXMgdGhlIHdob2xlIGdyYXBoOiBtb2R1bGUgaW5pdCBvcmRlciwgREkgcmVnaXN0cmF0aW9uIGFuZCB0aGUKICAgIC8vIHNwbGFzaCByZWRpcmVjdC4gSWYgYSBtb2R1bGUgZm9yZ2V0cyB0byByZWdpc3RlciBzb21ldGhpbmcsIHRoaXMgZmFpbHMKICAgIC8vIGhlcmUgcmF0aGVyIHRoYW4gb24gYSBkZXZpY2UuCiAgICBhd2FpdCBib290c3RyYXAuaW5pdCgpOwogICAgYXdhaXQgdGVzdGVyLnB1bXBXaWRnZXQoY29uc3QgQXBwKCkpOwogICAgYXdhaXQgdGVzdGVyLnB1bXBBbmRTZXR0bGUoKTsKCiAgICBleHBlY3QodGVzdGVyLnRha2VFeGNlcHRpb24oKSwgaXNOdWxsKTsKe3sjZGFzaGJvYXJkfX0gICAgZXhwZWN0KGZpbmQudGV4dCgnRXhwbG9yZScpLCBmaW5kc1dpZGdldHMpOwp7ey9kYXNoYm9hcmR9fSAgfSk7Cn0K", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/features/README.md", + "data": + "IyBGZWF0dXJlcwoKVmVydGljYWwgc2xpY2VzIG9mIHRoZSBwcm9kdWN0LiBBIGZlYXR1cmUgb3ducyBpdHMgVUksIHN0YXRlIGFuZCBkYXRhIGFjY2VzcywKYW5kIGV4cG9zZXMgdHdvIHRoaW5ncyB0byB0aGUgYXBwOgoKLSBhbiBgaW5pdCgpYCB0aGF0IHJlZ2lzdGVycyBpdHMgc2VydmljZXMgd2l0aCB0aGUgbG9jYXRvcgotIGEgYFJvdXRlcmAgZXhwb3NpbmcgdGhlIHJvdXRlcyB0aGUgYXBwIHNwbGljZXMgaW50byBpdHMgcm91dGUgdGFibGUKCkZlYXR1cmVzIG1heSBkZXBlbmQgb24gYHBhY2thZ2VzL2AsIGFuZCBzaG91bGQgYXZvaWQgZGVwZW5kaW5nIG9uIGVhY2ggb3RoZXIuCgpBZGQgb25lIHdpdGg6CgpgYGBzaApub25zdG9wIGNyZWF0ZSBwYWNrYWdlIG15X2ZlYXR1cmUgLW8gZmVhdHVyZXMKYGBgCg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/analytics/analytics.dart", + "data": + "e3sjYW5hbHl0aWNzfX1pbXBvcnQgJ3BhY2thZ2U6YW5hbHl0aWNzL2FuYWx5dGljcy5kYXJ0JzsKe3svYW5hbHl0aWNzfX1pbXBvcnQgJ3BhY2thZ2U6ZmlyZWJhc2VfYXV0aC9maXJlYmFzZV9hdXRoLmRhcnQnOwoKLy8vIEV2ZXJ5IGF1dGgtcmVsYXRlZCBhbmFseXRpY3MgZXZlbnQgaW4gb25lIHBsYWNlLgovLy8KLy8vIFNjcmVlbnMgY2FsbCB0aGVzZSBpbnN0ZWFkIG9mIGBBbmFseXRpY3NIZWxwZXJgIGRpcmVjdGx5IHNvIHRoZSBldmVudCBuYW1lcwovLy8gYW5kIHBhcmFtZXRlciBzaGFwZXMgc3RheSBjb25zaXN0ZW50IGFjcm9zcyBzaWduLWluLCBzaWduLXVwIGFuZCByZWNvdmVyeS4Ke3teYW5hbHl0aWNzfX0vLy8KLy8vIEFuYWx5dGljcyBpcyBub3QgZW5hYmxlZCBpbiB0aGlzIHByb2plY3QsIHNvIHRoZSBsb2cgbWV0aG9kcyBhcmUgbm8tb3BzLgovLy8gV2lyZSB0aGVtIHVwIGJ5IGFkZGluZyB0aGUgYGFuYWx5dGljc2AgcGFja2FnZSBiYWNrIHRvIGBwdWJzcGVjLnlhbWxgLgp7ey9hbmFseXRpY3N9fWNsYXNzIEF1dGhBbmFseXRpY3MgewogIGNvbnN0IEF1dGhBbmFseXRpY3MuXygpOwoKICAvLy8gJ2dvb2dsZScsICdhcHBsZScgb3IgJ2VtYWlsJywgZGVyaXZlZCBmcm9tIHRoZSBzaWduZWQtaW4gcHJvdmlkZXIuCiAgc3RhdGljIFN0cmluZyBnZXRBdXRoTWV0aG9kKExpc3Q8VXNlckluZm8+PyBwcm92aWRlckRhdGEpIHsKICAgIGlmIChwcm92aWRlckRhdGE/LmlzTm90RW1wdHkgPz8gZmFsc2UpIHsKICAgICAgZmluYWwgcHJvdmlkZXJJZCA9IHByb3ZpZGVyRGF0YSEuZmlyc3QucHJvdmlkZXJJZDsKICAgICAgaWYgKHByb3ZpZGVySWQuY29udGFpbnMoJ2dvb2dsZScpKSByZXR1cm4gJ2dvb2dsZSc7CiAgICAgIGlmIChwcm92aWRlcklkLmNvbnRhaW5zKCdhcHBsZScpKSByZXR1cm4gJ2FwcGxlJzsKICAgIH0KICAgIHJldHVybiAnZW1haWwnOwogIH0KCiAgc3RhdGljIEZ1dHVyZTx2b2lkPiBsb2dTaWduSW5TdWNjZXNzKHsKICAgIHJlcXVpcmVkIFN0cmluZyBtZXRob2QsCiAgICBTdHJpbmc/IHVzZXJFbWFpbCwKICB9KSBhc3luYyB7Cnt7I2FuYWx5dGljc319ICAgIGF3YWl0IEFuYWx5dGljc0hlbHBlci5sb2dTaWduSW4oCiAgICAgIG1ldGhvZDogbWV0aG9kLAogICAgICBwYXJhbWV0ZXJzOiBfc3VjY2Vzc1BhcmFtcyh1c2VyRW1haWwpLAogICAgKTsKe3svYW5hbHl0aWNzfX0gIH0KCiAgc3RhdGljIEZ1dHVyZTx2b2lkPiBsb2dTaWduVXBTdWNjZXNzKHsKICAgIHJlcXVpcmVkIFN0cmluZyBtZXRob2QsCiAgICBTdHJpbmc/IHVzZXJFbWFpbCwKICB9KSBhc3luYyB7Cnt7I2FuYWx5dGljc319ICAgIGF3YWl0IEFuYWx5dGljc0hlbHBlci5sb2dTaWduVXAoCiAgICAgIG1ldGhvZDogbWV0aG9kLAogICAgICBwYXJhbWV0ZXJzOiBfc3VjY2Vzc1BhcmFtcyh1c2VyRW1haWwpLAogICAgKTsKe3svYW5hbHl0aWNzfX0gIH0KCiAgc3RhdGljIEZ1dHVyZTx2b2lkPiBsb2dVc2VyQ3JlYXRpb25TdWNjZXNzKHsKICAgIHJlcXVpcmVkIFN0cmluZyBtZXRob2QsCiAgICBTdHJpbmc/IHVzZXJFbWFpbCwKICB9KSBhc3luYyB7Cnt7I2FuYWx5dGljc319ICAgIGF3YWl0IEFuYWx5dGljc0hlbHBlci5sb2dFdmVudCgKICAgICAgQW5hbHl0aWNzRXZlbnRzLnVzZXIuYXV0aGVudGljYXRlZFJlZGlyZWN0LAogICAgICBwYXJhbWV0ZXJzOiB7CiAgICAgICAgJ2F1dGhfbWV0aG9kJzogbWV0aG9kLAogICAgICAgICdmbG93X3R5cGUnOiAncmVnaXN0cmF0aW9uJywKICAgICAgICAuLi5fc3VjY2Vzc1BhcmFtcyh1c2VyRW1haWwpLAogICAgICB9LAogICAgKTsKe3svYW5hbHl0aWNzfX0gIH0KCiAgc3RhdGljIEZ1dHVyZTx2b2lkPiBsb2dTaWduT3V0U3VjY2VzcygpIGFzeW5jIHsKe3sjYW5hbHl0aWNzfX0gICAgYXdhaXQgQW5hbHl0aWNzSGVscGVyLmxvZ0V2ZW50KAogICAgICBBbmFseXRpY3NFdmVudHMuYXV0aC5zaWduT3V0LAogICAgICBwYXJhbWV0ZXJzOiBfc3VjY2Vzc1BhcmFtcyhudWxsKSwKICAgICk7Cnt7L2FuYWx5dGljc319ICB9CgogIC8vLyBbZmxvd1R5cGVdIGlzIG9uZSBvZiAnc2lnbl9pbicsICdzaWduX3VwJywgJ2ZvcmdvdF9wYXNzd29yZCcuCiAgc3RhdGljIEZ1dHVyZTx2b2lkPiBsb2dBdXRoRXJyb3IoewogICAgcmVxdWlyZWQgU3RyaW5nIGVycm9yVHlwZSwKICAgIHJlcXVpcmVkIFN0cmluZyBmbG93VHlwZSwKICAgIFN0cmluZz8gZXJyb3JNZXNzYWdlLAogICAgU3RyaW5nPyBtZXRob2QsCiAgfSkgYXN5bmMgewp7eyNhbmFseXRpY3N9fSAgICBhd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nRXZlbnQoCiAgICAgIEFuYWx5dGljc0V2ZW50cy5hdXRoLmZhaWxlZCwKICAgICAgcGFyYW1ldGVyczogewogICAgICAgICdlcnJvcl90eXBlJzogZXJyb3JUeXBlLAogICAgICAgICdmbG93X3R5cGUnOiBmbG93VHlwZSwKICAgICAgICAnYXV0aF9tZXRob2QnOiBtZXRob2QsCiAgICAgICAgJ2Vycm9yX21lc3NhZ2UnOiBlcnJvck1lc3NhZ2UsCiAgICAgICAgJ3RpbWVzdGFtcCc6IERhdGVUaW1lLm5vdygpLnRvSXNvODYwMVN0cmluZygpLAogICAgICB9LAogICAgKTsKe3svYW5hbHl0aWNzfX0gIH0KCiAgc3RhdGljIEZ1dHVyZTx2b2lkPiBsb2dTaWduT3V0RXJyb3Ioe3JlcXVpcmVkIFN0cmluZyBlcnJvck1lc3NhZ2V9KSBhc3luYyB7Cnt7I2FuYWx5dGljc319ICAgIGF3YWl0IEFuYWx5dGljc0hlbHBlci5sb2dFdmVudCgKICAgICAgQW5hbHl0aWNzRXZlbnRzLmVycm9yLmFwcEVycm9yLAogICAgICBwYXJhbWV0ZXJzOiB7CiAgICAgICAgJ2Vycm9yX3R5cGUnOiAnc2lnbl9vdXRfZmFpbGVkJywKICAgICAgICAnZXJyb3JfbWVzc2FnZSc6IGVycm9yTWVzc2FnZSwKICAgICAgICAnZmVhdHVyZSc6ICdhdXRoJywKICAgICAgICAndGltZXN0YW1wJzogRGF0ZVRpbWUubm93KCkudG9Jc284NjAxU3RyaW5nKCksCiAgICAgIH0sCiAgICApOwp7ey9hbmFseXRpY3N9fSAgfQp7eyNhbmFseXRpY3N9fQogIHN0YXRpYyBNYXA8U3RyaW5nLCBPYmplY3Q/PiBfc3VjY2Vzc1BhcmFtcyhTdHJpbmc/IHVzZXJFbWFpbCkgPT4gewogICAgJ3N1Y2Nlc3MnOiAndHJ1ZScsCiAgICAndXNlcl9lbWFpbF9kb21haW4nOiB1c2VyRW1haWw/LnNwbGl0KCdAJykubGFzdCwKICAgICd0aW1lc3RhbXAnOiBEYXRlVGltZS5ub3coKS50b0lzbzg2MDFTdHJpbmcoKSwKICB9Owp7ey9hbmFseXRpY3N9fX0K", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/auth.dart", + "data": + "aW1wb3J0ICdkYXJ0OmlvJzsKCmltcG9ydCAncGFja2FnZTphdXRoL2NvbmZpZy9jb25maWcuZGFydCc7CmltcG9ydCAncGFja2FnZTphdXRoL2RhdGEvaW5kZXguZGFydCc7CmltcG9ydCAncGFja2FnZTpjb3JlL2xvZ2dlci9sb2dnZXIuZGFydCc7CmltcG9ydCAncGFja2FnZTpkaS9kaS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZpcmViYXNlX3VpX2F1dGgvZmlyZWJhc2VfdWlfYXV0aC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZpcmViYXNlX3VpX29hdXRoX2FwcGxlL2ZpcmViYXNlX3VpX29hdXRoX2FwcGxlLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZmlyZWJhc2VfdWlfb2F1dGhfZ29vZ2xlL2ZpcmViYXNlX3VpX29hdXRoX2dvb2dsZS5kYXJ0JzsKe3sjbmV0d29ya319aW1wb3J0ICdwYWNrYWdlOm5ldHdvcmsvbmV0d29yay5kYXJ0JzsKe3svbmV0d29ya319CmV4cG9ydCAnYW5hbHl0aWNzL2FuYWx5dGljcy5kYXJ0JzsKZXhwb3J0ICdjb25maWcvaW5kZXguZGFydCc7CmV4cG9ydCAnY29uc3RhbnRzL2luZGV4LmRhcnQnOwpleHBvcnQgJ2RhdGEvaW5kZXguZGFydCc7CmV4cG9ydCAncm91dGVyL3JvdXRlci5kYXJ0JzsKZXhwb3J0ICd1aS9zY3JlZW5zL2luZGV4LmRhcnQnOwoKLy8vIFJlZ2lzdGVycyB0aGUgYXV0aCBwcm92aWRlcnMgYW5kIHNlcnZpY2VzLgovLy8KLy8vIENhbGwgdGhpcyBhZnRlciBgRmlyZWJhc2UuaW5pdGlhbGl6ZUFwcGAgYW5kIGJlZm9yZSB0aGUgcm91dGVyIGlzIGJ1aWx0IC0KLy8vIHJvdXRlIGd1YXJkcyByZXNvbHZlIFtBdXRoU2VydmljZV0gZnJvbSB0aGUgbG9jYXRvciBvbiB0aGUgZmlyc3QgZnJhbWUuCkZ1dHVyZTx2b2lkPiBpbml0KEF1dGhDb25maWcgY29uZmlnKSBhc3luYyB7CiAgZmluYWwgbG9nZ2VyID0gZGkuZ2V0PExvZ2dlcj4oKTsKICBsb2dnZXIuaSgnSW5pdGlhbGl6aW5nIEF1dGguLi4nKTsKCiAgRmlyZWJhc2VVSUF1dGguY29uZmlndXJlUHJvdmlkZXJzKFsKICAgIEVtYWlsQXV0aFByb3ZpZGVyKCksCiAgICBHb29nbGVQcm92aWRlcihjbGllbnRJZDogY29uZmlnLmNsaWVudElkLCBpT1NQcmVmZXJQbGlzdDogdHJ1ZSksCiAgICBpZiAoUGxhdGZvcm0uaXNJT1MpIEFwcGxlUHJvdmlkZXIoKSwKICBdKTsKCiAgZGkucmVnaXN0ZXI8QXV0aFNlcnZpY2U+KEF1dGhTZXJ2aWNlSW1wKCkpOwp7eyNuZXR3b3JrfX0KICAvLyBUaGUgbmV0d29yayBsYXllciByZWFkcyBiZWFyZXIgdG9rZW5zIHRocm91Z2ggdGhpcyBwcm92aWRlciwgc28gYXV0aCBoYXMKICAvLyB0byBvd24gaXQgLSByZWdpc3RlcmluZyBpdCBhbnl3aGVyZSBlbHNlIHJpc2tzIGEgc3RhbGUgdG9rZW4gb24gcmVmcmVzaC4KICBkaS5yZWdpc3RlcjxBdXRoVG9rZW5Qcm92aWRlcj4oRmlyZWJhc2VBdXRoVG9rZW5Qcm92aWRlcigpKTsKe3svbmV0d29ya319CiAgbG9nZ2VyLmkoJ0F1dGggaW5pdGlhbGl6ZWQnKTsKfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/config/config.dart", + "data": + "YWJzdHJhY3QgY2xhc3MgQXV0aENvbmZpZyB7CiAgU3RyaW5nIGdldCBjbGllbnRJZDsKfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/config/config_imp.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmF1dGgvY29uZmlnL2NvbmZpZy5kYXJ0JzsKCmNsYXNzIERlZmF1bHRBdXRoQ29uZmlnIGltcGxlbWVudHMgQXV0aENvbmZpZyB7CiAgQG92ZXJyaWRlCiAgU3RyaW5nIGNsaWVudElkOwoKICBEZWZhdWx0QXV0aENvbmZpZyh7cmVxdWlyZWQgdGhpcy5jbGllbnRJZH0pOwp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/config/index.dart", + "data": + "ZXhwb3J0ICdjb25maWcuZGFydCc7CmV4cG9ydCAnY29uZmlnX2ltcC5kYXJ0JzsK", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/constants/index.dart", + "data": "ZXhwb3J0ICdyb3V0ZXMuZGFydCc7Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/constants/routes.dart", + "data": + "Y2xhc3MgQXV0aFJvdXRlcyB7CiAgQXV0aFJvdXRlcy5fKCk7CgogIHN0YXRpYyBjb25zdCBTdHJpbmcgYXV0aCA9ICcvYXV0aCc7CgogIHN0YXRpYyBjb25zdCBTdHJpbmcgc2lnbkluID0gJyRhdXRoL3NpZ24taW4nOwogIHN0YXRpYyBjb25zdCBTdHJpbmcgc2lnblVwID0gJyRhdXRoL3NpZ24tdXAnOwogIHN0YXRpYyBjb25zdCBTdHJpbmcgZm9yZ290UGFzc3dvcmQgPSAnJGF1dGgvZm9yZ290LXBhc3N3b3JkJzsKICBzdGF0aWMgY29uc3QgU3RyaW5nIHJlc2V0UGFzc3dvcmQgPSAnJGF1dGgvcmVzZXQtcGFzc3dvcmQnOwp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/index.dart", + "data": + "ZXhwb3J0ICcuL3NlcnZpY2VzL2luZGV4LmRhcnQnOwp7eyNuZXR3b3JrfX1leHBvcnQgJy4vdG9rZW4vZmlyZWJhc2VfYXV0aF90b2tlbl9wcm92aWRlci5kYXJ0JzsKe3svbmV0d29ya319", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/services/auth_service.dart", + "data": + "YWJzdHJhY3QgY2xhc3MgQXV0aFNlcnZpY2UgewogIFN0cmluZyBnZXQgdWlkOwoKICBib29sIGdldCBpc1NpZ25lZEluOwoKICBGdXR1cmU8dm9pZD4gc2lnbk91dCgpOwp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/services/auth_service_imp.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmF1dGgvYW5hbHl0aWNzL2FuYWx5dGljcy5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmF1dGgvZGF0YS9zZXJ2aWNlcy9hdXRoX3NlcnZpY2UuZGFydCc7CmltcG9ydCAncGFja2FnZTpjb3JlL2NvcmUuZGFydCc7CmltcG9ydCAncGFja2FnZTpkaS9kaS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZpcmViYXNlX2F1dGgvZmlyZWJhc2VfYXV0aC5kYXJ0JzsKCmNsYXNzIEF1dGhTZXJ2aWNlSW1wIGltcGxlbWVudHMgQXV0aFNlcnZpY2UgewogIGZpbmFsIF9sb2dnZXIgPSBkaS5nZXQ8TG9nZ2VyPigpOwogIGZpbmFsIEZpcmViYXNlQXV0aCBfZmlyZWJhc2VBdXRoID0gRmlyZWJhc2VBdXRoLmluc3RhbmNlOwoKICBAb3ZlcnJpZGUKICBTdHJpbmcgZ2V0IHVpZCA9PiBfZmlyZWJhc2VBdXRoLmN1cnJlbnRVc2VyPy51aWQgPz8gJyc7CgogIEBvdmVycmlkZQogIGJvb2wgZ2V0IGlzU2lnbmVkSW4gPT4gX2ZpcmViYXNlQXV0aC5jdXJyZW50VXNlciAhPSBudWxsOwoKICBAb3ZlcnJpZGUKICBGdXR1cmU8dm9pZD4gc2lnbk91dCgpIGFzeW5jIHsKICAgIHRyeSB7CiAgICAgIF9sb2dnZXIuZCgnQXR0ZW1wdGluZyB0byBzaWduIG91dCB1c2VyJyk7CiAgICAgIGF3YWl0IF9maXJlYmFzZUF1dGguc2lnbk91dCgpOwogICAgICBfbG9nZ2VyLmQoJ1VzZXIgc2lnbmVkIG91dCBzdWNjZXNzZnVsbHknKTsKCiAgICAgIC8vIExvZyBzdWNjZXNzZnVsIHNpZ24gb3V0CiAgICAgIEF1dGhBbmFseXRpY3MubG9nU2lnbk91dFN1Y2Nlc3MoKTsKICAgIH0gY2F0Y2ggKGUsIHMpIHsKICAgICAgX2xvZ2dlci5lKCdFcnJvciBzaWduaW5nIG91dCB1c2VyOiAkZScsIGUsIHMpOwoKICAgICAgLy8gTG9nIHNpZ24gb3V0IGVycm9yCiAgICAgIEF1dGhBbmFseXRpY3MubG9nU2lnbk91dEVycm9yKGVycm9yTWVzc2FnZTogZS50b1N0cmluZygpKTsKCiAgICAgIHJldGhyb3c7CiAgICB9CiAgfQp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/services/index.dart", + "data": + "Ly9HRU5FUkFURUQgQkFSUkVMIEZJTEUKZXhwb3J0ICdhdXRoX3NlcnZpY2UuZGFydCc7CmV4cG9ydCAnYXV0aF9zZXJ2aWNlX2ltcC5kYXJ0JzsK", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/data/token/firebase_auth_token_provider.dart", + "data": + "e3sjbmV0d29ya319aW1wb3J0ICdkYXJ0OmFzeW5jJzsKCmltcG9ydCAncGFja2FnZTpjb3JlL2NvcmUuZGFydCc7CmltcG9ydCAncGFja2FnZTpkaS9kaS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZpcmViYXNlX2F1dGgvZmlyZWJhc2VfYXV0aC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOm5ldHdvcmsvbmV0d29yay5kYXJ0JzsKCmZpbmFsIF90YWcgPSAnRmlyZWJhc2VBdXRoVG9rZW5Qcm92aWRlcic7CgpjbGFzcyBGaXJlYmFzZUF1dGhUb2tlblByb3ZpZGVyIGltcGxlbWVudHMgQXV0aFRva2VuUHJvdmlkZXIgewogIEZpcmViYXNlQXV0aFRva2VuUHJvdmlkZXIoKSA6IF9sb2dnZXIgPSBkaS5nZXQ8TG9nZ2VyPigpIHsKICAgIF9pbml0aWFsaXplVG9rZW5TdHJlYW0oKTsKICB9CgogIGZpbmFsIEZpcmViYXNlQXV0aCBfZmlyZWJhc2VBdXRoID0gRmlyZWJhc2VBdXRoLmluc3RhbmNlOwogIGZpbmFsIExvZ2dlciBfbG9nZ2VyOwoKICBsYXRlIGZpbmFsIFN0cmVhbUNvbnRyb2xsZXI8U3RyaW5nPz4gX3Rva2VuQ29udHJvbGxlcjsKICBTdHJlYW1TdWJzY3JpcHRpb248VXNlcj8+PyBfYXV0aFN0YXRlU3Vic2NyaXB0aW9uOwogIFN0cmluZz8gX2NhY2hlZFRva2VuOwogIERhdGVUaW1lPyBfdG9rZW5FeHBpcnk7CgogIHZvaWQgX2luaXRpYWxpemVUb2tlblN0cmVhbSgpIHsKICAgIF90b2tlbkNvbnRyb2xsZXIgPSBTdHJlYW1Db250cm9sbGVyPFN0cmluZz8+LmJyb2FkY2FzdCgpOwoKICAgIC8vIExpc3RlbiB0byBhdXRoIHN0YXRlIGNoYW5nZXMKICAgIF9hdXRoU3RhdGVTdWJzY3JpcHRpb24gPSBfZmlyZWJhc2VBdXRoLmF1dGhTdGF0ZUNoYW5nZXMoKS5saXN0ZW4oCiAgICAgIChVc2VyPyB1c2VyKSBhc3luYyB7CiAgICAgICAgaWYgKHVzZXIgIT0gbnVsbCkgewogICAgICAgICAgdHJ5IHsKICAgICAgICAgICAgZmluYWwgdG9rZW4gPSBhd2FpdCB1c2VyLmdldElkVG9rZW4oKTsKICAgICAgICAgICAgX2NhY2hlZFRva2VuID0gdG9rZW47CiAgICAgICAgICAgIF9zZXRUb2tlbkV4cGlyeSh0b2tlbiEpOwogICAgICAgICAgICBfdG9rZW5Db250cm9sbGVyLmFkZCh0b2tlbik7CiAgICAgICAgICAgIF9sb2dnZXIuZCgn8J+UkCRfdGFnOiBBdXRoIHRva2VuIHVwZGF0ZWQgZm9yIHVzZXI6ICR7dXNlci51aWR9Jyk7CiAgICAgICAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgICAgICAgIF9sb2dnZXIuZSgKICAgICAgICAgICAgICAn8J+aqCRfdGFnOiBFcnJvciBnZXR0aW5nIHRva2VuIGZvciB1c2VyOiAke3VzZXIudWlkfScsCiAgICAgICAgICAgICAgZSwKICAgICAgICAgICAgICBzLAogICAgICAgICAgICApOwogICAgICAgICAgICBfY2FjaGVkVG9rZW4gPSBudWxsOwogICAgICAgICAgICBfdG9rZW5FeHBpcnkgPSBudWxsOwogICAgICAgICAgICBfdG9rZW5Db250cm9sbGVyLmFkZChudWxsKTsKICAgICAgICAgIH0KICAgICAgICB9IGVsc2UgewogICAgICAgICAgX2NhY2hlZFRva2VuID0gbnVsbDsKICAgICAgICAgIF90b2tlbkV4cGlyeSA9IG51bGw7CiAgICAgICAgICBfdG9rZW5Db250cm9sbGVyLmFkZChudWxsKTsKICAgICAgICAgIF9sb2dnZXIuZCgn8J+UkyRfdGFnOiBVc2VyIHNpZ25lZCBvdXQsIHRva2VuIGNsZWFyZWQnKTsKICAgICAgICB9CiAgICAgIH0sCiAgICAgIG9uRXJyb3I6IChlLCBzKSB7CiAgICAgICAgX2xvZ2dlci5lKCfwn5qoICRfdGFnOiBBdXRoIHN0YXRlIGNoYW5nZSBlcnJvcicsIGUsIHMpOwogICAgICAgIF90b2tlbkNvbnRyb2xsZXIuYWRkKG51bGwpOwogICAgICB9LAogICAgKTsKICB9CgogIHZvaWQgX3NldFRva2VuRXhwaXJ5KFN0cmluZyB0b2tlbikgewogICAgdHJ5IHsKICAgICAgLy8gRmlyZWJhc2UgSUQgdG9rZW5zIGFyZSB0eXBpY2FsbHkgdmFsaWQgZm9yIDEgaG91cgogICAgICAvLyBXZSdsbCByZWZyZXNoIDUgbWludXRlcyBiZWZvcmUgZXhwaXJ5CiAgICAgIF90b2tlbkV4cGlyeSA9IERhdGVUaW1lLm5vdygpLmFkZChjb25zdCBEdXJhdGlvbihtaW51dGVzOiA1NSkpOwogICAgfSBjYXRjaCAoZSkgewogICAgICBfbG9nZ2VyLncoJ+KaoO+4jyRfdGFnOiBDb3VsZCBub3QgZGV0ZXJtaW5lIHRva2VuIGV4cGlyeSwgdXNpbmcgZGVmYXVsdCAkZScpOwogICAgICBfdG9rZW5FeHBpcnkgPSBEYXRlVGltZS5ub3coKS5hZGQoY29uc3QgRHVyYXRpb24obWludXRlczogNTUpKTsKICAgIH0KICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTxTdHJpbmc/PiBnZXRWYWxpZFRva2VuKCkgYXN5bmMgewogICAgZmluYWwgdXNlciA9IF9maXJlYmFzZUF1dGguY3VycmVudFVzZXI7CiAgICBpZiAodXNlciA9PSBudWxsKSB7CiAgICAgIF9sb2dnZXIuZCgn8J+UkyRfdGFnOiBObyBhdXRoZW50aWNhdGVkIHVzZXIgYXZhaWxhYmxlJyk7CiAgICAgIHJldHVybiBudWxsOwogICAgfQoKICAgIC8vIENoZWNrIGlmIGNhY2hlZCB0b2tlbiBpcyBzdGlsbCB2YWxpZAogICAgaWYgKF9jYWNoZWRUb2tlbiAhPSBudWxsICYmICFpc1Rva2VuRXhwaXJlZCkgewogICAgICBfbG9nZ2VyLmQoJ/CflJAkX3RhZzogVXNpbmcgY2FjaGVkIHRva2VuJyk7CiAgICAgIHJldHVybiBfY2FjaGVkVG9rZW47CiAgICB9CgogICAgLy8gR2V0IGZyZXNoIHRva2VuCiAgICB0cnkgewogICAgICBfbG9nZ2VyLmQoJ/CflIQkX3RhZzogRmV0Y2hpbmcgZnJlc2ggdG9rZW4gZm9yIHVzZXI6ICR7dXNlci51aWR9Jyk7CiAgICAgIGZpbmFsIHRva2VuID0gYXdhaXQgdXNlci5nZXRJZFRva2VuKHRydWUpOyAvLyBGb3JjZSByZWZyZXNoCiAgICAgIF9jYWNoZWRUb2tlbiA9IHRva2VuOwogICAgICBfc2V0VG9rZW5FeHBpcnkodG9rZW4hKTsKICAgICAgX3Rva2VuQ29udHJvbGxlci5hZGQodG9rZW4pOwogICAgICByZXR1cm4gdG9rZW47CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2dnZXIuZSgn8J+aqCRfdGFnOiBFcnJvciBnZXR0aW5nIGZyZXNoIHRva2VuJywgZSwgcyk7CiAgICAgIF9jYWNoZWRUb2tlbiA9IG51bGw7CiAgICAgIF90b2tlbkV4cGlyeSA9IG51bGw7CiAgICAgIHJldHVybiBudWxsOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgU3RyaW5nPyBnZXRDdXJyZW50VG9rZW4oKSB7CiAgICByZXR1cm4gX2NhY2hlZFRva2VuOwogIH0KCiAgQG92ZXJyaWRlCiAgU3RyZWFtPFN0cmluZz8+IGdldCB0b2tlblN0cmVhbSA9PiBfdG9rZW5Db250cm9sbGVyLnN0cmVhbTsKCiAgQG92ZXJyaWRlCiAgRnV0dXJlPFN0cmluZz8+IHJlZnJlc2hUb2tlbigpIGFzeW5jIHsKICAgIGZpbmFsIHVzZXIgPSBfZmlyZWJhc2VBdXRoLmN1cnJlbnRVc2VyOwogICAgaWYgKHVzZXIgPT0gbnVsbCkgewogICAgICBfbG9nZ2VyLncoJ/CflJMkX3RhZzogQ2Fubm90IHJlZnJlc2ggdG9rZW4gLSBubyBhdXRoZW50aWNhdGVkIHVzZXInKTsKICAgICAgcmV0dXJuIG51bGw7CiAgICB9CgogICAgdHJ5IHsKICAgICAgX2xvZ2dlci5kKCfwn5SEJF90YWc6IFJlZnJlc2hpbmcgdG9rZW4gZm9yIHVzZXI6ICR7dXNlci51aWR9Jyk7CiAgICAgIGZpbmFsIHRva2VuID0gYXdhaXQgdXNlci5nZXRJZFRva2VuKHRydWUpOyAvLyBGb3JjZSByZWZyZXNoCiAgICAgIF9jYWNoZWRUb2tlbiA9IHRva2VuOwogICAgICBfc2V0VG9rZW5FeHBpcnkodG9rZW4hKTsKICAgICAgX3Rva2VuQ29udHJvbGxlci5hZGQodG9rZW4pOwogICAgICBfbG9nZ2VyLmkoJ+KchSRfdGFnOiBUb2tlbiByZWZyZXNoZWQgc3VjY2Vzc2Z1bGx5Jyk7CiAgICAgIHJldHVybiB0b2tlbjsKICAgIH0gY2F0Y2ggKGUsIHMpIHsKICAgICAgX2xvZ2dlci5lKCfwn5qoJF90YWc6IFRva2VuIHJlZnJlc2ggZmFpbGVkJywgZSwgcyk7CiAgICAgIF9jYWNoZWRUb2tlbiA9IG51bGw7CiAgICAgIF90b2tlbkV4cGlyeSA9IG51bGw7CiAgICAgIF90b2tlbkNvbnRyb2xsZXIuYWRkKG51bGwpOwogICAgICByZXR1cm4gbnVsbDsKICAgIH0KICB9CgogIEBvdmVycmlkZQogIGJvb2wgZ2V0IGlzQXV0aGVudGljYXRlZCA9PiBfZmlyZWJhc2VBdXRoLmN1cnJlbnRVc2VyICE9IG51bGw7CgogIEBvdmVycmlkZQogIGJvb2wgZ2V0IGlzVG9rZW5FeHBpcmVkIHsKICAgIGlmIChfdG9rZW5FeHBpcnkgPT0gbnVsbCkgcmV0dXJuIHRydWU7CiAgICByZXR1cm4gRGF0ZVRpbWUubm93KCkuaXNBZnRlcihfdG9rZW5FeHBpcnkhKTsKICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTx2b2lkPiBzaWduT3V0KCkgYXN5bmMgewogICAgdHJ5IHsKICAgICAgX2xvZ2dlci5kKCfwn5STJF90YWc6IFNpZ25pbmcgb3V0IHVzZXInKTsKICAgICAgYXdhaXQgX2ZpcmViYXNlQXV0aC5zaWduT3V0KCk7CiAgICAgIF9jYWNoZWRUb2tlbiA9IG51bGw7CiAgICAgIF90b2tlbkV4cGlyeSA9IG51bGw7CiAgICAgIF90b2tlbkNvbnRyb2xsZXIuYWRkKG51bGwpOwogICAgICBfbG9nZ2VyLmkoJ+KchSRfdGFnOiBVc2VyIHNpZ25lZCBvdXQgc3VjY2Vzc2Z1bGx5Jyk7CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2dnZXIuZSgn8J+aqCRfdGFnOiBFcnJvciBzaWduaW5nIG91dCB1c2VyJywgZSwgcyk7CiAgICAgIHJldGhyb3c7CiAgICB9CiAgfQoKICB2b2lkIGRpc3Bvc2UoKSB7CiAgICBfYXV0aFN0YXRlU3Vic2NyaXB0aW9uPy5jYW5jZWwoKTsKICAgIF90b2tlbkNvbnRyb2xsZXIuY2xvc2UoKTsKICAgIF9sb2dnZXIuZCgn8J+nuSRfdGFnOiBkaXNwb3NlZCcpOwogIH0KfXt7L25ldHdvcmt9fQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/router/router.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmF1dGgvY29uc3RhbnRzL2luZGV4LmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6YXV0aC9kYXRhL3NlcnZpY2VzL2F1dGhfc2VydmljZS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmF1dGgvdWkvc2NyZWVucy9pbmRleC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmNvcmUvY29yZS5kYXJ0JyBhcyBjb3JlOwppbXBvcnQgJ3BhY2thZ2U6ZGkvZGkuZGFydCc7CmltcG9ydCAncGFja2FnZTpmbHV0dGVyL21hdGVyaWFsLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Z29fcm91dGVyL2dvX3JvdXRlci5kYXJ0JzsKCmNsYXNzIEF1dGhSb3V0ZXIgZXh0ZW5kcyBjb3JlLkNvcmVSb3V0ZXIgewogIGZpbmFsIEZ1bmN0aW9uKEJ1aWxkQ29udGV4dCBjb250ZXh0KSBvblNpZ25lZEluOwogIGZpbmFsIEZ1bmN0aW9uKEJ1aWxkQ29udGV4dCBjb250ZXh0KSBvblNpZ25lZFVwOwoKICBBdXRoUm91dGVyKHtyZXF1aXJlZCB0aGlzLm9uU2lnbmVkSW4sIHJlcXVpcmVkIHRoaXMub25TaWduZWRVcH0pOwoKICBAb3ZlcnJpZGUKICBMaXN0PFJvdXRlQmFzZT4gZ2V0IHJvdXRlcyA9PiBbCiAgICBHb1JvdXRlKAogICAgICBwYXRoOiBBdXRoUm91dGVzLnNpZ25JbiwKICAgICAgYnVpbGRlcjogKGNvbnRleHQsIHN0YXRlKSA9PiBTaWduSW5TY3JlZW4ob25TaWduZWRJbjogb25TaWduZWRJbiksCiAgICApLAogICAgR29Sb3V0ZSgKICAgICAgcGF0aDogQXV0aFJvdXRlcy5zaWduVXAsCiAgICAgIGJ1aWxkZXI6IChjb250ZXh0LCBzdGF0ZSkgPT4gUmVnaXN0ZXJTY3JlZW4ob25TaWduZWRVcDogb25TaWduZWRVcCksCiAgICApLAogICAgR29Sb3V0ZSgKICAgICAgcGF0aDogQXV0aFJvdXRlcy5mb3Jnb3RQYXNzd29yZCwKICAgICAgYnVpbGRlcjogKGNvbnRleHQsIHN0YXRlKSB7CiAgICAgICAgZmluYWwgZW1haWwgPSBzdGF0ZS51cmkucXVlcnlQYXJhbWV0ZXJzW2NvcmUuS2V5cy5lbWFpbF07CiAgICAgICAgcmV0dXJuIEZvcmdvdFBhc3N3b3JkU2NyZWVuKGVtYWlsOiBlbWFpbCk7CiAgICAgIH0sCiAgICApLAogIF07Cn0KCmZpbmFsIGNsYXNzIEdvQXV0aFJvdXRlIGV4dGVuZHMgR29Sb3V0ZSB7CiAgLy8vIENyZWF0ZSBhIHNlY3VyZSByb3V0ZSB0aGF0IHJlcXVpcmVzIGF1dGhlbnRpY2F0aW9uCiAgR29BdXRoUm91dGUoewogICAgcmVxdWlyZWQgc3VwZXIucGF0aCwKICAgIHJlcXVpcmVkIHN1cGVyLmJ1aWxkZXIsCiAgICBMaXN0PEdvUm91dGU+IHN1cGVyLnJvdXRlcyA9IGNvbnN0IFtdLAogICAgc3VwZXIucGFnZUJ1aWxkZXIsCiAgfSkgOiBzdXBlcigKICAgICAgICAgcmVkaXJlY3Q6IChjb250ZXh0LCBzdGF0ZSkgYXN5bmMgewogICAgICAgICAgIGZpbmFsIGF1dGhTZXJ2aWNlID0gZGkuZ2V0PEF1dGhTZXJ2aWNlPigpOwoKICAgICAgICAgICBpZiAoIWF1dGhTZXJ2aWNlLmlzU2lnbmVkSW4pIHsKICAgICAgICAgICAgIHJldHVybiBBdXRoUm91dGVzLnNpZ25JbjsKICAgICAgICAgICB9CgogICAgICAgICAgIHJldHVybiBudWxsOwogICAgICAgICB9LAogICAgICAgKTsKfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/components/footer_builder.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmF1dGgvY29uc3RhbnRzL2luZGV4LmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Y29yZS9sb2dnZXIvbG9nZ2VyLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZGkvZGkuZGFydCc7CmltcG9ydCAncGFja2FnZTpmaXJlYmFzZV91aV9hdXRoL2ZpcmViYXNlX3VpX2F1dGguZGFydCc7CmltcG9ydCAncGFja2FnZTpmbHV0dGVyL21hdGVyaWFsLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Z29fcm91dGVyL2dvX3JvdXRlci5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmxvY2FsaXphdGlvbi9sb2NhbGl6YXRpb24uZGFydCc7CgplbnVtIEZvb3RlclR5cGUgeyBzaWduSW4sIHJlZ2lzdGVyIH0KCldpZGdldCBmb290ZXJCdWlsZGVyKEJ1aWxkQ29udGV4dCBjb250ZXh0LCBBdXRoQWN0aW9uIGFjdGlvbiwgRm9vdGVyVHlwZSB0eXBlKSB7CiAgZmluYWwgbG9nZ2VyID0gZGkuZ2V0PExvZ2dlcj4oKTsKICBzd2l0Y2ggKHR5cGUpIHsKICAgIGNhc2UgRm9vdGVyVHlwZS5zaWduSW46CiAgICAgIHJldHVybiBfQXV0aEZvb3RlcigKICAgICAgICB0aXRsZTogc3RyaW5ncy5hdXRoLmRvbnRfaGF2ZV9hY2NvdW50LAogICAgICAgIGxpbmtUZXh0OiBzdHJpbmdzLmF1dGgucmVnaXN0ZXIsCiAgICAgICAgb25UYXA6ICgpIHsKICAgICAgICAgIGxvZ2dlci5kKCdOYXZpZ2F0aW5nIHRvIHJlZ2lzdGVyIHNjcmVlbicpOwoKICAgICAgICAgIGNvbnRleHQuZ28oQXV0aFJvdXRlcy5zaWduVXApOwogICAgICAgIH0sCiAgICAgICk7CiAgICBjYXNlIEZvb3RlclR5cGUucmVnaXN0ZXI6CiAgICAgIHJldHVybiBfQXV0aEZvb3RlcigKICAgICAgICB0aXRsZTogc3RyaW5ncy5hdXRoLmFscmVhZHlfaGF2ZV9hY2NvdW50LAogICAgICAgIGxpbmtUZXh0OiBzdHJpbmdzLmF1dGguc2lnbl9pbiwKICAgICAgICBvblRhcDogKCkgewogICAgICAgICAgbG9nZ2VyLmQoJ05hdmlnYXRpbmcgdG8gc2lnbiBpbiBzY3JlZW4nKTsKCiAgICAgICAgICBjb250ZXh0LmdvKEF1dGhSb3V0ZXMuc2lnbkluKTsKICAgICAgICB9LAogICAgICApOwogIH0KfQoKY2xhc3MgX0F1dGhGb290ZXIgZXh0ZW5kcyBTdGF0ZWxlc3NXaWRnZXQgewogIGZpbmFsIFN0cmluZyB0aXRsZTsKICBmaW5hbCBTdHJpbmcgbGlua1RleHQ7CiAgZmluYWwgVm9pZENhbGxiYWNrIG9uVGFwOwoKICBjb25zdCBfQXV0aEZvb3Rlcih7CiAgICByZXF1aXJlZCB0aGlzLnRpdGxlLAogICAgcmVxdWlyZWQgdGhpcy5saW5rVGV4dCwKICAgIHJlcXVpcmVkIHRoaXMub25UYXAsCiAgfSk7CgogIEBvdmVycmlkZQogIFdpZGdldCBidWlsZChCdWlsZENvbnRleHQgY29udGV4dCkgewogICAgcmV0dXJuIFBhZGRpbmcoCiAgICAgIHBhZGRpbmc6IGNvbnN0IEVkZ2VJbnNldHMuc3ltbWV0cmljKHZlcnRpY2FsOiAxNi4wKSwKICAgICAgY2hpbGQ6IFJvdygKICAgICAgICBtYWluQXhpc0FsaWdubWVudDogTWFpbkF4aXNBbGlnbm1lbnQuY2VudGVyLAogICAgICAgIGNoaWxkcmVuOiBbCiAgICAgICAgICBUZXh0KHRpdGxlKSwKICAgICAgICAgIEdlc3R1cmVEZXRlY3RvcigKICAgICAgICAgICAgb25UYXA6IG9uVGFwLAogICAgICAgICAgICBjaGlsZDogVGV4dCgKICAgICAgICAgICAgICBsaW5rVGV4dCwKICAgICAgICAgICAgICBzdHlsZTogVGhlbWUub2YoY29udGV4dCkudGV4dFRoZW1lLmJvZHlNZWRpdW0/LmNvcHlXaXRoKAogICAgICAgICAgICAgICAgY29sb3I6IFRoZW1lLm9mKGNvbnRleHQpLmNvbG9yU2NoZW1lLnByaW1hcnksCiAgICAgICAgICAgICAgICBmb250V2VpZ2h0OiBGb250V2VpZ2h0LmJvbGQsCiAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgKSwKICAgICAgICAgICksCiAgICAgICAgXSwKICAgICAgKSwKICAgICk7CiAgfQp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/components/header_builder.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmRlc2lnbl9zeXN0ZW0vZGVzaWduX3N5c3RlbS5kYXJ0JyBhcyBkczsKaW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvd2lkZ2V0cy5kYXJ0JzsKCi8vLyBCYW5uZXIgc2hvd24gYWJvdmUgdGhlIHNpZ24taW4gYW5kIHJlZ2lzdGVyIGZvcm1zLgovLy8KLy8vIFN3YXAgdGhpcyBmb3IgeW91ciBvd24gYXJ0d29yazogYWRkIGl0IHVuZGVyIHRoZSBhcHAncyBgYXNzZXRzOmAgaW4KLy8vIHB1YnNwZWMueWFtbCBhbmQgc3dpdGNoIHRvIGBkcy5IZWFkZXJUeXBlLmFzc2V0YCB3aXRoIGFuIGBhc3NldFBhdGhgLgpXaWRnZXQgaGVhZGVyQnVpbGRlcihCdWlsZENvbnRleHQgY29udGV4dCkgewogIHJldHVybiBjb25zdCBQYWRkaW5nKAogICAgcGFkZGluZzogRWRnZUluc2V0cy5hbGwoOCksCiAgICBjaGlsZDogZHMuSGVhZGVyKAogICAgICBpbWFnZVVybDogJ2h0dHBzOi8vcGljc3VtLnBob3Rvcy8xMjAwLzMwMCcsCiAgICApLAogICk7Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/forgot_password_screen.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmF1dGgvdWkvY29tcG9uZW50cy9oZWFkZXJfYnVpbGRlci5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZpcmViYXNlX3VpX2F1dGgvZmlyZWJhc2VfdWlfYXV0aC5kYXJ0JyBhcyB1aV9hdXRoOwppbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlci9tYXRlcmlhbC5kYXJ0JzsKCmNsYXNzIEZvcmdvdFBhc3N3b3JkU2NyZWVuIGV4dGVuZHMgU3RhdGVsZXNzV2lkZ2V0IHsKICBmaW5hbCBTdHJpbmc/IGVtYWlsOwoKICBjb25zdCBGb3Jnb3RQYXNzd29yZFNjcmVlbih7c3VwZXIua2V5LCB0aGlzLmVtYWlsfSk7CgogIEBvdmVycmlkZQogIFdpZGdldCBidWlsZChCdWlsZENvbnRleHQgY29udGV4dCkgewogICAgcmV0dXJuIHVpX2F1dGguRm9yZ290UGFzc3dvcmRTY3JlZW4oCiAgICAgIGVtYWlsOiBlbWFpbCwKICAgICAgaGVhZGVyQnVpbGRlcjogKGNvbnRleHQsIGNvbnN0cmFpbnRzLCBzaHJpbmtPZmZzZXQpIHsKICAgICAgICByZXR1cm4gaGVhZGVyQnVpbGRlcihjb250ZXh0KTsKICAgICAgfSwKICAgICk7CiAgfQp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/index.dart", + "data": + "Ly9HRU5FUkFURUQgQkFSUkVMIEZJTEUKZXhwb3J0ICdmb3Jnb3RfcGFzc3dvcmRfc2NyZWVuLmRhcnQnOwpleHBvcnQgJ3JlZ2lzdGVyX3NjcmVlbi5kYXJ0JzsKZXhwb3J0ICdzaWduX2luX3NjcmVlbi5kYXJ0JzsK", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/register_screen.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmF1dGgvYW5hbHl0aWNzL2FuYWx5dGljcy5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmF1dGgvdWkvY29tcG9uZW50cy9mb290ZXJfYnVpbGRlci5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmF1dGgvdWkvY29tcG9uZW50cy9oZWFkZXJfYnVpbGRlci5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmNvcmUvbG9nZ2VyL2xvZ2dlci5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRpL2RpLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZmlyZWJhc2VfdWlfYXV0aC9maXJlYmFzZV91aV9hdXRoLmRhcnQnIGFzIHVpX2F1dGg7CmltcG9ydCAncGFja2FnZTpmbHV0dGVyL21hdGVyaWFsLmRhcnQnOwoKY2xhc3MgUmVnaXN0ZXJTY3JlZW4gZXh0ZW5kcyBTdGF0ZWxlc3NXaWRnZXQgewogIGNvbnN0IFJlZ2lzdGVyU2NyZWVuKHtzdXBlci5rZXksIHJlcXVpcmVkIHRoaXMub25TaWduZWRVcH0pOwoKICBmaW5hbCB2b2lkIEZ1bmN0aW9uKEJ1aWxkQ29udGV4dCBjb250ZXh0KSBvblNpZ25lZFVwOwoKICBAb3ZlcnJpZGUKICBXaWRnZXQgYnVpbGQoQnVpbGRDb250ZXh0IGNvbnRleHQpIHsKICAgIGZpbmFsIGxvZ2dlciA9IGRpLmdldDxMb2dnZXI+KCk7CgogICAgcmV0dXJuIHVpX2F1dGguUmVnaXN0ZXJTY3JlZW4oCiAgICAgIHNob3dBdXRoQWN0aW9uU3dpdGNoOiBmYWxzZSwKICAgICAgYWN0aW9uczogWwogICAgICAgIHVpX2F1dGguQXV0aFN0YXRlQ2hhbmdlQWN0aW9uPHVpX2F1dGguU2lnbmVkSW4+KChjb250ZXh0LCBzdGF0ZSkgYXN5bmMgewogICAgICAgICAgbG9nZ2VyLmQoJ1VzZXIgcmVnaXN0ZXJlZCBzdWNjZXNzZnVsbHk6ICR7c3RhdGUudXNlcj8uZW1haWx9Jyk7CgogICAgICAgICAgZmluYWwgbWV0aG9kID0gQXV0aEFuYWx5dGljcy5nZXRBdXRoTWV0aG9kKHN0YXRlLnVzZXI/LnByb3ZpZGVyRGF0YSk7CgogICAgICAgICAgLy8gTG9nIHN1Y2Nlc3NmdWwgc2lnbiBpbiAod2hlbiB1c2VyIGFscmVhZHkgZXhpc3RzKQogICAgICAgICAgQXV0aEFuYWx5dGljcy5sb2dTaWduSW5TdWNjZXNzKAogICAgICAgICAgICBtZXRob2Q6IG1ldGhvZCwKICAgICAgICAgICAgdXNlckVtYWlsOiBzdGF0ZS51c2VyPy5lbWFpbCwKICAgICAgICAgICk7CgogICAgICAgICAgb25TaWduZWRVcChjb250ZXh0KTsKICAgICAgICB9KSwKICAgICAgICB1aV9hdXRoLkF1dGhTdGF0ZUNoYW5nZUFjdGlvbjx1aV9hdXRoLlVzZXJDcmVhdGVkPigoCiAgICAgICAgICBjb250ZXh0LAogICAgICAgICAgc3RhdGUsCiAgICAgICAgKSBhc3luYyB7CiAgICAgICAgICBsb2dnZXIuZCgKICAgICAgICAgICAgJ1VzZXIgY3JlYXRlZCBzdWNjZXNzZnVsbHk6ICR7c3RhdGUuY3JlZGVudGlhbC51c2VyPy5lbWFpbH0nLAogICAgICAgICAgKTsKCiAgICAgICAgICBmaW5hbCBtZXRob2QgPSBBdXRoQW5hbHl0aWNzLmdldEF1dGhNZXRob2QoCiAgICAgICAgICAgIHN0YXRlLmNyZWRlbnRpYWwudXNlcj8ucHJvdmlkZXJEYXRhLAogICAgICAgICAgKTsKCiAgICAgICAgICAvLyBMb2cgc3VjY2Vzc2Z1bCBzaWduIHVwCiAgICAgICAgICBBdXRoQW5hbHl0aWNzLmxvZ1NpZ25VcFN1Y2Nlc3MoCiAgICAgICAgICAgIG1ldGhvZDogbWV0aG9kLAogICAgICAgICAgICB1c2VyRW1haWw6IHN0YXRlLmNyZWRlbnRpYWwudXNlcj8uZW1haWwsCiAgICAgICAgICApOwoKICAgICAgICAgIC8vIExvZyB1c2VyIGNyZWF0aW9uIHN1Y2Nlc3MKICAgICAgICAgIEF1dGhBbmFseXRpY3MubG9nVXNlckNyZWF0aW9uU3VjY2VzcygKICAgICAgICAgICAgbWV0aG9kOiBtZXRob2QsCiAgICAgICAgICAgIHVzZXJFbWFpbDogc3RhdGUuY3JlZGVudGlhbC51c2VyPy5lbWFpbCwKICAgICAgICAgICk7CgogICAgICAgICAgb25TaWduZWRVcChjb250ZXh0KTsKICAgICAgICB9KSwKICAgICAgICB1aV9hdXRoLkF1dGhTdGF0ZUNoYW5nZUFjdGlvbjx1aV9hdXRoLkF1dGhGYWlsZWQ+KCgKICAgICAgICAgIGNvbnRleHQsCiAgICAgICAgICBzdGF0ZSwKICAgICAgICApIGFzeW5jIHsKICAgICAgICAgIGxvZ2dlci5lKCdSZWdpc3RyYXRpb24gZmFpbGVkOiAke3N0YXRlLmV4Y2VwdGlvbn0nKTsKCiAgICAgICAgICAvLyBMb2cgYXV0aCBlcnJvcgogICAgICAgICAgQXV0aEFuYWx5dGljcy5sb2dBdXRoRXJyb3IoCiAgICAgICAgICAgIGVycm9yVHlwZTogc3RhdGUuZXhjZXB0aW9uLnJ1bnRpbWVUeXBlLnRvU3RyaW5nKCksCiAgICAgICAgICAgIGZsb3dUeXBlOiAnc2lnbl91cCcsCiAgICAgICAgICAgIGVycm9yTWVzc2FnZTogc3RhdGUuZXhjZXB0aW9uLnRvU3RyaW5nKCksCiAgICAgICAgICAgIG1ldGhvZDogJ2VtYWlsJywKICAgICAgICAgICk7CiAgICAgICAgfSksCiAgICAgIF0sCiAgICAgIGZvb3RlckJ1aWxkZXI6IChjb250ZXh0LCBhY3Rpb24pIHsKICAgICAgICByZXR1cm4gZm9vdGVyQnVpbGRlcihjb250ZXh0LCBhY3Rpb24sIEZvb3RlclR5cGUucmVnaXN0ZXIpOwogICAgICB9LAogICAgICBoZWFkZXJCdWlsZGVyOiAoY29udGV4dCwgY29uc3RyYWludHMsIHNocmlua09mZnNldCkgewogICAgICAgIHJldHVybiBoZWFkZXJCdWlsZGVyKGNvbnRleHQpOwogICAgICB9LAogICAgKTsKICB9Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/screens/sign_in_screen.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmF1dGgvYW5hbHl0aWNzL2FuYWx5dGljcy5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmF1dGgvY29uc3RhbnRzL2luZGV4LmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6YXV0aC91aS9jb21wb25lbnRzL2Zvb3Rlcl9idWlsZGVyLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6YXV0aC91aS9jb21wb25lbnRzL2hlYWRlcl9idWlsZGVyLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Y29yZS9jb3JlLmRhcnQnIGFzIGNvcmU7CmltcG9ydCAncGFja2FnZTpjb3JlL2xvZ2dlci9sb2dnZXIuZGFydCc7CmltcG9ydCAncGFja2FnZTpkZXNpZ25fc3lzdGVtL3RvYXN0L3RvYXN0cy5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRpL2RpLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZmlyZWJhc2VfdWlfYXV0aC9maXJlYmFzZV91aV9hdXRoLmRhcnQnIGFzIHVpX2F1dGg7CmltcG9ydCAncGFja2FnZTpmbHV0dGVyL21hdGVyaWFsLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Z29fcm91dGVyL2dvX3JvdXRlci5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmxvY2FsaXphdGlvbi9sb2NhbGl6YXRpb24uZGFydCc7CgpjbGFzcyBTaWduSW5TY3JlZW4gZXh0ZW5kcyBTdGF0ZWxlc3NXaWRnZXQgewogIGNvbnN0IFNpZ25JblNjcmVlbih7c3VwZXIua2V5LCByZXF1aXJlZCB0aGlzLm9uU2lnbmVkSW59KTsKCiAgZmluYWwgRnVuY3Rpb24oQnVpbGRDb250ZXh0IGNvbnRleHQpIG9uU2lnbmVkSW47CgogIEBvdmVycmlkZQogIFdpZGdldCBidWlsZChCdWlsZENvbnRleHQgY29udGV4dCkgewogICAgZmluYWwgbG9nZ2VyID0gZGkuZ2V0PExvZ2dlcj4oKTsKCiAgICByZXR1cm4gdWlfYXV0aC5TaWduSW5TY3JlZW4oCiAgICAgIHNob3dBdXRoQWN0aW9uU3dpdGNoOiBmYWxzZSwKICAgICAgb2F1dGhCdXR0b25WYXJpYW50OiB1aV9hdXRoLk9BdXRoQnV0dG9uVmFyaWFudC5pY29uX2FuZF90ZXh0LAogICAgICBhY3Rpb25zOiBbCiAgICAgICAgdWlfYXV0aC5Gb3Jnb3RQYXNzd29yZEFjdGlvbigoY29udGV4dCwgZW1haWwpIHsKICAgICAgICAgIGxvZ2dlci5kKCdOYXZpZ2F0aW5nIHRvIGZvcmdvdCBwYXNzd29yZCBzY3JlZW4gZm9yIGVtYWlsOiAkZW1haWwnKTsKCiAgICAgICAgICBmaW5hbCB1cmkgPSBVcmkoCiAgICAgICAgICAgIHBhdGg6IEF1dGhSb3V0ZXMuZm9yZ290UGFzc3dvcmQsCiAgICAgICAgICAgIHF1ZXJ5UGFyYW1ldGVyczoge2NvcmUuS2V5cy5lbWFpbDogZW1haWx9LAogICAgICAgICAgKTsKICAgICAgICAgIGNvbnRleHQucHVzaCh1cmkudG9TdHJpbmcoKSk7CiAgICAgICAgfSksCiAgICAgICAgdWlfYXV0aC5BdXRoU3RhdGVDaGFuZ2VBY3Rpb248dWlfYXV0aC5TaWduZWRJbj4oKGNvbnRleHQsIHN0YXRlKSBhc3luYyB7CiAgICAgICAgICBsb2dnZXIuZCgnVXNlciBzaWduZWQgaW4gc3VjY2Vzc2Z1bGx5OiAke3N0YXRlLnVzZXI/LmVtYWlsfScpOwoKICAgICAgICAgIGZpbmFsIG1ldGhvZCA9IEF1dGhBbmFseXRpY3MuZ2V0QXV0aE1ldGhvZChzdGF0ZS51c2VyPy5wcm92aWRlckRhdGEpOwoKICAgICAgICAgIC8vIExvZyBzdWNjZXNzZnVsIHNpZ24gaW4KICAgICAgICAgIEF1dGhBbmFseXRpY3MubG9nU2lnbkluU3VjY2VzcygKICAgICAgICAgICAgbWV0aG9kOiBtZXRob2QsCiAgICAgICAgICAgIHVzZXJFbWFpbDogc3RhdGUudXNlcj8uZW1haWwsCiAgICAgICAgICApOwoKICAgICAgICAgIG9uU2lnbmVkSW4oY29udGV4dCk7CiAgICAgICAgfSksCiAgICAgICAgdWlfYXV0aC5BdXRoU3RhdGVDaGFuZ2VBY3Rpb248dWlfYXV0aC5Vc2VyQ3JlYXRlZD4oKAogICAgICAgICAgY29udGV4dCwKICAgICAgICAgIHN0YXRlLAogICAgICAgICkgYXN5bmMgewogICAgICAgICAgbG9nZ2VyLmQoCiAgICAgICAgICAgICdVc2VyIHNpZ25lZCBpbiBzdWNjZXNzZnVsbHk6ICR7c3RhdGUuY3JlZGVudGlhbC51c2VyPy5lbWFpbH0nLAogICAgICAgICAgKTsKCiAgICAgICAgICBmaW5hbCBtZXRob2QgPSBBdXRoQW5hbHl0aWNzLmdldEF1dGhNZXRob2QoCiAgICAgICAgICAgIHN0YXRlLmNyZWRlbnRpYWwudXNlcj8ucHJvdmlkZXJEYXRhLAogICAgICAgICAgKTsKCiAgICAgICAgICAvLyBMb2cgc3VjY2Vzc2Z1bCB1c2VyIGNyZWF0aW9uIChmcm9tIHNpZ24gaW4gc2NyZWVuKQogICAgICAgICAgQXV0aEFuYWx5dGljcy5sb2dVc2VyQ3JlYXRpb25TdWNjZXNzKAogICAgICAgICAgICBtZXRob2Q6IG1ldGhvZCwKICAgICAgICAgICAgdXNlckVtYWlsOiBzdGF0ZS5jcmVkZW50aWFsLnVzZXI/LmVtYWlsLAogICAgICAgICAgKTsKCiAgICAgICAgICBvblNpZ25lZEluKGNvbnRleHQpOwogICAgICAgIH0pLAogICAgICAgIHVpX2F1dGguQXV0aFN0YXRlQ2hhbmdlQWN0aW9uPHVpX2F1dGguQXV0aEZhaWxlZD4oKAogICAgICAgICAgY29udGV4dCwKICAgICAgICAgIHN0YXRlLAogICAgICAgICkgYXN5bmMgewogICAgICAgICAgbG9nZ2VyLmUoJ1NpZ24taW4gZmFpbGVkOiAke3N0YXRlLmV4Y2VwdGlvbn0nKTsKCiAgICAgICAgICAvLyBMb2cgYXV0aCBlcnJvcgogICAgICAgICAgQXV0aEFuYWx5dGljcy5sb2dBdXRoRXJyb3IoCiAgICAgICAgICAgIGVycm9yVHlwZTogc3RhdGUuZXhjZXB0aW9uLnJ1bnRpbWVUeXBlLnRvU3RyaW5nKCksCiAgICAgICAgICAgIGZsb3dUeXBlOiAnc2lnbl9pbicsCiAgICAgICAgICAgIGVycm9yTWVzc2FnZTogc3RhdGUuZXhjZXB0aW9uLnRvU3RyaW5nKCksCiAgICAgICAgICApOwoKICAgICAgICAgIFRvYXN0LmVycm9yKGNvbnRleHQsIG1lc3NhZ2U6IHN0cmluZ3MuZXJyb3JzLmRlZmF1bHRfZXJyb3JfbWVzc2FnZSk7CiAgICAgICAgfSksCiAgICAgICAgLy9UT0RPOiBIYW5kbGUgb3RoZXIgQXV0aFN0YXRlCiAgICAgIF0sCiAgICAgIGZvb3RlckJ1aWxkZXI6IChjb250ZXh0LCBhY3Rpb24pIHsKICAgICAgICByZXR1cm4gZm9vdGVyQnVpbGRlcihjb250ZXh0LCBhY3Rpb24sIEZvb3RlclR5cGUuc2lnbkluKTsKICAgICAgfSwKICAgICAgaGVhZGVyQnVpbGRlcjogKGNvbnRleHQsIGNvbnN0cmFpbnRzLCBzaHJpbmtPZmZzZXQpIHsKICAgICAgICByZXR1cm4gaGVhZGVyQnVpbGRlcihjb250ZXh0KTsKICAgICAgfSwKICAgICk7CiAgfQp9Cg==", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/features/{{auth_dir}}/pubspec.yaml", + "data": + "bmFtZTogYXV0aApkZXNjcmlwdGlvbjogIkZpcmViYXNlIEF1dGhlbnRpY2F0aW9uIChlbWFpbCwgR29vZ2xlLCBBcHBsZSkgd2l0aCBnb19yb3V0ZXIgaW50ZWdyYXRpb24gZm9yIHt7bmFtZS50aXRsZUNhc2UoKX19LiIKdmVyc2lvbjogMC4xLjAKcHVibGlzaF90bzogJ25vbmUnCgplbnZpcm9ubWVudDoKICBzZGs6IF4zLjguMAogIGZsdXR0ZXI6ICI+PTMuMjkuMCIKCmRlcGVuZGVuY2llczoKICBmbHV0dGVyOgogICAgc2RrOiBmbHV0dGVyCiAgZXF1YXRhYmxlOiBeMi4wLjcKICBmaXJlYmFzZV9hdXRoOiBeNS42LjAKICBmaXJlYmFzZV9jb3JlOiBeMy4xNC4wCiAgZmlyZWJhc2VfdWlfYXV0aDogXjEuMTcuMAogIGZpcmViYXNlX3VpX29hdXRoX2FwcGxlOiBeMS4zLjIKICBmaXJlYmFzZV91aV9vYXV0aF9nb29nbGU6IF4xLjQuMgogIGdvX3JvdXRlcjogXjE1LjEuMwogIGpzb25fYW5ub3RhdGlvbjogXjQuOS4wCiAgY29yZToKICAgIHBhdGg6IC4uLy4uL3BhY2thZ2VzL2NvcmUKICBkZXNpZ25fc3lzdGVtOgogICAgcGF0aDogLi4vLi4vcGFja2FnZXMvZGVzaWduX3N5c3RlbQogIGRpOgogICAgcGF0aDogLi4vLi4vcGFja2FnZXMvZGkKICBsb2NhbGl6YXRpb246CiAgICBwYXRoOiAuLi8uLi9wYWNrYWdlcy9sb2NhbGl6YXRpb24Ke3sjZmlyZXN0b3JlfX0gIGNsb3VkX2ZpcmVzdG9yZTogXjUuNi4wCnt7L2ZpcmVzdG9yZX19e3sjbmV0d29ya319ICBuZXR3b3JrOgogICAgcGF0aDogLi4vLi4vcGFja2FnZXMvbmV0d29yawp7ey9uZXR3b3JrfX17eyNhbmFseXRpY3N9fSAgYW5hbHl0aWNzOgogICAgcGF0aDogLi4vLi4vcGFja2FnZXMvYW5hbHl0aWNzCnt7L2FuYWx5dGljc319CmRldl9kZXBlbmRlbmNpZXM6CiAgZmx1dHRlcl90ZXN0OgogICAgc2RrOiBmbHV0dGVyCiAgZmx1dHRlcl9saW50czogXjUuMC4wCgpmbHV0dGVyOgo=", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/features/{{auth_dir}}/README.md", + "data": + "IyBBdXRoCgpBIE1lbG9zLW1hbmFnZWQgcHJvamVjdCBmb3IgbW9uby1yZXBvLCBjcmVhdGVkIHVzaW5nIE5vblN0b3AgQ0xJLgoKWyFbbm9uc3RvcF9jbGldKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2Uvc3RhcnRlZCUyMHdpdGgtbm9uc3RvcF9jbGktMTY2QzRFLnN2Zz9zdHlsZT1mbGF0LXNxdWFyZSldKGh0dHBzOi8vcHViLmRldi9wYWNrYWdlcy9ub25zdG9wX2NsaSkKWyFbbWVsb3NdKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2UvbWFpbnRhaW5lZCUyMHdpdGgtbWVsb3MtZjcwMGZmLnN2Zz9zdHlsZT1mbGF0LXNxdWFyZSldKGh0dHBzOi8vZ2l0aHViLmNvbS9pbnZlcnRhc2UvbWVsb3MpCgojIyBJbnN0YWxsYXRpb24g8J+SuwoKKirinZcgSW4gb3JkZXIgdG8gc3RhcnQgdXNpbmcgQXV0aCB5b3UgbXVzdCBoYXZlIHRoZSBbRmx1dHRlciBTREtdW2ZsdXR0ZXJfaW5zdGFsbF9saW5rXSBpbnN0YWxsZWQgb24geW91ciBtYWNoaW5lLioqCgpJbnN0YWxsIHZpYSBgZGFydCBwdWIgYWRkYDoKCmBgYHNoCmRhcnQgcHViIGFkZCBhdXRoCmBgYAoKW2ZsdXR0ZXJfaW5zdGFsbF9saW5rXTogaHR0cHM6Ly9kb2NzLmZsdXR0ZXIuZGV2L2dldC1zdGFydGVkL2luc3RhbGw=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/dashboard.dart", + "data": + "bGlicmFyeTsKCmV4cG9ydCAncm91dGVyL2Rhc2hib2FyZF9yb3V0ZXIuZGFydCc7CmV4cG9ydCAndWkvc2NyZWVucy9pbmRleC5kYXJ0JzsKZXhwb3J0ICd1aS93aWRnZXRzL2luZGV4LmRhcnQnOwo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/router/dashboard_router.dart", + "data": + "e3sjYXV0aH19aW1wb3J0ICdwYWNrYWdlOmF1dGgvYXV0aC5kYXJ0JyBhcyBhdXRoOwp7ey9hdXRofX1pbXBvcnQgJ3BhY2thZ2U6ZGFzaGJvYXJkL3VpL3NjcmVlbnMvaW5kZXguZGFydCc7CmltcG9ydCAncGFja2FnZTpnb19yb3V0ZXIvZ29fcm91dGVyLmRhcnQnOwoKe3sjYXV0aH19Ly8vIEV2ZXJ5IHNoZWxsIGJyYW5jaCBpcyBndWFyZGVkIC0gdW5hdXRoZW50aWNhdGVkIHVzZXJzIGJvdW5jZSB0byBzaWduLWluLgp0eXBlZGVmIF9TaGVsbFJvdXRlID0gYXV0aC5Hb0F1dGhSb3V0ZTt7ey9hdXRofX17e15hdXRofX10eXBlZGVmIF9TaGVsbFJvdXRlID0gR29Sb3V0ZTt7ey9hdXRofX0KCi8vLyBCb3R0b20tbmF2aWdhdGlvbiBzaGVsbCBmb3IgdGhlIHNpZ25lZC1pbiBwYXJ0IG9mIHRoZSBhcHAuCi8vLwovLy8gQWRkIGEgdGFiIGJ5IGFkZGluZyBhIFtTdGF0ZWZ1bFNoZWxsQnJhbmNoXSBoZXJlIGFuZCBhIGRlc3RpbmF0aW9uIGluCi8vLyBbRGFzaGJvYXJkU2hlbGxTY3JlZW5dLgpjbGFzcyBEYXNoYm9hcmRSb3V0ZXIgewogIGNvbnN0IERhc2hib2FyZFJvdXRlci5fKCk7CgogIHN0YXRpYyBjb25zdCBTdHJpbmcgaG9tZSA9ICcvaG9tZS9kYXNoYm9hcmQnOwogIHN0YXRpYyBjb25zdCBTdHJpbmcgZXhwbG9yZSA9ICcvaG9tZS9leHBsb3JlJzsKICBzdGF0aWMgY29uc3QgU3RyaW5nIHByb2ZpbGUgPSAnL2hvbWUvcHJvZmlsZSc7CgogIHN0YXRpYyBTdGF0ZWZ1bFNoZWxsUm91dGUgY3JlYXRlU2hlbGxSb3V0ZSgpIHsKICAgIHJldHVybiBTdGF0ZWZ1bFNoZWxsUm91dGUuaW5kZXhlZFN0YWNrKAogICAgICBidWlsZGVyOiAoY29udGV4dCwgc3RhdGUsIG5hdmlnYXRpb25TaGVsbCkgPT4KICAgICAgICAgIERhc2hib2FyZFNoZWxsU2NyZWVuKG5hdmlnYXRpb25TaGVsbDogbmF2aWdhdGlvblNoZWxsKSwKICAgICAgYnJhbmNoZXM6IFsKICAgICAgICBTdGF0ZWZ1bFNoZWxsQnJhbmNoKAogICAgICAgICAgcm91dGVzOiBbCiAgICAgICAgICAgIF9TaGVsbFJvdXRlKAogICAgICAgICAgICAgIHBhdGg6IGhvbWUsCiAgICAgICAgICAgICAgYnVpbGRlcjogKGNvbnRleHQsIHN0YXRlKSA9PiBjb25zdCBIb21lVGFiU2NyZWVuKCksCiAgICAgICAgICAgICksCiAgICAgICAgICBdLAogICAgICAgICksCiAgICAgICAgU3RhdGVmdWxTaGVsbEJyYW5jaCgKICAgICAgICAgIHJvdXRlczogWwogICAgICAgICAgICBfU2hlbGxSb3V0ZSgKICAgICAgICAgICAgICBwYXRoOiBleHBsb3JlLAogICAgICAgICAgICAgIGJ1aWxkZXI6IChjb250ZXh0LCBzdGF0ZSkgPT4gY29uc3QgRXhwbG9yZVRhYlNjcmVlbigpLAogICAgICAgICAgICApLAogICAgICAgICAgXSwKICAgICAgICApLAogICAgICAgIFN0YXRlZnVsU2hlbGxCcmFuY2goCiAgICAgICAgICByb3V0ZXM6IFsKICAgICAgICAgICAgX1NoZWxsUm91dGUoCiAgICAgICAgICAgICAgcGF0aDogcHJvZmlsZSwKICAgICAgICAgICAgICBidWlsZGVyOiAoY29udGV4dCwgc3RhdGUpID0+IGNvbnN0IFByb2ZpbGVUYWJTY3JlZW4oKSwKICAgICAgICAgICAgKSwKICAgICAgICAgIF0sCiAgICAgICAgKSwKICAgICAgXSwKICAgICk7CiAgfQp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/dashboard_shell_screen.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmRlc2lnbl9zeXN0ZW0vZGVzaWduX3N5c3RlbS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CmltcG9ydCAncGFja2FnZTpnb19yb3V0ZXIvZ29fcm91dGVyLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6bG9jYWxpemF0aW9uL2xvY2FsaXphdGlvbi5kYXJ0JzsKCi8vLyBIb3N0cyB0aGUgYm90dG9tIG5hdmlnYXRpb24gYmFyIGFuZCBrZWVwcyBlYWNoIHRhYidzIG5hdmlnYXRpb24gc3RhY2sgYWxpdmUuCi8vLwovLy8gQWRkIGEgdGFiIGJ5IGFkZGluZyBhIFtTdGF0ZWZ1bFNoZWxsQnJhbmNoXSBpbiBgRGFzaGJvYXJkUm91dGVyYCBhbmQgYQovLy8gbWF0Y2hpbmcgZGVzdGluYXRpb24gaGVyZSAtIHRoZSB0d28gbGlzdHMgYXJlIHBvc2l0aW9uYWwuCmNsYXNzIERhc2hib2FyZFNoZWxsU2NyZWVuIGV4dGVuZHMgU3RhdGVmdWxXaWRnZXQgewogIGNvbnN0IERhc2hib2FyZFNoZWxsU2NyZWVuKHtzdXBlci5rZXksIHJlcXVpcmVkIHRoaXMubmF2aWdhdGlvblNoZWxsfSk7CgogIGZpbmFsIFN0YXRlZnVsTmF2aWdhdGlvblNoZWxsIG5hdmlnYXRpb25TaGVsbDsKCiAgQG92ZXJyaWRlCiAgU3RhdGU8RGFzaGJvYXJkU2hlbGxTY3JlZW4+IGNyZWF0ZVN0YXRlKCkgPT4gX0Rhc2hib2FyZFNoZWxsU2NyZWVuU3RhdGUoKTsKfQoKY2xhc3MgX0Rhc2hib2FyZFNoZWxsU2NyZWVuU3RhdGUgZXh0ZW5kcyBTdGF0ZTxEYXNoYm9hcmRTaGVsbFNjcmVlbj4gewogIHZvaWQgX29uRGVzdGluYXRpb25TZWxlY3RlZChpbnQgaW5kZXgpIHsKICAgIHdpZGdldC5uYXZpZ2F0aW9uU2hlbGwuZ29CcmFuY2goCiAgICAgIGluZGV4LAogICAgICBpbml0aWFsTG9jYXRpb246IGluZGV4ID09IHdpZGdldC5uYXZpZ2F0aW9uU2hlbGwuY3VycmVudEluZGV4LAogICAgKTsKICB9CgogIEBvdmVycmlkZQogIFdpZGdldCBidWlsZChCdWlsZENvbnRleHQgY29udGV4dCkgewogICAgcmV0dXJuIFNjYWZmb2xkKAogICAgICBib2R5OiB3aWRnZXQubmF2aWdhdGlvblNoZWxsLAogICAgICBib3R0b21OYXZpZ2F0aW9uQmFyOiBOYXZpZ2F0aW9uQmFyKAogICAgICAgIHNlbGVjdGVkSW5kZXg6IHdpZGdldC5uYXZpZ2F0aW9uU2hlbGwuY3VycmVudEluZGV4LAogICAgICAgIG9uRGVzdGluYXRpb25TZWxlY3RlZDogX29uRGVzdGluYXRpb25TZWxlY3RlZCwKICAgICAgICBkZXN0aW5hdGlvbnM6IFsKICAgICAgICAgIE5hdmlnYXRpb25EZXN0aW5hdGlvbigKICAgICAgICAgICAgaWNvbjogY29uc3QgSWNvbihOYXZpZ2F0aW9uSWNvbnMuaG9tZSksCiAgICAgICAgICAgIHNlbGVjdGVkSWNvbjogY29uc3QgSWNvbihOYXZpZ2F0aW9uSWNvbnMuaG9tZVNlbGVjdGVkKSwKICAgICAgICAgICAgbGFiZWw6IHN0cmluZ3MubmF2LmhvbWUsCiAgICAgICAgICApLAogICAgICAgICAgY29uc3QgTmF2aWdhdGlvbkRlc3RpbmF0aW9uKAogICAgICAgICAgICBpY29uOiBJY29uKE5hdmlnYXRpb25JY29ucy5leHBsb3JlKSwKICAgICAgICAgICAgc2VsZWN0ZWRJY29uOiBJY29uKE5hdmlnYXRpb25JY29ucy5leHBsb3JlU2VsZWN0ZWQpLAogICAgICAgICAgICBsYWJlbDogJ0V4cGxvcmUnLAogICAgICAgICAgKSwKICAgICAgICAgIE5hdmlnYXRpb25EZXN0aW5hdGlvbigKICAgICAgICAgICAgaWNvbjogY29uc3QgSWNvbihOYXZpZ2F0aW9uSWNvbnMucHJvZmlsZSksCiAgICAgICAgICAgIHNlbGVjdGVkSWNvbjogY29uc3QgSWNvbihOYXZpZ2F0aW9uSWNvbnMucHJvZmlsZVNlbGVjdGVkKSwKICAgICAgICAgICAgbGFiZWw6IHN0cmluZ3MubmF2LnByb2ZpbGUsCiAgICAgICAgICApLAogICAgICAgIF0sCiAgICAgICksCiAgICApOwogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/explore_tab_screen.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmRhc2hib2FyZC91aS93aWRnZXRzL2luZGV4LmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZGVzaWduX3N5c3RlbS9kZXNpZ25fc3lzdGVtLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlci9tYXRlcmlhbC5kYXJ0JzsKCmNsYXNzIEV4cGxvcmVUYWJTY3JlZW4gZXh0ZW5kcyBTdGF0ZWxlc3NXaWRnZXQgewogIGNvbnN0IEV4cGxvcmVUYWJTY3JlZW4oe3N1cGVyLmtleX0pOwoKICBAb3ZlcnJpZGUKICBXaWRnZXQgYnVpbGQoQnVpbGRDb250ZXh0IGNvbnRleHQpIHsKICAgIHJldHVybiBTY2FmZm9sZCgKICAgICAgYXBwQmFyOiBBcHBCYXIodGl0bGU6IGNvbnN0IFRleHQoJ0V4cGxvcmUnKSksCiAgICAgIGJvZHk6IGNvbnN0IFNhZmVBcmVhKAogICAgICAgIGNoaWxkOiBQbGFjZWhvbGRlclRhYigKICAgICAgICAgIGljb246IE5hdmlnYXRpb25JY29ucy5leHBsb3JlU2VsZWN0ZWQsCiAgICAgICAgICB0aXRsZTogJ0V4cGxvcmUnLAogICAgICAgICAgc3VidGl0bGU6ICdZb3VyIHNlY29uZCB0YWIuIFJlcGxhY2UgdGhpcyB3aXRoIGEgcmVhbCBmZWF0dXJlLicsCiAgICAgICAgKSwKICAgICAgKSwKICAgICk7CiAgfQp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/home_tab_screen.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmRhc2hib2FyZC91aS93aWRnZXRzL2luZGV4LmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZGVzaWduX3N5c3RlbS9kZXNpZ25fc3lzdGVtLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlci9tYXRlcmlhbC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmxvY2FsaXphdGlvbi9sb2NhbGl6YXRpb24uZGFydCc7CgpjbGFzcyBIb21lVGFiU2NyZWVuIGV4dGVuZHMgU3RhdGVsZXNzV2lkZ2V0IHsKICBjb25zdCBIb21lVGFiU2NyZWVuKHtzdXBlci5rZXl9KTsKCiAgQG92ZXJyaWRlCiAgV2lkZ2V0IGJ1aWxkKEJ1aWxkQ29udGV4dCBjb250ZXh0KSB7CiAgICByZXR1cm4gU2NhZmZvbGQoCiAgICAgIGFwcEJhcjogQXBwQmFyKHRpdGxlOiBUZXh0KHN0cmluZ3MubmF2LmRhc2hib2FyZCkpLAogICAgICBib2R5OiBTYWZlQXJlYSgKICAgICAgICBjaGlsZDogUGxhY2Vob2xkZXJUYWIoCiAgICAgICAgICBpY29uOiBOYXZpZ2F0aW9uSWNvbnMuaG9tZVNlbGVjdGVkLAogICAgICAgICAgdGl0bGU6IHN0cmluZ3MuYXBwLndlbGNvbWVfdG9fYXBwLAogICAgICAgICAgc3VidGl0bGU6IHN0cmluZ3MuYXBwLmRlc2NyaXB0aW9uLAogICAgICAgICksCiAgICAgICksCiAgICApOwogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/index.dart", + "data": + "ZXhwb3J0ICdkYXNoYm9hcmRfc2hlbGxfc2NyZWVuLmRhcnQnOwpleHBvcnQgJ2V4cGxvcmVfdGFiX3NjcmVlbi5kYXJ0JzsKZXhwb3J0ICdob21lX3RhYl9zY3JlZW4uZGFydCc7CmV4cG9ydCAncHJvZmlsZV90YWJfc2NyZWVuLmRhcnQnOwo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/screens/profile_tab_screen.dart", + "data": + "e3sjYXV0aH19aW1wb3J0ICdwYWNrYWdlOmF1dGgvYXV0aC5kYXJ0JyBhcyBhdXRoOwp7ey9hdXRofX1pbXBvcnQgJ3BhY2thZ2U6ZGVzaWduX3N5c3RlbS9kZXNpZ25fc3lzdGVtLmRhcnQnOwp7eyNkZXZlbG9wZXJ9fWltcG9ydCAncGFja2FnZTpkZXZlbG9wZXIvZGV2ZWxvcGVyLmRhcnQnOwp7ey9kZXZlbG9wZXJ9fXt7I2F1dGh9fWltcG9ydCAncGFja2FnZTpkaS9kaS5kYXJ0JzsKe3svYXV0aH19aW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7Cnt7I2F1dGh9fWltcG9ydCAncGFja2FnZTpnb19yb3V0ZXIvZ29fcm91dGVyLmRhcnQnOwp7ey9hdXRofX1pbXBvcnQgJ3BhY2thZ2U6bG9jYWxpemF0aW9uL2xvY2FsaXphdGlvbi5kYXJ0JzsKCi8vLyBBY2NvdW50IHRhYjogaWRlbnRpdHksIHNldHRpbmdzIGVudHJ5IHBvaW50cywgc2lnbiBvdXQuCi8vLwovLy8gVGhpcyBpcyBkZWxpYmVyYXRlbHkgdGhpbiAtIGhhbmcgeW91ciByZWFsIHByb2ZpbGUgZmVhdHVyZSBvZmYgaXQuCmNsYXNzIFByb2ZpbGVUYWJTY3JlZW4gZXh0ZW5kcyBTdGF0ZWxlc3NXaWRnZXQgewogIGNvbnN0IFByb2ZpbGVUYWJTY3JlZW4oe3N1cGVyLmtleX0pOwoKICBAb3ZlcnJpZGUKICBXaWRnZXQgYnVpbGQoQnVpbGRDb250ZXh0IGNvbnRleHQpIHsKICAgIHJldHVybiBTY2FmZm9sZCgKICAgICAgYXBwQmFyOiBBcHBCYXIodGl0bGU6IFRleHQoc3RyaW5ncy5wcm9maWxlLnByb2ZpbGUpKSwKICAgICAgYm9keTogU2FmZUFyZWEoCiAgICAgICAgY2hpbGQ6IExpc3RWaWV3KAogICAgICAgICAgY2hpbGRyZW46IFsKICAgICAgICAgICAgY29uc3QgU2l6ZWRCb3goaGVpZ2h0OiAxNiksCiAgICAgICAgICAgIHt7I2RldmVsb3Blcn19Ly8gVGFwIHRoZSBhdmF0YXIgNXggdG8gb3BlbiBkZXZlbG9wZXIgdG9vbHMuCiAgICAgICAgICAgIE9wZW5EZXZUb29sc1dyYXBwZXIoY2hpbGQ6IGNvbnN0IF9Qcm9maWxlQXZhdGFyKCkpLHt7L2RldmVsb3Blcn19e3teZGV2ZWxvcGVyfX1jb25zdCBfUHJvZmlsZUF2YXRhcigpLHt7L2RldmVsb3Blcn19CiAgICAgICAgICAgIGNvbnN0IFNpemVkQm94KGhlaWdodDogMjQpLAogICAgICAgICAgICBMaXN0VGlsZSgKICAgICAgICAgICAgICBsZWFkaW5nOiBjb25zdCBJY29uKE5hdmlnYXRpb25JY29ucy5zZXR0aW5ncyksCiAgICAgICAgICAgICAgdGl0bGU6IFRleHQoc3RyaW5ncy5wcm9maWxlLnNldHRpbmdzKSwKICAgICAgICAgICAgICB0cmFpbGluZzogY29uc3QgSWNvbihJY29ucy5jaGV2cm9uX3JpZ2h0KSwKICAgICAgICAgICAgICBvblRhcDogKCkge30sCiAgICAgICAgICAgICksCiAgICAgICAgICAgIHt7I2F1dGh9fUxpc3RUaWxlKAogICAgICAgICAgICAgIGxlYWRpbmc6IGNvbnN0IEljb24oSWNvbnMubG9nb3V0KSwKICAgICAgICAgICAgICB0aXRsZTogVGV4dChzdHJpbmdzLmF1dGguc2lnbl9vdXQpLAogICAgICAgICAgICAgIG9uVGFwOiAoKSA9PiBfY29uZmlybVNpZ25PdXQoY29udGV4dCksCiAgICAgICAgICAgICkse3svYXV0aH19CiAgICAgICAgICBdLAogICAgICAgICksCiAgICAgICksCiAgICApOwogIH0Ke3sjYXV0aH19CiAgRnV0dXJlPHZvaWQ+IF9jb25maXJtU2lnbk91dChCdWlsZENvbnRleHQgY29udGV4dCkgYXN5bmMgewogICAgZmluYWwgY29uZmlybWVkID0gYXdhaXQgc2hvd0RpYWxvZzxib29sPigKICAgICAgY29udGV4dDogY29udGV4dCwKICAgICAgYnVpbGRlcjogKGNvbnRleHQpID0+IEFsZXJ0RGlhbG9nKAogICAgICAgIHRpdGxlOiBUZXh0KHN0cmluZ3MuYXV0aC5zaWduX291dCksCiAgICAgICAgY29udGVudDogVGV4dChzdHJpbmdzLmF1dGguc2lnbl9vdXRfY29uZmlybWF0aW9uKSwKICAgICAgICBhY3Rpb25zOiBbCiAgICAgICAgICBUZXh0QnV0dG9uKAogICAgICAgICAgICBvblByZXNzZWQ6ICgpID0+IE5hdmlnYXRvci5vZihjb250ZXh0KS5wb3AoZmFsc2UpLAogICAgICAgICAgICBjaGlsZDogVGV4dChzdHJpbmdzLmdlbmVyaWMuY2FuY2VsKSwKICAgICAgICAgICksCiAgICAgICAgICBGaWxsZWRCdXR0b24oCiAgICAgICAgICAgIG9uUHJlc3NlZDogKCkgPT4gTmF2aWdhdG9yLm9mKGNvbnRleHQpLnBvcCh0cnVlKSwKICAgICAgICAgICAgY2hpbGQ6IFRleHQoc3RyaW5ncy5hdXRoLnNpZ25fb3V0KSwKICAgICAgICAgICksCiAgICAgICAgXSwKICAgICAgKSwKICAgICk7CgogICAgaWYgKGNvbmZpcm1lZCAhPSB0cnVlIHx8ICFjb250ZXh0Lm1vdW50ZWQpIHJldHVybjsKCiAgICBhd2FpdCBkaS5nZXQ8YXV0aC5BdXRoU2VydmljZT4oKS5zaWduT3V0KCk7CiAgICBpZiAoY29udGV4dC5tb3VudGVkKSBjb250ZXh0LmdvKGF1dGguQXV0aFJvdXRlcy5zaWduSW4pOwogIH0Ke3svYXV0aH19fQoKY2xhc3MgX1Byb2ZpbGVBdmF0YXIgZXh0ZW5kcyBTdGF0ZWxlc3NXaWRnZXQgewogIGNvbnN0IF9Qcm9maWxlQXZhdGFyKCk7CgogIEBvdmVycmlkZQogIFdpZGdldCBidWlsZChCdWlsZENvbnRleHQgY29udGV4dCkgewogICAgZmluYWwgY29sb3JTY2hlbWUgPSBUaGVtZS5vZihjb250ZXh0KS5jb2xvclNjaGVtZTsKICAgIHJldHVybiBDZW50ZXIoCiAgICAgIGNoaWxkOiBDaXJjbGVBdmF0YXIoCiAgICAgICAgcmFkaXVzOiA0NCwKICAgICAgICBiYWNrZ3JvdW5kQ29sb3I6IGNvbG9yU2NoZW1lLnByaW1hcnlDb250YWluZXIsCiAgICAgICAgY2hpbGQ6IEljb24oCiAgICAgICAgICBOYXZpZ2F0aW9uSWNvbnMucHJvZmlsZVNlbGVjdGVkLAogICAgICAgICAgc2l6ZTogNDQsCiAgICAgICAgICBjb2xvcjogY29sb3JTY2hlbWUub25QcmltYXJ5Q29udGFpbmVyLAogICAgICAgICksCiAgICAgICksCiAgICApOwogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/widgets/index.dart", + "data": "ZXhwb3J0ICdwbGFjZWhvbGRlcl90YWIuZGFydCc7Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/features/{{dashboard_dir}}/lib/ui/widgets/placeholder_tab.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CgovLy8gRHJvcC1pbiAibm90aGluZyBoZXJlIHlldCIgdGFiIGJvZHkuCi8vLwovLy8gRGVsZXRlIHRoaXMgdGhlIG1vbWVudCBhIHRhYiBncm93cyByZWFsIGNvbnRlbnQuCmNsYXNzIFBsYWNlaG9sZGVyVGFiIGV4dGVuZHMgU3RhdGVsZXNzV2lkZ2V0IHsKICBjb25zdCBQbGFjZWhvbGRlclRhYih7CiAgICBzdXBlci5rZXksCiAgICByZXF1aXJlZCB0aGlzLmljb24sCiAgICByZXF1aXJlZCB0aGlzLnRpdGxlLAogICAgcmVxdWlyZWQgdGhpcy5zdWJ0aXRsZSwKICAgIHRoaXMuYWN0aW9uLAogIH0pOwoKICBmaW5hbCBJY29uRGF0YSBpY29uOwogIGZpbmFsIFN0cmluZyB0aXRsZTsKICBmaW5hbCBTdHJpbmcgc3VidGl0bGU7CiAgZmluYWwgV2lkZ2V0PyBhY3Rpb247CgogIEBvdmVycmlkZQogIFdpZGdldCBidWlsZChCdWlsZENvbnRleHQgY29udGV4dCkgewogICAgZmluYWwgdGhlbWUgPSBUaGVtZS5vZihjb250ZXh0KTsKICAgIGZpbmFsIGNvbG9yU2NoZW1lID0gdGhlbWUuY29sb3JTY2hlbWU7CiAgICBmaW5hbCB0ZXh0VGhlbWUgPSB0aGVtZS50ZXh0VGhlbWU7CgogICAgcmV0dXJuIENlbnRlcigKICAgICAgY2hpbGQ6IFBhZGRpbmcoCiAgICAgICAgcGFkZGluZzogY29uc3QgRWRnZUluc2V0cy5hbGwoMzIpLAogICAgICAgIGNoaWxkOiBDb2x1bW4oCiAgICAgICAgICBtYWluQXhpc0FsaWdubWVudDogTWFpbkF4aXNBbGlnbm1lbnQuY2VudGVyLAogICAgICAgICAgY2hpbGRyZW46IFsKICAgICAgICAgICAgQ29udGFpbmVyKAogICAgICAgICAgICAgIHBhZGRpbmc6IGNvbnN0IEVkZ2VJbnNldHMuYWxsKDI0KSwKICAgICAgICAgICAgICBkZWNvcmF0aW9uOiBCb3hEZWNvcmF0aW9uKAogICAgICAgICAgICAgICAgY29sb3I6IGNvbG9yU2NoZW1lLnByaW1hcnkud2l0aFZhbHVlcyhhbHBoYTogMC4xKSwKICAgICAgICAgICAgICAgIHNoYXBlOiBCb3hTaGFwZS5jaXJjbGUsCiAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICBjaGlsZDogSWNvbihpY29uLCBzaXplOiA3MiwgY29sb3I6IGNvbG9yU2NoZW1lLnByaW1hcnkpLAogICAgICAgICAgICApLAogICAgICAgICAgICBjb25zdCBTaXplZEJveChoZWlnaHQ6IDMyKSwKICAgICAgICAgICAgVGV4dCgKICAgICAgICAgICAgICB0aXRsZSwKICAgICAgICAgICAgICBzdHlsZTogdGV4dFRoZW1lLmhlYWRsaW5lTWVkaXVtPy5jb3B5V2l0aCgKICAgICAgICAgICAgICAgIGNvbG9yOiBjb2xvclNjaGVtZS5vblN1cmZhY2UsCiAgICAgICAgICAgICAgICBmb250V2VpZ2h0OiBGb250V2VpZ2h0LmJvbGQsCiAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgKSwKICAgICAgICAgICAgY29uc3QgU2l6ZWRCb3goaGVpZ2h0OiAxNiksCiAgICAgICAgICAgIFRleHQoCiAgICAgICAgICAgICAgc3VidGl0bGUsCiAgICAgICAgICAgICAgc3R5bGU6IHRleHRUaGVtZS5ib2R5TGFyZ2U/LmNvcHlXaXRoKAogICAgICAgICAgICAgICAgY29sb3I6IGNvbG9yU2NoZW1lLm9uU3VyZmFjZVZhcmlhbnQsCiAgICAgICAgICAgICAgICBoZWlnaHQ6IDEuNSwKICAgICAgICAgICAgICApLAogICAgICAgICAgICAgIHRleHRBbGlnbjogVGV4dEFsaWduLmNlbnRlciwKICAgICAgICAgICAgKSwKICAgICAgICAgICAgaWYgKGFjdGlvbiAhPSBudWxsKSAuLi5bY29uc3QgU2l6ZWRCb3goaGVpZ2h0OiAzMiksIGFjdGlvbiFdLAogICAgICAgICAgXSwKICAgICAgICApLAogICAgICApLAogICAgKTsKICB9Cn0K", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/features/{{dashboard_dir}}/pubspec.yaml", + "data": + "bmFtZTogZGFzaGJvYXJkCmRlc2NyaXB0aW9uOiAiQm90dG9tLW5hdmlnYXRpb24gc2hlbGwgYW5kIHN0YXJ0ZXIgdGFicyBmb3Ige3tuYW1lLnRpdGxlQ2FzZSgpfX0uIgp2ZXJzaW9uOiAwLjEuMApwdWJsaXNoX3RvOiAnbm9uZScKCmVudmlyb25tZW50OgogIHNkazogXjMuOC4wCiAgZmx1dHRlcjogIj49My4yOS4wIgoKZGVwZW5kZW5jaWVzOgogIGZsdXR0ZXI6CiAgICBzZGs6IGZsdXR0ZXIKICBnb19yb3V0ZXI6IF4xNS4xLjMKICBjb3JlOgogICAgcGF0aDogLi4vLi4vcGFja2FnZXMvY29yZQogIGRlc2lnbl9zeXN0ZW06CiAgICBwYXRoOiAuLi8uLi9wYWNrYWdlcy9kZXNpZ25fc3lzdGVtCiAgZGk6CiAgICBwYXRoOiAuLi8uLi9wYWNrYWdlcy9kaQogIGxvY2FsaXphdGlvbjoKICAgIHBhdGg6IC4uLy4uL3BhY2thZ2VzL2xvY2FsaXphdGlvbgp7eyNhdXRofX0gIGF1dGg6CiAgICBwYXRoOiAuLi9hdXRoCnt7L2F1dGh9fXt7I2RldmVsb3Blcn19ICBkZXZlbG9wZXI6CiAgICBwYXRoOiAuLi8uLi9wYWNrYWdlcy9kZXZlbG9wZXIKe3svZGV2ZWxvcGVyfX0KZGV2X2RlcGVuZGVuY2llczoKICBmbHV0dGVyX3Rlc3Q6CiAgICBzZGs6IGZsdXR0ZXIKICBmbHV0dGVyX2xpbnRzOiBeNS4wLjAKCmZsdXR0ZXI6Cg==", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/features/{{dashboard_dir}}/README.md", + "data": + "IyBEYXNoYm9hcmQKCkRhc2hib2FyZCBmZWF0dXJlIHdpdGggYm90dG9tIG5hdmlnYXRpb24KClshW25vbnN0b3BfY2xpXShodHRwczovL2ltZy5zaGllbGRzLmlvL2JhZGdlL3N0YXJ0ZWQlMjB3aXRoLW5vbnN0b3BfY2xpLTE2NkM0RS5zdmc/c3R5bGU9ZmxhdC1zcXVhcmUpXShodHRwczovL3B1Yi5kZXYvcGFja2FnZXMvbm9uc3RvcF9jbGkpClshW21lbG9zXShodHRwczovL2ltZy5zaGllbGRzLmlvL2JhZGdlL21haW50YWluZWQlMjB3aXRoLW1lbG9zLWY3MDBmZi5zdmc/c3R5bGU9ZmxhdC1zcXVhcmUpXShodHRwczovL2dpdGh1Yi5jb20vaW52ZXJ0YXNlL21lbG9zKQoKIyMgSW5zdGFsbGF0aW9uIPCfkrsKCioq4p2XIEluIG9yZGVyIHRvIHN0YXJ0IHVzaW5nIERhc2hib2FyZCB5b3UgbXVzdCBoYXZlIHRoZSBbRmx1dHRlciBTREtdW2ZsdXR0ZXJfaW5zdGFsbF9saW5rXSBpbnN0YWxsZWQgb24geW91ciBtYWNoaW5lLioqCgpJbnN0YWxsIHZpYSBgZGFydCBwdWIgYWRkYDoKCmBgYHNoCmRhcnQgcHViIGFkZCBkYXNoYm9hcmQKYGBgCgpbZmx1dHRlcl9pbnN0YWxsX2xpbmtdOiBodHRwczovL2RvY3MuZmx1dHRlci5kZXYvZ2V0LXN0YXJ0ZWQvaW5zdGFsbA==", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/melos.yaml", + "data": + "bmFtZToge3sgbmFtZS5zbmFrZUNhc2UoKSB9fV93b3Jrc3BhY2UKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNT05PUkVQTyBDT05GSUdVUkFUSU9OCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBhcHBzLyAgICAgLSBydW5uYWJsZSBGbHV0dGVyIGFwcGxpY2F0aW9ucwojIGZlYXR1cmVzLyAtIHZlcnRpY2FsIHNsaWNlcyAodWkgKyBzdGF0ZSArIGRhdGEpIHRoYXQgYW4gYXBwIGNvbXBvc2VzCiMgcGFja2FnZXMvIC0gaG9yaXpvbnRhbCBjYXBhYmlsaXRpZXMgc2hhcmVkIGJ5IGZlYXR1cmVzIChuZXR3b3JrLCBkZXNpZ24sIC4uLikKIyBwbHVnaW5zLyAgLSBwbGF0Zm9ybSBjaGFubmVscyBhbmQgbmF0aXZlIGludGVncmF0aW9ucwpwYWNrYWdlczoKICAtIGFwcHMvKgogIC0gZmVhdHVyZXMvKgogIC0gcGFja2FnZXMvKgogIC0gcGx1Z2lucy8qCgpjb21tYW5kOgogIGJvb3RzdHJhcDoKICAgIGRlcGVuZGVuY2llczoKICAgICAganNvbl9hbm5vdGF0aW9uOiBeNC45LjAKICAgIGRldl9kZXBlbmRlbmNpZXM6CiAgICAgIGJ1aWxkX3J1bm5lcjogXjIuNC4xNQogICAgICBmbHV0dGVyX2xpbnRzOiBeNS4wLjAKICAgICAganNvbl9zZXJpYWxpemFibGU6IF42LjkuNQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNDUklQVFMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzY3JpcHRzOgogIGNsZWFuOmZsdXR0ZXI6CiAgICBkZXNjcmlwdGlvbjogIlJ1biBmbHV0dGVyIGNsZWFuIGluIGFsbCBGbHV0dGVyIHBhY2thZ2VzIgogICAgcnVuOiBmbHV0dGVyIGNsZWFuCiAgICBleGVjOgogICAgICBjb25jdXJyZW5jeTogNAogICAgcGFja2FnZUZpbHRlcnM6CiAgICAgIGZsdXR0ZXI6IHRydWUKCiAgbGludDoKICAgIGRlc2NyaXB0aW9uOiAiUnVuIGZvcm1hdCBhbmQgYW5hbHl6ZSBhbGwgcGFja2FnZXMiCiAgICAjIGBkYXJ0IGZvcm1hdGAgaGFzIG5vIGV4Y2x1ZGUgb2YgaXRzIG93biwgc28gYnVpbGQgb3V0cHV0IGFuZCB0aGUgcGx1Z2luCiAgICAjIHNvdXJjZXMgQ29jb2FQb2RzIHN5bWxpbmtzIGludG8gdGhlIGFwcCBhcmUgZmlsdGVyZWQgb3V0IGhlcmUuCiAgICBydW46IHwKICAgICAgZmluZCAuIC1uYW1lICcqLmRhcnQnIC1ub3QgLXBhdGggJyovYnVpbGQvKicgLW5vdCAtcGF0aCAnKi8uZGFydF90b29sLyonIFwKICAgICAgICAtbm90IC1wYXRoICcqLy5zeW1saW5rcy8qJyAtZXhlYyBkYXJ0IGZvcm1hdCAtLXNldC1leGl0LWlmLWNoYW5nZWQge30gKyAmJgogICAgICBkYXJ0IGFuYWx5emUgLS1mYXRhbC1pbmZvcyAuCiAgICBleGVjOgogICAgICBjb25jdXJyZW5jeTogNQogICAgICBmYWlsRmFzdDogdHJ1ZQoKICBmaXg6CiAgICBkZXNjcmlwdGlvbjogIkFwcGx5IERhcnQgYXV0b21hdGVkIGZpeGVzIHRvIGFsbCBwYWNrYWdlcyIKICAgIHJ1bjogZGFydCBmaXggLS1hcHBseQogICAgZXhlYzoKICAgICAgY29uY3VycmVuY3k6IDUKICAgIHBhY2thZ2VGaWx0ZXJzOgogICAgICBkaXJFeGlzdHM6IGxpYgoKICBmaXg6ZHJ5OgogICAgZGVzY3JpcHRpb246ICJTaG93IERhcnQgYXV0b21hdGVkIGZpeGVzIHdpdGhvdXQgYXBwbHlpbmcgdGhlbSIKICAgIHJ1bjogZGFydCBmaXggLS1kcnktcnVuCiAgICBleGVjOgogICAgICBjb25jdXJyZW5jeTogNQogICAgcGFja2FnZUZpbHRlcnM6CiAgICAgIGRpckV4aXN0czogbGliCgogIGdlbmVyYXRlOgogICAgZGVzY3JpcHRpb246ICJSdW4gYnVpbGRfcnVubmVyIGluIGV2ZXJ5IHBhY2thZ2UgdGhhdCBuZWVkcyBjb2RlZ2VuIgogICAgcnVuOiBkYXJ0IHJ1biBidWlsZF9ydW5uZXIgYnVpbGQgLS1kZWxldGUtY29uZmxpY3Rpbmctb3V0cHV0cwogICAgZXhlYzoKICAgICAgY29uY3VycmVuY3k6IDEKICAgIHBhY2thZ2VGaWx0ZXJzOgogICAgICBkZXBlbmRzT246IGJ1aWxkX3J1bm5lcgoKICBnZW5lcmF0ZTppNjluOgogICAgZGVzY3JpcHRpb246ICJSZWdlbmVyYXRlIGxvY2FsaXphdGlvbiBzdHJpbmdzIGZyb20gbWVzc2FnZXMuaTY5bi55YW1sIgogICAgcnVuOiB8CiAgICAgIGRhcnQgcnVuIGJ1aWxkX3J1bm5lciBidWlsZCAtLWRlbGV0ZS1jb25mbGljdGluZy1vdXRwdXRzICYmCiAgICAgIGRhcnQgZm9ybWF0IGxpYi9tZXNzYWdlcy5pNjluLmRhcnQKICAgIGV4ZWM6CiAgICAgIGNvbmN1cnJlbmN5OiAxCiAgICBwYWNrYWdlRmlsdGVyczoKICAgICAgc2NvcGU6ICJsb2NhbGl6YXRpb24iCgogIHRlc3Q6CiAgICBkZXNjcmlwdGlvbjogIlJ1biBhbGwgdGVzdHMgKERhcnQgYW5kIEZsdXR0ZXIpIgogICAgcnVuOiBtZWxvcyBydW4gdGVzdDpzZWxlY3QgLS1uby1zZWxlY3QKCiAgdGVzdDpzZWxlY3Q6CiAgICBkZXNjcmlwdGlvbjogIlJ1biB0ZXN0cyBpbiBGbHV0dGVyIHBhY2thZ2VzIgogICAgcnVuOiBmbHV0dGVyIHRlc3QgLS1jb3ZlcmFnZQogICAgZXhlYzoKICAgICAgY29uY3VycmVuY3k6IDUKICAgICAgZmFpbEZhc3Q6IHRydWUKICAgIHBhY2thZ2VGaWx0ZXJzOgogICAgICBkaXJFeGlzdHM6IHRlc3QKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBJREUgQ09ORklHVVJBVElPTgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCmlkZToKICBpbnRlbGxpajoKICAgIGVuYWJsZWQ6IHRydWUKICAgIG1vZHVsZU5hbWVQcmVmaXg6ICcnCg==", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/core/lib/bloc/global_event.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmVxdWF0YWJsZS9lcXVhdGFibGUuZGFydCc7CgphYnN0cmFjdCBjbGFzcyBHbG9iYWxFdmVudFR5cGUgZXh0ZW5kcyBFcXVhdGFibGUgewogIGNvbnN0IEdsb2JhbEV2ZW50VHlwZSgpOwoKICBAb3ZlcnJpZGUKICBMaXN0PE9iamVjdD8+IGdldCBwcm9wcyA9PiBbXTsKfQoKY2xhc3MgUmVmcmVzaFN1cnZleUFzc2lnbm1lbnQgZXh0ZW5kcyBHbG9iYWxFdmVudFR5cGUgewogIGZpbmFsIFN0cmluZz8gaWQ7CgogIGNvbnN0IFJlZnJlc2hTdXJ2ZXlBc3NpZ25tZW50KHRoaXMuaWQpOwp9CgpjbGFzcyBSZWZyZXNoUHJvZmlsZSBleHRlbmRzIEdsb2JhbEV2ZW50VHlwZSB7CiAgY29uc3QgUmVmcmVzaFByb2ZpbGUoKTsKfQoKY2xhc3MgRmlyZUdsb2JhbEV2ZW50IGV4dGVuZHMgRXF1YXRhYmxlIHsKICBmaW5hbCBHbG9iYWxFdmVudFR5cGUgZXZlbnRUeXBlOwoKICBjb25zdCBGaXJlR2xvYmFsRXZlbnQoe3JlcXVpcmVkIHRoaXMuZXZlbnRUeXBlfSk7CgogIEBvdmVycmlkZQogIExpc3Q8T2JqZWN0Pz4gZ2V0IHByb3BzID0+IFtldmVudFR5cGVdOwp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/core/lib/bloc/global_event_channel_bloc.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmJsb2MvYmxvYy5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmNvcmUvYmxvYy9nbG9iYWxfZXZlbnQuZGFydCc7CmltcG9ydCAncGFja2FnZTpjb3JlL2Jsb2MvZ2xvYmFsX2V2ZW50X2NoYW5uZWxfc3RhdGUuZGFydCc7CmltcG9ydCAncGFja2FnZTpjb3JlL2xvZ2dlci9sb2dnZXIuZGFydCc7CmltcG9ydCAncGFja2FnZTpkaS9kaS5kYXJ0JzsKCmNsYXNzIEdsb2JhbEV2ZW50Q2hhbm5lbCBleHRlbmRzIEJsb2M8RmlyZUdsb2JhbEV2ZW50LCBHbG9iYWxFdmVudFN0YXRlPiB7CiAgZmluYWwgTG9nZ2VyIF9sb2dnZXIgPSBkaS5nZXQ8TG9nZ2VyPigpOwoKICBHbG9iYWxFdmVudENoYW5uZWwoe3JlcXVpcmVkIGludCBtYXhSZWNlbnRFdmVudHN9KQogICAgOiBzdXBlcihHbG9iYWxFdmVudFN0YXRlKG1heFJlY2VudEV2ZW50czogbWF4UmVjZW50RXZlbnRzKSkgewogICAgb248RmlyZUdsb2JhbEV2ZW50Pihfb25GaXJlR2xvYmFsRXZlbnQpOwogIH0KCiAgdm9pZCBfb25GaXJlR2xvYmFsRXZlbnQoCiAgICBGaXJlR2xvYmFsRXZlbnQgZXZlbnQsCiAgICBFbWl0dGVyPEdsb2JhbEV2ZW50U3RhdGU+IGVtaXQsCiAgKSB7CiAgICBfbG9nZ2VyLmkoJ0dsb2JhbEV2ZW50Q2hhbm5lbDogUHJvY2Vzc2luZyAke2V2ZW50LmV2ZW50VHlwZS5ydW50aW1lVHlwZX0nKTsKCiAgICBmaW5hbCBldmVudFR5cGUgPSBldmVudC5ldmVudFR5cGU7CiAgICBmaW5hbCBuZXdIaXN0b3J5ID0gTGlzdDxHbG9iYWxFdmVudFR5cGU+LmZyb20oc3RhdGUuaGlzdG9yeSk7CiAgICBuZXdIaXN0b3J5Lmluc2VydCgwLCBldmVudFR5cGUpOwoKICAgIGlmIChuZXdIaXN0b3J5Lmxlbmd0aCA+IHN0YXRlLm1heFJlY2VudEV2ZW50cykgewogICAgICBuZXdIaXN0b3J5LnJlbW92ZUxhc3QoKTsKICAgIH0KCiAgICBmaW5hbCBuZXdFdmVudENvdW50cyA9IE1hcDxUeXBlLCBpbnQ+LmZyb20oc3RhdGUuZXZlbnRDb3VudHMpOwogICAgZmluYWwgZXZlbnRUeXBlS2V5ID0gZXZlbnRUeXBlLnJ1bnRpbWVUeXBlOwogICAgbmV3RXZlbnRDb3VudHNbZXZlbnRUeXBlS2V5XSA9IChuZXdFdmVudENvdW50c1tldmVudFR5cGVLZXldID8/IDApICsgMTsKCiAgICBlbWl0KAogICAgICBzdGF0ZS5jb3B5V2l0aCgKICAgICAgICBoaXN0b3J5OiBuZXdIaXN0b3J5LAogICAgICAgIGV2ZW50Q291bnRzOiBuZXdFdmVudENvdW50cywKICAgICAgICBjdXJyZW50OiBldmVudFR5cGUsCiAgICAgICksCiAgICApOwogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/core/lib/bloc/global_event_channel_provider.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNvcmUvYmxvYy9nbG9iYWxfZXZlbnRfY2hhbm5lbF9ibG9jLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Y29yZS9ibG9jL2dsb2JhbF9ldmVudF9jaGFubmVsX3N0YXRlLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlci9tYXRlcmlhbC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXJfYmxvYy9mbHV0dGVyX2Jsb2MuZGFydCc7CgpjbGFzcyBHbG9iYWxFdmVudENoYW5uZWxQcm92aWRlciBleHRlbmRzIFN0YXRlbGVzc1dpZGdldCB7CiAgZmluYWwgV2lkZ2V0IGNoaWxkOwogIGZpbmFsIGludCBtYXhSZWNlbnRFdmVudHM7CiAgZmluYWwgYm9vbCBsYXp5OwoKICBjb25zdCBHbG9iYWxFdmVudENoYW5uZWxQcm92aWRlcih7CiAgICBzdXBlci5rZXksCiAgICByZXF1aXJlZCB0aGlzLmNoaWxkLAogICAgdGhpcy5tYXhSZWNlbnRFdmVudHMgPSA1MCwKICAgIHRoaXMubGF6eSA9IHRydWUsCiAgfSk7CgogIEBvdmVycmlkZQogIFdpZGdldCBidWlsZChCdWlsZENvbnRleHQgY29udGV4dCkgewogICAgcmV0dXJuIEJsb2NQcm92aWRlcjxHbG9iYWxFdmVudENoYW5uZWw+KAogICAgICBsYXp5OiBsYXp5LAogICAgICBjcmVhdGU6IChjb250ZXh0KSA9PiBHbG9iYWxFdmVudENoYW5uZWwobWF4UmVjZW50RXZlbnRzOiBtYXhSZWNlbnRFdmVudHMpLAogICAgICBjaGlsZDogY2hpbGQsCiAgICApOwogIH0KfQoKY2xhc3MgR2xvYmFsRXZlbnRDaGFubmVsQnVpbGRlciBleHRlbmRzIFN0YXRlbGVzc1dpZGdldCB7CiAgZmluYWwgQmxvY1dpZGdldEJ1aWxkZXI8R2xvYmFsRXZlbnRTdGF0ZT4gYnVpbGRlcjsKICBmaW5hbCBCbG9jQnVpbGRlckNvbmRpdGlvbjxHbG9iYWxFdmVudFN0YXRlPj8gYnVpbGRXaGVuOwoKICBjb25zdCBHbG9iYWxFdmVudENoYW5uZWxCdWlsZGVyKHsKICAgIHN1cGVyLmtleSwKICAgIHJlcXVpcmVkIHRoaXMuYnVpbGRlciwKICAgIHRoaXMuYnVpbGRXaGVuLAogIH0pOwoKICBAb3ZlcnJpZGUKICBXaWRnZXQgYnVpbGQoQnVpbGRDb250ZXh0IGNvbnRleHQpIHsKICAgIHJldHVybiBCbG9jQnVpbGRlcjxHbG9iYWxFdmVudENoYW5uZWwsIEdsb2JhbEV2ZW50U3RhdGU+KAogICAgICBidWlsZFdoZW46IGJ1aWxkV2hlbiwKICAgICAgYnVpbGRlcjogYnVpbGRlciwKICAgICk7CiAgfQp9CgpjbGFzcyBHbG9iYWxFdmVudENoYW5uZWxMaXN0ZW5lciBleHRlbmRzIFN0YXRlbGVzc1dpZGdldCB7CiAgZmluYWwgV2lkZ2V0IGNoaWxkOwogIGZpbmFsIEJsb2NXaWRnZXRMaXN0ZW5lcjxHbG9iYWxFdmVudFN0YXRlPiBsaXN0ZW5lcjsKICBmaW5hbCBCbG9jTGlzdGVuZXJDb25kaXRpb248R2xvYmFsRXZlbnRTdGF0ZT4/IGxpc3RlbldoZW47CgogIGNvbnN0IEdsb2JhbEV2ZW50Q2hhbm5lbExpc3RlbmVyKHsKICAgIHN1cGVyLmtleSwKICAgIHJlcXVpcmVkIHRoaXMuY2hpbGQsCiAgICByZXF1aXJlZCB0aGlzLmxpc3RlbmVyLAogICAgdGhpcy5saXN0ZW5XaGVuLAogIH0pOwoKICBAb3ZlcnJpZGUKICBXaWRnZXQgYnVpbGQoQnVpbGRDb250ZXh0IGNvbnRleHQpIHsKICAgIHJldHVybiBCbG9jTGlzdGVuZXI8R2xvYmFsRXZlbnRDaGFubmVsLCBHbG9iYWxFdmVudFN0YXRlPigKICAgICAgbGlzdGVuV2hlbjogbGlzdGVuV2hlbiwKICAgICAgbGlzdGVuZXI6IGxpc3RlbmVyLAogICAgICBjaGlsZDogY2hpbGQsCiAgICApOwogIH0KfQoKY2xhc3MgR2xvYmFsRXZlbnRDaGFubmVsQ29uc3VtZXIgZXh0ZW5kcyBTdGF0ZWxlc3NXaWRnZXQgewogIGZpbmFsIEJsb2NXaWRnZXRCdWlsZGVyPEdsb2JhbEV2ZW50U3RhdGU+IGJ1aWxkZXI7CiAgZmluYWwgQmxvY1dpZGdldExpc3RlbmVyPEdsb2JhbEV2ZW50U3RhdGU+IGxpc3RlbmVyOwogIGZpbmFsIEJsb2NCdWlsZGVyQ29uZGl0aW9uPEdsb2JhbEV2ZW50U3RhdGU+PyBidWlsZFdoZW47CiAgZmluYWwgQmxvY0xpc3RlbmVyQ29uZGl0aW9uPEdsb2JhbEV2ZW50U3RhdGU+PyBsaXN0ZW5XaGVuOwoKICBjb25zdCBHbG9iYWxFdmVudENoYW5uZWxDb25zdW1lcih7CiAgICBzdXBlci5rZXksCiAgICByZXF1aXJlZCB0aGlzLmJ1aWxkZXIsCiAgICByZXF1aXJlZCB0aGlzLmxpc3RlbmVyLAogICAgdGhpcy5idWlsZFdoZW4sCiAgICB0aGlzLmxpc3RlbldoZW4sCiAgfSk7CgogIEBvdmVycmlkZQogIFdpZGdldCBidWlsZChCdWlsZENvbnRleHQgY29udGV4dCkgewogICAgcmV0dXJuIEJsb2NDb25zdW1lcjxHbG9iYWxFdmVudENoYW5uZWwsIEdsb2JhbEV2ZW50U3RhdGU+KAogICAgICBidWlsZFdoZW46IGJ1aWxkV2hlbiwKICAgICAgbGlzdGVuV2hlbjogbGlzdGVuV2hlbiwKICAgICAgYnVpbGRlcjogYnVpbGRlciwKICAgICAgbGlzdGVuZXI6IGxpc3RlbmVyLAogICAgKTsKICB9Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/core/lib/bloc/global_event_channel_state.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNvcmUvYmxvYy9nbG9iYWxfZXZlbnQuZGFydCc7CmltcG9ydCAncGFja2FnZTplcXVhdGFibGUvZXF1YXRhYmxlLmRhcnQnOwoKY2xhc3MgR2xvYmFsRXZlbnRTdGF0ZSBleHRlbmRzIEVxdWF0YWJsZSB7CiAgZmluYWwgTGlzdDxHbG9iYWxFdmVudFR5cGU+IGhpc3Rvcnk7CiAgZmluYWwgTWFwPFR5cGUsIGludD4gZXZlbnRDb3VudHM7CiAgZmluYWwgR2xvYmFsRXZlbnRUeXBlPyBjdXJyZW50OwogIGZpbmFsIGludCBtYXhSZWNlbnRFdmVudHM7CgogIGNvbnN0IEdsb2JhbEV2ZW50U3RhdGUoewogICAgdGhpcy5oaXN0b3J5ID0gY29uc3QgW10sCiAgICB0aGlzLmV2ZW50Q291bnRzID0gY29uc3Qge30sCiAgICB0aGlzLmN1cnJlbnQsCiAgICB0aGlzLm1heFJlY2VudEV2ZW50cyA9IDUwLAogIH0pOwoKICBHbG9iYWxFdmVudFN0YXRlIGNvcHlXaXRoKHsKICAgIExpc3Q8R2xvYmFsRXZlbnRUeXBlPj8gaGlzdG9yeSwKICAgIE1hcDxUeXBlLCBpbnQ+PyBldmVudENvdW50cywKICAgIEdsb2JhbEV2ZW50VHlwZT8gY3VycmVudCwKICAgIGludD8gbWF4UmVjZW50RXZlbnRzLAogIH0pIHsKICAgIHJldHVybiBHbG9iYWxFdmVudFN0YXRlKAogICAgICBoaXN0b3J5OiBoaXN0b3J5ID8/IHRoaXMuaGlzdG9yeSwKICAgICAgZXZlbnRDb3VudHM6IGV2ZW50Q291bnRzID8/IHRoaXMuZXZlbnRDb3VudHMsCiAgICAgIGN1cnJlbnQ6IGN1cnJlbnQgPz8gdGhpcy5jdXJyZW50LAogICAgICBtYXhSZWNlbnRFdmVudHM6IG1heFJlY2VudEV2ZW50cyA/PyB0aGlzLm1heFJlY2VudEV2ZW50cywKICAgICk7CiAgfQoKICBAb3ZlcnJpZGUKICBMaXN0PE9iamVjdD8+IGdldCBwcm9wcyA9PiBbaGlzdG9yeSwgZXZlbnRDb3VudHMsIGN1cnJlbnQsIG1heFJlY2VudEV2ZW50c107Cn0K", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/core/lib/bloc/index.dart", + "data": + "Ly9HRU5FUkFURUQgQkFSUkVMIEZJTEUKCmltcG9ydCAncGFja2FnZTpjb3JlL2Jsb2MvZ2xvYmFsX2V2ZW50LmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Y29yZS9ibG9jL2dsb2JhbF9ldmVudF9jaGFubmVsX2Jsb2MuZGFydCc7CmltcG9ydCAncGFja2FnZTpmbHV0dGVyL2N1cGVydGluby5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXJfYmxvYy9mbHV0dGVyX2Jsb2MuZGFydCc7CgpleHBvcnQgJ2dsb2JhbF9ldmVudC5kYXJ0JzsKZXhwb3J0ICdnbG9iYWxfZXZlbnRfY2hhbm5lbF9ibG9jLmRhcnQnOwpleHBvcnQgJ2dsb2JhbF9ldmVudF9jaGFubmVsX3Byb3ZpZGVyLmRhcnQnOwpleHBvcnQgJ2dsb2JhbF9ldmVudF9jaGFubmVsX3N0YXRlLmRhcnQnOwoKZXh0ZW5zaW9uIEdsb2JhbEV2ZW50Q2hhbm5lbEV4dGVuc2lvbiBvbiBCdWlsZENvbnRleHQgewogIC8vLyBGaXJlcyBhIGdsb2JhbCBldmVudCB3aXRoIHRoZSBnaXZlbiBbZXZlbnRUeXBlXS4KICB2b2lkIGZpcmUoR2xvYmFsRXZlbnRUeXBlIGV2ZW50VHlwZSkgewogICAgZmluYWwgYmxvYyA9IHJlYWQ8R2xvYmFsRXZlbnRDaGFubmVsPigpOwogICAgYmxvYy5hZGQoRmlyZUdsb2JhbEV2ZW50KGV2ZW50VHlwZTogZXZlbnRUeXBlKSk7CiAgfQp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/core/lib/constants/defaults.dart", + "data": + "Ly8vIEFwcC13aWRlIGRlZmF1bHQgdmFsdWVzLgpjbGFzcyBEZWZhdWx0cyB7CiAgRGVmYXVsdHMuXygpOwoKICBzdGF0aWMgY29uc3QgaW50IGRlZmF1bHRQYWdlU2l6ZSA9IDIwOwp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/core/lib/constants/environment.dart", + "data": + "Ly8vIEJ1aWxkLXRpbWUgY29uZmlndXJhdGlvbi4KLy8vCi8vLyBFdmVyeSB2YWx1ZSBpcyBhIGAtLWRhcnQtZGVmaW5lYCwgc28gdGhlIHNhbWUgYmluYXJ5IGNhbiBiZSBwb2ludGVkIGF0IGEKLy8vIGRpZmZlcmVudCBiYWNrZW5kIHdpdGhvdXQgYSBjb2RlIGNoYW5nZToKLy8vCi8vLyBgYGBzaAovLy8gZmx1dHRlciBydW4gLS1kYXJ0LWRlZmluZT1CQVNFX1VSTD1odHRwczovL3N0YWdpbmcuZXhhbXBsZS5jb20KLy8vIGBgYApjbGFzcyBFbnZpcm9ubWVudCB7CiAgRW52aXJvbm1lbnQuXygpOwoKICBzdGF0aWMgY29uc3QgYm9vbCB1c2VFbXVsYXRvcnMgPSBib29sLmZyb21FbnZpcm9ubWVudCgnVVNFX0VNVUxBVE9SUycpOwoKICBzdGF0aWMgY29uc3QgU3RyaW5nIGJhc2VVcmwgPSBTdHJpbmcuZnJvbUVudmlyb25tZW50KAogICAgJ0JBU0VfVVJMJywKICAgIGRlZmF1bHRWYWx1ZTogJ2h0dHBzOi8vYXBpLmV4YW1wbGUuY29tJywKICApOwp9Cg==", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/core/lib/constants/index.dart", + "data": + "ZXhwb3J0ICdkZWZhdWx0cy5kYXJ0JzsKZXhwb3J0ICdlbnZpcm9ubWVudC5kYXJ0JzsKZXhwb3J0ICdrZXlzLmRhcnQnOwpleHBvcnQgJ3JvdXRlcy5kYXJ0JzsK", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/core/lib/constants/keys.dart", + "data": + "Ly8vIFF1ZXJ5LXBhcmFtZXRlciBhbmQgYGV4dHJhYCBrZXlzIHNoYXJlZCBiZXR3ZWVuIHJvdXRlcnMgYW5kIHNjcmVlbnMuCi8vLwovLy8gS2VlcCB0aGVzZSBoZXJlIHJhdGhlciB0aGFuIGlubGluZSBzbyBhIHJlbmFtZSBpcyBhIHNpbmdsZSBlZGl0LgpjbGFzcyBLZXlzIHsKICBLZXlzLl8oKTsKCiAgc3RhdGljIGNvbnN0IFN0cmluZyBlbWFpbCA9ICdlbWFpbCc7CiAgc3RhdGljIGNvbnN0IFN0cmluZyB0aXRsZSA9ICd0aXRsZSc7CiAgc3RhdGljIGNvbnN0IFN0cmluZyBtZXNzYWdlID0gJ21lc3NhZ2UnOwogIHN0YXRpYyBjb25zdCBTdHJpbmcgZXJyb3IgPSAnZXJyb3InOwp9Cg==", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/core/lib/constants/routes.dart", + "data": + "Ly8vIFBhdGhzIG93bmVkIGJ5IHRoZSBhcHAgc2hlbGwgaXRzZWxmLgovLy8KLy8vIEZlYXR1cmUgcGFja2FnZXMgZGVjbGFyZSB0aGVpciBvd24gcm91dGUgY29uc3RhbnRzOyB0aGVzZSBhcmUgdGhlIG9uZXMKLy8vIGV2ZXJ5IGFwcCBoYXMuCmNsYXNzIENvcmVSb3V0ZXMgewogIENvcmVSb3V0ZXMuXygpOwoKICBzdGF0aWMgY29uc3QgU3RyaW5nIHJvb3QgPSAnLyc7CiAgc3RhdGljIGNvbnN0IFN0cmluZyBob21lID0gJy9ob21lJzsKICBzdGF0aWMgY29uc3QgU3RyaW5nIGRhc2hib2FyZCA9ICcvaG9tZS9kYXNoYm9hcmQnOwogIHN0YXRpYyBjb25zdCBTdHJpbmcgcHJvZmlsZSA9ICcvaG9tZS9wcm9maWxlJzsKICBzdGF0aWMgY29uc3QgU3RyaW5nIGVycm9yID0gJy9lcnJvcic7Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/core/lib/converters/datetime_converter.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmludGwvaW50bC5kYXJ0JzsKCmZpbmFsIGNsYXNzIERhdGVUaW1lQ29udmVydGVyIHsKICBjb25zdCBEYXRlVGltZUNvbnZlcnRlcigpOwoKICAvLy8gQ29udmVydHMgYSBbRGF0ZVRpbWVdIHRvIGEgW1N0cmluZ10gaW4gdGhlIGZvcm1hdCAnTU1NTSBkLCB5Jy4KICAvLy8gSWYgdGhlIFtkYXRlXSBpcyBudWxsLCBpdCByZXR1cm5zIGFuIGVtcHR5IHN0cmluZy4KICAvLy8KICAvLy8gRXhhbXBsZToKICAvLy8gYGBgZGFydAogIC8vLyBEYXRlVGltZSBkYXRlID0gRGF0ZVRpbWUubm93KCk7CiAgLy8vIFN0cmluZyBmb3JtYXR0ZWREYXRlID0gRGF0ZVRpbWVDb252ZXJ0ZXIudG9WaWV3Rm9ybWF0KGRhdGUpOwogIC8vLyBwcmludChmb3JtYXR0ZWREYXRlKTsgLy8gT3V0cHV0OiAnT2N0b2JlciAxLCAyMDIzJwogIC8vLyBgYGAKICBzdGF0aWMgU3RyaW5nIHRvVmlld0Zvcm1hdChEYXRlVGltZT8gZGF0ZSkgewogICAgaWYgKGRhdGUgPT0gbnVsbCkgcmV0dXJuICcnOwogICAgZmluYWwgZGF0ZUZvcm1hdGUgPSBEYXRlRm9ybWF0KCdNTU1NIGQsIHknKTsKCiAgICByZXR1cm4gZGF0ZUZvcm1hdGUuZm9ybWF0KGRhdGUudG9Mb2NhbCgpKTsKICB9CgogIHN0YXRpYyBTdHJpbmcgdG9WaWV3Rm9ybWF0V2l0aFRpbWUoRGF0ZVRpbWU/IGRhdGUpIHsKICAgIGlmIChkYXRlID09IG51bGwpIHJldHVybiAnJzsKICAgIGZpbmFsIGRhdGVGb3JtYXRlID0gRGF0ZUZvcm1hdCgnTU1NTSBkLCB5LCBoOm1tIGEnKTsKCiAgICByZXR1cm4gZGF0ZUZvcm1hdGUuZm9ybWF0KGRhdGUpOwogIH0KCiAgc3RhdGljIFN0cmluZyB0b1ZpZXdGb3JtYXRUaW1lKERhdGVUaW1lPyBkYXRlKSB7CiAgICBpZiAoZGF0ZSA9PSBudWxsKSByZXR1cm4gJyc7CiAgICBmaW5hbCBkYXRlRm9ybWF0ZSA9IERhdGVGb3JtYXQoJ2g6bW06c3MgYScpOwoKICAgIHJldHVybiBkYXRlRm9ybWF0ZS5mb3JtYXQoZGF0ZSk7CiAgfQp9Cg==", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/core/lib/converters/index.dart", + "data": + "Ly9HRU5FUkFURUQgQkFSUkVMIEZJTEUKZXhwb3J0ICdkYXRldGltZV9jb252ZXJ0ZXIuZGFydCc7CmV4cG9ydCAndGltZXN0YW1wX2NvbnZlcnRlci5kYXJ0JzsK", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/core/lib/converters/timestamp_converter.dart", + "data": + "e3sjZmlyZXN0b3JlfX1pbXBvcnQgJ3BhY2thZ2U6Y2xvdWRfZmlyZXN0b3JlL2Nsb3VkX2ZpcmVzdG9yZS5kYXJ0JzsKe3svZmlyZXN0b3JlfX1pbXBvcnQgJ3BhY2thZ2U6anNvbl9hbm5vdGF0aW9uL2pzb25fYW5ub3RhdGlvbi5kYXJ0JzsKCi8vLyBBY2NlcHRzIHRoZSB0aHJlZSBzaGFwZXMgYSB0aW1lc3RhbXAgYXJyaXZlcyBpbiAtIElTTyBzdHJpbmcse3sjZmlyZXN0b3JlfX0KLy8vIEZpcmVzdG9yZSBbVGltZXN0YW1wXSx7ey9maXJlc3RvcmV9fSBvciB0aGUgYHtfc2Vjb25kcywgX25hbm9zZWNvbmRzfWAgbWFwIHRoYXQKLy8vIENsb3VkIEZ1bmN0aW9ucyBlbWl0IC0gYW5kIGFsd2F5cyB3cml0ZXMgYmFjayBVVEMgSVNPLTg2MDEuCmNsYXNzIFRpbWVzdGFtcENvbnZlcnRlciBpbXBsZW1lbnRzIEpzb25Db252ZXJ0ZXI8RGF0ZVRpbWUsIGR5bmFtaWM+IHsKICBjb25zdCBUaW1lc3RhbXBDb252ZXJ0ZXIoKTsKCiAgQG92ZXJyaWRlCiAgRGF0ZVRpbWUgZnJvbUpzb24oZHluYW1pYyBqc29uKSB7CiAgICBmaW5hbCBwYXJzZWQgPSBfdHJ5UGFyc2UoanNvbik7CiAgICBpZiAocGFyc2VkID09IG51bGwpIHsKICAgICAgdGhyb3cgQXJndW1lbnRFcnJvcignSW52YWxpZCB0aW1lc3RhbXAgZm9ybWF0OiAkanNvbicpOwogICAgfQogICAgcmV0dXJuIHBhcnNlZDsKICB9CgogIEBvdmVycmlkZQogIFN0cmluZz8gdG9Kc29uKERhdGVUaW1lPyBkYXRlKSA9PiBkYXRlPy50b1V0YygpLnRvSXNvODYwMVN0cmluZygpOwp9CgpjbGFzcyBUaW1lc3RhbXBDb252ZXJ0ZXJOdWxsYWJsZSBpbXBsZW1lbnRzIEpzb25Db252ZXJ0ZXI8RGF0ZVRpbWU/LCBkeW5hbWljPiB7CiAgY29uc3QgVGltZXN0YW1wQ29udmVydGVyTnVsbGFibGUoKTsKCiAgQG92ZXJyaWRlCiAgRGF0ZVRpbWU/IGZyb21Kc29uKGR5bmFtaWMganNvbikgPT4gX3RyeVBhcnNlKGpzb24pOwoKICBAb3ZlcnJpZGUKICBTdHJpbmc/IHRvSnNvbihEYXRlVGltZT8gZGF0ZSkgPT4gZGF0ZT8udG9VdGMoKS50b0lzbzg2MDFTdHJpbmcoKTsKfQoKRGF0ZVRpbWU/IF90cnlQYXJzZShkeW5hbWljIGpzb24pIHsKICBpZiAoanNvbiBpcyBTdHJpbmcgJiYganNvbi5pc05vdEVtcHR5KSByZXR1cm4gRGF0ZVRpbWUucGFyc2UoanNvbikudG9Mb2NhbCgpOwp7eyNmaXJlc3RvcmV9fSAgaWYgKGpzb24gaXMgVGltZXN0YW1wKSByZXR1cm4ganNvbi50b0RhdGUoKTsKe3svZmlyZXN0b3JlfX0gIGlmIChqc29uIGlzIE1hcDxTdHJpbmcsIGR5bmFtaWM+KSB7CiAgICBmaW5hbCBzZWNvbmRzID0ganNvblsnX3NlY29uZHMnXSBhcyBpbnQ7CiAgICBmaW5hbCBuYW5vc2Vjb25kcyA9IGpzb25bJ19uYW5vc2Vjb25kcyddIGFzIGludD8gPz8gMDsKICAgIHJldHVybiBEYXRlVGltZS5mcm9tTWlsbGlzZWNvbmRzU2luY2VFcG9jaCgKICAgICAgc2Vjb25kcyAqIDEwMDAgKyBuYW5vc2Vjb25kcyB+LyAxMDAwMDAwLAogICAgKTsKICB9CiAgcmV0dXJuIG51bGw7Cn0K", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/core/lib/core.dart", + "data": + "bGlicmFyeTsKCmltcG9ydCAncGFja2FnZTpjb3JlL2xvZ2dlci9sb2dnZXIuZGFydCc7CmltcG9ydCAncGFja2FnZTpkaS9kaS5kYXJ0JzsKCmV4cG9ydCAnYmxvYy9pbmRleC5kYXJ0JzsKZXhwb3J0ICdjb25zdGFudHMvaW5kZXguZGFydCc7CmV4cG9ydCAnY29udmVydGVycy9pbmRleC5kYXJ0JzsKZXhwb3J0ICdlcnJvcnMvaW5kZXguZGFydCc7CmV4cG9ydCAnZXh0ZW5zaW9ucy9pbmRleC5kYXJ0JzsKZXhwb3J0ICdsb2dnZXIvbG9nZ2VyLmRhcnQnOwpleHBvcnQgJ29ic2VydmVyL2luZGV4LmRhcnQnOwpleHBvcnQgJ3JvdXRlci9jb3JlX3JvdXRlci5kYXJ0JzsKCkZ1dHVyZTx2b2lkPiBpbml0KCkgYXN5bmMgewogIHJlZ2lzdGVyTG9nZ2VyV2l0aERJKCk7CiAgZmluYWwgbG9nZ2VyID0gZGkuZ2V0PExvZ2dlcj4oKTsKICBsb2dnZXIuaSgnQ29yZSBtb2R1bGUgaW5pdGlhbGl6ZWQnKTsKfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/core/lib/developer/emulators.dart", + "data": + "e3sjZmlyZWJhc2V9fXt7I2F1dGh9fWltcG9ydCAncGFja2FnZTpmaXJlYmFzZV9hdXRoL2ZpcmViYXNlX2F1dGguZGFydCc7Cnt7L2F1dGh9fXt7I2ZpcmVzdG9yZX19aW1wb3J0ICdwYWNrYWdlOmNsb3VkX2ZpcmVzdG9yZS9jbG91ZF9maXJlc3RvcmUuZGFydCc7CmltcG9ydCAncGFja2FnZTpjbG91ZF9mdW5jdGlvbnMvY2xvdWRfZnVuY3Rpb25zLmRhcnQnOwp7ey9maXJlc3RvcmV9fWltcG9ydCAncGFja2FnZTpjb3JlL2xvZ2dlci9sb2dnZXIuZGFydCc7CmltcG9ydCAncGFja2FnZTpkaS9kaS5kYXJ0JzsKCi8vLyBQb2ludHMgZXZlcnkgRmlyZWJhc2UgU0RLIGF0IHRoZSBsb2NhbCBlbXVsYXRvciBzdWl0ZS4KLy8vCi8vLyBQb3J0cyBtYXRjaCB0aGUgZGVmYXVsdHMgaW4gYGZpcmViYXNlLmpzb25gOyBjaGFuZ2UgYm90aCB0b2dldGhlci4KRnV0dXJlPHZvaWQ+IGluaXQoKSBhc3luYyB7CiAgZmluYWwgbG9nZ2VyID0gZGkuZ2V0PExvZ2dlcj4oKTsKICBsb2dnZXIuaSgnVXNpbmcgRmlyZWJhc2UgZW11bGF0b3JzJyk7CiAgdHJ5IHsKe3sjYXV0aH19ICAgIGF3YWl0IEZpcmViYXNlQXV0aC5pbnN0YW5jZS51c2VBdXRoRW11bGF0b3IoJ2xvY2FsaG9zdCcsIDkwOTkpOwp7ey9hdXRofX17eyNmaXJlc3RvcmV9fSAgICBGaXJlYmFzZUZpcmVzdG9yZS5pbnN0YW5jZS51c2VGaXJlc3RvcmVFbXVsYXRvcignbG9jYWxob3N0JywgODA4MCk7CiAgICBGaXJlYmFzZUZ1bmN0aW9ucy5pbnN0YW5jZS51c2VGdW5jdGlvbnNFbXVsYXRvcignbG9jYWxob3N0JywgNTAwMSk7Cnt7L2ZpcmVzdG9yZX19ICAgIGxvZ2dlci5pKCdTdWNjZXNzZnVsbHkgaW5pdGlhbGlzZWQgdGhlIEZpcmViYXNlIGVtdWxhdG9ycycpOwogIH0gY2F0Y2ggKGVycm9yLCBzdGFja1RyYWNlKSB7CiAgICBsb2dnZXIuZSgnRmFpbGVkIHRvIGluaXRpYWxpc2UgdGhlIEZpcmViYXNlIGVtdWxhdG9yczonLCBlcnJvciwgc3RhY2tUcmFjZSk7CiAgfQp9Cnt7L2ZpcmViYXNlfX17e15maXJlYmFzZX19Ly8vIE5vIEZpcmViYXNlIHNlcnZpY2VzIGFyZSBlbmFibGVkIGluIHRoaXMgcHJvamVjdCwgc28gdGhlcmUgaXMgbm90aGluZyB0bwovLy8gcG9pbnQgYXQgYW4gZW11bGF0b3IuIEtlcHQgYXMgYSBuby1vcCBzbyBib290c3RyYXAgc3RheXMgdW5pZm9ybS4KRnV0dXJlPHZvaWQ+IGluaXQoKSBhc3luYyB7fQp7ey9maXJlYmFzZX19", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/core/lib/errors/exceptions.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmxvY2FsaXphdGlvbi9sb2NhbGl6YXRpb24uZGFydCc7CgpjbGFzcyBDb3JlRXhjZXB0aW9uIGltcGxlbWVudHMgRXhjZXB0aW9uIHsKICBmaW5hbCBTdHJpbmcgbWVzc2FnZTsKCiAgQ29yZUV4Y2VwdGlvbih7cmVxdWlyZWQgdGhpcy5tZXNzYWdlfSk7Cn0KCmNsYXNzIFVzZXJOb3RGb3VuZEV4Y2VwdGlvbiBleHRlbmRzIENvcmVFeGNlcHRpb24gewogIFVzZXJOb3RGb3VuZEV4Y2VwdGlvbigpIDogc3VwZXIobWVzc2FnZTogc3RyaW5ncy5lcnJvcnMudXNlcl9ub3RfZm91bmQpOwp9Cg==", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/core/lib/errors/index.dart", + "data": + "Ly9HRU5FUkFURUQgQkFSUkVMIEZJTEUKZXhwb3J0ICdleGNlcHRpb25zLmRhcnQnOwo=", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/core/lib/extensions/basic.dart", + "data": + "ZXh0ZW5zaW9uIEJhc2ljU3RyaW5nRXh0ZW5zaW9ucyBvbiBTdHJpbmc/IHsKICBib29sIGdldCBhc0Jvb2wgewogICAgaWYgKHRoaXMgPT0gbnVsbCkgcmV0dXJuIGZhbHNlOwogICAgZmluYWwgbG93ZXIgPSB0aGlzIS50b0xvd2VyQ2FzZSgpOwogICAgcmV0dXJuIGxvd2VyID09ICd0cnVlJyB8fCBsb3dlciA9PSAnMSc7CiAgfQp9Cg==", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/core/lib/extensions/index.dart", + "data": "ZXhwb3J0ICdiYXNpYy5kYXJ0JzsK", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/core/lib/logger/index.dart", + "data": + "Ly9HRU5FUkFURUQgQkFSUkVMIEZJTEUKZXhwb3J0ICdsb2dnZXIuZGFydCc7CmV4cG9ydCAndGFsa2VyX2xvZ2dlcl9pbXBsLmRhcnQnOwo=", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/core/lib/logger/logger.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNvcmUvbG9nZ2VyL3RhbGtlcl9sb2dnZXJfaW1wbC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRpL2RpLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6dGFsa2VyX2ZsdXR0ZXIvdGFsa2VyX2ZsdXR0ZXIuZGFydCc7Cgp2b2lkIHJlZ2lzdGVyTG9nZ2VyV2l0aERJKCkgewogIGZpbmFsIHRhbGtlciA9IFRhbGtlckZsdXR0ZXIuaW5pdCgpOwogIGZpbmFsIGxvZ2dlciA9IFRhbGtlckxvZ2dlckltcGwodGFsa2VyKTsKCiAgZGkucmVnaXN0ZXI8TG9nZ2VyPihsb2dnZXIpOwp9CgphYnN0cmFjdCBjbGFzcyBMb2dnZXIgewogIE9iamVjdCBnZXQgbG9nZ2VyOwoKICB2b2lkIGQoU3RyaW5nIG1lc3NhZ2UpOwoKICB2b2lkIGkoU3RyaW5nIG1lc3NhZ2UpOwoKICB2b2lkIHcoU3RyaW5nIG1lc3NhZ2UpOwoKICB2b2lkIGUoU3RyaW5nIG1lc3NhZ2UsIFtPYmplY3Q/IGVycm9yLCBTdGFja1RyYWNlPyBzdGFja1RyYWNlXSk7Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/core/lib/logger/talker_logger_impl.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNvcmUvbG9nZ2VyL2xvZ2dlci5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOnRhbGtlcl9mbHV0dGVyL3RhbGtlcl9mbHV0dGVyLmRhcnQnOwoKY2xhc3MgVGFsa2VyTG9nZ2VySW1wbCBpbXBsZW1lbnRzIExvZ2dlciB7CiAgZmluYWwgVGFsa2VyIF90YWxrZXI7CgogIFRhbGtlckxvZ2dlckltcGwodGhpcy5fdGFsa2VyKTsKCiAgQG92ZXJyaWRlCiAgdm9pZCBkKFN0cmluZyBtZXNzYWdlKSB7CiAgICBfdGFsa2VyLmRlYnVnKG1lc3NhZ2UpOwogIH0KCiAgQG92ZXJyaWRlCiAgdm9pZCBpKFN0cmluZyBtZXNzYWdlKSB7CiAgICBfdGFsa2VyLmluZm8obWVzc2FnZSk7CiAgfQoKICBAb3ZlcnJpZGUKICB2b2lkIHcoU3RyaW5nIG1lc3NhZ2UpIHsKICAgIF90YWxrZXIud2FybmluZyhtZXNzYWdlKTsKICB9CgogIEBvdmVycmlkZQogIHZvaWQgZShTdHJpbmcgbWVzc2FnZSwgW09iamVjdD8gZXJyb3IsIFN0YWNrVHJhY2U/IHN0YWNrVHJhY2VdKSB7CiAgICBfdGFsa2VyLmVycm9yKG1lc3NhZ2UsIGVycm9yLCBzdGFja1RyYWNlKTsKICB9CgogIEBvdmVycmlkZQogIE9iamVjdCBnZXQgbG9nZ2VyID0+IF90YWxrZXI7Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/core/lib/observer/core_bloc_observer.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmJsb2MvYmxvYy5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmNvcmUvY29yZS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRpL2RpLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6dGFsa2VyX2Jsb2NfbG9nZ2VyL3RhbGtlcl9ibG9jX2xvZ2dlcl9vYnNlcnZlci5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOnRhbGtlcl9ibG9jX2xvZ2dlci90YWxrZXJfYmxvY19sb2dnZXJfc2V0dGluZ3MuZGFydCc7CmltcG9ydCAncGFja2FnZTp0YWxrZXJfZmx1dHRlci90YWxrZXJfZmx1dHRlci5kYXJ0JzsKCmNsYXNzIENvcmVCbG9jT2JzZXJ2ZXIgZXh0ZW5kcyBCbG9jT2JzZXJ2ZXIgewogIGZpbmFsIFRhbGtlckJsb2NPYnNlcnZlciBfb2JzZXJ2ZXI7CgogIENvcmVCbG9jT2JzZXJ2ZXIoKQogICAgOiBfb2JzZXJ2ZXIgPSBUYWxrZXJCbG9jT2JzZXJ2ZXIoCiAgICAgICAgdGFsa2VyOiBkaS5nZXQ8TG9nZ2VyPigpLmxvZ2dlciBhcyBUYWxrZXIsCiAgICAgICAgc2V0dGluZ3M6IFRhbGtlckJsb2NMb2dnZXJTZXR0aW5ncyhwcmludEV2ZW50RnVsbERhdGE6IHRydWUpLAogICAgICApOwoKICBAb3ZlcnJpZGUKICB2b2lkIG9uRXZlbnQoQmxvYyBibG9jLCBPYmplY3Q/IGV2ZW50KSB7CiAgICBzdXBlci5vbkV2ZW50KGJsb2MsIGV2ZW50KTsKICAgIF9vYnNlcnZlci5vbkV2ZW50KGJsb2MsIGV2ZW50KTsKICB9CgogIEBvdmVycmlkZQogIHZvaWQgb25UcmFuc2l0aW9uKEJsb2MgYmxvYywgVHJhbnNpdGlvbiB0cmFuc2l0aW9uKSB7CiAgICBzdXBlci5vblRyYW5zaXRpb24oYmxvYywgdHJhbnNpdGlvbik7CiAgICBfb2JzZXJ2ZXIub25UcmFuc2l0aW9uKGJsb2MsIHRyYW5zaXRpb24pOwogIH0KCiAgQG92ZXJyaWRlCiAgdm9pZCBvbkVycm9yKEJsb2NCYXNlIGJsb2MsIE9iamVjdCBlcnJvciwgU3RhY2tUcmFjZSBzdGFja1RyYWNlKSB7CiAgICBzdXBlci5vbkVycm9yKGJsb2MsIGVycm9yLCBzdGFja1RyYWNlKTsKICAgIF9vYnNlcnZlci5vbkVycm9yKGJsb2MsIGVycm9yLCBzdGFja1RyYWNlKTsKICB9Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/core/lib/observer/core_route_observer.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNvcmUvY29yZS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRpL2RpLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlci9tYXRlcmlhbC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOm5hdmlnYXRpb25faGlzdG9yeV9vYnNlcnZlci9uYXZpZ2F0aW9uX2hpc3Rvcnlfb2JzZXJ2ZXIuZGFydCc7CmltcG9ydCAncGFja2FnZTp0YWxrZXJfZmx1dHRlci90YWxrZXJfZmx1dHRlci5kYXJ0JzsKCmNsYXNzIENvcmVSb3V0ZU9ic2VydmVyIGV4dGVuZHMgTmF2aWdhdG9yT2JzZXJ2ZXIgewogIENvcmVSb3V0ZU9ic2VydmVyKCkgOiBfb2JzZXJ2ZXIgPSBkaS5nZXQ8TG9nZ2VyPigpLmxvZ2dlciBhcyBUYWxrZXI7CgogIGZpbmFsIFRhbGtlciBfb2JzZXJ2ZXI7CgogIEBvdmVycmlkZQogIHZvaWQgZGlkUHVzaChSb3V0ZSByb3V0ZSwgUm91dGU/IHByZXZpb3VzUm91dGUpIHsKICAgIHN1cGVyLmRpZFB1c2gocm91dGUsIHByZXZpb3VzUm91dGUpOwogICAgaWYgKHJvdXRlLnNldHRpbmdzLm5hbWUgPT0gbnVsbCkgewogICAgICByZXR1cm47CiAgICB9CiAgICBfb2JzZXJ2ZXIubG9nQ3VzdG9tKFRhbGtlclJvdXRlTG9nKHJvdXRlOiByb3V0ZSwgdHlwZTogUm91dGVMb2dUeXBlLnB1c2gpKTsKICB9CgogIEBvdmVycmlkZQogIHZvaWQgZGlkUG9wKFJvdXRlIHJvdXRlLCBSb3V0ZT8gcHJldmlvdXNSb3V0ZSkgewogICAgc3VwZXIuZGlkUG9wKHJvdXRlLCBwcmV2aW91c1JvdXRlKTsKICAgIGlmIChyb3V0ZS5zZXR0aW5ncy5uYW1lID09IG51bGwpIHsKICAgICAgcmV0dXJuOwogICAgfQogICAgX29ic2VydmVyLmxvZ0N1c3RvbShUYWxrZXJSb3V0ZUxvZyhyb3V0ZTogcm91dGUsIHR5cGU6IFJvdXRlTG9nVHlwZS5wb3ApKTsKICB9CgogIEBvdmVycmlkZQogIHZvaWQgZGlkUmVtb3ZlKFJvdXRlPGR5bmFtaWM+IHJvdXRlLCBSb3V0ZTxkeW5hbWljPj8gcHJldmlvdXNSb3V0ZSkgewogICAgc3VwZXIuZGlkUmVtb3ZlKHJvdXRlLCBwcmV2aW91c1JvdXRlKTsKICAgIGlmIChyb3V0ZS5zZXR0aW5ncy5uYW1lID09IG51bGwpIHsKICAgICAgcmV0dXJuOwogICAgfQogICAgX29ic2VydmVyLmxvZ0N1c3RvbSgKICAgICAgVGFsa2VyUm91dGVMb2cocm91dGU6IHJvdXRlLCB0eXBlOiBSb3V0ZUxvZ1R5cGUucmVtb3ZlKSwKICAgICk7CiAgfQoKICBAb3ZlcnJpZGUKICB2b2lkIGRpZFJlcGxhY2Uoe1JvdXRlPGR5bmFtaWM+PyBuZXdSb3V0ZSwgUm91dGU8ZHluYW1pYz4/IG9sZFJvdXRlfSkgewogICAgc3VwZXIuZGlkUmVwbGFjZShuZXdSb3V0ZTogbmV3Um91dGUsIG9sZFJvdXRlOiBvbGRSb3V0ZSk7CiAgICBpZiAobmV3Um91dGU/LnNldHRpbmdzLm5hbWUgPT0gbnVsbCkgewogICAgICByZXR1cm47CiAgICB9CiAgICBfb2JzZXJ2ZXIubG9nQ3VzdG9tKAogICAgICBUYWxrZXJSb3V0ZUxvZyhyb3V0ZTogbmV3Um91dGUhLCB0eXBlOiBSb3V0ZUxvZ1R5cGUucmVwbGFjZSksCiAgICApOwogIH0KfQoKZW51bSBSb3V0ZUxvZ1R5cGUgeyBwdXNoLCBwb3AsIHJlbW92ZSwgcmVwbGFjZSB9CgpjbGFzcyBUYWxrZXJSb3V0ZUxvZyBleHRlbmRzIFRhbGtlckxvZyB7CiAgVGFsa2VyUm91dGVMb2coe3JlcXVpcmVkIFJvdXRlIHJvdXRlLCByZXF1aXJlZCBSb3V0ZUxvZ1R5cGUgdHlwZX0pCiAgICA6IHN1cGVyKF9jcmVhdGVNZXNzYWdlKHJvdXRlLCB0eXBlKSk7CgogIEBvdmVycmlkZQogIEFuc2lQZW4gZ2V0IHBlbiA9PiBBbnNpUGVuKCkuLnh0ZXJtKDEzNSk7CgogIEBvdmVycmlkZQogIFN0cmluZyBnZXQga2V5ID0+IFRhbGtlckxvZ1R5cGUucm91dGUua2V5OwoKICBzdGF0aWMgU3RyaW5nIF9jcmVhdGVNZXNzYWdlKFJvdXRlPGR5bmFtaWM+IHJvdXRlLCBSb3V0ZUxvZ1R5cGUgdHlwZSkgewogICAgZmluYWwgYnVmZmVyID0gU3RyaW5nQnVmZmVyKCk7CiAgICBidWZmZXIud3JpdGUodHlwZS5uYW1lKTsKICAgIGJ1ZmZlci53cml0ZSgnIHJvdXRlIG5hbWVkICcpOwogICAgYnVmZmVyLndyaXRlKHJvdXRlLnNldHRpbmdzLm5hbWUgPz8gJ251bGwnKTsKCiAgICBmaW5hbCBhcmdzID0gcm91dGUuc2V0dGluZ3MuYXJndW1lbnRzOwogICAgaWYgKGFyZ3MgIT0gbnVsbCkgewogICAgICBidWZmZXIud3JpdGUoJ1xuQXJndW1lbnRzOiAkYXJncycpOwogICAgfQoKICAgIGZpbmFsIGhpc3RvcnlPYnNlcnZlciA9IE5hdmlnYXRpb25IaXN0b3J5T2JzZXJ2ZXIoKTsKICAgIGZpbmFsIGhpc3RvcnkgPSBoaXN0b3J5T2JzZXJ2ZXIuaGlzdG9yeTsKICAgIGlmIChoaXN0b3J5LmlzRW1wdHkpIHsKICAgICAgYnVmZmVyLndyaXRlKCdcbkhpc3Rvcnk6IG5hJyk7CiAgICB9IGVsc2UgewogICAgICBmaW5hbCBoaXN0b3J5QXNTdHJpbmcgPSBoaXN0b3J5T2JzZXJ2ZXIuaGlzdG9yeQogICAgICAgICAgLm1hcCgocikgPT4gci5zZXR0aW5ncy5uYW1lKQogICAgICAgICAgLmpvaW4oJyAtPiAnKTsKICAgICAgYnVmZmVyLndyaXRlKCdcbkhpc3Rvcnk6ICRoaXN0b3J5QXNTdHJpbmcnKTsKICAgIH0KCiAgICByZXR1cm4gYnVmZmVyLnRvU3RyaW5nKCk7CiAgfQp9Cg==", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/core/lib/observer/index.dart", + "data": + "Ly9HRU5FUkFURUQgQkFSUkVMIEZJTEUKZXhwb3J0ICdjb3JlX2Jsb2Nfb2JzZXJ2ZXIuZGFydCc7CmV4cG9ydCAnY29yZV9yb3V0ZV9vYnNlcnZlci5kYXJ0JzsK", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/core/lib/router/core_router.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmdvX3JvdXRlci9nb19yb3V0ZXIuZGFydCc7CgphYnN0cmFjdCBjbGFzcyBDb3JlUm91dGVyIHsKICBMaXN0PFJvdXRlQmFzZT4gZ2V0IHJvdXRlczsKfQo=", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/core/pubspec.yaml", + "data": + "bmFtZTogY29yZQpkZXNjcmlwdGlvbjogIkNyb3NzLWN1dHRpbmcgZm91bmRhdGlvbiBmb3Ige3tuYW1lLnRpdGxlQ2FzZSgpfX06IGxvZ2dpbmcsIERJIHdpcmluZywgcm91dGluZyBjb250cmFjdHMsIGVycm9yIHR5cGVzIGFuZCBCTG9DIHBsdW1iaW5nLiIKdmVyc2lvbjogMC4xLjAKcHVibGlzaF90bzogJ25vbmUnCgplbnZpcm9ubWVudDoKICBzZGs6IF4zLjguMAogIGZsdXR0ZXI6ICI+PTMuMjkuMCIKCmRlcGVuZGVuY2llczoKICBmbHV0dGVyOgogICAgc2RrOiBmbHV0dGVyCiAgYmxvYzogXjkuMC4wCiAgZXF1YXRhYmxlOiBeMi4wLjUKICBmbHV0dGVyX2Jsb2M6IF45LjEuMQogIGdvX3JvdXRlcjogXjE1LjEuMwogIGludGw6IF4wLjIwLjIKICBqc29uX2Fubm90YXRpb246IF40LjkuMAogIG5hdmlnYXRpb25faGlzdG9yeV9vYnNlcnZlcjogXjEuMS4xCiAgdGFsa2VyX2Jsb2NfbG9nZ2VyOiBeNC45LjEKICB0YWxrZXJfZmx1dHRlcjogXjQuNC4xCiAgZGk6CiAgICBwYXRoOiAuLi9kaQogIGxvY2FsaXphdGlvbjoKICAgIHBhdGg6IC4uL2xvY2FsaXphdGlvbgp7eyNmaXJlYmFzZX19ICBmaXJlYmFzZV9jb3JlOiBeMy4xNC4wCnt7L2ZpcmViYXNlfX17eyNhdXRofX0gIGZpcmViYXNlX2F1dGg6IF41LjMuNAp7ey9hdXRofX17eyNmaXJlc3RvcmV9fSAgY2xvdWRfZmlyZXN0b3JlOiBeNS41LjMKICBjbG91ZF9mdW5jdGlvbnM6IF41LjIuMwp7ey9maXJlc3RvcmV9fQpkZXZfZGVwZW5kZW5jaWVzOgogIGZsdXR0ZXJfdGVzdDoKICAgIHNkazogZmx1dHRlcgogIGZsdXR0ZXJfbGludHM6IF41LjAuMAoKZGVwZW5kZW5jeV9vdmVycmlkZXM6CiAgaW50bDogXjAuMjAuMgoKZmx1dHRlcjoK", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/core/README.md", + "data": + "IyBDb3JlCgpBIE1lbG9zLW1hbmFnZWQgcHJvamVjdCBmb3IgbW9uby1yZXBvLCBjcmVhdGVkIHVzaW5nIE5vblN0b3AgQ0xJLgoKWyFbbm9uc3RvcF9jbGldKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2Uvc3RhcnRlZCUyMHdpdGgtbm9uc3RvcF9jbGktMTY2QzRFLnN2Zz9zdHlsZT1mbGF0LXNxdWFyZSldKGh0dHBzOi8vcHViLmRldi9wYWNrYWdlcy9ub25zdG9wX2NsaSkKWyFbbWVsb3NdKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2UvbWFpbnRhaW5lZCUyMHdpdGgtbWVsb3MtZjcwMGZmLnN2Zz9zdHlsZT1mbGF0LXNxdWFyZSldKGh0dHBzOi8vZ2l0aHViLmNvbS9pbnZlcnRhc2UvbWVsb3MpCgojIyBJbnN0YWxsYXRpb24g8J+SuwoKKirinZcgSW4gb3JkZXIgdG8gc3RhcnQgdXNpbmcgQ29yZSB5b3UgbXVzdCBoYXZlIHRoZSBbRmx1dHRlciBTREtdW2ZsdXR0ZXJfaW5zdGFsbF9saW5rXSBpbnN0YWxsZWQgb24geW91ciBtYWNoaW5lLioqCgpJbnN0YWxsIHZpYSBgZGFydCBwdWIgYWRkYDoKCmBgYHNoCmRhcnQgcHViIGFkZCBjb3JlCmBgYAoKW2ZsdXR0ZXJfaW5zdGFsbF9saW5rXTogaHR0cHM6Ly9kb2NzLmZsdXR0ZXIuZGV2L2dldC1zdGFydGVkL2luc3RhbGw=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/components/asset_image.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CmltcG9ydCAncGFja2FnZTpmbHV0dGVyX3N2Zy9zdmcuZGFydCc7CgpjbGFzcyBBc3NldEltYWdlIGV4dGVuZHMgU3RhdGVsZXNzV2lkZ2V0IHsKICBmaW5hbCBTdHJpbmcgYXNzZXRQYXRoOwogIGZpbmFsIGRvdWJsZT8gd2lkdGg7CiAgZmluYWwgZG91YmxlPyBoZWlnaHQ7CiAgZmluYWwgQm94Rml0IGJveEZpdDsKICBmaW5hbCBTdHJpbmc/IHBhY2thZ2U7CgogIGNvbnN0IEFzc2V0SW1hZ2UoewogICAgc3VwZXIua2V5LAogICAgcmVxdWlyZWQgdGhpcy5hc3NldFBhdGgsCiAgICB0aGlzLndpZHRoLAogICAgdGhpcy5oZWlnaHQsCiAgICB0aGlzLmJveEZpdCA9IEJveEZpdC5jb3ZlciwKICAgIHRoaXMucGFja2FnZSwKICB9KTsKICBAb3ZlcnJpZGUKICBXaWRnZXQgYnVpbGQoQnVpbGRDb250ZXh0IGNvbnRleHQpIHsKICAgIC8vIGlmIGFzc2V0IGlzIHN2ZywgdXNlIFN2Z1BpY3R1cmUuYXNzZXQKICAgIGlmIChhc3NldFBhdGguZW5kc1dpdGgoJy5zdmcnKSkgewogICAgICByZXR1cm4gU3ZnUGljdHVyZS5hc3NldCgKICAgICAgICBhc3NldFBhdGgsCiAgICAgICAgd2lkdGg6IHdpZHRoLAogICAgICAgIGhlaWdodDogaGVpZ2h0LAogICAgICAgIGZpdDogYm94Rml0LAogICAgICAgIHBhY2thZ2U6IHBhY2thZ2UsCiAgICAgICAgZXJyb3JCdWlsZGVyOiAoY29udGV4dCwgZXJyb3IsIHN0YWNrVHJhY2UpIHsKICAgICAgICAgIHJldHVybiBJY29uKAogICAgICAgICAgICBJY29ucy5pbWFnZSwKICAgICAgICAgICAgc2l6ZTogMzIsCiAgICAgICAgICAgIGNvbG9yOiBUaGVtZS5vZihjb250ZXh0KS5jb2xvclNjaGVtZS5vblN1cmZhY2UsCiAgICAgICAgICApOwogICAgICAgIH0sCiAgICAgICk7CiAgICB9CgogICAgLy8gb3RoZXJ3aXNlLCB1c2UgSW1hZ2UuYXNzZXQKICAgIHJldHVybiBJbWFnZS5hc3NldCgKICAgICAgYXNzZXRQYXRoLAogICAgICB3aWR0aDogd2lkdGgsCiAgICAgIGhlaWdodDogaGVpZ2h0LAogICAgICBmaXQ6IGJveEZpdCwKICAgICAgcGFja2FnZTogcGFja2FnZSwKICAgICAgZXJyb3JCdWlsZGVyOiAoY29udGV4dCwgZXJyb3IsIHN0YWNrVHJhY2UpIHsKICAgICAgICByZXR1cm4gSWNvbigKICAgICAgICAgIEljb25zLmltYWdlLAogICAgICAgICAgc2l6ZTogMzIsCiAgICAgICAgICBjb2xvcjogVGhlbWUub2YoY29udGV4dCkuY29sb3JTY2hlbWUub25TdXJmYWNlLAogICAgICAgICk7CiAgICAgIH0sCiAgICApOwogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/components/auth_headers_builder.dart", + "data": + "e3sjbmV0d29ya319aW1wb3J0ICdwYWNrYWdlOmNvcmUvY29yZS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRpL2RpLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlci9tYXRlcmlhbC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOm5ldHdvcmsvbmV0d29yay5kYXJ0JzsKCi8vLyBBIHdpZGdldCB0aGF0IHByb3ZpZGVzIGF1dGhlbnRpY2F0aW9uIGhlYWRlcnMgdG8gaXRzIGNoaWxkIGJ1aWxkZXIuCi8vLwovLy8gVGhpcyB3aWRnZXQgYXV0b21hdGljYWxseSBmZXRjaGVzIHRoZSBjdXJyZW50IGF1dGhlbnRpY2F0aW9uIHRva2VuCi8vLyB1c2luZyB0aGUgW0F1dGhUb2tlblByb3ZpZGVyXSBmcm9tIGRlcGVuZGVuY3kgaW5qZWN0aW9uIGFuZCBjb25zdHJ1Y3RzCi8vLyBIVFRQIGhlYWRlcnMgd2l0aCB0aGUgQmVhcmVyIHRva2VuLgovLy8KLy8vIEV4YW1wbGUgdXNhZ2U6Ci8vLyBgYGBkYXJ0Ci8vLyAvLyBXaXRob3V0IHRva2VuIHZhbGlkYXRpb24gKGRlZmF1bHQgLSBmYXN0ZXIsIHVzZXMgY2FjaGVkIHRva2VuKQovLy8gQXV0aEhlYWRlcnNCdWlsZGVyKAovLy8gICBidWlsZGVyOiAoY29udGV4dCwgaGVhZGVycykgewovLy8gICAgIHJldHVybiBDYWNoZWROZXR3b3JrSW1hZ2UoCi8vLyAgICAgICBpbWFnZVVybDogaW1hZ2VVcmwsCi8vLyAgICAgICBodHRwSGVhZGVyczogaGVhZGVycywKLy8vICAgICApOwovLy8gICB9LAovLy8gKQovLy8KLy8vIC8vIFdpdGggdG9rZW4gdmFsaWRhdGlvbiAoZW5zdXJlcyBmcmVzaCB0b2tlbikKLy8vIEF1dGhIZWFkZXJzQnVpbGRlcihkCi8vLyAgIHZhbGlkYXRlVG9rZW46IHRydWUsCi8vLyAgIGJ1aWxkZXI6IChjb250ZXh0LCBoZWFkZXJzKSB7Ci8vLyAgICAgcmV0dXJuIENhY2hlZE5ldHdvcmtJbWFnZSgKLy8vICAgICAgIGltYWdlVXJsOiBpbWFnZVVybCwKLy8vICAgICAgIGh0dHBIZWFkZXJzOiBoZWFkZXJzLAovLy8gICAgICk7Ci8vLyAgIH0sCi8vLyApCi8vLyBgYGAKY2xhc3MgQXV0aEhlYWRlcnNCdWlsZGVyIGV4dGVuZHMgU3RhdGVsZXNzV2lkZ2V0IHsKICBzdGF0aWMgZmluYWwgX2xvZ2dlciA9IGRpLmdldDxMb2dnZXI+KCk7CgogIC8vLyBDcmVhdGVzIGFuIFtBdXRoSGVhZGVyc0J1aWxkZXJdLgogIC8vLwogIC8vLyBUaGUgW2J1aWxkZXJdIGZ1bmN0aW9uIGlzIGNhbGxlZCB3aXRoIHRoZSBjdXJyZW50IFtCdWlsZENvbnRleHRdIGFuZAogIC8vLyBhIG1hcCBvZiBIVFRQIGhlYWRlcnMuIElmIGF1dGhlbnRpY2F0aW9uIGZhaWxzIG9yIG5vIHRva2VuIGlzIGF2YWlsYWJsZSwKICAvLy8gYW4gZW1wdHkgbWFwIGlzIHByb3ZpZGVkLgogIC8vLwogIC8vLyBJZiBbdmFsaWRhdGVUb2tlbl0gaXMgdHJ1ZSwgdGhlIHRva2VuIHdpbGwgYmUgdmFsaWRhdGVkIGFuZAogIC8vLyByZWZyZXNoZWQgaWYgbmVlZGVkLiBJZiBmYWxzZSAoZGVmYXVsdCksIHRoZSBjdXJyZW50IGNhY2hlZCB0b2tlbiB3aWxsIGJlIHVzZWQKICAvLy8gd2l0aG91dCB2YWxpZGF0aW9uLgogIGNvbnN0IEF1dGhIZWFkZXJzQnVpbGRlcih7CiAgICBzdXBlci5rZXksCiAgICByZXF1aXJlZCB0aGlzLmJ1aWxkZXIsCiAgICB0aGlzLnZhbGlkYXRlVG9rZW4gPSBmYWxzZSwKICB9KTsKCiAgLy8vIFRoZSBidWlsZGVyIGZ1bmN0aW9uIHRoYXQgcmVjZWl2ZXMgdGhlIGF1dGhlbnRpY2F0aW9uIGhlYWRlcnMuCiAgLy8vCiAgLy8vIFRoZSBoZWFkZXJzIG1hcCB3aWxsIGNvbnRhaW46CiAgLy8vIC0gJ0F1dGhvcml6YXRpb24nOiAnQmVhcmVyIHt0b2tlbn0nIChpZiBhdXRoZW50aWNhdGVkKQogIC8vLyAtICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicKICAvLy8KICAvLy8gSWYgbm8gYXV0aGVudGljYXRpb24gdG9rZW4gaXMgYXZhaWxhYmxlLCBhbiBlbXB0eSBtYXAge30gaXMgcHJvdmlkZWQuCiAgZmluYWwgV2lkZ2V0IEZ1bmN0aW9uKEJ1aWxkQ29udGV4dCBjb250ZXh0LCBNYXA8U3RyaW5nLCBTdHJpbmc+IGhlYWRlcnMpCiAgYnVpbGRlcjsKCiAgLy8vIFdoZXRoZXIgdG8gdmFsaWRhdGUgYW5kIHBvdGVudGlhbGx5IHJlZnJlc2ggdGhlIHRva2VuLgogIC8vLwogIC8vLyBJZiB0cnVlLCB1c2VzIFtBdXRoVG9rZW5Qcm92aWRlci5nZXRWYWxpZFRva2VuXSB3aGljaCB2YWxpZGF0ZXMKICAvLy8gdGhlIHRva2VuIGFuZCByZWZyZXNoZXMgaXQgaWYgbmVlZGVkLgogIC8vLyBJZiBmYWxzZSAoZGVmYXVsdCksIHVzZXMgW0F1dGhUb2tlblByb3ZpZGVyLmdldEN1cnJlbnRUb2tlbl0gd2hpY2ggcmV0dXJucyB0aGUKICAvLy8gY2FjaGVkIHRva2VuIHdpdGhvdXQgdmFsaWRhdGlvbi4KICBmaW5hbCBib29sIHZhbGlkYXRlVG9rZW47CgogIE1hcDxTdHJpbmcsIFN0cmluZz4gX2dldFN5bmNBdXRoSGVhZGVycygpIHsKICAgIHRyeSB7CiAgICAgIGZpbmFsIGF1dGhUb2tlblByb3ZpZGVyID0gZGkuZ2V0PEF1dGhUb2tlblByb3ZpZGVyPigpOwogICAgICBmaW5hbCB0b2tlbiA9IGF1dGhUb2tlblByb3ZpZGVyLmdldEN1cnJlbnRUb2tlbigpOwoKICAgICAgaWYgKHRva2VuICE9IG51bGwpIHsKICAgICAgICByZXR1cm4geydBdXRob3JpemF0aW9uJzogJ0JlYXJlciAkdG9rZW4nfTsKICAgICAgfQogICAgfSBjYXRjaCAoZSkgewogICAgICBfbG9nZ2VyLmUoJ0Vycm9yIGdldHRpbmcgYXV0aCBoZWFkZXJzOiAkZScpOwogICAgfQogICAgcmV0dXJuIHt9OwogIH0KCiAgRnV0dXJlPE1hcDxTdHJpbmcsIFN0cmluZz4+IF9nZXRBc3luY0F1dGhIZWFkZXJzKCkgYXN5bmMgewogICAgdHJ5IHsKICAgICAgZmluYWwgYXV0aFRva2VuUHJvdmlkZXIgPSBkaS5nZXQ8QXV0aFRva2VuUHJvdmlkZXI+KCk7CiAgICAgIGZpbmFsIHRva2VuID0gYXdhaXQgYXV0aFRva2VuUHJvdmlkZXIuZ2V0VmFsaWRUb2tlbigpOwoKICAgICAgaWYgKHRva2VuICE9IG51bGwpIHsKICAgICAgICByZXR1cm4geydBdXRob3JpemF0aW9uJzogJ0JlYXJlciAkdG9rZW4nfTsKICAgICAgfQogICAgfSBjYXRjaCAoZSkgewogICAgICBfbG9nZ2VyLmUoJ0Vycm9yIGdldHRpbmcgYXV0aCBoZWFkZXJzOiAkZScpOwogICAgfQogICAgcmV0dXJuIHt9OwogIH0KCiAgQG92ZXJyaWRlCiAgV2lkZ2V0IGJ1aWxkKEJ1aWxkQ29udGV4dCBjb250ZXh0KSB7CiAgICAvLyBJZiB2YWxpZGF0aW9uIGlzIG5vdCBuZWVkZWQsIHJldHVybiBzeW5jaHJvbm91c2x5IHdpdGhvdXQgRnV0dXJlQnVpbGRlcgogICAgaWYgKCF2YWxpZGF0ZVRva2VuKSB7CiAgICAgIGZpbmFsIGhlYWRlcnMgPSBfZ2V0U3luY0F1dGhIZWFkZXJzKCk7CiAgICAgIHJldHVybiBidWlsZGVyKGNvbnRleHQsIGhlYWRlcnMpOwogICAgfQoKICAgIC8vIElmIHZhbGlkYXRpb24gaXMgbmVlZGVkLCB1c2UgRnV0dXJlQnVpbGRlcgogICAgcmV0dXJuIEZ1dHVyZUJ1aWxkZXI8TWFwPFN0cmluZywgU3RyaW5nPj4oCiAgICAgIGZ1dHVyZTogX2dldEFzeW5jQXV0aEhlYWRlcnMoKSwKICAgICAgYnVpbGRlcjogKGNvbnRleHQsIHNuYXBzaG90KSB7CiAgICAgICAgZmluYWwgaGVhZGVycyA9IHNuYXBzaG90LmRhdGEgPz8ge307CiAgICAgICAgcmV0dXJuIGJ1aWxkZXIoY29udGV4dCwgaGVhZGVycyk7CiAgICAgIH0sCiAgICApOwogIH0KfXt7L25ldHdvcmt9fQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/components/date_filter_chips.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CmltcG9ydCAncGFja2FnZTpsb2NhbGl6YXRpb24vbG9jYWxpemF0aW9uLmRhcnQnOwoKZW51bSBEYXRlRmlsdGVyVHlwZSB7CiAgd2VlaywKICBtb250aDsKCiAgU3RyaW5nIGdldCBkaXNwbGF5TmFtZSB7CiAgICBzd2l0Y2ggKHRoaXMpIHsKICAgICAgY2FzZSBEYXRlRmlsdGVyVHlwZS53ZWVrOgogICAgICAgIHJldHVybiBzdHJpbmdzLmNvbW1vbi53ZWVrOwogICAgICBjYXNlIERhdGVGaWx0ZXJUeXBlLm1vbnRoOgogICAgICAgIHJldHVybiBzdHJpbmdzLmNvbW1vbi5tb250aDsKICAgIH0KICB9Cn0KCmNsYXNzIERhdGVGaWx0ZXJDaGlwcyBleHRlbmRzIFN0YXRlbGVzc1dpZGdldCB7CiAgZmluYWwgRGF0ZUZpbHRlclR5cGUgc2VsZWN0ZWRGaWx0ZXI7CiAgZmluYWwgVmFsdWVDaGFuZ2VkPERhdGVGaWx0ZXJUeXBlPiBvbkZpbHRlckNoYW5nZWQ7CgogIGNvbnN0IERhdGVGaWx0ZXJDaGlwcyh7CiAgICBzdXBlci5rZXksCiAgICByZXF1aXJlZCB0aGlzLnNlbGVjdGVkRmlsdGVyLAogICAgcmVxdWlyZWQgdGhpcy5vbkZpbHRlckNoYW5nZWQsCiAgfSk7CgogIEBvdmVycmlkZQogIFdpZGdldCBidWlsZChCdWlsZENvbnRleHQgY29udGV4dCkgewogICAgZmluYWwgY29sb3JTY2hlbWUgPSBUaGVtZS5vZihjb250ZXh0KS5jb2xvclNjaGVtZTsKCiAgICByZXR1cm4gUm93KAogICAgICBtYWluQXhpc0FsaWdubWVudDogTWFpbkF4aXNBbGlnbm1lbnQuY2VudGVyLAogICAgICBjaGlsZHJlbjogRGF0ZUZpbHRlclR5cGUudmFsdWVzLm1hcCgoZmlsdGVyKSB7CiAgICAgICAgZmluYWwgaXNTZWxlY3RlZCA9IGZpbHRlciA9PSBzZWxlY3RlZEZpbHRlcjsKCiAgICAgICAgcmV0dXJuIFBhZGRpbmcoCiAgICAgICAgICBwYWRkaW5nOiBjb25zdCBFZGdlSW5zZXRzLnN5bW1ldHJpYyhob3Jpem9udGFsOiA0KSwKICAgICAgICAgIGNoaWxkOiBGaWx0ZXJDaGlwKAogICAgICAgICAgICBsYWJlbDogVGV4dChmaWx0ZXIuZGlzcGxheU5hbWUpLAogICAgICAgICAgICBzZWxlY3RlZDogaXNTZWxlY3RlZCwKICAgICAgICAgICAgb25TZWxlY3RlZDogKHNlbGVjdGVkKSB7CiAgICAgICAgICAgICAgaWYgKHNlbGVjdGVkKSB7CiAgICAgICAgICAgICAgICBvbkZpbHRlckNoYW5nZWQoZmlsdGVyKTsKICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgIGJhY2tncm91bmRDb2xvcjogY29sb3JTY2hlbWUuc3VyZmFjZSwKICAgICAgICAgICAgc2VsZWN0ZWRDb2xvcjogY29sb3JTY2hlbWUucHJpbWFyeSwKICAgICAgICAgICAgY2hlY2ttYXJrQ29sb3I6IGNvbG9yU2NoZW1lLm9uUHJpbWFyeSwKICAgICAgICAgICAgbGFiZWxTdHlsZTogVGV4dFN0eWxlKAogICAgICAgICAgICAgIGNvbG9yOiBpc1NlbGVjdGVkID8gY29sb3JTY2hlbWUub25QcmltYXJ5IDogY29sb3JTY2hlbWUub25TdXJmYWNlLAogICAgICAgICAgICAgIGZvbnRXZWlnaHQ6IGlzU2VsZWN0ZWQgPyBGb250V2VpZ2h0Lnc2MDAgOiBGb250V2VpZ2h0Lm5vcm1hbCwKICAgICAgICAgICAgKSwKICAgICAgICAgICAgc2hhcGU6IFJvdW5kZWRSZWN0YW5nbGVCb3JkZXIoCiAgICAgICAgICAgICAgYm9yZGVyUmFkaXVzOiBCb3JkZXJSYWRpdXMuY2lyY3VsYXIoOCksCiAgICAgICAgICAgICAgc2lkZTogQm9yZGVyU2lkZSgKICAgICAgICAgICAgICAgIGNvbG9yOiBpc1NlbGVjdGVkCiAgICAgICAgICAgICAgICAgICAgPyBjb2xvclNjaGVtZS5wcmltYXJ5CiAgICAgICAgICAgICAgICAgICAgOiBjb2xvclNjaGVtZS5vdXRsaW5lLndpdGhWYWx1ZXMoYWxwaGE6IDAuNSksCiAgICAgICAgICAgICAgICB3aWR0aDogaXNTZWxlY3RlZCA/IDEuNSA6IDEsCiAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgKSwKICAgICAgICAgICksCiAgICAgICAgKTsKICAgICAgfSkudG9MaXN0KCksCiAgICApOwogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/components/default_error_view.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvZm91bmRhdGlvbi5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CmltcG9ydCAncGFja2FnZTpsb2NhbGl6YXRpb24vbG9jYWxpemF0aW9uLmRhcnQnOwoKY2xhc3MgRGVmYXVsdEVycm9yVmlldyBleHRlbmRzIFN0YXRlbGVzc1dpZGdldCB7CiAgY29uc3QgRGVmYXVsdEVycm9yVmlldyh7CiAgICBzdXBlci5rZXksCiAgICB0aGlzLmVycm9yLAogICAgdGhpcy5zdGFja1RyYWNlLAogICAgdGhpcy5vblJldHJ5LAogICAgdGhpcy5kZXNjcmlwdGlvbkJ1aWxkZXIsCiAgICB0aGlzLnBhZGRpbmcsCiAgfSk7CgogIGZpbmFsIE9iamVjdD8gZXJyb3I7CiAgZmluYWwgU3RhY2tUcmFjZT8gc3RhY2tUcmFjZTsKICBmaW5hbCBWb2lkQ2FsbGJhY2s/IG9uUmV0cnk7CiAgZmluYWwgU3RyaW5nPyBGdW5jdGlvbihPYmplY3Q/KT8gZGVzY3JpcHRpb25CdWlsZGVyOwogIGZpbmFsIEVkZ2VJbnNldHNHZW9tZXRyeT8gcGFkZGluZzsKCiAgQG92ZXJyaWRlCiAgV2lkZ2V0IGJ1aWxkKEJ1aWxkQ29udGV4dCBjb250ZXh0KSB7CiAgICBmaW5hbCB0aGVtZSA9IFRoZW1lLm9mKGNvbnRleHQpOwogICAgZmluYWwgY29sb3JTY2hlbWUgPSB0aGVtZS5jb2xvclNjaGVtZTsKICAgIGZpbmFsIHRleHRUaGVtZSA9IHRoZW1lLnRleHRUaGVtZTsKCiAgICBmaW5hbCB0aXRsZSA9IHN0cmluZ3MuZXJyb3JzLmRlZmF1bHRfZXJyb3JfbWVzc2FnZTsKICAgIGZpbmFsIGRlc2NyaXB0aW9uID0gZGVzY3JpcHRpb25CdWlsZGVyPy5jYWxsKGVycm9yKTsKCiAgICByZXR1cm4gU2FmZUFyZWEoCiAgICAgIGNoaWxkOiBQYWRkaW5nKAogICAgICAgIHBhZGRpbmc6IHBhZGRpbmcgPz8gY29uc3QgRWRnZUluc2V0cy5hbGwoNDAuMCksCiAgICAgICAgY2hpbGQ6IENlbnRlcigKICAgICAgICAgIGNoaWxkOiBDb2x1bW4oCiAgICAgICAgICAgIG1haW5BeGlzQWxpZ25tZW50OiBNYWluQXhpc0FsaWdubWVudC5jZW50ZXIsCiAgICAgICAgICAgIGNoaWxkcmVuOiBbCiAgICAgICAgICAgICAgQ29udGFpbmVyKAogICAgICAgICAgICAgICAgd2lkdGg6IDgwLAogICAgICAgICAgICAgICAgaGVpZ2h0OiA4MCwKICAgICAgICAgICAgICAgIGRlY29yYXRpb246IEJveERlY29yYXRpb24oCiAgICAgICAgICAgICAgICAgIGNvbG9yOiBjb2xvclNjaGVtZS5lcnJvckNvbnRhaW5lci53aXRoVmFsdWVzKGFscGhhOiAwLjE1KSwKICAgICAgICAgICAgICAgICAgYm9yZGVyUmFkaXVzOiBCb3JkZXJSYWRpdXMuY2lyY3VsYXIoMTYpLAogICAgICAgICAgICAgICAgICBib3JkZXI6IEJvcmRlci5hbGwoCiAgICAgICAgICAgICAgICAgICAgY29sb3I6IGNvbG9yU2NoZW1lLmVycm9yLndpdGhWYWx1ZXMoYWxwaGE6IDAuMiksCiAgICAgICAgICAgICAgICAgICAgd2lkdGg6IDEsCiAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgY2hpbGQ6IEljb24oCiAgICAgICAgICAgICAgICAgIEljb25zLmVycm9yX291dGxpbmVfcm91bmRlZCwKICAgICAgICAgICAgICAgICAgc2l6ZTogNDAsCiAgICAgICAgICAgICAgICAgIGNvbG9yOiBjb2xvclNjaGVtZS5lcnJvciwKICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICBjb25zdCBTaXplZEJveChoZWlnaHQ6IDI0KSwKICAgICAgICAgICAgICBUZXh0KAogICAgICAgICAgICAgICAgdGl0bGUsCiAgICAgICAgICAgICAgICBzdHlsZTogdGV4dFRoZW1lLnRpdGxlTWVkaXVtPy5jb3B5V2l0aCgKICAgICAgICAgICAgICAgICAgY29sb3I6IGNvbG9yU2NoZW1lLm9uU3VyZmFjZSwKICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICB0ZXh0QWxpZ246IFRleHRBbGlnbi5jZW50ZXIsCiAgICAgICAgICAgICAgICBtYXhMaW5lczogNSwKICAgICAgICAgICAgICAgIG92ZXJmbG93OiBUZXh0T3ZlcmZsb3cuZWxsaXBzaXMsCiAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICBpZiAoZGVzY3JpcHRpb24gIT0gbnVsbCkgLi4uWwogICAgICAgICAgICAgICAgY29uc3QgU2l6ZWRCb3goaGVpZ2h0OiA4KSwKICAgICAgICAgICAgICAgIFRleHQoCiAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uLAogICAgICAgICAgICAgICAgICBzdHlsZTogdGV4dFRoZW1lLmxhYmVsTWVkaXVtPy5jb3B5V2l0aCgKICAgICAgICAgICAgICAgICAgICBjb2xvcjogY29sb3JTY2hlbWUub25TdXJmYWNlVmFyaWFudCwKICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgdGV4dEFsaWduOiBUZXh0QWxpZ24uY2VudGVyLAogICAgICAgICAgICAgICAgICBtYXhMaW5lczogMjAsCiAgICAgICAgICAgICAgICAgIG92ZXJmbG93OiBUZXh0T3ZlcmZsb3cuZWxsaXBzaXMsCiAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgIF0sCiAgICAgICAgICAgICAgaWYgKGVycm9yICE9IG51bGwgJiYga0RlYnVnTW9kZSkgLi4uWwogICAgICAgICAgICAgICAgY29uc3QgU2l6ZWRCb3goaGVpZ2h0OiAxNiksCiAgICAgICAgICAgICAgICBDb250YWluZXIoCiAgICAgICAgICAgICAgICAgIHdpZHRoOiBkb3VibGUuaW5maW5pdHksCiAgICAgICAgICAgICAgICAgIHBhZGRpbmc6IGNvbnN0IEVkZ2VJbnNldHMuYWxsKDEyKSwKICAgICAgICAgICAgICAgICAgZGVjb3JhdGlvbjogQm94RGVjb3JhdGlvbigKICAgICAgICAgICAgICAgICAgICBjb2xvcjogY29sb3JTY2hlbWUuc3VyZmFjZUNvbnRhaW5lckhpZ2gsCiAgICAgICAgICAgICAgICAgICAgYm9yZGVyUmFkaXVzOiBCb3JkZXJSYWRpdXMuY2lyY3VsYXIoMTIpLAogICAgICAgICAgICAgICAgICAgIGJvcmRlcjogQm9yZGVyLmFsbCgKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yOiBjb2xvclNjaGVtZS5vdXRsaW5lLndpdGhWYWx1ZXMoYWxwaGE6IDAuMyksCiAgICAgICAgICAgICAgICAgICAgICB3aWR0aDogMSwKICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICBjaGlsZDogU2VsZWN0YWJsZVRleHQoCiAgICAgICAgICAgICAgICAgICAgJ3R5cGU6JHtlcnJvci5ydW50aW1lVHlwZX06XG4kZXJyb3JcbiR7c3RhY2tUcmFjZT8udG9TdHJpbmcoKSA/PyAnJ30nLAogICAgICAgICAgICAgICAgICAgIHN0eWxlOiB0ZXh0VGhlbWUuYm9keVNtYWxsPy5jb3B5V2l0aCgKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yOiBjb2xvclNjaGVtZS5vblN1cmZhY2UsCiAgICAgICAgICAgICAgICAgICAgICBmb250RmFtaWx5OiAnbW9ub3NwYWNlJywKICAgICAgICAgICAgICAgICAgICAgIGZvbnRTaXplOiAxMiwKICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgIHRleHRBbGlnbjogVGV4dEFsaWduLmxlZnQsCiAgICAgICAgICAgICAgICAgICAgbWF4TGluZXM6IDEwLAogICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICBdLAogICAgICAgICAgICAgIGNvbnN0IFNpemVkQm94KGhlaWdodDogMzIpLAogICAgICAgICAgICAgIFdyYXAoCiAgICAgICAgICAgICAgICBzcGFjaW5nOiAxMiwKICAgICAgICAgICAgICAgIHJ1blNwYWNpbmc6IDgsCiAgICAgICAgICAgICAgICBhbGlnbm1lbnQ6IFdyYXBBbGlnbm1lbnQuY2VudGVyLAogICAgICAgICAgICAgICAgY2hpbGRyZW46IFsKICAgICAgICAgICAgICAgICAgaWYgKG9uUmV0cnkgIT0gbnVsbCkKICAgICAgICAgICAgICAgICAgICBPdXRsaW5lZEJ1dHRvbigKICAgICAgICAgICAgICAgICAgICAgIG9uUHJlc3NlZDogb25SZXRyeSwKICAgICAgICAgICAgICAgICAgICAgIHN0eWxlOiBGaWxsZWRCdXR0b24uc3R5bGVGcm9tKAogICAgICAgICAgICAgICAgICAgICAgICBwYWRkaW5nOiBjb25zdCBFZGdlSW5zZXRzLnN5bW1ldHJpYygKICAgICAgICAgICAgICAgICAgICAgICAgICBob3Jpem9udGFsOiAyNCwKICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJ0aWNhbDogMTIsCiAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgIG1pbmltdW1TaXplOiBjb25zdCBTaXplKDg4LCA0NCksCiAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgY2hpbGQ6IFRleHQoc3RyaW5ncy5nZW5lcmljLnRyeV9hZ2FpbiksCiAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgIF0sCiAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgXSwKICAgICAgICAgICksCiAgICAgICAgKSwKICAgICAgKSwKICAgICk7CiAgfQp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/components/header.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmRlc2lnbl9zeXN0ZW0vY29tcG9uZW50cy9uZXR3b3JrX3VybF9pbWFnZS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRlc2lnbl9zeXN0ZW0vdXRpbHMvaW5kZXguZGFydCc7CmltcG9ydCAncGFja2FnZTpmbHV0dGVyL21hdGVyaWFsLmRhcnQnOwoKZW51bSBIZWFkZXJUeXBlIHsKICBpbWFnZSwKICBhc3NldDsKCiAgZG91YmxlIGdldCByYXRpbyA9PiBzd2l0Y2ggKHRoaXMpIHsKICAgIEhlYWRlclR5cGUuaW1hZ2UgPT4gNCAvIDEsCiAgICBIZWFkZXJUeXBlLmFzc2V0ID0+IDEgLyAxLAogIH07Cn0KCmNsYXNzIEhlYWRlciBleHRlbmRzIFN0YXRlbGVzc1dpZGdldCB7CiAgY29uc3QgSGVhZGVyKHsKICAgIHN1cGVyLmtleSwKICAgIHRoaXMudHlwZSA9IEhlYWRlclR5cGUuaW1hZ2UsCiAgICB0aGlzLmFzc2V0UGF0aCwKICAgIHRoaXMuaW1hZ2VVcmwsCiAgfSk7CgogIGZpbmFsIEhlYWRlclR5cGUgdHlwZTsKICBmaW5hbCBTdHJpbmc/IGFzc2V0UGF0aDsKICBmaW5hbCBTdHJpbmc/IGltYWdlVXJsOwoKICBAb3ZlcnJpZGUKICBXaWRnZXQgYnVpbGQoQnVpbGRDb250ZXh0IGNvbnRleHQpIHsKICAgIHJldHVybiBBc3BlY3RSYXRpbyhhc3BlY3RSYXRpbzogdHlwZS5yYXRpbywgY2hpbGQ6IF9idWlsZEltYWdlKGNvbnRleHQpKTsKICB9CgogIFdpZGdldCBfYnVpbGRJbWFnZShCdWlsZENvbnRleHQgY29udGV4dCkgewogICAgc3dpdGNoICh0eXBlKSB7CiAgICAgIGNhc2UgSGVhZGVyVHlwZS5hc3NldDoKICAgICAgICByZXR1cm4gSGVybygKICAgICAgICAgIHRhZzogYXNzZXRQYXRoISwKICAgICAgICAgIGNoaWxkOiBJbWFnZS5hc3NldCgKICAgICAgICAgICAgYXNzZXRQYXRoISwKICAgICAgICAgICAgZml0OiBCb3hGaXQuY29udGFpbiwKICAgICAgICAgICAgYWxpZ25tZW50OiBBbGlnbm1lbnQuY2VudGVyLAogICAgICAgICAgKSwKICAgICAgICApOwogICAgICBjYXNlIEhlYWRlclR5cGUuaW1hZ2U6CiAgICAgICAgcmV0dXJuIE5ldHdvcmtVcmxJbWFnZSh1cmw6IGltYWdlVXJsLmFzRW1wdHlJZk51bGwpOwogICAgfQogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/components/index.dart", + "data": + "Ly9HRU5FUkFURUQgQkFSUkVMIEZJTEUKZXhwb3J0ICdhc3NldF9pbWFnZS5kYXJ0JzsKZXhwb3J0ICdhdXRoX2hlYWRlcnNfYnVpbGRlci5kYXJ0JzsKZXhwb3J0ICdkYXRlX2ZpbHRlcl9jaGlwcy5kYXJ0JzsKZXhwb3J0ICdkZWZhdWx0X2Vycm9yX3ZpZXcuZGFydCc7CmV4cG9ydCAnaGVhZGVyLmRhcnQnOwpleHBvcnQgJ2xvYWRlcnMuZGFydCc7CmV4cG9ydCAnbmV0d29ya191cmxfaW1hZ2UuZGFydCc7Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/components/loaders.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CgpjbGFzcyBEZWZhdWx0TG9hZGVyIGV4dGVuZHMgU3RhdGVsZXNzV2lkZ2V0IHsKICBjb25zdCBEZWZhdWx0TG9hZGVyKHtzdXBlci5rZXksIHRoaXMuc2l6ZSA9IDIwfSk7CgogIGZpbmFsIGRvdWJsZSBzaXplOwoKICBAb3ZlcnJpZGUKICBXaWRnZXQgYnVpbGQoQnVpbGRDb250ZXh0IGNvbnRleHQpIHsKICAgIHJldHVybiBDZW50ZXIoCiAgICAgIGNoaWxkOiBTaXplZEJveCgKICAgICAgICB3aWR0aDogc2l6ZSwKICAgICAgICBoZWlnaHQ6IHNpemUsCiAgICAgICAgY2hpbGQ6IENpcmN1bGFyUHJvZ3Jlc3NJbmRpY2F0b3IuYWRhcHRpdmUoc3Ryb2tlV2lkdGg6IDQpLAogICAgICApLAogICAgKTsKICB9Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/components/network_url_image.dart", + "data": + "aW1wb3J0ICdkYXJ0Om1hdGgnOwoKaW1wb3J0ICdwYWNrYWdlOmNhY2hlZF9uZXR3b3JrX2ltYWdlL2NhY2hlZF9uZXR3b3JrX2ltYWdlLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlci9tYXRlcmlhbC5kYXJ0JzsKCmNsYXNzIE5ldHdvcmtVcmxJbWFnZSBleHRlbmRzIFN0YXRlbGVzc1dpZGdldCB7CiAgZmluYWwgU3RyaW5nIHVybDsKICBmaW5hbCBkb3VibGU/IHdpZHRoOwogIGZpbmFsIGRvdWJsZT8gaGVpZ2h0OwogIGZpbmFsIEJveEZpdD8gYm94Rml0OwoKICBjb25zdCBOZXR3b3JrVXJsSW1hZ2UoewogICAgc3VwZXIua2V5LAogICAgcmVxdWlyZWQgdGhpcy51cmwsCiAgICB0aGlzLndpZHRoLAogICAgdGhpcy5oZWlnaHQsCiAgICB0aGlzLmJveEZpdCA9IEJveEZpdC5jb3ZlciwKICB9KTsKCiAgQG92ZXJyaWRlCiAgV2lkZ2V0IGJ1aWxkKGZpbmFsIEJ1aWxkQ29udGV4dCBjb250ZXh0KSA9PiBDYWNoZWROZXR3b3JrSW1hZ2UoCiAgICBpbWFnZVVybDogdXJsLAogICAgcGxhY2Vob2xkZXI6IChmaW5hbCBjb250ZXh0LCBmaW5hbCBfXykgewogICAgICBmaW5hbCB0aGVtZSA9IFRoZW1lLm9mKGNvbnRleHQpOwoKICAgICAgcmV0dXJuIENvbnRhaW5lcigKICAgICAgICBjb2xvcjogdGhlbWUuY29sb3JTY2hlbWUuc3VyZmFjZSwKICAgICAgICB3aWR0aDogd2lkdGgsCiAgICAgICAgaGVpZ2h0OiBoZWlnaHQsCiAgICAgICAgY2hpbGQ6IEljb24oCiAgICAgICAgICBJY29ucy5pbWFnZSwKICAgICAgICAgIHNpemU6IG1pbih3aWR0aCA/PyAzMiwgaGVpZ2h0ID8/IDMyKSwKICAgICAgICAgIGNvbG9yOiB0aGVtZS5jYXJkQ29sb3IsCiAgICAgICAgKSwKICAgICAgKTsKICAgIH0sCiAgICB3aWR0aDogd2lkdGgsCiAgICBoZWlnaHQ6IGhlaWdodCwKICAgIGZpdDogYm94Rml0LAogICAgZmFkZU91dER1cmF0aW9uOiBjb25zdCBEdXJhdGlvbigpLAogICAgZmFkZUluRHVyYXRpb246IGNvbnN0IER1cmF0aW9uKCksCiAgICBlcnJvcldpZGdldDogKGZpbmFsIGNvbnRleHQsIGZpbmFsIHVybCwgZmluYWwgZXJyb3IpID0+CiAgICAgICAgSWNvbihJY29ucy5pbWFnZSwgc2l6ZTogMzIpLAogICk7Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/constants/navigation_icons.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CgovLy8gSWNvbnMgdXNlZCBieSB0aGUgYXBwJ3MgcHJpbWFyeSBuYXZpZ2F0aW9uLgovLy8KLy8vIFN3YXAgdGhlc2UgZm9yIHlvdXIgcHJvZHVjdCdzIGljb24gc2V0IC0gZXZlcnkgbmF2aWdhdGlvbiBzdXJmYWNlIHJlYWRzCi8vLyBmcm9tIGhlcmUgc28gYSBzaW5nbGUgZWRpdCByZS1za2lucyB0aGUgd2hvbGUgc2hlbGwuCmNsYXNzIE5hdmlnYXRpb25JY29ucyB7CiAgY29uc3QgTmF2aWdhdGlvbkljb25zLl8oKTsKCiAgc3RhdGljIGNvbnN0IEljb25EYXRhIGhvbWUgPSBJY29ucy5ob21lX291dGxpbmVkOwogIHN0YXRpYyBjb25zdCBJY29uRGF0YSBob21lU2VsZWN0ZWQgPSBJY29ucy5ob21lOwoKICBzdGF0aWMgY29uc3QgSWNvbkRhdGEgZXhwbG9yZSA9IEljb25zLmV4cGxvcmVfb3V0bGluZWQ7CiAgc3RhdGljIGNvbnN0IEljb25EYXRhIGV4cGxvcmVTZWxlY3RlZCA9IEljb25zLmV4cGxvcmU7CgogIHN0YXRpYyBjb25zdCBJY29uRGF0YSBkYXNoYm9hcmQgPSBJY29ucy5kYXNoYm9hcmRfb3V0bGluZWQ7CiAgc3RhdGljIGNvbnN0IEljb25EYXRhIGRhc2hib2FyZFNlbGVjdGVkID0gSWNvbnMuZGFzaGJvYXJkOwoKICBzdGF0aWMgY29uc3QgSWNvbkRhdGEgaW5zaWdodHMgPSBJY29ucy50cmVuZGluZ191cF9vdXRsaW5lZDsKICBzdGF0aWMgY29uc3QgSWNvbkRhdGEgaW5zaWdodHNTZWxlY3RlZCA9IEljb25zLnRyZW5kaW5nX3VwOwoKICBzdGF0aWMgY29uc3QgSWNvbkRhdGEgcHJvZmlsZSA9IEljb25zLnBlcnNvbl9vdXRsaW5lOwogIHN0YXRpYyBjb25zdCBJY29uRGF0YSBwcm9maWxlU2VsZWN0ZWQgPSBJY29ucy5wZXJzb247CgogIHN0YXRpYyBjb25zdCBJY29uRGF0YSBzZXR0aW5ncyA9IEljb25zLnNldHRpbmdzX291dGxpbmVkOwp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/design_system.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmRlc2lnbl9zeXN0ZW0vZ2VuZXJhdGVkL3RoZW1lLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZGVzaWduX3N5c3RlbS9nZW5lcmF0ZWQvdXRpbC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CgpleHBvcnQgJ2NvbXBvbmVudHMvaW5kZXguZGFydCc7CmV4cG9ydCAnY29uc3RhbnRzL25hdmlnYXRpb25faWNvbnMuZGFydCc7CmV4cG9ydCAnZGlhbG9ncy9pbmRleC5kYXJ0JzsKZXhwb3J0ICdnZW5lcmF0ZWQvdGhlbWUuZGFydCc7CmV4cG9ydCAnZ2VuZXJhdGVkL3V0aWwuZGFydCc7CmV4cG9ydCAnbG9hZGVyL2xvYWRlci5kYXJ0JzsKZXhwb3J0ICdzY3JlZW5zL2luZGV4LmRhcnQnOwpleHBvcnQgJ3dyYXBwZXIvd3JhcHBlcnMuZGFydCc7CgpjbGFzcyBEZXNpZ25TeXN0ZW0gewogIGZpbmFsIE1hdGVyaWFsVGhlbWUgdGhlbWU7CgogIERlc2lnblN5c3RlbSgKICAgIEJ1aWxkQ29udGV4dCBjb250ZXh0LCB7CiAgICBTdHJpbmcgYm9keUZvbnQgPSAiT3BlbiBTYW5zIiwKICAgIFN0cmluZyBkaXNwbGF5Rm9udCA9ICJPcGVuIFNhbnMiLAogIH0pIDogdGhlbWUgPSBidWlsZFRoZW1lKAogICAgICAgICBjb250ZXh0LAogICAgICAgICBib2R5Rm9udDogYm9keUZvbnQsCiAgICAgICAgIGRpc3BsYXlGb250OiBkaXNwbGF5Rm9udCwKICAgICAgICk7CgogIHN0YXRpYyBNYXRlcmlhbFRoZW1lIGJ1aWxkVGhlbWUoCiAgICBCdWlsZENvbnRleHQgY29udGV4dCwgewogICAgU3RyaW5nIGJvZHlGb250ID0gIk9wZW4gU2FucyIsCiAgICBTdHJpbmcgZGlzcGxheUZvbnQgPSAiT3BlbiBTYW5zIiwKICB9KSB7CiAgICBmaW5hbCB0ZXh0VGhlbWUgPSBjcmVhdGVUZXh0VGhlbWUoY29udGV4dCwgYm9keUZvbnQsIGRpc3BsYXlGb250KTsKICAgIHJldHVybiBNYXRlcmlhbFRoZW1lKHRleHRUaGVtZSk7CiAgfQoKICBUaGVtZURhdGEgX2Jhc2VUaGVtZSh7cmVxdWlyZWQgYm9vbCBpc0Rhcmt9KSB7CiAgICBmaW5hbCBiYXNlID0gaXNEYXJrID8gdGhlbWUuZGFyaygpIDogdGhlbWUubGlnaHQoKTsKICAgIHJldHVybiBiYXNlLmNvcHlXaXRoKAogICAgICBpbnB1dERlY29yYXRpb25UaGVtZTogYmFzZS5pbnB1dERlY29yYXRpb25UaGVtZS5jb3B5V2l0aCgKICAgICAgICBib3JkZXI6IFVuZGVybGluZUlucHV0Qm9yZGVyKAogICAgICAgICAgYm9yZGVyU2lkZTogQm9yZGVyU2lkZShjb2xvcjogQ29sb3JzLmdyZXkuc2hhZGUzMDApLAogICAgICAgICksCiAgICAgICAgaGludFN0eWxlOiBiYXNlLnRleHRUaGVtZS5sYWJlbFNtYWxsPy5jb3B5V2l0aCgKICAgICAgICAgIGNvbG9yOiBiYXNlLmNvbG9yU2NoZW1lLm9uU3VyZmFjZS53aXRoVmFsdWVzKGFscGhhOiAwLjYpLAogICAgICAgICksCiAgICAgICAgbGFiZWxTdHlsZTogYmFzZS50ZXh0VGhlbWUubGFiZWxNZWRpdW0/LmNvcHlXaXRoKAogICAgICAgICAgY29sb3I6IGJhc2UuY29sb3JTY2hlbWUub25TdXJmYWNlLndpdGhWYWx1ZXMoYWxwaGE6IDAuNiksCiAgICAgICAgKSwKICAgICAgKSwKICAgICAgYXBwQmFyVGhlbWU6IEFwcEJhclRoZW1lKAogICAgICAgIGJhY2tncm91bmRDb2xvcjogYmFzZS5jb2xvclNjaGVtZS5zdXJmYWNlLAogICAgICAgIHN1cmZhY2VUaW50Q29sb3I6IGJhc2UuY29sb3JTY2hlbWUuc3VyZmFjZVRpbnQsCiAgICAgICAgZm9yZWdyb3VuZENvbG9yOiBiYXNlLmNvbG9yU2NoZW1lLm9uU3VyZmFjZSwKICAgICAgICBlbGV2YXRpb246IDAsCiAgICAgICAgY2VudGVyVGl0bGU6IHRydWUsCiAgICAgICAgdGl0bGVUZXh0U3R5bGU6IGJhc2UudGV4dFRoZW1lLnRpdGxlTGFyZ2U/LmNvcHlXaXRoKAogICAgICAgICAgY29sb3I6IGJhc2UuY29sb3JTY2hlbWUub25TdXJmYWNlLAogICAgICAgICAgZm9udFdlaWdodDogRm9udFdlaWdodC53NjAwLAogICAgICAgICksCiAgICAgICAgaWNvblRoZW1lOiBJY29uVGhlbWVEYXRhKGNvbG9yOiBiYXNlLmNvbG9yU2NoZW1lLm9uU3VyZmFjZSksCiAgICAgICAgYWN0aW9uc0ljb25UaGVtZTogSWNvblRoZW1lRGF0YShjb2xvcjogYmFzZS5jb2xvclNjaGVtZS5vblN1cmZhY2UpLAogICAgICApLAogICAgICBuYXZpZ2F0aW9uQmFyVGhlbWU6IE5hdmlnYXRpb25CYXJUaGVtZURhdGEoCiAgICAgICAgYmFja2dyb3VuZENvbG9yOiBiYXNlLmNvbG9yU2NoZW1lLnN1cmZhY2UsCiAgICAgICAgc3VyZmFjZVRpbnRDb2xvcjogQ29sb3JzLnRyYW5zcGFyZW50LAogICAgICAgIGVsZXZhdGlvbjogMCwKICAgICAgICBoZWlnaHQ6IDcwLAogICAgICAgIGxhYmVsQmVoYXZpb3I6IE5hdmlnYXRpb25EZXN0aW5hdGlvbkxhYmVsQmVoYXZpb3IuYWx3YXlzU2hvdywKICAgICAgICBsYWJlbFRleHRTdHlsZTogV2lkZ2V0U3RhdGVQcm9wZXJ0eS5yZXNvbHZlV2l0aDxUZXh0U3R5bGU/Pigoc3RhdGVzKSB7CiAgICAgICAgICBpZiAoc3RhdGVzLmNvbnRhaW5zKFdpZGdldFN0YXRlLnNlbGVjdGVkKSkgewogICAgICAgICAgICByZXR1cm4gYmFzZS50ZXh0VGhlbWUubGFiZWxTbWFsbD8uY29weVdpdGgoCiAgICAgICAgICAgICAgY29sb3I6IGJhc2UuY29sb3JTY2hlbWUub25TdXJmYWNlLAogICAgICAgICAgICAgIGZvbnRXZWlnaHQ6IEZvbnRXZWlnaHQudzYwMCwKICAgICAgICAgICAgICBmb250U2l6ZTogMTIsCiAgICAgICAgICAgICk7CiAgICAgICAgICB9CiAgICAgICAgICByZXR1cm4gYmFzZS50ZXh0VGhlbWUubGFiZWxTbWFsbD8uY29weVdpdGgoCiAgICAgICAgICAgIGNvbG9yOiBiYXNlLmNvbG9yU2NoZW1lLm9uU3VyZmFjZVZhcmlhbnQsCiAgICAgICAgICAgIGZvbnRXZWlnaHQ6IEZvbnRXZWlnaHQudzUwMCwKICAgICAgICAgICAgZm9udFNpemU6IDEyLAogICAgICAgICAgKTsKICAgICAgICB9KSwKICAgICAgICBpY29uVGhlbWU6IFdpZGdldFN0YXRlUHJvcGVydHkucmVzb2x2ZVdpdGg8SWNvblRoZW1lRGF0YT8+KChzdGF0ZXMpIHsKICAgICAgICAgIGlmIChzdGF0ZXMuY29udGFpbnMoV2lkZ2V0U3RhdGUuc2VsZWN0ZWQpKSB7CiAgICAgICAgICAgIHJldHVybiBJY29uVGhlbWVEYXRhKGNvbG9yOiBiYXNlLmNvbG9yU2NoZW1lLm9uUHJpbWFyeSwgc2l6ZTogMjApOwogICAgICAgICAgfQogICAgICAgICAgcmV0dXJuIEljb25UaGVtZURhdGEoCiAgICAgICAgICAgIGNvbG9yOiBiYXNlLmNvbG9yU2NoZW1lLm9uU3VyZmFjZVZhcmlhbnQsCiAgICAgICAgICAgIHNpemU6IDIwLAogICAgICAgICAgKTsKICAgICAgICB9KSwKICAgICAgICBpbmRpY2F0b3JDb2xvcjogYmFzZS5jb2xvclNjaGVtZS5wcmltYXJ5LAogICAgICAgIGluZGljYXRvclNoYXBlOiBjb25zdCBSb3VuZGVkUmVjdGFuZ2xlQm9yZGVyKAogICAgICAgICAgYm9yZGVyUmFkaXVzOiBCb3JkZXJSYWRpdXMuYWxsKFJhZGl1cy5jaXJjdWxhcig0KSksCiAgICAgICAgKSwKICAgICAgKSwKICAgICAgZmlsbGVkQnV0dG9uVGhlbWU6IEZpbGxlZEJ1dHRvblRoZW1lRGF0YSgKICAgICAgICBzdHlsZTogRmlsbGVkQnV0dG9uLnN0eWxlRnJvbSgKICAgICAgICAgIHNoYXBlOiBSb3VuZGVkUmVjdGFuZ2xlQm9yZGVyKGJvcmRlclJhZGl1czogQm9yZGVyUmFkaXVzLmNpcmN1bGFyKDQpKSwKICAgICAgICAgIHRleHRTdHlsZTogYmFzZS50ZXh0VGhlbWUubGFiZWxMYXJnZT8uY29weVdpdGgoCiAgICAgICAgICAgIGZvbnRXZWlnaHQ6IEZvbnRXZWlnaHQudzYwMCwKICAgICAgICAgICksCiAgICAgICAgKSwKICAgICAgKSwKICAgICAgdGV4dEJ1dHRvblRoZW1lOiBUZXh0QnV0dG9uVGhlbWVEYXRhKAogICAgICAgIHN0eWxlOiBUZXh0QnV0dG9uLnN0eWxlRnJvbSgKICAgICAgICAgIGZvcmVncm91bmRDb2xvcjogYmFzZS5jb2xvclNjaGVtZS5wcmltYXJ5LAogICAgICAgICAgdGV4dFN0eWxlOiBiYXNlLnRleHRUaGVtZS5sYWJlbExhcmdlPy5jb3B5V2l0aCgKICAgICAgICAgICAgZm9udFdlaWdodDogRm9udFdlaWdodC53NjAwLAogICAgICAgICAgKSwKICAgICAgICAgIHNoYXBlOiBSb3VuZGVkUmVjdGFuZ2xlQm9yZGVyKGJvcmRlclJhZGl1czogQm9yZGVyUmFkaXVzLmNpcmN1bGFyKDQpKSwKICAgICAgICApLAogICAgICApLAogICAgICBlbGV2YXRlZEJ1dHRvblRoZW1lOiBFbGV2YXRlZEJ1dHRvblRoZW1lRGF0YSgKICAgICAgICBzdHlsZTogRWxldmF0ZWRCdXR0b24uc3R5bGVGcm9tKAogICAgICAgICAgc2hhcGU6IFJvdW5kZWRSZWN0YW5nbGVCb3JkZXIoYm9yZGVyUmFkaXVzOiBCb3JkZXJSYWRpdXMuY2lyY3VsYXIoNCkpLAogICAgICAgICAgdGV4dFN0eWxlOiBiYXNlLnRleHRUaGVtZS5sYWJlbExhcmdlPy5jb3B5V2l0aCgKICAgICAgICAgICAgZm9udFdlaWdodDogRm9udFdlaWdodC53NjAwLAogICAgICAgICAgKSwKICAgICAgICApLAogICAgICApLAogICAgICBvdXRsaW5lZEJ1dHRvblRoZW1lOiBPdXRsaW5lZEJ1dHRvblRoZW1lRGF0YSgKICAgICAgICBzdHlsZTogT3V0bGluZWRCdXR0b24uc3R5bGVGcm9tKAogICAgICAgICAgZm9yZWdyb3VuZENvbG9yOiBiYXNlLmNvbG9yU2NoZW1lLnByaW1hcnksCiAgICAgICAgICB0ZXh0U3R5bGU6IGJhc2UudGV4dFRoZW1lLmxhYmVsTGFyZ2U/LmNvcHlXaXRoKAogICAgICAgICAgICBmb250V2VpZ2h0OiBGb250V2VpZ2h0Lnc2MDAsCiAgICAgICAgICApLAogICAgICAgICAgc2hhcGU6IFJvdW5kZWRSZWN0YW5nbGVCb3JkZXIoYm9yZGVyUmFkaXVzOiBCb3JkZXJSYWRpdXMuY2lyY3VsYXIoNCkpLAogICAgICAgICAgc2lkZTogQm9yZGVyU2lkZShjb2xvcjogYmFzZS5jb2xvclNjaGVtZS5wcmltYXJ5LCB3aWR0aDogMS41KSwKICAgICAgICApLAogICAgICApLAogICAgKTsKICB9CgogIFRoZW1lRGF0YSBsaWdodCgpID0+IF9iYXNlVGhlbWUoaXNEYXJrOiBmYWxzZSk7CgogIFRoZW1lRGF0YSBkYXJrKCkgPT4gX2Jhc2VUaGVtZShpc0Rhcms6IHRydWUpOwp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/dialogs/file_info_dialog.dart", + "data": + "aW1wb3J0ICdkYXJ0OmlvJzsKCmltcG9ydCAncGFja2FnZTpmbHV0dGVyL21hdGVyaWFsLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlci9zZXJ2aWNlcy5kYXJ0JzsKCmNsYXNzIEZpbGVJbmZvRGlhbG9nIGV4dGVuZHMgU3RhdGVsZXNzV2lkZ2V0IHsKICBmaW5hbCBGaWxlIGZpbGU7CgogIGNvbnN0IEZpbGVJbmZvRGlhbG9nKHtzdXBlci5rZXksIHJlcXVpcmVkIHRoaXMuZmlsZX0pOwoKICBzdGF0aWMgRnV0dXJlPHZvaWQ+IHNob3coQnVpbGRDb250ZXh0IGNvbnRleHQsIEZpbGUgZmlsZSkgewogICAgcmV0dXJuIHNob3dEaWFsb2c8dm9pZD4oCiAgICAgIGNvbnRleHQ6IGNvbnRleHQsCiAgICAgIGJ1aWxkZXI6IChjb250ZXh0KSA9PiBGaWxlSW5mb0RpYWxvZyhmaWxlOiBmaWxlKSwKICAgICk7CiAgfQoKICBTdHJpbmcgX2dldEZpbGVUeXBlKFN0cmluZyBleHRlbnNpb24pIHsKICAgIHN3aXRjaCAoZXh0ZW5zaW9uLnRvTG93ZXJDYXNlKCkpIHsKICAgICAgY2FzZSAnanBnJzoKICAgICAgY2FzZSAnanBlZyc6CiAgICAgICAgcmV0dXJuICdKUEVHIEltYWdlJzsKICAgICAgY2FzZSAncG5nJzoKICAgICAgICByZXR1cm4gJ1BORyBJbWFnZSc7CiAgICAgIGNhc2UgJ2dpZic6CiAgICAgICAgcmV0dXJuICdHSUYgSW1hZ2UnOwogICAgICBjYXNlICd3ZWJwJzoKICAgICAgICByZXR1cm4gJ1dlYlAgSW1hZ2UnOwogICAgICBjYXNlICdibXAnOgogICAgICAgIHJldHVybiAnQk1QIEltYWdlJzsKICAgICAgY2FzZSAndGlmZic6CiAgICAgIGNhc2UgJ3RpZic6CiAgICAgICAgcmV0dXJuICdUSUZGIEltYWdlJzsKICAgICAgY2FzZSAnaGVpYyc6CiAgICAgIGNhc2UgJ2hlaWYnOgogICAgICAgIHJldHVybiAnSEVJRiBJbWFnZSc7CiAgICAgIGRlZmF1bHQ6CiAgICAgICAgcmV0dXJuICdJbWFnZSBGaWxlJzsKICAgIH0KICB9CgogIFN0cmluZyBfZm9ybWF0RmlsZVNpemUoaW50IGJ5dGVzKSB7CiAgICBpZiAoYnl0ZXMgPCAxMDI0KSByZXR1cm4gJyRieXRlcyBCJzsKICAgIGlmIChieXRlcyA8IDEwMjQgKiAxMDI0KSByZXR1cm4gJyR7KGJ5dGVzIC8gMTAyNCkudG9TdHJpbmdBc0ZpeGVkKDEpfSBLQic7CiAgICByZXR1cm4gJyR7KGJ5dGVzIC8gKDEwMjQgKiAxMDI0KSkudG9TdHJpbmdBc0ZpeGVkKDEpfSBNQic7CiAgfQoKICBXaWRnZXQgX2J1aWxkSW5mb1Jvdyh7CiAgICByZXF1aXJlZCBJY29uRGF0YSBpY29uLAogICAgcmVxdWlyZWQgU3RyaW5nIGxhYmVsLAogICAgcmVxdWlyZWQgU3RyaW5nIHZhbHVlLAogICAgcmVxdWlyZWQgQ29sb3JTY2hlbWUgY29sb3JTY2hlbWUsCiAgICByZXF1aXJlZCBUZXh0VGhlbWUgdGV4dFRoZW1lLAogICAgYm9vbCBpc1BhdGggPSBmYWxzZSwKICAgIFZvaWRDYWxsYmFjaz8gb25UYXAsCiAgfSkgewogICAgcmV0dXJuIElua1dlbGwoCiAgICAgIG9uVGFwOiBvblRhcCwKICAgICAgYm9yZGVyUmFkaXVzOiBCb3JkZXJSYWRpdXMuY2lyY3VsYXIoOCksCiAgICAgIGNoaWxkOiBQYWRkaW5nKAogICAgICAgIHBhZGRpbmc6IGNvbnN0IEVkZ2VJbnNldHMuc3ltbWV0cmljKHZlcnRpY2FsOiA4LCBob3Jpem9udGFsOiA0KSwKICAgICAgICBjaGlsZDogUm93KAogICAgICAgICAgY3Jvc3NBeGlzQWxpZ25tZW50OiBDcm9zc0F4aXNBbGlnbm1lbnQuc3RhcnQsCiAgICAgICAgICBjaGlsZHJlbjogWwogICAgICAgICAgICBJY29uKGljb24sIHNpemU6IDE4LCBjb2xvcjogY29sb3JTY2hlbWUub25TdXJmYWNlVmFyaWFudCksCiAgICAgICAgICAgIGNvbnN0IFNpemVkQm94KHdpZHRoOiAxMiksCiAgICAgICAgICAgIEV4cGFuZGVkKAogICAgICAgICAgICAgIGNoaWxkOiBDb2x1bW4oCiAgICAgICAgICAgICAgICBjcm9zc0F4aXNBbGlnbm1lbnQ6IENyb3NzQXhpc0FsaWdubWVudC5zdGFydCwKICAgICAgICAgICAgICAgIGNoaWxkcmVuOiBbCiAgICAgICAgICAgICAgICAgIFRleHQoCiAgICAgICAgICAgICAgICAgICAgbGFiZWwsCiAgICAgICAgICAgICAgICAgICAgc3R5bGU6IHRleHRUaGVtZS5ib2R5U21hbGw/LmNvcHlXaXRoKAogICAgICAgICAgICAgICAgICAgICAgY29sb3I6IGNvbG9yU2NoZW1lLm9uU3VyZmFjZVZhcmlhbnQsCiAgICAgICAgICAgICAgICAgICAgICBmb250V2VpZ2h0OiBGb250V2VpZ2h0Lnc1MDAsCiAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgY29uc3QgU2l6ZWRCb3goaGVpZ2h0OiA0KSwKICAgICAgICAgICAgICAgICAgU2VsZWN0YWJsZVRleHQoCiAgICAgICAgICAgICAgICAgICAgdmFsdWUsCiAgICAgICAgICAgICAgICAgICAgc3R5bGU6IHRleHRUaGVtZS5ib2R5TWVkaXVtPy5jb3B5V2l0aCgKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yOiBjb2xvclNjaGVtZS5vblN1cmZhY2UsCiAgICAgICAgICAgICAgICAgICAgICBmb250RmFtaWx5OiBpc1BhdGggPyAnbW9ub3NwYWNlJyA6IG51bGwsCiAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgIF0sCiAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgKSwKICAgICAgICAgICAgaWYgKG9uVGFwICE9IG51bGwpCiAgICAgICAgICAgICAgSWNvbigKICAgICAgICAgICAgICAgIEljb25zLmNvcHksCiAgICAgICAgICAgICAgICBzaXplOiAxNiwKICAgICAgICAgICAgICAgIGNvbG9yOiBjb2xvclNjaGVtZS5vblN1cmZhY2VWYXJpYW50LndpdGhWYWx1ZXMoYWxwaGE6IDAuNyksCiAgICAgICAgICAgICAgKSwKICAgICAgICAgIF0sCiAgICAgICAgKSwKICAgICAgKSwKICAgICk7CiAgfQoKICB2b2lkIF9jb3B5VG9DbGlwYm9hcmQoQnVpbGRDb250ZXh0IGNvbnRleHQsIFN0cmluZyB2YWx1ZSwgU3RyaW5nIGxhYmVsKSB7CiAgICBDbGlwYm9hcmQuc2V0RGF0YShDbGlwYm9hcmREYXRhKHRleHQ6IHZhbHVlKSk7CiAgICBTY2FmZm9sZE1lc3Nlbmdlci5vZihjb250ZXh0KS5zaG93U25hY2tCYXIoCiAgICAgIFNuYWNrQmFyKAogICAgICAgIGNvbnRlbnQ6IFRleHQoJyRsYWJlbCBjb3BpZWQgdG8gY2xpcGJvYXJkJyksCiAgICAgICAgZHVyYXRpb246IGNvbnN0IER1cmF0aW9uKHNlY29uZHM6IDIpLAogICAgICAgIGJlaGF2aW9yOiBTbmFja0JhckJlaGF2aW9yLmZsb2F0aW5nLAogICAgICApLAogICAgKTsKICB9CgogIEBvdmVycmlkZQogIFdpZGdldCBidWlsZChCdWlsZENvbnRleHQgY29udGV4dCkgewogICAgZmluYWwgdGhlbWUgPSBUaGVtZS5vZihjb250ZXh0KTsKICAgIGZpbmFsIGNvbG9yU2NoZW1lID0gdGhlbWUuY29sb3JTY2hlbWU7CiAgICBmaW5hbCB0ZXh0VGhlbWUgPSB0aGVtZS50ZXh0VGhlbWU7CgogICAgLy8gR2V0IGZpbGUgaW5mbwogICAgZmluYWwgZmlsZVN0YXQgPSBmaWxlLnN0YXRTeW5jKCk7CiAgICBmaW5hbCBmaWxlU2l6ZSA9IGZpbGVTdGF0LnNpemU7CiAgICBmaW5hbCBmaWxlTmFtZSA9IGZpbGUucGF0aC5zcGxpdCgnLycpLmxhc3Q7CiAgICBmaW5hbCBmaWxlRXh0ZW5zaW9uID0gZmlsZU5hbWUuc3BsaXQoJy4nKS5sYXN0LnRvTG93ZXJDYXNlKCk7CiAgICBmaW5hbCBmaWxlVHlwZSA9IF9nZXRGaWxlVHlwZShmaWxlRXh0ZW5zaW9uKTsKCiAgICByZXR1cm4gRGlhbG9nKAogICAgICBzaGFwZTogUm91bmRlZFJlY3RhbmdsZUJvcmRlcihib3JkZXJSYWRpdXM6IEJvcmRlclJhZGl1cy5jaXJjdWxhcigxNikpLAogICAgICBjaGlsZDogQ29udGFpbmVyKAogICAgICAgIGNvbnN0cmFpbnRzOiBjb25zdCBCb3hDb25zdHJhaW50cyhtYXhXaWR0aDogNDAwLCBtYXhIZWlnaHQ6IDYwMCksCiAgICAgICAgY2hpbGQ6IENvbHVtbigKICAgICAgICAgIG1haW5BeGlzU2l6ZTogTWFpbkF4aXNTaXplLm1pbiwKICAgICAgICAgIGNoaWxkcmVuOiBbCiAgICAgICAgICAgIC8vIEhlYWRlcgogICAgICAgICAgICBDb250YWluZXIoCiAgICAgICAgICAgICAgcGFkZGluZzogY29uc3QgRWRnZUluc2V0cy5hbGwoMjApLAogICAgICAgICAgICAgIGRlY29yYXRpb246IEJveERlY29yYXRpb24oCiAgICAgICAgICAgICAgICBjb2xvcjogY29sb3JTY2hlbWUucHJpbWFyeUNvbnRhaW5lciwKICAgICAgICAgICAgICAgIGJvcmRlclJhZGl1czogY29uc3QgQm9yZGVyUmFkaXVzLnZlcnRpY2FsKAogICAgICAgICAgICAgICAgICB0b3A6IFJhZGl1cy5jaXJjdWxhcigxNiksCiAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgY2hpbGQ6IFJvdygKICAgICAgICAgICAgICAgIGNoaWxkcmVuOiBbCiAgICAgICAgICAgICAgICAgIEljb24oCiAgICAgICAgICAgICAgICAgICAgSWNvbnMuaW5mb19vdXRsaW5lLAogICAgICAgICAgICAgICAgICAgIGNvbG9yOiBjb2xvclNjaGVtZS5vblByaW1hcnlDb250YWluZXIsCiAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgIGNvbnN0IFNpemVkQm94KHdpZHRoOiAxMiksCiAgICAgICAgICAgICAgICAgIEV4cGFuZGVkKAogICAgICAgICAgICAgICAgICAgIGNoaWxkOiBUZXh0KAogICAgICAgICAgICAgICAgICAgICAgJ0ZpbGUgSW5mb3JtYXRpb24nLAogICAgICAgICAgICAgICAgICAgICAgc3R5bGU6IHRleHRUaGVtZS50aXRsZUxhcmdlPy5jb3B5V2l0aCgKICAgICAgICAgICAgICAgICAgICAgICAgZm9udFdlaWdodDogRm9udFdlaWdodC53NjAwLAogICAgICAgICAgICAgICAgICAgICAgICBjb2xvcjogY29sb3JTY2hlbWUub25QcmltYXJ5Q29udGFpbmVyLAogICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICBJY29uQnV0dG9uKAogICAgICAgICAgICAgICAgICAgIG9uUHJlc3NlZDogKCkgPT4gTmF2aWdhdG9yLm9mKGNvbnRleHQpLnBvcCgpLAogICAgICAgICAgICAgICAgICAgIGljb246IEljb24oCiAgICAgICAgICAgICAgICAgICAgICBJY29ucy5jbG9zZSwKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yOiBjb2xvclNjaGVtZS5vblByaW1hcnlDb250YWluZXIsCiAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgIF0sCiAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgKSwKCiAgICAgICAgICAgIC8vIENvbnRlbnQKICAgICAgICAgICAgRmxleGlibGUoCiAgICAgICAgICAgICAgY2hpbGQ6IFNpbmdsZUNoaWxkU2Nyb2xsVmlldygKICAgICAgICAgICAgICAgIHBhZGRpbmc6IGNvbnN0IEVkZ2VJbnNldHMuYWxsKDIwKSwKICAgICAgICAgICAgICAgIGNoaWxkOiBDb2x1bW4oCiAgICAgICAgICAgICAgICAgIGNoaWxkcmVuOiBbCiAgICAgICAgICAgICAgICAgICAgX2J1aWxkSW5mb1JvdygKICAgICAgICAgICAgICAgICAgICAgIGljb246IEljb25zLmltYWdlX291dGxpbmVkLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWw6ICdGaWxlIE5hbWUnLAogICAgICAgICAgICAgICAgICAgICAgdmFsdWU6IGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgY29sb3JTY2hlbWU6IGNvbG9yU2NoZW1lLAogICAgICAgICAgICAgICAgICAgICAgdGV4dFRoZW1lOiB0ZXh0VGhlbWUsCiAgICAgICAgICAgICAgICAgICAgICBvblRhcDogKCkgPT4KICAgICAgICAgICAgICAgICAgICAgICAgICBfY29weVRvQ2xpcGJvYXJkKGNvbnRleHQsIGZpbGVOYW1lLCAnRmlsZSBuYW1lJyksCiAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICBfYnVpbGRJbmZvUm93KAogICAgICAgICAgICAgICAgICAgICAgaWNvbjogSWNvbnMuY2F0ZWdvcnlfb3V0bGluZWQsCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbDogJ0ZpbGUgVHlwZScsCiAgICAgICAgICAgICAgICAgICAgICB2YWx1ZTogZmlsZVR5cGUsCiAgICAgICAgICAgICAgICAgICAgICBjb2xvclNjaGVtZTogY29sb3JTY2hlbWUsCiAgICAgICAgICAgICAgICAgICAgICB0ZXh0VGhlbWU6IHRleHRUaGVtZSwKICAgICAgICAgICAgICAgICAgICAgIG9uVGFwOiAoKSA9PgogICAgICAgICAgICAgICAgICAgICAgICAgIF9jb3B5VG9DbGlwYm9hcmQoY29udGV4dCwgZmlsZVR5cGUsICdGaWxlIHR5cGUnKSwKICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgIF9idWlsZEluZm9Sb3coCiAgICAgICAgICAgICAgICAgICAgICBpY29uOiBJY29ucy5leHRlbnNpb25fb3V0bGluZWQsCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbDogJ0ZpbGUgRXh0ZW5zaW9uJywKICAgICAgICAgICAgICAgICAgICAgIHZhbHVlOiAnLiRmaWxlRXh0ZW5zaW9uJywKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yU2NoZW1lOiBjb2xvclNjaGVtZSwKICAgICAgICAgICAgICAgICAgICAgIHRleHRUaGVtZTogdGV4dFRoZW1lLAogICAgICAgICAgICAgICAgICAgICAgb25UYXA6ICgpID0+IF9jb3B5VG9DbGlwYm9hcmQoCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRleHQsCiAgICAgICAgICAgICAgICAgICAgICAgICcuJGZpbGVFeHRlbnNpb24nLAogICAgICAgICAgICAgICAgICAgICAgICAnRmlsZSBleHRlbnNpb24nLAogICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgIF9idWlsZEluZm9Sb3coCiAgICAgICAgICAgICAgICAgICAgICBpY29uOiBJY29ucy5zdG9yYWdlX291dGxpbmVkLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWw6ICdGaWxlIFNpemUnLAogICAgICAgICAgICAgICAgICAgICAgdmFsdWU6IF9mb3JtYXRGaWxlU2l6ZShmaWxlU2l6ZSksCiAgICAgICAgICAgICAgICAgICAgICBjb2xvclNjaGVtZTogY29sb3JTY2hlbWUsCiAgICAgICAgICAgICAgICAgICAgICB0ZXh0VGhlbWU6IHRleHRUaGVtZSwKICAgICAgICAgICAgICAgICAgICAgIG9uVGFwOiAoKSA9PiBfY29weVRvQ2xpcGJvYXJkKAogICAgICAgICAgICAgICAgICAgICAgICBjb250ZXh0LAogICAgICAgICAgICAgICAgICAgICAgICBfZm9ybWF0RmlsZVNpemUoZmlsZVNpemUpLAogICAgICAgICAgICAgICAgICAgICAgICAnRmlsZSBzaXplJywKICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICBfYnVpbGRJbmZvUm93KAogICAgICAgICAgICAgICAgICAgICAgaWNvbjogSWNvbnMuZm9sZGVyX291dGxpbmVkLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWw6ICdGaWxlIFBhdGgnLAogICAgICAgICAgICAgICAgICAgICAgdmFsdWU6IGZpbGUucGF0aCwKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yU2NoZW1lOiBjb2xvclNjaGVtZSwKICAgICAgICAgICAgICAgICAgICAgIHRleHRUaGVtZTogdGV4dFRoZW1lLAogICAgICAgICAgICAgICAgICAgICAgaXNQYXRoOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgb25UYXA6ICgpID0+CiAgICAgICAgICAgICAgICAgICAgICAgICAgX2NvcHlUb0NsaXBib2FyZChjb250ZXh0LCBmaWxlLnBhdGgsICdGaWxlIHBhdGgnKSwKICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgIGNvbnN0IFNpemVkQm94KGhlaWdodDogOCksCiAgICAgICAgICAgICAgICAgICAgQ29udGFpbmVyKAogICAgICAgICAgICAgICAgICAgICAgcGFkZGluZzogY29uc3QgRWRnZUluc2V0cy5hbGwoMTIpLAogICAgICAgICAgICAgICAgICAgICAgZGVjb3JhdGlvbjogQm94RGVjb3JhdGlvbigKICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I6IGNvbG9yU2NoZW1lLnN1cmZhY2VDb250YWluZXJIaWdoZXN0LndpdGhWYWx1ZXMoCiAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGE6IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgYm9yZGVyUmFkaXVzOiBCb3JkZXJSYWRpdXMuY2lyY3VsYXIoOCksCiAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgY2hpbGQ6IFJvdygKICAgICAgICAgICAgICAgICAgICAgICAgY2hpbGRyZW46IFsKICAgICAgICAgICAgICAgICAgICAgICAgICBJY29uKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgSWNvbnMudG91Y2hfYXBwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZTogMTYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcjogY29sb3JTY2hlbWUub25TdXJmYWNlVmFyaWFudCwKICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IFNpemVkQm94KHdpZHRoOiA4KSwKICAgICAgICAgICAgICAgICAgICAgICAgICBFeHBhbmRlZCgKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNoaWxkOiBUZXh0KAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnVGFwIGFueSBmaWVsZCB0byBjb3B5IHRvIGNsaXBib2FyZCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0eWxlOiB0ZXh0VGhlbWUuYm9keVNtYWxsPy5jb3B5V2l0aCgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcjogY29sb3JTY2hlbWUub25TdXJmYWNlVmFyaWFudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb250U3R5bGU6IEZvbnRTdHlsZS5pdGFsaWMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgIF0sCiAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgIF0sCiAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICksCiAgICAgICAgICAgICksCiAgICAgICAgICBdLAogICAgICAgICksCiAgICAgICksCiAgICApOwogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/dialogs/index.dart", + "data": "ZXhwb3J0ICdmaWxlX2luZm9fZGlhbG9nLmRhcnQnOwo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/generated/theme.dart", + "data": + "aW1wb3J0ICJwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCI7CgpjbGFzcyBNYXRlcmlhbFRoZW1lIHsKICBmaW5hbCBUZXh0VGhlbWUgdGV4dFRoZW1lOwoKICBjb25zdCBNYXRlcmlhbFRoZW1lKHRoaXMudGV4dFRoZW1lKTsKCiAgc3RhdGljIENvbG9yU2NoZW1lIGxpZ2h0U2NoZW1lKCkgewogICAgcmV0dXJuIGNvbnN0IENvbG9yU2NoZW1lKAogICAgICBicmlnaHRuZXNzOiBCcmlnaHRuZXNzLmxpZ2h0LAogICAgICBwcmltYXJ5OiBDb2xvcigweGZmNDQ2NzMyKSwKICAgICAgc3VyZmFjZVRpbnQ6IENvbG9yKDB4ZmY0NDY3MzIpLAogICAgICBvblByaW1hcnk6IENvbG9yKDB4ZmZmZmZmZmYpLAogICAgICBwcmltYXJ5Q29udGFpbmVyOiBDb2xvcigweGZmYzVlZmFiKSwKICAgICAgb25QcmltYXJ5Q29udGFpbmVyOiBDb2xvcigweGZmMmQ0ZjFjKSwKICAgICAgc2Vjb25kYXJ5OiBDb2xvcigweGZmNTU2MjRjKSwKICAgICAgb25TZWNvbmRhcnk6IENvbG9yKDB4ZmZmZmZmZmYpLAogICAgICBzZWNvbmRhcnlDb250YWluZXI6IENvbG9yKDB4ZmZkOWU3Y2IpLAogICAgICBvblNlY29uZGFyeUNvbnRhaW5lcjogQ29sb3IoMHhmZjNlNGEzNSksCiAgICAgIHRlcnRpYXJ5OiBDb2xvcigweGZmMzg2NjY2KSwKICAgICAgb25UZXJ0aWFyeTogQ29sb3IoMHhmZmZmZmZmZiksCiAgICAgIHRlcnRpYXJ5Q29udGFpbmVyOiBDb2xvcigweGZmYmJlYmVjKSwKICAgICAgb25UZXJ0aWFyeUNvbnRhaW5lcjogQ29sb3IoMHhmZjFlNGU0ZSksCiAgICAgIGVycm9yOiBDb2xvcigweGZmYmExYTFhKSwKICAgICAgb25FcnJvcjogQ29sb3IoMHhmZmZmZmZmZiksCiAgICAgIGVycm9yQ29udGFpbmVyOiBDb2xvcigweGZmZmZkYWQ2KSwKICAgICAgb25FcnJvckNvbnRhaW5lcjogQ29sb3IoMHhmZjkzMDAwYSksCiAgICAgIHN1cmZhY2U6IENvbG9yKDB4ZmZmOGZhZjApLAogICAgICBvblN1cmZhY2U6IENvbG9yKDB4ZmYxOTFkMTYpLAogICAgICBvblN1cmZhY2VWYXJpYW50OiBDb2xvcigweGZmNDM0ODNlKSwKICAgICAgb3V0bGluZTogQ29sb3IoMHhmZjc0Nzk2ZCksCiAgICAgIG91dGxpbmVWYXJpYW50OiBDb2xvcigweGZmYzNjOGJiKSwKICAgICAgc2hhZG93OiBDb2xvcigweGZmMDAwMDAwKSwKICAgICAgc2NyaW06IENvbG9yKDB4ZmYwMDAwMDApLAogICAgICBpbnZlcnNlU3VyZmFjZTogQ29sb3IoMHhmZjJlMzEyYiksCiAgICAgIGludmVyc2VQcmltYXJ5OiBDb2xvcigweGZmYWFkMjkxKSwKICAgICAgcHJpbWFyeUZpeGVkOiBDb2xvcigweGZmYzVlZmFiKSwKICAgICAgb25QcmltYXJ5Rml4ZWQ6IENvbG9yKDB4ZmYwNzIxMDApLAogICAgICBwcmltYXJ5Rml4ZWREaW06IENvbG9yKDB4ZmZhYWQyOTEpLAogICAgICBvblByaW1hcnlGaXhlZFZhcmlhbnQ6IENvbG9yKDB4ZmYyZDRmMWMpLAogICAgICBzZWNvbmRhcnlGaXhlZDogQ29sb3IoMHhmZmQ5ZTdjYiksCiAgICAgIG9uU2Vjb25kYXJ5Rml4ZWQ6IENvbG9yKDB4ZmYxMzFmMGQpLAogICAgICBzZWNvbmRhcnlGaXhlZERpbTogQ29sb3IoMHhmZmJkY2JiMCksCiAgICAgIG9uU2Vjb25kYXJ5Rml4ZWRWYXJpYW50OiBDb2xvcigweGZmM2U0YTM1KSwKICAgICAgdGVydGlhcnlGaXhlZDogQ29sb3IoMHhmZmJiZWJlYyksCiAgICAgIG9uVGVydGlhcnlGaXhlZDogQ29sb3IoMHhmZjAwMjAyMCksCiAgICAgIHRlcnRpYXJ5Rml4ZWREaW06IENvbG9yKDB4ZmZhMGNmY2YpLAogICAgICBvblRlcnRpYXJ5Rml4ZWRWYXJpYW50OiBDb2xvcigweGZmMWU0ZTRlKSwKICAgICAgc3VyZmFjZURpbTogQ29sb3IoMHhmZmQ5ZGJkMSksCiAgICAgIHN1cmZhY2VCcmlnaHQ6IENvbG9yKDB4ZmZmOGZhZjApLAogICAgICBzdXJmYWNlQ29udGFpbmVyTG93ZXN0OiBDb2xvcigweGZmZmZmZmZmKSwKICAgICAgc3VyZmFjZUNvbnRhaW5lckxvdzogQ29sb3IoMHhmZmYyZjVlYSksCiAgICAgIHN1cmZhY2VDb250YWluZXI6IENvbG9yKDB4ZmZlZGVmZTUpLAogICAgICBzdXJmYWNlQ29udGFpbmVySGlnaDogQ29sb3IoMHhmZmU3ZTlkZiksCiAgICAgIHN1cmZhY2VDb250YWluZXJIaWdoZXN0OiBDb2xvcigweGZmZTFlNGQ5KSwKICAgICk7CiAgfQoKICBUaGVtZURhdGEgbGlnaHQoKSB7CiAgICByZXR1cm4gdGhlbWUobGlnaHRTY2hlbWUoKSk7CiAgfQoKICBzdGF0aWMgQ29sb3JTY2hlbWUgbGlnaHRNZWRpdW1Db250cmFzdFNjaGVtZSgpIHsKICAgIHJldHVybiBjb25zdCBDb2xvclNjaGVtZSgKICAgICAgYnJpZ2h0bmVzczogQnJpZ2h0bmVzcy5saWdodCwKICAgICAgcHJpbWFyeTogQ29sb3IoMHhmZjFkM2UwYyksCiAgICAgIHN1cmZhY2VUaW50OiBDb2xvcigweGZmNDQ2NzMyKSwKICAgICAgb25QcmltYXJ5OiBDb2xvcigweGZmZmZmZmZmKSwKICAgICAgcHJpbWFyeUNvbnRhaW5lcjogQ29sb3IoMHhmZjUzNzczZiksCiAgICAgIG9uUHJpbWFyeUNvbnRhaW5lcjogQ29sb3IoMHhmZmZmZmZmZiksCiAgICAgIHNlY29uZGFyeTogQ29sb3IoMHhmZjJkM2EyNiksCiAgICAgIG9uU2Vjb25kYXJ5OiBDb2xvcigweGZmZmZmZmZmKSwKICAgICAgc2Vjb25kYXJ5Q29udGFpbmVyOiBDb2xvcigweGZmNjQ3MTVhKSwKICAgICAgb25TZWNvbmRhcnlDb250YWluZXI6IENvbG9yKDB4ZmZmZmZmZmYpLAogICAgICB0ZXJ0aWFyeTogQ29sb3IoMHhmZjA3M2QzZSksCiAgICAgIG9uVGVydGlhcnk6IENvbG9yKDB4ZmZmZmZmZmYpLAogICAgICB0ZXJ0aWFyeUNvbnRhaW5lcjogQ29sb3IoMHhmZjQ3NzU3NSksCiAgICAgIG9uVGVydGlhcnlDb250YWluZXI6IENvbG9yKDB4ZmZmZmZmZmYpLAogICAgICBlcnJvcjogQ29sb3IoMHhmZjc0MDAwNiksCiAgICAgIG9uRXJyb3I6IENvbG9yKDB4ZmZmZmZmZmYpLAogICAgICBlcnJvckNvbnRhaW5lcjogQ29sb3IoMHhmZmNmMmMyNyksCiAgICAgIG9uRXJyb3JDb250YWluZXI6IENvbG9yKDB4ZmZmZmZmZmYpLAogICAgICBzdXJmYWNlOiBDb2xvcigweGZmZjhmYWYwKSwKICAgICAgb25TdXJmYWNlOiBDb2xvcigweGZmMGYxMjBjKSwKICAgICAgb25TdXJmYWNlVmFyaWFudDogQ29sb3IoMHhmZjMzMzgyZSksCiAgICAgIG91dGxpbmU6IENvbG9yKDB4ZmY0ZjU0NGEpLAogICAgICBvdXRsaW5lVmFyaWFudDogQ29sb3IoMHhmZjZhNmY2NCksCiAgICAgIHNoYWRvdzogQ29sb3IoMHhmZjAwMDAwMCksCiAgICAgIHNjcmltOiBDb2xvcigweGZmMDAwMDAwKSwKICAgICAgaW52ZXJzZVN1cmZhY2U6IENvbG9yKDB4ZmYyZTMxMmIpLAogICAgICBpbnZlcnNlUHJpbWFyeTogQ29sb3IoMHhmZmFhZDI5MSksCiAgICAgIHByaW1hcnlGaXhlZDogQ29sb3IoMHhmZjUzNzczZiksCiAgICAgIG9uUHJpbWFyeUZpeGVkOiBDb2xvcigweGZmZmZmZmZmKSwKICAgICAgcHJpbWFyeUZpeGVkRGltOiBDb2xvcigweGZmM2I1ZDI5KSwKICAgICAgb25QcmltYXJ5Rml4ZWRWYXJpYW50OiBDb2xvcigweGZmZmZmZmZmKSwKICAgICAgc2Vjb25kYXJ5Rml4ZWQ6IENvbG9yKDB4ZmY2NDcxNWEpLAogICAgICBvblNlY29uZGFyeUZpeGVkOiBDb2xvcigweGZmZmZmZmZmKSwKICAgICAgc2Vjb25kYXJ5Rml4ZWREaW06IENvbG9yKDB4ZmY0YzU5NDMpLAogICAgICBvblNlY29uZGFyeUZpeGVkVmFyaWFudDogQ29sb3IoMHhmZmZmZmZmZiksCiAgICAgIHRlcnRpYXJ5Rml4ZWQ6IENvbG9yKDB4ZmY0Nzc1NzUpLAogICAgICBvblRlcnRpYXJ5Rml4ZWQ6IENvbG9yKDB4ZmZmZmZmZmYpLAogICAgICB0ZXJ0aWFyeUZpeGVkRGltOiBDb2xvcigweGZmMmU1YzVkKSwKICAgICAgb25UZXJ0aWFyeUZpeGVkVmFyaWFudDogQ29sb3IoMHhmZmZmZmZmZiksCiAgICAgIHN1cmZhY2VEaW06IENvbG9yKDB4ZmZjNWM4YmUpLAogICAgICBzdXJmYWNlQnJpZ2h0OiBDb2xvcigweGZmZjhmYWYwKSwKICAgICAgc3VyZmFjZUNvbnRhaW5lckxvd2VzdDogQ29sb3IoMHhmZmZmZmZmZiksCiAgICAgIHN1cmZhY2VDb250YWluZXJMb3c6IENvbG9yKDB4ZmZmMmY1ZWEpLAogICAgICBzdXJmYWNlQ29udGFpbmVyOiBDb2xvcigweGZmZTdlOWRmKSwKICAgICAgc3VyZmFjZUNvbnRhaW5lckhpZ2g6IENvbG9yKDB4ZmZkYmRlZDQpLAogICAgICBzdXJmYWNlQ29udGFpbmVySGlnaGVzdDogQ29sb3IoMHhmZmQwZDNjOSksCiAgICApOwogIH0KCiAgVGhlbWVEYXRhIGxpZ2h0TWVkaXVtQ29udHJhc3QoKSB7CiAgICByZXR1cm4gdGhlbWUobGlnaHRNZWRpdW1Db250cmFzdFNjaGVtZSgpKTsKICB9CgogIHN0YXRpYyBDb2xvclNjaGVtZSBsaWdodEhpZ2hDb250cmFzdFNjaGVtZSgpIHsKICAgIHJldHVybiBjb25zdCBDb2xvclNjaGVtZSgKICAgICAgYnJpZ2h0bmVzczogQnJpZ2h0bmVzcy5saWdodCwKICAgICAgcHJpbWFyeTogQ29sb3IoMHhmZjEzMzMwNCksCiAgICAgIHN1cmZhY2VUaW50OiBDb2xvcigweGZmNDQ2NzMyKSwKICAgICAgb25QcmltYXJ5OiBDb2xvcigweGZmZmZmZmZmKSwKICAgICAgcHJpbWFyeUNvbnRhaW5lcjogQ29sb3IoMHhmZjMwNTExZSksCiAgICAgIG9uUHJpbWFyeUNvbnRhaW5lcjogQ29sb3IoMHhmZmZmZmZmZiksCiAgICAgIHNlY29uZGFyeTogQ29sb3IoMHhmZjI0MmYxYyksCiAgICAgIG9uU2Vjb25kYXJ5OiBDb2xvcigweGZmZmZmZmZmKSwKICAgICAgc2Vjb25kYXJ5Q29udGFpbmVyOiBDb2xvcigweGZmNDA0ZDM4KSwKICAgICAgb25TZWNvbmRhcnlDb250YWluZXI6IENvbG9yKDB4ZmZmZmZmZmYpLAogICAgICB0ZXJ0aWFyeTogQ29sb3IoMHhmZjAwMzIzMyksCiAgICAgIG9uVGVydGlhcnk6IENvbG9yKDB4ZmZmZmZmZmYpLAogICAgICB0ZXJ0aWFyeUNvbnRhaW5lcjogQ29sb3IoMHhmZjIxNTA1MSksCiAgICAgIG9uVGVydGlhcnlDb250YWluZXI6IENvbG9yKDB4ZmZmZmZmZmYpLAogICAgICBlcnJvcjogQ29sb3IoMHhmZjYwMDAwNCksCiAgICAgIG9uRXJyb3I6IENvbG9yKDB4ZmZmZmZmZmYpLAogICAgICBlcnJvckNvbnRhaW5lcjogQ29sb3IoMHhmZjk4MDAwYSksCiAgICAgIG9uRXJyb3JDb250YWluZXI6IENvbG9yKDB4ZmZmZmZmZmYpLAogICAgICBzdXJmYWNlOiBDb2xvcigweGZmZjhmYWYwKSwKICAgICAgb25TdXJmYWNlOiBDb2xvcigweGZmMDAwMDAwKSwKICAgICAgb25TdXJmYWNlVmFyaWFudDogQ29sb3IoMHhmZjAwMDAwMCksCiAgICAgIG91dGxpbmU6IENvbG9yKDB4ZmYyOTJlMjUpLAogICAgICBvdXRsaW5lVmFyaWFudDogQ29sb3IoMHhmZjQ2NGI0MSksCiAgICAgIHNoYWRvdzogQ29sb3IoMHhmZjAwMDAwMCksCiAgICAgIHNjcmltOiBDb2xvcigweGZmMDAwMDAwKSwKICAgICAgaW52ZXJzZVN1cmZhY2U6IENvbG9yKDB4ZmYyZTMxMmIpLAogICAgICBpbnZlcnNlUHJpbWFyeTogQ29sb3IoMHhmZmFhZDI5MSksCiAgICAgIHByaW1hcnlGaXhlZDogQ29sb3IoMHhmZjMwNTExZSksCiAgICAgIG9uUHJpbWFyeUZpeGVkOiBDb2xvcigweGZmZmZmZmZmKSwKICAgICAgcHJpbWFyeUZpeGVkRGltOiBDb2xvcigweGZmMWEzYTA5KSwKICAgICAgb25QcmltYXJ5Rml4ZWRWYXJpYW50OiBDb2xvcigweGZmZmZmZmZmKSwKICAgICAgc2Vjb25kYXJ5Rml4ZWQ6IENvbG9yKDB4ZmY0MDRkMzgpLAogICAgICBvblNlY29uZGFyeUZpeGVkOiBDb2xvcigweGZmZmZmZmZmKSwKICAgICAgc2Vjb25kYXJ5Rml4ZWREaW06IENvbG9yKDB4ZmYyYTM2MjIpLAogICAgICBvblNlY29uZGFyeUZpeGVkVmFyaWFudDogQ29sb3IoMHhmZmZmZmZmZiksCiAgICAgIHRlcnRpYXJ5Rml4ZWQ6IENvbG9yKDB4ZmYyMTUwNTEpLAogICAgICBvblRlcnRpYXJ5Rml4ZWQ6IENvbG9yKDB4ZmZmZmZmZmYpLAogICAgICB0ZXJ0aWFyeUZpeGVkRGltOiBDb2xvcigweGZmMDIzOTNhKSwKICAgICAgb25UZXJ0aWFyeUZpeGVkVmFyaWFudDogQ29sb3IoMHhmZmZmZmZmZiksCiAgICAgIHN1cmZhY2VEaW06IENvbG9yKDB4ZmZiN2JhYjApLAogICAgICBzdXJmYWNlQnJpZ2h0OiBDb2xvcigweGZmZjhmYWYwKSwKICAgICAgc3VyZmFjZUNvbnRhaW5lckxvd2VzdDogQ29sb3IoMHhmZmZmZmZmZiksCiAgICAgIHN1cmZhY2VDb250YWluZXJMb3c6IENvbG9yKDB4ZmZmMGYyZTcpLAogICAgICBzdXJmYWNlQ29udGFpbmVyOiBDb2xvcigweGZmZTFlNGQ5KSwKICAgICAgc3VyZmFjZUNvbnRhaW5lckhpZ2g6IENvbG9yKDB4ZmZkM2Q1Y2IpLAogICAgICBzdXJmYWNlQ29udGFpbmVySGlnaGVzdDogQ29sb3IoMHhmZmM1YzhiZSksCiAgICApOwogIH0KCiAgVGhlbWVEYXRhIGxpZ2h0SGlnaENvbnRyYXN0KCkgewogICAgcmV0dXJuIHRoZW1lKGxpZ2h0SGlnaENvbnRyYXN0U2NoZW1lKCkpOwogIH0KCiAgc3RhdGljIENvbG9yU2NoZW1lIGRhcmtTY2hlbWUoKSB7CiAgICByZXR1cm4gY29uc3QgQ29sb3JTY2hlbWUoCiAgICAgIGJyaWdodG5lc3M6IEJyaWdodG5lc3MuZGFyaywKICAgICAgcHJpbWFyeTogQ29sb3IoMHhmZmFhZDI5MSksCiAgICAgIHN1cmZhY2VUaW50OiBDb2xvcigweGZmYWFkMjkxKSwKICAgICAgb25QcmltYXJ5OiBDb2xvcigweGZmMTczODA3KSwKICAgICAgcHJpbWFyeUNvbnRhaW5lcjogQ29sb3IoMHhmZjJkNGYxYyksCiAgICAgIG9uUHJpbWFyeUNvbnRhaW5lcjogQ29sb3IoMHhmZmM1ZWZhYiksCiAgICAgIHNlY29uZGFyeTogQ29sb3IoMHhmZmJkY2JiMCksCiAgICAgIG9uU2Vjb25kYXJ5OiBDb2xvcigweGZmMjgzNDIwKSwKICAgICAgc2Vjb25kYXJ5Q29udGFpbmVyOiBDb2xvcigweGZmM2U0YTM1KSwKICAgICAgb25TZWNvbmRhcnlDb250YWluZXI6IENvbG9yKDB4ZmZkOWU3Y2IpLAogICAgICB0ZXJ0aWFyeTogQ29sb3IoMHhmZmEwY2ZjZiksCiAgICAgIG9uVGVydGlhcnk6IENvbG9yKDB4ZmYwMDM3MzgpLAogICAgICB0ZXJ0aWFyeUNvbnRhaW5lcjogQ29sb3IoMHhmZjFlNGU0ZSksCiAgICAgIG9uVGVydGlhcnlDb250YWluZXI6IENvbG9yKDB4ZmZiYmViZWMpLAogICAgICBlcnJvcjogQ29sb3IoMHhmZmZmYjRhYiksCiAgICAgIG9uRXJyb3I6IENvbG9yKDB4ZmY2OTAwMDUpLAogICAgICBlcnJvckNvbnRhaW5lcjogQ29sb3IoMHhmZjkzMDAwYSksCiAgICAgIG9uRXJyb3JDb250YWluZXI6IENvbG9yKDB4ZmZmZmRhZDYpLAogICAgICBzdXJmYWNlOiBDb2xvcigweGZmMTExNDBlKSwKICAgICAgb25TdXJmYWNlOiBDb2xvcigweGZmZTFlNGQ5KSwKICAgICAgb25TdXJmYWNlVmFyaWFudDogQ29sb3IoMHhmZmMzYzhiYiksCiAgICAgIG91dGxpbmU6IENvbG9yKDB4ZmY4ZDkyODYpLAogICAgICBvdXRsaW5lVmFyaWFudDogQ29sb3IoMHhmZjQzNDgzZSksCiAgICAgIHNoYWRvdzogQ29sb3IoMHhmZjAwMDAwMCksCiAgICAgIHNjcmltOiBDb2xvcigweGZmMDAwMDAwKSwKICAgICAgaW52ZXJzZVN1cmZhY2U6IENvbG9yKDB4ZmZlMWU0ZDkpLAogICAgICBpbnZlcnNlUHJpbWFyeTogQ29sb3IoMHhmZjQ0NjczMiksCiAgICAgIHByaW1hcnlGaXhlZDogQ29sb3IoMHhmZmM1ZWZhYiksCiAgICAgIG9uUHJpbWFyeUZpeGVkOiBDb2xvcigweGZmMDcyMTAwKSwKICAgICAgcHJpbWFyeUZpeGVkRGltOiBDb2xvcigweGZmYWFkMjkxKSwKICAgICAgb25QcmltYXJ5Rml4ZWRWYXJpYW50OiBDb2xvcigweGZmMmQ0ZjFjKSwKICAgICAgc2Vjb25kYXJ5Rml4ZWQ6IENvbG9yKDB4ZmZkOWU3Y2IpLAogICAgICBvblNlY29uZGFyeUZpeGVkOiBDb2xvcigweGZmMTMxZjBkKSwKICAgICAgc2Vjb25kYXJ5Rml4ZWREaW06IENvbG9yKDB4ZmZiZGNiYjApLAogICAgICBvblNlY29uZGFyeUZpeGVkVmFyaWFudDogQ29sb3IoMHhmZjNlNGEzNSksCiAgICAgIHRlcnRpYXJ5Rml4ZWQ6IENvbG9yKDB4ZmZiYmViZWMpLAogICAgICBvblRlcnRpYXJ5Rml4ZWQ6IENvbG9yKDB4ZmYwMDIwMjApLAogICAgICB0ZXJ0aWFyeUZpeGVkRGltOiBDb2xvcigweGZmYTBjZmNmKSwKICAgICAgb25UZXJ0aWFyeUZpeGVkVmFyaWFudDogQ29sb3IoMHhmZjFlNGU0ZSksCiAgICAgIHN1cmZhY2VEaW06IENvbG9yKDB4ZmYxMTE0MGUpLAogICAgICBzdXJmYWNlQnJpZ2h0OiBDb2xvcigweGZmMzczYTMzKSwKICAgICAgc3VyZmFjZUNvbnRhaW5lckxvd2VzdDogQ29sb3IoMHhmZjBjMGYwOSksCiAgICAgIHN1cmZhY2VDb250YWluZXJMb3c6IENvbG9yKDB4ZmYxOTFkMTYpLAogICAgICBzdXJmYWNlQ29udGFpbmVyOiBDb2xvcigweGZmMWQyMTFhKSwKICAgICAgc3VyZmFjZUNvbnRhaW5lckhpZ2g6IENvbG9yKDB4ZmYyODJiMjQpLAogICAgICBzdXJmYWNlQ29udGFpbmVySGlnaGVzdDogQ29sb3IoMHhmZjMyMzYyZiksCiAgICApOwogIH0KCiAgVGhlbWVEYXRhIGRhcmsoKSB7CiAgICByZXR1cm4gdGhlbWUoZGFya1NjaGVtZSgpKTsKICB9CgogIHN0YXRpYyBDb2xvclNjaGVtZSBkYXJrTWVkaXVtQ29udHJhc3RTY2hlbWUoKSB7CiAgICByZXR1cm4gY29uc3QgQ29sb3JTY2hlbWUoCiAgICAgIGJyaWdodG5lc3M6IEJyaWdodG5lc3MuZGFyaywKICAgICAgcHJpbWFyeTogQ29sb3IoMHhmZmJmZThhNSksCiAgICAgIHN1cmZhY2VUaW50OiBDb2xvcigweGZmYWFkMjkxKSwKICAgICAgb25QcmltYXJ5OiBDb2xvcigweGZmMGMyYzAwKSwKICAgICAgcHJpbWFyeUNvbnRhaW5lcjogQ29sb3IoMHhmZjc1OWI1ZiksCiAgICAgIG9uUHJpbWFyeUNvbnRhaW5lcjogQ29sb3IoMHhmZjAwMDAwMCksCiAgICAgIHNlY29uZGFyeTogQ29sb3IoMHhmZmQyZTFjNSksCiAgICAgIG9uU2Vjb25kYXJ5OiBDb2xvcigweGZmMWQyOTE2KSwKICAgICAgc2Vjb25kYXJ5Q29udGFpbmVyOiBDb2xvcigweGZmODc5NTdjKSwKICAgICAgb25TZWNvbmRhcnlDb250YWluZXI6IENvbG9yKDB4ZmYwMDAwMDApLAogICAgICB0ZXJ0aWFyeTogQ29sb3IoMHhmZmI1ZTVlNSksCiAgICAgIG9uVGVydGlhcnk6IENvbG9yKDB4ZmYwMDJiMmMpLAogICAgICB0ZXJ0aWFyeUNvbnRhaW5lcjogQ29sb3IoMHhmZjZiOTk5OSksCiAgICAgIG9uVGVydGlhcnlDb250YWluZXI6IENvbG9yKDB4ZmYwMDAwMDApLAogICAgICBlcnJvcjogQ29sb3IoMHhmZmZmZDJjYyksCiAgICAgIG9uRXJyb3I6IENvbG9yKDB4ZmY1NDAwMDMpLAogICAgICBlcnJvckNvbnRhaW5lcjogQ29sb3IoMHhmZmZmNTQ0OSksCiAgICAgIG9uRXJyb3JDb250YWluZXI6IENvbG9yKDB4ZmYwMDAwMDApLAogICAgICBzdXJmYWNlOiBDb2xvcigweGZmMTExNDBlKSwKICAgICAgb25TdXJmYWNlOiBDb2xvcigweGZmZmZmZmZmKSwKICAgICAgb25TdXJmYWNlVmFyaWFudDogQ29sb3IoMHhmZmQ5ZGVkMCksCiAgICAgIG91dGxpbmU6IENvbG9yKDB4ZmZhZmI0YTcpLAogICAgICBvdXRsaW5lVmFyaWFudDogQ29sb3IoMHhmZjhkOTI4NiksCiAgICAgIHNoYWRvdzogQ29sb3IoMHhmZjAwMDAwMCksCiAgICAgIHNjcmltOiBDb2xvcigweGZmMDAwMDAwKSwKICAgICAgaW52ZXJzZVN1cmZhY2U6IENvbG9yKDB4ZmZlMWU0ZDkpLAogICAgICBpbnZlcnNlUHJpbWFyeTogQ29sb3IoMHhmZjJmNTAxZCksCiAgICAgIHByaW1hcnlGaXhlZDogQ29sb3IoMHhmZmM1ZWZhYiksCiAgICAgIG9uUHJpbWFyeUZpeGVkOiBDb2xvcigweGZmMDQxNTAwKSwKICAgICAgcHJpbWFyeUZpeGVkRGltOiBDb2xvcigweGZmYWFkMjkxKSwKICAgICAgb25QcmltYXJ5Rml4ZWRWYXJpYW50OiBDb2xvcigweGZmMWQzZTBjKSwKICAgICAgc2Vjb25kYXJ5Rml4ZWQ6IENvbG9yKDB4ZmZkOWU3Y2IpLAogICAgICBvblNlY29uZGFyeUZpeGVkOiBDb2xvcigweGZmMDkxNDA1KSwKICAgICAgc2Vjb25kYXJ5Rml4ZWREaW06IENvbG9yKDB4ZmZiZGNiYjApLAogICAgICBvblNlY29uZGFyeUZpeGVkVmFyaWFudDogQ29sb3IoMHhmZjJkM2EyNiksCiAgICAgIHRlcnRpYXJ5Rml4ZWQ6IENvbG9yKDB4ZmZiYmViZWMpLAogICAgICBvblRlcnRpYXJ5Rml4ZWQ6IENvbG9yKDB4ZmYwMDE0MTUpLAogICAgICB0ZXJ0aWFyeUZpeGVkRGltOiBDb2xvcigweGZmYTBjZmNmKSwKICAgICAgb25UZXJ0aWFyeUZpeGVkVmFyaWFudDogQ29sb3IoMHhmZjA3M2QzZSksCiAgICAgIHN1cmZhY2VEaW06IENvbG9yKDB4ZmYxMTE0MGUpLAogICAgICBzdXJmYWNlQnJpZ2h0OiBDb2xvcigweGZmNDI0NjNlKSwKICAgICAgc3VyZmFjZUNvbnRhaW5lckxvd2VzdDogQ29sb3IoMHhmZjA2MDgwNCksCiAgICAgIHN1cmZhY2VDb250YWluZXJMb3c6IENvbG9yKDB4ZmYxYjFmMTgpLAogICAgICBzdXJmYWNlQ29udGFpbmVyOiBDb2xvcigweGZmMjYyOTIyKSwKICAgICAgc3VyZmFjZUNvbnRhaW5lckhpZ2g6IENvbG9yKDB4ZmYzMDM0MmQpLAogICAgICBzdXJmYWNlQ29udGFpbmVySGlnaGVzdDogQ29sb3IoMHhmZjNiM2YzOCksCiAgICApOwogIH0KCiAgVGhlbWVEYXRhIGRhcmtNZWRpdW1Db250cmFzdCgpIHsKICAgIHJldHVybiB0aGVtZShkYXJrTWVkaXVtQ29udHJhc3RTY2hlbWUoKSk7CiAgfQoKICBzdGF0aWMgQ29sb3JTY2hlbWUgZGFya0hpZ2hDb250cmFzdFNjaGVtZSgpIHsKICAgIHJldHVybiBjb25zdCBDb2xvclNjaGVtZSgKICAgICAgYnJpZ2h0bmVzczogQnJpZ2h0bmVzcy5kYXJrLAogICAgICBwcmltYXJ5OiBDb2xvcigweGZmZDJmY2I3KSwKICAgICAgc3VyZmFjZVRpbnQ6IENvbG9yKDB4ZmZhYWQyOTEpLAogICAgICBvblByaW1hcnk6IENvbG9yKDB4ZmYwMDAwMDApLAogICAgICBwcmltYXJ5Q29udGFpbmVyOiBDb2xvcigweGZmYTZjZThkKSwKICAgICAgb25QcmltYXJ5Q29udGFpbmVyOiBDb2xvcigweGZmMDIwZTAwKSwKICAgICAgc2Vjb25kYXJ5OiBDb2xvcigweGZmZTZmNWQ4KSwKICAgICAgb25TZWNvbmRhcnk6IENvbG9yKDB4ZmYwMDAwMDApLAogICAgICBzZWNvbmRhcnlDb250YWluZXI6IENvbG9yKDB4ZmZiOWM3YWMpLAogICAgICBvblNlY29uZGFyeUNvbnRhaW5lcjogQ29sb3IoMHhmZjA0MGUwMiksCiAgICAgIHRlcnRpYXJ5OiBDb2xvcigweGZmYzlmOWY5KSwKICAgICAgb25UZXJ0aWFyeTogQ29sb3IoMHhmZjAwMDAwMCksCiAgICAgIHRlcnRpYXJ5Q29udGFpbmVyOiBDb2xvcigweGZmOWNjYmNiKSwKICAgICAgb25UZXJ0aWFyeUNvbnRhaW5lcjogQ29sb3IoMHhmZjAwMGUwZSksCiAgICAgIGVycm9yOiBDb2xvcigweGZmZmZlY2U5KSwKICAgICAgb25FcnJvcjogQ29sb3IoMHhmZjAwMDAwMCksCiAgICAgIGVycm9yQ29udGFpbmVyOiBDb2xvcigweGZmZmZhZWE0KSwKICAgICAgb25FcnJvckNvbnRhaW5lcjogQ29sb3IoMHhmZjIyMDAwMSksCiAgICAgIHN1cmZhY2U6IENvbG9yKDB4ZmYxMTE0MGUpLAogICAgICBvblN1cmZhY2U6IENvbG9yKDB4ZmZmZmZmZmYpLAogICAgICBvblN1cmZhY2VWYXJpYW50OiBDb2xvcigweGZmZmZmZmZmKSwKICAgICAgb3V0bGluZTogQ29sb3IoMHhmZmVkZjJlNCksCiAgICAgIG91dGxpbmVWYXJpYW50OiBDb2xvcigweGZmYmZjNGI3KSwKICAgICAgc2hhZG93OiBDb2xvcigweGZmMDAwMDAwKSwKICAgICAgc2NyaW06IENvbG9yKDB4ZmYwMDAwMDApLAogICAgICBpbnZlcnNlU3VyZmFjZTogQ29sb3IoMHhmZmUxZTRkOSksCiAgICAgIGludmVyc2VQcmltYXJ5OiBDb2xvcigweGZmMmY1MDFkKSwKICAgICAgcHJpbWFyeUZpeGVkOiBDb2xvcigweGZmYzVlZmFiKSwKICAgICAgb25QcmltYXJ5Rml4ZWQ6IENvbG9yKDB4ZmYwMDAwMDApLAogICAgICBwcmltYXJ5Rml4ZWREaW06IENvbG9yKDB4ZmZhYWQyOTEpLAogICAgICBvblByaW1hcnlGaXhlZFZhcmlhbnQ6IENvbG9yKDB4ZmYwNDE1MDApLAogICAgICBzZWNvbmRhcnlGaXhlZDogQ29sb3IoMHhmZmQ5ZTdjYiksCiAgICAgIG9uU2Vjb25kYXJ5Rml4ZWQ6IENvbG9yKDB4ZmYwMDAwMDApLAogICAgICBzZWNvbmRhcnlGaXhlZERpbTogQ29sb3IoMHhmZmJkY2JiMCksCiAgICAgIG9uU2Vjb25kYXJ5Rml4ZWRWYXJpYW50OiBDb2xvcigweGZmMDkxNDA1KSwKICAgICAgdGVydGlhcnlGaXhlZDogQ29sb3IoMHhmZmJiZWJlYyksCiAgICAgIG9uVGVydGlhcnlGaXhlZDogQ29sb3IoMHhmZjAwMDAwMCksCiAgICAgIHRlcnRpYXJ5Rml4ZWREaW06IENvbG9yKDB4ZmZhMGNmY2YpLAogICAgICBvblRlcnRpYXJ5Rml4ZWRWYXJpYW50OiBDb2xvcigweGZmMDAxNDE1KSwKICAgICAgc3VyZmFjZURpbTogQ29sb3IoMHhmZjExMTQwZSksCiAgICAgIHN1cmZhY2VCcmlnaHQ6IENvbG9yKDB4ZmY0ZTUxNGEpLAogICAgICBzdXJmYWNlQ29udGFpbmVyTG93ZXN0OiBDb2xvcigweGZmMDAwMDAwKSwKICAgICAgc3VyZmFjZUNvbnRhaW5lckxvdzogQ29sb3IoMHhmZjFkMjExYSksCiAgICAgIHN1cmZhY2VDb250YWluZXI6IENvbG9yKDB4ZmYyZTMxMmIpLAogICAgICBzdXJmYWNlQ29udGFpbmVySGlnaDogQ29sb3IoMHhmZjM5M2MzNSksCiAgICAgIHN1cmZhY2VDb250YWluZXJIaWdoZXN0OiBDb2xvcigweGZmNDQ0ODQwKSwKICAgICk7CiAgfQoKICBUaGVtZURhdGEgZGFya0hpZ2hDb250cmFzdCgpIHsKICAgIHJldHVybiB0aGVtZShkYXJrSGlnaENvbnRyYXN0U2NoZW1lKCkpOwogIH0KCiAgVGhlbWVEYXRhIHRoZW1lKENvbG9yU2NoZW1lIGNvbG9yU2NoZW1lKSA9PiBUaGVtZURhdGEoCiAgICB1c2VNYXRlcmlhbDM6IHRydWUsCiAgICBicmlnaHRuZXNzOiBjb2xvclNjaGVtZS5icmlnaHRuZXNzLAogICAgY29sb3JTY2hlbWU6IGNvbG9yU2NoZW1lLAogICAgdGV4dFRoZW1lOiB0ZXh0VGhlbWUuYXBwbHkoCiAgICAgIGJvZHlDb2xvcjogY29sb3JTY2hlbWUub25TdXJmYWNlLAogICAgICBkaXNwbGF5Q29sb3I6IGNvbG9yU2NoZW1lLm9uU3VyZmFjZSwKICAgICksCiAgICBzY2FmZm9sZEJhY2tncm91bmRDb2xvcjogY29sb3JTY2hlbWUuc3VyZmFjZSwKICAgIGNhbnZhc0NvbG9yOiBjb2xvclNjaGVtZS5zdXJmYWNlLAogICk7CgogIExpc3Q8RXh0ZW5kZWRDb2xvcj4gZ2V0IGV4dGVuZGVkQ29sb3JzID0+IFtdOwp9CgpjbGFzcyBFeHRlbmRlZENvbG9yIHsKICBmaW5hbCBDb2xvciBzZWVkLCB2YWx1ZTsKICBmaW5hbCBDb2xvckZhbWlseSBsaWdodDsKICBmaW5hbCBDb2xvckZhbWlseSBsaWdodEhpZ2hDb250cmFzdDsKICBmaW5hbCBDb2xvckZhbWlseSBsaWdodE1lZGl1bUNvbnRyYXN0OwogIGZpbmFsIENvbG9yRmFtaWx5IGRhcms7CiAgZmluYWwgQ29sb3JGYW1pbHkgZGFya0hpZ2hDb250cmFzdDsKICBmaW5hbCBDb2xvckZhbWlseSBkYXJrTWVkaXVtQ29udHJhc3Q7CgogIGNvbnN0IEV4dGVuZGVkQ29sb3IoewogICAgcmVxdWlyZWQgdGhpcy5zZWVkLAogICAgcmVxdWlyZWQgdGhpcy52YWx1ZSwKICAgIHJlcXVpcmVkIHRoaXMubGlnaHQsCiAgICByZXF1aXJlZCB0aGlzLmxpZ2h0SGlnaENvbnRyYXN0LAogICAgcmVxdWlyZWQgdGhpcy5saWdodE1lZGl1bUNvbnRyYXN0LAogICAgcmVxdWlyZWQgdGhpcy5kYXJrLAogICAgcmVxdWlyZWQgdGhpcy5kYXJrSGlnaENvbnRyYXN0LAogICAgcmVxdWlyZWQgdGhpcy5kYXJrTWVkaXVtQ29udHJhc3QsCiAgfSk7Cn0KCmNsYXNzIENvbG9yRmFtaWx5IHsKICBjb25zdCBDb2xvckZhbWlseSh7CiAgICByZXF1aXJlZCB0aGlzLmNvbG9yLAogICAgcmVxdWlyZWQgdGhpcy5vbkNvbG9yLAogICAgcmVxdWlyZWQgdGhpcy5jb2xvckNvbnRhaW5lciwKICAgIHJlcXVpcmVkIHRoaXMub25Db2xvckNvbnRhaW5lciwKICB9KTsKCiAgZmluYWwgQ29sb3IgY29sb3I7CiAgZmluYWwgQ29sb3Igb25Db2xvcjsKICBmaW5hbCBDb2xvciBjb2xvckNvbnRhaW5lcjsKICBmaW5hbCBDb2xvciBvbkNvbG9yQ29udGFpbmVyOwp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/generated/util.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CmltcG9ydCAncGFja2FnZTpnb29nbGVfZm9udHMvZ29vZ2xlX2ZvbnRzLmRhcnQnOwoKVGV4dFRoZW1lIGNyZWF0ZVRleHRUaGVtZSgKICBCdWlsZENvbnRleHQgY29udGV4dCwKICBTdHJpbmcgYm9keUZvbnRTdHJpbmcsCiAgU3RyaW5nIGRpc3BsYXlGb250U3RyaW5nLAopIHsKICBUZXh0VGhlbWUgYmFzZVRleHRUaGVtZSA9IFRoZW1lLm9mKGNvbnRleHQpLnRleHRUaGVtZTsKICBUZXh0VGhlbWUgYm9keVRleHRUaGVtZSA9IEdvb2dsZUZvbnRzLmdldFRleHRUaGVtZSgKICAgIGJvZHlGb250U3RyaW5nLAogICAgYmFzZVRleHRUaGVtZSwKICApOwogIFRleHRUaGVtZSBkaXNwbGF5VGV4dFRoZW1lID0gR29vZ2xlRm9udHMuZ2V0VGV4dFRoZW1lKAogICAgZGlzcGxheUZvbnRTdHJpbmcsCiAgICBiYXNlVGV4dFRoZW1lLAogICk7CiAgVGV4dFRoZW1lIHRleHRUaGVtZSA9IGRpc3BsYXlUZXh0VGhlbWUuY29weVdpdGgoCiAgICBib2R5TGFyZ2U6IGJvZHlUZXh0VGhlbWUuYm9keUxhcmdlLAogICAgYm9keU1lZGl1bTogYm9keVRleHRUaGVtZS5ib2R5TWVkaXVtLAogICAgYm9keVNtYWxsOiBib2R5VGV4dFRoZW1lLmJvZHlTbWFsbCwKICAgIGxhYmVsTGFyZ2U6IGJvZHlUZXh0VGhlbWUubGFiZWxMYXJnZSwKICAgIGxhYmVsTWVkaXVtOiBib2R5VGV4dFRoZW1lLmxhYmVsTWVkaXVtLAogICAgbGFiZWxTbWFsbDogYm9keVRleHRUaGVtZS5sYWJlbFNtYWxsLAogICk7CiAgcmV0dXJuIHRleHRUaGVtZTsKfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/loader/loader.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmRlc2lnbl9zeXN0ZW0vY29tcG9uZW50cy9pbmRleC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CmltcG9ydCAncGFja2FnZTpsb2FkZXJfb3ZlcmxheS9sb2FkZXJfb3ZlcmxheS5kYXJ0JzsKCmNsYXNzIExvYWRlciB7CiAgc3RhdGljIHZvaWQgc2hvdyhCdWlsZENvbnRleHQgY29udGV4dCkgewogICAgY29udGV4dC5sb2FkZXJPdmVybGF5LnNob3coCiAgICAgIHdpZGdldEJ1aWxkZXI6IChfKSA9PiBjb25zdCBDZW50ZXIoY2hpbGQ6IERlZmF1bHRMb2FkZXIoKSksCiAgICApOwogIH0KCiAgc3RhdGljIHZvaWQgaGlkZShCdWlsZENvbnRleHQgY29udGV4dCkgewogICAgY29udGV4dC5sb2FkZXJPdmVybGF5LmhpZGUoKTsKICB9Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/screens/error_screen.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CmltcG9ydCAncGFja2FnZTpsb2NhbGl6YXRpb24vbG9jYWxpemF0aW9uLmRhcnQnOwoKY2xhc3MgRXJyb3JTY3JlZW4gZXh0ZW5kcyBTdGF0ZWxlc3NXaWRnZXQgewogIGNvbnN0IEVycm9yU2NyZWVuKHsKICAgIHN1cGVyLmtleSwKICAgIHRoaXMudGl0bGUsCiAgICB0aGlzLm1lc3NhZ2UsCiAgICB0aGlzLm9uUmV0cnksCiAgICB0aGlzLm9uR29Ib21lLAogICAgdGhpcy5lcnJvciwKICB9KTsKCiAgZmluYWwgU3RyaW5nPyB0aXRsZTsKICBmaW5hbCBTdHJpbmc/IG1lc3NhZ2U7CiAgZmluYWwgVm9pZENhbGxiYWNrPyBvblJldHJ5OwogIGZpbmFsIFZvaWRDYWxsYmFjaz8gb25Hb0hvbWU7CiAgZmluYWwgU3RyaW5nPyBlcnJvcjsKCiAgQG92ZXJyaWRlCiAgV2lkZ2V0IGJ1aWxkKEJ1aWxkQ29udGV4dCBjb250ZXh0KSB7CiAgICBmaW5hbCB0aGVtZSA9IFRoZW1lLm9mKGNvbnRleHQpOwogICAgZmluYWwgY29sb3JTY2hlbWUgPSB0aGVtZS5jb2xvclNjaGVtZTsKICAgIGZpbmFsIHRleHRUaGVtZSA9IHRoZW1lLnRleHRUaGVtZTsKCiAgICBmaW5hbCByZXNvbHZlZFRpdGxlID0gdGl0bGUgPz8gc3RyaW5ncy5lcnJvcnMuZGVmYXVsdF9lcnJvcl9tZXNzYWdlOwogICAgZmluYWwgcmVzb2x2ZWRNZXNzYWdlID0gbWVzc2FnZSA/PyBzdHJpbmdzLmVycm9ycy5kZWZhdWx0X2Vycm9yX2Rlc2NyaXB0aW9uOwoKICAgIHJldHVybiBTY2FmZm9sZCgKICAgICAgYmFja2dyb3VuZENvbG9yOiBjb2xvclNjaGVtZS5zdXJmYWNlLAogICAgICBhcHBCYXI6IEFwcEJhcigKICAgICAgICBiYWNrZ3JvdW5kQ29sb3I6IGNvbG9yU2NoZW1lLnN1cmZhY2UsCiAgICAgICAgZm9yZWdyb3VuZENvbG9yOiBjb2xvclNjaGVtZS5vblN1cmZhY2UsCiAgICAgICAgZWxldmF0aW9uOiAwLAogICAgICAgIHN1cmZhY2VUaW50Q29sb3I6IGNvbG9yU2NoZW1lLnN1cmZhY2VUaW50LAogICAgICApLAogICAgICBib2R5OiBTYWZlQXJlYSgKICAgICAgICBjaGlsZDogUGFkZGluZygKICAgICAgICAgIHBhZGRpbmc6IGNvbnN0IEVkZ2VJbnNldHMuYWxsKDE2LjApLAogICAgICAgICAgY2hpbGQ6IENlbnRlcigKICAgICAgICAgICAgY2hpbGQ6IFNpbmdsZUNoaWxkU2Nyb2xsVmlldygKICAgICAgICAgICAgICBjaGlsZDogQ29sdW1uKAogICAgICAgICAgICAgICAgbWFpbkF4aXNBbGlnbm1lbnQ6IE1haW5BeGlzQWxpZ25tZW50LmNlbnRlciwKICAgICAgICAgICAgICAgIGNoaWxkcmVuOiBbCiAgICAgICAgICAgICAgICAgIENvbnRhaW5lcigKICAgICAgICAgICAgICAgICAgICB3aWR0aDogODAsCiAgICAgICAgICAgICAgICAgICAgaGVpZ2h0OiA4MCwKICAgICAgICAgICAgICAgICAgICBkZWNvcmF0aW9uOiBCb3hEZWNvcmF0aW9uKAogICAgICAgICAgICAgICAgICAgICAgY29sb3I6IGNvbG9yU2NoZW1lLmVycm9yQ29udGFpbmVyLndpdGhWYWx1ZXMoYWxwaGE6IDAuMTUpLAogICAgICAgICAgICAgICAgICAgICAgYm9yZGVyUmFkaXVzOiBCb3JkZXJSYWRpdXMuY2lyY3VsYXIoMTYpLAogICAgICAgICAgICAgICAgICAgICAgYm9yZGVyOiBCb3JkZXIuYWxsKAogICAgICAgICAgICAgICAgICAgICAgICBjb2xvcjogY29sb3JTY2hlbWUuZXJyb3Iud2l0aFZhbHVlcyhhbHBoYTogMC4yKSwKICAgICAgICAgICAgICAgICAgICAgICAgd2lkdGg6IDEsCiAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgY2hpbGQ6IEljb24oCiAgICAgICAgICAgICAgICAgICAgICBJY29ucy5lcnJvcl9vdXRsaW5lX3JvdW5kZWQsCiAgICAgICAgICAgICAgICAgICAgICBzaXplOiA0MCwKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yOiBjb2xvclNjaGVtZS5lcnJvciwKICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICBjb25zdCBTaXplZEJveChoZWlnaHQ6IDI0KSwKICAgICAgICAgICAgICAgICAgVGV4dCgKICAgICAgICAgICAgICAgICAgICByZXNvbHZlZFRpdGxlLAogICAgICAgICAgICAgICAgICAgIHN0eWxlOiB0ZXh0VGhlbWUudGl0bGVMYXJnZT8uY29weVdpdGgoCiAgICAgICAgICAgICAgICAgICAgICBjb2xvcjogY29sb3JTY2hlbWUub25TdXJmYWNlLAogICAgICAgICAgICAgICAgICAgICAgZm9udFdlaWdodDogRm9udFdlaWdodC53NjAwLAogICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgdGV4dEFsaWduOiBUZXh0QWxpZ24uY2VudGVyLAogICAgICAgICAgICAgICAgICAgIG1heExpbmVzOiAyLAogICAgICAgICAgICAgICAgICAgIG92ZXJmbG93OiBUZXh0T3ZlcmZsb3cuZWxsaXBzaXMsCiAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgIGNvbnN0IFNpemVkQm94KGhlaWdodDogOCksCiAgICAgICAgICAgICAgICAgIFRleHQoCiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZWRNZXNzYWdlLAogICAgICAgICAgICAgICAgICAgIHN0eWxlOiB0ZXh0VGhlbWUuYm9keU1lZGl1bT8uY29weVdpdGgoCiAgICAgICAgICAgICAgICAgICAgICBjb2xvcjogY29sb3JTY2hlbWUub25TdXJmYWNlVmFyaWFudCwKICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgIHRleHRBbGlnbjogVGV4dEFsaWduLmNlbnRlciwKICAgICAgICAgICAgICAgICAgICBtYXhMaW5lczogMywKICAgICAgICAgICAgICAgICAgICBvdmVyZmxvdzogVGV4dE92ZXJmbG93LmVsbGlwc2lzLAogICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICBpZiAoZXJyb3IgIT0gbnVsbCkgLi4uWwogICAgICAgICAgICAgICAgICAgIGNvbnN0IFNpemVkQm94KGhlaWdodDogMTYpLAogICAgICAgICAgICAgICAgICAgIENvbnRhaW5lcigKICAgICAgICAgICAgICAgICAgICAgIHdpZHRoOiBkb3VibGUuaW5maW5pdHksCiAgICAgICAgICAgICAgICAgICAgICBwYWRkaW5nOiBjb25zdCBFZGdlSW5zZXRzLmFsbCgxMiksCiAgICAgICAgICAgICAgICAgICAgICBkZWNvcmF0aW9uOiBCb3hEZWNvcmF0aW9uKAogICAgICAgICAgICAgICAgICAgICAgICBjb2xvcjogY29sb3JTY2hlbWUuc3VyZmFjZUNvbnRhaW5lckhpZ2gsCiAgICAgICAgICAgICAgICAgICAgICAgIGJvcmRlclJhZGl1czogQm9yZGVyUmFkaXVzLmNpcmN1bGFyKDEyKSwKICAgICAgICAgICAgICAgICAgICAgICAgYm9yZGVyOiBCb3JkZXIuYWxsKAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yOiBjb2xvclNjaGVtZS5vdXRsaW5lLndpdGhWYWx1ZXMoYWxwaGE6IDAuMyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgd2lkdGg6IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgY2hpbGQ6IFNlbGVjdGFibGVUZXh0KAogICAgICAgICAgICAgICAgICAgICAgICBlcnJvciEsCiAgICAgICAgICAgICAgICAgICAgICAgIHN0eWxlOiB0ZXh0VGhlbWUuYm9keVNtYWxsPy5jb3B5V2l0aCgKICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcjogY29sb3JTY2hlbWUub25TdXJmYWNlLAogICAgICAgICAgICAgICAgICAgICAgICAgIGZvbnRGYW1pbHk6ICdtb25vc3BhY2UnLAogICAgICAgICAgICAgICAgICAgICAgICAgIGZvbnRTaXplOiAxMiwKICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgdGV4dEFsaWduOiBUZXh0QWxpZ24ubGVmdCwKICAgICAgICAgICAgICAgICAgICAgICAgbWF4TGluZXM6IDQsCiAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgIF0sCiAgICAgICAgICAgICAgICAgIGNvbnN0IFNpemVkQm94KGhlaWdodDogMzIpLAogICAgICAgICAgICAgICAgICBXcmFwKAogICAgICAgICAgICAgICAgICAgIHNwYWNpbmc6IDEyLAogICAgICAgICAgICAgICAgICAgIHJ1blNwYWNpbmc6IDgsCiAgICAgICAgICAgICAgICAgICAgYWxpZ25tZW50OiBXcmFwQWxpZ25tZW50LmNlbnRlciwKICAgICAgICAgICAgICAgICAgICBjaGlsZHJlbjogWwogICAgICAgICAgICAgICAgICAgICAgaWYgKG9uUmV0cnkgIT0gbnVsbCkKICAgICAgICAgICAgICAgICAgICAgICAgRmlsbGVkQnV0dG9uKAogICAgICAgICAgICAgICAgICAgICAgICAgIG9uUHJlc3NlZDogb25SZXRyeSwKICAgICAgICAgICAgICAgICAgICAgICAgICBzdHlsZTogRmlsbGVkQnV0dG9uLnN0eWxlRnJvbSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhZGRpbmc6IGNvbnN0IEVkZ2VJbnNldHMuc3ltbWV0cmljKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3Jpem9udGFsOiAyNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGljYWw6IDEyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbmltdW1TaXplOiBjb25zdCBTaXplKDg4LCA0NCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjaGlsZDogVGV4dChzdHJpbmdzLmdlbmVyaWMudHJ5X2FnYWluKSwKICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgIGlmIChvbkdvSG9tZSAhPSBudWxsKQogICAgICAgICAgICAgICAgICAgICAgICBPdXRsaW5lZEJ1dHRvbigKICAgICAgICAgICAgICAgICAgICAgICAgICBvblByZXNzZWQ6IG9uR29Ib21lLAogICAgICAgICAgICAgICAgICAgICAgICAgIHN0eWxlOiBPdXRsaW5lZEJ1dHRvbi5zdHlsZUZyb20oCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYWRkaW5nOiBjb25zdCBFZGdlSW5zZXRzLnN5bW1ldHJpYygKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG9yaXpvbnRhbDogMjQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRpY2FsOiAxMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5pbXVtU2l6ZTogY29uc3QgU2l6ZSg4OCwgNDQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lkZTogQm9yZGVyU2lkZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I6IGNvbG9yU2NoZW1lLm91dGxpbmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdpZHRoOiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNoaWxkOiBUZXh0KHN0cmluZ3MuZ2VuZXJpYy5yZXRyeSksCiAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgXSwKICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgIF0sCiAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgKSwKICAgICAgICAgICksCiAgICAgICAgKSwKICAgICAgKSwKICAgICk7CiAgfQp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/screens/index.dart", + "data": "ZXhwb3J0ICdlcnJvcl9zY3JlZW4uZGFydCc7Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/toast/toasts.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CmltcG9ydCAncGFja2FnZTppY29uc2F4X2ZsdXR0ZXIvaWNvbnNheF9mbHV0dGVyLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6dG9hc3RpZmljYXRpb24vdG9hc3RpZmljYXRpb24uZGFydCc7CgpjbGFzcyBUb2FzdCB7CiAgc3RhdGljIHZvaWQgbm90aWZpY2F0aW9uKHtyZXF1aXJlZCBTdHJpbmcgdGl0bGUsIHJlcXVpcmVkIFN0cmluZyBib2R5fSkgPT4KICAgICAgdG9hc3RpZmljYXRpb24uc2hvdygKICAgICAgICB0eXBlOiBUb2FzdGlmaWNhdGlvblR5cGUuaW5mbywKICAgICAgICBzdHlsZTogVG9hc3RpZmljYXRpb25TdHlsZS5maWxsQ29sb3JlZCwKICAgICAgICB0aXRsZTogVGV4dCh0aXRsZSksCiAgICAgICAgZGVzY3JpcHRpb246IFRleHQoYm9keSksCiAgICAgICAgYWxpZ25tZW50OiBBbGlnbm1lbnQudG9wQ2VudGVyLAogICAgICAgIGF1dG9DbG9zZUR1cmF0aW9uOiBjb25zdCBEdXJhdGlvbihzZWNvbmRzOiAzLCBtaWxsaXNlY29uZHM6IDUwMCksCiAgICAgICAgYW5pbWF0aW9uQnVpbGRlcjogKGNvbnRleHQsIGFuaW1hdGlvbiwgYWxpZ25tZW50LCBjaGlsZCkgewogICAgICAgICAgcmV0dXJuIFNjYWxlVHJhbnNpdGlvbihzY2FsZTogYW5pbWF0aW9uLCBjaGlsZDogY2hpbGQpOwogICAgICAgIH0sCiAgICAgICAgaWNvbjogSWNvbihJY29uc2F4Lm5vdGlmaWNhdGlvbiksCiAgICAgICAgYm9yZGVyUmFkaXVzOiBCb3JkZXJSYWRpdXMuY2lyY3VsYXIoMTIuMCksCiAgICAgICAgc2hvd1Byb2dyZXNzQmFyOiB0cnVlLAogICAgICAgIGRyYWdUb0Nsb3NlOiB0cnVlLAogICAgICAgIGFwcGx5Qmx1ckVmZmVjdDogdHJ1ZSwKICAgICAgKTsKCiAgc3RhdGljIHZvaWQgZXJyb3IoCiAgICBCdWlsZENvbnRleHQgY29udGV4dCwgewogICAgcmVxdWlyZWQgU3RyaW5nIG1lc3NhZ2UsCiAgICBTdHJpbmc/IGRlc2NyaXB0aW9uLAogIH0pID0+IHRvYXN0aWZpY2F0aW9uLnNob3coCiAgICBjb250ZXh0OiBjb250ZXh0LAogICAgdHlwZTogVG9hc3RpZmljYXRpb25UeXBlLmVycm9yLAogICAgc3R5bGU6IFRvYXN0aWZpY2F0aW9uU3R5bGUuZmlsbENvbG9yZWQsCiAgICB0aXRsZTogVGV4dChtZXNzYWdlKSwKICAgIGRlc2NyaXB0aW9uOiBkZXNjcmlwdGlvbiAhPSBudWxsID8gVGV4dChkZXNjcmlwdGlvbikgOiBudWxsLAogICAgYWxpZ25tZW50OiBBbGlnbm1lbnQudG9wQ2VudGVyLAogICAgYXV0b0Nsb3NlRHVyYXRpb246IGNvbnN0IER1cmF0aW9uKHNlY29uZHM6IDMsIG1pbGxpc2Vjb25kczogNTAwKSwKICAgIGFuaW1hdGlvbkJ1aWxkZXI6IChjb250ZXh0LCBhbmltYXRpb24sIGFsaWdubWVudCwgY2hpbGQpIHsKICAgICAgcmV0dXJuIFNjYWxlVHJhbnNpdGlvbihzY2FsZTogYW5pbWF0aW9uLCBjaGlsZDogY2hpbGQpOwogICAgfSwKICAgIGljb246IEljb24oSWNvbnNheC53YXJuaW5nXzIpLAogICAgYm9yZGVyUmFkaXVzOiBCb3JkZXJSYWRpdXMuY2lyY3VsYXIoMTIuMCksCiAgICBzaG93UHJvZ3Jlc3NCYXI6IHRydWUsCiAgICBkcmFnVG9DbG9zZTogdHJ1ZSwKICAgIGFwcGx5Qmx1ckVmZmVjdDogdHJ1ZSwKICApOwoKICBzdGF0aWMgdm9pZCBzdWNjZXNzKAogICAgQnVpbGRDb250ZXh0IGNvbnRleHQsIHsKICAgIHJlcXVpcmVkIFN0cmluZyBtZXNzYWdlLAogICAgU3RyaW5nPyBkZXNjcmlwdGlvbiwKICB9KSA9PiB0b2FzdGlmaWNhdGlvbi5zaG93KAogICAgY29udGV4dDogY29udGV4dCwKICAgIHR5cGU6IFRvYXN0aWZpY2F0aW9uVHlwZS5zdWNjZXNzLAogICAgc3R5bGU6IFRvYXN0aWZpY2F0aW9uU3R5bGUuZmlsbENvbG9yZWQsCiAgICB0aXRsZTogVGV4dChtZXNzYWdlKSwKICAgIGRlc2NyaXB0aW9uOiBkZXNjcmlwdGlvbiAhPSBudWxsID8gVGV4dChkZXNjcmlwdGlvbikgOiBudWxsLAogICAgYWxpZ25tZW50OiBBbGlnbm1lbnQudG9wQ2VudGVyLAogICAgYXV0b0Nsb3NlRHVyYXRpb246IGNvbnN0IER1cmF0aW9uKHNlY29uZHM6IDMsIG1pbGxpc2Vjb25kczogNTAwKSwKICAgIGFuaW1hdGlvbkJ1aWxkZXI6IChjb250ZXh0LCBhbmltYXRpb24sIGFsaWdubWVudCwgY2hpbGQpIHsKICAgICAgcmV0dXJuIFNjYWxlVHJhbnNpdGlvbihzY2FsZTogYW5pbWF0aW9uLCBjaGlsZDogY2hpbGQpOwogICAgfSwKICAgIGljb246IEljb24oSWNvbnNheC50aWNrX2NpcmNsZSksCiAgICBib3JkZXJSYWRpdXM6IEJvcmRlclJhZGl1cy5jaXJjdWxhcigxMi4wKSwKICAgIHNob3dQcm9ncmVzc0JhcjogdHJ1ZSwKICAgIGRyYWdUb0Nsb3NlOiB0cnVlLAogICAgYXBwbHlCbHVyRWZmZWN0OiB0cnVlLAogICk7CgogIHN0YXRpYyB2b2lkIHdhcm5pbmcoCiAgICBCdWlsZENvbnRleHQgY29udGV4dCwgewogICAgcmVxdWlyZWQgU3RyaW5nIG1lc3NhZ2UsCiAgICBTdHJpbmc/IGRlc2NyaXB0aW9uLAogIH0pID0+IHRvYXN0aWZpY2F0aW9uLnNob3coCiAgICBjb250ZXh0OiBjb250ZXh0LAogICAgdHlwZTogVG9hc3RpZmljYXRpb25UeXBlLndhcm5pbmcsCiAgICBzdHlsZTogVG9hc3RpZmljYXRpb25TdHlsZS5maWxsQ29sb3JlZCwKICAgIHRpdGxlOiBUZXh0KG1lc3NhZ2UpLAogICAgZGVzY3JpcHRpb246IGRlc2NyaXB0aW9uICE9IG51bGwgPyBUZXh0KGRlc2NyaXB0aW9uKSA6IG51bGwsCiAgICBhbGlnbm1lbnQ6IEFsaWdubWVudC50b3BDZW50ZXIsCiAgICBhdXRvQ2xvc2VEdXJhdGlvbjogY29uc3QgRHVyYXRpb24oc2Vjb25kczogMywgbWlsbGlzZWNvbmRzOiA1MDApLAogICAgYW5pbWF0aW9uQnVpbGRlcjogKGNvbnRleHQsIGFuaW1hdGlvbiwgYWxpZ25tZW50LCBjaGlsZCkgewogICAgICByZXR1cm4gU2NhbGVUcmFuc2l0aW9uKHNjYWxlOiBhbmltYXRpb24sIGNoaWxkOiBjaGlsZCk7CiAgICB9LAogICAgaWNvbjogSWNvbihJY29uc2F4LmRhbmdlciksCiAgICBib3JkZXJSYWRpdXM6IEJvcmRlclJhZGl1cy5jaXJjdWxhcigxMi4wKSwKICAgIHNob3dQcm9ncmVzc0JhcjogdHJ1ZSwKICAgIGRyYWdUb0Nsb3NlOiB0cnVlLAogICAgYXBwbHlCbHVyRWZmZWN0OiB0cnVlLAogICk7Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/utils/extensions/index.dart", + "data": + "Ly9HRU5FUkFURUQgQkFSUkVMIEZJTEUKZXhwb3J0ICdzdHJpbmcuZGFydCc7Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/utils/extensions/string.dart", + "data": + "Ly8vIFN0cmluZyBleHRlbnNpb25zIGZvciBjb21tb24gdXRpbGl0eSBvcGVyYXRpb25zCmV4dGVuc2lvbiBTdHJpbmdFeHRlbnNpb25zIG9uIFN0cmluZz8gewogIC8vLyBSZXR1cm5zIHRydWUgaWYgdGhlIHN0cmluZyBpcyBub3QgbnVsbCBhbmQgbm90IGVtcHR5CiAgLy8vCiAgLy8vIEV4YW1wbGU6CiAgLy8vIGBgYGRhcnQKICAvLy8gU3RyaW5nPyB0ZXh0ID0gIkhlbGxvIjsKICAvLy8gcHJpbnQodGV4dC5pc05vdE51bGxBbmROb3RFbXB0eSk7IC8vIHRydWUKICAvLy8KICAvLy8gU3RyaW5nPyBlbXB0eVRleHQgPSAiIjsKICAvLy8gcHJpbnQoZW1wdHlUZXh0LmlzTm90TnVsbEFuZE5vdEVtcHR5KTsgLy8gZmFsc2UKICAvLy8KICAvLy8gU3RyaW5nPyBudWxsVGV4dCA9IG51bGw7CiAgLy8vIHByaW50KG51bGxUZXh0LmlzTm90TnVsbEFuZE5vdEVtcHR5KTsgLy8gZmFsc2UKICAvLy8gYGBgCiAgYm9vbCBnZXQgaXNOb3ROdWxsQW5kTm90RW1wdHkgewogICAgcmV0dXJuIHRoaXMgIT0gbnVsbCAmJiB0aGlzIS5pc05vdEVtcHR5OwogIH0KCiAgLy8vIFJldHVybnMgYW4gZW1wdHkgc3RyaW5nIGlmIHRoZSBzdHJpbmcgaXMgbnVsbCwgb3RoZXJ3aXNlIHJldHVybnMgdGhlIHN0cmluZyBpdHNlbGYKICAvLy8KICAvLy8gRXhhbXBsZToKICAvLy8gYGBgZGFydAogIC8vLyBTdHJpbmc/IHRleHQgPSAiSGVsbG8iOwogIC8vLyBwcmludCh0ZXh0LmFzRW1wdHlJZk51bGwpOyAvLyAiSGVsbG8iCiAgLy8vCiAgLy8vIFN0cmluZz8gbnVsbFRleHQgPSBudWxsOwogIC8vLyBwcmludChudWxsVGV4dC5hc0VtcHR5SWZOdWxsKTsgLy8gIiIKICAvLy8gYGBgCiAgU3RyaW5nIGdldCBhc0VtcHR5SWZOdWxsIHsKICAgIHJldHVybiB0aGlzID8/ICcnOwogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/utils/index.dart", + "data": + "Ly9HRU5FUkFURUQgQkFSUkVMIEZJTEUKZXhwb3J0ICdleHRlbnNpb25zL2luZGV4LmRhcnQnOwo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/lib/wrapper/wrappers.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmRlc2lnbl9zeXN0ZW0vZGVzaWduX3N5c3RlbS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CmltcG9ydCAncGFja2FnZTpsb2FkZXJfb3ZlcmxheS9sb2FkZXJfb3ZlcmxheS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOnRvYXN0aWZpY2F0aW9uL3RvYXN0aWZpY2F0aW9uLmRhcnQnOwoKY2xhc3MgRGVzaWduU3lzdGVtV3JhcHBlciBleHRlbmRzIFN0YXRlZnVsV2lkZ2V0IHsKICBjb25zdCBEZXNpZ25TeXN0ZW1XcmFwcGVyKHtzdXBlci5rZXksIHJlcXVpcmVkIHRoaXMuYnVpbGRlcn0pOwoKICBmaW5hbCBXaWRnZXQgRnVuY3Rpb24oQnVpbGRDb250ZXh0IGNvbnRleHQsIFRoZW1lRGF0YSB0aGVtZSkgYnVpbGRlcjsKCiAgQG92ZXJyaWRlCiAgU3RhdGU8RGVzaWduU3lzdGVtV3JhcHBlcj4gY3JlYXRlU3RhdGUoKSA9PiBfRGVzaWduU3lzdGVtV3JhcHBlclN0YXRlKCk7Cn0KCmNsYXNzIF9EZXNpZ25TeXN0ZW1XcmFwcGVyU3RhdGUgZXh0ZW5kcyBTdGF0ZTxEZXNpZ25TeXN0ZW1XcmFwcGVyPgogICAgd2l0aCBXaWRnZXRzQmluZGluZ09ic2VydmVyIHsKICBAb3ZlcnJpZGUKICB2b2lkIGluaXRTdGF0ZSgpIHsKICAgIHN1cGVyLmluaXRTdGF0ZSgpOwogICAgV2lkZ2V0c0JpbmRpbmcuaW5zdGFuY2UuYWRkT2JzZXJ2ZXIodGhpcyk7CiAgfQoKICBAb3ZlcnJpZGUKICB2b2lkIGRpc3Bvc2UoKSB7CiAgICBXaWRnZXRzQmluZGluZy5pbnN0YW5jZS5yZW1vdmVPYnNlcnZlcih0aGlzKTsKICAgIHN1cGVyLmRpc3Bvc2UoKTsKICB9CgogIEBvdmVycmlkZQogIHZvaWQgZGlkQ2hhbmdlUGxhdGZvcm1CcmlnaHRuZXNzKCkgewogICAgc2V0U3RhdGUoKCkge30pOwogIH0KCiAgQG92ZXJyaWRlCiAgV2lkZ2V0IGJ1aWxkKEJ1aWxkQ29udGV4dCBjb250ZXh0KSB7CiAgICBmaW5hbCBicmlnaHRuZXNzID0gTWVkaWFRdWVyeS5wbGF0Zm9ybUJyaWdodG5lc3NPZihjb250ZXh0KTsKICAgIGZpbmFsIG1hdGVyaWFsVGhlbWUgPSBEZXNpZ25TeXN0ZW0oY29udGV4dCk7CiAgICBmaW5hbCB0aGVtZSA9IGJyaWdodG5lc3MgIT0gQnJpZ2h0bmVzcy5kYXJrCiAgICAgICAgPyBtYXRlcmlhbFRoZW1lLmxpZ2h0KCkKICAgICAgICA6IG1hdGVyaWFsVGhlbWUuZGFyaygpOwoKICAgIHJldHVybiBUaGVtZSgKICAgICAgZGF0YTogdGhlbWUsCiAgICAgIGNoaWxkOiBHbG9iYWxMb2FkZXJPdmVybGF5KAogICAgICAgIG92ZXJsYXlDb2xvcjogdGhlbWUuY29sb3JTY2hlbWUuc3VyZmFjZS53aXRoVmFsdWVzKGFscGhhOiAwLjgpLAogICAgICAgIGR1cmF0aW9uOiBjb25zdCBEdXJhdGlvbihtaWxsaXNlY29uZHM6IDMwMCksCiAgICAgICAgcmV2ZXJzZUR1cmF0aW9uOiBjb25zdCBEdXJhdGlvbihtaWxsaXNlY29uZHM6IDIwMCksCiAgICAgICAgc3dpdGNoSW5DdXJ2ZTogQ3VydmVzLmVhc2VJbk91dCwKICAgICAgICBzd2l0Y2hPdXRDdXJ2ZTogQ3VydmVzLmVhc2VJbk91dCwKICAgICAgICBjaGlsZDogVG9hc3RpZmljYXRpb25XcmFwcGVyKGNoaWxkOiB3aWRnZXQuYnVpbGRlcihjb250ZXh0LCB0aGVtZSkpLAogICAgICApLAogICAgKTsKICB9Cn0K", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/design_system/pubspec.yaml", + "data": + "bmFtZTogZGVzaWduX3N5c3RlbQpkZXNjcmlwdGlvbjogIlRoZW1lLCBjb21wb25lbnRzIGFuZCBkaWFsb2dzIHNoYXJlZCBhY3Jvc3Mge3tuYW1lLnRpdGxlQ2FzZSgpfX0uIgp2ZXJzaW9uOiAwLjEuMApwdWJsaXNoX3RvOiAnbm9uZScKCmVudmlyb25tZW50OgogIHNkazogXjMuOC4wCiAgZmx1dHRlcjogIj49My4yOS4wIgoKZGVwZW5kZW5jaWVzOgogIGZsdXR0ZXI6CiAgICBzZGs6IGZsdXR0ZXIKICBjYWNoZWRfbmV0d29ya19pbWFnZTogXjMuNC4xCiAgZmx1dHRlcl9zdmc6IF4yLjIuMAogIGdvb2dsZV9mb250czogXjYuMi4xCiAgaWNvbnNheF9mbHV0dGVyOiBeMS4wLjEKICBsb2FkZXJfb3ZlcmxheTogXjUuMC4wCiAgdG9hc3RpZmljYXRpb246IF4zLjAuMwogIGNvcmU6CiAgICBwYXRoOiAuLi9jb3JlCiAgZGk6CiAgICBwYXRoOiAuLi9kaQogIGxvY2FsaXphdGlvbjoKICAgIHBhdGg6IC4uL2xvY2FsaXphdGlvbgp7eyNuZXR3b3JrfX0gIG5ldHdvcms6CiAgICBwYXRoOiAuLi9uZXR3b3JrCnt7L25ldHdvcmt9fQpkZXZfZGVwZW5kZW5jaWVzOgogIGZsdXR0ZXJfdGVzdDoKICAgIHNkazogZmx1dHRlcgogIGZsdXR0ZXJfbGludHM6IF41LjAuMAoKZmx1dHRlcjoK", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/design_system/README.md", + "data": + "IyBEZXNpZ24gU3lzdGVtCgpBIE1lbG9zLW1hbmFnZWQgcHJvamVjdCBmb3IgbW9uby1yZXBvLCBjcmVhdGVkIHVzaW5nIE5vblN0b3AgQ0xJLgoKWyFbbm9uc3RvcF9jbGldKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2Uvc3RhcnRlZCUyMHdpdGgtbm9uc3RvcF9jbGktMTY2QzRFLnN2Zz9zdHlsZT1mbGF0LXNxdWFyZSldKGh0dHBzOi8vcHViLmRldi9wYWNrYWdlcy9ub25zdG9wX2NsaSkKWyFbbWVsb3NdKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2UvbWFpbnRhaW5lZCUyMHdpdGgtbWVsb3MtZjcwMGZmLnN2Zz9zdHlsZT1mbGF0LXNxdWFyZSldKGh0dHBzOi8vZ2l0aHViLmNvbS9pbnZlcnRhc2UvbWVsb3MpCgojIyBJbnN0YWxsYXRpb24g8J+SuwoKKirinZcgSW4gb3JkZXIgdG8gc3RhcnQgdXNpbmcgRGVzaWduIFN5c3RlbSB5b3UgbXVzdCBoYXZlIHRoZSBbRmx1dHRlciBTREtdW2ZsdXR0ZXJfaW5zdGFsbF9saW5rXSBpbnN0YWxsZWQgb24geW91ciBtYWNoaW5lLioqCgpJbnN0YWxsIHZpYSBgZGFydCBwdWIgYWRkYDoKCmBgYHNoCmRhcnQgcHViIGFkZCBkZXNpZ25fc3lzdGVtCmBgYAoKW2ZsdXR0ZXJfaW5zdGFsbF9saW5rXTogaHR0cHM6Ly9kb2NzLmZsdXR0ZXIuZGV2L2dldC1zdGFydGVkL2luc3RhbGw=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/design_system/test/design_system_test.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmRlc2lnbl9zeXN0ZW0vZGVzaWduX3N5c3RlbS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CmltcG9ydCAncGFja2FnZTpmbHV0dGVyX3Rlc3QvZmx1dHRlcl90ZXN0LmRhcnQnOwoKdm9pZCBtYWluKCkgewogIHRlc3RXaWRnZXRzKCdEZXNpZ25TeXN0ZW1XcmFwcGVyIGhhbmRzIHRoZSBidWlsZGVyIGEgdGhlbWVkIGNvbnRleHQnLCAoCiAgICB0ZXN0ZXIsCiAgKSBhc3luYyB7CiAgICBsYXRlIFRoZW1lRGF0YSBidWlsdDsKCiAgICBhd2FpdCB0ZXN0ZXIucHVtcFdpZGdldCgKICAgICAgTWF0ZXJpYWxBcHAoCiAgICAgICAgaG9tZTogRGVzaWduU3lzdGVtV3JhcHBlcigKICAgICAgICAgIGJ1aWxkZXI6IChjb250ZXh0LCB0aGVtZSkgewogICAgICAgICAgICBidWlsdCA9IHRoZW1lOwogICAgICAgICAgICByZXR1cm4gY29uc3QgU2l6ZWRCb3guc2hyaW5rKCk7CiAgICAgICAgICB9LAogICAgICAgICksCiAgICAgICksCiAgICApOwoKICAgIGV4cGVjdChidWlsdC51c2VNYXRlcmlhbDMsIGlzVHJ1ZSk7CiAgICBleHBlY3QoYnVpbHQuY29sb3JTY2hlbWUucHJpbWFyeSwgaXNOb3QoQ29sb3JzLnRyYW5zcGFyZW50KSk7CiAgfSk7Cn0K", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/di/lib/di.dart", + "data": + "Ly8vIERlcGVuZGVuY3kgSW5qZWN0aW9uIHBhY2thZ2UgZm9yIHt7bmFtZS50aXRsZUNhc2UoKX19Ci8vLwovLy8gVGhpcyBwYWNrYWdlIHByb3ZpZGVzIGEgY2xlYW4gYWJzdHJhY3Rpb24gb3ZlciBkZXBlbmRlbmN5IGluamVjdGlvbgovLy8gdXNpbmcgR2V0SXQgYXMgdGhlIHVuZGVybHlpbmcgaW1wbGVtZW50YXRpb24uCmxpYnJhcnk7CgppbXBvcnQgJ3BhY2thZ2U6ZGkvZGkuZGFydCc7CgovLyBDb3JlIGludGVyZmFjZXMKZXhwb3J0ICdzcmMvZGVwZW5kZW5jeV9pbmplY3Rpb24uZGFydCc7Ci8vIEdldEl0IGltcGxlbWVudGF0aW9uCmV4cG9ydCAnc3JjL2RlcGVuZGVuY3lfaW5qZWN0aW9uX2ltcC5kYXJ0JzsKCmZpbmFsIGRpID0gR2V0SXREZXBlbmRlbmN5SW5qZWN0aW9uKCk7Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/di/lib/src/dependency_injection.dart", + "data": + "aW1wb3J0ICdkYXJ0OmFzeW5jJzsKCnR5cGVkZWYgRGlzcG9zZUZ1bmM8VD4gPSBGdXR1cmVPciBGdW5jdGlvbihUIHBhcmFtKTsKCmFic3RyYWN0IGNsYXNzIERlcGVuZGVuY3lJbmplY3Rpb24gewogIEZ1dHVyZTx2b2lkPiBpbml0KCk7CgogIEZ1dHVyZTx2b2lkPiBkaXNwb3NlKCk7CgogIHZvaWQgcmVnaXN0ZXI8VCBleHRlbmRzIE9iamVjdD4oCiAgICBmaW5hbCBUIGluc3RhbmNlLCB7CiAgICBmaW5hbCBEaXNwb3NlRnVuYz8gZGlzcG9zZSwKICB9KTsKCiAgRnV0dXJlPHZvaWQ+IHVucmVnaXN0ZXI8VCBleHRlbmRzIE9iamVjdD4oZmluYWwgVD8gaW5zdGFuY2UpOwoKICBUIGdldDxUIGV4dGVuZHMgT2JqZWN0PigpOwoKICBib29sIGhhczxUIGV4dGVuZHMgT2JqZWN0PigpOwoKICBGdXR1cmU8dm9pZD4gcmVzZXQoKTsKfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/di/lib/src/dependency_injection_imp.dart", + "data": + "aW1wb3J0ICdkYXJ0OmFzeW5jJzsKCmltcG9ydCAnZGFydDpkZXZlbG9wZXInIGFzIGRldjsKCmltcG9ydCAncGFja2FnZTpkaS9zcmMvZGVwZW5kZW5jeV9pbmplY3Rpb24uZGFydCc7CmltcG9ydCAncGFja2FnZTpnZXRfaXQvZ2V0X2l0LmRhcnQnOwoKLy8vIEltcGxlbWVudGF0aW9uIG9mIERlcGVuZGVuY3lJbmplY3Rpb24gdXNpbmcgR2V0SXQKY2xhc3MgR2V0SXREZXBlbmRlbmN5SW5qZWN0aW9uIGltcGxlbWVudHMgRGVwZW5kZW5jeUluamVjdGlvbiB7CiAgZmluYWwgR2V0SXQgX2dldEl0OwogIGZpbmFsIE1hcDxUeXBlLCBEaXNwb3NlRnVuYz4gX2Rpc3Bvc2VGdW5jdGlvbnMgPSB7fTsKICBmaW5hbCBNYXA8VHlwZSwgT2JqZWN0PiBfcmVnaXN0ZXJlZEluc3RhbmNlcyA9IHt9OwoKICAvLy8gQ3JlYXRlcyBhIG5ldyBpbnN0YW5jZSB3aXRoIG9wdGlvbmFsIEdldEl0IGluc3RhbmNlCiAgLy8vIElmIG5vIGluc3RhbmNlIGlzIHByb3ZpZGVkLCB1c2VzIEdldEl0Lmluc3RhbmNlCiAgR2V0SXREZXBlbmRlbmN5SW5qZWN0aW9uKHtHZXRJdD8gZ2V0SXR9KSA6IF9nZXRJdCA9IGdldEl0ID8/IEdldEl0Lmluc3RhbmNlOwoKICBAb3ZlcnJpZGUKICBGdXR1cmU8dm9pZD4gaW5pdCgpIGFzeW5jIHsKICAgIC8vIEdldEl0IGRvZXNuJ3QgcmVxdWlyZSBleHBsaWNpdCBpbml0aWFsaXphdGlvbgogICAgLy8gYnV0IHdlIGNhbiB1c2UgdGhpcyB0byBzZXQgdXAgYW55IGdsb2JhbCBjb25maWd1cmF0aW9ucwogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IGRpc3Bvc2UoKSBhc3luYyB7CiAgICAvLyBDYWxsIGRpc3Bvc2UgZnVuY3Rpb25zIGZvciBhbGwgcmVnaXN0ZXJlZCBpbnN0YW5jZXMKICAgIGZvciAoZmluYWwgZW50cnkgaW4gX2Rpc3Bvc2VGdW5jdGlvbnMuZW50cmllcykgewogICAgICBmaW5hbCB0eXBlID0gZW50cnkua2V5OwogICAgICBmaW5hbCBkaXNwb3NlRnVuYyA9IGVudHJ5LnZhbHVlOwogICAgICBmaW5hbCBpbnN0YW5jZSA9IF9yZWdpc3RlcmVkSW5zdGFuY2VzW3R5cGVdOwoKICAgICAgaWYgKGluc3RhbmNlICE9IG51bGwpIHsKICAgICAgICB0cnkgewogICAgICAgICAgYXdhaXQgZGlzcG9zZUZ1bmMoaW5zdGFuY2UpOwogICAgICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgICAgIC8vIExvZyBlcnJvciBidXQgY29udGludWUgZGlzcG9zaW5nIG90aGVyIGluc3RhbmNlcwogICAgICAgICAgZGV2LmxvZygKICAgICAgICAgICAgJ0Vycm9yIGRpc3Bvc2luZyBpbnN0YW5jZSBvZiB0eXBlICR0eXBlJywKICAgICAgICAgICAgbmFtZTogJ2RpJywKICAgICAgICAgICAgZXJyb3I6IGUsCiAgICAgICAgICApOwogICAgICAgIH0KICAgICAgfQogICAgfQoKICAgIC8vIENsZWFyIGRpc3Bvc2UgZnVuY3Rpb25zIGFuZCByZWdpc3RlcmVkIGluc3RhbmNlcyBtYXBzCiAgICBfZGlzcG9zZUZ1bmN0aW9ucy5jbGVhcigpOwogICAgX3JlZ2lzdGVyZWRJbnN0YW5jZXMuY2xlYXIoKTsKCiAgICAvLyBSZXNldCBHZXRJdCBpbnN0YW5jZQogICAgYXdhaXQgX2dldEl0LnJlc2V0KCk7CiAgfQoKICBAb3ZlcnJpZGUKICB2b2lkIHJlZ2lzdGVyPFQgZXh0ZW5kcyBPYmplY3Q+KAogICAgZmluYWwgVCBpbnN0YW5jZSwgewogICAgZmluYWwgRGlzcG9zZUZ1bmM/IGRpc3Bvc2UsCiAgfSkgewogICAgLy8gUmVnaXN0ZXIgdGhlIGluc3RhbmNlIGFzIGEgc2luZ2xldG9uCiAgICBfZ2V0SXQucmVnaXN0ZXJTaW5nbGV0b248VD4oaW5zdGFuY2UpOwoKICAgIC8vIFN0b3JlIHRoZSBpbnN0YW5jZSBhbmQgZGlzcG9zZSBmdW5jdGlvbiBpZiBwcm92aWRlZAogICAgX3JlZ2lzdGVyZWRJbnN0YW5jZXNbVF0gPSBpbnN0YW5jZTsKICAgIGlmIChkaXNwb3NlICE9IG51bGwpIHsKICAgICAgX2Rpc3Bvc2VGdW5jdGlvbnNbVF0gPSBkaXNwb3NlOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IHVucmVnaXN0ZXI8VCBleHRlbmRzIE9iamVjdD4oZmluYWwgVD8gaW5zdGFuY2UpIGFzeW5jIHsKICAgIGlmICghX2dldEl0LmlzUmVnaXN0ZXJlZDxUPigpKSB7CiAgICAgIHJldHVybjsKICAgIH0KCiAgICAvLyBDYWxsIGRpc3Bvc2UgZnVuY3Rpb24gaWYgaXQgZXhpc3RzCiAgICBmaW5hbCBkaXNwb3NlRnVuYyA9IF9kaXNwb3NlRnVuY3Rpb25zW1RdOwogICAgaWYgKGRpc3Bvc2VGdW5jICE9IG51bGwpIHsKICAgICAgdHJ5IHsKICAgICAgICBmaW5hbCByZWdpc3RlcmVkSW5zdGFuY2UgPSBpbnN0YW5jZSA/PyBfcmVnaXN0ZXJlZEluc3RhbmNlc1tUXTsKICAgICAgICBpZiAocmVnaXN0ZXJlZEluc3RhbmNlICE9IG51bGwpIHsKICAgICAgICAgIGF3YWl0IGRpc3Bvc2VGdW5jKHJlZ2lzdGVyZWRJbnN0YW5jZSk7CiAgICAgICAgfQogICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgLy8gTG9nIGVycm9yIGJ1dCBjb250aW51ZSB3aXRoIHVucmVnaXN0cmF0aW9uCiAgICAgICAgZGV2LmxvZygnRXJyb3IgZGlzcG9zaW5nIGluc3RhbmNlIG9mIHR5cGUgJFQnLCBuYW1lOiAnZGknLCBlcnJvcjogZSk7CiAgICAgIH0KICAgICAgX2Rpc3Bvc2VGdW5jdGlvbnMucmVtb3ZlKFQpOwogICAgfQoKICAgIC8vIFJlbW92ZSBmcm9tIG91ciB0cmFja2luZwogICAgX3JlZ2lzdGVyZWRJbnN0YW5jZXMucmVtb3ZlKFQpOwoKICAgIC8vIFVucmVnaXN0ZXIgZnJvbSBHZXRJdAogICAgYXdhaXQgX2dldEl0LnVucmVnaXN0ZXI8VD4oaW5zdGFuY2U6IGluc3RhbmNlKTsKICB9CgogIEBvdmVycmlkZQogIFQgZ2V0PFQgZXh0ZW5kcyBPYmplY3Q+KCkgewogICAgcmV0dXJuIF9nZXRJdC5nZXQ8VD4oKTsKICB9CgogIEBvdmVycmlkZQogIGJvb2wgaGFzPFQgZXh0ZW5kcyBPYmplY3Q+KCkgewogICAgcmV0dXJuIF9nZXRJdC5pc1JlZ2lzdGVyZWQ8VD4oKTsKICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTx2b2lkPiByZXNldCgpIGFzeW5jIHsKICAgIGF3YWl0IGRpc3Bvc2UoKTsKICAgIGF3YWl0IGluaXQoKTsKICB9CgogIC8vLyBHZXQgdGhlIHVuZGVybHlpbmcgR2V0SXQgaW5zdGFuY2UgKGZvciBhZHZhbmNlZCB1c2FnZSkKICBHZXRJdCBnZXQgZ2V0SXQgPT4gX2dldEl0Owp9Cg==", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/di/pubspec.yaml", + "data": + "bmFtZTogZGkKZGVzY3JpcHRpb246ICJTZXJ2aWNlIGxvY2F0b3IgYWJzdHJhY3Rpb24gZm9yIHt7bmFtZS50aXRsZUNhc2UoKX19LCBiYWNrZWQgYnkgR2V0SXQuIgp2ZXJzaW9uOiAwLjEuMApwdWJsaXNoX3RvOiAnbm9uZScKCmVudmlyb25tZW50OgogIHNkazogXjMuOC4wCiAgZmx1dHRlcjogIj49My4yOS4wIgoKZGVwZW5kZW5jaWVzOgogIGZsdXR0ZXI6CiAgICBzZGs6IGZsdXR0ZXIKICBnZXRfaXQ6IF44LjAuMwoKZGV2X2RlcGVuZGVuY2llczoKICBmbHV0dGVyX3Rlc3Q6CiAgICBzZGs6IGZsdXR0ZXIKICBmbHV0dGVyX2xpbnRzOiBeNS4wLjAKCmZsdXR0ZXI6Cg==", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/di/README.md", + "data": + "IyBEZXBlbmRlbmN5IEluamVjdGlvbiAoREkpIFBhY2thZ2UKCkEgY2xlYW4gZGVwZW5kZW5jeSBpbmplY3Rpb24gYWJzdHJhY3Rpb24gZm9yIHRoZSB7e25hbWUudGl0bGVDYXNlKCl9fSBtb25vcmVwbywgYnVpbHQgb24gdG9wIG9mIEdldEl0LgoKWyFbbm9uc3RvcF9jbGldKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2Uvc3RhcnRlZCUyMHdpdGgtbm9uc3RvcF9jbGktMTY2QzRFLnN2Zz9zdHlsZT1mbGF0LXNxdWFyZSldKGh0dHBzOi8vcHViLmRldi9wYWNrYWdlcy9ub25zdG9wX2NsaSkKWyFbbWVsb3NdKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2UvbWFpbnRhaW5lZCUyMHdpdGgtbWVsb3MtZjcwMGZmLnN2Zz9zdHlsZT1mbGF0LXNxdWFyZSldKGh0dHBzOi8vZ2l0aHViLmNvbS9pbnZlcnRhc2UvbWVsb3MpCg==", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/di/test/di_test.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmRpL2RpLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlcl90ZXN0L2ZsdXR0ZXJfdGVzdC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmdldF9pdC9nZXRfaXQuZGFydCc7CgovLyBUZXN0IGNsYXNzZXMgZm9yIGRlcGVuZGVuY3kgaW5qZWN0aW9uCmNsYXNzIFRlc3RTZXJ2aWNlIHsKICBmaW5hbCBTdHJpbmcgbmFtZTsKICBib29sIGlzRGlzcG9zZWQgPSBmYWxzZTsKCiAgVGVzdFNlcnZpY2UodGhpcy5uYW1lKTsKCiAgdm9pZCBkaXNwb3NlKCkgewogICAgaXNEaXNwb3NlZCA9IHRydWU7CiAgfQp9CgpjbGFzcyBUZXN0UmVwb3NpdG9yeSB7CiAgZmluYWwgVGVzdFNlcnZpY2Ugc2VydmljZTsKICBib29sIGlzRGlzcG9zZWQgPSBmYWxzZTsKCiAgVGVzdFJlcG9zaXRvcnkodGhpcy5zZXJ2aWNlKTsKCiAgdm9pZCBkaXNwb3NlKCkgewogICAgaXNEaXNwb3NlZCA9IHRydWU7CiAgfQp9Cgp2b2lkIG1haW4oKSB7CiAgZ3JvdXAoJ0dldEl0RGVwZW5kZW5jeUluamVjdGlvbicsICgpIHsKICAgIGxhdGUgR2V0SXREZXBlbmRlbmN5SW5qZWN0aW9uIGRpOwogICAgbGF0ZSBHZXRJdCB0ZXN0R2V0SXQ7CgogICAgc2V0VXAoKCkgewogICAgICAvLyBDcmVhdGUgYSBzZXBhcmF0ZSBHZXRJdCBpbnN0YW5jZSBmb3IgdGVzdGluZwogICAgICB0ZXN0R2V0SXQgPSBHZXRJdC5hc05ld0luc3RhbmNlKCk7CiAgICAgIGRpID0gR2V0SXREZXBlbmRlbmN5SW5qZWN0aW9uKGdldEl0OiB0ZXN0R2V0SXQpOwogICAgfSk7CgogICAgdGVhckRvd24oKCkgYXN5bmMgewogICAgICBhd2FpdCBkaS5kaXNwb3NlKCk7CiAgICB9KTsKCiAgICBncm91cCgnUmVnaXN0cmF0aW9uJywgKCkgewogICAgICBzZXRVcCgoKSBhc3luYyB7CiAgICAgICAgYXdhaXQgZGkuaW5pdCgpOwogICAgICB9KTsKCiAgICAgIHRlc3QoJ3Nob3VsZCByZWdpc3RlciBhbmQgcmV0cmlldmUgaW5zdGFuY2VzJywgKCkgYXN5bmMgewogICAgICAgIGZpbmFsIHNlcnZpY2UgPSBUZXN0U2VydmljZSgndGVzdCcpOwoKICAgICAgICBkaS5yZWdpc3RlcjxUZXN0U2VydmljZT4oc2VydmljZSk7CgogICAgICAgIGV4cGVjdChkaS5oYXM8VGVzdFNlcnZpY2U+KCksIHRydWUpOwogICAgICAgIGZpbmFsIHJldHJpZXZlZCA9IGRpLmdldDxUZXN0U2VydmljZT4oKTsKICAgICAgICBleHBlY3QocmV0cmlldmVkLCBzYW1lKHNlcnZpY2UpKTsKICAgICAgICBleHBlY3QocmV0cmlldmVkLm5hbWUsICd0ZXN0Jyk7CiAgICAgIH0pOwoKICAgICAgdGVzdCgnc2hvdWxkIHJlZ2lzdGVyIHdpdGggZGlzcG9zZSBmdW5jdGlvbicsICgpIGFzeW5jIHsKICAgICAgICBmaW5hbCBzZXJ2aWNlID0gVGVzdFNlcnZpY2UoJ3Rlc3QnKTsKICAgICAgICBib29sIGRpc3Bvc2VDYWxsZWQgPSBmYWxzZTsKCiAgICAgICAgZGkucmVnaXN0ZXI8VGVzdFNlcnZpY2U+KAogICAgICAgICAgc2VydmljZSwKICAgICAgICAgIGRpc3Bvc2U6IChpbnN0YW5jZSkgewogICAgICAgICAgICBkaXNwb3NlQ2FsbGVkID0gdHJ1ZTsKICAgICAgICAgICAgaW5zdGFuY2UuZGlzcG9zZSgpOwogICAgICAgICAgfSwKICAgICAgICApOwoKICAgICAgICBleHBlY3QoZGkuaGFzPFRlc3RTZXJ2aWNlPigpLCB0cnVlKTsKICAgICAgICBhd2FpdCBkaS51bnJlZ2lzdGVyPFRlc3RTZXJ2aWNlPihudWxsKTsKCiAgICAgICAgZXhwZWN0KGRpc3Bvc2VDYWxsZWQsIHRydWUpOwogICAgICAgIGV4cGVjdChzZXJ2aWNlLmlzRGlzcG9zZWQsIHRydWUpOwogICAgICAgIGV4cGVjdChkaS5oYXM8VGVzdFNlcnZpY2U+KCksIGZhbHNlKTsKICAgICAgfSk7CiAgICB9KTsKCiAgICBncm91cCgnRXJyb3IgaGFuZGxpbmcnLCAoKSB7CiAgICAgIHRlc3QoJ3JlZ2lzdGVyaW5nIGJlZm9yZSBpbml0KCkgaXMgYWxsb3dlZCcsICgpIHsKICAgICAgICAvLyBpbml0KCkgZXhpc3RzIGZvciBzeW1tZXRyeSB3aXRoIGRpc3Bvc2UoKTsgR2V0SXQgbmVlZHMgbm8gc2V0dXAsIHNvCiAgICAgICAgLy8gcmVnaXN0cmF0aW9uIHdvcmtzIHN0cmFpZ2h0IGF3YXkuCiAgICAgICAgZmluYWwgc2VydmljZSA9IFRlc3RTZXJ2aWNlKCd0ZXN0Jyk7CgogICAgICAgIGRpLnJlZ2lzdGVyPFRlc3RTZXJ2aWNlPihzZXJ2aWNlKTsKCiAgICAgICAgZXhwZWN0KGRpLmdldDxUZXN0U2VydmljZT4oKSwgc2FtZShzZXJ2aWNlKSk7CiAgICAgIH0pOwoKICAgICAgdGVzdCgnc2hvdWxkIHRocm93IHdoZW4gZ2V0dGluZyBiZWZvcmUgaW5pdGlhbGl6YXRpb24nLCAoKSB7CiAgICAgICAgZXhwZWN0KCgpID0+IGRpLmdldDxUZXN0U2VydmljZT4oKSwgdGhyb3dzQShpc0E8U3RhdGVFcnJvcj4oKSkpOwogICAgICB9KTsKCiAgICAgIHRlc3QoJ3Nob3VsZCByZXR1cm4gZmFsc2Ugd2hlbiBub3QgaW5pdGlhbGl6ZWQnLCAoKSB7CiAgICAgICAgZXhwZWN0KGRpLmhhczxUZXN0U2VydmljZT4oKSwgZmFsc2UpOwogICAgICB9KTsKICAgIH0pOwoKICAgIGdyb3VwKCdSZXRyaWV2YWwnLCAoKSB7CiAgICAgIHNldFVwKCgpIGFzeW5jIHsKICAgICAgICBhd2FpdCBkaS5pbml0KCk7CiAgICAgIH0pOwoKICAgICAgdGVzdCgnc2hvdWxkIGdldCByZWdpc3RlcmVkIGluc3RhbmNlcycsICgpIHsKICAgICAgICBmaW5hbCBzZXJ2aWNlID0gVGVzdFNlcnZpY2UoJ3Rlc3QnKTsKICAgICAgICBkaS5yZWdpc3RlcjxUZXN0U2VydmljZT4oc2VydmljZSk7CgogICAgICAgIGZpbmFsIHJldHJpZXZlZCA9IGRpLmdldDxUZXN0U2VydmljZT4oKTsKICAgICAgICBleHBlY3QocmV0cmlldmVkLCBzYW1lKHNlcnZpY2UpKTsKICAgICAgfSk7CgogICAgICB0ZXN0KCdzaG91bGQgdGhyb3cgd2hlbiBnZXR0aW5nIHVucmVnaXN0ZXJlZCB0eXBlJywgKCkgewogICAgICAgIGV4cGVjdCgoKSA9PiBkaS5nZXQ8VGVzdFNlcnZpY2U+KCksIHRocm93c0EoaXNBPE9iamVjdD4oKSkpOwogICAgICB9KTsKICAgIH0pOwoKICAgIGdyb3VwKCdIYXMvQ29udGFpbnMnLCAoKSB7CiAgICAgIHNldFVwKCgpIGFzeW5jIHsKICAgICAgICBhd2FpdCBkaS5pbml0KCk7CiAgICAgIH0pOwoKICAgICAgdGVzdCgnc2hvdWxkIHJldHVybiB0cnVlIGZvciByZWdpc3RlcmVkIHR5cGVzJywgKCkgewogICAgICAgIGZpbmFsIHNlcnZpY2UgPSBUZXN0U2VydmljZSgndGVzdCcpOwogICAgICAgIGRpLnJlZ2lzdGVyPFRlc3RTZXJ2aWNlPihzZXJ2aWNlKTsKCiAgICAgICAgZXhwZWN0KGRpLmhhczxUZXN0U2VydmljZT4oKSwgdHJ1ZSk7CiAgICAgIH0pOwoKICAgICAgdGVzdCgnc2hvdWxkIHJldHVybiBmYWxzZSBmb3IgdW5yZWdpc3RlcmVkIHR5cGVzJywgKCkgewogICAgICAgIGV4cGVjdChkaS5oYXM8VGVzdFNlcnZpY2U+KCksIGZhbHNlKTsKICAgICAgfSk7CiAgICB9KTsKCiAgICBncm91cCgnVW5yZWdpc3RyYXRpb24nLCAoKSB7CiAgICAgIHNldFVwKCgpIGFzeW5jIHsKICAgICAgICBhd2FpdCBkaS5pbml0KCk7CiAgICAgIH0pOwoKICAgICAgdGVzdCgnc2hvdWxkIHVucmVnaXN0ZXIgaW5zdGFuY2VzJywgKCkgYXN5bmMgewogICAgICAgIGZpbmFsIHNlcnZpY2UgPSBUZXN0U2VydmljZSgndGVzdCcpOwogICAgICAgIGRpLnJlZ2lzdGVyPFRlc3RTZXJ2aWNlPihzZXJ2aWNlKTsKCiAgICAgICAgZXhwZWN0KGRpLmhhczxUZXN0U2VydmljZT4oKSwgdHJ1ZSk7CiAgICAgICAgYXdhaXQgZGkudW5yZWdpc3RlcjxUZXN0U2VydmljZT4obnVsbCk7CiAgICAgICAgZXhwZWN0KGRpLmhhczxUZXN0U2VydmljZT4oKSwgZmFsc2UpOwogICAgICB9KTsKCiAgICAgIHRlc3QoJ3Nob3VsZCBjYWxsIGRpc3Bvc2UgZnVuY3Rpb24gd2hlbiB1bnJlZ2lzdGVyaW5nJywgKCkgYXN5bmMgewogICAgICAgIGZpbmFsIHNlcnZpY2UgPSBUZXN0U2VydmljZSgndGVzdCcpOwogICAgICAgIGJvb2wgZGlzcG9zZUNhbGxlZCA9IGZhbHNlOwoKICAgICAgICBkaS5yZWdpc3RlcjxUZXN0U2VydmljZT4oCiAgICAgICAgICBzZXJ2aWNlLAogICAgICAgICAgZGlzcG9zZTogKGluc3RhbmNlKSB7CiAgICAgICAgICAgIGRpc3Bvc2VDYWxsZWQgPSB0cnVlOwogICAgICAgICAgICBpbnN0YW5jZS5kaXNwb3NlKCk7CiAgICAgICAgICB9LAogICAgICAgICk7CgogICAgICAgIGF3YWl0IGRpLnVucmVnaXN0ZXI8VGVzdFNlcnZpY2U+KG51bGwpOwoKICAgICAgICBleHBlY3QoZGlzcG9zZUNhbGxlZCwgdHJ1ZSk7CiAgICAgICAgZXhwZWN0KHNlcnZpY2UuaXNEaXNwb3NlZCwgdHJ1ZSk7CiAgICAgIH0pOwoKICAgICAgdGVzdCgKICAgICAgICAnc2hvdWxkIGhhbmRsZSB1bnJlZ2lzdGVyaW5nIG5vbi1leGlzdGVudCB0eXBlcyBncmFjZWZ1bGx5JywKICAgICAgICAoKSBhc3luYyB7CiAgICAgICAgICAvLyBTaG91bGQgbm90IHRocm93CiAgICAgICAgICBhd2FpdCBkaS51bnJlZ2lzdGVyPFRlc3RTZXJ2aWNlPihudWxsKTsKICAgICAgICAgIGV4cGVjdChkaS5oYXM8VGVzdFNlcnZpY2U+KCksIGZhbHNlKTsKICAgICAgICB9LAogICAgICApOwoKICAgICAgdGVzdCgnc2hvdWxkIGhhbmRsZSB1bnJlZ2lzdGVyaW5nIHdoZW4gbm90IGluaXRpYWxpemVkJywgKCkgYXN5bmMgewogICAgICAgIC8vIFNob3VsZCBub3QgdGhyb3cKICAgICAgICBhd2FpdCBkaS51bnJlZ2lzdGVyPFRlc3RTZXJ2aWNlPihudWxsKTsKICAgICAgfSk7CiAgICB9KTsKCiAgICBncm91cCgnRGlzcG9zYWwnLCAoKSB7CiAgICAgIHNldFVwKCgpIGFzeW5jIHsKICAgICAgICBhd2FpdCBkaS5pbml0KCk7CiAgICAgIH0pOwoKICAgICAgdGVzdCgnc2hvdWxkIGRpc3Bvc2UgYWxsIHJlZ2lzdGVyZWQgaW5zdGFuY2VzJywgKCkgYXN5bmMgewogICAgICAgIGZpbmFsIHNlcnZpY2UgPSBUZXN0U2VydmljZSgnc2VydmljZScpOwogICAgICAgIGZpbmFsIHJlcG9zaXRvcnkgPSBUZXN0UmVwb3NpdG9yeShzZXJ2aWNlKTsKCiAgICAgICAgZGkucmVnaXN0ZXI8VGVzdFNlcnZpY2U+KAogICAgICAgICAgc2VydmljZSwKICAgICAgICAgIGRpc3Bvc2U6IChpbnN0YW5jZSkgPT4gaW5zdGFuY2UuZGlzcG9zZSgpLAogICAgICAgICk7CgogICAgICAgIGRpLnJlZ2lzdGVyPFRlc3RSZXBvc2l0b3J5PigKICAgICAgICAgIHJlcG9zaXRvcnksCiAgICAgICAgICBkaXNwb3NlOiAoaW5zdGFuY2UpID0+IGluc3RhbmNlLmRpc3Bvc2UoKSwKICAgICAgICApOwoKICAgICAgICBhd2FpdCBkaS5kaXNwb3NlKCk7CgogICAgICAgIGV4cGVjdChzZXJ2aWNlLmlzRGlzcG9zZWQsIHRydWUpOwogICAgICAgIGV4cGVjdChyZXBvc2l0b3J5LmlzRGlzcG9zZWQsIHRydWUpOwogICAgICAgIGV4cGVjdChkaS5oYXM8VGVzdFNlcnZpY2U+KCksIGZhbHNlKTsKICAgICAgICBleHBlY3QoZGkuaGFzPFRlc3RSZXBvc2l0b3J5PigpLCBmYWxzZSk7CiAgICAgIH0pOwogICAgfSk7CgogICAgZ3JvdXAoJ1Jlc2V0JywgKCkgewogICAgICB0ZXN0KCdzaG91bGQgcmVzZXQgYW5kIHJlaW5pdGlhbGl6ZScsICgpIGFzeW5jIHsKICAgICAgICBhd2FpdCBkaS5pbml0KCk7CgogICAgICAgIGZpbmFsIHNlcnZpY2UgPSBUZXN0U2VydmljZSgndGVzdCcpOwogICAgICAgIGRpLnJlZ2lzdGVyPFRlc3RTZXJ2aWNlPigKICAgICAgICAgIHNlcnZpY2UsCiAgICAgICAgICBkaXNwb3NlOiAoaW5zdGFuY2UpID0+IGluc3RhbmNlLmRpc3Bvc2UoKSwKICAgICAgICApOwoKICAgICAgICBleHBlY3QoZGkuaGFzPFRlc3RTZXJ2aWNlPigpLCB0cnVlKTsKCiAgICAgICAgYXdhaXQgZGkucmVzZXQoKTsKCiAgICAgICAgZXhwZWN0KGRpLmhhczxUZXN0U2VydmljZT4oKSwgZmFsc2UpOwogICAgICAgIGV4cGVjdChzZXJ2aWNlLmlzRGlzcG9zZWQsIHRydWUpOwogICAgICB9KTsKICAgIH0pOwoKICAgIGdyb3VwKCdDb21wbGV4IHNjZW5hcmlvcycsICgpIHsKICAgICAgc2V0VXAoKCkgYXN5bmMgewogICAgICAgIGF3YWl0IGRpLmluaXQoKTsKICAgICAgfSk7CgogICAgICB0ZXN0KCdzaG91bGQgaGFuZGxlIG11bHRpcGxlIGRlcGVuZGVuY2llcycsICgpIHsKICAgICAgICBmaW5hbCBzZXJ2aWNlID0gVGVzdFNlcnZpY2UoJ3NlcnZpY2UnKTsKICAgICAgICBmaW5hbCByZXBvc2l0b3J5ID0gVGVzdFJlcG9zaXRvcnkoc2VydmljZSk7CgogICAgICAgIGRpLnJlZ2lzdGVyPFRlc3RTZXJ2aWNlPihzZXJ2aWNlKTsKICAgICAgICBkaS5yZWdpc3RlcjxUZXN0UmVwb3NpdG9yeT4ocmVwb3NpdG9yeSk7CgogICAgICAgIGV4cGVjdChkaS5oYXM8VGVzdFNlcnZpY2U+KCksIHRydWUpOwogICAgICAgIGV4cGVjdChkaS5oYXM8VGVzdFJlcG9zaXRvcnk+KCksIHRydWUpOwoKICAgICAgICBmaW5hbCByZXRyaWV2ZWRTZXJ2aWNlID0gZGkuZ2V0PFRlc3RTZXJ2aWNlPigpOwogICAgICAgIGZpbmFsIHJldHJpZXZlZFJlcG9zaXRvcnkgPSBkaS5nZXQ8VGVzdFJlcG9zaXRvcnk+KCk7CgogICAgICAgIGV4cGVjdChyZXRyaWV2ZWRTZXJ2aWNlLCBzYW1lKHNlcnZpY2UpKTsKICAgICAgICBleHBlY3QocmV0cmlldmVkUmVwb3NpdG9yeSwgc2FtZShyZXBvc2l0b3J5KSk7CiAgICAgICAgZXhwZWN0KHJldHJpZXZlZFJlcG9zaXRvcnkuc2VydmljZSwgc2FtZShzZXJ2aWNlKSk7CiAgICAgIH0pOwogICAgfSk7CiAgfSk7Cn0K", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/localization/build.yaml", + "data": + "dGFyZ2V0czoKICAkZGVmYXVsdDoKICAgIGJ1aWxkZXJzOgogICAgICBpNjluOgogICAgICAgIG9wdGlvbnM6CiAgICAgICAgICBkZWZhdWx0X2xvY2FsZTogZW4KICAgICAgICAgIGxvY2FsZXM6CiAgICAgICAgICAgIC0gZW4KICAgICAgICAgICAgLSBlcwogICAgICAgICAgICAtIGZyCiAgICAgICAgICAgIC0gZGUKICAgICAgICBnZW5lcmF0ZV9mb3I6CiAgICAgICAgICAtIGxpYi9tZXNzYWdlcy5pNjluLnlhbWw=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/localization/lib/localization.dart", + "data": + "Ly8vIHt7bmFtZS50aXRsZUNhc2UoKX19IExvY2FsaXphdGlvbiBQYWNrYWdlCi8vLwovLy8gVGhpcyBwYWNrYWdlIHByb3ZpZGVzIGludGVybmF0aW9uYWxpemF0aW9uIHN1cHBvcnQgdXNpbmcgaTY5bi4KLy8vIEl0IGNvbnRhaW5zIGdlbmVyYXRlZCBtZXNzYWdlIGNsYXNzZXMgYW5kIHV0aWxpdGllcyBmb3IgbWFuYWdpbmcgdHJhbnNsYXRpb25zLgpsaWJyYXJ5OwoKLy8gRXhwb3J0IGdlbmVyYXRlZCBpNjluIG1lc3NhZ2VzCmV4cG9ydCAnbWVzc2FnZXMuaTY5bi5kYXJ0JzsKCi8vIEV4cG9ydCBsb2NhbGl6YXRpb24gdXRpbGl0aWVzCmV4cG9ydCAnc3JjL2xvY2FsaXphdGlvbl9wcm92aWRlci5kYXJ0JzsKCi8vIEV4cG9ydCBnbG9iYWwgc3RyaW5ncyBpbnN0YW5jZSBmb3IgZWFzeSBhY2Nlc3MKZXhwb3J0ICdzcmMvc3RyaW5ncy5kYXJ0JzsK", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/localization/lib/messages.i69n.dart", + "data": + "Ly8gaWdub3JlX2Zvcl9maWxlOiB1bnVzZWRfZWxlbWVudCwgdW51c2VkX2ZpZWxkLCBjYW1lbF9jYXNlX3R5cGVzLCBhbm5vdGF0ZV9vdmVycmlkZXMsIHByZWZlcl9zaW5nbGVfcXVvdGVzCi8vIEdFTkVSQVRFRCBGSUxFLCBkbyBub3QgZWRpdCEKLy8gZGFydCBmb3JtYXQgb2ZmCmltcG9ydCAncGFja2FnZTppNjluL2k2OW4uZGFydCcgYXMgaTY5bjsKClN0cmluZyBnZXQgX2xhbmd1YWdlQ29kZSA9PiAnZW4nOwpTdHJpbmcgZ2V0IF9sb2NhbGVOYW1lID0+ICdlbic7CgpTdHJpbmcgX3BsdXJhbChpbnQgY291bnQsCiAgICAgICAge1N0cmluZz8gemVybywKICAgICAgICBTdHJpbmc/IG9uZSwKICAgICAgICBTdHJpbmc/IHR3bywKICAgICAgICBTdHJpbmc/IGZldywKICAgICAgICBTdHJpbmc/IG1hbnksCiAgICAgICAgU3RyaW5nPyBvdGhlcn0pID0+CiAgICBpNjluLnBsdXJhbChjb3VudCwgX2xhbmd1YWdlQ29kZSwKICAgICAgICB6ZXJvOiB6ZXJvLCBvbmU6IG9uZSwgdHdvOiB0d28sIGZldzogZmV3LCBtYW55OiBtYW55LCBvdGhlcjogb3RoZXIpOwpTdHJpbmcgX29yZGluYWwoaW50IGNvdW50LAogICAgICAgIHtTdHJpbmc/IHplcm8sCiAgICAgICAgU3RyaW5nPyBvbmUsCiAgICAgICAgU3RyaW5nPyB0d28sCiAgICAgICAgU3RyaW5nPyBmZXcsCiAgICAgICAgU3RyaW5nPyBtYW55LAogICAgICAgIFN0cmluZz8gb3RoZXJ9KSA9PgogICAgaTY5bi5vcmRpbmFsKGNvdW50LCBfbGFuZ3VhZ2VDb2RlLAogICAgICAgIHplcm86IHplcm8sIG9uZTogb25lLCB0d286IHR3bywgZmV3OiBmZXcsIG1hbnk6IG1hbnksIG90aGVyOiBvdGhlcik7ClN0cmluZyBfY2FyZGluYWwoaW50IGNvdW50LAogICAgICAgIHtTdHJpbmc/IHplcm8sCiAgICAgICAgU3RyaW5nPyBvbmUsCiAgICAgICAgU3RyaW5nPyB0d28sCiAgICAgICAgU3RyaW5nPyBmZXcsCiAgICAgICAgU3RyaW5nPyBtYW55LAogICAgICAgIFN0cmluZz8gb3RoZXJ9KSA9PgogICAgaTY5bi5jYXJkaW5hbChjb3VudCwgX2xhbmd1YWdlQ29kZSwKICAgICAgICB6ZXJvOiB6ZXJvLCBvbmU6IG9uZSwgdHdvOiB0d28sIGZldzogZmV3LCBtYW55OiBtYW55LCBvdGhlcjogb3RoZXIpOwoKY2xhc3MgTWVzc2FnZXMgaW1wbGVtZW50cyBpNjluLkk2OW5NZXNzYWdlQnVuZGxlIHsKICBjb25zdCBNZXNzYWdlcygpOwogIEFwcE1lc3NhZ2VzIGdldCBhcHAgPT4gQXBwTWVzc2FnZXModGhpcyk7CiAgR2VuZXJpY01lc3NhZ2VzIGdldCBnZW5lcmljID0+IEdlbmVyaWNNZXNzYWdlcyh0aGlzKTsKICBDb21tb25NZXNzYWdlcyBnZXQgY29tbW9uID0+IENvbW1vbk1lc3NhZ2VzKHRoaXMpOwogIEF1dGhNZXNzYWdlcyBnZXQgYXV0aCA9PiBBdXRoTWVzc2FnZXModGhpcyk7CiAgUHJvZmlsZU1lc3NhZ2VzIGdldCBwcm9maWxlID0+IFByb2ZpbGVNZXNzYWdlcyh0aGlzKTsKICBOYXZNZXNzYWdlcyBnZXQgbmF2ID0+IE5hdk1lc3NhZ2VzKHRoaXMpOwogIE5vdGlmaWNhdGlvbnNNZXNzYWdlcyBnZXQgbm90aWZpY2F0aW9ucyA9PiBOb3RpZmljYXRpb25zTWVzc2FnZXModGhpcyk7CiAgRXJyb3JzTWVzc2FnZXMgZ2V0IGVycm9ycyA9PiBFcnJvcnNNZXNzYWdlcyh0aGlzKTsKICBWYWxpZGF0aW9uTWVzc2FnZXMgZ2V0IHZhbGlkYXRpb24gPT4gVmFsaWRhdGlvbk1lc3NhZ2VzKHRoaXMpOwogIE9iamVjdCBvcGVyYXRvciBbXShTdHJpbmcga2V5KSB7CiAgICB2YXIgaW5kZXggPSBrZXkuaW5kZXhPZignLicpOwogICAgaWYgKGluZGV4ID4gMCkgewogICAgICByZXR1cm4gKHRoaXNba2V5LnN1YnN0cmluZygwLCBpbmRleCldCiAgICAgICAgICBhcyBpNjluLkk2OW5NZXNzYWdlQnVuZGxlKVtrZXkuc3Vic3RyaW5nKGluZGV4ICsgMSldOwogICAgfQogICAgc3dpdGNoIChrZXkpIHsKICAgICAgY2FzZSAnYXBwJzoKICAgICAgICByZXR1cm4gYXBwOwogICAgICBjYXNlICdnZW5lcmljJzoKICAgICAgICByZXR1cm4gZ2VuZXJpYzsKICAgICAgY2FzZSAnY29tbW9uJzoKICAgICAgICByZXR1cm4gY29tbW9uOwogICAgICBjYXNlICdhdXRoJzoKICAgICAgICByZXR1cm4gYXV0aDsKICAgICAgY2FzZSAncHJvZmlsZSc6CiAgICAgICAgcmV0dXJuIHByb2ZpbGU7CiAgICAgIGNhc2UgJ25hdic6CiAgICAgICAgcmV0dXJuIG5hdjsKICAgICAgY2FzZSAnbm90aWZpY2F0aW9ucyc6CiAgICAgICAgcmV0dXJuIG5vdGlmaWNhdGlvbnM7CiAgICAgIGNhc2UgJ2Vycm9ycyc6CiAgICAgICAgcmV0dXJuIGVycm9yczsKICAgICAgY2FzZSAndmFsaWRhdGlvbic6CiAgICAgICAgcmV0dXJuIHZhbGlkYXRpb247CiAgICAgIGRlZmF1bHQ6CiAgICAgICAgcmV0dXJuIGtleTsKICAgIH0KICB9Cn0KCmNsYXNzIEFwcE1lc3NhZ2VzIGltcGxlbWVudHMgaTY5bi5JNjluTWVzc2FnZUJ1bmRsZSB7CiAgZmluYWwgTWVzc2FnZXMgX3BhcmVudDsKICBjb25zdCBBcHBNZXNzYWdlcyh0aGlzLl9wYXJlbnQpOwogIFN0cmluZyBnZXQgbmFtZSA9PiAie3tuYW1lLnRpdGxlQ2FzZSgpfX0iOwogIFN0cmluZyBnZXQgZGVzY3JpcHRpb24gPT4gInt7e2Rlc2NyaXB0aW9ufX19IjsKICBTdHJpbmcgZ2V0IHdlbGNvbWVfdG9fYXBwID0+ICJXZWxjb21lIHRvIHt7bmFtZS50aXRsZUNhc2UoKX19ISI7CiAgT2JqZWN0IG9wZXJhdG9yIFtdKFN0cmluZyBrZXkpIHsKICAgIHZhciBpbmRleCA9IGtleS5pbmRleE9mKCcuJyk7CiAgICBpZiAoaW5kZXggPiAwKSB7CiAgICAgIHJldHVybiAodGhpc1trZXkuc3Vic3RyaW5nKDAsIGluZGV4KV0KICAgICAgICAgIGFzIGk2OW4uSTY5bk1lc3NhZ2VCdW5kbGUpW2tleS5zdWJzdHJpbmcoaW5kZXggKyAxKV07CiAgICB9CiAgICBzd2l0Y2ggKGtleSkgewogICAgICBjYXNlICduYW1lJzoKICAgICAgICByZXR1cm4gbmFtZTsKICAgICAgY2FzZSAnZGVzY3JpcHRpb24nOgogICAgICAgIHJldHVybiBkZXNjcmlwdGlvbjsKICAgICAgY2FzZSAnd2VsY29tZV90b19hcHAnOgogICAgICAgIHJldHVybiB3ZWxjb21lX3RvX2FwcDsKICAgICAgZGVmYXVsdDoKICAgICAgICByZXR1cm4ga2V5OwogICAgfQogIH0KfQoKY2xhc3MgR2VuZXJpY01lc3NhZ2VzIGltcGxlbWVudHMgaTY5bi5JNjluTWVzc2FnZUJ1bmRsZSB7CiAgZmluYWwgTWVzc2FnZXMgX3BhcmVudDsKICBjb25zdCBHZW5lcmljTWVzc2FnZXModGhpcy5fcGFyZW50KTsKICBTdHJpbmcgZ2V0IG9rID0+ICJPSyI7CiAgU3RyaW5nIGdldCBjYW5jZWwgPT4gIkNhbmNlbCI7CiAgU3RyaW5nIGdldCBzYXZlID0+ICJTYXZlIjsKICBTdHJpbmcgZ2V0IGRlbGV0ZSA9PiAiRGVsZXRlIjsKICBTdHJpbmcgZ2V0IGVkaXQgPT4gIkVkaXQiOwogIFN0cmluZyBnZXQgdXBkYXRlID0+ICJVcGRhdGUiOwogIFN0cmluZyBnZXQgc3VibWl0ID0+ICJTdWJtaXQiOwogIFN0cmluZyBnZXQgY2xvc2UgPT4gIkNsb3NlIjsKICBTdHJpbmcgZ2V0IGJhY2sgPT4gIkJhY2siOwogIFN0cmluZyBnZXQgbmV4dCA9PiAiTmV4dCI7CiAgU3RyaW5nIGdldCBwcmV2aW91cyA9PiAiUHJldmlvdXMiOwogIFN0cmluZyBnZXQgZG9uZSA9PiAiRG9uZSI7CiAgU3RyaW5nIGdldCBsb2FkaW5nID0+ICJMb2FkaW5nLi4uIjsKICBTdHJpbmcgZ2V0IGVycm9yID0+ICJFcnJvciI7CiAgU3RyaW5nIGdldCBzdWNjZXNzID0+ICJTdWNjZXNzIjsKICBTdHJpbmcgZ2V0IHdhcm5pbmcgPT4gIldhcm5pbmciOwogIFN0cmluZyBnZXQgaW5mbyA9PiAiSW5mbyI7CiAgU3RyaW5nIGdldCByZXRyeSA9PiAiUmV0cnkiOwogIFN0cmluZyBnZXQgcmVmcmVzaCA9PiAiUmVmcmVzaCI7CiAgU3RyaW5nIGdldCB5ZXMgPT4gIlllcyI7CiAgU3RyaW5nIGdldCBubyA9PiAiTm8iOwogIFN0cmluZyBnZXQgYWRkID0+ICIrIEFkZCI7CiAgU3RyaW5nIGdldCB0cnlfYWdhaW4gPT4gIlRyeSBBZ2FpbiI7CiAgT2JqZWN0IG9wZXJhdG9yIFtdKFN0cmluZyBrZXkpIHsKICAgIHZhciBpbmRleCA9IGtleS5pbmRleE9mKCcuJyk7CiAgICBpZiAoaW5kZXggPiAwKSB7CiAgICAgIHJldHVybiAodGhpc1trZXkuc3Vic3RyaW5nKDAsIGluZGV4KV0KICAgICAgICAgIGFzIGk2OW4uSTY5bk1lc3NhZ2VCdW5kbGUpW2tleS5zdWJzdHJpbmcoaW5kZXggKyAxKV07CiAgICB9CiAgICBzd2l0Y2ggKGtleSkgewogICAgICBjYXNlICdvayc6CiAgICAgICAgcmV0dXJuIG9rOwogICAgICBjYXNlICdjYW5jZWwnOgogICAgICAgIHJldHVybiBjYW5jZWw7CiAgICAgIGNhc2UgJ3NhdmUnOgogICAgICAgIHJldHVybiBzYXZlOwogICAgICBjYXNlICdkZWxldGUnOgogICAgICAgIHJldHVybiBkZWxldGU7CiAgICAgIGNhc2UgJ2VkaXQnOgogICAgICAgIHJldHVybiBlZGl0OwogICAgICBjYXNlICd1cGRhdGUnOgogICAgICAgIHJldHVybiB1cGRhdGU7CiAgICAgIGNhc2UgJ3N1Ym1pdCc6CiAgICAgICAgcmV0dXJuIHN1Ym1pdDsKICAgICAgY2FzZSAnY2xvc2UnOgogICAgICAgIHJldHVybiBjbG9zZTsKICAgICAgY2FzZSAnYmFjayc6CiAgICAgICAgcmV0dXJuIGJhY2s7CiAgICAgIGNhc2UgJ25leHQnOgogICAgICAgIHJldHVybiBuZXh0OwogICAgICBjYXNlICdwcmV2aW91cyc6CiAgICAgICAgcmV0dXJuIHByZXZpb3VzOwogICAgICBjYXNlICdkb25lJzoKICAgICAgICByZXR1cm4gZG9uZTsKICAgICAgY2FzZSAnbG9hZGluZyc6CiAgICAgICAgcmV0dXJuIGxvYWRpbmc7CiAgICAgIGNhc2UgJ2Vycm9yJzoKICAgICAgICByZXR1cm4gZXJyb3I7CiAgICAgIGNhc2UgJ3N1Y2Nlc3MnOgogICAgICAgIHJldHVybiBzdWNjZXNzOwogICAgICBjYXNlICd3YXJuaW5nJzoKICAgICAgICByZXR1cm4gd2FybmluZzsKICAgICAgY2FzZSAnaW5mbyc6CiAgICAgICAgcmV0dXJuIGluZm87CiAgICAgIGNhc2UgJ3JldHJ5JzoKICAgICAgICByZXR1cm4gcmV0cnk7CiAgICAgIGNhc2UgJ3JlZnJlc2gnOgogICAgICAgIHJldHVybiByZWZyZXNoOwogICAgICBjYXNlICd5ZXMnOgogICAgICAgIHJldHVybiB5ZXM7CiAgICAgIGNhc2UgJ25vJzoKICAgICAgICByZXR1cm4gbm87CiAgICAgIGNhc2UgJ2FkZCc6CiAgICAgICAgcmV0dXJuIGFkZDsKICAgICAgY2FzZSAndHJ5X2FnYWluJzoKICAgICAgICByZXR1cm4gdHJ5X2FnYWluOwogICAgICBkZWZhdWx0OgogICAgICAgIHJldHVybiBrZXk7CiAgICB9CiAgfQp9CgpjbGFzcyBDb21tb25NZXNzYWdlcyBpbXBsZW1lbnRzIGk2OW4uSTY5bk1lc3NhZ2VCdW5kbGUgewogIGZpbmFsIE1lc3NhZ2VzIF9wYXJlbnQ7CiAgY29uc3QgQ29tbW9uTWVzc2FnZXModGhpcy5fcGFyZW50KTsKICBTdHJpbmcgZ2V0IGNhbmNlbCA9PiAiQ2FuY2VsIjsKICBTdHJpbmcgZ2V0IG9rID0+ICJPSyI7CiAgU3RyaW5nIGdldCBzYXZlID0+ICJTYXZlIjsKICBTdHJpbmcgZ2V0IGRlbGV0ZSA9PiAiRGVsZXRlIjsKICBTdHJpbmcgZ2V0IGVkaXQgPT4gIkVkaXQiOwogIFN0cmluZyBnZXQgdHJ5X2FnYWluID0+ICJUcnkgQWdhaW4iOwogIFN0cmluZyBnZXQgd2VlayA9PiAiV2VlayI7CiAgU3RyaW5nIGdldCBtb250aCA9PiAiTW9udGgiOwogIE9iamVjdCBvcGVyYXRvciBbXShTdHJpbmcga2V5KSB7CiAgICB2YXIgaW5kZXggPSBrZXkuaW5kZXhPZignLicpOwogICAgaWYgKGluZGV4ID4gMCkgewogICAgICByZXR1cm4gKHRoaXNba2V5LnN1YnN0cmluZygwLCBpbmRleCldCiAgICAgICAgICBhcyBpNjluLkk2OW5NZXNzYWdlQnVuZGxlKVtrZXkuc3Vic3RyaW5nKGluZGV4ICsgMSldOwogICAgfQogICAgc3dpdGNoIChrZXkpIHsKICAgICAgY2FzZSAnY2FuY2VsJzoKICAgICAgICByZXR1cm4gY2FuY2VsOwogICAgICBjYXNlICdvayc6CiAgICAgICAgcmV0dXJuIG9rOwogICAgICBjYXNlICdzYXZlJzoKICAgICAgICByZXR1cm4gc2F2ZTsKICAgICAgY2FzZSAnZGVsZXRlJzoKICAgICAgICByZXR1cm4gZGVsZXRlOwogICAgICBjYXNlICdlZGl0JzoKICAgICAgICByZXR1cm4gZWRpdDsKICAgICAgY2FzZSAndHJ5X2FnYWluJzoKICAgICAgICByZXR1cm4gdHJ5X2FnYWluOwogICAgICBjYXNlICd3ZWVrJzoKICAgICAgICByZXR1cm4gd2VlazsKICAgICAgY2FzZSAnbW9udGgnOgogICAgICAgIHJldHVybiBtb250aDsKICAgICAgZGVmYXVsdDoKICAgICAgICByZXR1cm4ga2V5OwogICAgfQogIH0KfQoKY2xhc3MgQXV0aE1lc3NhZ2VzIGltcGxlbWVudHMgaTY5bi5JNjluTWVzc2FnZUJ1bmRsZSB7CiAgZmluYWwgTWVzc2FnZXMgX3BhcmVudDsKICBjb25zdCBBdXRoTWVzc2FnZXModGhpcy5fcGFyZW50KTsKICBTdHJpbmcgZ2V0IHJlZ2lzdGVyID0+ICJSZWdpc3RlciI7CiAgU3RyaW5nIGdldCBzaWduX2luID0+ICJTaWduIEluIjsKICBTdHJpbmcgZ2V0IHNpZ25fb3V0ID0+ICJTaWduIE91dCI7CiAgU3RyaW5nIGdldCBkb250X2hhdmVfYWNjb3VudCA9PiAiRG9uJ3QgaGF2ZSBhbiBhY2NvdW50PyAiOwogIFN0cmluZyBnZXQgYWxyZWFkeV9oYXZlX2FjY291bnQgPT4gIkFscmVhZHkgaGF2ZSBhbiBhY2NvdW50PyAiOwogIFN0cmluZyBnZXQgc2lnbl9vdXRfY29uZmlybWF0aW9uID0+ICJBcmUgeW91IHN1cmUgeW91IHdhbnQgdG8gc2lnbiBvdXQ/IjsKICBPYmplY3Qgb3BlcmF0b3IgW10oU3RyaW5nIGtleSkgewogICAgdmFyIGluZGV4ID0ga2V5LmluZGV4T2YoJy4nKTsKICAgIGlmIChpbmRleCA+IDApIHsKICAgICAgcmV0dXJuICh0aGlzW2tleS5zdWJzdHJpbmcoMCwgaW5kZXgpXQogICAgICAgICAgYXMgaTY5bi5JNjluTWVzc2FnZUJ1bmRsZSlba2V5LnN1YnN0cmluZyhpbmRleCArIDEpXTsKICAgIH0KICAgIHN3aXRjaCAoa2V5KSB7CiAgICAgIGNhc2UgJ3JlZ2lzdGVyJzoKICAgICAgICByZXR1cm4gcmVnaXN0ZXI7CiAgICAgIGNhc2UgJ3NpZ25faW4nOgogICAgICAgIHJldHVybiBzaWduX2luOwogICAgICBjYXNlICdzaWduX291dCc6CiAgICAgICAgcmV0dXJuIHNpZ25fb3V0OwogICAgICBjYXNlICdkb250X2hhdmVfYWNjb3VudCc6CiAgICAgICAgcmV0dXJuIGRvbnRfaGF2ZV9hY2NvdW50OwogICAgICBjYXNlICdhbHJlYWR5X2hhdmVfYWNjb3VudCc6CiAgICAgICAgcmV0dXJuIGFscmVhZHlfaGF2ZV9hY2NvdW50OwogICAgICBjYXNlICdzaWduX291dF9jb25maXJtYXRpb24nOgogICAgICAgIHJldHVybiBzaWduX291dF9jb25maXJtYXRpb247CiAgICAgIGRlZmF1bHQ6CiAgICAgICAgcmV0dXJuIGtleTsKICAgIH0KICB9Cn0KCmNsYXNzIFByb2ZpbGVNZXNzYWdlcyBpbXBsZW1lbnRzIGk2OW4uSTY5bk1lc3NhZ2VCdW5kbGUgewogIGZpbmFsIE1lc3NhZ2VzIF9wYXJlbnQ7CiAgY29uc3QgUHJvZmlsZU1lc3NhZ2VzKHRoaXMuX3BhcmVudCk7CiAgU3RyaW5nIGdldCBwcm9maWxlID0+ICJQcm9maWxlIjsKICBTdHJpbmcgZ2V0IHNldHRpbmdzID0+ICJTZXR0aW5ncyI7CiAgU3RyaW5nIGdldCBhY2NvdW50ID0+ICJBY2NvdW50IjsKICBTdHJpbmcgZ2V0IHBlcnNvbmFsX2luZm8gPT4gIlBlcnNvbmFsIEluZm9ybWF0aW9uIjsKICBTdHJpbmcgZ2V0IHByaXZhY3lfc2V0dGluZ3MgPT4gIlByaXZhY3kgU2V0dGluZ3MiOwogIFN0cmluZyBnZXQgbmFtZSA9PiAiTmFtZSI7CiAgU3RyaW5nIGdldCBlbWFpbF9hZGRyZXNzID0+ICJFbWFpbCBBZGRyZXNzIjsKICBTdHJpbmcgZ2V0IHBob25lX251bWJlciA9PiAiUGhvbmUgTnVtYmVyIjsKICBTdHJpbmcgZ2V0IGRhdGVfb2ZfYmlydGggPT4gIkRhdGUgb2YgQmlydGgiOwogIFN0cmluZyBnZXQgZGVsZXRlX2NvbmZpcm1hdGlvbiA9PiAiRGVsZXRlIENvbmZpcm1hdGlvbiI7CiAgU3RyaW5nIGdldCBkZWxldGVfY29uZmlybWF0aW9uX21lc3NhZ2UgPT4KICAgICAgIkFyZSB5b3Ugc3VyZSB5b3Ugd2FudCB0byBkZWxldGUgdGhpcyBpdGVtPyBUaGlzIGFjdGlvbiBjYW5ub3QgYmUgdW5kb25lLiI7CiAgT2JqZWN0IG9wZXJhdG9yIFtdKFN0cmluZyBrZXkpIHsKICAgIHZhciBpbmRleCA9IGtleS5pbmRleE9mKCcuJyk7CiAgICBpZiAoaW5kZXggPiAwKSB7CiAgICAgIHJldHVybiAodGhpc1trZXkuc3Vic3RyaW5nKDAsIGluZGV4KV0KICAgICAgICAgIGFzIGk2OW4uSTY5bk1lc3NhZ2VCdW5kbGUpW2tleS5zdWJzdHJpbmcoaW5kZXggKyAxKV07CiAgICB9CiAgICBzd2l0Y2ggKGtleSkgewogICAgICBjYXNlICdwcm9maWxlJzoKICAgICAgICByZXR1cm4gcHJvZmlsZTsKICAgICAgY2FzZSAnc2V0dGluZ3MnOgogICAgICAgIHJldHVybiBzZXR0aW5nczsKICAgICAgY2FzZSAnYWNjb3VudCc6CiAgICAgICAgcmV0dXJuIGFjY291bnQ7CiAgICAgIGNhc2UgJ3BlcnNvbmFsX2luZm8nOgogICAgICAgIHJldHVybiBwZXJzb25hbF9pbmZvOwogICAgICBjYXNlICdwcml2YWN5X3NldHRpbmdzJzoKICAgICAgICByZXR1cm4gcHJpdmFjeV9zZXR0aW5nczsKICAgICAgY2FzZSAnbmFtZSc6CiAgICAgICAgcmV0dXJuIG5hbWU7CiAgICAgIGNhc2UgJ2VtYWlsX2FkZHJlc3MnOgogICAgICAgIHJldHVybiBlbWFpbF9hZGRyZXNzOwogICAgICBjYXNlICdwaG9uZV9udW1iZXInOgogICAgICAgIHJldHVybiBwaG9uZV9udW1iZXI7CiAgICAgIGNhc2UgJ2RhdGVfb2ZfYmlydGgnOgogICAgICAgIHJldHVybiBkYXRlX29mX2JpcnRoOwogICAgICBjYXNlICdkZWxldGVfY29uZmlybWF0aW9uJzoKICAgICAgICByZXR1cm4gZGVsZXRlX2NvbmZpcm1hdGlvbjsKICAgICAgY2FzZSAnZGVsZXRlX2NvbmZpcm1hdGlvbl9tZXNzYWdlJzoKICAgICAgICByZXR1cm4gZGVsZXRlX2NvbmZpcm1hdGlvbl9tZXNzYWdlOwogICAgICBkZWZhdWx0OgogICAgICAgIHJldHVybiBrZXk7CiAgICB9CiAgfQp9CgpjbGFzcyBOYXZNZXNzYWdlcyBpbXBsZW1lbnRzIGk2OW4uSTY5bk1lc3NhZ2VCdW5kbGUgewogIGZpbmFsIE1lc3NhZ2VzIF9wYXJlbnQ7CiAgY29uc3QgTmF2TWVzc2FnZXModGhpcy5fcGFyZW50KTsKICBTdHJpbmcgZ2V0IGhvbWUgPT4gIkhvbWUiOwogIFN0cmluZyBnZXQgZGFzaGJvYXJkID0+ICJEYXNoYm9hcmQiOwogIFN0cmluZyBnZXQgcHJvZmlsZSA9PiAiUHJvZmlsZSI7CiAgU3RyaW5nIGdldCBzZXR0aW5ncyA9PiAiU2V0dGluZ3MiOwogIFN0cmluZyBnZXQgbm90aWZpY2F0aW9ucyA9PiAiTm90aWZpY2F0aW9ucyI7CiAgT2JqZWN0IG9wZXJhdG9yIFtdKFN0cmluZyBrZXkpIHsKICAgIHZhciBpbmRleCA9IGtleS5pbmRleE9mKCcuJyk7CiAgICBpZiAoaW5kZXggPiAwKSB7CiAgICAgIHJldHVybiAodGhpc1trZXkuc3Vic3RyaW5nKDAsIGluZGV4KV0KICAgICAgICAgIGFzIGk2OW4uSTY5bk1lc3NhZ2VCdW5kbGUpW2tleS5zdWJzdHJpbmcoaW5kZXggKyAxKV07CiAgICB9CiAgICBzd2l0Y2ggKGtleSkgewogICAgICBjYXNlICdob21lJzoKICAgICAgICByZXR1cm4gaG9tZTsKICAgICAgY2FzZSAnZGFzaGJvYXJkJzoKICAgICAgICByZXR1cm4gZGFzaGJvYXJkOwogICAgICBjYXNlICdwcm9maWxlJzoKICAgICAgICByZXR1cm4gcHJvZmlsZTsKICAgICAgY2FzZSAnc2V0dGluZ3MnOgogICAgICAgIHJldHVybiBzZXR0aW5nczsKICAgICAgY2FzZSAnbm90aWZpY2F0aW9ucyc6CiAgICAgICAgcmV0dXJuIG5vdGlmaWNhdGlvbnM7CiAgICAgIGRlZmF1bHQ6CiAgICAgICAgcmV0dXJuIGtleTsKICAgIH0KICB9Cn0KCmNsYXNzIE5vdGlmaWNhdGlvbnNNZXNzYWdlcyBpbXBsZW1lbnRzIGk2OW4uSTY5bk1lc3NhZ2VCdW5kbGUgewogIGZpbmFsIE1lc3NhZ2VzIF9wYXJlbnQ7CiAgY29uc3QgTm90aWZpY2F0aW9uc01lc3NhZ2VzKHRoaXMuX3BhcmVudCk7CiAgU3RyaW5nIGdldCB0aXRsZSA9PiAiTm90aWZpY2F0aW9ucyI7CiAgU3RyaW5nIGdldCBtYXJrX2FzX3JlYWQgPT4gIk1hcmsgYXMgcmVhZCI7CiAgU3RyaW5nIGdldCBtYXJrX2FsbF9yZWFkID0+ICJNYXJrIGFsbCBhcyByZWFkIjsKICBTdHJpbmcgZ2V0IGRlbGV0ZSA9PiAiRGVsZXRlIjsKICBTdHJpbmcgZ2V0IGZpbHRlcl9hbGwgPT4gIkFsbCI7CiAgU3RyaW5nIGdldCBmaWx0ZXJfdW5yZWFkID0+ICJVbnJlYWQiOwogIFN0cmluZyBnZXQgZmlsdGVyX3JlYWQgPT4gIlJlYWQiOwogIFN0cmluZyBnZXQgdHlwZV9yZW1pbmRlciA9PiAiUmVtaW5kZXIiOwogIFN0cmluZyBnZXQgdHlwZV9hbGVydCA9PiAiQWxlcnQiOwogIFN0cmluZyBnZXQgdHlwZV9wcm9tb3Rpb24gPT4gIlByb21vdGlvbiI7CiAgU3RyaW5nIGdldCB0eXBlX3N5c3RlbSA9PiAiU3lzdGVtIjsKICBTdHJpbmcgZ2V0IHR5cGVfY3VzdG9tID0+ICJDdXN0b20iOwogIFN0cmluZyBnZXQgZW1wdHlfdGl0bGUgPT4gIk5vIG5vdGlmaWNhdGlvbnMiOwogIFN0cmluZyBnZXQgZW1wdHlfZGVzY3JpcHRpb24gPT4KICAgICAgIllvdSdyZSBhbGwgY2F1Z2h0IHVwISBOZXcgbm90aWZpY2F0aW9ucyB3aWxsIGFwcGVhciBoZXJlLiI7CiAgU3RyaW5nIGdldCBkZWxldGVfY29uZmlybWF0aW9uX3RpdGxlID0+ICJEZWxldGUgbm90aWZpY2F0aW9uPyI7CiAgU3RyaW5nIGdldCBkZWxldGVfY29uZmlybWF0aW9uX21lc3NhZ2UgPT4KICAgICAgIlRoaXMgbm90aWZpY2F0aW9uIHdpbGwgYmUgcGVybWFuZW50bHkgcmVtb3ZlZCBmcm9tIHlvdXIgbGlzdC4iOwogIE9iamVjdCBvcGVyYXRvciBbXShTdHJpbmcga2V5KSB7CiAgICB2YXIgaW5kZXggPSBrZXkuaW5kZXhPZignLicpOwogICAgaWYgKGluZGV4ID4gMCkgewogICAgICByZXR1cm4gKHRoaXNba2V5LnN1YnN0cmluZygwLCBpbmRleCldCiAgICAgICAgICBhcyBpNjluLkk2OW5NZXNzYWdlQnVuZGxlKVtrZXkuc3Vic3RyaW5nKGluZGV4ICsgMSldOwogICAgfQogICAgc3dpdGNoIChrZXkpIHsKICAgICAgY2FzZSAndGl0bGUnOgogICAgICAgIHJldHVybiB0aXRsZTsKICAgICAgY2FzZSAnbWFya19hc19yZWFkJzoKICAgICAgICByZXR1cm4gbWFya19hc19yZWFkOwogICAgICBjYXNlICdtYXJrX2FsbF9yZWFkJzoKICAgICAgICByZXR1cm4gbWFya19hbGxfcmVhZDsKICAgICAgY2FzZSAnZGVsZXRlJzoKICAgICAgICByZXR1cm4gZGVsZXRlOwogICAgICBjYXNlICdmaWx0ZXJfYWxsJzoKICAgICAgICByZXR1cm4gZmlsdGVyX2FsbDsKICAgICAgY2FzZSAnZmlsdGVyX3VucmVhZCc6CiAgICAgICAgcmV0dXJuIGZpbHRlcl91bnJlYWQ7CiAgICAgIGNhc2UgJ2ZpbHRlcl9yZWFkJzoKICAgICAgICByZXR1cm4gZmlsdGVyX3JlYWQ7CiAgICAgIGNhc2UgJ3R5cGVfcmVtaW5kZXInOgogICAgICAgIHJldHVybiB0eXBlX3JlbWluZGVyOwogICAgICBjYXNlICd0eXBlX2FsZXJ0JzoKICAgICAgICByZXR1cm4gdHlwZV9hbGVydDsKICAgICAgY2FzZSAndHlwZV9wcm9tb3Rpb24nOgogICAgICAgIHJldHVybiB0eXBlX3Byb21vdGlvbjsKICAgICAgY2FzZSAndHlwZV9zeXN0ZW0nOgogICAgICAgIHJldHVybiB0eXBlX3N5c3RlbTsKICAgICAgY2FzZSAndHlwZV9jdXN0b20nOgogICAgICAgIHJldHVybiB0eXBlX2N1c3RvbTsKICAgICAgY2FzZSAnZW1wdHlfdGl0bGUnOgogICAgICAgIHJldHVybiBlbXB0eV90aXRsZTsKICAgICAgY2FzZSAnZW1wdHlfZGVzY3JpcHRpb24nOgogICAgICAgIHJldHVybiBlbXB0eV9kZXNjcmlwdGlvbjsKICAgICAgY2FzZSAnZGVsZXRlX2NvbmZpcm1hdGlvbl90aXRsZSc6CiAgICAgICAgcmV0dXJuIGRlbGV0ZV9jb25maXJtYXRpb25fdGl0bGU7CiAgICAgIGNhc2UgJ2RlbGV0ZV9jb25maXJtYXRpb25fbWVzc2FnZSc6CiAgICAgICAgcmV0dXJuIGRlbGV0ZV9jb25maXJtYXRpb25fbWVzc2FnZTsKICAgICAgZGVmYXVsdDoKICAgICAgICByZXR1cm4ga2V5OwogICAgfQogIH0KfQoKY2xhc3MgRXJyb3JzTWVzc2FnZXMgaW1wbGVtZW50cyBpNjluLkk2OW5NZXNzYWdlQnVuZGxlIHsKICBmaW5hbCBNZXNzYWdlcyBfcGFyZW50OwogIGNvbnN0IEVycm9yc01lc3NhZ2VzKHRoaXMuX3BhcmVudCk7CiAgU3RyaW5nIGdldCBuZXR3b3JrX2Vycm9yID0+ICJOZXR3b3JrIGVycm9yLiBQbGVhc2UgY2hlY2sgeW91ciBjb25uZWN0aW9uLiI7CiAgU3RyaW5nIGdldCB1bmtub3duX2Vycm9yID0+ICJBbiB1bmtub3duIGVycm9yIG9jY3VycmVkLiI7CiAgU3RyaW5nIGdldCB2YWxpZGF0aW9uX2Vycm9yID0+ICJQbGVhc2UgY2hlY2sgeW91ciBpbnB1dCBhbmQgdHJ5IGFnYWluLiI7CiAgU3RyaW5nIGdldCBzZXJ2ZXJfZXJyb3IgPT4gIlNlcnZlciBlcnJvci4gUGxlYXNlIHRyeSBhZ2FpbiBsYXRlci4iOwogIFN0cmluZyBnZXQgZGVmYXVsdF9lcnJvcl9tZXNzYWdlID0+CiAgICAgICJPb3BzISBTb21ldGhpbmcgd2VudCB3cm9uZy4gUGxlYXNlIHRyeSBhZ2Fpbi4iOwogIFN0cmluZyBnZXQgdXNlcl9ub3RfZm91bmQgPT4gIlVzZXIgbm90IGZvdW5kLiBQbGVhc2UgY2hlY2sgeW91ciBjcmVkZW50aWFscy4iOwogIFN0cmluZyBnZXQgZGVmYXVsdF9lcnJvcl9kZXNjcmlwdGlvbiA9PgogICAgICAiV2UgZW5jb3VudGVyZWQgYW4gZXJyb3Igd2hpbGUgcHJvY2Vzc2luZyB5b3VyIHJlcXVlc3QuIFdlIGFwb2xvZ2l6ZSBmb3IgdGhlIGluY29udmVuaWVuY2UuIFBsZWFzZSB0cnkgYWdhaW4gbGF0ZXIgb3IgY29udGFjdCBzdXBwb3J0IGlmIHRoZSBpc3N1ZSBwZXJzaXN0cy4iOwogIFN0cmluZyBnZXQgcGFnZV9ub3RfZm91bmQgPT4gIlBhZ2UgTm90IEZvdW5kIjsKICBTdHJpbmcgZ2V0IHBhZ2Vfbm90X2ZvdW5kX2Rlc2NyaXB0aW9uID0+CiAgICAgICJUaGUgcGFnZSB5b3UgYXJlIGxvb2tpbmcgZm9yIGRvZXMgbm90IGV4aXN0LiI7CiAgU3RyaW5nIGdldCB1bmV4cGVjdGVkX2Vycm9yID0+ICJBbiB1bmV4cGVjdGVkIGVycm9yIG9jY3VycmVkLiI7CiAgU3RyaW5nIGdldCByZWRpcmVjdF9lcnJvciA9PiAiUmVkaXJlY3QgRXJyb3IiOwogIFN0cmluZyBnZXQgYmFkX3JlcXVlc3QgPT4gIkludmFsaWQgcmVxdWVzdC4gUGxlYXNlIGNoZWNrIHlvdXIgaW5wdXQuIjsKICBTdHJpbmcgZ2V0IHVuYXV0aG9yaXplZCA9PiAiQXV0aGVudGljYXRpb24gcmVxdWlyZWQuIFBsZWFzZSBzaWduIGluIGFnYWluLiI7CiAgU3RyaW5nIGdldCBmb3JiaWRkZW4gPT4gIkFjY2VzcyBkZW5pZWQuIFlvdSBkb24ndCBoYXZlIHBlcm1pc3Npb24uIjsKICBTdHJpbmcgZ2V0IG5vdF9mb3VuZCA9PiAiUmVxdWVzdGVkIHJlc291cmNlIG5vdCBmb3VuZC4iOwogIFN0cmluZyBnZXQgY29uZmxpY3QgPT4gIkRhdGEgY29uZmxpY3QuIFBsZWFzZSByZWZyZXNoIGFuZCB0cnkgYWdhaW4uIjsKICBTdHJpbmcgZ2V0IHVucHJvY2Vzc2FibGVfZW50aXR5ID0+CiAgICAgICJJbnZhbGlkIGRhdGEgZm9ybWF0LiBQbGVhc2UgY2hlY2sgeW91ciBpbnB1dC4iOwogIFN0cmluZyBnZXQgaW50ZXJuYWxfc2VydmVyX2Vycm9yID0+ICJTZXJ2ZXIgZXJyb3IuIFBsZWFzZSB0cnkgYWdhaW4gbGF0ZXIuIjsKICBTdHJpbmcgZ2V0IGNvbm5lY3Rpb25fdGltZW91dCA9PgogICAgICAiQ29ubmVjdGlvbiB0aW1lb3V0LiBQbGVhc2UgY2hlY2sgeW91ciBpbnRlcm5ldC4iOwogIFN0cmluZyBnZXQgcmVjZWl2ZV90aW1lb3V0ID0+ICJSZXF1ZXN0IHRpbWVvdXQuIFBsZWFzZSB0cnkgYWdhaW4uIjsKICBTdHJpbmcgZ2V0IHNlbmRfdGltZW91dCA9PiAiVXBsb2FkIHRpbWVvdXQuIFBsZWFzZSB0cnkgYWdhaW4uIjsKICBTdHJpbmcgZ2V0IG5vX2ludGVybmV0ID0+CiAgICAgICJObyBpbnRlcm5ldCBjb25uZWN0aW9uLiBQbGVhc2UgY2hlY2sgeW91ciBuZXR3b3JrLiI7CiAgU3RyaW5nIGdldCB1bmtub3duX25ldHdvcmsgPT4gIk5ldHdvcmsgZXJyb3Igb2NjdXJyZWQuIFBsZWFzZSB0cnkgYWdhaW4uIjsKICBTdHJpbmcgZm9ybWF0X2V4Y2VwdGlvbl9tZXNzYWdlKFN0cmluZyBjb2RlLCBTdHJpbmcgcG9zdGZpeCkgPT4KICAgICAgIlRoaXMgZGF0YSBpcyB3ZWFyaW5nIHRoZSB3cm9uZyBjb3N0dW1lLCBJIGRvbid0IHJlY29nbml6ZSBpdCBbJGNvZGVdICRwb3N0Zml4IjsKICBTdHJpbmcgdHlwZV9lcnJvcl9tZXNzYWdlKFN0cmluZyBjb2RlLCBTdHJpbmcgcG9zdGZpeCkgPT4KICAgICAgIlRoaXMgZGF0YSBpcyBub3Qgd2hhdCBJIGV4cGVjdGVkLCBJIGNhbid0IHByb2Nlc3MgaXQgWyRjb2RlXSAkcG9zdGZpeCI7CiAgU3RyaW5nIGluZGV4X2Vycm9yX21lc3NhZ2UoU3RyaW5nIGNvZGUsIFN0cmluZyBwb3N0Zml4KSA9PgogICAgICAiSG1tLCBJIGNhbid0IHNlZW0gdG8gZmluZCB0aGF0IGl0ZW0gaW4gdGhlIGxpc3QgWyRjb2RlXSAkcG9zdGZpeCI7CiAgU3RyaW5nIHJhbmdlX2Vycm9yX21lc3NhZ2UoU3RyaW5nIGNvZGUsIFN0cmluZyBwb3N0Zml4KSA9PgogICAgICAiT29wcyEgVGhhdCBudW1iZXIgaXMgd2F5IG91dCBvZiBteSBjb21mb3J0IHpvbmUgWyRjb2RlXSAkcG9zdGZpeCI7CiAgU3RyaW5nIGFyZ3VtZW50X2Vycm9yX21lc3NhZ2UoU3RyaW5nIGNvZGUsIFN0cmluZyBwb3N0Zml4KSA9PgogICAgICAiSGV5ISBTb21ldGhpbmcncyBub3QgcmlnaHQgd2l0aCB3aGF0IHlvdSBnYXZlIG1lIFskY29kZV0gJHBvc3RmaXgiOwogIFN0cmluZyBzdGF0ZV9lcnJvcl9tZXNzYWdlKFN0cmluZyBjb2RlLCBTdHJpbmcgcG9zdGZpeCkgPT4KICAgICAgIkknbSBhIGJpdCBjb25mdXNlZCBhYm91dCB3aGF0IEkgc2hvdWxkIGJlIGRvaW5nIHJpZ2h0IG5vdyBbJGNvZGVdICRwb3N0Zml4IjsKICBTdHJpbmcgdW5pbXBsZW1lbnRlZF9lcnJvcl9tZXNzYWdlKFN0cmluZyBjb2RlLCBTdHJpbmcgcG9zdGZpeCkgPT4KICAgICAgIlRoaXMgZmVhdHVyZSBpcyBzdGlsbCB1bmRlciBjb25zdHJ1Y3Rpb24gWyRjb2RlXSAkcG9zdGZpeCI7CiAgU3RyaW5nIHVuc3VwcG9ydGVkX2Vycm9yX21lc3NhZ2UoU3RyaW5nIGNvZGUsIFN0cmluZyBwb3N0Zml4KSA9PgogICAgICAiU29ycnksIEkgZG9uJ3Qga25vdyBob3cgdG8gZG8gdGhhdCB5ZXQgWyRjb2RlXSAkcG9zdGZpeCI7CiAgU3RyaW5nIGNvbmN1cnJlbnRfbW9kaWZpY2F0aW9uX2Vycm9yX21lc3NhZ2UoU3RyaW5nIGNvZGUsIFN0cmluZyBwb3N0Zml4KSA9PgogICAgICAiV2hvYSEgVG9vIG1hbnkgdGhpbmdzIGhhcHBlbmluZyBhdCBvbmNlIFskY29kZV0gJHBvc3RmaXgiOwogIFN0cmluZyBvdXRfb2ZfbWVtb3J5X2Vycm9yX21lc3NhZ2UoU3RyaW5nIGNvZGUsIFN0cmluZyBwb3N0Zml4KSA9PgogICAgICAiTXkgYnJhaW4gaXMgZnVsbCEgTmVlZCB0byBjbGVhciBzb21lIHNwYWNlIFskY29kZV0gJHBvc3RmaXgiOwogIFN0cmluZyBzdGFja19vdmVyZmxvd19lcnJvcl9tZXNzYWdlKFN0cmluZyBjb2RlLCBTdHJpbmcgcG9zdGZpeCkgPT4KICAgICAgIkknbSBzdHVjayBpbiBhIGxvb3AgYW5kIGdldHRpbmcgZGl6enkgWyRjb2RlXSAkcG9zdGZpeCI7CiAgU3RyaW5nIHVua25vd25fZXJyb3JfbWVzc2FnZShTdHJpbmcgY29kZSwgU3RyaW5nIHBvc3RmaXgpID0+CiAgICAgICJTb21ldGhpbmcgdW5leHBlY3RlZCBoYXBwZW5lZCwgYnV0IGRvbid0IHdvcnJ5IFskY29kZV0gJHBvc3RmaXgiOwogIE9iamVjdCBvcGVyYXRvciBbXShTdHJpbmcga2V5KSB7CiAgICB2YXIgaW5kZXggPSBrZXkuaW5kZXhPZignLicpOwogICAgaWYgKGluZGV4ID4gMCkgewogICAgICByZXR1cm4gKHRoaXNba2V5LnN1YnN0cmluZygwLCBpbmRleCldCiAgICAgICAgICBhcyBpNjluLkk2OW5NZXNzYWdlQnVuZGxlKVtrZXkuc3Vic3RyaW5nKGluZGV4ICsgMSldOwogICAgfQogICAgc3dpdGNoIChrZXkpIHsKICAgICAgY2FzZSAnbmV0d29ya19lcnJvcic6CiAgICAgICAgcmV0dXJuIG5ldHdvcmtfZXJyb3I7CiAgICAgIGNhc2UgJ3Vua25vd25fZXJyb3InOgogICAgICAgIHJldHVybiB1bmtub3duX2Vycm9yOwogICAgICBjYXNlICd2YWxpZGF0aW9uX2Vycm9yJzoKICAgICAgICByZXR1cm4gdmFsaWRhdGlvbl9lcnJvcjsKICAgICAgY2FzZSAnc2VydmVyX2Vycm9yJzoKICAgICAgICByZXR1cm4gc2VydmVyX2Vycm9yOwogICAgICBjYXNlICdkZWZhdWx0X2Vycm9yX21lc3NhZ2UnOgogICAgICAgIHJldHVybiBkZWZhdWx0X2Vycm9yX21lc3NhZ2U7CiAgICAgIGNhc2UgJ3VzZXJfbm90X2ZvdW5kJzoKICAgICAgICByZXR1cm4gdXNlcl9ub3RfZm91bmQ7CiAgICAgIGNhc2UgJ2RlZmF1bHRfZXJyb3JfZGVzY3JpcHRpb24nOgogICAgICAgIHJldHVybiBkZWZhdWx0X2Vycm9yX2Rlc2NyaXB0aW9uOwogICAgICBjYXNlICdwYWdlX25vdF9mb3VuZCc6CiAgICAgICAgcmV0dXJuIHBhZ2Vfbm90X2ZvdW5kOwogICAgICBjYXNlICdwYWdlX25vdF9mb3VuZF9kZXNjcmlwdGlvbic6CiAgICAgICAgcmV0dXJuIHBhZ2Vfbm90X2ZvdW5kX2Rlc2NyaXB0aW9uOwogICAgICBjYXNlICd1bmV4cGVjdGVkX2Vycm9yJzoKICAgICAgICByZXR1cm4gdW5leHBlY3RlZF9lcnJvcjsKICAgICAgY2FzZSAncmVkaXJlY3RfZXJyb3InOgogICAgICAgIHJldHVybiByZWRpcmVjdF9lcnJvcjsKICAgICAgY2FzZSAnYmFkX3JlcXVlc3QnOgogICAgICAgIHJldHVybiBiYWRfcmVxdWVzdDsKICAgICAgY2FzZSAndW5hdXRob3JpemVkJzoKICAgICAgICByZXR1cm4gdW5hdXRob3JpemVkOwogICAgICBjYXNlICdmb3JiaWRkZW4nOgogICAgICAgIHJldHVybiBmb3JiaWRkZW47CiAgICAgIGNhc2UgJ25vdF9mb3VuZCc6CiAgICAgICAgcmV0dXJuIG5vdF9mb3VuZDsKICAgICAgY2FzZSAnY29uZmxpY3QnOgogICAgICAgIHJldHVybiBjb25mbGljdDsKICAgICAgY2FzZSAndW5wcm9jZXNzYWJsZV9lbnRpdHknOgogICAgICAgIHJldHVybiB1bnByb2Nlc3NhYmxlX2VudGl0eTsKICAgICAgY2FzZSAnaW50ZXJuYWxfc2VydmVyX2Vycm9yJzoKICAgICAgICByZXR1cm4gaW50ZXJuYWxfc2VydmVyX2Vycm9yOwogICAgICBjYXNlICdjb25uZWN0aW9uX3RpbWVvdXQnOgogICAgICAgIHJldHVybiBjb25uZWN0aW9uX3RpbWVvdXQ7CiAgICAgIGNhc2UgJ3JlY2VpdmVfdGltZW91dCc6CiAgICAgICAgcmV0dXJuIHJlY2VpdmVfdGltZW91dDsKICAgICAgY2FzZSAnc2VuZF90aW1lb3V0JzoKICAgICAgICByZXR1cm4gc2VuZF90aW1lb3V0OwogICAgICBjYXNlICdub19pbnRlcm5ldCc6CiAgICAgICAgcmV0dXJuIG5vX2ludGVybmV0OwogICAgICBjYXNlICd1bmtub3duX25ldHdvcmsnOgogICAgICAgIHJldHVybiB1bmtub3duX25ldHdvcms7CiAgICAgIGNhc2UgJ2Zvcm1hdF9leGNlcHRpb25fbWVzc2FnZSc6CiAgICAgICAgcmV0dXJuIGZvcm1hdF9leGNlcHRpb25fbWVzc2FnZTsKICAgICAgY2FzZSAndHlwZV9lcnJvcl9tZXNzYWdlJzoKICAgICAgICByZXR1cm4gdHlwZV9lcnJvcl9tZXNzYWdlOwogICAgICBjYXNlICdpbmRleF9lcnJvcl9tZXNzYWdlJzoKICAgICAgICByZXR1cm4gaW5kZXhfZXJyb3JfbWVzc2FnZTsKICAgICAgY2FzZSAncmFuZ2VfZXJyb3JfbWVzc2FnZSc6CiAgICAgICAgcmV0dXJuIHJhbmdlX2Vycm9yX21lc3NhZ2U7CiAgICAgIGNhc2UgJ2FyZ3VtZW50X2Vycm9yX21lc3NhZ2UnOgogICAgICAgIHJldHVybiBhcmd1bWVudF9lcnJvcl9tZXNzYWdlOwogICAgICBjYXNlICdzdGF0ZV9lcnJvcl9tZXNzYWdlJzoKICAgICAgICByZXR1cm4gc3RhdGVfZXJyb3JfbWVzc2FnZTsKICAgICAgY2FzZSAndW5pbXBsZW1lbnRlZF9lcnJvcl9tZXNzYWdlJzoKICAgICAgICByZXR1cm4gdW5pbXBsZW1lbnRlZF9lcnJvcl9tZXNzYWdlOwogICAgICBjYXNlICd1bnN1cHBvcnRlZF9lcnJvcl9tZXNzYWdlJzoKICAgICAgICByZXR1cm4gdW5zdXBwb3J0ZWRfZXJyb3JfbWVzc2FnZTsKICAgICAgY2FzZSAnY29uY3VycmVudF9tb2RpZmljYXRpb25fZXJyb3JfbWVzc2FnZSc6CiAgICAgICAgcmV0dXJuIGNvbmN1cnJlbnRfbW9kaWZpY2F0aW9uX2Vycm9yX21lc3NhZ2U7CiAgICAgIGNhc2UgJ291dF9vZl9tZW1vcnlfZXJyb3JfbWVzc2FnZSc6CiAgICAgICAgcmV0dXJuIG91dF9vZl9tZW1vcnlfZXJyb3JfbWVzc2FnZTsKICAgICAgY2FzZSAnc3RhY2tfb3ZlcmZsb3dfZXJyb3JfbWVzc2FnZSc6CiAgICAgICAgcmV0dXJuIHN0YWNrX292ZXJmbG93X2Vycm9yX21lc3NhZ2U7CiAgICAgIGNhc2UgJ3Vua25vd25fZXJyb3JfbWVzc2FnZSc6CiAgICAgICAgcmV0dXJuIHVua25vd25fZXJyb3JfbWVzc2FnZTsKICAgICAgZGVmYXVsdDoKICAgICAgICByZXR1cm4ga2V5OwogICAgfQogIH0KfQoKY2xhc3MgVmFsaWRhdGlvbk1lc3NhZ2VzIGltcGxlbWVudHMgaTY5bi5JNjluTWVzc2FnZUJ1bmRsZSB7CiAgZmluYWwgTWVzc2FnZXMgX3BhcmVudDsKICBjb25zdCBWYWxpZGF0aW9uTWVzc2FnZXModGhpcy5fcGFyZW50KTsKICBTdHJpbmcgZ2V0IHJlcXVpcmVkX2ZpZWxkID0+ICJUaGlzIGZpZWxkIGlzIHJlcXVpcmVkIjsKICBTdHJpbmcgZ2V0IGludmFsaWRfZW1haWwgPT4gIlBsZWFzZSBlbnRlciBhIHZhbGlkIGVtYWlsIGFkZHJlc3MiOwogIFN0cmluZyBnZXQgcGFzc3dvcmRfdG9vX3Nob3J0ID0+ICJQYXNzd29yZCBtdXN0IGJlIGF0IGxlYXN0IDggY2hhcmFjdGVycyI7CiAgU3RyaW5nIGdldCBwYXNzd29yZHNfZG9udF9tYXRjaCA9PiAiUGFzc3dvcmRzIGRvIG5vdCBtYXRjaCI7CiAgU3RyaW5nIGludmFsaWRfa2V5X2NvbmZpZyhTdHJpbmcgb2YsIFN0cmluZyBrZXkpID0+CiAgICAgICJJbnZhbGlkIGNvbmZpZ3VyYXRpb24gZm9yICRrZXkgaW4gJG9mLiBQbGVhc2UgY2hlY2sgeW91ciBzZXR0aW5ncy4iOwogIE9iamVjdCBvcGVyYXRvciBbXShTdHJpbmcga2V5KSB7CiAgICB2YXIgaW5kZXggPSBrZXkuaW5kZXhPZignLicpOwogICAgaWYgKGluZGV4ID4gMCkgewogICAgICByZXR1cm4gKHRoaXNba2V5LnN1YnN0cmluZygwLCBpbmRleCldCiAgICAgICAgICBhcyBpNjluLkk2OW5NZXNzYWdlQnVuZGxlKVtrZXkuc3Vic3RyaW5nKGluZGV4ICsgMSldOwogICAgfQogICAgc3dpdGNoIChrZXkpIHsKICAgICAgY2FzZSAncmVxdWlyZWRfZmllbGQnOgogICAgICAgIHJldHVybiByZXF1aXJlZF9maWVsZDsKICAgICAgY2FzZSAnaW52YWxpZF9lbWFpbCc6CiAgICAgICAgcmV0dXJuIGludmFsaWRfZW1haWw7CiAgICAgIGNhc2UgJ3Bhc3N3b3JkX3Rvb19zaG9ydCc6CiAgICAgICAgcmV0dXJuIHBhc3N3b3JkX3Rvb19zaG9ydDsKICAgICAgY2FzZSAncGFzc3dvcmRzX2RvbnRfbWF0Y2gnOgogICAgICAgIHJldHVybiBwYXNzd29yZHNfZG9udF9tYXRjaDsKICAgICAgY2FzZSAnaW52YWxpZF9rZXlfY29uZmlnJzoKICAgICAgICByZXR1cm4gaW52YWxpZF9rZXlfY29uZmlnOwogICAgICBkZWZhdWx0OgogICAgICAgIHJldHVybiBrZXk7CiAgICB9CiAgfQp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/localization/lib/messages.i69n.yaml", + "data": + "IyB7e25hbWUudGl0bGVDYXNlKCl9fSAtIEludGVybmF0aW9uYWxpemF0aW9uIE1lc3NhZ2VzCiMgQmFzZSB0cmFuc2xhdGlvbnMgZm9yIHRoZSBhcHBsaWNhdGlvbgoKIyBhcHBsaWNhdGlvbiBtZXRhZGF0YQphcHA6CiAgbmFtZTogInt7bmFtZS50aXRsZUNhc2UoKX19IgogIGRlc2NyaXB0aW9uOiAie3t7ZGVzY3JpcHRpb259fX0iCiAgd2VsY29tZV90b19hcHA6ICJXZWxjb21lIHRvIHt7bmFtZS50aXRsZUNhc2UoKX19ISIKCiMgQ29tbW9uL0dlbmVyaWMgbWVzc2FnZXMKZ2VuZXJpYzoKICBvazogIk9LIgogIGNhbmNlbDogIkNhbmNlbCIKICBzYXZlOiAiU2F2ZSIKICBkZWxldGU6ICJEZWxldGUiCiAgZWRpdDogIkVkaXQiCiAgdXBkYXRlOiAiVXBkYXRlIgogIHN1Ym1pdDogIlN1Ym1pdCIKICBjbG9zZTogIkNsb3NlIgogIGJhY2s6ICJCYWNrIgogIG5leHQ6ICJOZXh0IgogIHByZXZpb3VzOiAiUHJldmlvdXMiCiAgZG9uZTogIkRvbmUiCiAgbG9hZGluZzogIkxvYWRpbmcuLi4iCiAgZXJyb3I6ICJFcnJvciIKICBzdWNjZXNzOiAiU3VjY2VzcyIKICB3YXJuaW5nOiAiV2FybmluZyIKICBpbmZvOiAiSW5mbyIKICByZXRyeTogIlJldHJ5IgogIHJlZnJlc2g6ICJSZWZyZXNoIgogIHllczogIlllcyIKICBubzogIk5vIgogIGFkZDogIisgQWRkIgogIHRyeV9hZ2FpbjogIlRyeSBBZ2FpbiIKCiMgQ29tbW9uIG1lc3NhZ2VzICh1c2VkIGZvciBjb25zaXN0ZW5jeSBhY3Jvc3MgbW9kdWxlcykKY29tbW9uOgogIGNhbmNlbDogIkNhbmNlbCIKICBvazogIk9LIgogIHNhdmU6ICJTYXZlIgogIGRlbGV0ZTogIkRlbGV0ZSIKICBlZGl0OiAiRWRpdCIKICB0cnlfYWdhaW46ICJUcnkgQWdhaW4iCiAgd2VlazogIldlZWsiCiAgbW9udGg6ICJNb250aCIKCiMgQXV0aGVudGljYXRpb24KYXV0aDoKICByZWdpc3RlcjogIlJlZ2lzdGVyIgogIHNpZ25faW46ICJTaWduIEluIgogIHNpZ25fb3V0OiAiU2lnbiBPdXQiCiAgZG9udF9oYXZlX2FjY291bnQ6ICJEb24ndCBoYXZlIGFuIGFjY291bnQ/ICIKICBhbHJlYWR5X2hhdmVfYWNjb3VudDogIkFscmVhZHkgaGF2ZSBhbiBhY2NvdW50PyAiCiAgc2lnbl9vdXRfY29uZmlybWF0aW9uOiAiQXJlIHlvdSBzdXJlIHlvdSB3YW50IHRvIHNpZ24gb3V0PyIKCiMgUHJvZmlsZQojIFByb2ZpbGUKcHJvZmlsZToKICBwcm9maWxlOiAiUHJvZmlsZSIKICBzZXR0aW5nczogIlNldHRpbmdzIgogIGFjY291bnQ6ICJBY2NvdW50IgogIHBlcnNvbmFsX2luZm86ICJQZXJzb25hbCBJbmZvcm1hdGlvbiIKICBwcml2YWN5X3NldHRpbmdzOiAiUHJpdmFjeSBTZXR0aW5ncyIKICBuYW1lOiAiTmFtZSIKICBlbWFpbF9hZGRyZXNzOiAiRW1haWwgQWRkcmVzcyIKICBwaG9uZV9udW1iZXI6ICJQaG9uZSBOdW1iZXIiCiAgZGF0ZV9vZl9iaXJ0aDogIkRhdGUgb2YgQmlydGgiCiAgZGVsZXRlX2NvbmZpcm1hdGlvbjogIkRlbGV0ZSBDb25maXJtYXRpb24iCiAgZGVsZXRlX2NvbmZpcm1hdGlvbl9tZXNzYWdlOiAiQXJlIHlvdSBzdXJlIHlvdSB3YW50IHRvIGRlbGV0ZSB0aGlzIGl0ZW0/IFRoaXMgYWN0aW9uIGNhbm5vdCBiZSB1bmRvbmUuIgoKbmF2OgogIGhvbWU6ICJIb21lIgogIGRhc2hib2FyZDogIkRhc2hib2FyZCIKICBwcm9maWxlOiAiUHJvZmlsZSIKICBzZXR0aW5nczogIlNldHRpbmdzIgogIG5vdGlmaWNhdGlvbnM6ICJOb3RpZmljYXRpb25zIgoKIyBOb3RpZmljYXRpb25zCm5vdGlmaWNhdGlvbnM6CiAgdGl0bGU6ICJOb3RpZmljYXRpb25zIgogIG1hcmtfYXNfcmVhZDogIk1hcmsgYXMgcmVhZCIKICBtYXJrX2FsbF9yZWFkOiAiTWFyayBhbGwgYXMgcmVhZCIKICBkZWxldGU6ICJEZWxldGUiCiAgCiAgIyBGaWx0ZXIgdGFicwogIGZpbHRlcl9hbGw6ICJBbGwiCiAgZmlsdGVyX3VucmVhZDogIlVucmVhZCIKICBmaWx0ZXJfcmVhZDogIlJlYWQiCiAgCiAgIyBOb3RpZmljYXRpb24gdHlwZXMKICB0eXBlX3JlbWluZGVyOiAiUmVtaW5kZXIiCiAgdHlwZV9hbGVydDogIkFsZXJ0IgogIHR5cGVfcHJvbW90aW9uOiAiUHJvbW90aW9uIgogIHR5cGVfc3lzdGVtOiAiU3lzdGVtIgogIHR5cGVfY3VzdG9tOiAiQ3VzdG9tIgogIAogICMgRW1wdHkgc3RhdGVzCiAgZW1wdHlfdGl0bGU6ICJObyBub3RpZmljYXRpb25zIgogIGVtcHR5X2Rlc2NyaXB0aW9uOiAiWW91J3JlIGFsbCBjYXVnaHQgdXAhIE5ldyBub3RpZmljYXRpb25zIHdpbGwgYXBwZWFyIGhlcmUuIgogIAogICMgRGVsZXRlIGNvbmZpcm1hdGlvbgogIGRlbGV0ZV9jb25maXJtYXRpb25fdGl0bGU6ICJEZWxldGUgbm90aWZpY2F0aW9uPyIKICBkZWxldGVfY29uZmlybWF0aW9uX21lc3NhZ2U6ICJUaGlzIG5vdGlmaWNhdGlvbiB3aWxsIGJlIHBlcm1hbmVudGx5IHJlbW92ZWQgZnJvbSB5b3VyIGxpc3QuIgoKIyBFcnJvcnMKZXJyb3JzOgogIG5ldHdvcmtfZXJyb3I6ICJOZXR3b3JrIGVycm9yLiBQbGVhc2UgY2hlY2sgeW91ciBjb25uZWN0aW9uLiIKICB1bmtub3duX2Vycm9yOiAiQW4gdW5rbm93biBlcnJvciBvY2N1cnJlZC4iCiAgdmFsaWRhdGlvbl9lcnJvcjogIlBsZWFzZSBjaGVjayB5b3VyIGlucHV0IGFuZCB0cnkgYWdhaW4uIgogIHNlcnZlcl9lcnJvcjogIlNlcnZlciBlcnJvci4gUGxlYXNlIHRyeSBhZ2FpbiBsYXRlci4iCiAgZGVmYXVsdF9lcnJvcl9tZXNzYWdlOiAiT29wcyEgU29tZXRoaW5nIHdlbnQgd3JvbmcuIFBsZWFzZSB0cnkgYWdhaW4uIgogIHVzZXJfbm90X2ZvdW5kOiAiVXNlciBub3QgZm91bmQuIFBsZWFzZSBjaGVjayB5b3VyIGNyZWRlbnRpYWxzLiIKICBkZWZhdWx0X2Vycm9yX2Rlc2NyaXB0aW9uOiAiV2UgZW5jb3VudGVyZWQgYW4gZXJyb3Igd2hpbGUgcHJvY2Vzc2luZyB5b3VyIHJlcXVlc3QuIFdlIGFwb2xvZ2l6ZSBmb3IgdGhlIGluY29udmVuaWVuY2UuIFBsZWFzZSB0cnkgYWdhaW4gbGF0ZXIgb3IgY29udGFjdCBzdXBwb3J0IGlmIHRoZSBpc3N1ZSBwZXJzaXN0cy4iCiAgcGFnZV9ub3RfZm91bmQ6ICJQYWdlIE5vdCBGb3VuZCIKICBwYWdlX25vdF9mb3VuZF9kZXNjcmlwdGlvbjogIlRoZSBwYWdlIHlvdSBhcmUgbG9va2luZyBmb3IgZG9lcyBub3QgZXhpc3QuIgogIHVuZXhwZWN0ZWRfZXJyb3I6ICJBbiB1bmV4cGVjdGVkIGVycm9yIG9jY3VycmVkLiIKICByZWRpcmVjdF9lcnJvcjogIlJlZGlyZWN0IEVycm9yIgogIGJhZF9yZXF1ZXN0OiAiSW52YWxpZCByZXF1ZXN0LiBQbGVhc2UgY2hlY2sgeW91ciBpbnB1dC4iCiAgdW5hdXRob3JpemVkOiAiQXV0aGVudGljYXRpb24gcmVxdWlyZWQuIFBsZWFzZSBzaWduIGluIGFnYWluLiIKICBmb3JiaWRkZW46ICJBY2Nlc3MgZGVuaWVkLiBZb3UgZG9uJ3QgaGF2ZSBwZXJtaXNzaW9uLiIKICBub3RfZm91bmQ6ICJSZXF1ZXN0ZWQgcmVzb3VyY2Ugbm90IGZvdW5kLiIKICBjb25mbGljdDogIkRhdGEgY29uZmxpY3QuIFBsZWFzZSByZWZyZXNoIGFuZCB0cnkgYWdhaW4uIgogIHVucHJvY2Vzc2FibGVfZW50aXR5OiAiSW52YWxpZCBkYXRhIGZvcm1hdC4gUGxlYXNlIGNoZWNrIHlvdXIgaW5wdXQuIgogIGludGVybmFsX3NlcnZlcl9lcnJvcjogIlNlcnZlciBlcnJvci4gUGxlYXNlIHRyeSBhZ2FpbiBsYXRlci4iCiAgY29ubmVjdGlvbl90aW1lb3V0OiAiQ29ubmVjdGlvbiB0aW1lb3V0LiBQbGVhc2UgY2hlY2sgeW91ciBpbnRlcm5ldC4iCiAgcmVjZWl2ZV90aW1lb3V0OiAiUmVxdWVzdCB0aW1lb3V0LiBQbGVhc2UgdHJ5IGFnYWluLiIKICBzZW5kX3RpbWVvdXQ6ICJVcGxvYWQgdGltZW91dC4gUGxlYXNlIHRyeSBhZ2Fpbi4iCiAgbm9faW50ZXJuZXQ6ICJObyBpbnRlcm5ldCBjb25uZWN0aW9uLiBQbGVhc2UgY2hlY2sgeW91ciBuZXR3b3JrLiIKICB1bmtub3duX25ldHdvcms6ICJOZXR3b3JrIGVycm9yIG9jY3VycmVkLiBQbGVhc2UgdHJ5IGFnYWluLiIKICBmb3JtYXRfZXhjZXB0aW9uX21lc3NhZ2UoU3RyaW5nIGNvZGUsIFN0cmluZyBwb3N0Zml4KTogIlRoaXMgZGF0YSBpcyB3ZWFyaW5nIHRoZSB3cm9uZyBjb3N0dW1lLCBJIGRvbid0IHJlY29nbml6ZSBpdCBbJGNvZGVdICRwb3N0Zml4IgogIHR5cGVfZXJyb3JfbWVzc2FnZShTdHJpbmcgY29kZSwgU3RyaW5nIHBvc3RmaXgpOiAiVGhpcyBkYXRhIGlzIG5vdCB3aGF0IEkgZXhwZWN0ZWQsIEkgY2FuJ3QgcHJvY2VzcyBpdCBbJGNvZGVdICRwb3N0Zml4IgogIGluZGV4X2Vycm9yX21lc3NhZ2UoU3RyaW5nIGNvZGUsIFN0cmluZyBwb3N0Zml4KTogIkhtbSwgSSBjYW4ndCBzZWVtIHRvIGZpbmQgdGhhdCBpdGVtIGluIHRoZSBsaXN0IFskY29kZV0gJHBvc3RmaXgiCiAgcmFuZ2VfZXJyb3JfbWVzc2FnZShTdHJpbmcgY29kZSwgU3RyaW5nIHBvc3RmaXgpOiAiT29wcyEgVGhhdCBudW1iZXIgaXMgd2F5IG91dCBvZiBteSBjb21mb3J0IHpvbmUgWyRjb2RlXSAkcG9zdGZpeCIKICBhcmd1bWVudF9lcnJvcl9tZXNzYWdlKFN0cmluZyBjb2RlLCBTdHJpbmcgcG9zdGZpeCk6ICJIZXkhIFNvbWV0aGluZydzIG5vdCByaWdodCB3aXRoIHdoYXQgeW91IGdhdmUgbWUgWyRjb2RlXSAkcG9zdGZpeCIKICBzdGF0ZV9lcnJvcl9tZXNzYWdlKFN0cmluZyBjb2RlLCBTdHJpbmcgcG9zdGZpeCk6ICJJJ20gYSBiaXQgY29uZnVzZWQgYWJvdXQgd2hhdCBJIHNob3VsZCBiZSBkb2luZyByaWdodCBub3cgWyRjb2RlXSAkcG9zdGZpeCIKICB1bmltcGxlbWVudGVkX2Vycm9yX21lc3NhZ2UoU3RyaW5nIGNvZGUsIFN0cmluZyBwb3N0Zml4KTogIlRoaXMgZmVhdHVyZSBpcyBzdGlsbCB1bmRlciBjb25zdHJ1Y3Rpb24gWyRjb2RlXSAkcG9zdGZpeCIKICB1bnN1cHBvcnRlZF9lcnJvcl9tZXNzYWdlKFN0cmluZyBjb2RlLCBTdHJpbmcgcG9zdGZpeCk6ICJTb3JyeSwgSSBkb24ndCBrbm93IGhvdyB0byBkbyB0aGF0IHlldCBbJGNvZGVdICRwb3N0Zml4IgogIGNvbmN1cnJlbnRfbW9kaWZpY2F0aW9uX2Vycm9yX21lc3NhZ2UoU3RyaW5nIGNvZGUsIFN0cmluZyBwb3N0Zml4KTogIldob2EhIFRvbyBtYW55IHRoaW5ncyBoYXBwZW5pbmcgYXQgb25jZSBbJGNvZGVdICRwb3N0Zml4IgogIG91dF9vZl9tZW1vcnlfZXJyb3JfbWVzc2FnZShTdHJpbmcgY29kZSwgU3RyaW5nIHBvc3RmaXgpOiAiTXkgYnJhaW4gaXMgZnVsbCEgTmVlZCB0byBjbGVhciBzb21lIHNwYWNlIFskY29kZV0gJHBvc3RmaXgiCiAgc3RhY2tfb3ZlcmZsb3dfZXJyb3JfbWVzc2FnZShTdHJpbmcgY29kZSwgU3RyaW5nIHBvc3RmaXgpOiAiSSdtIHN0dWNrIGluIGEgbG9vcCBhbmQgZ2V0dGluZyBkaXp6eSBbJGNvZGVdICRwb3N0Zml4IgogIHVua25vd25fZXJyb3JfbWVzc2FnZShTdHJpbmcgY29kZSwgU3RyaW5nIHBvc3RmaXgpOiAiU29tZXRoaW5nIHVuZXhwZWN0ZWQgaGFwcGVuZWQsIGJ1dCBkb24ndCB3b3JyeSBbJGNvZGVdICRwb3N0Zml4IgojIFZhbGlkYXRpb24gbWVzc2FnZXMKdmFsaWRhdGlvbjoKICByZXF1aXJlZF9maWVsZDogIlRoaXMgZmllbGQgaXMgcmVxdWlyZWQiCiAgaW52YWxpZF9lbWFpbDogIlBsZWFzZSBlbnRlciBhIHZhbGlkIGVtYWlsIGFkZHJlc3MiCiAgcGFzc3dvcmRfdG9vX3Nob3J0OiAiUGFzc3dvcmQgbXVzdCBiZSBhdCBsZWFzdCA4IGNoYXJhY3RlcnMiCiAgcGFzc3dvcmRzX2RvbnRfbWF0Y2g6ICJQYXNzd29yZHMgZG8gbm90IG1hdGNoIgogIGludmFsaWRfa2V5X2NvbmZpZyhTdHJpbmcgb2YsIFN0cmluZyBrZXkpOiAiSW52YWxpZCBjb25maWd1cmF0aW9uIGZvciAka2V5IGluICRvZi4gUGxlYXNlIGNoZWNrIHlvdXIgc2V0dGluZ3MuIgoKIyBCaW9LaXQKCg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/localization/lib/src/localization_provider.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CmltcG9ydCAncGFja2FnZTpsb2NhbGl6YXRpb24vbWVzc2FnZXMuaTY5bi5kYXJ0JzsKCi8vLyBQcm92aWRlcyBsb2NhbGl6YXRpb24gdXRpbGl0aWVzIGZvciB0aGUge3tuYW1lLnRpdGxlQ2FzZSgpfX0gYXBwbGljYXRpb24KY2xhc3MgTG9jYWxpemF0aW9uUHJvdmlkZXIgewogIHN0YXRpYyBNZXNzYWdlcyBfbWVzc2FnZXMgPSBjb25zdCBNZXNzYWdlcygpOwoKICAvLy8gR2V0IHRoZSBjdXJyZW50IG1lc3NhZ2VzIGluc3RhbmNlCiAgc3RhdGljIE1lc3NhZ2VzIGdldCBtZXNzYWdlcyA9PiBfbWVzc2FnZXM7CgogIC8vLyBJbml0aWFsaXplIGxvY2FsaXphdGlvbiAoY3VycmVudGx5IG9ubHkgc3VwcG9ydHMgRW5nbGlzaCkKICBzdGF0aWMgdm9pZCBpbml0aWFsaXplKHtTdHJpbmcgbG9jYWxlID0gJ2VuJ30pIHsKICAgIC8vIEN1cnJlbnRseSBvbmx5IHN1cHBvcnRzIEVuZ2xpc2gsIGZ1dHVyZSB2ZXJzaW9ucyB3aWxsIHN1cHBvcnQgbXVsdGlwbGUgbG9jYWxlcwogICAgX21lc3NhZ2VzID0gY29uc3QgTWVzc2FnZXMoKTsKICB9CgogIC8vLyBHZXQgbWVzc2FnZXMgKGN1cnJlbnRseSBvbmx5IHN1cHBvcnRzIEVuZ2xpc2gpCiAgc3RhdGljIE1lc3NhZ2VzIGdldE1lc3NhZ2VzKFN0cmluZyBsb2NhbGUpIHsKICAgIC8vIEN1cnJlbnRseSBvbmx5IHN1cHBvcnRzIEVuZ2xpc2gsIGZ1dHVyZSB2ZXJzaW9ucyB3aWxsIHN1cHBvcnQgbXVsdGlwbGUgbG9jYWxlcwogICAgcmV0dXJuIGNvbnN0IE1lc3NhZ2VzKCk7CiAgfQoKICAvLy8gR2V0IHRoZSBjdXJyZW50IGxvY2FsZQogIHN0YXRpYyBTdHJpbmcgZ2V0IGN1cnJlbnRMb2NhbGUgPT4gJ2VuJzsKCiAgLy8vIEdldCBzdXBwb3J0ZWQgbG9jYWxlcyAoY3VycmVudGx5IG9ubHkgRW5nbGlzaCkKICBzdGF0aWMgTGlzdDxTdHJpbmc+IGdldCBzdXBwb3J0ZWRMb2NhbGVzID0+IFsnZW4nXTsKCiAgLy8vIENoZWNrIGlmIGEgbG9jYWxlIGlzIHN1cHBvcnRlZAogIHN0YXRpYyBib29sIGlzTG9jYWxlU3VwcG9ydGVkKFN0cmluZyBsb2NhbGUpIHsKICAgIHJldHVybiBzdXBwb3J0ZWRMb2NhbGVzLmNvbnRhaW5zKGxvY2FsZSk7CiAgfQoKICAvLy8gR2V0IHRoZSBiZXN0IG1hdGNoaW5nIGxvY2FsZSBmcm9tIGEgbGlzdCBvZiBwcmVmZXJyZWQgbG9jYWxlcwogIHN0YXRpYyBTdHJpbmcgZ2V0QmVzdE1hdGNoaW5nTG9jYWxlKExpc3Q8TG9jYWxlPiBwcmVmZXJyZWRMb2NhbGVzKSB7CiAgICBmb3IgKGZpbmFsIGxvY2FsZSBpbiBwcmVmZXJyZWRMb2NhbGVzKSB7CiAgICAgIGlmIChpc0xvY2FsZVN1cHBvcnRlZChsb2NhbGUubGFuZ3VhZ2VDb2RlKSkgewogICAgICAgIHJldHVybiBsb2NhbGUubGFuZ3VhZ2VDb2RlOwogICAgICB9CiAgICB9CiAgICByZXR1cm4gJ2VuJzsgLy8gRGVmYXVsdCB0byBFbmdsaXNoCiAgfQp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/localization/lib/src/strings.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmxvY2FsaXphdGlvbi9sb2NhbGl6YXRpb24uZGFydCc7CgovLy8gR2xvYmFsIHN0cmluZ3MgaW5zdGFuY2UgZm9yIGVhc3kgYWNjZXNzIHRocm91Z2hvdXQgdGhlIGFwcGxpY2F0aW9uCi8vLwovLy8gVXNhZ2U6Ci8vLyBgYGBkYXJ0Ci8vLyBpbXBvcnQgJ3BhY2thZ2U6bG9jYWxpemF0aW9uL2xvY2FsaXphdGlvbi5kYXJ0JzsKLy8vCi8vLyAvLyBBY2Nlc3Mgc3RyaW5ncyBlYXNpbHkKLy8vIFRleHQoc3RyaW5ncy5hdXRoLmxvZ2luKTsKLy8vIFRleHQoc3RyaW5ncy5nZW5lcmljLm9rKTsKLy8vIFRleHQoc3RyaW5ncy5wcm9maWxlLm5hbWUpOwovLy8gYGBgCk1lc3NhZ2VzIGdldCBzdHJpbmdzID0+IExvY2FsaXphdGlvblByb3ZpZGVyLm1lc3NhZ2VzOwo=", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/localization/pubspec.yaml", + "data": + "bmFtZTogbG9jYWxpemF0aW9uCmRlc2NyaXB0aW9uOiAiVHlwZS1zYWZlIHN0cmluZ3MgZm9yIHt7bmFtZS50aXRsZUNhc2UoKX19LCBnZW5lcmF0ZWQgZnJvbSBZQU1MIHdpdGggaTY5bi4iCnZlcnNpb246IDAuMS4wCnB1Ymxpc2hfdG86ICdub25lJwoKZW52aXJvbm1lbnQ6CiAgc2RrOiBeMy44LjAKICBmbHV0dGVyOiAiPj0zLjI5LjAiCgpkZXBlbmRlbmNpZXM6CiAgZmx1dHRlcjoKICAgIHNkazogZmx1dHRlcgogIGk2OW46IF4zLjEuMAoKZGV2X2RlcGVuZGVuY2llczoKICBmbHV0dGVyX3Rlc3Q6CiAgICBzZGs6IGZsdXR0ZXIKICBmbHV0dGVyX2xpbnRzOiBeNS4wLjAKICBidWlsZF9ydW5uZXI6IF4yLjQuMTUKCmZsdXR0ZXI6Cg==", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/localization/README.md", + "data": + "IyBMb2NhbGl6YXRpb24KCkxvY2FsaXphdGlvbiBwYWNrYWdlIGZvciB7e25hbWUudGl0bGVDYXNlKCl9fSB1c2luZyBpNjluIGZvciB0eXBlLXNhZmUgaW50ZXJuYXRpb25hbGl6YXRpb24uCgpbIVtub25zdG9wX2NsaV0oaHR0cHM6Ly9pbWcuc2hpZWxkcy5pby9iYWRnZS9zdGFydGVkJTIwd2l0aC1ub25zdG9wX2NsaS0xNjZDNEUuc3ZnP3N0eWxlPWZsYXQtc3F1YXJlKV0oaHR0cHM6Ly9wdWIuZGV2L3BhY2thZ2VzL25vbnN0b3BfY2xpKQpbIVttZWxvc10oaHR0cHM6Ly9pbWcuc2hpZWxkcy5pby9iYWRnZS9tYWludGFpbmVkJTIwd2l0aC1tZWxvcy1mNzAwZmYuc3ZnP3N0eWxlPWZsYXQtc3F1YXJlKV0oaHR0cHM6Ly9naXRodWIuY29tL2ludmVydGFzZS9tZWxvcykKCiMjIEZlYXR1cmVzIOKcqAoKLSAqKlR5cGUtc2FmZSB0cmFuc2xhdGlvbnMqKiAtIEdlbmVyYXRlZCBjbGFzc2VzIHdpdGggY29tcGlsZS10aW1lIHN0cmluZyB2YWxpZGF0aW9uCi0gKipFYXN5IGludGVncmF0aW9uKiogLSBTaW1wbGUgQVBJIHdpdGggZ2xvYmFsIGBzdHJpbmdzYCBhY2Nlc3MKLSAqKk9yZ2FuaXplZCBzdHJ1Y3R1cmUqKiAtIENhdGVnb3JpemVkIG1lc3NhZ2VzIChhdXRoLCBwcm9maWxlLCBnZW5lcmljLCBldGMuKQotICoqRnV0dXJlLXJlYWR5KiogLSBCdWlsdCBmb3IgbXVsdGktbGFuZ3VhZ2Ugc3VwcG9ydAotICoqTWVsb3MgaW50ZWdyYXRpb24qKiAtIFNlYW1sZXNzIG1vbm9yZXBvIHdvcmtmbG93CgojIyBJbnN0YWxsYXRpb24g8J+SuwoKQWRkIHRvIHlvdXIgYHB1YnNwZWMueWFtbGA6CgpgYGB5YW1sCmRlcGVuZGVuY2llczoKICBsb2NhbGl6YXRpb246CiAgICBwYXRoOiAuLi9wYWNrYWdlcy9sb2NhbGl6YXRpb24KYGBgCgojIyBVc2FnZSDwn5qACgojIyMgQmFzaWMgVXNhZ2UKCmBgYGRhcnQKaW1wb3J0ICdwYWNrYWdlOmxvY2FsaXphdGlvbi9sb2NhbGl6YXRpb24uZGFydCc7CgovLyBTaW1wbGUgYWNjZXNzIHRvIGFueSBzdHJpbmcKVGV4dChzdHJpbmdzLmF1dGgubG9naW4pOyAgICAgICAgICAgLy8gIkxvZ2luIgpUZXh0KHN0cmluZ3MuZ2VuZXJpYy5vayk7ICAgICAgICAgICAvLyAiT0siClRleHQoc3RyaW5ncy5wcm9maWxlLm5hbWUpOyAgICAgICAgIC8vICJOYW1lIgpUZXh0KHN0cmluZ3MudmFsaWRhdGlvbi5yZXF1aXJlZF9maWVsZCk7IC8vICJUaGlzIGZpZWxkIGlzIHJlcXVpcmVkIgpgYGAKCiMjIyBJbiBXaWRnZXRzCgpgYGBkYXJ0CmNsYXNzIExvZ2luQnV0dG9uIGV4dGVuZHMgU3RhdGVsZXNzV2lkZ2V0IHsKICBAb3ZlcnJpZGUKICBXaWRnZXQgYnVpbGQoQnVpbGRDb250ZXh0IGNvbnRleHQpIHsKICAgIHJldHVybiBFbGV2YXRlZEJ1dHRvbigKICAgICAgb25QcmVzc2VkOiAoKSA9PiBfbG9naW4oKSwKICAgICAgY2hpbGQ6IFRleHQoc3RyaW5ncy5hdXRoLmxvZ2luKSwKICAgICk7CiAgfQp9CmBgYAoKIyMjIEVycm9yIEhhbmRsaW5nCgpgYGBkYXJ0CnZvaWQgc2hvd0Vycm9yKCkgewogIFNjYWZmb2xkTWVzc2VuZ2VyLm9mKGNvbnRleHQpLnNob3dTbmFja0JhcigKICAgIFNuYWNrQmFyKGNvbnRlbnQ6IFRleHQoc3RyaW5ncy5lcnJvcnMubmV0d29ya19lcnJvcikpLAogICk7Cn0KYGBgCgojIyMgRm9ybSBWYWxpZGF0aW9uCgpgYGBkYXJ0ClN0cmluZz8gdmFsaWRhdGVFbWFpbChTdHJpbmc/IHZhbHVlKSB7CiAgaWYgKHZhbHVlPy5pc0VtcHR5ID8/IHRydWUpIHsKICAgIHJldHVybiBzdHJpbmdzLnZhbGlkYXRpb24ucmVxdWlyZWRfZmllbGQ7CiAgfQogIGlmICghdmFsdWUhLmNvbnRhaW5zKCdAJykpIHsKICAgIHJldHVybiBzdHJpbmdzLnZhbGlkYXRpb24uaW52YWxpZF9lbWFpbDsKICB9CiAgcmV0dXJuIG51bGw7Cn0KYGBgCgojIyBEZXZlbG9wbWVudCDwn5ug77iPCgojIyMgQWRkaW5nIE5ldyBTdHJpbmdzCgoxLiBFZGl0IGBsaWIvbWVzc2FnZXMuaTY5bi55YW1sYDoKYGBgeWFtbApuZXdfY2F0ZWdvcnk6CiAgbmV3X21lc3NhZ2U6ICJOZXcgTWVzc2FnZSBUZXh0IgogIGFub3RoZXJfbWVzc2FnZTogIkFub3RoZXIgTWVzc2FnZSIKYGBgCgoyLiBHZW5lcmF0ZSBjb2RlOgpgYGBiYXNoCiMgRnJvbSB0aGUgbW9ub3JlcG8gcm9vdAptZWxvcyBydW4gZ2VuZXJhdGU6aTY5bgoKIyBPciBmcm9tIHRoaXMgcGFja2FnZQpkYXJ0IHJ1biBidWlsZF9ydW5uZXIgYnVpbGQgLS1kZWxldGUtY29uZmxpY3Rpbmctb3V0cHV0cwpgYGAKCjMuIFVzZSB0aGUgbmV3IHN0cmluZ3M6CmBgYGRhcnQKVGV4dChzdHJpbmdzLm5ld19jYXRlZ29yeS5uZXdfbWVzc2FnZSk7IC8vICJOZXcgTWVzc2FnZSBUZXh0IgpgYGAKCiMjIyBUZXN0aW5nCgpgYGBiYXNoCiMgUnVuIHRlc3RzCmZsdXR0ZXIgdGVzdAoKIyBSdW4gd2l0aCBjb3ZlcmFnZQpmbHV0dGVyIHRlc3QgLS1jb3ZlcmFnZQpgYGAKCiMjIyBMaW50aW5nCgpgYGBiYXNoCiMgRnJvbSBtb25vcmVwbyByb290Cm1lbG9zIHJ1biBsaW50CgojIE9yIGZyb20gdGhpcyBwYWNrYWdlCmRhcnQgZm9ybWF0IC4gJiYgZGFydCBhbmFseXplIC0tZmF0YWwtaW5mb3MgLgpgYGAKCiMjIEFkdmFuY2VkIFVzYWdlIPCflKcKCiMjIyBVc2luZyBMb2NhbGl6YXRpb25Qcm92aWRlcgoKYGBgZGFydAppbXBvcnQgJ3BhY2thZ2U6bG9jYWxpemF0aW9uL2xvY2FsaXphdGlvbi5kYXJ0JzsKCi8vIEluaXRpYWxpemUgKGN1cnJlbnRseSBvbmx5IHN1cHBvcnRzIEVuZ2xpc2gpCkxvY2FsaXphdGlvblByb3ZpZGVyLmluaXRpYWxpemUoKTsKCi8vIENoZWNrIHN1cHBvcnRlZCBsb2NhbGVzCmlmIChMb2NhbGl6YXRpb25Qcm92aWRlci5pc0xvY2FsZVN1cHBvcnRlZCgnZW4nKSkgewogIC8vIEhhbmRsZSBzdXBwb3J0ZWQgbG9jYWxlCn0KCi8vIFNpbXBsZSBhY2Nlc3MgdG8gYW55IHN0cmluZwpUZXh0KHN0cmluZ3MuYXV0aC5sb2dpbik7ICAgICAgICAgICAvLyAiTG9naW4iCgpgYGAKCiMjIyBGdXR1cmUgTXVsdGktbGFuZ3VhZ2UgU3VwcG9ydAoKVGhpcyBwYWNrYWdlIGlzIGRlc2lnbmVkIHRvIHN1cHBvcnQgbXVsdGlwbGUgbGFuZ3VhZ2VzIGluIHRoZSBmdXR1cmUuIFRoZSBjdXJyZW50IGltcGxlbWVudGF0aW9uIHN1cHBvcnRzIEVuZ2xpc2ggb25seSwgYnV0IHRoZSBzdHJ1Y3R1cmUgaXMgcmVhZHkgZm9yOgoKLSBTcGFuaXNoIChgZXNgKQotIEZyZW5jaCAoYGZyYCkKLSBHZXJtYW4gKGBkZWApCi0gQWRkaXRpb25hbCBsYW5ndWFnZXMgYXMgbmVlZGVkCgojIyBQYWNrYWdlIFN0cnVjdHVyZSDwn5OBCgpgYGAKcGFja2FnZXMvbG9jYWxpemF0aW9uLwrilJzilIDilIAgbGliLwrilIIgICDilJzilIDilIAgbG9jYWxpemF0aW9uLmRhcnQgICAgICAgICAgIyBNYWluIGV4cG9ydCBmaWxlCuKUgiAgIOKUnOKUgOKUgCBtZXNzYWdlcy5pNjluLmRhcnQgICAgICAgICAjIEdlbmVyYXRlZCBpNjluIGNsYXNzZXMK4pSCICAg4pSc4pSA4pSAIG1lc3NhZ2VzLmk2OW4ueWFtbCAgICAgICAgICMgVHJhbnNsYXRpb24gc291cmNlCuKUgiAgIOKUlOKUgOKUgCBzcmMvCuKUgiAgICAgICDilJzilIDilIAgbG9jYWxpemF0aW9uX3Byb3ZpZGVyLmRhcnQgIyBBZHZhbmNlZCB1dGlsaXRpZXMK4pSCICAgICAgIOKUlOKUgOKUgCBzdHJpbmdzLmRhcnQgICAgICAgICAgICMgR2xvYmFsIHN0cmluZ3MgaW5zdGFuY2UK4pSc4pSA4pSAIHRlc3QvCuKUgiAgIOKUlOKUgOKUgCBsb2NhbGl6YXRpb25fdGVzdC5kYXJ0ICAgICAjIENvbXByZWhlbnNpdmUgdGVzdHMK4pSc4pSA4pSAIGJ1aWxkLnlhbWwgICAgICAgICAgICAgICAgICAgICAjIGk2OW4gYnVpbGQgY29uZmlndXJhdGlvbgrilJTilIDilIAgcHVic3BlYy55YW1sICAgICAgICAgICAgICAgICAgICMgUGFja2FnZSBkZXBlbmRlbmNpZXMKYGBgCgpbZmx1dHRlcl9pbnN0YWxsX2xpbmtdOiBodHRwczovL2RvY3MuZmx1dHRlci5kZXYvZ2V0LXN0YXJ0ZWQvaW5zdGFsbAo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/localization/test/localization_test.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXJfdGVzdC9mbHV0dGVyX3Rlc3QuZGFydCc7CmltcG9ydCAncGFja2FnZTpsb2NhbGl6YXRpb24vbG9jYWxpemF0aW9uLmRhcnQnOwoKdm9pZCBtYWluKCkgewogIGdyb3VwKCdMb2NhbGl6YXRpb25Qcm92aWRlciBUZXN0cycsICgpIHsKICAgIHRlc3QoJ3Nob3VsZCBpbml0aWFsaXplIHdpdGggZGVmYXVsdCBsb2NhbGUnLCAoKSB7CiAgICAgIExvY2FsaXphdGlvblByb3ZpZGVyLmluaXRpYWxpemUoKTsKICAgICAgZXhwZWN0KExvY2FsaXphdGlvblByb3ZpZGVyLmN1cnJlbnRMb2NhbGUsICdlbicpOwogICAgfSk7CgogICAgdGVzdCgnc2hvdWxkIHJldHVybiBNZXNzYWdlcyBpbnN0YW5jZScsICgpIHsKICAgICAgZmluYWwgbWVzc2FnZXMgPSBMb2NhbGl6YXRpb25Qcm92aWRlci5tZXNzYWdlczsKICAgICAgZXhwZWN0KG1lc3NhZ2VzLCBpc0E8TWVzc2FnZXM+KCkpOwogICAgfSk7CgogICAgdGVzdCgnc2hvdWxkIHJldHVybiBzdXBwb3J0ZWQgbG9jYWxlcycsICgpIHsKICAgICAgZmluYWwgbG9jYWxlcyA9IExvY2FsaXphdGlvblByb3ZpZGVyLnN1cHBvcnRlZExvY2FsZXM7CiAgICAgIGV4cGVjdChsb2NhbGVzLCBjb250YWlucygnZW4nKSk7CiAgICB9KTsKCiAgICB0ZXN0KCdzaG91bGQgY2hlY2sgaWYgbG9jYWxlIGlzIHN1cHBvcnRlZCcsICgpIHsKICAgICAgZXhwZWN0KExvY2FsaXphdGlvblByb3ZpZGVyLmlzTG9jYWxlU3VwcG9ydGVkKCdlbicpLCB0cnVlKTsKICAgICAgZXhwZWN0KExvY2FsaXphdGlvblByb3ZpZGVyLmlzTG9jYWxlU3VwcG9ydGVkKCdlcycpLCBmYWxzZSk7CiAgICB9KTsKICB9KTsKfQo=", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/README.md", + "data": + "IyBQYWNrYWdlcwoKSG9yaXpvbnRhbCBjYXBhYmlsaXRpZXMgc2hhcmVkIGFjcm9zcyBmZWF0dXJlczogbmV0d29ya2luZywgZGVzaWduIHN5c3RlbSwKbG9nZ2luZywgYW5hbHl0aWNzLCBkZXBlbmRlbmN5IGluamVjdGlvbi4KCkEgcGFja2FnZSBoZXJlIG11c3Qgbm90IGRlcGVuZCBvbiBhIGBmZWF0dXJlcy9gIHBhY2thZ2UgLSBkZXBlbmRlbmNpZXMgcG9pbnQKaW53YXJkLCBmcm9tIGZlYXR1cmVzIHRvIHBhY2thZ2VzLgoKQWRkIG9uZSB3aXRoOgoKYGBgc2gKbm9uc3RvcCBjcmVhdGUgcGFja2FnZSBteV9wYWNrYWdlIC1vIHBhY2thZ2VzCmBgYAo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/utils/lib/src/number_formatter.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmludGwvaW50bC5kYXJ0JzsKCi8vLyBFeHRlbnNpb24gZm9yIGZvcm1hdHRpbmcgZGVjaW1hbCBudW1iZXJzIHdpdGggY2xlYW4sIHByb2Zlc3Npb25hbCBkaXNwbGF5CmV4dGVuc2lvbiBEb3VibGVGb3JtYXR0aW5nIG9uIGRvdWJsZSB7CiAgLy8vIEZvcm1hdHMgZGVjaW1hbCB2YWx1ZXMsIHJlbW92aW5nIHVubmVjZXNzYXJ5IC4wIGZvciB3aG9sZSBudW1iZXJzCiAgU3RyaW5nIGZvcm1hdCh7aW50IGRlY2ltYWxQbGFjZXMgPSAxfSkgewogICAgZmluYWwgZm9ybWF0dGVyID0gTnVtYmVyRm9ybWF0KCcjLiR7JyMnICogZGVjaW1hbFBsYWNlc30nKTsKICAgIHJldHVybiBmb3JtYXR0ZXIuZm9ybWF0KHRoaXMpOwogIH0KCiAgLy8vIEZvcm1hdHMgc2NvcmUgdmFsdWVzIHdpdGggY2xlYW4gZGVjaW1hbCBoYW5kbGluZwogIFN0cmluZyBhc1Njb3JlKCkgPT4gZm9ybWF0KGRlY2ltYWxQbGFjZXM6IDEpOwoKICAvLy8gRm9ybWF0cyBIUlYgdmFsdWVzIHdpdGggc21hcnQgZGVjaW1hbCBoYW5kbGluZwogIFN0cmluZyBhc0hydigpID0+IGZvcm1hdChkZWNpbWFsUGxhY2VzOiAxKTsKCiAgLy8vIEZvcm1hdHMgc2xlZXAgaG91cnMgd2l0aCBzbWFydCBkZWNpbWFsIGhhbmRsaW5nCiAgU3RyaW5nIGFzU2xlZXBIb3VycygpID0+IGZvcm1hdChkZWNpbWFsUGxhY2VzOiAxKTsKCiAgLy8vIEZvcm1hdHMgcmVzcGlyYXRvcnkgcmF0ZSB3aXRoIHNtYXJ0IGRlY2ltYWwgaGFuZGxpbmcKICBTdHJpbmcgYXNSZXNwaXJhdG9yeVJhdGUoKSA9PiBmb3JtYXQoZGVjaW1hbFBsYWNlczogMSk7Cn0KCi8vLyBFeHRlbnNpb24gZm9yIGZvcm1hdHRpbmcgYW55IG51bWVyaWMgdmFsdWUKZXh0ZW5zaW9uIE51bUZvcm1hdHRpbmcgb24gbnVtIHsKICAvLy8gRm9ybWF0cyBsYXJnZSBudW1iZXJzIHdpdGggSywgTSwgQiBzdWZmaXhlcyBhbmQgc21hcnQgZGVjaW1hbCBoYW5kbGluZwogIFN0cmluZyBjb21wYWN0KCkgewogICAgZmluYWwgTnVtYmVyRm9ybWF0IGZvcm1hdHRlciA9IE51bWJlckZvcm1hdC5jb21wYWN0KGxvY2FsZTogJ2VuX1VTJyk7CiAgICByZXR1cm4gZm9ybWF0dGVyLmZvcm1hdCh0aGlzKTsKICB9CgogIC8vLyBGb3JtYXRzIGhlYXJ0IHJhdGUgdmFsdWVzIChhbHdheXMgd2hvbGUgbnVtYmVycykKICBTdHJpbmcgYXNIZWFydFJhdGUoKSA9PiByb3VuZCgpLnRvU3RyaW5nKCk7Cn0KCi8vLyBFeHRlbnNpb24gZm9yIGZvcm1hdHRpbmcgdGltZSBkdXJhdGlvbiBmcm9tIHNlY29uZHMKZXh0ZW5zaW9uIEludFRpbWVGb3JtYXR0aW5nIG9uIGludCB7CiAgLy8vIEZvcm1hdCB0aW1lIGZyb20gc2Vjb25kcyB0byAxIHNlYyBvciAxbWluIDFzZWMgb3IgMWhyIDFtaW4KICBTdHJpbmcgYXNUaW1lKCkgewogICAgaWYgKHRoaXMgPCA2MCkgewogICAgICByZXR1cm4gJyR0aGlzIHNlYyc7CiAgICB9IGVsc2UgaWYgKHRoaXMgPCAzNjAwKSB7CiAgICAgIGZpbmFsIG1pbnV0ZXMgPSB0aGlzIH4vIDYwOwogICAgICBmaW5hbCByZW1haW5pbmdTZWNvbmRzID0gdGhpcyAlIDYwOwogICAgICBpZiAocmVtYWluaW5nU2Vjb25kcyA9PSAwKSB7CiAgICAgICAgcmV0dXJuICckbWludXRlcyBtaW4nOwogICAgICB9CiAgICAgIHJldHVybiAnJG1pbnV0ZXMgbWluICRyZW1haW5pbmdTZWNvbmRzIHNlYyc7CiAgICB9IGVsc2UgewogICAgICBmaW5hbCBob3VycyA9IHRoaXMgfi8gMzYwMDsKICAgICAgZmluYWwgcmVtYWluaW5nTWludXRlcyA9ICh0aGlzICUgMzYwMCkgfi8gNjA7CiAgICAgIGlmIChyZW1haW5pbmdNaW51dGVzID09IDApIHsKICAgICAgICByZXR1cm4gJyRob3VycyBocic7CiAgICAgIH0KICAgICAgcmV0dXJuICckaG91cnMgaHIgJHJlbWFpbmluZ01pbnV0ZXMgbWluJzsKICAgIH0KICB9Cn0KCmV4dGVuc2lvbiBEYXRlVGltZUZvcm1hdHRpbmcgb24gRGF0ZVRpbWUgewogIFN0cmluZyBmb3JtYXRSZWxhdGl2ZSgpIHsKICAgIGZpbmFsIG5vdyA9IERhdGVUaW1lLm5vdygpOwogICAgZmluYWwgZGlmZmVyZW5jZSA9IG5vdy5kaWZmZXJlbmNlKHRoaXMpOwoKICAgIGlmIChkaWZmZXJlbmNlLmluTWludXRlcyA8IDEpIHsKICAgICAgcmV0dXJuICdKdXN0IG5vdyc7CiAgICB9IGVsc2UgaWYgKGRpZmZlcmVuY2UuaW5NaW51dGVzIDwgNjApIHsKICAgICAgcmV0dXJuICcke2RpZmZlcmVuY2UuaW5NaW51dGVzfW0gYWdvJzsKICAgIH0gZWxzZSBpZiAoZGlmZmVyZW5jZS5pbkhvdXJzIDwgMjQpIHsKICAgICAgcmV0dXJuICcke2RpZmZlcmVuY2UuaW5Ib3Vyc31oIGFnbyc7CiAgICB9IGVsc2UgaWYgKGRpZmZlcmVuY2UuaW5EYXlzID09IDEpIHsKICAgICAgcmV0dXJuICdZZXN0ZXJkYXknOwogICAgfSBlbHNlIGlmIChkaWZmZXJlbmNlLmluRGF5cyA8IDcpIHsKICAgICAgcmV0dXJuICcke2RpZmZlcmVuY2UuaW5EYXlzfWQgYWdvJzsKICAgIH0gZWxzZSB7CiAgICAgIHJldHVybiAnJGRheS8kbW9udGgvJHllYXInOwogICAgfQogIH0KfQo=", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/utils/lib/utils.dart", + "data": + "bGlicmFyeTsKCmV4cG9ydCAnc3JjL251bWJlcl9mb3JtYXR0ZXIuZGFydCc7Cg==", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/utils/pubspec.yaml", + "data": + "bmFtZTogdXRpbHMKZGVzY3JpcHRpb246ICJTaGFyZWQsIGRlcGVuZGVuY3ktbGlnaHQgaGVscGVycyBmb3Ige3tuYW1lLnRpdGxlQ2FzZSgpfX0uIgp2ZXJzaW9uOiAwLjEuMApwdWJsaXNoX3RvOiAnbm9uZScKCmVudmlyb25tZW50OgogIHNkazogXjMuOC4wCiAgZmx1dHRlcjogIj49My4yOS4wIgoKZGVwZW5kZW5jaWVzOgogIGZsdXR0ZXI6CiAgICBzZGs6IGZsdXR0ZXIKICBpbnRsOiBeMC4yMC4yCgpkZXZfZGVwZW5kZW5jaWVzOgogIGZsdXR0ZXJfdGVzdDoKICAgIHNkazogZmx1dHRlcgogIGZsdXR0ZXJfbGludHM6IF41LjAuMAoKZmx1dHRlcjoK", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/analytics.dart", + "data": + "bGlicmFyeTsKCmltcG9ydCAncGFja2FnZTphbmFseXRpY3Mvc3JjL2NsaWVudC9pbmRleC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmFuYWx5dGljcy9zcmMvY29uZmlnL2luZGV4LmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Y29yZS9jb3JlLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZGkvZGkuZGFydCc7CgpleHBvcnQgJ3NyYy9jbGllbnQvaW5kZXguZGFydCc7CmV4cG9ydCAnc3JjL2NvbmZpZy9pbmRleC5kYXJ0JzsKZXhwb3J0ICdzcmMvbW9kZWxzL2luZGV4LmRhcnQnOwpleHBvcnQgJ3NyYy9vYnNlcnZlci9pbmRleC5kYXJ0JzsKZXhwb3J0ICdzcmMvdXRpbHMvaW5kZXguZGFydCc7CgpGdXR1cmU8dm9pZD4gaW5pdCh7CiAgQW5hbHl0aWNzQ29uZmlnIGNvbmZpZyA9IGNvbnN0IERlZmF1bHRBbmFseXRpY3NDb25maWcoKSwKfSkgYXN5bmMgewogIGF3YWl0IHJlZ2lzdGVyQW5hbHl0aWNzV2l0aERJKGNvbmZpZyk7CiAgZmluYWwgbG9nZ2VyID0gZGkuZ2V0PExvZ2dlcj4oKTsKICBsb2dnZXIuaSgnQW5hbHl0aWNzIG1vZHVsZSBpbml0aWFsaXplZCB3aXRoIEZpcmViYXNlIEFuYWx5dGljcycpOwp9CgpGdXR1cmU8dm9pZD4gcmVnaXN0ZXJBbmFseXRpY3NXaXRoREkoQW5hbHl0aWNzQ29uZmlnIGNvbmZpZykgYXN5bmMgewogIGZpbmFsIGxvZ2dlciA9IGRpLmdldDxMb2dnZXI+KCk7CgogIGZpbmFsIGFuYWx5dGljc0NsaWVudCA9IEZpcmViYXNlQW5hbHl0aWNzQ2xpZW50KGNvbmZpZyk7CiAgZGkucmVnaXN0ZXI8QW5hbHl0aWNzQ2xpZW50PigKICAgIGFuYWx5dGljc0NsaWVudCwKICAgIGRpc3Bvc2U6IChjbGllbnQpID0+IGNsaWVudC5kaXNwb3NlKCksCiAgKTsKICBkaS5yZWdpc3RlcjxBbmFseXRpY3NDb25maWc+KGNvbmZpZyk7CgogIGlmIChjb25maWcuZW5hYmxlQW5hbHl0aWNzKSB7CiAgICBsb2dnZXIuaSgn8J+UjSBBbmFseXRpY3MgY2xpZW50IHJlZ2lzdGVyZWQgd2l0aCBjb2xsZWN0aW9uIGVuYWJsZWQnKTsKICB9IGVsc2UgewogICAgbG9nZ2VyLmkoJ/CflI0gQW5hbHl0aWNzIGNsaWVudCByZWdpc3RlcmVkIHdpdGggY29sbGVjdGlvbiBkaXNhYmxlZCcpOwogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/client/analytics_client.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmFuYWx5dGljcy9zcmMvbW9kZWxzL2FuYWx5dGljc19ldmVudC5kYXJ0JzsKCmFic3RyYWN0IGNsYXNzIEFuYWx5dGljc0NsaWVudCB7CiAgRnV0dXJlPHZvaWQ+IGxvZ0V2ZW50KHsKICAgIHJlcXVpcmVkIFN0cmluZyBuYW1lLAogICAgTWFwPFN0cmluZywgZHluYW1pYz4/IHBhcmFtZXRlcnMsCiAgfSk7CgogIEZ1dHVyZTx2b2lkPiBsb2dDdXN0b21FdmVudChBbmFseXRpY3NFdmVudCBldmVudCk7CgogIEZ1dHVyZTx2b2lkPiBzZXRVc2VySWQoU3RyaW5nPyB1c2VySWQpOwoKICBGdXR1cmU8dm9pZD4gc2V0VXNlclByb3BlcnR5KHtyZXF1aXJlZCBTdHJpbmcgbmFtZSwgcmVxdWlyZWQgU3RyaW5nPyB2YWx1ZX0pOwoKICBGdXR1cmU8dm9pZD4gcmVzZXRBbmFseXRpY3NEYXRhKCk7CgogIEZ1dHVyZTx2b2lkPiBzZXRBbmFseXRpY3NDb2xsZWN0aW9uRW5hYmxlZChib29sIGVuYWJsZWQpOwoKICBGdXR1cmU8dm9pZD4gbG9nU2NyZWVuVmlldyh7CiAgICByZXF1aXJlZCBTdHJpbmcgc2NyZWVuTmFtZSwKICAgIFN0cmluZz8gc2NyZWVuQ2xhc3MsCiAgICBNYXA8U3RyaW5nLCBkeW5hbWljPj8gcGFyYW1ldGVycywKICB9KTsKCiAgRnV0dXJlPHZvaWQ+IGxvZ0FwcE9wZW4oe01hcDxTdHJpbmcsIGR5bmFtaWM+PyBwYXJhbWV0ZXJzfSk7CgogIEZ1dHVyZTx2b2lkPiBsb2dMb2dpbih7CiAgICBTdHJpbmc/IGxvZ2luTWV0aG9kLAogICAgTWFwPFN0cmluZywgZHluYW1pYz4/IHBhcmFtZXRlcnMsCiAgfSk7CgogIEZ1dHVyZTx2b2lkPiBsb2dTaWduVXAoewogICAgU3RyaW5nPyBzaWduVXBNZXRob2QsCiAgICBNYXA8U3RyaW5nLCBkeW5hbWljPj8gcGFyYW1ldGVycywKICB9KTsKCiAgRnV0dXJlPHZvaWQ+IGxvZ1B1cmNoYXNlKHsKICAgIHJlcXVpcmVkIFN0cmluZyBjdXJyZW5jeSwKICAgIHJlcXVpcmVkIGRvdWJsZSB2YWx1ZSwKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBwYXJhbWV0ZXJzLAogIH0pOwoKICB2b2lkIGRpc3Bvc2UoKTsKfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/client/firebase_analytics_client.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmFuYWx5dGljcy9zcmMvY2xpZW50L2FuYWx5dGljc19jbGllbnQuZGFydCc7CmltcG9ydCAncGFja2FnZTphbmFseXRpY3Mvc3JjL2NvbmZpZy9hbmFseXRpY3NfY29uZmlnLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6YW5hbHl0aWNzL3NyYy9tb2RlbHMvYW5hbHl0aWNzX2V2ZW50LmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Y29yZS9jb3JlLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZGkvZGkuZGFydCc7CmltcG9ydCAncGFja2FnZTpmaXJlYmFzZV9hbmFseXRpY3MvZmlyZWJhc2VfYW5hbHl0aWNzLmRhcnQnOwoKY2xhc3MgRmlyZWJhc2VBbmFseXRpY3NDbGllbnQgaW1wbGVtZW50cyBBbmFseXRpY3NDbGllbnQgewogIEZpcmViYXNlQW5hbHl0aWNzQ2xpZW50KHRoaXMuX2NvbmZpZykKICAgIDogX2FuYWx5dGljcyA9IEZpcmViYXNlQW5hbHl0aWNzLmluc3RhbmNlIHsKICAgIF9zZXR1cEFuYWx5dGljcygpOwogIH0KCiAgZmluYWwgQW5hbHl0aWNzQ29uZmlnIF9jb25maWc7CiAgZmluYWwgRmlyZWJhc2VBbmFseXRpY3MgX2FuYWx5dGljczsKICBmaW5hbCBMb2dnZXIgX2xvZ2dlciA9IGRpLmdldDxMb2dnZXI+KCk7CgogIHZvaWQgX3NldHVwQW5hbHl0aWNzKCkgewogICAgX2FuYWx5dGljcy5zZXRBbmFseXRpY3NDb2xsZWN0aW9uRW5hYmxlZChfY29uZmlnLmVuYWJsZUFuYWx5dGljcyk7CgogICAgaWYgKF9jb25maWcudXNlcklkICE9IG51bGwpIHsKICAgICAgX2FuYWx5dGljcy5zZXRVc2VySWQoaWQ6IF9jb25maWcudXNlcklkKTsKICAgICAgX2xvZ2dlci5kKCfwn5SNIEFuYWx5dGljcyB1c2VyIElEIHNldDogJHtfY29uZmlnLnVzZXJJZH0nKTsKICAgIH0KCiAgICBpZiAoX2NvbmZpZy5kZWZhdWx0VXNlclByb3BlcnRpZXMgIT0gbnVsbCkgewogICAgICBmb3IgKGZpbmFsIGVudHJ5IGluIF9jb25maWcuZGVmYXVsdFVzZXJQcm9wZXJ0aWVzIS5lbnRyaWVzKSB7CiAgICAgICAgX2FuYWx5dGljcy5zZXRVc2VyUHJvcGVydHkobmFtZTogZW50cnkua2V5LCB2YWx1ZTogZW50cnkudmFsdWUpOwogICAgICB9CiAgICAgIF9sb2dnZXIuZCgn8J+UjSBBbmFseXRpY3MgZGVmYXVsdCB1c2VyIHByb3BlcnRpZXMgc2V0Jyk7CiAgICB9CgogICAgaWYgKF9jb25maWcuZW5hYmxlRGVidWdMb2dnaW5nKSB7CiAgICAgIF9sb2dnZXIuZCgn8J+UjSBGaXJlYmFzZSBBbmFseXRpY3MgaW5pdGlhbGl6ZWQgd2l0aCBkZWJ1ZyBsb2dnaW5nIGVuYWJsZWQnKTsKICAgIH0gZWxzZSB7CiAgICAgIF9sb2dnZXIuaSgn8J+UjSBGaXJlYmFzZSBBbmFseXRpY3MgaW5pdGlhbGl6ZWQnKTsKICAgIH0KICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTx2b2lkPiBsb2dFdmVudCh7CiAgICByZXF1aXJlZCBTdHJpbmcgbmFtZSwKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBwYXJhbWV0ZXJzLAogIH0pIGFzeW5jIHsKICAgIHRyeSB7CiAgICAgIGlmICghX2NvbmZpZy5lbmFibGVBbmFseXRpY3MpIHsKICAgICAgICBpZiAoX2NvbmZpZy5lbmFibGVEZWJ1Z0xvZ2dpbmcpIHsKICAgICAgICAgIF9sb2dnZXIuZCgn8J+UjSBBbmFseXRpY3MgZGlzYWJsZWQsIHNraXBwaW5nIGV2ZW50OiAkbmFtZScpOwogICAgICAgIH0KICAgICAgICByZXR1cm47CiAgICAgIH0KCiAgICAgIGF3YWl0IF9hbmFseXRpY3MubG9nRXZlbnQoCiAgICAgICAgbmFtZTogbmFtZSwKICAgICAgICBwYXJhbWV0ZXJzOiBwYXJhbWV0ZXJzPy5jYXN0PFN0cmluZywgT2JqZWN0PigpLAogICAgICApOwoKICAgICAgaWYgKF9jb25maWcuZW5hYmxlRGVidWdMb2dnaW5nKSB7CiAgICAgICAgX2xvZ2dlci5kKAogICAgICAgICAgJ/CflI0gQW5hbHl0aWNzIGV2ZW50IGxvZ2dlZDogJG5hbWUgd2l0aCBwYXJhbWV0ZXJzOiAkcGFyYW1ldGVycycsCiAgICAgICAgKTsKICAgICAgfQogICAgfSBjYXRjaCAoZSwgcykgewogICAgICBfbG9nZ2VyLmUoJ0ZhaWxlZCB0byBsb2cgYW5hbHl0aWNzIGV2ZW50OiAkbmFtZScsIGUsIHMpOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IGxvZ0N1c3RvbUV2ZW50KEFuYWx5dGljc0V2ZW50IGV2ZW50KSBhc3luYyB7CiAgICBhd2FpdCBsb2dFdmVudChuYW1lOiBldmVudC5uYW1lLCBwYXJhbWV0ZXJzOiBldmVudC5wYXJhbWV0ZXJzKTsKICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTx2b2lkPiBzZXRVc2VySWQoU3RyaW5nPyB1c2VySWQpIGFzeW5jIHsKICAgIHRyeSB7CiAgICAgIGF3YWl0IF9hbmFseXRpY3Muc2V0VXNlcklkKGlkOiB1c2VySWQpOwoKICAgICAgaWYgKF9jb25maWcuZW5hYmxlRGVidWdMb2dnaW5nKSB7CiAgICAgICAgX2xvZ2dlci5kKCfwn5SNIEFuYWx5dGljcyB1c2VyIElEIHNldDogJHVzZXJJZCcpOwogICAgICB9CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2dnZXIuZSgnRmFpbGVkIHRvIHNldCBhbmFseXRpY3MgdXNlciBJRDogJHVzZXJJZCcsIGUsIHMpOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IHNldFVzZXJQcm9wZXJ0eSh7CiAgICByZXF1aXJlZCBTdHJpbmcgbmFtZSwKICAgIHJlcXVpcmVkIFN0cmluZz8gdmFsdWUsCiAgfSkgYXN5bmMgewogICAgdHJ5IHsKICAgICAgYXdhaXQgX2FuYWx5dGljcy5zZXRVc2VyUHJvcGVydHkobmFtZTogbmFtZSwgdmFsdWU6IHZhbHVlKTsKCiAgICAgIGlmIChfY29uZmlnLmVuYWJsZURlYnVnTG9nZ2luZykgewogICAgICAgIF9sb2dnZXIuZCgn8J+UjSBBbmFseXRpY3MgdXNlciBwcm9wZXJ0eSBzZXQ6ICRuYW1lID0gJHZhbHVlJyk7CiAgICAgIH0KICAgIH0gY2F0Y2ggKGUsIHMpIHsKICAgICAgX2xvZ2dlci5lKCdGYWlsZWQgdG8gc2V0IGFuYWx5dGljcyB1c2VyIHByb3BlcnR5OiAkbmFtZScsIGUsIHMpOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IHJlc2V0QW5hbHl0aWNzRGF0YSgpIGFzeW5jIHsKICAgIHRyeSB7CiAgICAgIGF3YWl0IF9hbmFseXRpY3MucmVzZXRBbmFseXRpY3NEYXRhKCk7CgogICAgICBpZiAoX2NvbmZpZy5lbmFibGVEZWJ1Z0xvZ2dpbmcpIHsKICAgICAgICBfbG9nZ2VyLmQoJ/CflI0gQW5hbHl0aWNzIGRhdGEgcmVzZXQnKTsKICAgICAgfQogICAgfSBjYXRjaCAoZSwgcykgewogICAgICBfbG9nZ2VyLmUoJ0ZhaWxlZCB0byByZXNldCBhbmFseXRpY3MgZGF0YScsIGUsIHMpOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IHNldEFuYWx5dGljc0NvbGxlY3Rpb25FbmFibGVkKGJvb2wgZW5hYmxlZCkgYXN5bmMgewogICAgdHJ5IHsKICAgICAgYXdhaXQgX2FuYWx5dGljcy5zZXRBbmFseXRpY3NDb2xsZWN0aW9uRW5hYmxlZChlbmFibGVkKTsKCiAgICAgIGlmIChfY29uZmlnLmVuYWJsZURlYnVnTG9nZ2luZykgewogICAgICAgIF9sb2dnZXIuZCgKICAgICAgICAgICfwn5SNIEFuYWx5dGljcyBjb2xsZWN0aW9uICR7ZW5hYmxlZCA/ICdlbmFibGVkJyA6ICdkaXNhYmxlZCd9JywKICAgICAgICApOwogICAgICB9CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2dnZXIuZSgnRmFpbGVkIHRvIHNldCBhbmFseXRpY3MgY29sbGVjdGlvbiBlbmFibGVkOiAkZW5hYmxlZCcsIGUsIHMpOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IGxvZ1NjcmVlblZpZXcoewogICAgcmVxdWlyZWQgU3RyaW5nIHNjcmVlbk5hbWUsCiAgICBTdHJpbmc/IHNjcmVlbkNsYXNzLAogICAgTWFwPFN0cmluZywgZHluYW1pYz4/IHBhcmFtZXRlcnMsCiAgfSkgYXN5bmMgewogICAgZmluYWwgZXZlbnRQYXJhbWV0ZXJzID0gPFN0cmluZywgZHluYW1pYz57CiAgICAgIFByZWRlZmluZWRQYXJhbWV0ZXJzLnNjcmVlbk5hbWU6IHNjcmVlbk5hbWUsCiAgICAgIGlmIChzY3JlZW5DbGFzcyAhPSBudWxsKSBQcmVkZWZpbmVkUGFyYW1ldGVycy5zY3JlZW5DbGFzczogc2NyZWVuQ2xhc3MsCiAgICAgIC4uLj9wYXJhbWV0ZXJzLAogICAgfTsKCiAgICBhd2FpdCBsb2dFdmVudCgKICAgICAgbmFtZTogUHJlZGVmaW5lZEV2ZW50cy5zY3JlZW5WaWV3LAogICAgICBwYXJhbWV0ZXJzOiBldmVudFBhcmFtZXRlcnMsCiAgICApOwogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IGxvZ0FwcE9wZW4oe01hcDxTdHJpbmcsIGR5bmFtaWM+PyBwYXJhbWV0ZXJzfSkgYXN5bmMgewogICAgYXdhaXQgbG9nRXZlbnQobmFtZTogUHJlZGVmaW5lZEV2ZW50cy5hcHBPcGVuLCBwYXJhbWV0ZXJzOiBwYXJhbWV0ZXJzKTsKICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTx2b2lkPiBsb2dMb2dpbih7CiAgICBTdHJpbmc/IGxvZ2luTWV0aG9kLAogICAgTWFwPFN0cmluZywgZHluYW1pYz4/IHBhcmFtZXRlcnMsCiAgfSkgYXN5bmMgewogICAgZmluYWwgZXZlbnRQYXJhbWV0ZXJzID0gPFN0cmluZywgZHluYW1pYz57CiAgICAgIGlmIChsb2dpbk1ldGhvZCAhPSBudWxsKSBQcmVkZWZpbmVkUGFyYW1ldGVycy5tZXRob2Q6IGxvZ2luTWV0aG9kLAogICAgICAuLi4/cGFyYW1ldGVycywKICAgIH07CgogICAgYXdhaXQgbG9nRXZlbnQobmFtZTogUHJlZGVmaW5lZEV2ZW50cy5sb2dpbiwgcGFyYW1ldGVyczogZXZlbnRQYXJhbWV0ZXJzKTsKICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTx2b2lkPiBsb2dTaWduVXAoewogICAgU3RyaW5nPyBzaWduVXBNZXRob2QsCiAgICBNYXA8U3RyaW5nLCBkeW5hbWljPj8gcGFyYW1ldGVycywKICB9KSBhc3luYyB7CiAgICBmaW5hbCBldmVudFBhcmFtZXRlcnMgPSA8U3RyaW5nLCBkeW5hbWljPnsKICAgICAgaWYgKHNpZ25VcE1ldGhvZCAhPSBudWxsKSBQcmVkZWZpbmVkUGFyYW1ldGVycy5tZXRob2Q6IHNpZ25VcE1ldGhvZCwKICAgICAgLi4uP3BhcmFtZXRlcnMsCiAgICB9OwoKICAgIGF3YWl0IGxvZ0V2ZW50KG5hbWU6IFByZWRlZmluZWRFdmVudHMuc2lnblVwLCBwYXJhbWV0ZXJzOiBldmVudFBhcmFtZXRlcnMpOwogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IGxvZ1B1cmNoYXNlKHsKICAgIHJlcXVpcmVkIFN0cmluZyBjdXJyZW5jeSwKICAgIHJlcXVpcmVkIGRvdWJsZSB2YWx1ZSwKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBwYXJhbWV0ZXJzLAogIH0pIGFzeW5jIHsKICAgIGZpbmFsIGV2ZW50UGFyYW1ldGVycyA9IDxTdHJpbmcsIGR5bmFtaWM+ewogICAgICBQcmVkZWZpbmVkUGFyYW1ldGVycy5jdXJyZW5jeTogY3VycmVuY3ksCiAgICAgIFByZWRlZmluZWRQYXJhbWV0ZXJzLnZhbHVlOiB2YWx1ZSwKICAgICAgLi4uP3BhcmFtZXRlcnMsCiAgICB9OwoKICAgIGF3YWl0IGxvZ0V2ZW50KAogICAgICBuYW1lOiBQcmVkZWZpbmVkRXZlbnRzLnB1cmNoYXNlLAogICAgICBwYXJhbWV0ZXJzOiBldmVudFBhcmFtZXRlcnMsCiAgICApOwogIH0KCiAgQG92ZXJyaWRlCiAgdm9pZCBkaXNwb3NlKCkgewogICAgLy8gRmlyZWJhc2UgQW5hbHl0aWNzIGRvZXNuJ3QgbmVlZCBleHBsaWNpdCBkaXNwb3NhbAogICAgaWYgKF9jb25maWcuZW5hYmxlRGVidWdMb2dnaW5nKSB7CiAgICAgIF9sb2dnZXIuZCgn8J+UjSBGaXJlYmFzZSBBbmFseXRpY3MgY2xpZW50IGRpc3Bvc2VkJyk7CiAgICB9CiAgfQp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/client/index.dart", + "data": + "ZXhwb3J0ICdhbmFseXRpY3NfY2xpZW50LmRhcnQnOwpleHBvcnQgJ2ZpcmViYXNlX2FuYWx5dGljc19jbGllbnQuZGFydCc7Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/config/analytics_config.dart", + "data": + "YWJzdHJhY3QgY2xhc3MgQW5hbHl0aWNzQ29uZmlnIHsKICBib29sIGdldCBlbmFibGVBbmFseXRpY3M7CiAgYm9vbCBnZXQgZW5hYmxlRGVidWdMb2dnaW5nOwogIFN0cmluZz8gZ2V0IHVzZXJJZDsKICBNYXA8U3RyaW5nLCBTdHJpbmc+PyBnZXQgZGVmYXVsdFVzZXJQcm9wZXJ0aWVzOwp9CgpjbGFzcyBEZWZhdWx0QW5hbHl0aWNzQ29uZmlnIGltcGxlbWVudHMgQW5hbHl0aWNzQ29uZmlnIHsKICBjb25zdCBEZWZhdWx0QW5hbHl0aWNzQ29uZmlnKHsKICAgIHRoaXMuZW5hYmxlQW5hbHl0aWNzID0gdHJ1ZSwKICAgIHRoaXMuZW5hYmxlRGVidWdMb2dnaW5nID0gZmFsc2UsCiAgICB0aGlzLnVzZXJJZCwKICAgIHRoaXMuZGVmYXVsdFVzZXJQcm9wZXJ0aWVzLAogIH0pOwoKICBAb3ZlcnJpZGUKICBmaW5hbCBib29sIGVuYWJsZUFuYWx5dGljczsKCiAgQG92ZXJyaWRlCiAgZmluYWwgYm9vbCBlbmFibGVEZWJ1Z0xvZ2dpbmc7CgogIEBvdmVycmlkZQogIGZpbmFsIFN0cmluZz8gdXNlcklkOwoKICBAb3ZlcnJpZGUKICBmaW5hbCBNYXA8U3RyaW5nLCBTdHJpbmc+PyBkZWZhdWx0VXNlclByb3BlcnRpZXM7CgogIERlZmF1bHRBbmFseXRpY3NDb25maWcgY29weVdpdGgoewogICAgYm9vbD8gZW5hYmxlQW5hbHl0aWNzLAogICAgYm9vbD8gZW5hYmxlRGVidWdMb2dnaW5nLAogICAgU3RyaW5nPyB1c2VySWQsCiAgICBNYXA8U3RyaW5nLCBTdHJpbmc+PyBkZWZhdWx0VXNlclByb3BlcnRpZXMsCiAgfSkgewogICAgcmV0dXJuIERlZmF1bHRBbmFseXRpY3NDb25maWcoCiAgICAgIGVuYWJsZUFuYWx5dGljczogZW5hYmxlQW5hbHl0aWNzID8/IHRoaXMuZW5hYmxlQW5hbHl0aWNzLAogICAgICBlbmFibGVEZWJ1Z0xvZ2dpbmc6IGVuYWJsZURlYnVnTG9nZ2luZyA/PyB0aGlzLmVuYWJsZURlYnVnTG9nZ2luZywKICAgICAgdXNlcklkOiB1c2VySWQgPz8gdGhpcy51c2VySWQsCiAgICAgIGRlZmF1bHRVc2VyUHJvcGVydGllczoKICAgICAgICAgIGRlZmF1bHRVc2VyUHJvcGVydGllcyA/PyB0aGlzLmRlZmF1bHRVc2VyUHJvcGVydGllcywKICAgICk7CiAgfQp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/config/index.dart", + "data": "ZXhwb3J0ICdhbmFseXRpY3NfY29uZmlnLmRhcnQnOwo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/analytics_event.dart", + "data": + "Y2xhc3MgQW5hbHl0aWNzRXZlbnQgewogIGNvbnN0IEFuYWx5dGljc0V2ZW50KHtyZXF1aXJlZCB0aGlzLm5hbWUsIHRoaXMucGFyYW1ldGVyc30pOwoKICBmaW5hbCBTdHJpbmcgbmFtZTsKICBmaW5hbCBNYXA8U3RyaW5nLCBkeW5hbWljPj8gcGFyYW1ldGVyczsKCiAgTWFwPFN0cmluZywgZHluYW1pYz4gdG9NYXAoKSB7CiAgICByZXR1cm4geyduYW1lJzogbmFtZSwgaWYgKHBhcmFtZXRlcnMgIT0gbnVsbCkgJ3BhcmFtZXRlcnMnOiBwYXJhbWV0ZXJzfTsKICB9CgogIEBvdmVycmlkZQogIFN0cmluZyB0b1N0cmluZygpID0+ICdBbmFseXRpY3NFdmVudChuYW1lOiAkbmFtZSwgcGFyYW1ldGVyczogJHBhcmFtZXRlcnMpJzsKCiAgQG92ZXJyaWRlCiAgYm9vbCBvcGVyYXRvciA9PShPYmplY3Qgb3RoZXIpIHsKICAgIGlmIChpZGVudGljYWwodGhpcywgb3RoZXIpKSByZXR1cm4gdHJ1ZTsKICAgIHJldHVybiBvdGhlciBpcyBBbmFseXRpY3NFdmVudCAmJgogICAgICAgIG90aGVyLm5hbWUgPT0gbmFtZSAmJgogICAgICAgIF9tYXBFcXVhbHMob3RoZXIucGFyYW1ldGVycywgcGFyYW1ldGVycyk7CiAgfQoKICBAb3ZlcnJpZGUKICBpbnQgZ2V0IGhhc2hDb2RlID0+IG5hbWUuaGFzaENvZGUgXiBwYXJhbWV0ZXJzLmhhc2hDb2RlOwoKICBib29sIF9tYXBFcXVhbHMoTWFwPFN0cmluZywgZHluYW1pYz4/IGEsIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBiKSB7CiAgICBpZiAoYSA9PSBudWxsKSByZXR1cm4gYiA9PSBudWxsOwogICAgaWYgKGIgPT0gbnVsbCB8fCBhLmxlbmd0aCAhPSBiLmxlbmd0aCkgcmV0dXJuIGZhbHNlOwogICAgZm9yIChmaW5hbCBrZXkgaW4gYS5rZXlzKSB7CiAgICAgIGlmICghYi5jb250YWluc0tleShrZXkpIHx8IGFba2V5XSAhPSBiW2tleV0pIHJldHVybiBmYWxzZTsKICAgIH0KICAgIHJldHVybiB0cnVlOwogIH0KfQoKY2xhc3MgUHJlZGVmaW5lZEV2ZW50cyB7CiAgc3RhdGljIGNvbnN0IFN0cmluZyBhcHBPcGVuID0gJ2FwcF9vcGVuJzsKICBzdGF0aWMgY29uc3QgU3RyaW5nIGxvZ2luID0gJ2xvZ2luJzsKICBzdGF0aWMgY29uc3QgU3RyaW5nIHNpZ25VcCA9ICdzaWduX3VwJzsKICBzdGF0aWMgY29uc3QgU3RyaW5nIHNjcmVlblZpZXcgPSAnc2NyZWVuX3ZpZXcnOwogIHN0YXRpYyBjb25zdCBTdHJpbmcgcHVyY2hhc2UgPSAncHVyY2hhc2UnOwogIHN0YXRpYyBjb25zdCBTdHJpbmcgc2VsZWN0Q29udGVudCA9ICdzZWxlY3RfY29udGVudCc7CiAgc3RhdGljIGNvbnN0IFN0cmluZyBzaGFyZSA9ICdzaGFyZSc7CiAgc3RhdGljIGNvbnN0IFN0cmluZyBzZWFyY2ggPSAnc2VhcmNoJzsKICBzdGF0aWMgY29uc3QgU3RyaW5nIGJlZ2luQ2hlY2tvdXQgPSAnYmVnaW5fY2hlY2tvdXQnOwogIHN0YXRpYyBjb25zdCBTdHJpbmcgYWRkVG9DYXJ0ID0gJ2FkZF90b19jYXJ0JzsKICBzdGF0aWMgY29uc3QgU3RyaW5nIHJlbW92ZUZyb21DYXJ0ID0gJ3JlbW92ZV9mcm9tX2NhcnQnOwogIHN0YXRpYyBjb25zdCBTdHJpbmcgdmlld0l0ZW0gPSAndmlld19pdGVtJzsKICBzdGF0aWMgY29uc3QgU3RyaW5nIHZpZXdJdGVtTGlzdCA9ICd2aWV3X2l0ZW1fbGlzdCc7Cn0KCmNsYXNzIFByZWRlZmluZWRQYXJhbWV0ZXJzIHsKICBzdGF0aWMgY29uc3QgU3RyaW5nIHNjcmVlbk5hbWUgPSAnc2NyZWVuX25hbWUnOwogIHN0YXRpYyBjb25zdCBTdHJpbmcgc2NyZWVuQ2xhc3MgPSAnc2NyZWVuX2NsYXNzJzsKICBzdGF0aWMgY29uc3QgU3RyaW5nIG1ldGhvZCA9ICdtZXRob2QnOwogIHN0YXRpYyBjb25zdCBTdHJpbmcgY3VycmVuY3kgPSAnY3VycmVuY3knOwogIHN0YXRpYyBjb25zdCBTdHJpbmcgdmFsdWUgPSAndmFsdWUnOwogIHN0YXRpYyBjb25zdCBTdHJpbmcgaXRlbUlkID0gJ2l0ZW1faWQnOwogIHN0YXRpYyBjb25zdCBTdHJpbmcgaXRlbU5hbWUgPSAnaXRlbV9uYW1lJzsKICBzdGF0aWMgY29uc3QgU3RyaW5nIGl0ZW1DYXRlZ29yeSA9ICdpdGVtX2NhdGVnb3J5JzsKICBzdGF0aWMgY29uc3QgU3RyaW5nIGNvbnRlbnRUeXBlID0gJ2NvbnRlbnRfdHlwZSc7CiAgc3RhdGljIGNvbnN0IFN0cmluZyBzZWFyY2hUZXJtID0gJ3NlYXJjaF90ZXJtJzsKICBzdGF0aWMgY29uc3QgU3RyaW5nIHVzZXJJZCA9ICd1c2VyX2lkJzsKfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/analytics_events.dart", + "data": + "Ly8vIFN0cnVjdHVyZWQgYW5hbHl0aWNzIGV2ZW50IG5hbWVzIG9yZ2FuaXplZCBieSBjYXRlZ29yeQovLy8KLy8vIFVzYWdlOiBBbmFseXRpY3NFdmVudHMudXNlci5hdXRoZW50aWNhdGVkUmVkaXJlY3QKLy8vIFRoaXMgcHJvdmlkZXMgdHlwZS1zYWZlLCBkaXNjb3ZlcmFibGUgZXZlbnQgbmFtZXMgYW5kIHByZXZlbnRzIHR5cG9zCmNsYXNzIEFuYWx5dGljc0V2ZW50cyB7CiAgQW5hbHl0aWNzRXZlbnRzLl8oKTsKCiAgLy8vIFVzZXItcmVsYXRlZCBldmVudHMKICBzdGF0aWMgY29uc3QgdXNlciA9IF9Vc2VyRXZlbnRzKCk7CgogIC8vLyBBdXRoZW50aWNhdGlvbi1yZWxhdGVkIGV2ZW50cwogIHN0YXRpYyBjb25zdCBhdXRoID0gX0F1dGhFdmVudHMoKTsKCiAgLy8vIE5hdmlnYXRpb24tcmVsYXRlZCBldmVudHMKICBzdGF0aWMgY29uc3QgbmF2aWdhdGlvbiA9IF9OYXZpZ2F0aW9uRXZlbnRzKCk7CgogIC8vLyBGZWF0dXJlIHVzYWdlIGV2ZW50cwogIHN0YXRpYyBjb25zdCBmZWF0dXJlID0gX0ZlYXR1cmVFdmVudHMoKTsKCiAgLy8vIEVycm9yIGFuZCBkZWJ1Z2dpbmcgZXZlbnRzCiAgc3RhdGljIGNvbnN0IGVycm9yID0gX0Vycm9yRXZlbnRzKCk7CgogIC8vLyBBcHAgbGlmZWN5Y2xlIGV2ZW50cwogIHN0YXRpYyBjb25zdCBhcHAgPSBfQXBwRXZlbnRzKCk7CgogIC8vLyBTdXJ2ZXkgYW5kIGFzc2Vzc21lbnQgZXZlbnRzCiAgc3RhdGljIGNvbnN0IHN1cnZleSA9IF9TdXJ2ZXlFdmVudHMoKTsKCiAgLy8vIEhlYWx0aCBkYXRhIGFuZCBiaW9raXQgZXZlbnRzCiAgc3RhdGljIGNvbnN0IGhlYWx0aCA9IF9IZWFsdGhFdmVudHMoKTsKCiAgLy8vIFByb2ZpbGUtcmVsYXRlZCBldmVudHMKICBzdGF0aWMgY29uc3QgcHJvZmlsZSA9IF9Qcm9maWxlRXZlbnRzKCk7Cn0KCmNsYXNzIF9Vc2VyRXZlbnRzIHsKICBjb25zdCBfVXNlckV2ZW50cygpOwoKICAvLy8gVXNlciBzdWNjZXNzZnVsbHkgYXV0aGVudGljYXRlZCBhbmQgcmVkaXJlY3RlZAogIFN0cmluZyBnZXQgYXV0aGVudGljYXRlZFJlZGlyZWN0ID0+ICd1c2VyX2F1dGhlbnRpY2F0ZWRfcmVkaXJlY3QnOwoKICAvLy8gVXNlciBwcm9maWxlIHVwZGF0ZWQKICBTdHJpbmcgZ2V0IHByb2ZpbGVVcGRhdGVkID0+ICd1c2VyX3Byb2ZpbGVfdXBkYXRlZCc7CgogIC8vLyBVc2VyIHByZWZlcmVuY2VzIGNoYW5nZWQKICBTdHJpbmcgZ2V0IHByZWZlcmVuY2VzQ2hhbmdlZCA9PiAndXNlcl9wcmVmZXJlbmNlc19jaGFuZ2VkJzsKCiAgLy8vIFVzZXIgY29tcGxldGVkIG9uYm9hcmRpbmcKICBTdHJpbmcgZ2V0IG9uYm9hcmRpbmdDb21wbGV0ZWQgPT4gJ3VzZXJfb25ib2FyZGluZ19jb21wbGV0ZWQnOwoKICAvLy8gVXNlciBhY2NvdW50IGRlbGV0ZWQKICBTdHJpbmcgZ2V0IGFjY291bnREZWxldGVkID0+ICd1c2VyX2FjY291bnRfZGVsZXRlZCc7Cn0KCmNsYXNzIF9BdXRoRXZlbnRzIHsKICBjb25zdCBfQXV0aEV2ZW50cygpOwoKICAvLy8gVXNlciBzaWduZWQgaW4KICBTdHJpbmcgZ2V0IHNpZ25JbiA9PiAnYXV0aF9zaWduX2luJzsKCiAgLy8vIFVzZXIgc2lnbmVkIHVwCiAgU3RyaW5nIGdldCBzaWduVXAgPT4gJ2F1dGhfc2lnbl91cCc7CgogIC8vLyBVc2VyIHNpZ25lZCBvdXQKICBTdHJpbmcgZ2V0IHNpZ25PdXQgPT4gJ2F1dGhfc2lnbl9vdXQnOwoKICAvLy8gUGFzc3dvcmQgcmVzZXQgcmVxdWVzdGVkCiAgU3RyaW5nIGdldCBwYXNzd29yZFJlc2V0ID0+ICdhdXRoX3Bhc3N3b3JkX3Jlc2V0JzsKCiAgLy8vIEF1dGhlbnRpY2F0aW9uIGZhaWxlZAogIFN0cmluZyBnZXQgZmFpbGVkID0+ICdhdXRoX2ZhaWxlZCc7CgogIC8vLyBTb2NpYWwgYXV0aGVudGljYXRpb24gKEdvb2dsZSwgQXBwbGUsIGV0Yy4pCiAgU3RyaW5nIGdldCBzb2NpYWxMb2dpbiA9PiAnYXV0aF9zb2NpYWxfbG9naW4nOwp9CgpjbGFzcyBfTmF2aWdhdGlvbkV2ZW50cyB7CiAgY29uc3QgX05hdmlnYXRpb25FdmVudHMoKTsKCiAgLy8vIFNjcmVlbiB2aWV3ZWQgKGF1dG9tYXRpY2FsbHkgdHJhY2tlZCkKICBTdHJpbmcgZ2V0IHNjcmVlblZpZXcgPT4gJ3NjcmVlbl92aWV3JzsKCiAgLy8vIFRhYiBzd2l0Y2hlZCBpbiBib3R0b20gbmF2aWdhdGlvbgogIFN0cmluZyBnZXQgdGFiU3dpdGNoZWQgPT4gJ25hdmlnYXRpb25fdGFiX3N3aXRjaGVkJzsKCiAgLy8vIEJhY2sgYnV0dG9uIHByZXNzZWQKICBTdHJpbmcgZ2V0IGJhY2tQcmVzc2VkID0+ICduYXZpZ2F0aW9uX2JhY2tfcHJlc3NlZCc7CgogIC8vLyBEZWVwIGxpbmsgb3BlbmVkCiAgU3RyaW5nIGdldCBkZWVwTGlua09wZW5lZCA9PiAnbmF2aWdhdGlvbl9kZWVwX2xpbmtfb3BlbmVkJzsKfQoKY2xhc3MgX0ZlYXR1cmVFdmVudHMgewogIGNvbnN0IF9GZWF0dXJlRXZlbnRzKCk7CgogIC8vLyBGZWF0dXJlIHdhcyB1c2VkL2FjY2Vzc2VkCiAgU3RyaW5nIGdldCB1c2VkID0+ICdmZWF0dXJlX3VzZWQnOwoKICAvLy8gRmVhdHVyZSB0dXRvcmlhbCBzdGFydGVkCiAgU3RyaW5nIGdldCB0dXRvcmlhbFN0YXJ0ZWQgPT4gJ2ZlYXR1cmVfdHV0b3JpYWxfc3RhcnRlZCc7CgogIC8vLyBGZWF0dXJlIHR1dG9yaWFsIGNvbXBsZXRlZAogIFN0cmluZyBnZXQgdHV0b3JpYWxDb21wbGV0ZWQgPT4gJ2ZlYXR1cmVfdHV0b3JpYWxfY29tcGxldGVkJzsKCiAgLy8vIEZlYXR1cmUgZmVlZGJhY2sgZ2l2ZW4KICBTdHJpbmcgZ2V0IGZlZWRiYWNrR2l2ZW4gPT4gJ2ZlYXR1cmVfZmVlZGJhY2tfZ2l2ZW4nOwoKICAvLy8gQnV0dG9uIHByZXNzZWQKICBTdHJpbmcgZ2V0IGJ1dHRvblByZXNzZWQgPT4gJ2J1dHRvbl9wcmVzc2VkJzsKfQoKY2xhc3MgX0Vycm9yRXZlbnRzIHsKICBjb25zdCBfRXJyb3JFdmVudHMoKTsKCiAgLy8vIEdlbmVyYWwgYXBwIGVycm9yIG9jY3VycmVkCiAgU3RyaW5nIGdldCBhcHBFcnJvciA9PiAnYXBwX2Vycm9yJzsKCiAgLy8vIE5ldHdvcmsgZXJyb3Igb2NjdXJyZWQKICBTdHJpbmcgZ2V0IG5ldHdvcmtFcnJvciA9PiAnbmV0d29ya19lcnJvcic7CgogIC8vLyBDcmFzaCBvY2N1cnJlZAogIFN0cmluZyBnZXQgY3Jhc2ggPT4gJ2FwcF9jcmFzaCc7CgogIC8vLyBBUEkgZXJyb3Igb2NjdXJyZWQKICBTdHJpbmcgZ2V0IGFwaUVycm9yID0+ICdhcGlfZXJyb3InOwoKICAvLy8gVmFsaWRhdGlvbiBlcnJvcgogIFN0cmluZyBnZXQgdmFsaWRhdGlvbkVycm9yID0+ICd2YWxpZGF0aW9uX2Vycm9yJzsKfQoKY2xhc3MgX0FwcEV2ZW50cyB7CiAgY29uc3QgX0FwcEV2ZW50cygpOwoKICAvLy8gQXBwIG9wZW5lZC9sYXVuY2hlZAogIFN0cmluZyBnZXQgb3BlbiA9PiAnYXBwX29wZW4nOwoKICAvLy8gQXBwIHdlbnQgdG8gYmFja2dyb3VuZAogIFN0cmluZyBnZXQgYmFja2dyb3VuZCA9PiAnYXBwX2JhY2tncm91bmQnOwoKICAvLy8gQXBwIGNhbWUgdG8gZm9yZWdyb3VuZAogIFN0cmluZyBnZXQgZm9yZWdyb3VuZCA9PiAnYXBwX2ZvcmVncm91bmQnOwoKICAvLy8gQXBwIHVwZGF0ZWQgdG8gbmV3IHZlcnNpb24KICBTdHJpbmcgZ2V0IHVwZGF0ZWQgPT4gJ2FwcF91cGRhdGVkJzsKCiAgLy8vIEFwcCBpbnN0YWxsZWQgZm9yIGZpcnN0IHRpbWUKICBTdHJpbmcgZ2V0IGluc3RhbGxlZCA9PiAnYXBwX2luc3RhbGxlZCc7CgogIC8vLyBBcHAgdW5pbnN0YWxsZWQKICBTdHJpbmcgZ2V0IHVuaW5zdGFsbGVkID0+ICdhcHBfdW5pbnN0YWxsZWQnOwp9CgpjbGFzcyBfU3VydmV5RXZlbnRzIHsKICBjb25zdCBfU3VydmV5RXZlbnRzKCk7CgogIC8vLyBTdXJ2ZXkgc3RhcnRlZAogIFN0cmluZyBnZXQgc3RhcnRlZCA9PiAnc3VydmV5X3N0YXJ0ZWQnOwoKICAvLy8gU3VydmV5IGNvbXBsZXRlZAogIFN0cmluZyBnZXQgY29tcGxldGVkID0+ICdzdXJ2ZXlfY29tcGxldGVkJzsKCiAgLy8vIFN1cnZleSBhYmFuZG9uZWQvY2FuY2VsbGVkCiAgU3RyaW5nIGdldCBhYmFuZG9uZWQgPT4gJ3N1cnZleV9hYmFuZG9uZWQnOwoKICAvLy8gU3VydmV5IHF1ZXN0aW9uIGFuc3dlcmVkCiAgU3RyaW5nIGdldCBxdWVzdGlvbkFuc3dlcmVkID0+ICdzdXJ2ZXlfcXVlc3Rpb25fYW5zd2VyZWQnOwoKICAvLy8gU3VydmV5IHByb2dyZXNzIHNhdmVkCiAgU3RyaW5nIGdldCBwcm9ncmVzc1NhdmVkID0+ICdzdXJ2ZXlfcHJvZ3Jlc3Nfc2F2ZWQnOwoKICAvLy8gU3VydmV5IHJlc3VsdHMgdmlld2VkCiAgU3RyaW5nIGdldCByZXN1bHRzVmlld2VkID0+ICdzdXJ2ZXlfcmVzdWx0c192aWV3ZWQnOwp9CgpjbGFzcyBfSGVhbHRoRXZlbnRzIHsKICBjb25zdCBfSGVhbHRoRXZlbnRzKCk7CgogIC8vLyBIZWFsdGggZGF0YSBzeW5jZWQKICBTdHJpbmcgZ2V0IGRhdGFTeW5jZWQgPT4gJ2hlYWx0aF9kYXRhX3N5bmNlZCc7CgogIC8vLyBXZWFyYWJsZSBkZXZpY2UgY29ubmVjdGVkCiAgU3RyaW5nIGdldCBkZXZpY2VDb25uZWN0ZWQgPT4gJ2hlYWx0aF9kZXZpY2VfY29ubmVjdGVkJzsKCiAgLy8vIFdlYXJhYmxlIGRldmljZSBkaXNjb25uZWN0ZWQKICBTdHJpbmcgZ2V0IGRldmljZURpc2Nvbm5lY3RlZCA9PiAnaGVhbHRoX2RldmljZV9kaXNjb25uZWN0ZWQnOwoKICAvLy8gSGVhbHRoIG1ldHJpYyByZWNvcmRlZAogIFN0cmluZyBnZXQgbWV0cmljUmVjb3JkZWQgPT4gJ2hlYWx0aF9tZXRyaWNfcmVjb3JkZWQnOwoKICAvLy8gSGVhbHRoIGdvYWwgc2V0CiAgU3RyaW5nIGdldCBnb2FsU2V0ID0+ICdoZWFsdGhfZ29hbF9zZXQnOwoKICAvLy8gSGVhbHRoIGdvYWwgYWNoaWV2ZWQKICBTdHJpbmcgZ2V0IGdvYWxBY2hpZXZlZCA9PiAnaGVhbHRoX2dvYWxfYWNoaWV2ZWQnOwoKICAvLy8gSGVhbHRoIHJlcG9ydCBnZW5lcmF0ZWQKICBTdHJpbmcgZ2V0IHJlcG9ydEdlbmVyYXRlZCA9PiAnaGVhbHRoX3JlcG9ydF9nZW5lcmF0ZWQnOwp9CgpjbGFzcyBfUHJvZmlsZUV2ZW50cyB7CiAgY29uc3QgX1Byb2ZpbGVFdmVudHMoKTsKCiAgLy8vIFByb2ZpbGUgdmlld2VkCiAgU3RyaW5nIGdldCB2aWV3ZWQgPT4gJ3Byb2ZpbGVfdmlld2VkJzsKCiAgLy8vIFByb2ZpbGUgZWRpdGVkCiAgU3RyaW5nIGdldCBlZGl0ZWQgPT4gJ3Byb2ZpbGVfZWRpdGVkJzsKCiAgLy8vIFByb2ZpbGUgcGhvdG8gdXBkYXRlZAogIFN0cmluZyBnZXQgcGhvdG9VcGRhdGVkID0+ICdwcm9maWxlX3Bob3RvX3VwZGF0ZWQnOwoKICAvLy8gUHJvZmlsZSBzZXR0aW5ncyBjaGFuZ2VkCiAgU3RyaW5nIGdldCBzZXR0aW5nc0NoYW5nZWQgPT4gJ3Byb2ZpbGVfc2V0dGluZ3NfY2hhbmdlZCc7CgogIC8vLyBQcm9maWxlIHByaXZhY3kgc2V0dGluZ3MgdXBkYXRlZAogIFN0cmluZyBnZXQgcHJpdmFjeVVwZGF0ZWQgPT4gJ3Byb2ZpbGVfcHJpdmFjeV91cGRhdGVkJzsKfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/index.dart", + "data": + "ZXhwb3J0ICdhbmFseXRpY3NfZXZlbnQuZGFydCc7CmV4cG9ydCAnYW5hbHl0aWNzX2V2ZW50cy5kYXJ0JzsK", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/observer/analytics_route_observer.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNvcmUvY29yZS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRpL2RpLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZmlyZWJhc2VfYW5hbHl0aWNzL2ZpcmViYXNlX2FuYWx5dGljcy5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CgovLy8gQW5hbHl0aWNzIHJvdXRlIG9ic2VydmVyIHRoYXQgdXNlcyBGaXJlYmFzZSBBbmFseXRpY3MgT2JzZXJ2ZXIgaW50ZXJuYWxseQovLy8gd2hpbGUgcHJvdmlkaW5nIGFkZGl0aW9uYWwgbG9nZ2luZyBhbmQgZXJyb3IgaGFuZGxpbmcgY2FwYWJpbGl0aWVzCmNsYXNzIEFuYWx5dGljc1JvdXRlT2JzZXJ2ZXIgZXh0ZW5kcyBOYXZpZ2F0b3JPYnNlcnZlciB7CiAgQW5hbHl0aWNzUm91dGVPYnNlcnZlcigpCiAgICA6IF9sb2dnZXIgPSBkaS5nZXQ8TG9nZ2VyPigpLAogICAgICBfZmlyZWJhc2VPYnNlcnZlciA9IEZpcmViYXNlQW5hbHl0aWNzT2JzZXJ2ZXIoCiAgICAgICAgYW5hbHl0aWNzOiBGaXJlYmFzZUFuYWx5dGljcy5pbnN0YW5jZSwKICAgICAgKTsKCiAgZmluYWwgTG9nZ2VyIF9sb2dnZXI7CiAgZmluYWwgRmlyZWJhc2VBbmFseXRpY3NPYnNlcnZlciBfZmlyZWJhc2VPYnNlcnZlcjsKCiAgQG92ZXJyaWRlCiAgdm9pZCBkaWRQdXNoKFJvdXRlIHJvdXRlLCBSb3V0ZT8gcHJldmlvdXNSb3V0ZSkgewogICAgc3VwZXIuZGlkUHVzaChyb3V0ZSwgcHJldmlvdXNSb3V0ZSk7CgogICAgdHJ5IHsKICAgICAgLy8gRGVsZWdhdGUgdG8gRmlyZWJhc2UgQW5hbHl0aWNzIE9ic2VydmVyIGZvciBuYXRpdmUgc2NyZWVuIHRyYWNraW5nCiAgICAgIF9maXJlYmFzZU9ic2VydmVyLmRpZFB1c2gocm91dGUsIHByZXZpb3VzUm91dGUpOwogICAgfSBjYXRjaCAoZSwgcykgewogICAgICBfbG9nZ2VyLmUoJ0ZhaWxlZCB0byB0cmFjayBzY3JlZW4gcHVzaCBldmVudCcsIGUsIHMpOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgdm9pZCBkaWRQb3AoUm91dGUgcm91dGUsIFJvdXRlPyBwcmV2aW91c1JvdXRlKSB7CiAgICBzdXBlci5kaWRQb3Aocm91dGUsIHByZXZpb3VzUm91dGUpOwoKICAgIHRyeSB7CiAgICAgIC8vIERlbGVnYXRlIHRvIEZpcmViYXNlIEFuYWx5dGljcyBPYnNlcnZlcgogICAgICBfZmlyZWJhc2VPYnNlcnZlci5kaWRQb3Aocm91dGUsIHByZXZpb3VzUm91dGUpOwogICAgfSBjYXRjaCAoZSwgcykgewogICAgICBfbG9nZ2VyLmUoJ0ZhaWxlZCB0byB0cmFjayBzY3JlZW4gcG9wIGV2ZW50JywgZSwgcyk7CiAgICB9CiAgfQoKICBAb3ZlcnJpZGUKICB2b2lkIGRpZFJlcGxhY2Uoe1JvdXRlPGR5bmFtaWM+PyBuZXdSb3V0ZSwgUm91dGU8ZHluYW1pYz4/IG9sZFJvdXRlfSkgewogICAgc3VwZXIuZGlkUmVwbGFjZShuZXdSb3V0ZTogbmV3Um91dGUsIG9sZFJvdXRlOiBvbGRSb3V0ZSk7CgogICAgdHJ5IHsKICAgICAgLy8gRGVsZWdhdGUgdG8gRmlyZWJhc2UgQW5hbHl0aWNzIE9ic2VydmVyCiAgICAgIF9maXJlYmFzZU9ic2VydmVyLmRpZFJlcGxhY2UobmV3Um91dGU6IG5ld1JvdXRlLCBvbGRSb3V0ZTogb2xkUm91dGUpOwogICAgfSBjYXRjaCAoZSwgcykgewogICAgICBfbG9nZ2VyLmUoJ0ZhaWxlZCB0byB0cmFjayBzY3JlZW4gcmVwbGFjZSBldmVudCcsIGUsIHMpOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgdm9pZCBkaWRSZW1vdmUoUm91dGU8ZHluYW1pYz4gcm91dGUsIFJvdXRlPGR5bmFtaWM+PyBwcmV2aW91c1JvdXRlKSB7CiAgICBzdXBlci5kaWRSZW1vdmUocm91dGUsIHByZXZpb3VzUm91dGUpOwoKICAgIHRyeSB7CiAgICAgIC8vIERlbGVnYXRlIHRvIEZpcmViYXNlIEFuYWx5dGljcyBPYnNlcnZlcgogICAgICBfZmlyZWJhc2VPYnNlcnZlci5kaWRSZW1vdmUocm91dGUsIHByZXZpb3VzUm91dGUpOwogICAgfSBjYXRjaCAoZSwgcykgewogICAgICBfbG9nZ2VyLmUoJ0ZhaWxlZCB0byB0cmFjayBzY3JlZW4gcmVtb3ZlIGV2ZW50JywgZSwgcyk7CiAgICB9CiAgfQp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/observer/index.dart", + "data": "ZXhwb3J0ICdhbmFseXRpY3Nfcm91dGVfb2JzZXJ2ZXIuZGFydCc7Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/utils/analytics_helper.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmFuYWx5dGljcy9zcmMvY2xpZW50L2FuYWx5dGljc19jbGllbnQuZGFydCc7CmltcG9ydCAncGFja2FnZTphbmFseXRpY3Mvc3JjL21vZGVscy9hbmFseXRpY3NfZXZlbnQuZGFydCc7CmltcG9ydCAncGFja2FnZTphbmFseXRpY3Mvc3JjL21vZGVscy9hbmFseXRpY3NfZXZlbnRzLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Y29yZS9jb3JlLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZGkvZGkuZGFydCc7CgpjbGFzcyBBbmFseXRpY3NIZWxwZXIgewogIHN0YXRpYyBBbmFseXRpY3NDbGllbnQ/IF9jbGllbnQ7CiAgc3RhdGljIExvZ2dlcj8gX2xvZ2dlcjsKCiAgc3RhdGljIEFuYWx5dGljc0NsaWVudD8gZ2V0IF9hbmFseXRpY3NDbGllbnQgewogICAgX2NsaWVudCA/Pz0gX2dldEFuYWx5dGljc0NsaWVudCgpOwogICAgcmV0dXJuIF9jbGllbnQ7CiAgfQoKICBzdGF0aWMgTG9nZ2VyIGdldCBfbG9nIHsKICAgIF9sb2dnZXIgPz89IGRpLmdldDxMb2dnZXI+KCk7CiAgICByZXR1cm4gX2xvZ2dlciE7CiAgfQoKICBzdGF0aWMgQW5hbHl0aWNzQ2xpZW50PyBfZ2V0QW5hbHl0aWNzQ2xpZW50KCkgewogICAgdHJ5IHsKICAgICAgcmV0dXJuIGRpLmdldDxBbmFseXRpY3NDbGllbnQ+KCk7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIF9sb2cudygnQW5hbHl0aWNzIGNsaWVudCBub3QgYXZhaWxhYmxlOiAkZScpOwogICAgICByZXR1cm4gbnVsbDsKICAgIH0KICB9CgogIC8vLyBMb2cgYSBjdXN0b20gZXZlbnQgd2l0aCBvcHRpb25hbCBwYXJhbWV0ZXJzCiAgc3RhdGljIEZ1dHVyZTx2b2lkPiBsb2dFdmVudCgKICAgIFN0cmluZyBuYW1lLCB7CiAgICBNYXA8U3RyaW5nLCBkeW5hbWljPj8gcGFyYW1ldGVycywKICB9KSBhc3luYyB7CiAgICBmaW5hbCBjbGllbnQgPSBfYW5hbHl0aWNzQ2xpZW50OwogICAgaWYgKGNsaWVudCA9PSBudWxsKSByZXR1cm47CgogICAgdHJ5IHsKICAgICAgYXdhaXQgY2xpZW50LmxvZ0V2ZW50KG5hbWU6IG5hbWUsIHBhcmFtZXRlcnM6IHBhcmFtZXRlcnMpOwogICAgfSBjYXRjaCAoZSwgcykgewogICAgICBfbG9nLmUoJ0ZhaWxlZCB0byBsb2cgYW5hbHl0aWNzIGV2ZW50OiAkbmFtZScsIGUsIHMpOwogICAgfQogIH0KCiAgLy8vIExvZyB1c2VyIHNpZ24gaW4KICBzdGF0aWMgRnV0dXJlPHZvaWQ+IGxvZ1NpZ25Jbih7CiAgICBTdHJpbmc/IG1ldGhvZCwKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBwYXJhbWV0ZXJzLAogIH0pIGFzeW5jIHsKICAgIGZpbmFsIGNsaWVudCA9IF9hbmFseXRpY3NDbGllbnQ7CiAgICBpZiAoY2xpZW50ID09IG51bGwpIHJldHVybjsKCiAgICB0cnkgewogICAgICBhd2FpdCBjbGllbnQubG9nTG9naW4obG9naW5NZXRob2Q6IG1ldGhvZCwgcGFyYW1ldGVyczogcGFyYW1ldGVycyk7CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2cuZSgnRmFpbGVkIHRvIGxvZyBzaWduIGluIGV2ZW50JywgZSwgcyk7CiAgICB9CiAgfQoKICAvLy8gTG9nIHVzZXIgc2lnbiB1cAogIHN0YXRpYyBGdXR1cmU8dm9pZD4gbG9nU2lnblVwKHsKICAgIFN0cmluZz8gbWV0aG9kLAogICAgTWFwPFN0cmluZywgZHluYW1pYz4/IHBhcmFtZXRlcnMsCiAgfSkgYXN5bmMgewogICAgZmluYWwgY2xpZW50ID0gX2FuYWx5dGljc0NsaWVudDsKICAgIGlmIChjbGllbnQgPT0gbnVsbCkgcmV0dXJuOwoKICAgIHRyeSB7CiAgICAgIGF3YWl0IGNsaWVudC5sb2dTaWduVXAoc2lnblVwTWV0aG9kOiBtZXRob2QsIHBhcmFtZXRlcnM6IHBhcmFtZXRlcnMpOwogICAgfSBjYXRjaCAoZSwgcykgewogICAgICBfbG9nLmUoJ0ZhaWxlZCB0byBsb2cgc2lnbiB1cCBldmVudCcsIGUsIHMpOwogICAgfQogIH0KCiAgLy8vIFNldCB1c2VyIElEIGZvciBhbmFseXRpY3MKICBzdGF0aWMgRnV0dXJlPHZvaWQ+IHNldFVzZXJJZChTdHJpbmc/IHVzZXJJZCkgYXN5bmMgewogICAgZmluYWwgY2xpZW50ID0gX2FuYWx5dGljc0NsaWVudDsKICAgIGlmIChjbGllbnQgPT0gbnVsbCkgcmV0dXJuOwoKICAgIHRyeSB7CiAgICAgIGF3YWl0IGNsaWVudC5zZXRVc2VySWQodXNlcklkKTsKICAgIH0gY2F0Y2ggKGUsIHMpIHsKICAgICAgX2xvZy5lKCdGYWlsZWQgdG8gc2V0IGFuYWx5dGljcyB1c2VyIElEJywgZSwgcyk7CiAgICB9CiAgfQoKICAvLy8gU2V0IHVzZXIgcHJvcGVydHkKICBzdGF0aWMgRnV0dXJlPHZvaWQ+IHNldFVzZXJQcm9wZXJ0eSh7CiAgICByZXF1aXJlZCBTdHJpbmcgbmFtZSwKICAgIHJlcXVpcmVkIFN0cmluZz8gdmFsdWUsCiAgfSkgYXN5bmMgewogICAgZmluYWwgY2xpZW50ID0gX2FuYWx5dGljc0NsaWVudDsKICAgIGlmIChjbGllbnQgPT0gbnVsbCkgcmV0dXJuOwoKICAgIHRyeSB7CiAgICAgIGF3YWl0IGNsaWVudC5zZXRVc2VyUHJvcGVydHkobmFtZTogbmFtZSwgdmFsdWU6IHZhbHVlKTsKICAgIH0gY2F0Y2ggKGUsIHMpIHsKICAgICAgX2xvZy5lKCdGYWlsZWQgdG8gc2V0IGFuYWx5dGljcyB1c2VyIHByb3BlcnR5OiAkbmFtZScsIGUsIHMpOwogICAgfQogIH0KCiAgLy8vIExvZyBhcHAgb3BlbiBldmVudAogIHN0YXRpYyBGdXR1cmU8dm9pZD4gbG9nQXBwT3Blbih7TWFwPFN0cmluZywgZHluYW1pYz4/IHBhcmFtZXRlcnN9KSBhc3luYyB7CiAgICBmaW5hbCBjbGllbnQgPSBfYW5hbHl0aWNzQ2xpZW50OwogICAgaWYgKGNsaWVudCA9PSBudWxsKSByZXR1cm47CgogICAgdHJ5IHsKICAgICAgYXdhaXQgY2xpZW50LmxvZ0FwcE9wZW4ocGFyYW1ldGVyczogcGFyYW1ldGVycyk7CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2cuZSgnRmFpbGVkIHRvIGxvZyBhcHAgb3BlbiBldmVudCcsIGUsIHMpOwogICAgfQogIH0KCiAgLy8vIExvZyBjdXN0b20gYW5hbHl0aWNzIGV2ZW50CiAgc3RhdGljIEZ1dHVyZTx2b2lkPiBsb2dDdXN0b21FdmVudChBbmFseXRpY3NFdmVudCBldmVudCkgYXN5bmMgewogICAgZmluYWwgY2xpZW50ID0gX2FuYWx5dGljc0NsaWVudDsKICAgIGlmIChjbGllbnQgPT0gbnVsbCkgcmV0dXJuOwoKICAgIHRyeSB7CiAgICAgIGF3YWl0IGNsaWVudC5sb2dDdXN0b21FdmVudChldmVudCk7CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2cuZSgnRmFpbGVkIHRvIGxvZyBjdXN0b20gYW5hbHl0aWNzIGV2ZW50OiAke2V2ZW50Lm5hbWV9JywgZSwgcyk7CiAgICB9CiAgfQoKICAvLy8gQ29tbW9uIGJ1c2luZXNzIGV2ZW50cwogIHN0YXRpYyBGdXR1cmU8dm9pZD4gbG9nRmVhdHVyZVVzZWQoCiAgICBTdHJpbmcgZmVhdHVyZU5hbWUsIHsKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBwYXJhbWV0ZXJzLAogIH0pIGFzeW5jIHsKICAgIGF3YWl0IGxvZ0V2ZW50KAogICAgICBBbmFseXRpY3NFdmVudHMuZmVhdHVyZS51c2VkLAogICAgICBwYXJhbWV0ZXJzOiB7CiAgICAgICAgJ2ZlYXR1cmVfbmFtZSc6IGZlYXR1cmVOYW1lLAogICAgICAgICd0aW1lc3RhbXAnOiBEYXRlVGltZS5ub3coKS50b0lzbzg2MDFTdHJpbmcoKSwKICAgICAgICAuLi4/cGFyYW1ldGVycywKICAgICAgfSwKICAgICk7CiAgfQoKICBzdGF0aWMgRnV0dXJlPHZvaWQ+IGxvZ0J1dHRvblByZXNzZWQoCiAgICBTdHJpbmcgYnV0dG9uTmFtZSwgewogICAgU3RyaW5nPyBzY3JlZW5OYW1lLAogICAgTWFwPFN0cmluZywgZHluYW1pYz4/IHBhcmFtZXRlcnMsCiAgfSkgYXN5bmMgewogICAgYXdhaXQgbG9nRXZlbnQoCiAgICAgIEFuYWx5dGljc0V2ZW50cy5mZWF0dXJlLmJ1dHRvblByZXNzZWQsCiAgICAgIHBhcmFtZXRlcnM6IHsKICAgICAgICAnYnV0dG9uX25hbWUnOiBidXR0b25OYW1lLAogICAgICAgIGlmIChzY3JlZW5OYW1lICE9IG51bGwpICdzY3JlZW5fbmFtZSc6IHNjcmVlbk5hbWUsCiAgICAgICAgJ3RpbWVzdGFtcCc6IERhdGVUaW1lLm5vdygpLnRvSXNvODYwMVN0cmluZygpLAogICAgICAgIC4uLj9wYXJhbWV0ZXJzLAogICAgICB9LAogICAgKTsKICB9CgogIHN0YXRpYyBGdXR1cmU8dm9pZD4gbG9nQXBwRXJyb3IoCiAgICBTdHJpbmcgZXJyb3JUeXBlLCB7CiAgICBTdHJpbmc/IGVycm9yTWVzc2FnZSwKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBwYXJhbWV0ZXJzLAogIH0pIGFzeW5jIHsKICAgIGF3YWl0IGxvZ0V2ZW50KAogICAgICBBbmFseXRpY3NFdmVudHMuZXJyb3IuYXBwRXJyb3IsCiAgICAgIHBhcmFtZXRlcnM6IHsKICAgICAgICAnZXJyb3JfdHlwZSc6IGVycm9yVHlwZSwKICAgICAgICBpZiAoZXJyb3JNZXNzYWdlICE9IG51bGwpICdlcnJvcl9tZXNzYWdlJzogZXJyb3JNZXNzYWdlLAogICAgICAgICd0aW1lc3RhbXAnOiBEYXRlVGltZS5ub3coKS50b0lzbzg2MDFTdHJpbmcoKSwKICAgICAgICAuLi4/cGFyYW1ldGVycywKICAgICAgfSwKICAgICk7CiAgfQoKICAvLy8gQWRkaXRpb25hbCBzdHJ1Y3R1cmVkIGV2ZW50IGhlbHBlcnMKICBzdGF0aWMgRnV0dXJlPHZvaWQ+IGxvZ1N1cnZleVN0YXJ0ZWQoCiAgICBTdHJpbmcgc3VydmV5SWQsIHsKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBwYXJhbWV0ZXJzLAogIH0pIGFzeW5jIHsKICAgIGF3YWl0IGxvZ0V2ZW50KAogICAgICBBbmFseXRpY3NFdmVudHMuc3VydmV5LnN0YXJ0ZWQsCiAgICAgIHBhcmFtZXRlcnM6IHsKICAgICAgICAnc3VydmV5X2lkJzogc3VydmV5SWQsCiAgICAgICAgJ3RpbWVzdGFtcCc6IERhdGVUaW1lLm5vdygpLnRvSXNvODYwMVN0cmluZygpLAogICAgICAgIC4uLj9wYXJhbWV0ZXJzLAogICAgICB9LAogICAgKTsKICB9CgogIHN0YXRpYyBGdXR1cmU8dm9pZD4gbG9nU3VydmV5Q29tcGxldGVkKAogICAgU3RyaW5nIHN1cnZleUlkLCB7CiAgICBpbnQ/IGR1cmF0aW9uLAogICAgTWFwPFN0cmluZywgZHluYW1pYz4/IHBhcmFtZXRlcnMsCiAgfSkgYXN5bmMgewogICAgYXdhaXQgbG9nRXZlbnQoCiAgICAgIEFuYWx5dGljc0V2ZW50cy5zdXJ2ZXkuY29tcGxldGVkLAogICAgICBwYXJhbWV0ZXJzOiB7CiAgICAgICAgJ3N1cnZleV9pZCc6IHN1cnZleUlkLAogICAgICAgIGlmIChkdXJhdGlvbiAhPSBudWxsKSAnZHVyYXRpb25fc2Vjb25kcyc6IGR1cmF0aW9uLAogICAgICAgICd0aW1lc3RhbXAnOiBEYXRlVGltZS5ub3coKS50b0lzbzg2MDFTdHJpbmcoKSwKICAgICAgICAuLi4/cGFyYW1ldGVycywKICAgICAgfSwKICAgICk7CiAgfQp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/utils/index.dart", + "data": "ZXhwb3J0ICdhbmFseXRpY3NfaGVscGVyLmRhcnQnOwo=", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/{{analytics_dir}}/pubspec.yaml", + "data": + "bmFtZTogYW5hbHl0aWNzCmRlc2NyaXB0aW9uOiAiRXZlbnQgdHJhY2tpbmcgZm9yIHt7bmFtZS50aXRsZUNhc2UoKX19IGJlaGluZCBhIHN3YXBwYWJsZSBjbGllbnQgaW50ZXJmYWNlLiIKdmVyc2lvbjogMC4xLjAKcHVibGlzaF90bzogJ25vbmUnCgplbnZpcm9ubWVudDoKICBzZGs6IF4zLjguMAogIGZsdXR0ZXI6ICI+PTMuMjkuMCIKCmRlcGVuZGVuY2llczoKICBmbHV0dGVyOgogICAgc2RrOiBmbHV0dGVyCiAgZmlyZWJhc2VfYW5hbHl0aWNzOiBeMTEuMy41CiAgY29yZToKICAgIHBhdGg6IC4uL2NvcmUKICBkaToKICAgIHBhdGg6IC4uL2RpCgpkZXZfZGVwZW5kZW5jaWVzOgogIGZsdXR0ZXJfdGVzdDoKICAgIHNkazogZmx1dHRlcgogIGZsdXR0ZXJfbGludHM6IF41LjAuMAoKZmx1dHRlcjoK", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/{{analytics_dir}}/README.md", + "data": + "IyBBbmFseXRpY3MgUGFja2FnZQoKQSBzdHJ1Y3R1cmVkIGFuYWx5dGljcyBwYWNrYWdlIGZvciB7e25hbWUudGl0bGVDYXNlKCl9fSB3aXRoIEZpcmViYXNlIEFuYWx5dGljcyBpbnRlZ3JhdGlvbiBhbmQgcGx1Z2dhYmxlIHByb3ZpZGVyIGFyY2hpdGVjdHVyZS4KCiMjIEZlYXR1cmVzCgotIOKchSAqKkludGVyZmFjZS1iYXNlZCBkZXNpZ24qKiAtIEVhc3kgdG8gc3dpdGNoIGFuYWx5dGljcyBwcm92aWRlcnMKLSDinIUgKipBdXRvbWF0aWMgc2NyZWVuIHRyYWNraW5nKiogLSBSb3V0ZSBvYnNlcnZlciB0cmFja3MgbmF2aWdhdGlvbiBhdXRvbWF0aWNhbGx5Ci0g4pyFICoqU3RydWN0dXJlZCBldmVudCBuYW1lcyoqIC0gVHlwZS1zYWZlLCBvcmdhbml6ZWQgZXZlbnQgY29uc3RhbnRzCi0g4pyFICoqRGVwZW5kZW5jeSBpbmplY3Rpb24qKiAtIEludGVncmF0ZWQgd2l0aCBESSBjb250YWluZXIKLSDinIUgKipFcnJvciBoYW5kbGluZyoqIC0gR3JhY2VmdWwgZmFpbHVyZXMgZG9uJ3QgY3Jhc2ggdGhlIGFwcAotIOKchSAqKkRlYnVnIGxvZ2dpbmcqKiAtIENvbmZpZ3VyYWJsZSBsb2dnaW5nIGZvciBkZXZlbG9wbWVudAoKIyMgUXVpY2sgU3RhcnQKCiMjIyAxLiBJbml0aWFsaXplIEFuYWx5dGljcwoKYGBgZGFydAppbXBvcnQgJ3BhY2thZ2U6YW5hbHl0aWNzL2FuYWx5dGljcy5kYXJ0JzsKCmZpbmFsIGFuYWx5dGljc0NvbmZpZyA9IERlZmF1bHRBbmFseXRpY3NDb25maWcoCiAgZW5hYmxlQW5hbHl0aWNzOiB0cnVlLAogIGVuYWJsZURlYnVnTG9nZ2luZzogdHJ1ZSwgLy8gU2V0IHRvIGZhbHNlIGluIHByb2R1Y3Rpb24KKTsKYXdhaXQgaW5pdChjb25maWc6IGFuYWx5dGljc0NvbmZpZyk7CmBgYAoKIyMjIDIuIEF1dG9tYXRpYyBTY3JlZW4gVHJhY2tpbmcKCkFkZCB0aGUgYEFuYWx5dGljc1JvdXRlT2JzZXJ2ZXJgIHRvIHlvdXIgcm91dGVyOgoKYGBgZGFydApHb1JvdXRlcigKICBvYnNlcnZlcnM6IFsKICAgIEFuYWx5dGljc1JvdXRlT2JzZXJ2ZXIoKSwgLy8gVXNlcyBGaXJlYmFzZUFuYWx5dGljc09ic2VydmVyIGludGVybmFsbHkKICBdLAogIC8vIC4uLiByb3V0ZXMKKTsKYGBgCgpUaGUgYEFuYWx5dGljc1JvdXRlT2JzZXJ2ZXJgIGludGVybmFsbHkgdXNlcyBGaXJlYmFzZSdzIG5hdGl2ZSBgRmlyZWJhc2VBbmFseXRpY3NPYnNlcnZlcmAgZm9yIHByb3BlciBzY3JlZW4gdHJhY2tpbmcgd2hpbGUgYWRkaW5nIGFkZGl0aW9uYWwgbG9nZ2luZyBhbmQgZXJyb3IgaGFuZGxpbmcuCgojIyMgMy4gTWFudWFsIEV2ZW50IFRyYWNraW5nCgojIyMjIFVzaW5nIFN0cnVjdHVyZWQgRXZlbnQgTmFtZXMgKFJlY29tbWVuZGVkKQoKYGBgZGFydAovLyBVc2VyIGV2ZW50cwphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nRXZlbnQoQW5hbHl0aWNzRXZlbnRzLnVzZXIuYXV0aGVudGljYXRlZFJlZGlyZWN0KTsKCi8vIEF1dGhlbnRpY2F0aW9uIGV2ZW50cwphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nRXZlbnQoCiAgQW5hbHl0aWNzRXZlbnRzLmF1dGguc2lnbkluLAogIHBhcmFtZXRlcnM6IHsnbWV0aG9kJzogJ2dvb2dsZSd9LAopOwoKLy8gRmVhdHVyZSB1c2FnZQphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nRXZlbnQoCiAgQW5hbHl0aWNzRXZlbnRzLmZlYXR1cmUudXNlZCwKICBwYXJhbWV0ZXJzOiB7J2ZlYXR1cmVfbmFtZSc6ICdoZWFsdGhfc3luYyd9LAopOwoKLy8gU3VydmV5IGV2ZW50cwphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nRXZlbnQoCiAgQW5hbHl0aWNzRXZlbnRzLnN1cnZleS5jb21wbGV0ZWQsCiAgcGFyYW1ldGVyczogewogICAgJ3N1cnZleV9pZCc6ICd3ZWxsbmVzc19hc3Nlc3NtZW50JywKICAgICdkdXJhdGlvbl9zZWNvbmRzJzogMTIwLAogIH0sCik7CmBgYAoKIyMjIyBVc2luZyBIZWxwZXIgTWV0aG9kcwoKYGBgZGFydAovLyBGZWF0dXJlIHRyYWNraW5nCmF3YWl0IEFuYWx5dGljc0hlbHBlci5sb2dGZWF0dXJlVXNlZCgnaGVhbHRoX2Rhc2hib2FyZCcpOwoKLy8gU3VydmV5IHRyYWNraW5nCmF3YWl0IEFuYWx5dGljc0hlbHBlci5sb2dTdXJ2ZXlTdGFydGVkKCdtb29kX3RyYWNrZXInKTsKYXdhaXQgQW5hbHl0aWNzSGVscGVyLmxvZ1N1cnZleUNvbXBsZXRlZCgnbW9vZF90cmFja2VyJywgZHVyYXRpb246IDQ1KTsKCi8vIEVycm9yIHRyYWNraW5nCmF3YWl0IEFuYWx5dGljc0hlbHBlci5sb2dBcHBFcnJvcignbmV0d29ya190aW1lb3V0Jyk7CgovLyBCdXR0b24gaW50ZXJhY3Rpb25zCmF3YWl0IEFuYWx5dGljc0hlbHBlci5sb2dCdXR0b25QcmVzc2VkKCdzdGFydF9hc3Nlc3NtZW50Jyk7CgovLyBBdXRoZW50aWNhdGlvbiBldmVudHMKYXdhaXQgQW5hbHl0aWNzSGVscGVyLmxvZ1NpZ25JbihtZXRob2Q6ICdnb29nbGUnKTsKYXdhaXQgQW5hbHl0aWNzSGVscGVyLmxvZ1NpZ25VcChtZXRob2Q6ICdlbWFpbCcpOwoKLy8gVXNlciBtYW5hZ2VtZW50CmF3YWl0IEFuYWx5dGljc0hlbHBlci5zZXRVc2VySWQoJ3VzZXIxMjMnKTsKYXdhaXQgQW5hbHl0aWNzSGVscGVyLnNldFVzZXJQcm9wZXJ0eShuYW1lOiAncGxhbicsIHZhbHVlOiAncHJlbWl1bScpOwoKLy8gQXBwIGxpZmVjeWNsZQphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nQXBwT3BlbigpOwpgYGAKCiMjIyMgRGlyZWN0IENsaWVudCBVc2FnZQoKYGBgZGFydApmaW5hbCBhbmFseXRpY3NDbGllbnQgPSBkaS5nZXQ8QW5hbHl0aWNzQ2xpZW50PigpOwoKYXdhaXQgYW5hbHl0aWNzQ2xpZW50LmxvZ0V2ZW50KAogIG5hbWU6ICdjdXN0b21fZXZlbnQnLAogIHBhcmFtZXRlcnM6IHsna2V5JzogJ3ZhbHVlJ30sCik7Cgphd2FpdCBhbmFseXRpY3NDbGllbnQuc2V0VXNlcklkKCd1c2VyMTIzJyk7CmF3YWl0IGFuYWx5dGljc0NsaWVudC5zZXRVc2VyUHJvcGVydHkobmFtZTogJ3BsYW4nLCB2YWx1ZTogJ3ByZW1pdW0nKTsKYGBgCgojIyBTdHJ1Y3R1cmVkIEV2ZW50IE5hbWVzCgpUaGUgcGFja2FnZSBwcm92aWRlcyBvcmdhbml6ZWQsIHR5cGUtc2FmZSBldmVudCBuYW1lcyB0aHJvdWdoIGBBbmFseXRpY3NFdmVudHNgOgoKIyMjIEF2YWlsYWJsZSBFdmVudCBDYXRlZ29yaWVzCgpgYGBkYXJ0Ci8vIFVzZXItcmVsYXRlZCBldmVudHMKQW5hbHl0aWNzRXZlbnRzLnVzZXIuYXV0aGVudGljYXRlZFJlZGlyZWN0CkFuYWx5dGljc0V2ZW50cy51c2VyLnByb2ZpbGVVcGRhdGVkCkFuYWx5dGljc0V2ZW50cy51c2VyLm9uYm9hcmRpbmdDb21wbGV0ZWQKCi8vIEF1dGhlbnRpY2F0aW9uIGV2ZW50cwpBbmFseXRpY3NFdmVudHMuYXV0aC5zaWduSW4KQW5hbHl0aWNzRXZlbnRzLmF1dGguc2lnblVwCkFuYWx5dGljc0V2ZW50cy5hdXRoLnNpZ25PdXQKCi8vIE5hdmlnYXRpb24gZXZlbnRzCkFuYWx5dGljc0V2ZW50cy5uYXZpZ2F0aW9uLnNjcmVlblZpZXcKQW5hbHl0aWNzRXZlbnRzLm5hdmlnYXRpb24udGFiU3dpdGNoZWQKCi8vIEZlYXR1cmUgdXNhZ2UKQW5hbHl0aWNzRXZlbnRzLmZlYXR1cmUudXNlZApBbmFseXRpY3NFdmVudHMuZmVhdHVyZS50dXRvcmlhbENvbXBsZXRlZAoKLy8gQXBwIGxpZmVjeWNsZQpBbmFseXRpY3NFdmVudHMuYXBwLm9wZW4KQW5hbHl0aWNzRXZlbnRzLmFwcC5iYWNrZ3JvdW5kCkFuYWx5dGljc0V2ZW50cy5hcHAuZm9yZWdyb3VuZAoKLy8gU3VydmV5IGV2ZW50cwpBbmFseXRpY3NFdmVudHMuc3VydmV5LnN0YXJ0ZWQKQW5hbHl0aWNzRXZlbnRzLnN1cnZleS5jb21wbGV0ZWQKQW5hbHl0aWNzRXZlbnRzLnN1cnZleS5hYmFuZG9uZWQKCi8vIEhlYWx0aCBkYXRhIGV2ZW50cwpBbmFseXRpY3NFdmVudHMuaGVhbHRoLmRhdGFTeW5jZWQKQW5hbHl0aWNzRXZlbnRzLmhlYWx0aC5kZXZpY2VDb25uZWN0ZWQKQW5hbHl0aWNzRXZlbnRzLmhlYWx0aC5nb2FsQWNoaWV2ZWQKCi8vIEVycm9yIGV2ZW50cwpBbmFseXRpY3NFdmVudHMuZXJyb3IuYXBwRXJyb3IKQW5hbHl0aWNzRXZlbnRzLmVycm9yLm5ldHdvcmtFcnJvcgpgYGAKCiMjIENvbmZpZ3VyYXRpb24KCiMjIyBBbmFseXRpY3NDb25maWcgT3B0aW9ucwoKYGBgZGFydApmaW5hbCBjb25maWcgPSBEZWZhdWx0QW5hbHl0aWNzQ29uZmlnKAogIGVuYWJsZUFuYWx5dGljczogdHJ1ZSwgICAgICAgIC8vIEVuYWJsZS9kaXNhYmxlIGFuYWx5dGljcyBjb2xsZWN0aW9uCiAgZW5hYmxlRGVidWdMb2dnaW5nOiBmYWxzZSwgICAgLy8gRGVidWcgbG9nZ2luZyAoZGV2ZWxvcG1lbnQgb25seSkKICB1c2VySWQ6ICd1c2VyMTIzJywgICAgICAgICAgIC8vIE9wdGlvbmFsOiBTZXQgaW5pdGlhbCB1c2VyIElECiAgZGVmYXVsdFVzZXJQcm9wZXJ0aWVzOiB7ICAgICAgLy8gT3B0aW9uYWw6IERlZmF1bHQgdXNlciBwcm9wZXJ0aWVzCiAgICAndXNlcl90eXBlJzogJ3ByZW1pdW0nLAogICAgJ2FwcF92ZXJzaW9uJzogJzEuMC4wJywKICB9LAopOwpgYGAKCiMjIyBFbnZpcm9ubWVudC1iYXNlZCBDb25maWd1cmF0aW9uCgpgYGBkYXJ0CmltcG9ydCAncGFja2FnZTpmbHV0dGVyL2ZvdW5kYXRpb24uZGFydCc7CgpmaW5hbCBjb25maWcgPSBEZWZhdWx0QW5hbHl0aWNzQ29uZmlnKAogIGVuYWJsZUFuYWx5dGljczogIWtEZWJ1Z01vZGUsICAgICAgICAgIC8vIERpc2FibGUgaW4gZGVidWcgYnVpbGRzCiAgZW5hYmxlRGVidWdMb2dnaW5nOiBrRGVidWdNb2RlLCAgICAgICAgLy8gRGVidWcgbG9nZ2luZyBpbiBkZXZlbG9wbWVudAopOwpgYGAKCiMjIEFyY2hpdGVjdHVyZQoKIyMjIENvbXBvbmVudHMKCi0gKipBbmFseXRpY3NDbGllbnQqKiAtIEFic3RyYWN0IGludGVyZmFjZSBmb3IgYW5hbHl0aWNzIG9wZXJhdGlvbnMKLSAqKkZpcmViYXNlQW5hbHl0aWNzQ2xpZW50KiogLSBGaXJlYmFzZSBBbmFseXRpY3MgaW1wbGVtZW50YXRpb24KLSAqKkFuYWx5dGljc1JvdXRlT2JzZXJ2ZXIqKiAtIEF1dG9tYXRpYyBzY3JlZW4gdHJhY2tpbmcKLSAqKkFuYWx5dGljc0hlbHBlcioqIC0gQ29udmVuaWVuY2UgbWV0aG9kcyBmb3IgY29tbW9uIGV2ZW50cwotICoqQW5hbHl0aWNzRXZlbnRzKiogLSBTdHJ1Y3R1cmVkIGV2ZW50IG5hbWUgY29uc3RhbnRzCgojIyMgUGx1Z2dhYmxlIERlc2lnbgoKU3dpdGNoIGFuYWx5dGljcyBwcm92aWRlcnMgYnkgaW1wbGVtZW50aW5nIGBBbmFseXRpY3NDbGllbnRgOgoKYGBgZGFydApjbGFzcyBNaXhwYW5lbEFuYWx5dGljc0NsaWVudCBpbXBsZW1lbnRzIEFuYWx5dGljc0NsaWVudCB7CiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IGxvZ0V2ZW50KHtyZXF1aXJlZCBTdHJpbmcgbmFtZSwgTWFwPFN0cmluZywgZHluYW1pYz4/IHBhcmFtZXRlcnN9KSB7CiAgICAvLyBNaXhwYW5lbCBpbXBsZW1lbnRhdGlvbgogIH0KICAvLyAuLi4gb3RoZXIgbWV0aG9kcwp9CgovLyBSZWdpc3RlciB3aXRoIERJCmRpLnJlZ2lzdGVyPEFuYWx5dGljc0NsaWVudD4oTWl4cGFuZWxBbmFseXRpY3NDbGllbnQoY29uZmlnKSk7CmBgYAoKIyMgQmVzdCBQcmFjdGljZXMKCjEuICoqVXNlIFN0cnVjdHVyZWQgRXZlbnRzKio6IEFsd2F5cyBwcmVmZXIgYEFuYWx5dGljc0V2ZW50cy5jYXRlZ29yeS5ldmVudE5hbWVgIG92ZXIgc3RyaW5nIGxpdGVyYWxzCjIuICoqSW5jbHVkZSBDb250ZXh0Kio6IEFkZCByZWxldmFudCBwYXJhbWV0ZXJzIGxpa2UgYHNjcmVlbl9uYW1lYCwgYHVzZXJfaWRgLCBldGMuCjMuICoqQ29uc2lzdGVudCBOYW1pbmcqKjogVXNlIHNuYWtlX2Nhc2UgZm9yIGV2ZW50IG5hbWVzIGFuZCBwYXJhbWV0ZXJzCjQuICoqRXJyb3IgSGFuZGxpbmcqKjogQW5hbHl0aWNzIGZhaWx1cmVzIHNob3VsZCBuZXZlciBjcmFzaCB0aGUgYXBwCjUuICoqUHJpdmFjeSoqOiBSZXNwZWN0IHVzZXIgcHJpdmFjeSBzZXR0aW5ncyBhbmQgZGF0YSByZWd1bGF0aW9ucwoKIyMgRXhhbXBsZXMKCiMjIyBUcmFjayBVc2VyIEpvdXJuZXkKCmBgYGRhcnQKLy8gQXBwIGxhdW5jaAphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nQXBwT3BlbigpOwoKLy8gVXNlciBzaWducyBpbgphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nU2lnbkluKG1ldGhvZDogJ2dvb2dsZScpOwoKLy8gVXNlciBjb21wbGV0ZXMgb25ib2FyZGluZwphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nRXZlbnQoCiAgQW5hbHl0aWNzRXZlbnRzLnVzZXIub25ib2FyZGluZ0NvbXBsZXRlZCwKICBwYXJhbWV0ZXJzOiB7J3N0ZXBzX2NvbXBsZXRlZCc6IDV9LAopOwoKLy8gVXNlciB1c2VzIGEgZmVhdHVyZQphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nRmVhdHVyZVVzZWQoJ2hlYWx0aF9zeW5jJyk7CgovLyBVc2VyIGNvbXBsZXRlcyBhIHN1cnZleQphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nU3VydmV5Q29tcGxldGVkKAogICd3ZWxsbmVzc19hc3Nlc3NtZW50JywKICBkdXJhdGlvbjogMTgwLAopOwpgYGAKCiMjIyBFcnJvciBUcmFja2luZwoKYGBgZGFydAp0cnkgewogIC8vIFNvbWUgb3BlcmF0aW9uCn0gY2F0Y2ggKGVycm9yKSB7CiAgYXdhaXQgQW5hbHl0aWNzSGVscGVyLmxvZ0FwcEVycm9yKAogICAgJ2FwaV9jYWxsX2ZhaWxlZCcsCiAgICBlcnJvck1lc3NhZ2U6IGVycm9yLnRvU3RyaW5nKCksCiAgICBwYXJhbWV0ZXJzOiB7CiAgICAgICdlbmRwb2ludCc6ICcvYXBpL2hlYWx0aC9zeW5jJywKICAgICAgJ3VzZXJfaWQnOiB1c2VySWQsCiAgICB9LAogICk7CiAgcmV0aHJvdzsKfQpgYGAKClRoaXMgc3RydWN0dXJlZCBhcHByb2FjaCBlbnN1cmVzIGNvbnNpc3RlbnQsIGRpc2NvdmVyYWJsZSwgYW5kIG1haW50YWluYWJsZSBhbmFseXRpY3MgdGhyb3VnaG91dCB0aGUge3tuYW1lLnRpdGxlQ2FzZSgpfX0gYXBwLg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{analytics_dir}}/test/analytics_test.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmFuYWx5dGljcy9hbmFseXRpY3MuZGFydCc7CmltcG9ydCAncGFja2FnZTpmbHV0dGVyX3Rlc3QvZmx1dHRlcl90ZXN0LmRhcnQnOwoKdm9pZCBtYWluKCkgewogIGdyb3VwKCdBbmFseXRpY3MgUGFja2FnZScsICgpIHsKICAgIHRlc3QoJ3Nob3VsZCBjcmVhdGUgQW5hbHl0aWNzRXZlbnQgY29ycmVjdGx5JywgKCkgewogICAgICBjb25zdCBldmVudCA9IEFuYWx5dGljc0V2ZW50KAogICAgICAgIG5hbWU6ICd0ZXN0X2V2ZW50JywKICAgICAgICBwYXJhbWV0ZXJzOiB7J2tleSc6ICd2YWx1ZSd9LAogICAgICApOwoKICAgICAgZXhwZWN0KGV2ZW50Lm5hbWUsICd0ZXN0X2V2ZW50Jyk7CiAgICAgIGV4cGVjdChldmVudC5wYXJhbWV0ZXJzLCB7J2tleSc6ICd2YWx1ZSd9KTsKICAgIH0pOwoKICAgIHRlc3QoJ3Nob3VsZCBjcmVhdGUgRGVmYXVsdEFuYWx5dGljc0NvbmZpZyBjb3JyZWN0bHknLCAoKSB7CiAgICAgIGNvbnN0IGNvbmZpZyA9IERlZmF1bHRBbmFseXRpY3NDb25maWcoCiAgICAgICAgZW5hYmxlQW5hbHl0aWNzOiB0cnVlLAogICAgICAgIGVuYWJsZURlYnVnTG9nZ2luZzogZmFsc2UsCiAgICAgICk7CgogICAgICBleHBlY3QoY29uZmlnLmVuYWJsZUFuYWx5dGljcywgaXNUcnVlKTsKICAgICAgZXhwZWN0KGNvbmZpZy5lbmFibGVEZWJ1Z0xvZ2dpbmcsIGlzRmFsc2UpOwogICAgfSk7CiAgfSk7Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/crashlytics.dart", + "data": + "bGlicmFyeTsKCmltcG9ydCAnZGFydDphc3luYyc7CgppbXBvcnQgJ3BhY2thZ2U6Y29yZS9jb3JlLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Y3Jhc2hseXRpY3Mvc3JjL2NsaWVudC9pbmRleC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmNyYXNobHl0aWNzL3NyYy9jb25maWcvaW5kZXguZGFydCc7CmltcG9ydCAncGFja2FnZTpkaS9kaS5kYXJ0JzsKCmV4cG9ydCAnc3JjL2NsaWVudC9pbmRleC5kYXJ0JzsKZXhwb3J0ICdzcmMvY29uZmlnL2luZGV4LmRhcnQnOwpleHBvcnQgJ3NyYy9tb2RlbHMvaW5kZXguZGFydCc7CgovLy8gSW5pdGlhbGl6ZSB0aGUgY3Jhc2hseXRpY3MgbW9kdWxlIHdpdGggZGVwZW5kZW5jeSBpbmplY3Rpb24KRnV0dXJlPHZvaWQ+IGluaXQoe0NyYXNobHl0aWNzQ29uZmlnPyBjb25maWd9KSBhc3luYyB7CiAgYXdhaXQgcmVnaXN0ZXJDcmFzaGx5dGljc1dpdGhESShjb25maWcgPz8gY29uc3QgRGVmYXVsdENyYXNobHl0aWNzQ29uZmlnKCkpOwogIGZpbmFsIGxvZ2dlciA9IGRpLmdldDxMb2dnZXI+KCk7CiAgbG9nZ2VyLmkoJ/CflKUgQ3Jhc2hseXRpY3MgbW9kdWxlIGluaXRpYWxpemVkJyk7Cn0KCi8vLyBSZWdpc3RlciBjcmFzaGx5dGljcyBjb21wb25lbnRzIHdpdGggREkgY29udGFpbmVyCkZ1dHVyZTx2b2lkPiByZWdpc3RlckNyYXNobHl0aWNzV2l0aERJKENyYXNobHl0aWNzQ29uZmlnIGNvbmZpZykgYXN5bmMgewogIGZpbmFsIGxvZ2dlciA9IGRpLmdldDxMb2dnZXI+KCk7CgogIHRyeSB7CiAgICAvLyBSZWdpc3RlciBjb25maWcKICAgIGRpLnJlZ2lzdGVyPENyYXNobHl0aWNzQ29uZmlnPihjb25maWcpOwoKICAgIC8vIENyZWF0ZSBhbmQgcmVnaXN0ZXIgY3Jhc2hseXRpY3MgY2xpZW50CiAgICBmaW5hbCBjbGllbnQgPSBGaXJlYmFzZUNyYXNobHl0aWNzQ2xpZW50KGNvbmZpZzogY29uZmlnLCBsb2dnZXI6IGxvZ2dlcik7CiAgICBkaS5yZWdpc3RlcjxDcmFzaGx5dGljc0NsaWVudD4oCiAgICAgIGNsaWVudCwKICAgICAgZGlzcG9zZTogKGNsaWVudCkgPT4gY2xpZW50LmRpc3Bvc2UoKSwKICAgICk7CgogICAgLy8gSW5pdGlhbGl6ZSB0aGUgY2xpZW50CiAgICBhd2FpdCBjbGllbnQuaW5pdGlhbGl6ZSgpOwoKICAgIC8vIENoZWNrIGlmIHRoZXJlIGFyZSBhbnkgdW5zZW50IHJlcG9ydHMKICAgIHVuYXdhaXRlZCgKICAgICAgY2xpZW50LmNoZWNrRm9yVW5zZW50UmVwb3J0cygpLnRoZW48dm9pZD4oKGhhc1JlcG9ydHMpIHsKICAgICAgICBpZiAoaGFzUmVwb3J0cykgewogICAgICAgICAgbG9nZ2VyLncoJ+KaoO+4jyBUaGVyZSBhcmUgdW5zZW50IGNyYXNoIHJlcG9ydHMuJyk7CiAgICAgICAgICB1bmF3YWl0ZWQoY2xpZW50LnNlbmRVbnNlbnRSZXBvcnRzKCkpOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICBsb2dnZXIuaSgn4pyFIE5vIHVuc2VudCBjcmFzaCByZXBvcnRzIGZvdW5kLicpOwogICAgICAgIH0KICAgICAgfSksCiAgICApOwoKICAgIGxvZ2dlci5pKCfwn5SlIENyYXNobHl0aWNzIHJlZ2lzdGVyZWQgd2l0aCBESSBjb250YWluZXInKTsKICB9IGNhdGNoIChlLCBzdGFja1RyYWNlKSB7CiAgICBsb2dnZXIuZSgn4p2MIEZhaWxlZCB0byByZWdpc3RlciBjcmFzaGx5dGljcyB3aXRoIERJJywgZSwgc3RhY2tUcmFjZSk7CiAgICByZXRocm93OwogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/client/crashlytics_client.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNyYXNobHl0aWNzL3NyYy9tb2RlbHMvdXNlcl9tZXRhZGF0YS5kYXJ0JzsKCmFic3RyYWN0IGNsYXNzIENyYXNobHl0aWNzQ2xpZW50IHsKICAvLy8gSW5pdGlhbGl6ZSB0aGUgY3Jhc2hseXRpY3MgY2xpZW50CiAgRnV0dXJlPHZvaWQ+IGluaXRpYWxpemUoKTsKCiAgLy8vIFJlY29yZCBhIGZhdGFsIGVycm9yL2V4Y2VwdGlvbgogIEZ1dHVyZTx2b2lkPiByZWNvcmRFcnJvcigKICAgIGR5bmFtaWMgZXhjZXB0aW9uLAogICAgU3RhY2tUcmFjZT8gc3RhY2tUcmFjZSwgewogICAgYm9vbCBmYXRhbCA9IGZhbHNlLAogICAgSXRlcmFibGU8T2JqZWN0PiBpbmZvcm1hdGlvbiA9IGNvbnN0IFtdLAogIH0pOwoKICAvLy8gUmVjb3JkIGEgbm9uLWZhdGFsIGVycm9yIHdpdGggY3VzdG9tIGNvbnRleHQKICBGdXR1cmU8dm9pZD4gcmVjb3JkRmx1dHRlckZhdGFsRXJyb3IoCiAgICBkeW5hbWljIGV4Y2VwdGlvbiwKICAgIFN0YWNrVHJhY2Ugc3RhY2tUcmFjZSwgewogICAgTWFwPFN0cmluZywgZHluYW1pYz4/IGNvbnRleHQsCiAgfSk7CgogIC8vLyBMb2cgYSBjdXN0b20gbWVzc2FnZSBmb3IgZGVidWdnaW5nCiAgRnV0dXJlPHZvaWQ+IGxvZyhTdHJpbmcgbWVzc2FnZSk7CgogIC8vLyBTZXQgdXNlciBpZGVudGlmaWVyIGZvciBjcmFzaCByZXBvcnRzCiAgRnV0dXJlPHZvaWQ+IHNldFVzZXJJZGVudGlmaWVyKFN0cmluZyBpZGVudGlmaWVyKTsKCiAgLy8vIFNldCB1c2VyIG1ldGFkYXRhCiAgRnV0dXJlPHZvaWQ+IHNldFVzZXJNZXRhZGF0YShVc2VyTWV0YWRhdGEgbWV0YWRhdGEpOwoKICAvLy8gU2V0IGN1c3RvbSBrZXktdmFsdWUgcGFpcnMgZm9yIGRlYnVnZ2luZwogIEZ1dHVyZTx2b2lkPiBzZXRDdXN0b21LZXkoU3RyaW5nIGtleSwgZHluYW1pYyB2YWx1ZSk7CgogIC8vLyBTZXQgY3VzdG9tIGtleXMgZnJvbSBhIG1hcAogIEZ1dHVyZTx2b2lkPiBzZXRDdXN0b21LZXlzKE1hcDxTdHJpbmcsIGR5bmFtaWM+IGtleXMpOwoKICAvLy8gQ2hlY2sgaWYgY3Jhc2hseXRpY3MgY29sbGVjdGlvbiBpcyBlbmFibGVkCiAgYm9vbCBnZXQgaXNDcmFzaGx5dGljc0NvbGxlY3Rpb25FbmFibGVkOwoKICAvLy8gRW5hYmxlIG9yIGRpc2FibGUgY3Jhc2hseXRpY3MgY29sbGVjdGlvbgogIEZ1dHVyZTx2b2lkPiBzZXRDcmFzaGx5dGljc0NvbGxlY3Rpb25FbmFibGVkKGJvb2wgZW5hYmxlZCk7CgogIC8vLyBTZW5kIGFueSB1bnNlbnQgY3Jhc2ggcmVwb3J0cwogIEZ1dHVyZTx2b2lkPiBzZW5kVW5zZW50UmVwb3J0cygpOwoKICAvLy8gRGVsZXRlIGFueSB1bnNlbnQgY3Jhc2ggcmVwb3J0cwogIEZ1dHVyZTx2b2lkPiBkZWxldGVVbnNlbnRSZXBvcnRzKCk7CgogIC8vLyBDaGVjayBpZiB0aGVyZSBhcmUgYW55IHVuc2VudCBjcmFzaCByZXBvcnRzCiAgRnV0dXJlPGJvb2w+IGNoZWNrRm9yVW5zZW50UmVwb3J0cygpOwoKICAvLy8gRGlzcG9zZSBvZiByZXNvdXJjZXMKICB2b2lkIGRpc3Bvc2UoKTsKfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/client/firebase_crashlytics_client.dart", + "data": + "aW1wb3J0ICdkYXJ0OmFzeW5jJzsKaW1wb3J0ICdkYXJ0OmNvbGxlY3Rpb24nOwoKaW1wb3J0ICdwYWNrYWdlOmNvcmUvY29yZS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmNyYXNobHl0aWNzL3NyYy9jbGllbnQvY3Jhc2hseXRpY3NfY2xpZW50LmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Y3Jhc2hseXRpY3Mvc3JjL2NvbmZpZy9jcmFzaGx5dGljc19jb25maWcuZGFydCc7CmltcG9ydCAncGFja2FnZTpjcmFzaGx5dGljcy9zcmMvbW9kZWxzL3VzZXJfbWV0YWRhdGEuZGFydCc7CmltcG9ydCAncGFja2FnZTpkaS9kaS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZpcmViYXNlX2NyYXNobHl0aWNzL2ZpcmViYXNlX2NyYXNobHl0aWNzLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlci9mb3VuZGF0aW9uLmRhcnQnOwoKY2xhc3MgRmlyZWJhc2VDcmFzaGx5dGljc0NsaWVudCBpbXBsZW1lbnRzIENyYXNobHl0aWNzQ2xpZW50IHsKICBGaXJlYmFzZUNyYXNobHl0aWNzQ2xpZW50KHtyZXF1aXJlZCB0aGlzLmNvbmZpZywgTG9nZ2VyPyBsb2dnZXJ9KQogICAgOiBfbG9nZ2VyID0gbG9nZ2VyID8/IGRpLmdldDxMb2dnZXI+KCk7CgogIGZpbmFsIENyYXNobHl0aWNzQ29uZmlnIGNvbmZpZzsKICBmaW5hbCBMb2dnZXIgX2xvZ2dlcjsKICBsYXRlIGZpbmFsIEZpcmViYXNlQ3Jhc2hseXRpY3MgX2NyYXNobHl0aWNzOwogIGZpbmFsIFF1ZXVlPFN0cmluZz4gX2xvZ0J1ZmZlciA9IFF1ZXVlPFN0cmluZz4oKTsKICBib29sIF9pc0luaXRpYWxpemVkID0gZmFsc2U7CgogIEBvdmVycmlkZQogIEZ1dHVyZTx2b2lkPiBpbml0aWFsaXplKCkgYXN5bmMgewogICAgdHJ5IHsKICAgICAgX2NyYXNobHl0aWNzID0gRmlyZWJhc2VDcmFzaGx5dGljcy5pbnN0YW5jZTsKCiAgICAgIC8vIFNldCBjcmFzaGx5dGljcyBjb2xsZWN0aW9uIGVuYWJsZWQgYmFzZWQgb24gY29uZmlnCiAgICAgIGF3YWl0IF9jcmFzaGx5dGljcy5zZXRDcmFzaGx5dGljc0NvbGxlY3Rpb25FbmFibGVkKAogICAgICAgIGNvbmZpZy5lbmFibGVJbkRlYnVnTW9kZSB8fCBrUmVsZWFzZU1vZGUsCiAgICAgICk7CgogICAgICAvLyBTZXQgdXAgYXV0b21hdGljIEZsdXR0ZXIgZXJyb3IgaGFuZGxpbmcKICAgICAgaWYgKGNvbmZpZy5lbmFibGVBdXRvbWF0aWNEYXRhQ29sbGVjdGlvbikgewogICAgICAgIEZsdXR0ZXJFcnJvci5vbkVycm9yID0gKGVycm9yRGV0YWlscykgewogICAgICAgICAgX2NyYXNobHl0aWNzLnJlY29yZEZsdXR0ZXJGYXRhbEVycm9yKGVycm9yRGV0YWlscyk7CiAgICAgICAgfTsKCiAgICAgICAgLy8gQ2F0Y2ggZXJyb3JzIGZyb20gdGhlIEZsdXR0ZXIgZnJhbWV3b3JrIHRoYXQgYXJlIG5vdCBoYW5kbGVkIGJ5IEZsdXR0ZXJFcnJvcgogICAgICAgIFBsYXRmb3JtRGlzcGF0Y2hlci5pbnN0YW5jZS5vbkVycm9yID0gKGVycm9yLCBzdGFjaykgewogICAgICAgICAgX2NyYXNobHl0aWNzLnJlY29yZEVycm9yKGVycm9yLCBzdGFjaywgZmF0YWw6IHRydWUpOwogICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgfTsKICAgICAgfQoKICAgICAgLy8gU2V0IGRlZmF1bHQgY3VzdG9tIGtleXMKICAgICAgaWYgKGNvbmZpZy5jdXN0b21LZXlzLmlzTm90RW1wdHkpIHsKICAgICAgICBhd2FpdCBzZXRDdXN0b21LZXlzKGNvbmZpZy5jdXN0b21LZXlzKTsKICAgICAgfQoKICAgICAgX2lzSW5pdGlhbGl6ZWQgPSB0cnVlOwogICAgICBfbG9nZ2VyLmkoJ/CflKUgRmlyZWJhc2UgQ3Jhc2hseXRpY3MgaW5pdGlhbGl6ZWQgc3VjY2Vzc2Z1bGx5Jyk7CiAgICB9IGNhdGNoIChlLCBzdGFja1RyYWNlKSB7CiAgICAgIF9sb2dnZXIuZSgn4p2MIEZhaWxlZCB0byBpbml0aWFsaXplIEZpcmViYXNlIENyYXNobHl0aWNzOiAkZScsIHN0YWNrVHJhY2UpOwogICAgICByZXRocm93OwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IHJlY29yZEVycm9yKAogICAgZHluYW1pYyBleGNlcHRpb24sCiAgICBTdGFja1RyYWNlPyBzdGFja1RyYWNlLCB7CiAgICBib29sIGZhdGFsID0gZmFsc2UsCiAgICBJdGVyYWJsZTxPYmplY3Q+IGluZm9ybWF0aW9uID0gY29uc3QgW10sCiAgfSkgYXN5bmMgewogICAgaWYgKCFfaXNJbml0aWFsaXplZCkgewogICAgICBfbG9nZ2VyLncoJ+KaoO+4jyBDcmFzaGx5dGljcyBub3QgaW5pdGlhbGl6ZWQsIHNraXBwaW5nIGVycm9yIHJlY29yZGluZycpOwogICAgICByZXR1cm47CiAgICB9CgogICAgdHJ5IHsKICAgICAgYXdhaXQgX2NyYXNobHl0aWNzLnJlY29yZEVycm9yKAogICAgICAgIGV4Y2VwdGlvbiwKICAgICAgICBzdGFja1RyYWNlLAogICAgICAgIGZhdGFsOiBmYXRhbCwKICAgICAgICBpbmZvcm1hdGlvbjogaW5mb3JtYXRpb24sCiAgICAgICk7CgogICAgICBfbG9nZ2VyLmQoCiAgICAgICAgJ/Cfk50gUmVjb3JkZWQgJHtmYXRhbCA/ICdmYXRhbCcgOiAnbm9uLWZhdGFsJ30gZXJyb3I6ICRleGNlcHRpb24nLAogICAgICApOwogICAgfSBjYXRjaCAoZSkgewogICAgICBfbG9nZ2VyLmUoJ+KdjCBGYWlsZWQgdG8gcmVjb3JkIGVycm9yOiAkZScpOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IHJlY29yZEZsdXR0ZXJGYXRhbEVycm9yKAogICAgZHluYW1pYyBleGNlcHRpb24sCiAgICBTdGFja1RyYWNlIHN0YWNrVHJhY2UsIHsKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBjb250ZXh0LAogIH0pIGFzeW5jIHsKICAgIGlmICghX2lzSW5pdGlhbGl6ZWQpIHsKICAgICAgX2xvZ2dlci53KAogICAgICAgICfimqDvuI8gQ3Jhc2hseXRpY3Mgbm90IGluaXRpYWxpemVkLCBza2lwcGluZyBGbHV0dGVyIGZhdGFsIGVycm9yIHJlY29yZGluZycsCiAgICAgICk7CiAgICAgIHJldHVybjsKICAgIH0KCiAgICB0cnkgewogICAgICAvLyBTZXQgY29udGV4dCBhcyBjdXN0b20ga2V5cyBpZiBwcm92aWRlZAogICAgICBpZiAoY29udGV4dCAhPSBudWxsICYmIGNvbnRleHQuaXNOb3RFbXB0eSkgewogICAgICAgIGF3YWl0IHNldEN1c3RvbUtleXMoY29udGV4dCk7CiAgICAgIH0KCiAgICAgIGF3YWl0IF9jcmFzaGx5dGljcy5yZWNvcmRFcnJvcihleGNlcHRpb24sIHN0YWNrVHJhY2UsIGZhdGFsOiB0cnVlKTsKCiAgICAgIF9sb2dnZXIuZCgn8J+TnSBSZWNvcmRlZCBGbHV0dGVyIGZhdGFsIGVycm9yOiAkZXhjZXB0aW9uJyk7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIF9sb2dnZXIuZSgn4p2MIEZhaWxlZCB0byByZWNvcmQgRmx1dHRlciBmYXRhbCBlcnJvcjogJGUnKTsKICAgIH0KICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTx2b2lkPiBsb2coU3RyaW5nIG1lc3NhZ2UpIGFzeW5jIHsKICAgIGlmICghX2lzSW5pdGlhbGl6ZWQgfHwgIWNvbmZpZy5lbmFibGVDdXN0b21Mb2dzKSB7CiAgICAgIHJldHVybjsKICAgIH0KCiAgICB0cnkgewogICAgICBhd2FpdCBfY3Jhc2hseXRpY3MubG9nKG1lc3NhZ2UpOwoKICAgICAgLy8gQWRkIHRvIGxvY2FsIGJ1ZmZlciBmb3IgZGVidWdnaW5nCiAgICAgIF9sb2dCdWZmZXIuYWRkKCdbJHtEYXRlVGltZS5ub3coKS50b0lzbzg2MDFTdHJpbmcoKX1dICRtZXNzYWdlJyk7CgogICAgICAvLyBNYWludGFpbiBidWZmZXIgc2l6ZQogICAgICB3aGlsZSAoX2xvZ0J1ZmZlci5sZW5ndGggPiBjb25maWcubG9nQnVmZmVyU2l6ZSkgewogICAgICAgIF9sb2dCdWZmZXIucmVtb3ZlRmlyc3QoKTsKICAgICAgfQoKICAgICAgX2xvZ2dlci5kKCfwn5OKIExvZ2dlZCBtZXNzYWdlOiAkbWVzc2FnZScpOwogICAgfSBjYXRjaCAoZSkgewogICAgICBfbG9nZ2VyLmUoJ+KdjCBGYWlsZWQgdG8gbG9nIG1lc3NhZ2U6ICRlJyk7CiAgICB9CiAgfQoKICBAb3ZlcnJpZGUKICBGdXR1cmU8dm9pZD4gc2V0VXNlcklkZW50aWZpZXIoU3RyaW5nIGlkZW50aWZpZXIpIGFzeW5jIHsKICAgIGlmICghX2lzSW5pdGlhbGl6ZWQgfHwgIWNvbmZpZy5lbmFibGVVc2VyTWV0YWRhdGEpIHsKICAgICAgcmV0dXJuOwogICAgfQoKICAgIHRyeSB7CiAgICAgIGF3YWl0IF9jcmFzaGx5dGljcy5zZXRVc2VySWRlbnRpZmllcihpZGVudGlmaWVyKTsKICAgICAgX2xvZ2dlci5kKCfwn5GkIFNldCB1c2VyIGlkZW50aWZpZXI6ICRpZGVudGlmaWVyJyk7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIF9sb2dnZXIuZSgn4p2MIEZhaWxlZCB0byBzZXQgdXNlciBpZGVudGlmaWVyOiAkZScpOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IHNldFVzZXJNZXRhZGF0YShVc2VyTWV0YWRhdGEgbWV0YWRhdGEpIGFzeW5jIHsKICAgIGlmICghX2lzSW5pdGlhbGl6ZWQgfHwgIWNvbmZpZy5lbmFibGVVc2VyTWV0YWRhdGEpIHsKICAgICAgcmV0dXJuOwogICAgfQoKICAgIHRyeSB7CiAgICAgIGlmIChtZXRhZGF0YS51c2VySWQgIT0gbnVsbCkgewogICAgICAgIGF3YWl0IHNldFVzZXJJZGVudGlmaWVyKG1ldGFkYXRhLnVzZXJJZCEpOwogICAgICB9CgogICAgICAvLyBTZXQgY3VzdG9tIGF0dHJpYnV0ZXMKICAgICAgZmluYWwgYWxsQXR0cmlidXRlcyA9IDxTdHJpbmcsIGR5bmFtaWM+ewogICAgICAgIGlmIChtZXRhZGF0YS5lbWFpbCAhPSBudWxsKSAnZW1haWwnOiBtZXRhZGF0YS5lbWFpbCEsCiAgICAgICAgaWYgKG1ldGFkYXRhLm5hbWUgIT0gbnVsbCkgJ25hbWUnOiBtZXRhZGF0YS5uYW1lISwKICAgICAgICAuLi5tZXRhZGF0YS5jdXN0b21BdHRyaWJ1dGVzLAogICAgICB9OwoKICAgICAgYXdhaXQgc2V0Q3VzdG9tS2V5cyhhbGxBdHRyaWJ1dGVzKTsKICAgICAgX2xvZ2dlci5kKCfwn5GkIFNldCB1c2VyIG1ldGFkYXRhOiAkbWV0YWRhdGEnKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgX2xvZ2dlci5lKCfinYwgRmFpbGVkIHRvIHNldCB1c2VyIG1ldGFkYXRhOiAkZScpOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IHNldEN1c3RvbUtleShTdHJpbmcga2V5LCBkeW5hbWljIHZhbHVlKSBhc3luYyB7CiAgICBpZiAoIV9pc0luaXRpYWxpemVkKSB7CiAgICAgIHJldHVybjsKICAgIH0KCiAgICB0cnkgewogICAgICBhd2FpdCBfY3Jhc2hseXRpY3Muc2V0Q3VzdG9tS2V5KGtleSwgdmFsdWUpOwogICAgICBfbG9nZ2VyLmQoJ/CflJEgU2V0IGN1c3RvbSBrZXk6ICRrZXkgPSAkdmFsdWUnKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgX2xvZ2dlci5lKCfinYwgRmFpbGVkIHRvIHNldCBjdXN0b20ga2V5OiAkZScpOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IHNldEN1c3RvbUtleXMoTWFwPFN0cmluZywgZHluYW1pYz4ga2V5cykgYXN5bmMgewogICAgaWYgKCFfaXNJbml0aWFsaXplZCB8fCBrZXlzLmlzRW1wdHkpIHsKICAgICAgcmV0dXJuOwogICAgfQoKICAgIHRyeSB7CiAgICAgIGZvciAoZmluYWwgZW50cnkgaW4ga2V5cy5lbnRyaWVzKSB7CiAgICAgICAgYXdhaXQgX2NyYXNobHl0aWNzLnNldEN1c3RvbUtleShlbnRyeS5rZXksIGVudHJ5LnZhbHVlKTsKICAgICAgfQogICAgICBfbG9nZ2VyLmQoJ/CflJEgU2V0ICR7a2V5cy5sZW5ndGh9IGN1c3RvbSBrZXlzJyk7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIF9sb2dnZXIuZSgn4p2MIEZhaWxlZCB0byBzZXQgY3VzdG9tIGtleXM6ICRlJyk7CiAgICB9CiAgfQoKICBAb3ZlcnJpZGUKICBib29sIGdldCBpc0NyYXNobHl0aWNzQ29sbGVjdGlvbkVuYWJsZWQgewogICAgaWYgKCFfaXNJbml0aWFsaXplZCkgcmV0dXJuIGZhbHNlOwogICAgcmV0dXJuIF9jcmFzaGx5dGljcy5pc0NyYXNobHl0aWNzQ29sbGVjdGlvbkVuYWJsZWQ7CiAgfQoKICBAb3ZlcnJpZGUKICBGdXR1cmU8dm9pZD4gc2V0Q3Jhc2hseXRpY3NDb2xsZWN0aW9uRW5hYmxlZChib29sIGVuYWJsZWQpIGFzeW5jIHsKICAgIGlmICghX2lzSW5pdGlhbGl6ZWQpIHsKICAgICAgcmV0dXJuOwogICAgfQoKICAgIHRyeSB7CiAgICAgIGF3YWl0IF9jcmFzaGx5dGljcy5zZXRDcmFzaGx5dGljc0NvbGxlY3Rpb25FbmFibGVkKGVuYWJsZWQpOwogICAgICBfbG9nZ2VyLmQoCiAgICAgICAgJ+Kame+4jyBDcmFzaGx5dGljcyBjb2xsZWN0aW9uICR7ZW5hYmxlZCA/ICdlbmFibGVkJyA6ICdkaXNhYmxlZCd9JywKICAgICAgKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgX2xvZ2dlci5lKCfinYwgRmFpbGVkIHRvIHNldCBjcmFzaGx5dGljcyBjb2xsZWN0aW9uIGVuYWJsZWQ6ICRlJyk7CiAgICB9CiAgfQoKICBAb3ZlcnJpZGUKICBGdXR1cmU8dm9pZD4gc2VuZFVuc2VudFJlcG9ydHMoKSBhc3luYyB7CiAgICBpZiAoIV9pc0luaXRpYWxpemVkKSB7CiAgICAgIHJldHVybjsKICAgIH0KCiAgICB0cnkgewogICAgICBhd2FpdCBfY3Jhc2hseXRpY3Muc2VuZFVuc2VudFJlcG9ydHMoKTsKICAgICAgX2xvZ2dlci5kKCfwn5OkIFNlbnQgdW5zZW50IGNyYXNoIHJlcG9ydHMnKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgX2xvZ2dlci5lKCfinYwgRmFpbGVkIHRvIHNlbmQgdW5zZW50IHJlcG9ydHM6ICRlJyk7CiAgICB9CiAgfQoKICBAb3ZlcnJpZGUKICBGdXR1cmU8dm9pZD4gZGVsZXRlVW5zZW50UmVwb3J0cygpIGFzeW5jIHsKICAgIGlmICghX2lzSW5pdGlhbGl6ZWQpIHsKICAgICAgcmV0dXJuOwogICAgfQoKICAgIHRyeSB7CiAgICAgIGF3YWl0IF9jcmFzaGx5dGljcy5kZWxldGVVbnNlbnRSZXBvcnRzKCk7CiAgICAgIF9sb2dnZXIuZCgn8J+Xke+4jyBEZWxldGVkIHVuc2VudCBjcmFzaCByZXBvcnRzJyk7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIF9sb2dnZXIuZSgn4p2MIEZhaWxlZCB0byBkZWxldGUgdW5zZW50IHJlcG9ydHM6ICRlJyk7CiAgICB9CiAgfQoKICBAb3ZlcnJpZGUKICBGdXR1cmU8Ym9vbD4gY2hlY2tGb3JVbnNlbnRSZXBvcnRzKCkgYXN5bmMgewogICAgaWYgKCFfaXNJbml0aWFsaXplZCkgewogICAgICByZXR1cm4gZmFsc2U7CiAgICB9CgogICAgdHJ5IHsKICAgICAgZmluYWwgaGFzVW5zZW50ID0gYXdhaXQgX2NyYXNobHl0aWNzLmNoZWNrRm9yVW5zZW50UmVwb3J0cygpOwogICAgICBfbG9nZ2VyLmQoJ/Cfk4sgSGFzIHVuc2VudCByZXBvcnRzOiAkaGFzVW5zZW50Jyk7CiAgICAgIHJldHVybiBoYXNVbnNlbnQ7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIF9sb2dnZXIuZSgn4p2MIEZhaWxlZCB0byBjaGVjayBmb3IgdW5zZW50IHJlcG9ydHM6ICRlJyk7CiAgICAgIHJldHVybiBmYWxzZTsKICAgIH0KICB9CgogIC8vLyBHZXQgYnVmZmVyZWQgbG9nIG1lc3NhZ2VzIGZvciBkZWJ1Z2dpbmcKICBMaXN0PFN0cmluZz4gZ2V0IGJ1ZmZlcmVkTG9ncyA9PiBfbG9nQnVmZmVyLnRvTGlzdCgpOwoKICAvLy8gQ2xlYXIgdGhlIGxvZyBidWZmZXIKICB2b2lkIGNsZWFyTG9nQnVmZmVyKCkgewogICAgX2xvZ0J1ZmZlci5jbGVhcigpOwogICAgX2xvZ2dlci5kKCfwn6e5IENsZWFyZWQgbG9nIGJ1ZmZlcicpOwogIH0KCiAgQG92ZXJyaWRlCiAgdm9pZCBkaXNwb3NlKCkgewogICAgX2xvZ0J1ZmZlci5jbGVhcigpOwogICAgX2lzSW5pdGlhbGl6ZWQgPSBmYWxzZTsKICAgIF9sb2dnZXIuZCgn8J+nuSBGaXJlYmFzZSBDcmFzaGx5dGljcyBjbGllbnQgZGlzcG9zZWQnKTsKICB9Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/client/index.dart", + "data": + "ZXhwb3J0ICdjcmFzaGx5dGljc19jbGllbnQuZGFydCc7CmV4cG9ydCAnZmlyZWJhc2VfY3Jhc2hseXRpY3NfY2xpZW50LmRhcnQnOwo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/config/crashlytics_config.dart", + "data": + "Y2xhc3MgQ3Jhc2hseXRpY3NDb25maWcgewogIGNvbnN0IENyYXNobHl0aWNzQ29uZmlnKHsKICAgIHRoaXMuZW5hYmxlSW5EZWJ1Z01vZGUgPSBmYWxzZSwKICAgIHRoaXMuZW5hYmxlQXV0b21hdGljRGF0YUNvbGxlY3Rpb24gPSB0cnVlLAogICAgdGhpcy5lbmFibGVDdXN0b21Mb2dzID0gdHJ1ZSwKICAgIHRoaXMubG9nQnVmZmVyU2l6ZSA9IDEwMCwKICAgIHRoaXMuZW5hYmxlVXNlck1ldGFkYXRhID0gdHJ1ZSwKICAgIHRoaXMuY3VzdG9tS2V5cyA9IGNvbnN0IHt9LAogIH0pOwoKICAvLy8gRW5hYmxlIGNyYXNobHl0aWNzIGluIGRlYnVnIG1vZGUgKHR5cGljYWxseSBkaXNhYmxlZCBmb3IgZGV2ZWxvcG1lbnQpCiAgZmluYWwgYm9vbCBlbmFibGVJbkRlYnVnTW9kZTsKCiAgLy8vIEVuYWJsZSBhdXRvbWF0aWMgZGF0YSBjb2xsZWN0aW9uCiAgZmluYWwgYm9vbCBlbmFibGVBdXRvbWF0aWNEYXRhQ29sbGVjdGlvbjsKCiAgLy8vIEVuYWJsZSBjdXN0b20gbG9nZ2luZwogIGZpbmFsIGJvb2wgZW5hYmxlQ3VzdG9tTG9nczsKCiAgLy8vIE1heGltdW0gbnVtYmVyIG9mIGxvZyBtZXNzYWdlcyB0byBidWZmZXIKICBmaW5hbCBpbnQgbG9nQnVmZmVyU2l6ZTsKCiAgLy8vIEVuYWJsZSB1c2VyIG1ldGFkYXRhIGNvbGxlY3Rpb24KICBmaW5hbCBib29sIGVuYWJsZVVzZXJNZXRhZGF0YTsKCiAgLy8vIERlZmF1bHQgY3VzdG9tIGtleXMgdG8gc2V0IG9uIGluaXRpYWxpemF0aW9uCiAgZmluYWwgTWFwPFN0cmluZywgZHluYW1pYz4gY3VzdG9tS2V5czsKCiAgQ3Jhc2hseXRpY3NDb25maWcgY29weVdpdGgoewogICAgYm9vbD8gZW5hYmxlSW5EZWJ1Z01vZGUsCiAgICBib29sPyBlbmFibGVBdXRvbWF0aWNEYXRhQ29sbGVjdGlvbiwKICAgIGJvb2w/IGVuYWJsZUN1c3RvbUxvZ3MsCiAgICBpbnQ/IGxvZ0J1ZmZlclNpemUsCiAgICBib29sPyBlbmFibGVVc2VyTWV0YWRhdGEsCiAgICBNYXA8U3RyaW5nLCBkeW5hbWljPj8gY3VzdG9tS2V5cywKICB9KSB7CiAgICByZXR1cm4gQ3Jhc2hseXRpY3NDb25maWcoCiAgICAgIGVuYWJsZUluRGVidWdNb2RlOiBlbmFibGVJbkRlYnVnTW9kZSA/PyB0aGlzLmVuYWJsZUluRGVidWdNb2RlLAogICAgICBlbmFibGVBdXRvbWF0aWNEYXRhQ29sbGVjdGlvbjoKICAgICAgICAgIGVuYWJsZUF1dG9tYXRpY0RhdGFDb2xsZWN0aW9uID8/IHRoaXMuZW5hYmxlQXV0b21hdGljRGF0YUNvbGxlY3Rpb24sCiAgICAgIGVuYWJsZUN1c3RvbUxvZ3M6IGVuYWJsZUN1c3RvbUxvZ3MgPz8gdGhpcy5lbmFibGVDdXN0b21Mb2dzLAogICAgICBsb2dCdWZmZXJTaXplOiBsb2dCdWZmZXJTaXplID8/IHRoaXMubG9nQnVmZmVyU2l6ZSwKICAgICAgZW5hYmxlVXNlck1ldGFkYXRhOiBlbmFibGVVc2VyTWV0YWRhdGEgPz8gdGhpcy5lbmFibGVVc2VyTWV0YWRhdGEsCiAgICAgIGN1c3RvbUtleXM6IGN1c3RvbUtleXMgPz8gdGhpcy5jdXN0b21LZXlzLAogICAgKTsKICB9CgogIE1hcDxTdHJpbmcsIGR5bmFtaWM+IHRvTWFwKCkgewogICAgcmV0dXJuIHsKICAgICAgJ2VuYWJsZUluRGVidWdNb2RlJzogZW5hYmxlSW5EZWJ1Z01vZGUsCiAgICAgICdlbmFibGVBdXRvbWF0aWNEYXRhQ29sbGVjdGlvbic6IGVuYWJsZUF1dG9tYXRpY0RhdGFDb2xsZWN0aW9uLAogICAgICAnZW5hYmxlQ3VzdG9tTG9ncyc6IGVuYWJsZUN1c3RvbUxvZ3MsCiAgICAgICdsb2dCdWZmZXJTaXplJzogbG9nQnVmZmVyU2l6ZSwKICAgICAgJ2VuYWJsZVVzZXJNZXRhZGF0YSc6IGVuYWJsZVVzZXJNZXRhZGF0YSwKICAgICAgJ2N1c3RvbUtleXMnOiBjdXN0b21LZXlzLAogICAgfTsKICB9CgogIEBvdmVycmlkZQogIFN0cmluZyB0b1N0cmluZygpIHsKICAgIHJldHVybiAnQ3Jhc2hseXRpY3NDb25maWd7ZW5hYmxlSW5EZWJ1Z01vZGU6ICRlbmFibGVJbkRlYnVnTW9kZSwgZW5hYmxlQXV0b21hdGljRGF0YUNvbGxlY3Rpb246ICRlbmFibGVBdXRvbWF0aWNEYXRhQ29sbGVjdGlvbiwgZW5hYmxlQ3VzdG9tTG9nczogJGVuYWJsZUN1c3RvbUxvZ3MsIGxvZ0J1ZmZlclNpemU6ICRsb2dCdWZmZXJTaXplLCBlbmFibGVVc2VyTWV0YWRhdGE6ICRlbmFibGVVc2VyTWV0YWRhdGEsIGN1c3RvbUtleXM6ICRjdXN0b21LZXlzfSc7CiAgfQoKICBAb3ZlcnJpZGUKICBib29sIG9wZXJhdG9yID09KE9iamVjdCBvdGhlcikgPT4KICAgICAgaWRlbnRpY2FsKHRoaXMsIG90aGVyKSB8fAogICAgICBvdGhlciBpcyBDcmFzaGx5dGljc0NvbmZpZyAmJgogICAgICAgICAgcnVudGltZVR5cGUgPT0gb3RoZXIucnVudGltZVR5cGUgJiYKICAgICAgICAgIGVuYWJsZUluRGVidWdNb2RlID09IG90aGVyLmVuYWJsZUluRGVidWdNb2RlICYmCiAgICAgICAgICBlbmFibGVBdXRvbWF0aWNEYXRhQ29sbGVjdGlvbiA9PQogICAgICAgICAgICAgIG90aGVyLmVuYWJsZUF1dG9tYXRpY0RhdGFDb2xsZWN0aW9uICYmCiAgICAgICAgICBlbmFibGVDdXN0b21Mb2dzID09IG90aGVyLmVuYWJsZUN1c3RvbUxvZ3MgJiYKICAgICAgICAgIGxvZ0J1ZmZlclNpemUgPT0gb3RoZXIubG9nQnVmZmVyU2l6ZSAmJgogICAgICAgICAgZW5hYmxlVXNlck1ldGFkYXRhID09IG90aGVyLmVuYWJsZVVzZXJNZXRhZGF0YSAmJgogICAgICAgICAgX21hcEVxdWFscyhjdXN0b21LZXlzLCBvdGhlci5jdXN0b21LZXlzKTsKCiAgQG92ZXJyaWRlCiAgaW50IGdldCBoYXNoQ29kZSA9PgogICAgICBlbmFibGVJbkRlYnVnTW9kZS5oYXNoQ29kZSBeCiAgICAgIGVuYWJsZUF1dG9tYXRpY0RhdGFDb2xsZWN0aW9uLmhhc2hDb2RlIF4KICAgICAgZW5hYmxlQ3VzdG9tTG9ncy5oYXNoQ29kZSBeCiAgICAgIGxvZ0J1ZmZlclNpemUuaGFzaENvZGUgXgogICAgICBlbmFibGVVc2VyTWV0YWRhdGEuaGFzaENvZGUgXgogICAgICBjdXN0b21LZXlzLmhhc2hDb2RlOwoKICBib29sIF9tYXBFcXVhbHMoTWFwPFN0cmluZywgZHluYW1pYz4gYSwgTWFwPFN0cmluZywgZHluYW1pYz4gYikgewogICAgaWYgKGEubGVuZ3RoICE9IGIubGVuZ3RoKSByZXR1cm4gZmFsc2U7CiAgICBmb3IgKGZpbmFsIGtleSBpbiBhLmtleXMpIHsKICAgICAgaWYgKCFiLmNvbnRhaW5zS2V5KGtleSkgfHwgYVtrZXldICE9IGJba2V5XSkgcmV0dXJuIGZhbHNlOwogICAgfQogICAgcmV0dXJuIHRydWU7CiAgfQp9CgpjbGFzcyBEZWZhdWx0Q3Jhc2hseXRpY3NDb25maWcgZXh0ZW5kcyBDcmFzaGx5dGljc0NvbmZpZyB7CiAgY29uc3QgRGVmYXVsdENyYXNobHl0aWNzQ29uZmlnKHsKICAgIHN1cGVyLmVuYWJsZUluRGVidWdNb2RlID0gZmFsc2UsCiAgICBzdXBlci5lbmFibGVBdXRvbWF0aWNEYXRhQ29sbGVjdGlvbiA9IHRydWUsCiAgICBzdXBlci5lbmFibGVDdXN0b21Mb2dzID0gdHJ1ZSwKICAgIHN1cGVyLmxvZ0J1ZmZlclNpemUgPSAxMDAsCiAgICBzdXBlci5lbmFibGVVc2VyTWV0YWRhdGEgPSB0cnVlLAogICAgc3VwZXIuY3VzdG9tS2V5cyA9IGNvbnN0IHt9LAogIH0pOwp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/config/index.dart", + "data": "ZXhwb3J0ICdjcmFzaGx5dGljc19jb25maWcuZGFydCc7Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/models/crash_report.dart", + "data": + "Y2xhc3MgQ3Jhc2hSZXBvcnQgewogIGNvbnN0IENyYXNoUmVwb3J0KHsKICAgIHJlcXVpcmVkIHRoaXMuZXhjZXB0aW9uLAogICAgdGhpcy5zdGFja1RyYWNlLAogICAgdGhpcy5mYXRhbCA9IGZhbHNlLAogICAgdGhpcy50aW1lc3RhbXAsCiAgICB0aGlzLmN1c3RvbUtleXMgPSBjb25zdCB7fSwKICAgIHRoaXMubG9ncyA9IGNvbnN0IFtdLAogICAgdGhpcy51c2VyTWV0YWRhdGEsCiAgfSk7CgogIGZpbmFsIGR5bmFtaWMgZXhjZXB0aW9uOwogIGZpbmFsIFN0YWNrVHJhY2U/IHN0YWNrVHJhY2U7CiAgZmluYWwgYm9vbCBmYXRhbDsKICBmaW5hbCBEYXRlVGltZT8gdGltZXN0YW1wOwogIGZpbmFsIE1hcDxTdHJpbmcsIGR5bmFtaWM+IGN1c3RvbUtleXM7CiAgZmluYWwgTGlzdDxTdHJpbmc+IGxvZ3M7CiAgZmluYWwgTWFwPFN0cmluZywgZHluYW1pYz4/IHVzZXJNZXRhZGF0YTsKCiAgQ3Jhc2hSZXBvcnQgY29weVdpdGgoewogICAgZHluYW1pYyBleGNlcHRpb24sCiAgICBTdGFja1RyYWNlPyBzdGFja1RyYWNlLAogICAgYm9vbD8gZmF0YWwsCiAgICBEYXRlVGltZT8gdGltZXN0YW1wLAogICAgTWFwPFN0cmluZywgZHluYW1pYz4/IGN1c3RvbUtleXMsCiAgICBMaXN0PFN0cmluZz4/IGxvZ3MsCiAgICBNYXA8U3RyaW5nLCBkeW5hbWljPj8gdXNlck1ldGFkYXRhLAogIH0pIHsKICAgIHJldHVybiBDcmFzaFJlcG9ydCgKICAgICAgZXhjZXB0aW9uOiBleGNlcHRpb24gPz8gdGhpcy5leGNlcHRpb24sCiAgICAgIHN0YWNrVHJhY2U6IHN0YWNrVHJhY2UgPz8gdGhpcy5zdGFja1RyYWNlLAogICAgICBmYXRhbDogZmF0YWwgPz8gdGhpcy5mYXRhbCwKICAgICAgdGltZXN0YW1wOiB0aW1lc3RhbXAgPz8gdGhpcy50aW1lc3RhbXAsCiAgICAgIGN1c3RvbUtleXM6IGN1c3RvbUtleXMgPz8gdGhpcy5jdXN0b21LZXlzLAogICAgICBsb2dzOiBsb2dzID8/IHRoaXMubG9ncywKICAgICAgdXNlck1ldGFkYXRhOiB1c2VyTWV0YWRhdGEgPz8gdGhpcy51c2VyTWV0YWRhdGEsCiAgICApOwogIH0KCiAgTWFwPFN0cmluZywgZHluYW1pYz4gdG9NYXAoKSB7CiAgICByZXR1cm4gewogICAgICAnZXhjZXB0aW9uJzogZXhjZXB0aW9uLnRvU3RyaW5nKCksCiAgICAgICdzdGFja1RyYWNlJzogc3RhY2tUcmFjZT8udG9TdHJpbmcoKSwKICAgICAgJ2ZhdGFsJzogZmF0YWwsCiAgICAgICd0aW1lc3RhbXAnOiB0aW1lc3RhbXA/LnRvSXNvODYwMVN0cmluZygpLAogICAgICAnY3VzdG9tS2V5cyc6IGN1c3RvbUtleXMsCiAgICAgICdsb2dzJzogbG9ncywKICAgICAgJ3VzZXJNZXRhZGF0YSc6IHVzZXJNZXRhZGF0YSwKICAgIH07CiAgfQoKICBAb3ZlcnJpZGUKICBTdHJpbmcgdG9TdHJpbmcoKSB7CiAgICByZXR1cm4gJ0NyYXNoUmVwb3J0e2V4Y2VwdGlvbjogJGV4Y2VwdGlvbiwgZmF0YWw6ICRmYXRhbCwgdGltZXN0YW1wOiAkdGltZXN0YW1wfSc7CiAgfQoKICBAb3ZlcnJpZGUKICBib29sIG9wZXJhdG9yID09KE9iamVjdCBvdGhlcikgPT4KICAgICAgaWRlbnRpY2FsKHRoaXMsIG90aGVyKSB8fAogICAgICBvdGhlciBpcyBDcmFzaFJlcG9ydCAmJgogICAgICAgICAgcnVudGltZVR5cGUgPT0gb3RoZXIucnVudGltZVR5cGUgJiYKICAgICAgICAgIGV4Y2VwdGlvbi50b1N0cmluZygpID09IG90aGVyLmV4Y2VwdGlvbi50b1N0cmluZygpICYmCiAgICAgICAgICBzdGFja1RyYWNlLnRvU3RyaW5nKCkgPT0gb3RoZXIuc3RhY2tUcmFjZS50b1N0cmluZygpICYmCiAgICAgICAgICBmYXRhbCA9PSBvdGhlci5mYXRhbCAmJgogICAgICAgICAgdGltZXN0YW1wID09IG90aGVyLnRpbWVzdGFtcDsKCiAgQG92ZXJyaWRlCiAgaW50IGdldCBoYXNoQ29kZSA9PgogICAgICBleGNlcHRpb24uaGFzaENvZGUgXgogICAgICBzdGFja1RyYWNlLmhhc2hDb2RlIF4KICAgICAgZmF0YWwuaGFzaENvZGUgXgogICAgICB0aW1lc3RhbXAuaGFzaENvZGU7Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/models/index.dart", + "data": + "ZXhwb3J0ICdjcmFzaF9yZXBvcnQuZGFydCc7CmV4cG9ydCAndXNlcl9tZXRhZGF0YS5kYXJ0JzsK", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/lib/src/models/user_metadata.dart", + "data": + "Y2xhc3MgVXNlck1ldGFkYXRhIHsKICBjb25zdCBVc2VyTWV0YWRhdGEoewogICAgdGhpcy51c2VySWQsCiAgICB0aGlzLmVtYWlsLAogICAgdGhpcy5uYW1lLAogICAgdGhpcy5jdXN0b21BdHRyaWJ1dGVzID0gY29uc3Qge30sCiAgfSk7CgogIGZpbmFsIFN0cmluZz8gdXNlcklkOwogIGZpbmFsIFN0cmluZz8gZW1haWw7CiAgZmluYWwgU3RyaW5nPyBuYW1lOwogIGZpbmFsIE1hcDxTdHJpbmcsIGR5bmFtaWM+IGN1c3RvbUF0dHJpYnV0ZXM7CgogIFVzZXJNZXRhZGF0YSBjb3B5V2l0aCh7CiAgICBTdHJpbmc/IHVzZXJJZCwKICAgIFN0cmluZz8gZW1haWwsCiAgICBTdHJpbmc/IG5hbWUsCiAgICBNYXA8U3RyaW5nLCBkeW5hbWljPj8gY3VzdG9tQXR0cmlidXRlcywKICB9KSB7CiAgICByZXR1cm4gVXNlck1ldGFkYXRhKAogICAgICB1c2VySWQ6IHVzZXJJZCA/PyB0aGlzLnVzZXJJZCwKICAgICAgZW1haWw6IGVtYWlsID8/IHRoaXMuZW1haWwsCiAgICAgIG5hbWU6IG5hbWUgPz8gdGhpcy5uYW1lLAogICAgICBjdXN0b21BdHRyaWJ1dGVzOiBjdXN0b21BdHRyaWJ1dGVzID8/IHRoaXMuY3VzdG9tQXR0cmlidXRlcywKICAgICk7CiAgfQoKICBNYXA8U3RyaW5nLCBkeW5hbWljPiB0b01hcCgpIHsKICAgIHJldHVybiB7CiAgICAgICd1c2VySWQnOiB1c2VySWQsCiAgICAgICdlbWFpbCc6IGVtYWlsLAogICAgICAnbmFtZSc6IG5hbWUsCiAgICAgICdjdXN0b21BdHRyaWJ1dGVzJzogY3VzdG9tQXR0cmlidXRlcywKICAgIH07CiAgfQoKICBAb3ZlcnJpZGUKICBTdHJpbmcgdG9TdHJpbmcoKSB7CiAgICByZXR1cm4gJ1VzZXJNZXRhZGF0YXt1c2VySWQ6ICR1c2VySWQsIGVtYWlsOiAkZW1haWwsIG5hbWU6ICRuYW1lLCBjdXN0b21BdHRyaWJ1dGVzOiAkY3VzdG9tQXR0cmlidXRlc30nOwogIH0KCiAgQG92ZXJyaWRlCiAgYm9vbCBvcGVyYXRvciA9PShPYmplY3Qgb3RoZXIpID0+CiAgICAgIGlkZW50aWNhbCh0aGlzLCBvdGhlcikgfHwKICAgICAgb3RoZXIgaXMgVXNlck1ldGFkYXRhICYmCiAgICAgICAgICBydW50aW1lVHlwZSA9PSBvdGhlci5ydW50aW1lVHlwZSAmJgogICAgICAgICAgdXNlcklkID09IG90aGVyLnVzZXJJZCAmJgogICAgICAgICAgZW1haWwgPT0gb3RoZXIuZW1haWwgJiYKICAgICAgICAgIG5hbWUgPT0gb3RoZXIubmFtZSAmJgogICAgICAgICAgX21hcEVxdWFscyhjdXN0b21BdHRyaWJ1dGVzLCBvdGhlci5jdXN0b21BdHRyaWJ1dGVzKTsKCiAgQG92ZXJyaWRlCiAgaW50IGdldCBoYXNoQ29kZSA9PgogICAgICB1c2VySWQuaGFzaENvZGUgXgogICAgICBlbWFpbC5oYXNoQ29kZSBeCiAgICAgIG5hbWUuaGFzaENvZGUgXgogICAgICBjdXN0b21BdHRyaWJ1dGVzLmhhc2hDb2RlOwoKICBib29sIF9tYXBFcXVhbHMoTWFwPFN0cmluZywgZHluYW1pYz4gYSwgTWFwPFN0cmluZywgZHluYW1pYz4gYikgewogICAgaWYgKGEubGVuZ3RoICE9IGIubGVuZ3RoKSByZXR1cm4gZmFsc2U7CiAgICBmb3IgKGZpbmFsIGtleSBpbiBhLmtleXMpIHsKICAgICAgaWYgKCFiLmNvbnRhaW5zS2V5KGtleSkgfHwgYVtrZXldICE9IGJba2V5XSkgcmV0dXJuIGZhbHNlOwogICAgfQogICAgcmV0dXJuIHRydWU7CiAgfQp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/pubspec.yaml", + "data": + "bmFtZTogY3Jhc2hseXRpY3MKZGVzY3JpcHRpb246ICJDcmFzaCBhbmQgbm9uLWZhdGFsIGVycm9yIHJlcG9ydGluZyBmb3Ige3tuYW1lLnRpdGxlQ2FzZSgpfX0uIgp2ZXJzaW9uOiAwLjEuMApwdWJsaXNoX3RvOiAnbm9uZScKCmVudmlyb25tZW50OgogIHNkazogXjMuOC4wCiAgZmx1dHRlcjogIj49My4yOS4wIgoKZGVwZW5kZW5jaWVzOgogIGZsdXR0ZXI6CiAgICBzZGs6IGZsdXR0ZXIKICBmaXJlYmFzZV9jb3JlOiBeMy4xNC4wCiAgZmlyZWJhc2VfY3Jhc2hseXRpY3M6IF40LjEuNAogIGNvcmU6CiAgICBwYXRoOiAuLi9jb3JlCiAgZGk6CiAgICBwYXRoOiAuLi9kaQoKZGV2X2RlcGVuZGVuY2llczoKICBmbHV0dGVyX3Rlc3Q6CiAgICBzZGs6IGZsdXR0ZXIKICBmbHV0dGVyX2xpbnRzOiBeNS4wLjAKCmZsdXR0ZXI6Cg==", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/README.md", + "data": + "IyBDcmFzaGx5dGljcwoKRmlyZWJhc2UgQ3Jhc2hseXRpY3MgaW50ZWdyYXRpb24gcGFja2FnZSBmb3Ige3tuYW1lLnRpdGxlQ2FzZSgpfX0gLSBwcm92aWRpbmcgY29tcHJlaGVuc2l2ZSBjcmFzaCByZXBvcnRpbmcgYW5kIGVycm9yIHRyYWNraW5nIGNhcGFiaWxpdGllcy4KClshW25vbnN0b3BfY2xpXShodHRwczovL2ltZy5zaGllbGRzLmlvL2JhZGdlL3N0YXJ0ZWQlMjB3aXRoLW5vbnN0b3BfY2xpLTE2NkM0RS5zdmc/c3R5bGU9ZmxhdC1zcXVhcmUpXShodHRwczovL3B1Yi5kZXYvcGFja2FnZXMvbm9uc3RvcF9jbGkpClshW21lbG9zXShodHRwczovL2ltZy5zaGllbGRzLmlvL2JhZGdlL21haW50YWluZWQlMjB3aXRoLW1lbG9zLWY3MDBmZi5zdmc/c3R5bGU9ZmxhdC1zcXVhcmUpXShodHRwczovL2dpdGh1Yi5jb20vaW52ZXJ0YXNlL21lbG9zKQoKIyMgRmVhdHVyZXMg4pyoCgotIPCflKUgKipGaXJlYmFzZSBDcmFzaGx5dGljcyBJbnRlZ3JhdGlvbioqIC0gQ29tcGxldGUgRmlyZWJhc2UgQ3Jhc2hseXRpY3MgaW1wbGVtZW50YXRpb24KLSDwn5qAICoqQXV0b21hdGljIEVycm9yIEhhbmRsaW5nKiogLSBDYXRjaGVzIEZsdXR0ZXIgZnJhbWV3b3JrIGVycm9ycyBhdXRvbWF0aWNhbGx5Ci0g8J+RpCAqKlVzZXIgQ29udGV4dCoqIC0gVHJhY2sgY3Jhc2hlcyB3aXRoIHVzZXIgaW5mb3JtYXRpb24gYW5kIG1ldGFkYXRhCi0g8J+UkSAqKkN1c3RvbSBBdHRyaWJ1dGVzKiogLSBBZGQgY3VzdG9tIGtleS12YWx1ZSBwYWlycyBmb3IgZGVidWdnaW5nCi0g8J+TiiAqKkN1c3RvbSBMb2dnaW5nKiogLSBMb2cgY3VzdG9tIG1lc3NhZ2VzIGZvciBjcmFzaCBhbmFseXNpcwotIOKame+4jyAqKkNvbmZpZ3VyYWJsZSoqIC0gRmxleGlibGUgY29uZmlndXJhdGlvbiBmb3IgZGlmZmVyZW50IGVudmlyb25tZW50cwotIPCfp6kgKipESSBJbnRlZ3JhdGlvbioqIC0gU2VhbWxlc3MgaW50ZWdyYXRpb24gd2l0aCBkZXBlbmRlbmN5IGluamVjdGlvbgotIPCfm6HvuI8gKipUeXBlIFNhZmUqKiAtIEZ1bGwgVHlwZVNjcmlwdC1zdHlsZSB0eXBlIHNhZmV0eSB3aXRoIERhcnQKCiMjIEluc3RhbGxhdGlvbiDwn5K7CgpBZGQgdG8geW91ciBgcHVic3BlYy55YW1sYDoKCmBgYHlhbWwKZGVwZW5kZW5jaWVzOgogIGNyYXNobHl0aWNzOgogICAgcGF0aDogLi4vcGFja2FnZXMvY3Jhc2hseXRpY3MKYGBgCgojIyBRdWljayBTdGFydCDwn5qACgojIyMgMS4gSW5pdGlhbGl6ZSBpbiB5b3VyIGFwcAoKYGBgZGFydAppbXBvcnQgJ3BhY2thZ2U6Y3Jhc2hseXRpY3MvY3Jhc2hseXRpY3MuZGFydCc7Cgp2b2lkIG1haW4oKSBhc3luYyB7CiAgV2lkZ2V0c0ZsdXR0ZXJCaW5kaW5nLmVuc3VyZUluaXRpYWxpemVkKCk7CiAgCiAgLy8gSW5pdGlhbGl6ZSBjcmFzaGx5dGljcyB3aXRoIGRlZmF1bHQgY29uZmlnCiAgYXdhaXQgY3Jhc2hseXRpY3MuaW5pdCgpOwogIAogIHJ1bkFwcChNeUFwcCgpKTsKfQpgYGAKCiMjIyAyLiBVc2UgdGhlIHNlcnZpY2UKCmBgYGRhcnQKaW1wb3J0ICdwYWNrYWdlOmNyYXNobHl0aWNzL2NyYXNobHl0aWNzLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZGkvZGkuZGFydCc7CgpjbGFzcyBNeVdpZGdldCBleHRlbmRzIFN0YXRlZnVsV2lkZ2V0IHsKICBAb3ZlcnJpZGUKICBfTXlXaWRnZXRTdGF0ZSBjcmVhdGVTdGF0ZSgpID0+IF9NeVdpZGdldFN0YXRlKCk7Cn0KCmNsYXNzIF9NeVdpZGdldFN0YXRlIGV4dGVuZHMgU3RhdGU8TXlXaWRnZXQ+IHsKICBsYXRlIGZpbmFsIENyYXNobHl0aWNzU2VydmljZSBfY3Jhc2hseXRpY3M7CgogIEBvdmVycmlkZQogIHZvaWQgaW5pdFN0YXRlKCkgewogICAgc3VwZXIuaW5pdFN0YXRlKCk7CiAgICBfY3Jhc2hseXRpY3MgPSBkaS5nZXQ8Q3Jhc2hseXRpY3NTZXJ2aWNlPigpOwogIH0KCiAgdm9pZCBfcmVwb3J0RXJyb3IoKSBhc3luYyB7CiAgICB0cnkgewogICAgICAvLyBTb21lIHJpc2t5IG9wZXJhdGlvbgogICAgICB0aHJvdyBFeGNlcHRpb24oJ1NvbWV0aGluZyB3ZW50IHdyb25nIScpOwogICAgfSBjYXRjaCAoZXJyb3IsIHN0YWNrVHJhY2UpIHsKICAgICAgLy8gUmVwb3J0IHRoZSBlcnJvcgogICAgICBhd2FpdCBfY3Jhc2hseXRpY3MucmVwb3J0RXJyb3IoCiAgICAgICAgZXJyb3IsIAogICAgICAgIHN0YWNrVHJhY2U6IHN0YWNrVHJhY2UsCiAgICAgICAgY29udGV4dDogeydmZWF0dXJlJzogJ3VzZXJfYWN0aW9uJywgJ3NjcmVlbic6ICdob21lJ30sCiAgICAgICk7CiAgICB9CiAgfQp9CmBgYAoKIyMgQ29uZmlndXJhdGlvbiDimpnvuI8KCiMjIyBCYXNpYyBDb25maWd1cmF0aW9uCgpgYGBkYXJ0CmltcG9ydCAncGFja2FnZTpjcmFzaGx5dGljcy9jcmFzaGx5dGljcy5kYXJ0JzsKCmF3YWl0IGNyYXNobHl0aWNzLmluaXQoCiAgY29uZmlnOiBDcmFzaGx5dGljc0NvbmZpZygKICAgIGVuYWJsZUluRGVidWdNb2RlOiBmYWxzZSwKICAgIGVuYWJsZUF1dG9tYXRpY0RhdGFDb2xsZWN0aW9uOiB0cnVlLAogICAgZW5hYmxlQ3VzdG9tTG9nczogdHJ1ZSwKICAgIGxvZ0J1ZmZlclNpemU6IDEwMCwKICAgIGVuYWJsZVVzZXJNZXRhZGF0YTogdHJ1ZSwKICAgIGN1c3RvbUtleXM6IHsKICAgICAgJ2FwcF92ZXJzaW9uJzogJzEuMC4wJywKICAgICAgJ2Vudmlyb25tZW50JzogJ3Byb2R1Y3Rpb24nLAogICAgfSwKICApLAopOwpgYGAKCiMjIyBBZHZhbmNlZCBDb25maWd1cmF0aW9uCgpgYGBkYXJ0Ci8vIEN1c3RvbSBjb25maWd1cmF0aW9uIGZvciBkaWZmZXJlbnQgZW52aXJvbm1lbnRzCmZpbmFsIGNvbmZpZyA9IGtEZWJ1Z01vZGUgCiAgPyBDcmFzaGx5dGljc0NvbmZpZygKICAgICAgZW5hYmxlSW5EZWJ1Z01vZGU6IHRydWUsCiAgICAgIGVuYWJsZUF1dG9tYXRpY0RhdGFDb2xsZWN0aW9uOiBmYWxzZSwKICAgICAgY3VzdG9tS2V5czogeydlbnZpcm9ubWVudCc6ICdkZWJ1Zyd9LAogICAgKQogIDogQ3Jhc2hseXRpY3NDb25maWcoCiAgICAgIGVuYWJsZUF1dG9tYXRpY0RhdGFDb2xsZWN0aW9uOiB0cnVlLAogICAgICBsb2dCdWZmZXJTaXplOiAyMDAsCiAgICAgIGN1c3RvbUtleXM6IHsnZW52aXJvbm1lbnQnOiAncHJvZHVjdGlvbid9LAogICAgKTsKCmF3YWl0IGNyYXNobHl0aWNzLmluaXQoY29uZmlnOiBjb25maWcpOwpgYGAKCiMjIFVzYWdlIEV4YW1wbGVzIPCfk50KCiMjIyBFcnJvciBSZXBvcnRpbmcKCmBgYGRhcnQKZmluYWwgY3Jhc2hseXRpY3MgPSBkaS5nZXQ8Q3Jhc2hseXRpY3NTZXJ2aWNlPigpOwoKLy8gUmVwb3J0IGEgbm9uLWZhdGFsIGVycm9yCmF3YWl0IGNyYXNobHl0aWNzLnJlcG9ydEVycm9yKAogIEV4Y2VwdGlvbignTmV0d29yayB0aW1lb3V0JyksCiAgc3RhY2tUcmFjZTogU3RhY2tUcmFjZS5jdXJyZW50LAogIGNvbnRleHQ6IHsnZW5kcG9pbnQnOiAnL2FwaS91c2VycycsICd0aW1lb3V0JzogJzMwcyd9LAopOwoKLy8gUmVwb3J0IGEgZmF0YWwgZXJyb3IKYXdhaXQgY3Jhc2hseXRpY3MucmVwb3J0RXJyb3IoCiAgZXJyb3IsCiAgc3RhY2tUcmFjZTogc3RhY2tUcmFjZSwKICBmYXRhbDogdHJ1ZSwKICB1c2VySWQ6ICd1c2VyMTIzJywKKTsKYGBgCgojIyMgVXNlciBDb250ZXh0CgpgYGBkYXJ0Ci8vIFNldCB1c2VyIGluZm9ybWF0aW9uCmF3YWl0IGNyYXNobHl0aWNzLnNldFVzZXIoCiAgdXNlcklkOiAndXNlcjEyMycsCiAgZW1haWw6ICd1c2VyQGV4YW1wbGUuY29tJywKICBuYW1lOiAnSm9obiBEb2UnLAogIGN1c3RvbUF0dHJpYnV0ZXM6IHsKICAgICdzdWJzY3JpcHRpb24nOiAncHJlbWl1bScsCiAgICAnbGFzdF9sb2dpbic6IERhdGVUaW1lLm5vdygpLnRvSXNvODYwMVN0cmluZygpLAogIH0sCik7CgovLyBDbGVhciB1c2VyIGluZm9ybWF0aW9uIChlLmcuLCBvbiBsb2dvdXQpCmF3YWl0IGNyYXNobHl0aWNzLmNsZWFyVXNlcigpOwpgYGAKCiMjIyBDdXN0b20gTG9nZ2luZwoKYGBgZGFydAovLyBMb2cgY3VzdG9tIG1lc3NhZ2VzCmF3YWl0IGNyYXNobHl0aWNzLmxvZ01lc3NhZ2UoJ1VzZXIgY29tcGxldGVkIG9uYm9hcmRpbmcnKTsKYXdhaXQgY3Jhc2hseXRpY3MubG9nTWVzc2FnZSgnQVBJIGNhbGwgc3RhcnRlZDogL3VzZXJzL3Byb2ZpbGUnKTsKCi8vIFNldCBjdXN0b20gYXR0cmlidXRlcwphd2FpdCBjcmFzaGx5dGljcy5zZXRDdXN0b21BdHRyaWJ1dGVzKHsKICAnZmVhdHVyZV9mbGFnX2EnOiB0cnVlLAogICdhcGlfdmVyc2lvbic6ICd2MicsCiAgJ2RldmljZV90eXBlJzogJ21vYmlsZScsCn0pOwoKLy8gU2V0IGluZGl2aWR1YWwgY3VzdG9tIGF0dHJpYnV0ZQphd2FpdCBjcmFzaGx5dGljcy5zZXRDdXN0b21BdHRyaWJ1dGUoJ2N1cnJlbnRfc2NyZWVuJywgJ3Byb2ZpbGUnKTsKYGBgCgojIyMgTWFudWFsIENyYXNoIFJlcG9ydGluZwoKYGBgZGFydAovLyBVc2UgdGhlIGNsaWVudCBkaXJlY3RseSBmb3IgYWR2YW5jZWQgc2NlbmFyaW9zCmZpbmFsIGNsaWVudCA9IGRpLmdldDxDcmFzaGx5dGljc0NsaWVudD4oKTsKCmF3YWl0IGNsaWVudC5yZWNvcmRFcnJvcigKICBDdXN0b21FeGNlcHRpb24oJ0RhdGFiYXNlIGNvbm5lY3Rpb24gZmFpbGVkJyksCiAgU3RhY2tUcmFjZS5jdXJyZW50LAogIGZhdGFsOiBmYWxzZSwKICBpbmZvcm1hdGlvbjogWydEYXRhYmFzZSBob3N0OiBkYi5leGFtcGxlLmNvbScsICdDb25uZWN0aW9uIHRpbWVvdXQ6IDMwcyddLAopOwpgYGAKCiMjIEFQSSBSZWZlcmVuY2Ug8J+TmgoKIyMjIENyYXNobHl0aWNzU2VydmljZQoKTWFpbiBzZXJ2aWNlIGNsYXNzIGZvciBjcmFzaCByZXBvcnRpbmc6Cgp8IE1ldGhvZCB8IERlc2NyaXB0aW9uIHwKfC0tLS0tLS0tfC0tLS0tLS0tLS0tLS18CnwgYHJlcG9ydEVycm9yKClgIHwgUmVwb3J0IGFuIGVycm9yIG9yIGV4Y2VwdGlvbiB8CnwgYHJlcG9ydEZsdXR0ZXJFcnJvcigpYCB8IFJlcG9ydCBGbHV0dGVyIGZyYW1ld29yayBlcnJvcnMgfAp8IGBsb2dNZXNzYWdlKClgIHwgTG9nIGN1c3RvbSBtZXNzYWdlcyB8CnwgYHNldFVzZXIoKWAgfCBTZXQgdXNlciBpbmZvcm1hdGlvbiB8CnwgYGNsZWFyVXNlcigpYCB8IENsZWFyIHVzZXIgaW5mb3JtYXRpb24gfAp8IGBzZXRDdXN0b21BdHRyaWJ1dGVzKClgIHwgU2V0IG11bHRpcGxlIGN1c3RvbSBhdHRyaWJ1dGVzIHwKfCBgc2V0Q3VzdG9tQXR0cmlidXRlKClgIHwgU2V0IHNpbmdsZSBjdXN0b20gYXR0cmlidXRlIHwKfCBgc2V0Q3Jhc2hSZXBvcnRpbmdFbmFibGVkKClgIHwgRW5hYmxlL2Rpc2FibGUgY3Jhc2ggcmVwb3J0aW5nIHwKfCBgc2VuZFBlbmRpbmdSZXBvcnRzKClgIHwgU2VuZCB1bnNlbnQgY3Jhc2ggcmVwb3J0cyB8CnwgYGRlbGV0ZVBlbmRpbmdSZXBvcnRzKClgIHwgRGVsZXRlIHVuc2VudCBjcmFzaCByZXBvcnRzIHwKCiMjIyBDcmFzaGx5dGljc0NvbmZpZwoKQ29uZmlndXJhdGlvbiBvcHRpb25zOgoKfCBQcm9wZXJ0eSB8IERlZmF1bHQgfCBEZXNjcmlwdGlvbiB8CnwtLS0tLS0tLS0tfC0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tfAp8IGBlbmFibGVJbkRlYnVnTW9kZWAgfCBgZmFsc2VgIHwgRW5hYmxlIGNyYXNobHl0aWNzIGluIGRlYnVnIG1vZGUgfAp8IGBlbmFibGVBdXRvbWF0aWNEYXRhQ29sbGVjdGlvbmAgfCBgdHJ1ZWAgfCBFbmFibGUgYXV0b21hdGljIGVycm9yIGNvbGxlY3Rpb24gfAp8IGBlbmFibGVDdXN0b21Mb2dzYCB8IGB0cnVlYCB8IEVuYWJsZSBjdXN0b20gbG9nZ2luZyB8CnwgYGxvZ0J1ZmZlclNpemVgIHwgYDEwMGAgfCBNYXhpbXVtIGxvZyBidWZmZXIgc2l6ZSB8CnwgYGVuYWJsZVVzZXJNZXRhZGF0YWAgfCBgdHJ1ZWAgfCBFbmFibGUgdXNlciBtZXRhZGF0YSBjb2xsZWN0aW9uIHwKfCBgY3VzdG9tS2V5c2AgfCBge31gIHwgRGVmYXVsdCBjdXN0b20ga2V5cyB8CgojIyBJbnRlZ3JhdGlvbiB3aXRoIERJIPCflKcKClRoZSBwYWNrYWdlIGludGVncmF0ZXMgc2VhbWxlc3NseSB3aXRoIHRoZSBESSBjb250YWluZXI6CgpgYGBkYXJ0Ci8vIEdldCBzZXJ2aWNlcyBmcm9tIERJCmZpbmFsIGNyYXNobHl0aWNzID0gZGkuZ2V0PENyYXNobHl0aWNzU2VydmljZT4oKTsKZmluYWwgY2xpZW50ID0gZGkuZ2V0PENyYXNobHl0aWNzQ2xpZW50PigpOwpmaW5hbCBjb25maWcgPSBkaS5nZXQ8Q3Jhc2hseXRpY3NDb25maWc+KCk7CgovLyBTZXJ2aWNlcyBhcmUgYXV0b21hdGljYWxseSBkaXNwb3NlZCB3aGVuIERJIGNvbnRhaW5lciBpcyBkaXNwb3NlZApgYGAKCiMjIFRlc3Rpbmcg8J+nqgoKYGBgZGFydAovLyBVcGRhdGUgdGVzdCBmaWxlCmltcG9ydCAncGFja2FnZTpmbHV0dGVyX3Rlc3QvZmx1dHRlcl90ZXN0LmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Y3Jhc2hseXRpY3MvY3Jhc2hseXRpY3MuZGFydCc7Cgp2b2lkIG1haW4oKSB7CiAgZ3JvdXAoJ0NyYXNobHl0aWNzJywgKCkgewogICAgdGVzdCgnY2FuIGJlIGluaXRpYWxpemVkJywgKCkgYXN5bmMgewogICAgICAvLyBUZXN0IGluaXRpYWxpemF0aW9uCiAgICAgIGV4cGVjdCgoKSA9PiBjcmFzaGx5dGljcy5pbml0KCksIGlzTm90KHRocm93c0V4Y2VwdGlvbikpOwogICAgfSk7CiAgfSk7Cn0KYGBgCgojIyBCZXN0IFByYWN0aWNlcyDwn5KhCgoxLiAqKkluaXRpYWxpemUgRWFybHkqKjogSW5pdGlhbGl6ZSBjcmFzaGx5dGljcyBhcyBlYXJseSBhcyBwb3NzaWJsZSBpbiB5b3VyIGFwcAoyLiAqKkVudmlyb25tZW50IENvbmZpZ3VyYXRpb24qKjogVXNlIGRpZmZlcmVudCBjb25maWdzIGZvciBkZWJ1Zy9yZWxlYXNlIGJ1aWxkcwozLiAqKlVzZXIgUHJpdmFjeSoqOiBCZSBtaW5kZnVsIG9mIHVzZXIgcHJpdmFjeSB3aGVuIHNldHRpbmcgdXNlciBtZXRhZGF0YQo0LiAqKkNvbnRleHQgaXMgS2V5Kio6IEFsd2F5cyBwcm92aWRlIGNvbnRleHQgd2hlbiByZXBvcnRpbmcgZXJyb3JzCjUuICoqVGVzdCBpbiBEZWJ1ZyoqOiBFbmFibGUgZGVidWcgbW9kZSBkdXJpbmcgZGV2ZWxvcG1lbnQgdG8gdGVzdCBpbnRlZ3JhdGlvbgo2LiAqKk1vbml0b3IgVXNhZ2UqKjogUmVndWxhcmx5IGNoZWNrIEZpcmViYXNlIGNvbnNvbGUgZm9yIGNyYXNoIHJlcG9ydHMKCiMjIFJlcXVpcmVtZW50cyDwn5OLCgotIEZsdXR0ZXIgU0RLID49My4xOS4wCi0gRGFydCBTREsgXjMuMy4wCi0gRmlyZWJhc2UgcHJvamVjdCB3aXRoIENyYXNobHl0aWNzIGVuYWJsZWQKLSBgY29yZWAgYW5kIGBkaWAgcGFja2FnZXMgZnJvbSB7e25hbWUudGl0bGVDYXNlKCl9fSBtb25vcmVwbwoKW2ZsdXR0ZXJfaW5zdGFsbF9saW5rXTogaHR0cHM6Ly9kb2NzLmZsdXR0ZXIuZGV2L2dldC1zdGFydGVkL2luc3RhbGw=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{crashlytics_dir}}/test/crashlytics_test.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNyYXNobHl0aWNzL2NyYXNobHl0aWNzLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlcl90ZXN0L2ZsdXR0ZXJfdGVzdC5kYXJ0JzsKCnZvaWQgbWFpbigpIHsKICBncm91cCgnQ3Jhc2hseXRpY3MnLCAoKSB7CiAgICBncm91cCgnQ3Jhc2hseXRpY3NDb25maWcnLCAoKSB7CiAgICAgIHRlc3QoJ2NyZWF0ZXMgZGVmYXVsdCBjb25maWcgY29ycmVjdGx5JywgKCkgewogICAgICAgIGNvbnN0IGNvbmZpZyA9IERlZmF1bHRDcmFzaGx5dGljc0NvbmZpZygpOwoKICAgICAgICBleHBlY3QoY29uZmlnLmVuYWJsZUluRGVidWdNb2RlLCBmYWxzZSk7CiAgICAgICAgZXhwZWN0KGNvbmZpZy5lbmFibGVBdXRvbWF0aWNEYXRhQ29sbGVjdGlvbiwgdHJ1ZSk7CiAgICAgICAgZXhwZWN0KGNvbmZpZy5lbmFibGVDdXN0b21Mb2dzLCB0cnVlKTsKICAgICAgICBleHBlY3QoY29uZmlnLmxvZ0J1ZmZlclNpemUsIDEwMCk7CiAgICAgICAgZXhwZWN0KGNvbmZpZy5lbmFibGVVc2VyTWV0YWRhdGEsIHRydWUpOwogICAgICAgIGV4cGVjdChjb25maWcuY3VzdG9tS2V5cywgaXNFbXB0eSk7CiAgICAgIH0pOwoKICAgICAgdGVzdCgnY3JlYXRlcyBjdXN0b20gY29uZmlnIGNvcnJlY3RseScsICgpIHsKICAgICAgICBjb25zdCBjb25maWcgPSBDcmFzaGx5dGljc0NvbmZpZygKICAgICAgICAgIGVuYWJsZUluRGVidWdNb2RlOiB0cnVlLAogICAgICAgICAgZW5hYmxlQXV0b21hdGljRGF0YUNvbGxlY3Rpb246IGZhbHNlLAogICAgICAgICAgbG9nQnVmZmVyU2l6ZTogNTAsCiAgICAgICAgICBjdXN0b21LZXlzOiB7J3Rlc3QnOiAndmFsdWUnfSwKICAgICAgICApOwoKICAgICAgICBleHBlY3QoY29uZmlnLmVuYWJsZUluRGVidWdNb2RlLCB0cnVlKTsKICAgICAgICBleHBlY3QoY29uZmlnLmVuYWJsZUF1dG9tYXRpY0RhdGFDb2xsZWN0aW9uLCBmYWxzZSk7CiAgICAgICAgZXhwZWN0KGNvbmZpZy5sb2dCdWZmZXJTaXplLCA1MCk7CiAgICAgICAgZXhwZWN0KGNvbmZpZy5jdXN0b21LZXlzLCB7J3Rlc3QnOiAndmFsdWUnfSk7CiAgICAgIH0pOwoKICAgICAgdGVzdCgnY29weVdpdGggd29ya3MgY29ycmVjdGx5JywgKCkgewogICAgICAgIGNvbnN0IGNvbmZpZyA9IENyYXNobHl0aWNzQ29uZmlnKCk7CiAgICAgICAgZmluYWwgdXBkYXRlZCA9IGNvbmZpZy5jb3B5V2l0aChlbmFibGVJbkRlYnVnTW9kZTogdHJ1ZSk7CgogICAgICAgIGV4cGVjdCh1cGRhdGVkLmVuYWJsZUluRGVidWdNb2RlLCB0cnVlKTsKICAgICAgICBleHBlY3QoCiAgICAgICAgICB1cGRhdGVkLmVuYWJsZUF1dG9tYXRpY0RhdGFDb2xsZWN0aW9uLAogICAgICAgICAgY29uZmlnLmVuYWJsZUF1dG9tYXRpY0RhdGFDb2xsZWN0aW9uLAogICAgICAgICk7CiAgICAgIH0pOwogICAgfSk7CgogICAgZ3JvdXAoJ1VzZXJNZXRhZGF0YScsICgpIHsKICAgICAgdGVzdCgnY3JlYXRlcyB1c2VyIG1ldGFkYXRhIGNvcnJlY3RseScsICgpIHsKICAgICAgICBjb25zdCBtZXRhZGF0YSA9IFVzZXJNZXRhZGF0YSgKICAgICAgICAgIHVzZXJJZDogJ3VzZXIxMjMnLAogICAgICAgICAgZW1haWw6ICd0ZXN0QGV4YW1wbGUuY29tJywKICAgICAgICAgIG5hbWU6ICdUZXN0IFVzZXInLAogICAgICAgICAgY3VzdG9tQXR0cmlidXRlczogeydyb2xlJzogJ2FkbWluJ30sCiAgICAgICAgKTsKCiAgICAgICAgZXhwZWN0KG1ldGFkYXRhLnVzZXJJZCwgJ3VzZXIxMjMnKTsKICAgICAgICBleHBlY3QobWV0YWRhdGEuZW1haWwsICd0ZXN0QGV4YW1wbGUuY29tJyk7CiAgICAgICAgZXhwZWN0KG1ldGFkYXRhLm5hbWUsICdUZXN0IFVzZXInKTsKICAgICAgICBleHBlY3QobWV0YWRhdGEuY3VzdG9tQXR0cmlidXRlcywgeydyb2xlJzogJ2FkbWluJ30pOwogICAgICB9KTsKCiAgICAgIHRlc3QoJ3RvTWFwIHdvcmtzIGNvcnJlY3RseScsICgpIHsKICAgICAgICBjb25zdCBtZXRhZGF0YSA9IFVzZXJNZXRhZGF0YSgKICAgICAgICAgIHVzZXJJZDogJ3VzZXIxMjMnLAogICAgICAgICAgZW1haWw6ICd0ZXN0QGV4YW1wbGUuY29tJywKICAgICAgICApOwoKICAgICAgICBmaW5hbCBtYXAgPSBtZXRhZGF0YS50b01hcCgpOwogICAgICAgIGV4cGVjdChtYXBbJ3VzZXJJZCddLCAndXNlcjEyMycpOwogICAgICAgIGV4cGVjdChtYXBbJ2VtYWlsJ10sICd0ZXN0QGV4YW1wbGUuY29tJyk7CiAgICAgICAgZXhwZWN0KG1hcFsnbmFtZSddLCBudWxsKTsKICAgICAgICBleHBlY3QobWFwWydjdXN0b21BdHRyaWJ1dGVzJ10sIGlzRW1wdHkpOwogICAgICB9KTsKCiAgICAgIHRlc3QoJ2NvcHlXaXRoIHdvcmtzIGNvcnJlY3RseScsICgpIHsKICAgICAgICBjb25zdCBtZXRhZGF0YSA9IFVzZXJNZXRhZGF0YSh1c2VySWQ6ICd1c2VyMTIzJyk7CiAgICAgICAgZmluYWwgdXBkYXRlZCA9IG1ldGFkYXRhLmNvcHlXaXRoKGVtYWlsOiAnbmV3QGV4YW1wbGUuY29tJyk7CgogICAgICAgIGV4cGVjdCh1cGRhdGVkLnVzZXJJZCwgJ3VzZXIxMjMnKTsKICAgICAgICBleHBlY3QodXBkYXRlZC5lbWFpbCwgJ25ld0BleGFtcGxlLmNvbScpOwogICAgICAgIGV4cGVjdCh1cGRhdGVkLm5hbWUsIG51bGwpOwogICAgICB9KTsKCiAgICAgIHRlc3QoJ2VxdWFsaXR5IHdvcmtzIGNvcnJlY3RseScsICgpIHsKICAgICAgICBjb25zdCBtZXRhZGF0YTEgPSBVc2VyTWV0YWRhdGEodXNlcklkOiAndXNlcjEyMycpOwogICAgICAgIGNvbnN0IG1ldGFkYXRhMiA9IFVzZXJNZXRhZGF0YSh1c2VySWQ6ICd1c2VyMTIzJyk7CiAgICAgICAgY29uc3QgbWV0YWRhdGEzID0gVXNlck1ldGFkYXRhKHVzZXJJZDogJ3VzZXI0NTYnKTsKCiAgICAgICAgZXhwZWN0KG1ldGFkYXRhMSwgZXF1YWxzKG1ldGFkYXRhMikpOwogICAgICAgIGV4cGVjdChtZXRhZGF0YTEsIGlzTm90KGVxdWFscyhtZXRhZGF0YTMpKSk7CiAgICAgIH0pOwogICAgfSk7CgogICAgZ3JvdXAoJ0NyYXNoUmVwb3J0JywgKCkgewogICAgICB0ZXN0KCdjcmVhdGVzIGNyYXNoIHJlcG9ydCBjb3JyZWN0bHknLCAoKSB7CiAgICAgICAgZmluYWwgdGltZXN0YW1wID0gRGF0ZVRpbWUubm93KCk7CiAgICAgICAgZmluYWwgcmVwb3J0ID0gQ3Jhc2hSZXBvcnQoCiAgICAgICAgICBleGNlcHRpb246IEV4Y2VwdGlvbignVGVzdCBlcnJvcicpLAogICAgICAgICAgc3RhY2tUcmFjZTogU3RhY2tUcmFjZS5jdXJyZW50LAogICAgICAgICAgZmF0YWw6IHRydWUsCiAgICAgICAgICB0aW1lc3RhbXA6IHRpbWVzdGFtcCwKICAgICAgICAgIGN1c3RvbUtleXM6IHsna2V5JzogJ3ZhbHVlJ30sCiAgICAgICAgKTsKCiAgICAgICAgZXhwZWN0KHJlcG9ydC5leGNlcHRpb24udG9TdHJpbmcoKSwgY29udGFpbnMoJ1Rlc3QgZXJyb3InKSk7CiAgICAgICAgZXhwZWN0KHJlcG9ydC5mYXRhbCwgdHJ1ZSk7CiAgICAgICAgZXhwZWN0KHJlcG9ydC50aW1lc3RhbXAsIHRpbWVzdGFtcCk7CiAgICAgICAgZXhwZWN0KHJlcG9ydC5jdXN0b21LZXlzLCB7J2tleSc6ICd2YWx1ZSd9KTsKICAgICAgfSk7CgogICAgICB0ZXN0KCd0b01hcCB3b3JrcyBjb3JyZWN0bHknLCAoKSB7CiAgICAgICAgZmluYWwgcmVwb3J0ID0gQ3Jhc2hSZXBvcnQoCiAgICAgICAgICBleGNlcHRpb246IEV4Y2VwdGlvbignVGVzdCBlcnJvcicpLAogICAgICAgICAgZmF0YWw6IGZhbHNlLAogICAgICAgICAgY3VzdG9tS2V5czogeyd0ZXN0JzogJ2RhdGEnfSwKICAgICAgICApOwoKICAgICAgICBmaW5hbCBtYXAgPSByZXBvcnQudG9NYXAoKTsKICAgICAgICBleHBlY3QobWFwWydmYXRhbCddLCBmYWxzZSk7CiAgICAgICAgZXhwZWN0KG1hcFsnY3VzdG9tS2V5cyddLCB7J3Rlc3QnOiAnZGF0YSd9KTsKICAgICAgICBleHBlY3QobWFwWydleGNlcHRpb24nXSwgY29udGFpbnMoJ1Rlc3QgZXJyb3InKSk7CiAgICAgIH0pOwoKICAgICAgdGVzdCgnY29weVdpdGggd29ya3MgY29ycmVjdGx5JywgKCkgewogICAgICAgIGZpbmFsIHJlcG9ydCA9IENyYXNoUmVwb3J0KAogICAgICAgICAgZXhjZXB0aW9uOiBFeGNlcHRpb24oJ1Rlc3QgZXJyb3InKSwKICAgICAgICAgIGZhdGFsOiBmYWxzZSwKICAgICAgICApOwoKICAgICAgICBmaW5hbCB1cGRhdGVkID0gcmVwb3J0LmNvcHlXaXRoKGZhdGFsOiB0cnVlKTsKICAgICAgICBleHBlY3QodXBkYXRlZC5mYXRhbCwgdHJ1ZSk7CiAgICAgICAgZXhwZWN0KHVwZGF0ZWQuZXhjZXB0aW9uLnRvU3RyaW5nKCksIHJlcG9ydC5leGNlcHRpb24udG9TdHJpbmcoKSk7CiAgICAgIH0pOwogICAgfSk7CiAgfSk7Cn0K", "type": "text" }, { - "path": "{{ name.snakeCase() }}/features/README.md", - "data": "IyBQdXQgYWxsIHlvdXIgZmVhdHVyZXMgaW4gdGhpcyBmb2xkZXI=", + "path": + "{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/developer.dart", + "data": + "ZXhwb3J0ICdzcmMvcm91dGVzL2luZGV4LmRhcnQnOwpleHBvcnQgJ3NyYy9zY3JlZW5zL2luZGV4LmRhcnQnOwpleHBvcnQgJ3NyYy93aWRnZXRzL2luZGV4LmRhcnQnOwo=", "type": "text" }, { - "path": "{{ name.snakeCase() }}/melos.yaml", + "path": + "{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/routes/developer_routes.dart", "data": - "bmFtZToge3sgbmFtZS5zbmFrZUNhc2UoKSB9fV93b3Jrc3BhY2UKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBNT05PUkVQTyBDT05GSUdVUkFUSU9OCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBEZWZpbmUgdGhlIHBhY2thZ2VzIHRoYXQgbWFrZSB1cCB5b3VyIG1vbm9yZXBvCiMgQ3VzdG9taXplIHRoZXNlIHBhdHRlcm5zIHRvIG1hdGNoIHlvdXIgcHJvamVjdCBzdHJ1Y3R1cmUKcGFja2FnZXM6CiAgLSBhcHBzLyoKICAtIGZlYXR1cmVzLyoKICAtIHBhY2thZ2VzLyoKICAtIHBsdWdpbnMvKgoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNDUklQVFMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzY3JpcHRzOgogIGNsZWFuOmZsdXR0ZXI6CiAgICBkZXNjcmlwdGlvbjogIlJ1biBmbHV0dGVyIGNsZWFuIGluIGFsbCBGbHV0dGVyIHBhY2thZ2VzIgogICAgcnVuOiBmbHV0dGVyIGNsZWFuCiAgICBleGVjOgogICAgICBjb25jdXJyZW5jeTogNAogICAgcGFja2FnZUZpbHRlcnM6CiAgICAgIGZsdXR0ZXI6IHRydWUKCiAgbGludDoKICAgIGRlc2NyaXB0aW9uOiAiUnVuIGZvcm1hdCBhbmQgYW5hbHl6ZSBhbGwgcGFja2FnZXMiCiAgICBydW46IHwKICAgICAgZGFydCBmb3JtYXQgLS1zZXQtZXhpdC1pZi1jaGFuZ2VkIC4gJiYKICAgICAgZGFydCBhbmFseXplIC0tZmF0YWwtaW5mb3MgLgogICAgZXhlYzoKICAgICAgY29uY3VycmVuY3k6IDUKICAgICAgZmFpbEZhc3Q6IHRydWUKCiAgZml4OgogICAgZGVzY3JpcHRpb246ICJBcHBseSBEYXJ0IGF1dG9tYXRlZCBmaXhlcyB0byBhbGwgcGFja2FnZXMiCiAgICBydW46IGRhcnQgZml4IC0tYXBwbHkKICAgIGV4ZWM6CiAgICAgIGNvbmN1cnJlbmN5OiA1CiAgICBwYWNrYWdlRmlsdGVyczoKICAgICAgZGlyRXhpc3RzOiBsaWIKCiAgZml4OmRyeToKICAgIGRlc2NyaXB0aW9uOiAiU2hvdyBEYXJ0IGF1dG9tYXRlZCBmaXhlcyB3aXRob3V0IGFwcGx5aW5nIHRoZW0iCiAgICBydW46IGRhcnQgZml4IC0tZHJ5LXJ1bgogICAgZXhlYzoKICAgICAgY29uY3VycmVuY3k6IDUKICAgIHBhY2thZ2VGaWx0ZXJzOgogICAgICBkaXJFeGlzdHM6IGxpYgoKICBnZW5lcmF0ZToKICAgIGRlc2NyaXB0aW9uOiAiR2VuZXJhdGUgY29kZSBmb3IgRGFydCBwYWNrYWdlcyB1c2luZyBidWlsZF9ydW5uZXIiCiAgICBydW46IGRhcnQgcnVuIGJ1aWxkX3J1bm5lciBidWlsZCAtLWRlbGV0ZS1jb25mbGljdGluZy1vdXRwdXRzCiAgICBleGVjOgogICAgICBjb25jdXJyZW5jeTogMQogICAgcGFja2FnZUZpbHRlcnM6CiAgICAgIGRlcGVuZHNPbjogYnVpbGRfcnVubmVyCgogIHRlc3Q6CiAgICBkZXNjcmlwdGlvbjogIlJ1biBhbGwgdGVzdHMgKERhcnQgYW5kIEZsdXR0ZXIpIgogICAgcnVuOiBtZWxvcyBydW4gdGVzdDpzZWxlY3QgLS1uby1zZWxlY3QKCiAgdGVzdDpzZWxlY3Q6CiAgICBkZXNjcmlwdGlvbjogIlJ1biB0ZXN0cyBpbiBGbHV0dGVyIHBhY2thZ2VzIgogICAgcnVuOiBmbHV0dGVyIHRlc3QgLS1jb3ZlcmFnZQogICAgZXhlYzoKICAgICAgY29uY3VycmVuY3k6IDUKICAgICAgZmFpbEZhc3Q6IHRydWUKICAgIHBhY2thZ2VGaWx0ZXJzOgogICAgICBkaXJFeGlzdHM6IHRlc3QKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBJREUgQ09ORklHVVJBVElPTgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgT3B0aW9uYWw6IENvbmZpZ3VyZSBJREUgaW50ZWdyYXRpb24KaWRlOgogIGludGVsbGlqOgogICAgZW5hYmxlZDogdHJ1ZQogICAgbW9kdWxlTmFtZVByZWZpeDogJyc=", + "aW1wb3J0ICdwYWNrYWdlOmNvcmUvY29yZS5kYXJ0JyBhcyBjb3JlOwppbXBvcnQgJ3BhY2thZ2U6ZGV2ZWxvcGVyL3NyYy9zY3JlZW5zL2luZGV4LmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlci9tYXRlcmlhbC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmdvX3JvdXRlci9nb19yb3V0ZXIuZGFydCc7CgpjbGFzcyBEZXZlbG9wZXJSb3V0ZXMgewogIHN0YXRpYyBjb25zdCBTdHJpbmcgZGV2ZWxvcGVyID0gJy9kZXZlbG9wZXInOwp9CgpjbGFzcyBEZXZlbG9wZXJSb3V0ZXIgZXh0ZW5kcyBjb3JlLkNvcmVSb3V0ZXIgewogIEBvdmVycmlkZQogIExpc3Q8Um91dGVCYXNlPiBnZXQgcm91dGVzID0+IFsKICAgIEdvUm91dGUoCiAgICAgIHBhdGg6IERldmVsb3BlclJvdXRlcy5kZXZlbG9wZXIsCiAgICAgIG5hbWU6ICdkZXZlbG9wZXItc2NyZWVuJywKICAgICAgYnVpbGRlcjogKEJ1aWxkQ29udGV4dCBjb250ZXh0LCBHb1JvdXRlclN0YXRlIHN0YXRlKSB7CiAgICAgICAgcmV0dXJuIGNvbnN0IERldmVsb3BlclNjcmVlbigpOwogICAgICB9LAogICAgKSwKICBdOwp9Cg==", "type": "text" }, { - "path": "{{ name.snakeCase() }}/packages/README.md", + "path": + "{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/routes/index.dart", + "data": "ZXhwb3J0ICdkZXZlbG9wZXJfcm91dGVzLmRhcnQnOwo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/screens/developer_screen.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNvcmUvY29yZS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRpL2RpLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlci9tYXRlcmlhbC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOnRhbGtlcl9mbHV0dGVyL3RhbGtlcl9mbHV0dGVyLmRhcnQnOwoKY2xhc3MgRGV2ZWxvcGVyU2NyZWVuIGV4dGVuZHMgU3RhdGVsZXNzV2lkZ2V0IHsKICBjb25zdCBEZXZlbG9wZXJTY3JlZW4oe3N1cGVyLmtleX0pOwoKICBAb3ZlcnJpZGUKICBXaWRnZXQgYnVpbGQoQnVpbGRDb250ZXh0IGNvbnRleHQpIHsKICAgIHJldHVybiBUYWxrZXJTY3JlZW4odGFsa2VyOiBkaS5nZXQ8TG9nZ2VyPigpLmxvZ2dlciBhcyBUYWxrZXIpOwogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/screens/index.dart", + "data": "ZXhwb3J0ICdkZXZlbG9wZXJfc2NyZWVuLmRhcnQnOwo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/widgets/index.dart", + "data": "ZXhwb3J0ICdvcGVuX2Rldl90b29sc193cmFwcGVyLmRhcnQnOwo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{developer_dir}}/lib/src/widgets/open_dev_tools_wrapper.dart", + "data": + "aW1wb3J0ICdkYXJ0OmFzeW5jJzsKCmltcG9ydCAncGFja2FnZTpkZXZlbG9wZXIvc3JjL3JvdXRlcy9pbmRleC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZlYXR1cmVfZmxhZ3MvZmVhdHVyZV9mbGFncy5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CmltcG9ydCAncGFja2FnZTpmbHV0dGVyL3NlcnZpY2VzLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Z29fcm91dGVyL2dvX3JvdXRlci5kYXJ0JzsKCmNsYXNzIE9wZW5EZXZUb29sc1dyYXBwZXIgZXh0ZW5kcyBTdGF0ZWZ1bFdpZGdldCB7CiAgY29uc3QgT3BlbkRldlRvb2xzV3JhcHBlcih7CiAgICBzdXBlci5rZXksCiAgICByZXF1aXJlZCB0aGlzLmNoaWxkLAogICAgdGhpcy50YXBDb3VudCA9IDUsCiAgICB0aGlzLnRhcFdpbmRvdyA9IGNvbnN0IER1cmF0aW9uKHNlY29uZHM6IDIpLAogICAgdGhpcy5lbmFibGVIYXB0aWNzID0gdHJ1ZSwKICAgIHRoaXMuZW5hYmxlVmlzdWFsRmVlZGJhY2sgPSBmYWxzZSwKICB9KTsKCiAgZmluYWwgV2lkZ2V0IGNoaWxkOwogIGZpbmFsIGludCB0YXBDb3VudDsKICBmaW5hbCBEdXJhdGlvbiB0YXBXaW5kb3c7CiAgZmluYWwgYm9vbCBlbmFibGVIYXB0aWNzOwogIGZpbmFsIGJvb2wgZW5hYmxlVmlzdWFsRmVlZGJhY2s7CgogIEBvdmVycmlkZQogIFN0YXRlPE9wZW5EZXZUb29sc1dyYXBwZXI+IGNyZWF0ZVN0YXRlKCkgPT4gX09wZW5EZXZUb29sc1dyYXBwZXJTdGF0ZSgpOwp9CgpjbGFzcyBfT3BlbkRldlRvb2xzV3JhcHBlclN0YXRlIGV4dGVuZHMgU3RhdGU8T3BlbkRldlRvb2xzV3JhcHBlcj4KICAgIHdpdGggU2luZ2xlVGlja2VyUHJvdmlkZXJTdGF0ZU1peGluIHsKICBpbnQgX2N1cnJlbnRUYXBDb3VudCA9IDA7CiAgVGltZXI/IF9yZXNldFRpbWVyOwogIGxhdGUgQW5pbWF0aW9uQ29udHJvbGxlciBfYW5pbWF0aW9uQ29udHJvbGxlcjsKICBsYXRlIEFuaW1hdGlvbjxkb3VibGU+IF9zY2FsZUFuaW1hdGlvbjsKICBib29sIF9pc0FuaW1hdGluZyA9IGZhbHNlOwoKICBAb3ZlcnJpZGUKICB2b2lkIGluaXRTdGF0ZSgpIHsKICAgIHN1cGVyLmluaXRTdGF0ZSgpOwogICAgX2FuaW1hdGlvbkNvbnRyb2xsZXIgPSBBbmltYXRpb25Db250cm9sbGVyKAogICAgICBkdXJhdGlvbjogY29uc3QgRHVyYXRpb24obWlsbGlzZWNvbmRzOiAyMDApLAogICAgICB2c3luYzogdGhpcywKICAgICk7CiAgICBfc2NhbGVBbmltYXRpb24gPSBUd2Vlbjxkb3VibGU+KGJlZ2luOiAxLjAsIGVuZDogMC45NSkuYW5pbWF0ZSgKICAgICAgQ3VydmVkQW5pbWF0aW9uKHBhcmVudDogX2FuaW1hdGlvbkNvbnRyb2xsZXIsIGN1cnZlOiBDdXJ2ZXMuZWFzZUluT3V0KSwKICAgICk7CiAgfQoKICBAb3ZlcnJpZGUKICB2b2lkIGRpc3Bvc2UoKSB7CiAgICBfcmVzZXRUaW1lcj8uY2FuY2VsKCk7CiAgICBfYW5pbWF0aW9uQ29udHJvbGxlci5kaXNwb3NlKCk7CiAgICBzdXBlci5kaXNwb3NlKCk7CiAgfQoKICB2b2lkIF9oYW5kbGVUYXAoKSB7CiAgICBzZXRTdGF0ZSgoKSB7CiAgICAgIF9jdXJyZW50VGFwQ291bnQrKzsKICAgIH0pOwoKICAgIC8vIFByb3ZpZGUgaGFwdGljIGZlZWRiYWNrIGZvciBwcm9ncmVzcwogICAgaWYgKHdpZGdldC5lbmFibGVIYXB0aWNzKSB7CiAgICAgIEhhcHRpY0ZlZWRiYWNrLnNlbGVjdGlvbkNsaWNrKCk7CiAgICB9CgogICAgLy8gVmlzdWFsIGZlZWRiYWNrIGFuaW1hdGlvbgogICAgaWYgKHdpZGdldC5lbmFibGVWaXN1YWxGZWVkYmFjayAmJiAhX2lzQW5pbWF0aW5nKSB7CiAgICAgIF9pc0FuaW1hdGluZyA9IHRydWU7CiAgICAgIF9hbmltYXRpb25Db250cm9sbGVyLmZvcndhcmQoKS50aGVuKChfKSB7CiAgICAgICAgX2FuaW1hdGlvbkNvbnRyb2xsZXIucmV2ZXJzZSgpLnRoZW4oKF8pIHsKICAgICAgICAgIGlmIChtb3VudGVkKSB7CiAgICAgICAgICAgIHNldFN0YXRlKCgpIHsKICAgICAgICAgICAgICBfaXNBbmltYXRpbmcgPSBmYWxzZTsKICAgICAgICAgICAgfSk7CiAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICAgIH0pOwogICAgfQoKICAgIC8vIENoZWNrIGlmIHdlJ3ZlIHJlYWNoZWQgdGhlIHJlcXVpcmVkIHRhcCBjb3VudAogICAgaWYgKF9jdXJyZW50VGFwQ291bnQgPj0gd2lkZ2V0LnRhcENvdW50KSB7CiAgICAgIF9uYXZpZ2F0ZVRvRGV2ZWxvcGVyU2NyZWVuKCk7CiAgICAgIHJldHVybjsKICAgIH0KCiAgICAvLyBSZXNldCB0aGUgdGltZXIKICAgIF9yZXNldFRpbWVyPy5jYW5jZWwoKTsKICAgIF9yZXNldFRpbWVyID0gVGltZXIod2lkZ2V0LnRhcFdpbmRvdywgX3Jlc2V0VGFwQ291bnQpOwogIH0KCiAgdm9pZCBfbmF2aWdhdGVUb0RldmVsb3BlclNjcmVlbigpIHsKICAgIC8vIFByb3ZpZGUgc3VjY2VzcyBoYXB0aWMgZmVlZGJhY2sKICAgIGlmICh3aWRnZXQuZW5hYmxlSGFwdGljcykgewogICAgICBIYXB0aWNGZWVkYmFjay5tZWRpdW1JbXBhY3QoKTsKICAgIH0KCiAgICAvLyBSZXNldCBzdGF0ZQogICAgX3Jlc2V0VGFwQ291bnQoKTsKCiAgICAvLyBOYXZpZ2F0ZSB0byBkZXZlbG9wZXIgc2NyZWVuCiAgICBpZiAobW91bnRlZCkgewogICAgICBjb250ZXh0LnB1c2goRGV2ZWxvcGVyUm91dGVzLmRldmVsb3Blcik7CiAgICB9CiAgfQoKICB2b2lkIF9yZXNldFRhcENvdW50KCkgewogICAgX3Jlc2V0VGltZXI/LmNhbmNlbCgpOwogICAgaWYgKG1vdW50ZWQpIHsKICAgICAgc2V0U3RhdGUoKCkgewogICAgICAgIF9jdXJyZW50VGFwQ291bnQgPSAwOwogICAgICB9KTsKICAgIH0KICB9CgogIFdpZGdldCBfYnVpbGRDaGlsZCgpIHsKICAgIGlmICh3aWRnZXQuZW5hYmxlVmlzdWFsRmVlZGJhY2spIHsKICAgICAgcmV0dXJuIEFuaW1hdGVkQnVpbGRlcigKICAgICAgICBhbmltYXRpb246IF9zY2FsZUFuaW1hdGlvbiwKICAgICAgICBidWlsZGVyOiAoY29udGV4dCwgY2hpbGQpIHsKICAgICAgICAgIHJldHVybiBUcmFuc2Zvcm0uc2NhbGUoCiAgICAgICAgICAgIHNjYWxlOiBfc2NhbGVBbmltYXRpb24udmFsdWUsCiAgICAgICAgICAgIGNoaWxkOiB3aWRnZXQuY2hpbGQsCiAgICAgICAgICApOwogICAgICAgIH0sCiAgICAgICk7CiAgICB9CiAgICByZXR1cm4gd2lkZ2V0LmNoaWxkOwogIH0KCiAgQG92ZXJyaWRlCiAgV2lkZ2V0IGJ1aWxkKEJ1aWxkQ29udGV4dCBjb250ZXh0KSB7CiAgICBmaW5hbCB0aGVtZSA9IFRoZW1lLm9mKGNvbnRleHQpOwoKICAgIHJldHVybiBGZWF0dXJlRmxhZ1dyYXBwZXIoCiAgICAgIGZsYWdLZXk6ICdkZXZlbG9wZXJfc2NyZWVuX2VuYWJsZWQnLAogICAgICBidWlsZGVyOiAoY29udGV4dCwgaXNFbmFibGVkKSB7CiAgICAgICAgaWYgKCFpc0VuYWJsZWQpIHsKICAgICAgICAgIHJldHVybiB3aWRnZXQuY2hpbGQ7IC8vIEp1c3QgcmV0dXJuIHRoZSBjaGlsZCBpZiBmZWF0dXJlIGlzIGRpc2FibGVkCiAgICAgICAgfQoKICAgICAgICByZXR1cm4gR2VzdHVyZURldGVjdG9yKAogICAgICAgICAgb25UYXA6IF9oYW5kbGVUYXAsCiAgICAgICAgICBiZWhhdmlvcjogSGl0VGVzdEJlaGF2aW9yLnRyYW5zbHVjZW50LAogICAgICAgICAgY2hpbGQ6IFN0YWNrKAogICAgICAgICAgICBjaGlsZHJlbjogWwogICAgICAgICAgICAgIF9idWlsZENoaWxkKCksCiAgICAgICAgICAgICAgLy8gT3B0aW9uYWwgZGVidWcgaW5kaWNhdG9yIChvbmx5IHZpc2libGUgaW4gZGVidWcgYW5kIHdpdGggdmlzdWFsIGZlZWRiYWNrIGVuYWJsZWQpCiAgICAgICAgICAgICAgaWYgKHdpZGdldC5lbmFibGVWaXN1YWxGZWVkYmFjayAmJiBfY3VycmVudFRhcENvdW50ID4gMCkgLi4uWwogICAgICAgICAgICAgICAgUG9zaXRpb25lZC5maWxsKAogICAgICAgICAgICAgICAgICBjaGlsZDogSWdub3JlUG9pbnRlcigKICAgICAgICAgICAgICAgICAgICBjaGlsZDogQ29udGFpbmVyKAogICAgICAgICAgICAgICAgICAgICAgZGVjb3JhdGlvbjogQm94RGVjb3JhdGlvbigKICAgICAgICAgICAgICAgICAgICAgICAgYm9yZGVyOiBCb3JkZXIuYWxsKAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yOiB0aGVtZS5jb2xvclNjaGVtZS5lcnJvci53aXRoVmFsdWVzKGFscGhhOiAwLjMpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHdpZHRoOiAyLAogICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgIGNoaWxkOiBDZW50ZXIoCiAgICAgICAgICAgICAgICAgICAgICAgIGNoaWxkOiBDb250YWluZXIoCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGFkZGluZzogY29uc3QgRWRnZUluc2V0cy5zeW1tZXRyaWMoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3Jpem9udGFsOiA4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGljYWw6IDQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBkZWNvcmF0aW9uOiBCb3hEZWNvcmF0aW9uKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I6IENvbG9ycy5yZWQud2l0aFZhbHVlcyhhbHBoYTogMC44KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJvcmRlclJhZGl1czogQm9yZGVyUmFkaXVzLmNpcmN1bGFyKDQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgY2hpbGQ6IFRleHQoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnJF9jdXJyZW50VGFwQ291bnQvJHt3aWRnZXQudGFwQ291bnR9JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0eWxlOiB0aGVtZS50ZXh0VGhlbWUubGFiZWxTbWFsbD8uY29weVdpdGgoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yOiB0aGVtZS5jb2xvclNjaGVtZS5vbkVycm9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb250V2VpZ2h0OiBGb250V2VpZ2h0LmJvbGQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgIF0sCiAgICAgICAgICAgIF0sCiAgICAgICAgICApLAogICAgICAgICk7CiAgICAgIH0sCiAgICApOwogIH0KfQo=", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/{{developer_dir}}/pubspec.yaml", + "data": + "bmFtZTogZGV2ZWxvcGVyCmRlc2NyaXB0aW9uOiAiSW4tYXBwIGRldmVsb3BlciB0b29scyBmb3Ige3tuYW1lLnRpdGxlQ2FzZSgpfX06IGxvZ3MsIGZsYWdzIGFuZCBlbnZpcm9ubWVudCBpbnNwZWN0aW9uLiIKdmVyc2lvbjogMC4xLjAKcHVibGlzaF90bzogJ25vbmUnCgplbnZpcm9ubWVudDoKICBzZGs6IF4zLjguMAogIGZsdXR0ZXI6ICI+PTMuMjkuMCIKCmRlcGVuZGVuY2llczoKICBmbHV0dGVyOgogICAgc2RrOiBmbHV0dGVyCiAgZ29fcm91dGVyOiBeMTUuMS4zCiAgdGFsa2VyX2ZsdXR0ZXI6IF40LjQuMQogIGNvcmU6CiAgICBwYXRoOiAuLi9jb3JlCiAgZGVzaWduX3N5c3RlbToKICAgIHBhdGg6IC4uL2Rlc2lnbl9zeXN0ZW0KICBkaToKICAgIHBhdGg6IC4uL2RpCiAgZmVhdHVyZV9mbGFnczoKICAgIHBhdGg6IC4uL2ZlYXR1cmVfZmxhZ3MKCmRldl9kZXBlbmRlbmNpZXM6CiAgZmx1dHRlcl90ZXN0OgogICAgc2RrOiBmbHV0dGVyCiAgZmx1dHRlcl9saW50czogXjUuMC4wCgpmbHV0dGVyOgo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{developer_dir}}/test/developer_test.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmRldmVsb3Blci9kZXZlbG9wZXIuZGFydCc7CmltcG9ydCAncGFja2FnZTpmbHV0dGVyL21hdGVyaWFsLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlcl90ZXN0L2ZsdXR0ZXJfdGVzdC5kYXJ0JzsKCnZvaWQgbWFpbigpIHsKICBncm91cCgnRGV2ZWxvcGVyIFBhY2thZ2UnLCAoKSB7CiAgICB0ZXN0KCdwYWNrYWdlIGV4cG9ydHMgYXJlIGF2YWlsYWJsZScsICgpIHsKICAgICAgZXhwZWN0KERldmVsb3BlclJvdXRlcy5kZXZlbG9wZXIsICcvZGV2ZWxvcGVyJyk7CiAgICB9KTsKCiAgICB0ZXN0V2lkZ2V0cygnRGVidWdHZXN0dXJlRGV0ZWN0b3IgZXhwb3J0cyBhcmUgYXZhaWxhYmxlJywgKHRlc3RlcikgYXN5bmMgewogICAgICAvLyBUZXN0IHRoYXQgdGhlIHdpZGdldCBjYW4gYmUgaW5zdGFudGlhdGVkCiAgICAgIGNvbnN0IHdpZGdldCA9IE9wZW5EZXZUb29sc1dyYXBwZXIoY2hpbGQ6IFRleHQoJ1Rlc3QnKSk7CgogICAgICBleHBlY3Qod2lkZ2V0LCBpc05vdE51bGwpOwogICAgICBleHBlY3Qod2lkZ2V0LnRhcENvdW50LCA1KTsgLy8gRGVmYXVsdCB2YWx1ZQogICAgICBleHBlY3Qod2lkZ2V0LnRhcFdpbmRvdywgY29uc3QgRHVyYXRpb24oc2Vjb25kczogMikpOyAvLyBEZWZhdWx0IHZhbHVlCiAgICAgIGV4cGVjdCh3aWRnZXQuZW5hYmxlSGFwdGljcywgdHJ1ZSk7IC8vIERlZmF1bHQgdmFsdWUKICAgICAgZXhwZWN0KHdpZGdldC5lbmFibGVWaXN1YWxGZWVkYmFjaywgZmFsc2UpOyAvLyBEZWZhdWx0IHZhbHVlCiAgICB9KTsKCiAgICB0ZXN0V2lkZ2V0cygnRGVidWdHZXN0dXJlRGV0ZWN0b3IgY3VzdG9tIHZhbHVlcyB3b3JrJywgKHRlc3RlcikgYXN5bmMgewogICAgICAvLyBUZXN0IGN1c3RvbSBjb25maWd1cmF0aW9uCiAgICAgIGNvbnN0IHdpZGdldCA9IE9wZW5EZXZUb29sc1dyYXBwZXIoCiAgICAgICAgdGFwQ291bnQ6IDcsCiAgICAgICAgdGFwV2luZG93OiBEdXJhdGlvbihzZWNvbmRzOiAyKSwKICAgICAgICBlbmFibGVIYXB0aWNzOiBmYWxzZSwKICAgICAgICBlbmFibGVWaXN1YWxGZWVkYmFjazogdHJ1ZSwKICAgICAgICBjaGlsZDogVGV4dCgnVGVzdCcpLAogICAgICApOwoKICAgICAgZXhwZWN0KHdpZGdldC50YXBDb3VudCwgNyk7CiAgICAgIGV4cGVjdCh3aWRnZXQudGFwV2luZG93LCBjb25zdCBEdXJhdGlvbihzZWNvbmRzOiAyKSk7CiAgICAgIGV4cGVjdCh3aWRnZXQuZW5hYmxlSGFwdGljcywgZmFsc2UpOwogICAgICBleHBlY3Qod2lkZ2V0LmVuYWJsZVZpc3VhbEZlZWRiYWNrLCB0cnVlKTsKICAgIH0pOwogIH0pOwp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/feature_flags.dart", + "data": + "bGlicmFyeTsKCmltcG9ydCAncGFja2FnZTpjb3JlL2xvZ2dlci9sb2dnZXIuZGFydCc7CmltcG9ydCAncGFja2FnZTpkaS9kaS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZlYXR1cmVfZmxhZ3Mvc3JjL2ZlYXR1cmVfZmxhZ19zZXJ2aWNlLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZmVhdHVyZV9mbGFncy9zcmMvcmVnaXN0ZXIuZGFydCc7CgpleHBvcnQgJ3NyYy9pbmRleC5kYXJ0JzsKCi8vLyBJbml0aWFsaXplIHRoZSBmZWF0dXJlIGZsYWdzIG1vZHVsZSBhbmQgcmVnaXN0ZXIgd2l0aCBESQpGdXR1cmU8dm9pZD4gaW5pdCh7CiAgRmVhdHVyZUZsYWdzQ29uZmlnIGNvbmZpZyA9IGNvbnN0IEZlYXR1cmVGbGFnc0NvbmZpZygKICAgIGZldGNoVGltZW91dDogRHVyYXRpb24oc2Vjb25kczogMTApLAogICAgbWluaW11bUZldGNoSW50ZXJ2YWw6IER1cmF0aW9uKG1pbnV0ZXM6IDMwKSwKICAgIGRlZmF1bHRQYXJhbWV0ZXJzOiB7fSwKICApLAp9KSBhc3luYyB7CiAgZmluYWwgbG9nZ2VyID0gZGkuZ2V0PExvZ2dlcj4oKTsKICB0cnkgewogICAgbG9nZ2VyLmkoJ2luaXQgZmVhdHVyZSBmbGFncyBtb2R1bGUgd2l0aCBGaXJlYmFzZSBSZW1vdGUgQ29uZmlnJyk7CiAgICBhd2FpdCByZWdpc3RlckZlYXR1cmVGbGFnc1dpdGhESShjb25maWc6IGNvbmZpZyk7CiAgICBmaW5hbCBzZXJ2aWNlID0gZGkuZ2V0PEZlYXR1cmVGbGFnPigpOwogICAgYXdhaXQgc2VydmljZS5pbml0KCk7CiAgICBsb2dnZXIuaSgnRmVhdHVyZSBmbGFncyBtb2R1bGUgaW5pdGlhbGl6ZWQgd2l0aCBGaXJlYmFzZSBSZW1vdGUgQ29uZmlnJyk7CiAgfSBjYXRjaCAoZSwgc3RhY2tUcmFjZSkgewogICAgbG9nZ2VyLmUoJ0ZhaWxlZCB0byBpbml0aWFsaXplIGZlYXR1cmUgZmxhZ3MgbW9kdWxlJywgZSwgc3RhY2tUcmFjZSk7CiAgICByZXRocm93OwogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/feature_flag_provider.dart", + "data": + "Ly8vIEFic3RyYWN0IGludGVyZmFjZSBmb3IgZmVhdHVyZSBmbGFnIHByb3ZpZGVycwphYnN0cmFjdCBjbGFzcyBGZWF0dXJlRmxhZ1Byb3ZpZGVyIHsKICAvLy8gSW5pdGlhbGl6ZSB0aGUgZmVhdHVyZSBmbGFnIHByb3ZpZGVyCiAgRnV0dXJlPHZvaWQ+IGluaXQoKTsKCiAgLy8vIEdldCBhIGJvb2xlYW4gZmVhdHVyZSBmbGFnIHZhbHVlCiAgRnV0dXJlPGJvb2w+IGdldEJvb2woU3RyaW5nIGtleSwge2Jvb2wgZGVmYXVsdFZhbHVlID0gZmFsc2V9KTsKCiAgLy8vIEdldCBhIHN0cmluZyBmZWF0dXJlIGZsYWcgdmFsdWUKICBGdXR1cmU8U3RyaW5nPiBnZXRTdHJpbmcoU3RyaW5nIGtleSwge1N0cmluZyBkZWZhdWx0VmFsdWUgPSAnJ30pOwoKICAvLy8gR2V0IGFuIGludGVnZXIgZmVhdHVyZSBmbGFnIHZhbHVlCiAgRnV0dXJlPGludD4gZ2V0SW50KFN0cmluZyBrZXksIHtpbnQgZGVmYXVsdFZhbHVlID0gMH0pOwoKICAvLy8gR2V0IGEgZG91YmxlIGZlYXR1cmUgZmxhZyB2YWx1ZQogIEZ1dHVyZTxkb3VibGU+IGdldERvdWJsZShTdHJpbmcga2V5LCB7ZG91YmxlIGRlZmF1bHRWYWx1ZSA9IDAuMH0pOwoKICAvLy8gQ2hlY2sgaWYgYSBmZWF0dXJlIGZsYWcgZXhpc3RzCiAgRnV0dXJlPGJvb2w+IGhhc0ZsYWcoU3RyaW5nIGtleSk7CgogIC8vLyBEaXNwb3NlIHJlc291cmNlcwogIHZvaWQgZGlzcG9zZSgpOwp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/feature_flag_service.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNvcmUvbG9nZ2VyL2xvZ2dlci5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZlYXR1cmVfZmxhZ3Mvc3JjL2ZlYXR1cmVfZmxhZ19wcm92aWRlci5kYXJ0JzsKCi8vLyBDb25maWd1cmF0aW9uIGNsYXNzIGZvciBmZWF0dXJlIGZsYWdzIGluaXRpYWxpemF0aW9uCmNsYXNzIEZlYXR1cmVGbGFnc0NvbmZpZyB7CiAgY29uc3QgRmVhdHVyZUZsYWdzQ29uZmlnKHsKICAgIHRoaXMuZmV0Y2hUaW1lb3V0LAogICAgdGhpcy5taW5pbXVtRmV0Y2hJbnRlcnZhbCwKICAgIHJlcXVpcmVkIHRoaXMuZGVmYXVsdFBhcmFtZXRlcnMsCiAgfSk7CgogIGZpbmFsIER1cmF0aW9uPyBmZXRjaFRpbWVvdXQ7CiAgZmluYWwgRHVyYXRpb24/IG1pbmltdW1GZXRjaEludGVydmFsOwogIGZpbmFsIE1hcDxTdHJpbmcsIGR5bmFtaWM+IGRlZmF1bHRQYXJhbWV0ZXJzOwp9CgovLy8gU2VydmljZSBmb3IgbWFuYWdpbmcgZmVhdHVyZSBmbGFncyBhY3Jvc3MgdGhlIGFwcGxpY2F0aW9uCmNsYXNzIEZlYXR1cmVGbGFnIHsKICBmaW5hbCBGZWF0dXJlRmxhZ1Byb3ZpZGVyIF9wcm92aWRlcjsKICBmaW5hbCBMb2dnZXIgX2xvZ2dlcjsKICBib29sIF9pbml0aWFsaXplZCA9IGZhbHNlOwoKICBGZWF0dXJlRmxhZyh7cmVxdWlyZWQgRmVhdHVyZUZsYWdQcm92aWRlciBwcm92aWRlciwgcmVxdWlyZWQgTG9nZ2VyIGxvZ2dlcn0pCiAgICA6IF9wcm92aWRlciA9IHByb3ZpZGVyLAogICAgICBfbG9nZ2VyID0gbG9nZ2VyOwoKICAvLy8gSW5pdGlhbGl6ZSB0aGUgZmVhdHVyZSBmbGFnIHNlcnZpY2UKICBGdXR1cmU8dm9pZD4gaW5pdCgpIGFzeW5jIHsKICAgIGlmIChfaW5pdGlhbGl6ZWQpIHsKICAgICAgX2xvZ2dlci53KCdGZWF0dXJlRmxhZyBhbHJlYWR5IGluaXRpYWxpemVkJyk7CiAgICAgIHJldHVybjsKICAgIH0KCiAgICB0cnkgewogICAgICBhd2FpdCBfcHJvdmlkZXIuaW5pdCgpOwogICAgICBfaW5pdGlhbGl6ZWQgPSB0cnVlOwogICAgICBfbG9nZ2VyLmkoJ0ZlYXR1cmVGbGFnIGluaXRpYWxpemVkIHN1Y2Nlc3NmdWxseScpOwogICAgfSBjYXRjaCAoZSwgc3RhY2tUcmFjZSkgewogICAgICBfbG9nZ2VyLmUoJ0ZhaWxlZCB0byBpbml0aWFsaXplIEZlYXR1cmVGbGFnJywgZSwgc3RhY2tUcmFjZSk7CiAgICAgIHJldGhyb3c7CiAgICB9CiAgfQoKICAvLy8gQ2hlY2sgaWYgc2VydmljZSBpcyBpbml0aWFsaXplZAogIGJvb2wgZ2V0IGlzSW5pdGlhbGl6ZWQgPT4gX2luaXRpYWxpemVkOwoKICAvLy8gR2V0IGEgYm9vbGVhbiBmZWF0dXJlIGZsYWcKICBGdXR1cmU8Ym9vbD4gaXNFbmFibGVkKFN0cmluZyBrZXksIHtib29sIGRlZmF1bHRWYWx1ZSA9IGZhbHNlfSkgYXN5bmMgewogICAgX2Vuc3VyZUluaXRpYWxpemVkKCk7CiAgICBmaW5hbCB2YWx1ZSA9IGF3YWl0IF9wcm92aWRlci5nZXRCb29sKGtleSwgZGVmYXVsdFZhbHVlOiBkZWZhdWx0VmFsdWUpOwogICAgX2xvZ2dlci5pKCdpc0VuYWJsZWQoJGtleSk6IFByb3ZpZGVyIHJldHVybmVkID0gJHZhbHVlJyk7CiAgICByZXR1cm4gdmFsdWU7CiAgfQoKICAvLy8gR2V0IGEgc3RyaW5nIGNvbmZpZ3VyYXRpb24gdmFsdWUKICBGdXR1cmU8U3RyaW5nPiBnZXRDb25maWcoU3RyaW5nIGtleSwge1N0cmluZyBkZWZhdWx0VmFsdWUgPSAnJ30pIGFzeW5jIHsKICAgIF9lbnN1cmVJbml0aWFsaXplZCgpOwoKICAgIGZpbmFsIHZhbHVlID0gYXdhaXQgX3Byb3ZpZGVyLmdldFN0cmluZyhrZXksIGRlZmF1bHRWYWx1ZTogZGVmYXVsdFZhbHVlKTsKICAgIHJldHVybiB2YWx1ZTsKICB9CgogIC8vLyBHZXQgYW4gaW50ZWdlciBjb25maWd1cmF0aW9uIHZhbHVlCiAgRnV0dXJlPGludD4gZ2V0SW50Q29uZmlnKFN0cmluZyBrZXksIHtpbnQgZGVmYXVsdFZhbHVlID0gMH0pIGFzeW5jIHsKICAgIF9lbnN1cmVJbml0aWFsaXplZCgpOwoKICAgIGZpbmFsIHZhbHVlID0gYXdhaXQgX3Byb3ZpZGVyLmdldEludChrZXksIGRlZmF1bHRWYWx1ZTogZGVmYXVsdFZhbHVlKTsKICAgIHJldHVybiB2YWx1ZTsKICB9CgogIC8vLyBHZXQgYSBkb3VibGUgY29uZmlndXJhdGlvbiB2YWx1ZQogIEZ1dHVyZTxkb3VibGU+IGdldERvdWJsZUNvbmZpZygKICAgIFN0cmluZyBrZXksIHsKICAgIGRvdWJsZSBkZWZhdWx0VmFsdWUgPSAwLjAsCiAgfSkgYXN5bmMgewogICAgX2Vuc3VyZUluaXRpYWxpemVkKCk7CgogICAgZmluYWwgdmFsdWUgPSBhd2FpdCBfcHJvdmlkZXIuZ2V0RG91YmxlKGtleSwgZGVmYXVsdFZhbHVlOiBkZWZhdWx0VmFsdWUpOwogICAgcmV0dXJuIHZhbHVlOwogIH0KCiAgLy8vIERpc3Bvc2UgdGhlIHNlcnZpY2UKICB2b2lkIGRpc3Bvc2UoKSB7CiAgICBfcHJvdmlkZXIuZGlzcG9zZSgpOwogICAgX2luaXRpYWxpemVkID0gZmFsc2U7CiAgICBfbG9nZ2VyLmkoJ0ZlYXR1cmVGbGFnIGRpc3Bvc2VkJyk7CiAgfQoKICAvLy8gRW5zdXJlIHRoZSBzZXJ2aWNlIGlzIGluaXRpYWxpemVkCiAgdm9pZCBfZW5zdXJlSW5pdGlhbGl6ZWQoKSB7CiAgICBpZiAoIV9pbml0aWFsaXplZCkgewogICAgICB0aHJvdyBTdGF0ZUVycm9yKCdGZWF0dXJlRmxhZyBub3QgaW5pdGlhbGl6ZWQuIENhbGwgaW5pdGlhbGl6ZSgpIGZpcnN0LicpOwogICAgfQogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/feature_flag_wrapper.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmRpL2RpLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZmVhdHVyZV9mbGFncy9zcmMvZmVhdHVyZV9mbGFnX3NlcnZpY2UuZGFydCc7CmltcG9ydCAncGFja2FnZTpmbHV0dGVyL21hdGVyaWFsLmRhcnQnOwoKLy8vIEEgd3JhcHBlciB3aWRnZXQgdGhhdCBjb25kaXRpb25hbGx5IHJlbmRlcnMgY29udGVudCBiYXNlZCBvbiBmZWF0dXJlIGZsYWdzCmNsYXNzIEZlYXR1cmVGbGFnV3JhcHBlciBleHRlbmRzIFN0YXRlbGVzc1dpZGdldCB7CiAgY29uc3QgRmVhdHVyZUZsYWdXcmFwcGVyKHsKICAgIHN1cGVyLmtleSwKICAgIHJlcXVpcmVkIHRoaXMuZmxhZ0tleSwKICAgIHJlcXVpcmVkIHRoaXMuYnVpbGRlciwKICAgIHRoaXMuZGVmYXVsdFZhbHVlID0gZmFsc2UsCiAgICB0aGlzLmxvYWRpbmcsCiAgfSk7CgogIC8vLyBUaGUgZmVhdHVyZSBmbGFnIGtleSB0byBjaGVjawogIGZpbmFsIFN0cmluZyBmbGFnS2V5OwoKICAvLy8gQnVpbGRlciBmdW5jdGlvbiB0aGF0IHJlY2VpdmVzIHRoZSBjb250ZXh0IGFuZCBmZWF0dXJlIGZsYWcgdmFsdWUKICBmaW5hbCBXaWRnZXQgRnVuY3Rpb24oQnVpbGRDb250ZXh0IGNvbnRleHQsIGJvb2wgdmFsdWUpIGJ1aWxkZXI7CgogIC8vLyBUaGUgZGVmYXVsdCB2YWx1ZSB0byB1c2UgaWYgdGhlIGZlYXR1cmUgZmxhZyBzZXJ2aWNlIGlzIHVuYXZhaWxhYmxlCiAgZmluYWwgYm9vbCBkZWZhdWx0VmFsdWU7CgogIC8vLyBXaWRnZXQgdG8gc2hvdyB3aGlsZSBsb2FkaW5nIHRoZSBmZWF0dXJlIGZsYWcgdmFsdWUKICBmaW5hbCBXaWRnZXQ/IGxvYWRpbmc7CgogIEBvdmVycmlkZQogIFdpZGdldCBidWlsZChCdWlsZENvbnRleHQgY29udGV4dCkgewogICAgcmV0dXJuIEZ1dHVyZUJ1aWxkZXI8Ym9vbD4oCiAgICAgIGZ1dHVyZTogX2NoZWNrRmVhdHVyZUZsYWcoKSwKICAgICAgYnVpbGRlcjogKGNvbnRleHQsIHNuYXBzaG90KSB7CiAgICAgICAgLy8gU2hvdyBsb2FkaW5nIHdpZGdldCB3aGlsZSB3YWl0aW5nIGZvciBmZWF0dXJlIGZsYWcKICAgICAgICBpZiAoc25hcHNob3QuY29ubmVjdGlvblN0YXRlID09IENvbm5lY3Rpb25TdGF0ZS53YWl0aW5nKSB7CiAgICAgICAgICByZXR1cm4gbG9hZGluZyA/PyBjb25zdCBTaXplZEJveC5zaHJpbmsoKTsKICAgICAgICB9CgogICAgICAgIC8vIEdldCB0aGUgZmVhdHVyZSBmbGFnIHZhbHVlLCBkZWZhdWx0aW5nIHRvIGZhbHNlIGlmIHRoZXJlJ3MgYW4gZXJyb3IKICAgICAgICBmaW5hbCBpc0VuYWJsZWQgPSBzbmFwc2hvdC5kYXRhID8/IGRlZmF1bHRWYWx1ZTsKCiAgICAgICAgLy8gUmV0dXJuIHRoZSB3aWRnZXQgYnVpbHQgYnkgdGhlIGJ1aWxkZXIgZnVuY3Rpb24KICAgICAgICByZXR1cm4gYnVpbGRlcihjb250ZXh0LCBpc0VuYWJsZWQpOwogICAgICB9LAogICAgKTsKICB9CgogIEZ1dHVyZTxib29sPiBfY2hlY2tGZWF0dXJlRmxhZygpIGFzeW5jIHsKICAgIHRyeSB7CiAgICAgIGZpbmFsIGZlYXR1cmVGbGFnID0gZGkuZ2V0PEZlYXR1cmVGbGFnPigpOwogICAgICByZXR1cm4gYXdhaXQgZmVhdHVyZUZsYWcuaXNFbmFibGVkKGZsYWdLZXksIGRlZmF1bHRWYWx1ZTogZGVmYXVsdFZhbHVlKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgLy8gSWYgdGhlcmUncyBhbiBlcnJvciBhY2Nlc3NpbmcgdGhlIGZlYXR1cmUgZmxhZyBzZXJ2aWNlLAogICAgICAvLyByZXR1cm4gdGhlIGRlZmF1bHQgdmFsdWUKICAgICAgcmV0dXJuIGRlZmF1bHRWYWx1ZTsKICAgIH0KICB9Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/firebase_remote_config_provider.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNvcmUvbG9nZ2VyL2xvZ2dlci5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRpL2RpLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZmVhdHVyZV9mbGFncy9mZWF0dXJlX2ZsYWdzLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZmlyZWJhc2VfcmVtb3RlX2NvbmZpZy9maXJlYmFzZV9yZW1vdGVfY29uZmlnLmRhcnQnOwoKLy8vIEZpcmViYXNlIFJlbW90ZSBDb25maWcgaW1wbGVtZW50YXRpb24gb2YgRmVhdHVyZUZsYWdQcm92aWRlcgpjbGFzcyBGaXJlYmFzZVJlbW90ZUNvbmZpZ1Byb3ZpZGVyIGltcGxlbWVudHMgRmVhdHVyZUZsYWdQcm92aWRlciB7CiAgbGF0ZSBmaW5hbCBGaXJlYmFzZVJlbW90ZUNvbmZpZyBfcmVtb3RlQ29uZmlnOwogIGZpbmFsIEZlYXR1cmVGbGFnc0NvbmZpZyBfY29uZmlnOwogIGxhdGUgZmluYWwgTG9nZ2VyIGxvZ2dlcjsKCiAgRmlyZWJhc2VSZW1vdGVDb25maWdQcm92aWRlcih7RmVhdHVyZUZsYWdzQ29uZmlnPyBjb25maWd9KQogICAgOiBfY29uZmlnID0KICAgICAgICAgIGNvbmZpZyA/PwogICAgICAgICAgY29uc3QgRmVhdHVyZUZsYWdzQ29uZmlnKAogICAgICAgICAgICBkZWZhdWx0UGFyYW1ldGVyczoge30sCiAgICAgICAgICAgIGZldGNoVGltZW91dDogRHVyYXRpb24oc2Vjb25kczogMTApLAogICAgICAgICAgICBtaW5pbXVtRmV0Y2hJbnRlcnZhbDogRHVyYXRpb24oaG91cnM6IDEpLAogICAgICAgICAgKSB7CiAgICBpZiAoZGkuaGFzPExvZ2dlcj4oKSkgewogICAgICBsb2dnZXIgPSBkaS5nZXQ8TG9nZ2VyPigpOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IGluaXQoKSBhc3luYyB7CiAgICB0cnkgewogICAgICBfcmVtb3RlQ29uZmlnID0gRmlyZWJhc2VSZW1vdGVDb25maWcuaW5zdGFuY2U7CgogICAgICAvLyBDb25maWd1cmUgc2V0dGluZ3MKICAgICAgYXdhaXQgX3JlbW90ZUNvbmZpZy5zZXRDb25maWdTZXR0aW5ncygKICAgICAgICBSZW1vdGVDb25maWdTZXR0aW5ncygKICAgICAgICAgIGZldGNoVGltZW91dDogX2NvbmZpZy5mZXRjaFRpbWVvdXQgPz8gY29uc3QgRHVyYXRpb24oc2Vjb25kczogMTApLAogICAgICAgICAgbWluaW11bUZldGNoSW50ZXJ2YWw6CiAgICAgICAgICAgICAgX2NvbmZpZy5taW5pbXVtRmV0Y2hJbnRlcnZhbCA/PyBjb25zdCBEdXJhdGlvbihob3VyczogMSksCiAgICAgICAgKSwKICAgICAgKTsKCiAgICAgIC8vIFNldCBkZWZhdWx0IHZhbHVlcwogICAgICBhd2FpdCBfc2V0RGVmYXVsdHMoKTsKCiAgICAgIC8vIEZldGNoIGFuZCBhY3RpdmF0ZQogICAgICBmaW5hbCBhY3RpdmF0ZWQgPSBhd2FpdCBfcmVtb3RlQ29uZmlnLmZldGNoQW5kQWN0aXZhdGUoKTsKCiAgICAgIGxvZ2dlci5pKCdGaXJlYmFzZSBSZW1vdGUgQ29uZmlnIGluaXRpYWxpemVkIHN1Y2Nlc3NmdWxseScpOwogICAgICBsb2dnZXIuaSgnUmVtb3RlIENvbmZpZyBhY3RpdmF0ZWQ6ICRhY3RpdmF0ZWQnKTsKICAgICAgbG9nZ2VyLmkoJ0xhc3QgZmV0Y2ggc3RhdHVzOiAke19yZW1vdGVDb25maWcubGFzdEZldGNoU3RhdHVzfScpOwogICAgICBsb2dnZXIuaSgnTGFzdCBmZXRjaCB0aW1lOiAke19yZW1vdGVDb25maWcubGFzdEZldGNoVGltZX0nKTsKCiAgICAgIC8vIExvZyBhbGwgY3VycmVudCB2YWx1ZXMgZm9yIGRlYnVnZ2luZwogICAgICBmaW5hbCBhbGxWYWx1ZXMgPSBfcmVtb3RlQ29uZmlnLmdldEFsbCgpOwogICAgICBmb3IgKGZpbmFsIGVudHJ5IGluIGFsbFZhbHVlcy5lbnRyaWVzKSB7CiAgICAgICAgbG9nZ2VyLmkoJ1JlbW90ZSBDb25maWcgLSAke2VudHJ5LmtleX06ICR7ZW50cnkudmFsdWUuYXNTdHJpbmcoKX0nKTsKICAgICAgfQogICAgfSBjYXRjaCAoZSwgc3RhY2tUcmFjZSkgewogICAgICBsb2dnZXIuZSgnRmFpbGVkIHRvIGluaXRpYWxpemUgRmlyZWJhc2UgUmVtb3RlIENvbmZpZycsIGUsIHN0YWNrVHJhY2UpOwogICAgICByZXRocm93OwogICAgfQogIH0KCiAgRnV0dXJlPHZvaWQ+IF9zZXREZWZhdWx0cygpIGFzeW5jIHsKICAgIGF3YWl0IF9yZW1vdGVDb25maWcuc2V0RGVmYXVsdHMoX2NvbmZpZy5kZWZhdWx0UGFyYW1ldGVycyk7CiAgfQoKICBAb3ZlcnJpZGUKICBGdXR1cmU8Ym9vbD4gZ2V0Qm9vbChTdHJpbmcga2V5LCB7Ym9vbCBkZWZhdWx0VmFsdWUgPSBmYWxzZX0pIGFzeW5jIHsKICAgIHRyeSB7CiAgICAgIGZpbmFsIHZhbHVlID0gX3JlbW90ZUNvbmZpZy5nZXRCb29sKGtleSk7CiAgICAgIGZpbmFsIGNvbmZpZ1ZhbHVlID0gX3JlbW90ZUNvbmZpZy5nZXRWYWx1ZShrZXkpOwogICAgICBsb2dnZXIuaSgKICAgICAgICAnR2V0dGluZyBib29sIGZsYWcgIiRrZXkiOiB2YWx1ZT0kdmFsdWUsJwogICAgICAgICcgc291cmNlPSR7Y29uZmlnVmFsdWUuc291cmNlfScsCiAgICAgICk7CiAgICAgIHJldHVybiB2YWx1ZTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgbG9nZ2VyLmUoCiAgICAgICAgJ0ZhaWxlZCB0byBnZXQgYm9vbCBmbGFnOiAka2V5LCB1c2luZyBkZWZhdWx0OiAkZGVmYXVsdFZhbHVlJywKICAgICAgICBlLAogICAgICApOwogICAgICByZXR1cm4gZGVmYXVsdFZhbHVlOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPFN0cmluZz4gZ2V0U3RyaW5nKFN0cmluZyBrZXksIHtTdHJpbmcgZGVmYXVsdFZhbHVlID0gJyd9KSBhc3luYyB7CiAgICB0cnkgewogICAgICBmaW5hbCB2YWx1ZSA9IF9yZW1vdGVDb25maWcuZ2V0U3RyaW5nKGtleSk7CiAgICAgIHJldHVybiB2YWx1ZS5pc0VtcHR5ID8gZGVmYXVsdFZhbHVlIDogdmFsdWU7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIGxvZ2dlci53KCdGYWlsZWQgdG8gZ2V0IHN0cmluZyBmbGFnOiAka2V5LCB1c2luZyBkZWZhdWx0OiAkZGVmYXVsdFZhbHVlJyk7CiAgICAgIHJldHVybiBkZWZhdWx0VmFsdWU7CiAgICB9CiAgfQoKICBAb3ZlcnJpZGUKICBGdXR1cmU8aW50PiBnZXRJbnQoU3RyaW5nIGtleSwge2ludCBkZWZhdWx0VmFsdWUgPSAwfSkgYXN5bmMgewogICAgdHJ5IHsKICAgICAgcmV0dXJuIF9yZW1vdGVDb25maWcuZ2V0SW50KGtleSk7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIGxvZ2dlci53KCdGYWlsZWQgdG8gZ2V0IGludCBmbGFnOiAka2V5LCB1c2luZyBkZWZhdWx0OiAkZGVmYXVsdFZhbHVlJyk7CiAgICAgIHJldHVybiBkZWZhdWx0VmFsdWU7CiAgICB9CiAgfQoKICBAb3ZlcnJpZGUKICBGdXR1cmU8ZG91YmxlPiBnZXREb3VibGUoU3RyaW5nIGtleSwge2RvdWJsZSBkZWZhdWx0VmFsdWUgPSAwLjB9KSBhc3luYyB7CiAgICB0cnkgewogICAgICByZXR1cm4gX3JlbW90ZUNvbmZpZy5nZXREb3VibGUoa2V5KTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgbG9nZ2VyLncoJ0ZhaWxlZCB0byBnZXQgZG91YmxlIGZsYWc6ICRrZXksIHVzaW5nIGRlZmF1bHQ6ICRkZWZhdWx0VmFsdWUnKTsKICAgICAgcmV0dXJuIGRlZmF1bHRWYWx1ZTsKICAgIH0KICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTxib29sPiBoYXNGbGFnKFN0cmluZyBrZXkpIGFzeW5jIHsKICAgIHRyeSB7CiAgICAgIGZpbmFsIHZhbHVlID0gX3JlbW90ZUNvbmZpZy5nZXRWYWx1ZShrZXkpOwogICAgICByZXR1cm4gdmFsdWUuc291cmNlICE9IFZhbHVlU291cmNlLnZhbHVlU3RhdGljOwogICAgfSBjYXRjaCAoZSkgewogICAgICBsb2dnZXIudygnRmFpbGVkIHRvIGNoZWNrIGlmIGZsYWcgZXhpc3RzOiAka2V5Jyk7CiAgICAgIHJldHVybiBmYWxzZTsKICAgIH0KICB9CgogIEBvdmVycmlkZQogIHZvaWQgZGlzcG9zZSgpIHsKICAgIC8vIEZpcmViYXNlIFJlbW90ZSBDb25maWcgZG9lc24ndCByZXF1aXJlIGV4cGxpY2l0IGRpc3Bvc2FsCiAgICBsb2dnZXIuaSgnRmlyZWJhc2UgUmVtb3RlIENvbmZpZyBwcm92aWRlciBkaXNwb3NlZCcpOwogIH0KCiAgLy8vIEdldCB0aGUgbGFzdCBmZXRjaCB0aW1lCiAgRGF0ZVRpbWUgZ2V0IGxhc3RGZXRjaFRpbWUgPT4gX3JlbW90ZUNvbmZpZy5sYXN0RmV0Y2hUaW1lOwoKICAvLy8gR2V0IHRoZSBsYXN0IGZldGNoIHN0YXR1cwogIFJlbW90ZUNvbmZpZ0ZldGNoU3RhdHVzIGdldCBsYXN0RmV0Y2hTdGF0dXMgPT4gX3JlbW90ZUNvbmZpZy5sYXN0RmV0Y2hTdGF0dXM7CgogIC8vLyBBZGQgYSBsaXN0ZW5lciBmb3IgY29uZmlnIHVwZGF0ZXMKICBTdHJlYW08UmVtb3RlQ29uZmlnVXBkYXRlPiBnZXQgb25Db25maWdVcGRhdGVkID0+CiAgICAgIF9yZW1vdGVDb25maWcub25Db25maWdVcGRhdGVkOwoKICAvLy8gR2V0IHRoZSBjdXJyZW50IGNvbmZpZ3VyYXRpb24KICBGZWF0dXJlRmxhZ3NDb25maWcgZ2V0IGNvbmZpZyA9PiBfY29uZmlnOwp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/index.dart", + "data": + "ZXhwb3J0ICdmZWF0dXJlX2ZsYWdfcHJvdmlkZXIuZGFydCc7CmV4cG9ydCAnZmVhdHVyZV9mbGFnX3NlcnZpY2UuZGFydCc7CmV4cG9ydCAnZmVhdHVyZV9mbGFnX3dyYXBwZXIuZGFydCc7CmV4cG9ydCAnZmlyZWJhc2VfcmVtb3RlX2NvbmZpZ19wcm92aWRlci5kYXJ0JzsKZXhwb3J0ICdyZWdpc3Rlci5kYXJ0JzsK", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/lib/src/register.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNvcmUvbG9nZ2VyL2xvZ2dlci5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRpL2RpLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZmVhdHVyZV9mbGFncy9zcmMvZmVhdHVyZV9mbGFnX3Byb3ZpZGVyLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZmVhdHVyZV9mbGFncy9zcmMvZmVhdHVyZV9mbGFnX3NlcnZpY2UuZGFydCc7CmltcG9ydCAncGFja2FnZTpmZWF0dXJlX2ZsYWdzL3NyYy9maXJlYmFzZV9yZW1vdGVfY29uZmlnX3Byb3ZpZGVyLmRhcnQnOwoKLy8vIFJlZ2lzdGVyIGZlYXR1cmUgZmxhZ3Mgc2VydmljZXMgd2l0aCBkZXBlbmRlbmN5IGluamVjdGlvbgovLy8KRnV0dXJlPHZvaWQ+IHJlZ2lzdGVyRmVhdHVyZUZsYWdzV2l0aERJKHtGZWF0dXJlRmxhZ3NDb25maWc/IGNvbmZpZ30pIGFzeW5jIHsKICBmaW5hbCBsb2dnZXIgPSBkaS5nZXQ8TG9nZ2VyPigpOwoKICAvLyBSZWdpc3RlciB0aGUgcHJvdmlkZXIKICBmaW5hbCBwcm92aWRlciA9IEZpcmViYXNlUmVtb3RlQ29uZmlnUHJvdmlkZXIoY29uZmlnOiBjb25maWcpOwogIGRpLnJlZ2lzdGVyPEZlYXR1cmVGbGFnUHJvdmlkZXI+KHByb3ZpZGVyLCBkaXNwb3NlOiAocCkgPT4gcC5kaXNwb3NlKCkpOwoKICAvLyBSZWdpc3RlciB0aGUgc2VydmljZQogIGZpbmFsIHNlcnZpY2UgPSBGZWF0dXJlRmxhZyhwcm92aWRlcjogcHJvdmlkZXIsIGxvZ2dlcjogbG9nZ2VyKTsKICBkaS5yZWdpc3RlcjxGZWF0dXJlRmxhZz4oc2VydmljZSwgZGlzcG9zZTogKHMpID0+IHMuZGlzcG9zZSgpKTsKfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/pubspec.yaml", + "data": + "bmFtZTogZmVhdHVyZV9mbGFncwpkZXNjcmlwdGlvbjogIlJlbW90ZS1jb25maWcgYmFja2VkIGZlYXR1cmUgZmxhZ3MgZm9yIHt7bmFtZS50aXRsZUNhc2UoKX19LiIKdmVyc2lvbjogMC4xLjAKcHVibGlzaF90bzogJ25vbmUnCgplbnZpcm9ubWVudDoKICBzZGs6IF4zLjguMAogIGZsdXR0ZXI6ICI+PTMuMjkuMCIKCmRlcGVuZGVuY2llczoKICBmbHV0dGVyOgogICAgc2RrOiBmbHV0dGVyCiAgZmlyZWJhc2VfY29yZTogXjMuMTQuMAogIGZpcmViYXNlX3JlbW90ZV9jb25maWc6IF41LjEuNgogIGNvcmU6CiAgICBwYXRoOiAuLi9jb3JlCiAgZGk6CiAgICBwYXRoOiAuLi9kaQoKZGV2X2RlcGVuZGVuY2llczoKICBmbHV0dGVyX3Rlc3Q6CiAgICBzZGs6IGZsdXR0ZXIKICBmbHV0dGVyX2xpbnRzOiBeNS4wLjAKCmZsdXR0ZXI6Cg==", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/{{feature_flags_dir}}/README.md", + "data": + "IyBGZWF0dXJlIEZsYWdzCgpGZWF0dXJlIGZsYWdzIGFuZCByZW1vdGUgY29uZmlndXJhdGlvbiBtYW5hZ2VtZW50IGZvciB7e25hbWUudGl0bGVDYXNlKCl9fQoKWyFbbm9uc3RvcF9jbGldKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2Uvc3RhcnRlZCUyMHdpdGgtbm9uc3RvcF9jbGktMTY2QzRFLnN2Zz9zdHlsZT1mbGF0LXNxdWFyZSldKGh0dHBzOi8vcHViLmRldi9wYWNrYWdlcy9ub25zdG9wX2NsaSkKWyFbbWVsb3NdKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2UvbWFpbnRhaW5lZCUyMHdpdGgtbWVsb3MtZjcwMGZmLnN2Zz9zdHlsZT1mbGF0LXNxdWFyZSldKGh0dHBzOi8vZ2l0aHViLmNvbS9pbnZlcnRhc2UvbWVsb3MpCgojIyBJbnN0YWxsYXRpb24g8J+SuwoKKirinZcgSW4gb3JkZXIgdG8gc3RhcnQgdXNpbmcgRmVhdHVyZSBGbGFncyB5b3UgbXVzdCBoYXZlIHRoZSBbRmx1dHRlciBTREtdW2ZsdXR0ZXJfaW5zdGFsbF9saW5rXSBpbnN0YWxsZWQgb24geW91ciBtYWNoaW5lLioqCgpJbnN0YWxsIHZpYSBgZGFydCBwdWIgYWRkYDoKCmBgYHNoCmRhcnQgcHViIGFkZCBmZWF0dXJlX2ZsYWdzCmBgYAoKW2ZsdXR0ZXJfaW5zdGFsbF9saW5rXTogaHR0cHM6Ly9kb2NzLmZsdXR0ZXIuZGV2L2dldC1zdGFydGVkL2luc3RhbGw=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/network.dart", + "data": + "bGlicmFyeTsKCmltcG9ydCAncGFja2FnZTpjb3JlL2NvcmUuZGFydCc7CmltcG9ydCAncGFja2FnZTpkaS9kaS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOm5ldHdvcmsvc3JjL2F1dGgvaW5kZXguZGFydCc7CmltcG9ydCAncGFja2FnZTpuZXR3b3JrL3NyYy9jbGllbnQvaW5kZXguZGFydCc7CmltcG9ydCAncGFja2FnZTpuZXR3b3JrL3NyYy9jb25maWcvaW5kZXguZGFydCc7CgpleHBvcnQgJ3NyYy9hcGkvaW5kZXguZGFydCc7CmV4cG9ydCAnc3JjL2F1dGgvaW5kZXguZGFydCc7CmV4cG9ydCAnc3JjL2NsaWVudC9pbmRleC5kYXJ0JzsKZXhwb3J0ICdzcmMvY29uZmlnL2luZGV4LmRhcnQnOwpleHBvcnQgJ3NyYy9leGNlcHRpb25zL2luZGV4LmRhcnQnOwpleHBvcnQgJ3NyYy9pbnRlcmNlcHRvcnMvaW5kZXguZGFydCc7CmV4cG9ydCAnc3JjL21vZGVscy9pbmRleC5kYXJ0JzsKZXhwb3J0ICdzcmMvdXRpbHMvaW5kZXguZGFydCc7CgpGdXR1cmU8dm9pZD4gaW5pdCh7CiAgcmVxdWlyZWQgTmV0d29ya0NvbmZpZyBjb25maWcsCiAgYm9vbCB1c2VBdXRoZW50aWNhdGlvbiA9IHRydWUsCn0pIGFzeW5jIHsKICBhd2FpdCByZWdpc3Rlck5ldHdvcmtXaXRoREkoY29uZmlnLCB1c2VBdXRoZW50aWNhdGlvbjogdXNlQXV0aGVudGljYXRpb24pOwogIGZpbmFsIGxvZ2dlciA9IGRpLmdldDxMb2dnZXI+KCk7CiAgbG9nZ2VyLmkoJ05ldHdvcmsgbW9kdWxlIGluaXRpYWxpemVkIHdpdGggYmFzZSBVUkw6ICR7Y29uZmlnLmJhc2VVcmx9Jyk7Cn0KCkZ1dHVyZTx2b2lkPiByZWdpc3Rlck5ldHdvcmtXaXRoREkoCiAgTmV0d29ya0NvbmZpZyBjb25maWcsIHsKICBib29sIHVzZUF1dGhlbnRpY2F0aW9uID0gdHJ1ZSwKfSkgYXN5bmMgewogIGZpbmFsIGxvZ2dlciA9IGRpLmdldDxMb2dnZXI+KCk7CgogIC8vIFRyeSB0byBnZXQgQXV0aFRva2VuUHJvdmlkZXIgZnJvbSBESSBpZiBhdXRoZW50aWNhdGlvbiBpcyBlbmFibGVkCiAgQXV0aFRva2VuUHJvdmlkZXI/IGF1dGhUb2tlblByb3ZpZGVyOwogIGlmICh1c2VBdXRoZW50aWNhdGlvbiAmJiBjb25maWcuYXV0aFRva2VuUHJvdmlkZXIgPT0gbnVsbCkgewogICAgdHJ5IHsKICAgICAgYXV0aFRva2VuUHJvdmlkZXIgPSBkaS5nZXQ8QXV0aFRva2VuUHJvdmlkZXI+KCk7CiAgICAgIGxvZ2dlci5kKCfwn5SQIEZvdW5kIEF1dGhUb2tlblByb3ZpZGVyIGluIERJLCB1c2luZyBmb3IgYXV0aGVudGljYXRpb24nKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgbG9nZ2VyLmQoCiAgICAgICAgJ/CflJMgTm8gQXV0aFRva2VuUHJvdmlkZXIgZm91bmQgaW4gREksIGNyZWF0aW5nIHVuYXV0aGVudGljYXRlZCBjbGllbnQnLAogICAgICApOwogICAgfQogIH0gZWxzZSBpZiAoY29uZmlnLmF1dGhUb2tlblByb3ZpZGVyICE9IG51bGwpIHsKICAgIGF1dGhUb2tlblByb3ZpZGVyID0gY29uZmlnLmF1dGhUb2tlblByb3ZpZGVyOwogICAgbG9nZ2VyLmQoJ/CflJAgVXNpbmcgcHJvdmlkZWQgQXV0aFRva2VuUHJvdmlkZXInKTsKICB9CgogIC8vIENyZWF0ZSBuZXR3b3JrIGNvbmZpZyB3aXRoIGF1dGggcHJvdmlkZXIgaWYgYXZhaWxhYmxlCiAgbGF0ZSBmaW5hbCBOZXR3b3JrQ29uZmlnIGZpbmFsQ29uZmlnOwogIGlmIChhdXRoVG9rZW5Qcm92aWRlciAhPSBudWxsICYmIGNvbmZpZy5hdXRoVG9rZW5Qcm92aWRlciA9PSBudWxsKSB7CiAgICAvLyBDcmVhdGUgYSBuZXcgY29uZmlnIHdpdGggdGhlIGF1dGggcHJvdmlkZXIKICAgIGZpbmFsQ29uZmlnID0gRGVmYXVsdE5ldHdvcmtDb25maWcoCiAgICAgIGJhc2VVcmw6IGNvbmZpZy5iYXNlVXJsLAogICAgICBjb25uZWN0VGltZW91dDogY29uZmlnLmNvbm5lY3RUaW1lb3V0LAogICAgICByZWNlaXZlVGltZW91dDogY29uZmlnLnJlY2VpdmVUaW1lb3V0LAogICAgICBzZW5kVGltZW91dDogY29uZmlnLnNlbmRUaW1lb3V0LAogICAgICBkZWZhdWx0SGVhZGVyczogY29uZmlnLmRlZmF1bHRIZWFkZXJzLAogICAgICBlbmFibGVMb2dnaW5nOiBjb25maWcuZW5hYmxlTG9nZ2luZywKICAgICAgYXV0aFRva2VuUHJvdmlkZXI6IGF1dGhUb2tlblByb3ZpZGVyLAogICAgKTsKICB9IGVsc2UgewogICAgZmluYWxDb25maWcgPSBjb25maWc7CiAgfQoKICBmaW5hbCBuZXR3b3JrQ2xpZW50ID0gRGlvTmV0d29ya0NsaWVudChmaW5hbENvbmZpZyk7CiAgZGkucmVnaXN0ZXI8TmV0d29ya0NsaWVudD4oCiAgICBuZXR3b3JrQ2xpZW50LAogICAgZGlzcG9zZTogKGNsaWVudCkgPT4gY2xpZW50LmRpc3Bvc2UoKSwKICApOwogIGRpLnJlZ2lzdGVyPE5ldHdvcmtDb25maWc+KGZpbmFsQ29uZmlnKTsKCiAgaWYgKGZpbmFsQ29uZmlnLmF1dGhUb2tlblByb3ZpZGVyICE9IG51bGwpIHsKICAgIGxvZ2dlci5pKCfwn5SQIE5ldHdvcmsgY2xpZW50IHJlZ2lzdGVyZWQgd2l0aCBhdXRoZW50aWNhdGlvbiBzdXBwb3J0Jyk7CiAgfSBlbHNlIHsKICAgIGxvZ2dlci5pKCfwn5STIE5ldHdvcmsgY2xpZW50IHJlZ2lzdGVyZWQgd2l0aG91dCBhdXRoZW50aWNhdGlvbicpOwogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/api/index.dart", + "data": + "Ly9HRU5FUkFURUQgQkFSUkVMIEZJTEUKZXhwb3J0ICduZXR3b3JrX2FwaS5kYXJ0JzsK", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/api/network_api.dart", + "data": + "YWJzdHJhY3QgY2xhc3MgQXBpVXJsQ29uZmlnIHsKICBmaW5hbCBTdHJpbmcgcGF0aFByZWZpeDsKCiAgY29uc3QgQXBpVXJsQ29uZmlnKHtyZXF1aXJlZCB0aGlzLnBhdGhQcmVmaXh9KTsKfQoKYWJzdHJhY3QgY2xhc3MgTmV0d29ya0FwaTxUIGV4dGVuZHMgQXBpVXJsQ29uZmlnPiB7CiAgZmluYWwgVCB1cmxDb25maWc7CgogIGNvbnN0IE5ldHdvcmtBcGkoe3JlcXVpcmVkIHRoaXMudXJsQ29uZmlnfSk7CgogIE1hcDxTdHJpbmcsIFN0cmluZz4gZ2V0IGhlYWRlcnM7Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/auth/auth_token_provider.dart", + "data": + "YWJzdHJhY3QgY2xhc3MgQXV0aFRva2VuUHJvdmlkZXIgewogIC8vLyBHZXRzIGEgdmFsaWQgYXV0aGVudGljYXRpb24gdG9rZW4KICAvLy8gUmV0dXJucyBudWxsIGlmIHVzZXIgaXMgbm90IGF1dGhlbnRpY2F0ZWQKICBGdXR1cmU8U3RyaW5nPz4gZ2V0VmFsaWRUb2tlbigpOwoKICAvLy8gR2V0cyB0aGUgY3VycmVudCB0b2tlbiB3aXRob3V0IHZhbGlkYXRpb24KICAvLy8gUmV0dXJucyBudWxsIGlmIG5vIHRva2VuIGlzIGF2YWlsYWJsZQogIFN0cmluZz8gZ2V0Q3VycmVudFRva2VuKCk7CgogIC8vLyBTdHJlYW0gb2YgYXV0aGVudGljYXRpb24gdG9rZW4gY2hhbmdlcwogIC8vLyBFbWl0cyBuZXcgdG9rZW5zIHdoZW4gYXV0aGVudGljYXRpb24gc3RhdGUgY2hhbmdlcwogIFN0cmVhbTxTdHJpbmc/PiBnZXQgdG9rZW5TdHJlYW07CgogIC8vLyBGb3JjZXMgYSB0b2tlbiByZWZyZXNoCiAgLy8vIFJldHVybnMgdGhlIG5ldyB0b2tlbiBvciBudWxsIGlmIHJlZnJlc2ggZmFpbHMKICBGdXR1cmU8U3RyaW5nPz4gcmVmcmVzaFRva2VuKCk7CgogIC8vLyBDaGVja3MgaWYgdGhlIHVzZXIgaXMgY3VycmVudGx5IGF1dGhlbnRpY2F0ZWQKICBib29sIGdldCBpc0F1dGhlbnRpY2F0ZWQ7CgogIC8vLyBDaGVja3MgaWYgdGhlIGN1cnJlbnQgdG9rZW4gaXMgZXhwaXJlZAogIC8vLyBSZXR1cm5zIHRydWUgaWYgdG9rZW4gaXMgbnVsbCBvciBleHBpcmVkCiAgYm9vbCBnZXQgaXNUb2tlbkV4cGlyZWQ7CgogIC8vLyBTaWduIG91dCB0aGUgY3VycmVudCB1c2VyCiAgRnV0dXJlPHZvaWQ+IHNpZ25PdXQoKTsKfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/auth/index.dart", + "data": "ZXhwb3J0ICdhdXRoX3Rva2VuX3Byb3ZpZGVyLmRhcnQnOwo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/client/dio_network_client.dart", + "data": + "aW1wb3J0ICdkYXJ0OmlvJzsKCmltcG9ydCAncGFja2FnZTpjb3JlL2NvcmUuZGFydCc7CmltcG9ydCAncGFja2FnZTpkaS9kaS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRpby9kaW8uZGFydCc7CmltcG9ydCAncGFja2FnZTpmbHV0dGVyL2ZvdW5kYXRpb24uZGFydCc7CmltcG9ydCAncGFja2FnZTpsb2NhbGl6YXRpb24vbG9jYWxpemF0aW9uLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6bmV0d29yay9zcmMvY2xpZW50L25ldHdvcmtfY2xpZW50LmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6bmV0d29yay9zcmMvY29uZmlnL25ldHdvcmtfY29uZmlnLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6bmV0d29yay9zcmMvZXhjZXB0aW9ucy9uZXR3b3JrX2V4Y2VwdGlvbnMuZGFydCc7CmltcG9ydCAncGFja2FnZTpuZXR3b3JrL3NyYy9pbnRlcmNlcHRvcnMvYXV0aF9pbnRlcmNlcHRvci5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOm5ldHdvcmsvc3JjL21vZGVscy9uZXR3b3JrX3Jlc3BvbnNlLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6dGFsa2VyX2Rpb19sb2dnZXIvdGFsa2VyX2Rpb19sb2dnZXIuZGFydCc7CmltcG9ydCAncGFja2FnZTp0YWxrZXJfZmx1dHRlci90YWxrZXJfZmx1dHRlci5kYXJ0JzsKCmNsYXNzIERpb05ldHdvcmtDbGllbnQgaW1wbGVtZW50cyBOZXR3b3JrQ2xpZW50IHsKICBEaW9OZXR3b3JrQ2xpZW50KHRoaXMuX2NvbmZpZykgOiBfZGlvID0gRGlvKCkgewogICAgX3NldHVwRGlvKCk7CiAgfQoKICBmaW5hbCBOZXR3b3JrQ29uZmlnIF9jb25maWc7CiAgZmluYWwgRGlvIF9kaW87CiAgZmluYWwgTG9nZ2VyIF9sb2dnZXIgPSBkaS5nZXQ8TG9nZ2VyPigpOwoKICB2b2lkIF9zZXR1cERpbygpIHsKICAgIF9kaW8ub3B0aW9ucyA9IEJhc2VPcHRpb25zKAogICAgICBiYXNlVXJsOiBfY29uZmlnLmJhc2VVcmwsCiAgICAgIGNvbm5lY3RUaW1lb3V0OiBfY29uZmlnLmNvbm5lY3RUaW1lb3V0LAogICAgICByZWNlaXZlVGltZW91dDogX2NvbmZpZy5yZWNlaXZlVGltZW91dCwKICAgICAgLy9zZW5kVGltZW91dCBjYW5ub3QgYmUgdXNlZCB3aXRob3V0IGEgcmVxdWVzdCBib2R5IHRvIHNlbmQgb24gV2ViCiAgICAgIHNlbmRUaW1lb3V0OiBrSXNXZWIgPyBudWxsIDogX2NvbmZpZy5zZW5kVGltZW91dCwKICAgICAgaGVhZGVyczogX2NvbmZpZy5kZWZhdWx0SGVhZGVycywKICAgICk7CgogICAgLy8gQWRkIGF1dGhlbnRpY2F0aW9uIGludGVyY2VwdG9yIGlmIHRva2VuIHByb3ZpZGVyIGlzIGF2YWlsYWJsZQogICAgaWYgKF9jb25maWcuYXV0aFRva2VuUHJvdmlkZXIgIT0gbnVsbCkgewogICAgICBfZGlvLmludGVyY2VwdG9ycy5hZGQoQXV0aEludGVyY2VwdG9yKF9jb25maWcuYXV0aFRva2VuUHJvdmlkZXIhKSk7CiAgICAgIF9sb2dnZXIuZCgn8J+UkCBBdXRoIGludGVyY2VwdG9yIGFkZGVkIHRvIG5ldHdvcmsgY2xpZW50Jyk7CiAgICB9CgogICAgaWYgKF9jb25maWcuZW5hYmxlTG9nZ2luZykgewogICAgICBfZGlvLmludGVyY2VwdG9ycy5hZGQoCiAgICAgICAgVGFsa2VyRGlvTG9nZ2VyKAogICAgICAgICAgdGFsa2VyOiBkaS5nZXQ8TG9nZ2VyPigpLmxvZ2dlciBhcyBUYWxrZXIsCiAgICAgICAgICBzZXR0aW5nczogVGFsa2VyRGlvTG9nZ2VyU2V0dGluZ3MoCiAgICAgICAgICAgIHByaW50UmVzcG9uc2VEYXRhOiB0cnVlLAogICAgICAgICAgICBwcmludFJlc3BvbnNlVGltZTogdHJ1ZSwKICAgICAgICAgICksCiAgICAgICAgKSwKICAgICAgKTsKICAgIH0KICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTxOZXR3b3JrUmVzcG9uc2U8VD4+IGdldDxUPigKICAgIFN0cmluZyBwYXRoLCB7CiAgICBNYXA8U3RyaW5nLCBkeW5hbWljPj8gcXVlcnlQYXJhbWV0ZXJzLAogICAgTWFwPFN0cmluZywgU3RyaW5nPj8gaGVhZGVycywKICAgIHJlcXVpcmVkIFQgRnVuY3Rpb24oT2JqZWN0PyBqc29uKSBmcm9tSnNvblQsCiAgfSkgYXN5bmMgewogICAgdHJ5IHsKICAgICAgZmluYWwgcmVzcG9uc2UgPSBhd2FpdCBfZGlvLmdldCgKICAgICAgICBwYXRoLAogICAgICAgIHF1ZXJ5UGFyYW1ldGVyczogcXVlcnlQYXJhbWV0ZXJzLAogICAgICAgIG9wdGlvbnM6IE9wdGlvbnMoaGVhZGVyczogaGVhZGVycyksCiAgICAgICk7CiAgICAgIHJldHVybiBfaGFuZGxlUmVzcG9uc2U8VD4ocmVzcG9uc2UsIGZyb21Kc29uVDogZnJvbUpzb25UKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgcmV0dXJuIF9oYW5kbGVFcnJvcjxUPihlKTsKICAgIH0KICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTxOZXR3b3JrUmVzcG9uc2U8VD4+IHBvc3Q8VD4oCiAgICBTdHJpbmcgcGF0aCwgewogICAgZHluYW1pYyBkYXRhLAogICAgTWFwPFN0cmluZywgZHluYW1pYz4/IHF1ZXJ5UGFyYW1ldGVycywKICAgIE1hcDxTdHJpbmcsIFN0cmluZz4/IGhlYWRlcnMsCiAgICByZXF1aXJlZCBUIEZ1bmN0aW9uKE9iamVjdD8ganNvbikgZnJvbUpzb25ULAogIH0pIGFzeW5jIHsKICAgIHRyeSB7CiAgICAgIGZpbmFsIHJlc3BvbnNlID0gYXdhaXQgX2Rpby5wb3N0KAogICAgICAgIHBhdGgsCiAgICAgICAgZGF0YTogZGF0YSwKICAgICAgICBxdWVyeVBhcmFtZXRlcnM6IHF1ZXJ5UGFyYW1ldGVycywKICAgICAgICBvcHRpb25zOiBPcHRpb25zKGhlYWRlcnM6IGhlYWRlcnMpLAogICAgICApOwogICAgICByZXR1cm4gX2hhbmRsZVJlc3BvbnNlPFQ+KHJlc3BvbnNlLCBmcm9tSnNvblQ6IGZyb21Kc29uVCk7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIHJldHVybiBfaGFuZGxlRXJyb3I8VD4oZSk7CiAgICB9CiAgfQoKICBAb3ZlcnJpZGUKICBGdXR1cmU8TmV0d29ya1Jlc3BvbnNlPFQ+PiBwdXQ8VD4oCiAgICBTdHJpbmcgcGF0aCwgewogICAgZHluYW1pYyBkYXRhLAogICAgTWFwPFN0cmluZywgZHluYW1pYz4/IHF1ZXJ5UGFyYW1ldGVycywKICAgIE1hcDxTdHJpbmcsIFN0cmluZz4/IGhlYWRlcnMsCiAgICByZXF1aXJlZCBUIEZ1bmN0aW9uKE9iamVjdD8ganNvbikgZnJvbUpzb25ULAogIH0pIGFzeW5jIHsKICAgIHRyeSB7CiAgICAgIGZpbmFsIHJlc3BvbnNlID0gYXdhaXQgX2Rpby5wdXQoCiAgICAgICAgcGF0aCwKICAgICAgICBkYXRhOiBkYXRhLAogICAgICAgIHF1ZXJ5UGFyYW1ldGVyczogcXVlcnlQYXJhbWV0ZXJzLAogICAgICAgIG9wdGlvbnM6IE9wdGlvbnMoaGVhZGVyczogaGVhZGVycyksCiAgICAgICk7CiAgICAgIHJldHVybiBfaGFuZGxlUmVzcG9uc2U8VD4ocmVzcG9uc2UsIGZyb21Kc29uVDogZnJvbUpzb25UKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgcmV0dXJuIF9oYW5kbGVFcnJvcjxUPihlKTsKICAgIH0KICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTxOZXR3b3JrUmVzcG9uc2U8VD4+IHBhdGNoPFQ+KAogICAgU3RyaW5nIHBhdGgsIHsKICAgIGR5bmFtaWMgZGF0YSwKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBxdWVyeVBhcmFtZXRlcnMsCiAgICBNYXA8U3RyaW5nLCBTdHJpbmc+PyBoZWFkZXJzLAogICAgcmVxdWlyZWQgVCBGdW5jdGlvbihPYmplY3Q/IGpzb24pIGZyb21Kc29uVCwKICB9KSBhc3luYyB7CiAgICB0cnkgewogICAgICBmaW5hbCByZXNwb25zZSA9IGF3YWl0IF9kaW8ucGF0Y2goCiAgICAgICAgcGF0aCwKICAgICAgICBkYXRhOiBkYXRhLAogICAgICAgIHF1ZXJ5UGFyYW1ldGVyczogcXVlcnlQYXJhbWV0ZXJzLAogICAgICAgIG9wdGlvbnM6IE9wdGlvbnMoaGVhZGVyczogaGVhZGVycyksCiAgICAgICk7CiAgICAgIHJldHVybiBfaGFuZGxlUmVzcG9uc2U8VD4ocmVzcG9uc2UsIGZyb21Kc29uVDogZnJvbUpzb25UKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgcmV0dXJuIF9oYW5kbGVFcnJvcjxUPihlKTsKICAgIH0KICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTxOZXR3b3JrUmVzcG9uc2U8VD4+IGRlbGV0ZTxUPigKICAgIFN0cmluZyBwYXRoLCB7CiAgICBkeW5hbWljIGRhdGEsCiAgICBNYXA8U3RyaW5nLCBkeW5hbWljPj8gcXVlcnlQYXJhbWV0ZXJzLAogICAgTWFwPFN0cmluZywgU3RyaW5nPj8gaGVhZGVycywKICAgIHJlcXVpcmVkIFQgRnVuY3Rpb24oT2JqZWN0PyBqc29uKSBmcm9tSnNvblQsCiAgfSkgYXN5bmMgewogICAgdHJ5IHsKICAgICAgZmluYWwgcmVzcG9uc2UgPSBhd2FpdCBfZGlvLmRlbGV0ZSgKICAgICAgICBwYXRoLAogICAgICAgIGRhdGE6IGRhdGEsCiAgICAgICAgcXVlcnlQYXJhbWV0ZXJzOiBxdWVyeVBhcmFtZXRlcnMsCiAgICAgICAgb3B0aW9uczogT3B0aW9ucyhoZWFkZXJzOiBoZWFkZXJzKSwKICAgICAgKTsKICAgICAgcmV0dXJuIF9oYW5kbGVSZXNwb25zZTxUPihyZXNwb25zZSwgZnJvbUpzb25UOiBmcm9tSnNvblQpOwogICAgfSBjYXRjaCAoZSkgewogICAgICByZXR1cm4gX2hhbmRsZUVycm9yPFQ+KGUpOwogICAgfQogIH0KCiAgTmV0d29ya1Jlc3BvbnNlPFQ+IF9oYW5kbGVSZXNwb25zZTxUPigKICAgIFJlc3BvbnNlIHJlc3BvbnNlLCB7CiAgICByZXF1aXJlZCBUIEZ1bmN0aW9uKE9iamVjdD8ganNvbikgZnJvbUpzb25ULAogIH0pIHsKICAgIHRyeSB7CiAgICAgIGlmIChyZXNwb25zZS5kYXRhIGlzIE1hcDxTdHJpbmcsIGR5bmFtaWM+KSB7CiAgICAgICAgZmluYWwgcmVzcG9uc2VEYXRhID0gcmVzcG9uc2UuZGF0YSBhcyBNYXA8U3RyaW5nLCBkeW5hbWljPjsKCiAgICAgICAgaWYgKHJlc3BvbnNlRGF0YS5jb250YWluc0tleSgnc3VjY2VzcycpKSB7CiAgICAgICAgICBpZiAocmVzcG9uc2VEYXRhWydzdWNjZXNzJ10gPT0gdHJ1ZSkgewogICAgICAgICAgICByZXR1cm4gU3VjY2Vzc1Jlc3BvbnNlLmZyb21Kc29uKHsKICAgICAgICAgICAgICAnc3RhdHVzQ29kZSc6IHJlc3BvbnNlLnN0YXR1c0NvZGUgPz8gMjAwLAogICAgICAgICAgICAgICdoZWFkZXJzJzogcmVzcG9uc2UuaGVhZGVycy5tYXAsCiAgICAgICAgICAgICAgLi4ucmVzcG9uc2VEYXRhLAogICAgICAgICAgICB9LCBmcm9tSnNvblQpOwogICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgRXJyb3JSZXNwb25zZS5mcm9tSnNvbih7CiAgICAgICAgICAgICAgJ3N0YXR1c0NvZGUnOiByZXNwb25zZS5zdGF0dXNDb2RlID8/IDUwMCwKICAgICAgICAgICAgICAnaGVhZGVycyc6IHJlc3BvbnNlLmhlYWRlcnMubWFwLAogICAgICAgICAgICAgIC4uLnJlc3BvbnNlRGF0YSwKICAgICAgICAgICAgfSk7CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CgogICAgICAvLyBJZGVhbGx5LCBzaG91bGQgbm90IHJlYWNoIGhlcmUgaWYgdGhlIEFQSSBmb2xsb3dzIHRoZQogICAgICAvLyBzdGFuZGFyZCByZXNwb25zZSBmb3JtYXQKICAgICAgX2xvZ2dlci53KCdJbnZhbGlkIHJlc3BvbnNlIGZvcm1hdDogJHtyZXNwb25zZS5kYXRhfScpOwoKICAgICAgLy8gRmFsbGJhY2sgdG8gYSBnZW5lcmljIHN1Y2Nlc3MgcmVzcG9uc2UKICAgICAgcmV0dXJuIFN1Y2Nlc3NSZXNwb25zZS5mcm9tSnNvbih7CiAgICAgICAgJ3N0YXR1c0NvZGUnOiByZXNwb25zZS5zdGF0dXNDb2RlID8/IDIwMCwKICAgICAgICAnaGVhZGVycyc6IHJlc3BvbnNlLmhlYWRlcnMubWFwLAogICAgICAgICdkYXRhJzogcmVzcG9uc2UuZGF0YSwKICAgICAgfSwgKHZhbHVlKSA9PiB2YWx1ZSBhcyBUKTsKICAgIH0gY2F0Y2ggKGUsIHMpIHsKICAgICAgX2xvZ2dlci5lKCdFcnJvciBwYXJzaW5nIHJlc3BvbnNlJywgZSk7CiAgICAgIHJldHVybiBFcnJvclJlc3BvbnNlKAogICAgICAgIHN0YXR1c0NvZGU6IHJlc3BvbnNlLnN0YXR1c0NvZGUgPz8gNTAwLAogICAgICAgIHN1Y2Nlc3M6IGZhbHNlLAogICAgICAgIG1lc3NhZ2U6ICdOZXR3b3JrIENsaWVudCBmYWlsZWQgdG8gcGFyc2UgcmVzcG9uc2UhJywKICAgICAgICBlcnJvcjogRXJyb3JEZXRhaWxzKAogICAgICAgICAgY29kZTogJ1BBUlNFX0VSUk9SJywKICAgICAgICAgIG1lc3NhZ2U6IGUudG9TdHJpbmcoKSwKICAgICAgICAgIGRldGFpbHM6IHsnc3RhY2tUcmFjZSc6IHMudG9TdHJpbmcoKX0sCiAgICAgICAgKSwKICAgICAgICB0aW1lc3RhbXA6IERhdGVUaW1lLm5vdygpLnRvSXNvODYwMVN0cmluZygpLAogICAgICApOwogICAgfQogIH0KCiAgTmV0d29ya1Jlc3BvbnNlPFQ+IF9oYW5kbGVFcnJvcjxUPihkeW5hbWljIGVycm9yKSB7CiAgICBOZXR3b3JrRXhjZXB0aW9uIG5ldHdvcmtFeGNlcHRpb247CiAgICBFcnJvclJlc3BvbnNlPyBhcGlFcnJvcjsKCiAgICBpZiAoZXJyb3IgaXMgRGlvRXhjZXB0aW9uKSB7CiAgICAgIGFwaUVycm9yID0gX3BhcnNlQXBpRXJyb3IoZXJyb3IucmVzcG9uc2UpOwogICAgICBuZXR3b3JrRXhjZXB0aW9uID0gX21hcERpb0V4Y2VwdGlvbihlcnJvciwgYXBpRXJyb3IpOwogICAgfSBlbHNlIHsKICAgICAgbmV0d29ya0V4Y2VwdGlvbiA9IFVua25vd25OZXR3b3JrRXhjZXB0aW9uKG1lc3NhZ2U6IGVycm9yLnRvU3RyaW5nKCkpOwogICAgfQoKICAgIF9sb2dnZXIuZSgnTmV0d29yayBlcnJvciBvY2N1cnJlZCcsIG5ldHdvcmtFeGNlcHRpb24pOwoKICAgIHRocm93IG5ldHdvcmtFeGNlcHRpb247CiAgfQoKICBFcnJvclJlc3BvbnNlPyBfcGFyc2VBcGlFcnJvcihSZXNwb25zZT8gcmVzcG9uc2UpIHsKICAgIHRyeSB7CiAgICAgIGZpbmFsIHJlc3BvbnNlRGF0YSA9IHJlc3BvbnNlPy5kYXRhOwogICAgICBpZiAocmVzcG9uc2VEYXRhIGlzIE1hcDxTdHJpbmcsIGR5bmFtaWM+ICYmCiAgICAgICAgICByZXNwb25zZURhdGEuY29udGFpbnNLZXkoJ3N1Y2Nlc3MnKSAmJgogICAgICAgICAgcmVzcG9uc2VEYXRhWydzdWNjZXNzJ10gPT0gZmFsc2UpIHsKICAgICAgICByZXR1cm4gRXJyb3JSZXNwb25zZS5mcm9tSnNvbih7CiAgICAgICAgICAnc3RhdHVzQ29kZSc6IHJlc3BvbnNlPy5zdGF0dXNDb2RlID8/IDUwMCwKICAgICAgICAgICdoZWFkZXJzJzogcmVzcG9uc2U/LmhlYWRlcnMubWFwLAogICAgICAgICAgLi4ucmVzcG9uc2VEYXRhLAogICAgICAgIH0pOwogICAgICB9CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIF9sb2dnZXIudygnRmFpbGVkIHRvIHBhcnNlIEFQSSBlcnJvcjogJGUnKTsKICAgIH0KICAgIHJldHVybiBudWxsOwogIH0KCiAgTmV0d29ya0V4Y2VwdGlvbiBfbWFwRGlvRXhjZXB0aW9uKAogICAgRGlvRXhjZXB0aW9uIGRpb0V4Y2VwdGlvbiwKICAgIEVycm9yUmVzcG9uc2U/IGFwaUVycm9yLAogICkgewogICAgZmluYWwgbWVzc2FnZSA9IGFwaUVycm9yPy5lcnJvci5tZXNzYWdlOwoKICAgIHN3aXRjaCAoZGlvRXhjZXB0aW9uLnR5cGUpIHsKICAgICAgY2FzZSBEaW9FeGNlcHRpb25UeXBlLmNvbm5lY3Rpb25UaW1lb3V0OgogICAgICAgIHJldHVybiBDb25uZWN0aW9uVGltZW91dEV4Y2VwdGlvbihtZXNzYWdlOiBtZXNzYWdlKTsKICAgICAgY2FzZSBEaW9FeGNlcHRpb25UeXBlLnJlY2VpdmVUaW1lb3V0OgogICAgICAgIHJldHVybiBSZWNlaXZlVGltZW91dEV4Y2VwdGlvbihtZXNzYWdlOiBtZXNzYWdlKTsKICAgICAgY2FzZSBEaW9FeGNlcHRpb25UeXBlLnNlbmRUaW1lb3V0OgogICAgICAgIHJldHVybiBTZW5kVGltZW91dEV4Y2VwdGlvbihtZXNzYWdlOiBtZXNzYWdlKTsKICAgICAgY2FzZSBEaW9FeGNlcHRpb25UeXBlLmNvbm5lY3Rpb25FcnJvcjoKICAgICAgICBpZiAoZGlvRXhjZXB0aW9uLmVycm9yIGlzIFNvY2tldEV4Y2VwdGlvbikgewogICAgICAgICAgcmV0dXJuIE5vSW50ZXJuZXRDb25uZWN0aW9uRXhjZXB0aW9uKG1lc3NhZ2U6IG1lc3NhZ2UpOwogICAgICAgIH0KICAgICAgICByZXR1cm4gVW5rbm93bk5ldHdvcmtFeGNlcHRpb24obWVzc2FnZTogbWVzc2FnZSk7CiAgICAgIGNhc2UgRGlvRXhjZXB0aW9uVHlwZS5iYWRSZXNwb25zZToKICAgICAgICBmaW5hbCBzdGF0dXNDb2RlID0gZGlvRXhjZXB0aW9uLnJlc3BvbnNlPy5zdGF0dXNDb2RlOwogICAgICAgIHN3aXRjaCAoc3RhdHVzQ29kZSkgewogICAgICAgICAgY2FzZSA0MDA6CiAgICAgICAgICAgIHJldHVybiBCYWRSZXF1ZXN0RXhjZXB0aW9uKG1lc3NhZ2U6IG1lc3NhZ2UsIGVycm9yOiBhcGlFcnJvcik7CiAgICAgICAgICBjYXNlIDQwMToKICAgICAgICAgICAgcmV0dXJuIFVuYXV0aG9yaXplZEV4Y2VwdGlvbihtZXNzYWdlOiBtZXNzYWdlLCBlcnJvcjogYXBpRXJyb3IpOwogICAgICAgICAgY2FzZSA0MDM6CiAgICAgICAgICAgIHJldHVybiBGb3JiaWRkZW5FeGNlcHRpb24obWVzc2FnZTogbWVzc2FnZSwgZXJyb3I6IGFwaUVycm9yKTsKICAgICAgICAgIGNhc2UgNDA0OgogICAgICAgICAgICByZXR1cm4gTm90Rm91bmRFeGNlcHRpb24obWVzc2FnZTogbWVzc2FnZSwgZXJyb3I6IGFwaUVycm9yKTsKICAgICAgICAgIGNhc2UgNDA5OgogICAgICAgICAgICByZXR1cm4gQ29uZmxpY3RFeGNlcHRpb24obWVzc2FnZTogbWVzc2FnZSwgZXJyb3I6IGFwaUVycm9yKTsKICAgICAgICAgIGNhc2UgNDIyOgogICAgICAgICAgICByZXR1cm4gVW5wcm9jZXNzYWJsZUVudGl0eUV4Y2VwdGlvbigKICAgICAgICAgICAgICBtZXNzYWdlOiBtZXNzYWdlLAogICAgICAgICAgICAgIGVycm9yOiBhcGlFcnJvciwKICAgICAgICAgICAgKTsKICAgICAgICAgIGNhc2UgNTAwOgogICAgICAgICAgY2FzZSA1MDI6CiAgICAgICAgICBjYXNlIDUwMzoKICAgICAgICAgIGNhc2UgNTA0OgogICAgICAgICAgICByZXR1cm4gSW50ZXJuYWxTZXJ2ZXJFeGNlcHRpb24obWVzc2FnZTogbWVzc2FnZSwgZXJyb3I6IGFwaUVycm9yKTsKICAgICAgICAgIGRlZmF1bHQ6CiAgICAgICAgICAgIHJldHVybiBVbmtub3duTmV0d29ya0V4Y2VwdGlvbigKICAgICAgICAgICAgICBtZXNzYWdlOiBtZXNzYWdlLAogICAgICAgICAgICAgIHN0YXR1c0NvZGU6IHN0YXR1c0NvZGUsCiAgICAgICAgICAgICAgZXJyb3I6IGFwaUVycm9yLAogICAgICAgICAgICApOwogICAgICAgIH0KICAgICAgZGVmYXVsdDoKICAgICAgICByZXR1cm4gVW5rbm93bk5ldHdvcmtFeGNlcHRpb24obWVzc2FnZTogbWVzc2FnZSwgZXJyb3I6IGFwaUVycm9yKTsKICAgIH0KICB9CgogIEBvdmVycmlkZQogIFQgaGFuZGxlUmVzcG9uc2U8VD4oZmluYWwgTmV0d29ya1Jlc3BvbnNlPFQ+IHJlc3BvbnNlKSB7CiAgICBzd2l0Y2ggKHJlc3BvbnNlKSB7CiAgICAgIGNhc2UgU3VjY2Vzc1Jlc3BvbnNlPFQ+KCk6CiAgICAgICAgcmV0dXJuIHJlc3BvbnNlLmRhdGEhOwogICAgICBjYXNlIEVycm9yUmVzcG9uc2U8VD4oKToKICAgICAgICB0aHJvdyBVbmtub3duTmV0d29ya0V4Y2VwdGlvbigKICAgICAgICAgIG1lc3NhZ2U6IHJlc3BvbnNlLm1lc3NhZ2UgPz8gc3RyaW5ncy5lcnJvcnMubmV0d29ya19lcnJvciwKICAgICAgICAgIHN0YXR1c0NvZGU6IHJlc3BvbnNlLnN0YXR1c0NvZGUsCiAgICAgICAgICBlcnJvcjogcmVzcG9uc2UsCiAgICAgICAgKTsKICAgICAgZGVmYXVsdDoKICAgICAgICB0aHJvdyBVbmtub3duTmV0d29ya0V4Y2VwdGlvbigKICAgICAgICAgIG1lc3NhZ2U6IHN0cmluZ3MuZXJyb3JzLnVua25vd25fZXJyb3IsCiAgICAgICAgICBzdGF0dXNDb2RlOiByZXNwb25zZS5zdGF0dXNDb2RlLAogICAgICAgICk7CiAgICB9CiAgfQoKICBAb3ZlcnJpZGUKICBTdWNjZXNzUmVzcG9uc2U8VD4gaGFuZGxlU3VjY2Vzc1Jlc3BvbnNlPFQ+KAogICAgZmluYWwgTmV0d29ya1Jlc3BvbnNlPFQ+IHJlc3BvbnNlLAogICkgewogICAgc3dpdGNoIChyZXNwb25zZSkgewogICAgICBjYXNlIFN1Y2Nlc3NSZXNwb25zZTxUPigpOgogICAgICAgIHJldHVybiByZXNwb25zZTsKICAgICAgY2FzZSBFcnJvclJlc3BvbnNlPFQ+KCk6CiAgICAgICAgdGhyb3cgVW5rbm93bk5ldHdvcmtFeGNlcHRpb24oCiAgICAgICAgICBtZXNzYWdlOiByZXNwb25zZS5tZXNzYWdlID8/IHN0cmluZ3MuZXJyb3JzLm5ldHdvcmtfZXJyb3IsCiAgICAgICAgICBzdGF0dXNDb2RlOiByZXNwb25zZS5zdGF0dXNDb2RlLAogICAgICAgICAgZXJyb3I6IHJlc3BvbnNlLAogICAgICAgICk7CiAgICAgIGRlZmF1bHQ6CiAgICAgICAgdGhyb3cgVW5rbm93bk5ldHdvcmtFeGNlcHRpb24oCiAgICAgICAgICBtZXNzYWdlOiBzdHJpbmdzLmVycm9ycy51bmtub3duX2Vycm9yLAogICAgICAgICAgc3RhdHVzQ29kZTogcmVzcG9uc2Uuc3RhdHVzQ29kZSwKICAgICAgICApOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgdm9pZCBkaXNwb3NlKCkgewogICAgX2Rpby5jbG9zZSgpOwogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/client/index.dart", + "data": + "ZXhwb3J0ICdkaW9fbmV0d29ya19jbGllbnQuZGFydCc7CmV4cG9ydCAnbmV0d29ya19jbGllbnQuZGFydCc7Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/client/network_client.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOm5ldHdvcmsvc3JjL21vZGVscy9uZXR3b3JrX3Jlc3BvbnNlLmRhcnQnOwoKYWJzdHJhY3QgY2xhc3MgTmV0d29ya0NsaWVudCB7CiAgRnV0dXJlPE5ldHdvcmtSZXNwb25zZTxUPj4gZ2V0PFQ+KAogICAgU3RyaW5nIHBhdGgsIHsKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBxdWVyeVBhcmFtZXRlcnMsCiAgICBNYXA8U3RyaW5nLCBTdHJpbmc+PyBoZWFkZXJzLAogICAgcmVxdWlyZWQgVCBGdW5jdGlvbihPYmplY3Q/IGpzb24pIGZyb21Kc29uVCwKICB9KTsKCiAgRnV0dXJlPE5ldHdvcmtSZXNwb25zZTxUPj4gcG9zdDxUPigKICAgIFN0cmluZyBwYXRoLCB7CiAgICBkeW5hbWljIGRhdGEsCiAgICBNYXA8U3RyaW5nLCBkeW5hbWljPj8gcXVlcnlQYXJhbWV0ZXJzLAogICAgTWFwPFN0cmluZywgU3RyaW5nPj8gaGVhZGVycywKICAgIHJlcXVpcmVkIFQgRnVuY3Rpb24oT2JqZWN0PyBqc29uKSBmcm9tSnNvblQsCiAgfSk7CgogIEZ1dHVyZTxOZXR3b3JrUmVzcG9uc2U8VD4+IHB1dDxUPigKICAgIFN0cmluZyBwYXRoLCB7CiAgICBkeW5hbWljIGRhdGEsCiAgICBNYXA8U3RyaW5nLCBkeW5hbWljPj8gcXVlcnlQYXJhbWV0ZXJzLAogICAgTWFwPFN0cmluZywgU3RyaW5nPj8gaGVhZGVycywKICAgIHJlcXVpcmVkIFQgRnVuY3Rpb24oT2JqZWN0PyBqc29uKSBmcm9tSnNvblQsCiAgfSk7CgogIEZ1dHVyZTxOZXR3b3JrUmVzcG9uc2U8VD4+IHBhdGNoPFQ+KAogICAgU3RyaW5nIHBhdGgsIHsKICAgIGR5bmFtaWMgZGF0YSwKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBxdWVyeVBhcmFtZXRlcnMsCiAgICBNYXA8U3RyaW5nLCBTdHJpbmc+PyBoZWFkZXJzLAogICAgcmVxdWlyZWQgVCBGdW5jdGlvbihPYmplY3Q/IGpzb24pIGZyb21Kc29uVCwKICB9KTsKCiAgRnV0dXJlPE5ldHdvcmtSZXNwb25zZTxUPj4gZGVsZXRlPFQ+KAogICAgU3RyaW5nIHBhdGgsIHsKICAgIGR5bmFtaWMgZGF0YSwKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBxdWVyeVBhcmFtZXRlcnMsCiAgICBNYXA8U3RyaW5nLCBTdHJpbmc+PyBoZWFkZXJzLAogICAgcmVxdWlyZWQgVCBGdW5jdGlvbihPYmplY3Q/IGpzb24pIGZyb21Kc29uVCwKICB9KTsKCiAgVCBoYW5kbGVSZXNwb25zZTxUPihmaW5hbCBOZXR3b3JrUmVzcG9uc2U8VD4gcmVzcG9uc2UpOwoKICBTdWNjZXNzUmVzcG9uc2U8VD4gaGFuZGxlU3VjY2Vzc1Jlc3BvbnNlPFQ+KAogICAgZmluYWwgTmV0d29ya1Jlc3BvbnNlPFQ+IHJlc3BvbnNlLAogICk7CgogIHZvaWQgZGlzcG9zZSgpOwp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/config/index.dart", + "data": "ZXhwb3J0ICduZXR3b3JrX2NvbmZpZy5kYXJ0JzsK", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/config/network_config.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOm5ldHdvcmsvc3JjL2F1dGgvYXV0aF90b2tlbl9wcm92aWRlci5kYXJ0JzsKCmFic3RyYWN0IGNsYXNzIE5ldHdvcmtDb25maWcgewogIFN0cmluZyBnZXQgYmFzZVVybDsKICBEdXJhdGlvbiBnZXQgY29ubmVjdFRpbWVvdXQ7CiAgRHVyYXRpb24gZ2V0IHJlY2VpdmVUaW1lb3V0OwogIER1cmF0aW9uIGdldCBzZW5kVGltZW91dDsKICBNYXA8U3RyaW5nLCBTdHJpbmc+IGdldCBkZWZhdWx0SGVhZGVyczsKICBib29sIGdldCBlbmFibGVMb2dnaW5nOwogIEF1dGhUb2tlblByb3ZpZGVyPyBnZXQgYXV0aFRva2VuUHJvdmlkZXI7Cn0KCmNsYXNzIERlZmF1bHROZXR3b3JrQ29uZmlnIGltcGxlbWVudHMgTmV0d29ya0NvbmZpZyB7CiAgY29uc3QgRGVmYXVsdE5ldHdvcmtDb25maWcoewogICAgcmVxdWlyZWQgdGhpcy5iYXNlVXJsLAogICAgdGhpcy5jb25uZWN0VGltZW91dCA9IGNvbnN0IER1cmF0aW9uKHNlY29uZHM6IDMwKSwKICAgIHRoaXMucmVjZWl2ZVRpbWVvdXQgPSBjb25zdCBEdXJhdGlvbihzZWNvbmRzOiAzMCksCiAgICB0aGlzLnNlbmRUaW1lb3V0ID0gY29uc3QgRHVyYXRpb24oc2Vjb25kczogMzApLAogICAgdGhpcy5kZWZhdWx0SGVhZGVycyA9IGNvbnN0IHt9LAogICAgdGhpcy5lbmFibGVMb2dnaW5nID0gdHJ1ZSwKICAgIHRoaXMuYXV0aFRva2VuUHJvdmlkZXIsCiAgfSk7CgogIEBvdmVycmlkZQogIGZpbmFsIFN0cmluZyBiYXNlVXJsOwoKICBAb3ZlcnJpZGUKICBmaW5hbCBEdXJhdGlvbiBjb25uZWN0VGltZW91dDsKCiAgQG92ZXJyaWRlCiAgZmluYWwgRHVyYXRpb24gcmVjZWl2ZVRpbWVvdXQ7CgogIEBvdmVycmlkZQogIGZpbmFsIER1cmF0aW9uIHNlbmRUaW1lb3V0OwoKICBAb3ZlcnJpZGUKICBmaW5hbCBNYXA8U3RyaW5nLCBTdHJpbmc+IGRlZmF1bHRIZWFkZXJzOwoKICBAb3ZlcnJpZGUKICBmaW5hbCBib29sIGVuYWJsZUxvZ2dpbmc7CgogIEBvdmVycmlkZQogIGZpbmFsIEF1dGhUb2tlblByb3ZpZGVyPyBhdXRoVG9rZW5Qcm92aWRlcjsKfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/exceptions/index.dart", + "data": "ZXhwb3J0ICduZXR3b3JrX2V4Y2VwdGlvbnMuZGFydCc7Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/exceptions/network_exceptions.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmxvY2FsaXphdGlvbi9sb2NhbGl6YXRpb24uZGFydCc7CmltcG9ydCAncGFja2FnZTpuZXR3b3JrL25ldHdvcmsuZGFydCc7CgphYnN0cmFjdCBjbGFzcyBOZXR3b3JrRXhjZXB0aW9uIGltcGxlbWVudHMgRXhjZXB0aW9uIHsKICBjb25zdCBOZXR3b3JrRXhjZXB0aW9uKHt0aGlzLm1lc3NhZ2UsIHRoaXMuc3RhdHVzQ29kZSwgdGhpcy5lcnJvcn0pOwoKICBmaW5hbCBTdHJpbmc/IG1lc3NhZ2U7CiAgZmluYWwgaW50PyBzdGF0dXNDb2RlOwogIGZpbmFsIEVycm9yUmVzcG9uc2U/IGVycm9yOwoKICBTdHJpbmcgZ2V0IGRpc3BsYXlNZXNzYWdlID0+IG1lc3NhZ2UgPz8gc3RyaW5ncy5lcnJvcnMudW5rbm93bl9uZXR3b3JrOwoKICBAb3ZlcnJpZGUKICBTdHJpbmcgdG9TdHJpbmcoKSA9PiAnJHJ1bnRpbWVUeXBlOiAkZGlzcGxheU1lc3NhZ2UnOwp9CgpjbGFzcyBCYWRSZXF1ZXN0RXhjZXB0aW9uIGV4dGVuZHMgTmV0d29ya0V4Y2VwdGlvbiB7CiAgY29uc3QgQmFkUmVxdWVzdEV4Y2VwdGlvbih7c3VwZXIubWVzc2FnZSwgc3VwZXIuZXJyb3J9KQogICAgOiBzdXBlcihzdGF0dXNDb2RlOiA0MDApOwoKICBAb3ZlcnJpZGUKICBTdHJpbmcgZ2V0IGRpc3BsYXlNZXNzYWdlID0+IG1lc3NhZ2UgPz8gc3RyaW5ncy5lcnJvcnMuYmFkX3JlcXVlc3Q7Cn0KCmNsYXNzIFVuYXV0aG9yaXplZEV4Y2VwdGlvbiBleHRlbmRzIE5ldHdvcmtFeGNlcHRpb24gewogIGNvbnN0IFVuYXV0aG9yaXplZEV4Y2VwdGlvbih7c3VwZXIubWVzc2FnZSwgc3VwZXIuZXJyb3J9KQogICAgOiBzdXBlcihzdGF0dXNDb2RlOiA0MDEpOwoKICBAb3ZlcnJpZGUKICBTdHJpbmcgZ2V0IGRpc3BsYXlNZXNzYWdlID0+IG1lc3NhZ2UgPz8gc3RyaW5ncy5lcnJvcnMudW5hdXRob3JpemVkOwp9CgpjbGFzcyBGb3JiaWRkZW5FeGNlcHRpb24gZXh0ZW5kcyBOZXR3b3JrRXhjZXB0aW9uIHsKICBjb25zdCBGb3JiaWRkZW5FeGNlcHRpb24oe3N1cGVyLm1lc3NhZ2UsIHN1cGVyLmVycm9yfSkKICAgIDogc3VwZXIoc3RhdHVzQ29kZTogNDAzKTsKCiAgQG92ZXJyaWRlCiAgU3RyaW5nIGdldCBkaXNwbGF5TWVzc2FnZSA9PiBtZXNzYWdlID8/IHN0cmluZ3MuZXJyb3JzLmZvcmJpZGRlbjsKfQoKY2xhc3MgTm90Rm91bmRFeGNlcHRpb24gZXh0ZW5kcyBOZXR3b3JrRXhjZXB0aW9uIHsKICBjb25zdCBOb3RGb3VuZEV4Y2VwdGlvbih7c3VwZXIubWVzc2FnZSwgc3VwZXIuZXJyb3J9KQogICAgOiBzdXBlcihzdGF0dXNDb2RlOiA0MDQpOwoKICBAb3ZlcnJpZGUKICBTdHJpbmcgZ2V0IGRpc3BsYXlNZXNzYWdlID0+IG1lc3NhZ2UgPz8gc3RyaW5ncy5lcnJvcnMubm90X2ZvdW5kOwp9CgpjbGFzcyBDb25mbGljdEV4Y2VwdGlvbiBleHRlbmRzIE5ldHdvcmtFeGNlcHRpb24gewogIGNvbnN0IENvbmZsaWN0RXhjZXB0aW9uKHtzdXBlci5tZXNzYWdlLCBzdXBlci5lcnJvcn0pCiAgICA6IHN1cGVyKHN0YXR1c0NvZGU6IDQwOSk7CgogIEBvdmVycmlkZQogIFN0cmluZyBnZXQgZGlzcGxheU1lc3NhZ2UgPT4gbWVzc2FnZSA/PyBzdHJpbmdzLmVycm9ycy5jb25mbGljdDsKfQoKY2xhc3MgVW5wcm9jZXNzYWJsZUVudGl0eUV4Y2VwdGlvbiBleHRlbmRzIE5ldHdvcmtFeGNlcHRpb24gewogIGNvbnN0IFVucHJvY2Vzc2FibGVFbnRpdHlFeGNlcHRpb24oe3N1cGVyLm1lc3NhZ2UsIHN1cGVyLmVycm9yfSkKICAgIDogc3VwZXIoc3RhdHVzQ29kZTogNDIyKTsKCiAgQG92ZXJyaWRlCiAgU3RyaW5nIGdldCBkaXNwbGF5TWVzc2FnZSA9PiBtZXNzYWdlID8/IHN0cmluZ3MuZXJyb3JzLnVucHJvY2Vzc2FibGVfZW50aXR5Owp9CgpjbGFzcyBJbnRlcm5hbFNlcnZlckV4Y2VwdGlvbiBleHRlbmRzIE5ldHdvcmtFeGNlcHRpb24gewogIGNvbnN0IEludGVybmFsU2VydmVyRXhjZXB0aW9uKHtzdXBlci5tZXNzYWdlLCBzdXBlci5lcnJvcn0pCiAgICA6IHN1cGVyKHN0YXR1c0NvZGU6IDUwMCk7CgogIEBvdmVycmlkZQogIFN0cmluZyBnZXQgZGlzcGxheU1lc3NhZ2UgPT4gbWVzc2FnZSA/PyBzdHJpbmdzLmVycm9ycy5pbnRlcm5hbF9zZXJ2ZXJfZXJyb3I7Cn0KCmNsYXNzIENvbm5lY3Rpb25UaW1lb3V0RXhjZXB0aW9uIGV4dGVuZHMgTmV0d29ya0V4Y2VwdGlvbiB7CiAgY29uc3QgQ29ubmVjdGlvblRpbWVvdXRFeGNlcHRpb24oe3N1cGVyLm1lc3NhZ2V9KSA6IHN1cGVyKHN0YXR1c0NvZGU6IDU5OSk7CgogIEBvdmVycmlkZQogIFN0cmluZyBnZXQgZGlzcGxheU1lc3NhZ2UgPT4gbWVzc2FnZSA/PyBzdHJpbmdzLmVycm9ycy5jb25uZWN0aW9uX3RpbWVvdXQ7Cn0KCmNsYXNzIFJlY2VpdmVUaW1lb3V0RXhjZXB0aW9uIGV4dGVuZHMgTmV0d29ya0V4Y2VwdGlvbiB7CiAgY29uc3QgUmVjZWl2ZVRpbWVvdXRFeGNlcHRpb24oe3N1cGVyLm1lc3NhZ2V9KSA6IHN1cGVyKHN0YXR1c0NvZGU6IG51bGwpOwoKICBAb3ZlcnJpZGUKICBTdHJpbmcgZ2V0IGRpc3BsYXlNZXNzYWdlID0+IG1lc3NhZ2UgPz8gc3RyaW5ncy5lcnJvcnMucmVjZWl2ZV90aW1lb3V0Owp9CgpjbGFzcyBTZW5kVGltZW91dEV4Y2VwdGlvbiBleHRlbmRzIE5ldHdvcmtFeGNlcHRpb24gewogIGNvbnN0IFNlbmRUaW1lb3V0RXhjZXB0aW9uKHtzdXBlci5tZXNzYWdlfSkgOiBzdXBlcihzdGF0dXNDb2RlOiBudWxsKTsKCiAgQG92ZXJyaWRlCiAgU3RyaW5nIGdldCBkaXNwbGF5TWVzc2FnZSA9PiBtZXNzYWdlID8/IHN0cmluZ3MuZXJyb3JzLnNlbmRfdGltZW91dDsKfQoKY2xhc3MgTm9JbnRlcm5ldENvbm5lY3Rpb25FeGNlcHRpb24gZXh0ZW5kcyBOZXR3b3JrRXhjZXB0aW9uIHsKICBjb25zdCBOb0ludGVybmV0Q29ubmVjdGlvbkV4Y2VwdGlvbih7c3VwZXIubWVzc2FnZX0pCiAgICA6IHN1cGVyKHN0YXR1c0NvZGU6IG51bGwpOwoKICBAb3ZlcnJpZGUKICBTdHJpbmcgZ2V0IGRpc3BsYXlNZXNzYWdlID0+IG1lc3NhZ2UgPz8gc3RyaW5ncy5lcnJvcnMubm9faW50ZXJuZXQ7Cn0KCmNsYXNzIFVua25vd25OZXR3b3JrRXhjZXB0aW9uIGV4dGVuZHMgTmV0d29ya0V4Y2VwdGlvbiB7CiAgY29uc3QgVW5rbm93bk5ldHdvcmtFeGNlcHRpb24oe3N1cGVyLm1lc3NhZ2UsIHN1cGVyLnN0YXR1c0NvZGUsIHN1cGVyLmVycm9yfSk7CgogIEBvdmVycmlkZQogIFN0cmluZyBnZXQgZGlzcGxheU1lc3NhZ2UgPT4gbWVzc2FnZSA/PyBzdHJpbmdzLmVycm9ycy51bmtub3duX25ldHdvcms7Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/interceptors/auth_interceptor.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNvcmUvY29yZS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRpL2RpLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZGlvL2Rpby5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOm5ldHdvcmsvc3JjL2F1dGgvYXV0aF90b2tlbl9wcm92aWRlci5kYXJ0JzsKCmNsYXNzIEF1dGhJbnRlcmNlcHRvciBleHRlbmRzIEludGVyY2VwdG9yIHsKICBBdXRoSW50ZXJjZXB0b3IodGhpcy5fYXV0aFRva2VuUHJvdmlkZXIpIDogX2xvZ2dlciA9IGRpLmdldDxMb2dnZXI+KCk7CgogIGZpbmFsIEF1dGhUb2tlblByb3ZpZGVyIF9hdXRoVG9rZW5Qcm92aWRlcjsKICBmaW5hbCBMb2dnZXIgX2xvZ2dlcjsKCiAgQG92ZXJyaWRlCiAgdm9pZCBvblJlcXVlc3QoCiAgICBSZXF1ZXN0T3B0aW9ucyBvcHRpb25zLAogICAgUmVxdWVzdEludGVyY2VwdG9ySGFuZGxlciBoYW5kbGVyLAogICkgYXN5bmMgewogICAgdHJ5IHsKICAgICAgLy8gU2tpcCBhdXRoZW50aWNhdGlvbiBmb3IgcmVxdWVzdHMgdGhhdCBhbHJlYWR5IGhhdmUgYXV0aG9yaXphdGlvbiBoZWFkZXIKICAgICAgaWYgKG9wdGlvbnMuaGVhZGVycy5jb250YWluc0tleSgnQXV0aG9yaXphdGlvbicpKSB7CiAgICAgICAgaGFuZGxlci5uZXh0KG9wdGlvbnMpOwogICAgICAgIHJldHVybjsKICAgICAgfQoKICAgICAgZmluYWwgdG9rZW4gPSBhd2FpdCBfYXV0aFRva2VuUHJvdmlkZXIuZ2V0VmFsaWRUb2tlbigpOwogICAgICBpZiAodG9rZW4gIT0gbnVsbCkgewogICAgICAgIG9wdGlvbnMuaGVhZGVyc1snQXV0aG9yaXphdGlvbiddID0gJ0JlYXJlciAkdG9rZW4nOwogICAgICAgIF9sb2dnZXIuZCgn8J+UkCBBZGRlZCBCZWFyZXIgdG9rZW4gdG8gcmVxdWVzdDogJHtvcHRpb25zLnVyaX0nKTsKICAgICAgfSBlbHNlIHsKICAgICAgICBfbG9nZ2VyLmQoJ/CflJMgTm8gYXV0aCB0b2tlbiBhdmFpbGFibGUgZm9yIHJlcXVlc3Q6ICR7b3B0aW9ucy51cml9Jyk7CiAgICAgIH0KCiAgICAgIGhhbmRsZXIubmV4dChvcHRpb25zKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgX2xvZ2dlci5lKCfwn5qoIEVycm9yIGFkZGluZyBhdXRoIHRva2VuIHRvIHJlcXVlc3QnLCBlKTsKICAgICAgaGFuZGxlci5uZXh0KG9wdGlvbnMpOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgdm9pZCBvbkVycm9yKERpb0V4Y2VwdGlvbiBlcnIsIEVycm9ySW50ZXJjZXB0b3JIYW5kbGVyIGhhbmRsZXIpIGFzeW5jIHsKICAgIC8vIEhhbmRsZSA0MDEgVW5hdXRob3JpemVkIC0gdG9rZW4gbWlnaHQgYmUgZXhwaXJlZAogICAgaWYgKGVyci5yZXNwb25zZT8uc3RhdHVzQ29kZSA9PSA0MDEpIHsKICAgICAgX2xvZ2dlci53KCfwn5SQIFJlY2VpdmVkIDQwMSBVbmF1dGhvcml6ZWQsIGF0dGVtcHRpbmcgdG9rZW4gcmVmcmVzaCcpOwoKICAgICAgdHJ5IHsKICAgICAgICBmaW5hbCBuZXdUb2tlbiA9IGF3YWl0IF9hdXRoVG9rZW5Qcm92aWRlci5yZWZyZXNoVG9rZW4oKTsKICAgICAgICBpZiAobmV3VG9rZW4gIT0gbnVsbCkgewogICAgICAgICAgX2xvZ2dlci5pKCfwn5SEIFRva2VuIHJlZnJlc2hlZCBzdWNjZXNzZnVsbHksIHJldHJ5aW5nIHJlcXVlc3QnKTsKCiAgICAgICAgICAvLyBDbG9uZSB0aGUgcmVxdWVzdCB3aXRoIG5ldyB0b2tlbgogICAgICAgICAgZmluYWwgcmVxdWVzdE9wdGlvbnMgPSBlcnIucmVxdWVzdE9wdGlvbnM7CiAgICAgICAgICByZXF1ZXN0T3B0aW9ucy5oZWFkZXJzWydBdXRob3JpemF0aW9uJ10gPSAnQmVhcmVyICRuZXdUb2tlbic7CgogICAgICAgICAgLy8gUmV0cnkgdGhlIHJlcXVlc3QKICAgICAgICAgIGZpbmFsIHJlc3BvbnNlID0gYXdhaXQgRGlvKCkuZmV0Y2gocmVxdWVzdE9wdGlvbnMpOwogICAgICAgICAgaGFuZGxlci5yZXNvbHZlKHJlc3BvbnNlKTsKICAgICAgICAgIHJldHVybjsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgX2xvZ2dlci53KCfinYwgVG9rZW4gcmVmcmVzaCBmYWlsZWQsIHVzZXIgbmVlZHMgdG8gcmUtYXV0aGVudGljYXRlJyk7CiAgICAgICAgfQogICAgICB9IGNhdGNoIChyZWZyZXNoRXJyb3IpIHsKICAgICAgICBfbG9nZ2VyLmUoJ/CfmqggRXJyb3IgZHVyaW5nIHRva2VuIHJlZnJlc2gnLCByZWZyZXNoRXJyb3IpOwogICAgICB9CiAgICB9CgogICAgLy8gSGFuZGxlIDQwMyBGb3JiaWRkZW4gLSB1c2VyIGRvZXNuJ3QgaGF2ZSBwZXJtaXNzaW9uCiAgICBpZiAoZXJyLnJlc3BvbnNlPy5zdGF0dXNDb2RlID09IDQwMykgewogICAgICBfbG9nZ2VyLncoJ/CfmqsgUmVjZWl2ZWQgNDAzIEZvcmJpZGRlbiwgaW5zdWZmaWNpZW50IHBlcm1pc3Npb25zJyk7CiAgICB9CgogICAgaGFuZGxlci5uZXh0KGVycik7CiAgfQp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/interceptors/index.dart", + "data": + "ZXhwb3J0ICdhdXRoX2ludGVyY2VwdG9yLmRhcnQnOwpleHBvcnQgJ2xvZ2dpbmdfaW50ZXJjZXB0b3IuZGFydCc7Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/interceptors/logging_interceptor.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNvcmUvY29yZS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRpL2RpLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZGlvL2Rpby5kYXJ0JzsKCmNsYXNzIExvZ2dpbmdJbnRlcmNlcHRvciBleHRlbmRzIEludGVyY2VwdG9yIHsKICBMb2dnaW5nSW50ZXJjZXB0b3IoKSA6IF9sb2dnZXIgPSBkaS5nZXQ8TG9nZ2VyPigpOwoKICBmaW5hbCBMb2dnZXIgX2xvZ2dlcjsKCiAgQG92ZXJyaWRlCiAgdm9pZCBvblJlcXVlc3QoUmVxdWVzdE9wdGlvbnMgb3B0aW9ucywgUmVxdWVzdEludGVyY2VwdG9ySGFuZGxlciBoYW5kbGVyKSB7CiAgICBfbG9nZ2VyLmQoJ/CfmoAgUmVxdWVzdDogJHtvcHRpb25zLm1ldGhvZH0gJHtvcHRpb25zLnVyaX0nKTsKICAgIF9sb2dnZXIuZCgnSGVhZGVyczogJHtvcHRpb25zLmhlYWRlcnN9Jyk7CiAgICBpZiAob3B0aW9ucy5kYXRhICE9IG51bGwpIHsKICAgICAgX2xvZ2dlci5kKCdEYXRhOiAke29wdGlvbnMuZGF0YX0nKTsKICAgIH0KICAgIGlmIChvcHRpb25zLnF1ZXJ5UGFyYW1ldGVycy5pc05vdEVtcHR5KSB7CiAgICAgIF9sb2dnZXIuZCgnUXVlcnkgUGFyYW1ldGVyczogJHtvcHRpb25zLnF1ZXJ5UGFyYW1ldGVyc30nKTsKICAgIH0KICAgIHN1cGVyLm9uUmVxdWVzdChvcHRpb25zLCBoYW5kbGVyKTsKICB9CgogIEBvdmVycmlkZQogIHZvaWQgb25SZXNwb25zZShSZXNwb25zZSByZXNwb25zZSwgUmVzcG9uc2VJbnRlcmNlcHRvckhhbmRsZXIgaGFuZGxlcikgewogICAgX2xvZ2dlci5kKAogICAgICAn4pyFIFJlc3BvbnNlOiAke3Jlc3BvbnNlLnN0YXR1c0NvZGV9ICR7cmVzcG9uc2UucmVxdWVzdE9wdGlvbnMudXJpfScsCiAgICApOwogICAgX2xvZ2dlci5kKCdSZXNwb25zZSBEYXRhOiAke3Jlc3BvbnNlLmRhdGF9Jyk7CiAgICBzdXBlci5vblJlc3BvbnNlKHJlc3BvbnNlLCBoYW5kbGVyKTsKICB9CgogIEBvdmVycmlkZQogIHZvaWQgb25FcnJvcihEaW9FeGNlcHRpb24gZXJyLCBFcnJvckludGVyY2VwdG9ySGFuZGxlciBoYW5kbGVyKSB7CiAgICBfbG9nZ2VyLmUoCiAgICAgICfinYwgRXJyb3I6ICR7ZXJyLnJlc3BvbnNlPy5zdGF0dXNDb2RlfSAke2Vyci5yZXF1ZXN0T3B0aW9ucy51cml9JywKICAgICAgZXJyLAogICAgICBlcnIuc3RhY2tUcmFjZSwKICAgICk7CiAgICBpZiAoZXJyLnJlc3BvbnNlPy5kYXRhICE9IG51bGwpIHsKICAgICAgX2xvZ2dlci5lKCdFcnJvciBEYXRhOiAke2Vyci5yZXNwb25zZT8uZGF0YX0nKTsKICAgIH0KICAgIHN1cGVyLm9uRXJyb3IoZXJyLCBoYW5kbGVyKTsKICB9Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/models/index.dart", + "data": "ZXhwb3J0ICduZXR3b3JrX3Jlc3BvbnNlLmRhcnQnOwo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/models/network_response.dart", "data": - "IyBQdXQgYWxsIHlvdXIgc2hhcmVkIHBhY2thZ2VzIGluIHRoaXMgZm9sZGVyCg==", + "aW1wb3J0ICdwYWNrYWdlOmpzb25fYW5ub3RhdGlvbi9qc29uX2Fubm90YXRpb24uZGFydCc7CgpwYXJ0ICduZXR3b3JrX3Jlc3BvbnNlLmcuZGFydCc7CgphYnN0cmFjdCBjbGFzcyBOZXR3b3JrUmVzcG9uc2U8VD4gewogIGNvbnN0IE5ldHdvcmtSZXNwb25zZSh7CiAgICByZXF1aXJlZCB0aGlzLnN0YXR1c0NvZGUsCiAgICByZXF1aXJlZCB0aGlzLnN1Y2Nlc3MsCiAgICB0aGlzLm1lc3NhZ2UsCiAgICByZXF1aXJlZCB0aGlzLnRpbWVzdGFtcCwKICAgIHRoaXMuaGVhZGVycywKICB9KTsKCiAgZmluYWwgaW50IHN0YXR1c0NvZGU7CiAgZmluYWwgYm9vbCBzdWNjZXNzOwogIGZpbmFsIFN0cmluZz8gbWVzc2FnZTsKICBmaW5hbCBTdHJpbmcgdGltZXN0YW1wOwogIGZpbmFsIE1hcDxTdHJpbmcsIExpc3Q8U3RyaW5nPj4/IGhlYWRlcnM7CgogIGJvb2wgZ2V0IGlzU3VjY2VzcyA9PiBzdWNjZXNzICYmIHN0YXR1c0NvZGUgPj0gMjAwICYmIHN0YXR1c0NvZGUgPCAzMDA7Cn0KCkBKc29uU2VyaWFsaXphYmxlKGdlbmVyaWNBcmd1bWVudEZhY3RvcmllczogdHJ1ZSkKY2xhc3MgU3VjY2Vzc1Jlc3BvbnNlPFQ+IGV4dGVuZHMgTmV0d29ya1Jlc3BvbnNlPFQ+IHsKICBmaW5hbCBUIGRhdGE7CiAgZmluYWwgUGFnaW5hdGlvbj8gcGFnaW5hdGlvbjsKCiAgY29uc3QgU3VjY2Vzc1Jlc3BvbnNlKHsKICAgIHJlcXVpcmVkIHN1cGVyLnN0YXR1c0NvZGUsCiAgICByZXF1aXJlZCBzdXBlci5zdWNjZXNzLAogICAgcmVxdWlyZWQgdGhpcy5kYXRhLAogICAgc3VwZXIubWVzc2FnZSwKICAgIHJlcXVpcmVkIHN1cGVyLnRpbWVzdGFtcCwKICAgIHRoaXMucGFnaW5hdGlvbiwKICAgIHN1cGVyLmhlYWRlcnMsCiAgfSk7CgogIGZhY3RvcnkgU3VjY2Vzc1Jlc3BvbnNlLmZyb21Kc29uKAogICAgTWFwPFN0cmluZywgZHluYW1pYz4ganNvbiwKICAgIFQgRnVuY3Rpb24oT2JqZWN0PyBqc29uKSBmcm9tSnNvblQsCiAgKSA9PiBfJFN1Y2Nlc3NSZXNwb25zZUZyb21Kc29uKGpzb24sIGZyb21Kc29uVCk7CgogIE1hcDxTdHJpbmcsIGR5bmFtaWM+IHRvSnNvbihPYmplY3QgRnVuY3Rpb24oVCB2YWx1ZSkgdG9Kc29uVCkgPT4KICAgICAgXyRTdWNjZXNzUmVzcG9uc2VUb0pzb24odGhpcywgdG9Kc29uVCk7Cn0KCkBKc29uU2VyaWFsaXphYmxlKCkKY2xhc3MgRXJyb3JSZXNwb25zZTxUPiBleHRlbmRzIE5ldHdvcmtSZXNwb25zZTxUPiB7CiAgZmluYWwgRXJyb3JEZXRhaWxzIGVycm9yOwoKICBjb25zdCBFcnJvclJlc3BvbnNlKHsKICAgIHJlcXVpcmVkIHN1cGVyLnN0YXR1c0NvZGUsCiAgICByZXF1aXJlZCBzdXBlci5zdWNjZXNzLAogICAgcmVxdWlyZWQgdGhpcy5lcnJvciwKICAgIHJlcXVpcmVkIHN1cGVyLnRpbWVzdGFtcCwKICAgIHN1cGVyLm1lc3NhZ2UsCiAgICBzdXBlci5oZWFkZXJzLAogIH0pOwoKICBmYWN0b3J5IEVycm9yUmVzcG9uc2UuZnJvbUpzb24oTWFwPFN0cmluZywgZHluYW1pYz4ganNvbikgPT4KICAgICAgXyRFcnJvclJlc3BvbnNlRnJvbUpzb24oanNvbik7CgogIE1hcDxTdHJpbmcsIGR5bmFtaWM+IHRvSnNvbigpID0+IF8kRXJyb3JSZXNwb25zZVRvSnNvbih0aGlzKTsKfQoKQEpzb25TZXJpYWxpemFibGUoKQpjbGFzcyBFcnJvckRldGFpbHMgewogIGNvbnN0IEVycm9yRGV0YWlscyh7cmVxdWlyZWQgdGhpcy5jb2RlLCByZXF1aXJlZCB0aGlzLm1lc3NhZ2UsIHRoaXMuZGV0YWlsc30pOwoKICBmaW5hbCBTdHJpbmcgY29kZTsKICBmaW5hbCBTdHJpbmcgbWVzc2FnZTsKICBmaW5hbCBNYXA8U3RyaW5nLCBkeW5hbWljPj8gZGV0YWlsczsKCiAgZmFjdG9yeSBFcnJvckRldGFpbHMuZnJvbUpzb24oTWFwPFN0cmluZywgZHluYW1pYz4ganNvbikgPT4KICAgICAgXyRFcnJvckRldGFpbHNGcm9tSnNvbihqc29uKTsKCiAgTWFwPFN0cmluZywgZHluYW1pYz4gdG9Kc29uKCkgPT4gXyRFcnJvckRldGFpbHNUb0pzb24odGhpcyk7Cn0KCkBKc29uU2VyaWFsaXphYmxlKCkKY2xhc3MgUGFnaW5hdGlvbiB7CiAgY29uc3QgUGFnaW5hdGlvbih7CiAgICByZXF1aXJlZCB0aGlzLnBhZ2UsCiAgICByZXF1aXJlZCB0aGlzLmxpbWl0LAogICAgcmVxdWlyZWQgdGhpcy50b3RhbCwKICAgIHJlcXVpcmVkIHRoaXMudG90YWxQYWdlcywKICAgIHJlcXVpcmVkIHRoaXMuaGFzTmV4dFBhZ2UsCiAgICByZXF1aXJlZCB0aGlzLmhhc1ByZXZpb3VzUGFnZSwKICB9KTsKCiAgZmluYWwgaW50IHBhZ2U7CiAgZmluYWwgaW50IGxpbWl0OwogIGZpbmFsIGludCB0b3RhbDsKICBmaW5hbCBpbnQgdG90YWxQYWdlczsKICBmaW5hbCBib29sIGhhc05leHRQYWdlOwogIGZpbmFsIGJvb2wgaGFzUHJldmlvdXNQYWdlOwoKICBmYWN0b3J5IFBhZ2luYXRpb24uZnJvbUpzb24oTWFwPFN0cmluZywgZHluYW1pYz4ganNvbikgPT4KICAgICAgXyRQYWdpbmF0aW9uRnJvbUpzb24oanNvbik7CgogIE1hcDxTdHJpbmcsIGR5bmFtaWM+IHRvSnNvbigpID0+IF8kUGFnaW5hdGlvblRvSnNvbih0aGlzKTsKfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/models/network_response.g.dart", + "data": + "Ly8gR0VORVJBVEVEIENPREUgLSBETyBOT1QgTU9ESUZZIEJZIEhBTkQKCnBhcnQgb2YgJ25ldHdvcmtfcmVzcG9uc2UuZGFydCc7CgovLyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKgovLyBKc29uU2VyaWFsaXphYmxlR2VuZXJhdG9yCi8vICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqCgpTdWNjZXNzUmVzcG9uc2U8VD4gXyRTdWNjZXNzUmVzcG9uc2VGcm9tSnNvbjxUPigKICBNYXA8U3RyaW5nLCBkeW5hbWljPiBqc29uLAogIFQgRnVuY3Rpb24oT2JqZWN0PyBqc29uKSBmcm9tSnNvblQsCikgPT4gU3VjY2Vzc1Jlc3BvbnNlPFQ+KAogIHN0YXR1c0NvZGU6IChqc29uWydzdGF0dXNDb2RlJ10gYXMgbnVtKS50b0ludCgpLAogIHN1Y2Nlc3M6IGpzb25bJ3N1Y2Nlc3MnXSBhcyBib29sLAogIGRhdGE6IGZyb21Kc29uVChqc29uWydkYXRhJ10pLAogIG1lc3NhZ2U6IGpzb25bJ21lc3NhZ2UnXSBhcyBTdHJpbmc/LAogIHRpbWVzdGFtcDoganNvblsndGltZXN0YW1wJ10gYXMgU3RyaW5nLAogIHBhZ2luYXRpb246IGpzb25bJ3BhZ2luYXRpb24nXSA9PSBudWxsCiAgICAgID8gbnVsbAogICAgICA6IFBhZ2luYXRpb24uZnJvbUpzb24oanNvblsncGFnaW5hdGlvbiddIGFzIE1hcDxTdHJpbmcsIGR5bmFtaWM+KSwKICBoZWFkZXJzOiAoanNvblsnaGVhZGVycyddIGFzIE1hcDxTdHJpbmcsIGR5bmFtaWM+Pyk/Lm1hcCgKICAgIChrLCBlKSA9PgogICAgICAgIE1hcEVudHJ5KGssIChlIGFzIExpc3Q8ZHluYW1pYz4pLm1hcCgoZSkgPT4gZSBhcyBTdHJpbmcpLnRvTGlzdCgpKSwKICApLAopOwoKTWFwPFN0cmluZywgZHluYW1pYz4gXyRTdWNjZXNzUmVzcG9uc2VUb0pzb248VD4oCiAgU3VjY2Vzc1Jlc3BvbnNlPFQ+IGluc3RhbmNlLAogIE9iamVjdD8gRnVuY3Rpb24oVCB2YWx1ZSkgdG9Kc29uVCwKKSA9PiA8U3RyaW5nLCBkeW5hbWljPnsKICAnc3RhdHVzQ29kZSc6IGluc3RhbmNlLnN0YXR1c0NvZGUsCiAgJ3N1Y2Nlc3MnOiBpbnN0YW5jZS5zdWNjZXNzLAogICdtZXNzYWdlJzogaW5zdGFuY2UubWVzc2FnZSwKICAndGltZXN0YW1wJzogaW5zdGFuY2UudGltZXN0YW1wLAogICdoZWFkZXJzJzogaW5zdGFuY2UuaGVhZGVycywKICAnZGF0YSc6IHRvSnNvblQoaW5zdGFuY2UuZGF0YSksCiAgJ3BhZ2luYXRpb24nOiBpbnN0YW5jZS5wYWdpbmF0aW9uLAp9OwoKRXJyb3JSZXNwb25zZTxUPiBfJEVycm9yUmVzcG9uc2VGcm9tSnNvbjxUPihNYXA8U3RyaW5nLCBkeW5hbWljPiBqc29uKSA9PgogICAgRXJyb3JSZXNwb25zZTxUPigKICAgICAgc3RhdHVzQ29kZTogKGpzb25bJ3N0YXR1c0NvZGUnXSBhcyBudW0pLnRvSW50KCksCiAgICAgIHN1Y2Nlc3M6IGpzb25bJ3N1Y2Nlc3MnXSBhcyBib29sLAogICAgICBlcnJvcjogRXJyb3JEZXRhaWxzLmZyb21Kc29uKGpzb25bJ2Vycm9yJ10gYXMgTWFwPFN0cmluZywgZHluYW1pYz4pLAogICAgICB0aW1lc3RhbXA6IGpzb25bJ3RpbWVzdGFtcCddIGFzIFN0cmluZywKICAgICAgbWVzc2FnZToganNvblsnbWVzc2FnZSddIGFzIFN0cmluZz8sCiAgICAgIGhlYWRlcnM6IChqc29uWydoZWFkZXJzJ10gYXMgTWFwPFN0cmluZywgZHluYW1pYz4/KT8ubWFwKAogICAgICAgIChrLCBlKSA9PgogICAgICAgICAgICBNYXBFbnRyeShrLCAoZSBhcyBMaXN0PGR5bmFtaWM+KS5tYXAoKGUpID0+IGUgYXMgU3RyaW5nKS50b0xpc3QoKSksCiAgICAgICksCiAgICApOwoKTWFwPFN0cmluZywgZHluYW1pYz4gXyRFcnJvclJlc3BvbnNlVG9Kc29uPFQ+KEVycm9yUmVzcG9uc2U8VD4gaW5zdGFuY2UpID0+CiAgICA8U3RyaW5nLCBkeW5hbWljPnsKICAgICAgJ3N0YXR1c0NvZGUnOiBpbnN0YW5jZS5zdGF0dXNDb2RlLAogICAgICAnc3VjY2Vzcyc6IGluc3RhbmNlLnN1Y2Nlc3MsCiAgICAgICdtZXNzYWdlJzogaW5zdGFuY2UubWVzc2FnZSwKICAgICAgJ3RpbWVzdGFtcCc6IGluc3RhbmNlLnRpbWVzdGFtcCwKICAgICAgJ2hlYWRlcnMnOiBpbnN0YW5jZS5oZWFkZXJzLAogICAgICAnZXJyb3InOiBpbnN0YW5jZS5lcnJvciwKICAgIH07CgpFcnJvckRldGFpbHMgXyRFcnJvckRldGFpbHNGcm9tSnNvbihNYXA8U3RyaW5nLCBkeW5hbWljPiBqc29uKSA9PiBFcnJvckRldGFpbHMoCiAgY29kZToganNvblsnY29kZSddIGFzIFN0cmluZywKICBtZXNzYWdlOiBqc29uWydtZXNzYWdlJ10gYXMgU3RyaW5nLAogIGRldGFpbHM6IGpzb25bJ2RldGFpbHMnXSBhcyBNYXA8U3RyaW5nLCBkeW5hbWljPj8sCik7CgpNYXA8U3RyaW5nLCBkeW5hbWljPiBfJEVycm9yRGV0YWlsc1RvSnNvbihFcnJvckRldGFpbHMgaW5zdGFuY2UpID0+CiAgICA8U3RyaW5nLCBkeW5hbWljPnsKICAgICAgJ2NvZGUnOiBpbnN0YW5jZS5jb2RlLAogICAgICAnbWVzc2FnZSc6IGluc3RhbmNlLm1lc3NhZ2UsCiAgICAgICdkZXRhaWxzJzogaW5zdGFuY2UuZGV0YWlscywKICAgIH07CgpQYWdpbmF0aW9uIF8kUGFnaW5hdGlvbkZyb21Kc29uKE1hcDxTdHJpbmcsIGR5bmFtaWM+IGpzb24pID0+IFBhZ2luYXRpb24oCiAgcGFnZTogKGpzb25bJ3BhZ2UnXSBhcyBudW0pLnRvSW50KCksCiAgbGltaXQ6IChqc29uWydsaW1pdCddIGFzIG51bSkudG9JbnQoKSwKICB0b3RhbDogKGpzb25bJ3RvdGFsJ10gYXMgbnVtKS50b0ludCgpLAogIHRvdGFsUGFnZXM6IChqc29uWyd0b3RhbFBhZ2VzJ10gYXMgbnVtKS50b0ludCgpLAogIGhhc05leHRQYWdlOiBqc29uWydoYXNOZXh0UGFnZSddIGFzIGJvb2wsCiAgaGFzUHJldmlvdXNQYWdlOiBqc29uWydoYXNQcmV2aW91c1BhZ2UnXSBhcyBib29sLAopOwoKTWFwPFN0cmluZywgZHluYW1pYz4gXyRQYWdpbmF0aW9uVG9Kc29uKFBhZ2luYXRpb24gaW5zdGFuY2UpID0+CiAgICA8U3RyaW5nLCBkeW5hbWljPnsKICAgICAgJ3BhZ2UnOiBpbnN0YW5jZS5wYWdlLAogICAgICAnbGltaXQnOiBpbnN0YW5jZS5saW1pdCwKICAgICAgJ3RvdGFsJzogaW5zdGFuY2UudG90YWwsCiAgICAgICd0b3RhbFBhZ2VzJzogaW5zdGFuY2UudG90YWxQYWdlcywKICAgICAgJ2hhc05leHRQYWdlJzogaW5zdGFuY2UuaGFzTmV4dFBhZ2UsCiAgICAgICdoYXNQcmV2aW91c1BhZ2UnOiBpbnN0YW5jZS5oYXNQcmV2aW91c1BhZ2UsCiAgICB9Owo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/utils/extensions/error_message_extension.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNvcmUvY29yZS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvZm91bmRhdGlvbi5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmxvY2FsaXphdGlvbi9sb2NhbGl6YXRpb24uZGFydCc7CmltcG9ydCAncGFja2FnZTpuZXR3b3JrL25ldHdvcmsuZGFydCc7CgpleHRlbnNpb24gRXJyb3JNZXNzYWdlRXh0ZW5zaW9uIG9uIE9iamVjdD8gewogIFN0cmluZyBtZXNzYWdlKCkgewogICAgcmV0dXJuIHN3aXRjaCAodGhpcykgewogICAgICBOZXR3b3JrRXhjZXB0aW9uIGVycm9yID0+IGVycm9yLmRpc3BsYXlNZXNzYWdlLAogICAgICBDb3JlRXhjZXB0aW9uIGVycm9yID0+IGVycm9yLm1lc3NhZ2UsCiAgICAgIEZvcm1hdEV4Y2VwdGlvbiBfID0+IHN0cmluZ3MuZXJyb3JzLmZvcm1hdF9leGNlcHRpb25fbWVzc2FnZSgKICAgICAgICBFcnJvckNvZGVzLmZvcm1hdEV4Y2VwdGlvbiwKICAgICAgICBkZWJ1Z01lc3NhZ2UsCiAgICAgICksCiAgICAgIEVycm9yIGVycm9yID0+IGVycm9yLm1lc3NhZ2UoKSwKICAgICAgXyA9PiBzdHJpbmdzLmVycm9ycy5kZWZhdWx0X2Vycm9yX21lc3NhZ2UsCiAgICB9OwogIH0KCiAgU3RyaW5nIGdldCBkZWJ1Z01lc3NhZ2UgewogICAgaWYgKGtEZWJ1Z01vZGUpIHsKICAgICAgcmV0dXJuICdcbiR7dG9TdHJpbmcoKX1cbnR5cGU6ICRydW50aW1lVHlwZSc7CiAgICB9CiAgICByZXR1cm4gJyc7CiAgfQp9CgovLyBTaW1wbGUgY3JlYXRpdmUgZXJyb3IgbWVzc2FnZXMgd2l0aCBjb2RlcwpleHRlbnNpb24gRXJyb3JNZXNzYWdlT25FcnJvciBvbiBFcnJvciB7CiAgU3RyaW5nIG1lc3NhZ2UoKSB7CiAgICByZXR1cm4gc3dpdGNoICh0aGlzKSB7CiAgICAgIFR5cGVFcnJvciBfID0+IHN0cmluZ3MuZXJyb3JzLnR5cGVfZXJyb3JfbWVzc2FnZSgKICAgICAgICBFcnJvckNvZGVzLnR5cGVFcnJvciwKICAgICAgICBkZWJ1Z01lc3NhZ2UsCiAgICAgICksCiAgICAgIEluZGV4RXJyb3IgXyA9PiBzdHJpbmdzLmVycm9ycy5pbmRleF9lcnJvcl9tZXNzYWdlKAogICAgICAgIEVycm9yQ29kZXMuaW5kZXhFcnJvciwKICAgICAgICBkZWJ1Z01lc3NhZ2UsCiAgICAgICksCiAgICAgIFJhbmdlRXJyb3IgXyA9PiBzdHJpbmdzLmVycm9ycy5yYW5nZV9lcnJvcl9tZXNzYWdlKAogICAgICAgIEVycm9yQ29kZXMucmFuZ2VFcnJvciwKICAgICAgICBkZWJ1Z01lc3NhZ2UsCiAgICAgICksCiAgICAgIEFyZ3VtZW50RXJyb3IgXyA9PiBzdHJpbmdzLmVycm9ycy5hcmd1bWVudF9lcnJvcl9tZXNzYWdlKAogICAgICAgIEVycm9yQ29kZXMuYXJndW1lbnRFcnJvciwKICAgICAgICBkZWJ1Z01lc3NhZ2UsCiAgICAgICksCiAgICAgIFN0YXRlRXJyb3IgXyA9PiBzdHJpbmdzLmVycm9ycy5zdGF0ZV9lcnJvcl9tZXNzYWdlKAogICAgICAgIEVycm9yQ29kZXMuc3RhdGVFcnJvciwKICAgICAgICBkZWJ1Z01lc3NhZ2UsCiAgICAgICksCiAgICAgIFVuaW1wbGVtZW50ZWRFcnJvciBfID0+IHN0cmluZ3MuZXJyb3JzLnVuaW1wbGVtZW50ZWRfZXJyb3JfbWVzc2FnZSgKICAgICAgICBFcnJvckNvZGVzLnVuaW1wbGVtZW50ZWRFcnJvciwKICAgICAgICBkZWJ1Z01lc3NhZ2UsCiAgICAgICksCiAgICAgIFVuc3VwcG9ydGVkRXJyb3IgXyA9PiBzdHJpbmdzLmVycm9ycy51bnN1cHBvcnRlZF9lcnJvcl9tZXNzYWdlKAogICAgICAgIEVycm9yQ29kZXMudW5zdXBwb3J0ZWRFcnJvciwKICAgICAgICBkZWJ1Z01lc3NhZ2UsCiAgICAgICksCiAgICAgIENvbmN1cnJlbnRNb2RpZmljYXRpb25FcnJvciBfID0+CiAgICAgICAgc3RyaW5ncy5lcnJvcnMuY29uY3VycmVudF9tb2RpZmljYXRpb25fZXJyb3JfbWVzc2FnZSgKICAgICAgICAgIEVycm9yQ29kZXMuY29uY3VycmVudE1vZGlmaWNhdGlvbkVycm9yLAogICAgICAgICAgZGVidWdNZXNzYWdlLAogICAgICAgICksCiAgICAgIE91dE9mTWVtb3J5RXJyb3IgXyA9PiBzdHJpbmdzLmVycm9ycy5vdXRfb2ZfbWVtb3J5X2Vycm9yX21lc3NhZ2UoCiAgICAgICAgRXJyb3JDb2Rlcy5vdXRPZk1lbW9yeUVycm9yLAogICAgICAgIGRlYnVnTWVzc2FnZSwKICAgICAgKSwKICAgICAgU3RhY2tPdmVyZmxvd0Vycm9yIF8gPT4gc3RyaW5ncy5lcnJvcnMuc3RhY2tfb3ZlcmZsb3dfZXJyb3JfbWVzc2FnZSgKICAgICAgICBFcnJvckNvZGVzLnN0YWNrT3ZlcmZsb3dFcnJvciwKICAgICAgICBkZWJ1Z01lc3NhZ2UsCiAgICAgICksCiAgICAgIF8gPT4gc3RyaW5ncy5lcnJvcnMudW5rbm93bl9lcnJvcl9tZXNzYWdlKAogICAgICAgIEVycm9yQ29kZXMudW5rbm93bkVycm9yLAogICAgICAgIGRlYnVnTWVzc2FnZSwKICAgICAgKSwKICAgIH07CiAgfQp9CgpzZWFsZWQgY2xhc3MgRXJyb3JDb2RlcyB7CiAgc3RhdGljIGNvbnN0IFN0cmluZyB0eXBlRXJyb3IgPSAiRTAwMSI7CiAgc3RhdGljIGNvbnN0IFN0cmluZyBhcmd1bWVudEVycm9yID0gIkUwMDIiOwogIHN0YXRpYyBjb25zdCBTdHJpbmcgcmFuZ2VFcnJvciA9ICJFMDAzIjsKICBzdGF0aWMgY29uc3QgU3RyaW5nIGluZGV4RXJyb3IgPSAiRTAwNCI7CiAgc3RhdGljIGNvbnN0IFN0cmluZyBzdGF0ZUVycm9yID0gIkUwMDUiOwogIHN0YXRpYyBjb25zdCBTdHJpbmcgdW5zdXBwb3J0ZWRFcnJvciA9ICJFMDA2IjsKICBzdGF0aWMgY29uc3QgU3RyaW5nIHVuaW1wbGVtZW50ZWRFcnJvciA9ICJFMDA3IjsKICBzdGF0aWMgY29uc3QgU3RyaW5nIGNvbmN1cnJlbnRNb2RpZmljYXRpb25FcnJvciA9ICJFMDA4IjsKICBzdGF0aWMgY29uc3QgU3RyaW5nIG91dE9mTWVtb3J5RXJyb3IgPSAiRTAwOSI7CiAgc3RhdGljIGNvbnN0IFN0cmluZyBzdGFja092ZXJmbG93RXJyb3IgPSAiRTAxMCI7CiAgc3RhdGljIGNvbnN0IFN0cmluZyBmb3JtYXRFeGNlcHRpb24gPSAiRTAxMSI7CiAgc3RhdGljIGNvbnN0IFN0cmluZyB1bmtub3duRXJyb3IgPSAiRTk5OSI7CiAgc3RhdGljIGNvbnN0IFN0cmluZyBuZXR3b3JrRXhjZXB0aW9uID0gIkUxMDAiOwogIHN0YXRpYyBjb25zdCBTdHJpbmcgY29yZUV4Y2VwdGlvbiA9ICJFMTAxIjsKICBzdGF0aWMgY29uc3QgU3RyaW5nIGRlZmF1bHRFcnJvciA9ICJFMDAwIjsKfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/utils/extensions/index.dart", + "data": "ZXhwb3J0ICdlcnJvcl9tZXNzYWdlX2V4dGVuc2lvbi5kYXJ0JzsK", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/lib/src/utils/index.dart", + "data": "ZXhwb3J0ICdleHRlbnNpb25zL2luZGV4LmRhcnQnOwo=", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/{{network_dir}}/pubspec.yaml", + "data": + "bmFtZTogbmV0d29yawpkZXNjcmlwdGlvbjogIkhUVFAgbGF5ZXIgZm9yIHt7bmFtZS50aXRsZUNhc2UoKX19OiBEaW8gY2xpZW50LCBpbnRlcmNlcHRvcnMsIHR5cGVkIHJlc3BvbnNlcyBhbmQgZXJyb3IgbWFwcGluZy4iCnZlcnNpb246IDAuMS4wCnB1Ymxpc2hfdG86ICdub25lJwoKZW52aXJvbm1lbnQ6CiAgc2RrOiBeMy44LjAKICBmbHV0dGVyOiAiPj0zLjI5LjAiCgpkZXBlbmRlbmNpZXM6CiAgZmx1dHRlcjoKICAgIHNkazogZmx1dHRlcgogIGRpbzogXjUuNy4wCiAganNvbl9hbm5vdGF0aW9uOiBeNC45LjAKICB0YWxrZXJfZGlvX2xvZ2dlcjogXjQuOS4xCiAgdGFsa2VyX2ZsdXR0ZXI6IF40LjQuMQogIGNvcmU6CiAgICBwYXRoOiAuLi9jb3JlCiAgbG9jYWxpemF0aW9uOgogICAgcGF0aDogLi4vbG9jYWxpemF0aW9uCiAgZGk6CiAgICBwYXRoOiAuLi9kaQoKZGV2X2RlcGVuZGVuY2llczoKICBmbHV0dGVyX3Rlc3Q6CiAgICBzZGs6IGZsdXR0ZXIKICBmbHV0dGVyX2xpbnRzOiBeNS4wLjAKICBidWlsZF9ydW5uZXI6IF4yLjQuMTUKICBqc29uX3NlcmlhbGl6YWJsZTogXjYuOS41CgpmbHV0dGVyOgo=", + "type": "text" + }, + { + "path": "{{ name.snakeCase() }}/packages/{{network_dir}}/README.md", + "data": + "IyBOZXR3b3JrCgpBIE1lbG9zLW1hbmFnZWQgcHJvamVjdCBmb3IgbW9uby1yZXBvLCBjcmVhdGVkIHVzaW5nIE5vblN0b3AgQ0xJLgoKWyFbbm9uc3RvcF9jbGldKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2Uvc3RhcnRlZCUyMHdpdGgtbm9uc3RvcF9jbGktMTY2QzRFLnN2Zz9zdHlsZT1mbGF0LXNxdWFyZSldKGh0dHBzOi8vcHViLmRldi9wYWNrYWdlcy9ub25zdG9wX2NsaSkKWyFbbWVsb3NdKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2UvbWFpbnRhaW5lZCUyMHdpdGgtbWVsb3MtZjcwMGZmLnN2Zz9zdHlsZT1mbGF0LXNxdWFyZSldKGh0dHBzOi8vZ2l0aHViLmNvbS9pbnZlcnRhc2UvbWVsb3MpCgojIyBJbnN0YWxsYXRpb24g8J+SuwoKKirinZcgSW4gb3JkZXIgdG8gc3RhcnQgdXNpbmcgTmV0d29yayB5b3UgbXVzdCBoYXZlIHRoZSBbRmx1dHRlciBTREtdW2ZsdXR0ZXJfaW5zdGFsbF9saW5rXSBpbnN0YWxsZWQgb24geW91ciBtYWNoaW5lLioqCgpJbnN0YWxsIHZpYSBgZGFydCBwdWIgYWRkYDoKCmBgYHNoCmRhcnQgcHViIGFkZCBuZXR3b3JrCmBgYAoKW2ZsdXR0ZXJfaW5zdGFsbF9saW5rXTogaHR0cHM6Ly9kb2NzLmZsdXR0ZXIuZGV2L2dldC1zdGFydGVkL2luc3RhbGw=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{network_dir}}/test/network_test.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXJfdGVzdC9mbHV0dGVyX3Rlc3QuZGFydCc7CmltcG9ydCAncGFja2FnZTpuZXR3b3JrL25ldHdvcmsuZGFydCc7Cgp2b2lkIG1haW4oKSB7CiAgdGVzdCgnRGVmYXVsdE5ldHdvcmtDb25maWcgY3JlYXRlcyBpbnN0YW5jZSB3aXRoIHByb3ZpZGVkIHZhbHVlcycsICgpIHsKICAgIGNvbnN0IGNvbmZpZyA9IERlZmF1bHROZXR3b3JrQ29uZmlnKAogICAgICBiYXNlVXJsOiAnaHR0cHM6Ly9hcGkuZXhhbXBsZS5jb20nLAogICAgICBlbmFibGVMb2dnaW5nOiB0cnVlLAogICAgKTsKCiAgICBleHBlY3QoY29uZmlnLmJhc2VVcmwsICdodHRwczovL2FwaS5leGFtcGxlLmNvbScpOwogICAgZXhwZWN0KGNvbmZpZy5lbmFibGVMb2dnaW5nLCB0cnVlKTsKICAgIGV4cGVjdChjb25maWcuY29ubmVjdFRpbWVvdXQsIGNvbnN0IER1cmF0aW9uKHNlY29uZHM6IDMwKSk7CiAgfSk7Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/notifications.dart", + "data": + "bGlicmFyeTsKCmltcG9ydCAnZGFydDphc3luYyc7CgppbXBvcnQgJ3BhY2thZ2U6Y29yZS9jb3JlLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZGkvZGkuZGFydCc7CmltcG9ydCAncGFja2FnZTpub3RpZmljYXRpb25zL3NyYy9jb25maWcvaW5kZXguZGFydCc7CmltcG9ydCAncGFja2FnZTpub3RpZmljYXRpb25zL3NyYy9kaS9pbmRleC5kYXJ0JzsKCmV4cG9ydCAnc3JjL2NsaWVudC9pbmRleC5kYXJ0JzsKZXhwb3J0ICdzcmMvY29uZmlnL2luZGV4LmRhcnQnOwpleHBvcnQgJ3NyYy9kZXZpY2VfaW5mby9pbmRleC5kYXJ0JzsKZXhwb3J0ICdzcmMvZXhjZXB0aW9ucy9pbmRleC5kYXJ0JzsKZXhwb3J0ICdzcmMvbW9kZWxzL2luZGV4LmRhcnQnOwpleHBvcnQgJ3NyYy9zZXJ2aWNlcy9pbmRleC5kYXJ0JzsKCi8vLyBJbml0aWFsaXplIHRoZSBub3RpZmljYXRpb25zIG1vZHVsZSB3aXRoIGRlcGVuZGVuY3kgaW5qZWN0aW9uCkZ1dHVyZTx2b2lkPiBpbml0KHsKICBOb3RpZmljYXRpb25Db25maWcgY29uZmlnID0gY29uc3QgRGVmYXVsdE5vdGlmaWNhdGlvbkNvbmZpZygpLAp9KSBhc3luYyB7CiAgZmluYWwgbG9nZ2VyID0gZGkuZ2V0PExvZ2dlcj4oKTsKICB0cnkgewogICAgYXdhaXQgcmVnaXN0ZXJOb3RpZmljYXRpb25XaXRoREkoY29uZmlnKTsKICAgIGxvZ2dlci5pKCfwn5SUIE5vdGlmaWNhdGlvbnMgbW9kdWxlIGluaXRpYWxpemVkJyk7CiAgfSBjYXRjaCAoZSwgcykgewogICAgbG9nZ2VyLmUoJ/CflJQgRmFpbGVkIHRvIGluaXRpYWxpemUgbm90aWZpY2F0aW9ucyBtb2R1bGUnLCBlLCBzKTsKICB9Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/firebase_notification_client.dart", + "data": + "aW1wb3J0ICdkYXJ0OmFzeW5jJzsKCmltcG9ydCAncGFja2FnZTphcHBfYmFkZ2VfcGx1cy9hcHBfYmFkZ2VfcGx1cy5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmNvcmUvbG9nZ2VyL2xvZ2dlci5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRlc2lnbl9zeXN0ZW0vdG9hc3QvdG9hc3RzLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZGkvZGkuZGFydCc7CmltcG9ydCAncGFja2FnZTpmaXJlYmFzZV9tZXNzYWdpbmcvZmlyZWJhc2VfbWVzc2FnaW5nLmRhcnQnCiAgICBoaWRlIE5vdGlmaWNhdGlvblNldHRpbmdzOwppbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlci9tYXRlcmlhbC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmdvX3JvdXRlci9nb19yb3V0ZXIuZGFydCc7CmltcG9ydCAncGFja2FnZTpuZXR3b3JrL25ldHdvcmsuZGFydCc7CmltcG9ydCAncGFja2FnZTpub3RpZmljYXRpb25zL25vdGlmaWNhdGlvbnMuZGFydCc7Cgpjb25zdCBTdHJpbmcgX3RhZyA9ICdGaXJlYmFzZU5vdGlmaWNhdGlvbkNsaWVudCc7CgovLy8gRmlyZWJhc2UgaW1wbGVtZW50YXRpb24gb2YgdGhlIG5vdGlmaWNhdGlvbiBjbGllbnQKY2xhc3MgRmlyZWJhc2VOb3RpZmljYXRpb25DbGllbnQgaW1wbGVtZW50cyBOb3RpZmljYXRpb25DbGllbnQgewogIEZpcmViYXNlTm90aWZpY2F0aW9uQ2xpZW50KHsKICAgIHJlcXVpcmVkIHRoaXMuY29uZmlnLAogICAgTG9nZ2VyPyBsb2dnZXIsCiAgICBOb3RpZmljYXRpb25Ub2tlbk1hbmFnZXI/IHRva2VuTWFuYWdlciwKICAgIE5vdGlmaWNhdGlvblBlcm1pc3Npb25NYW5hZ2VyPyBwZXJtaXNzaW9uTWFuYWdlciwKICB9KSA6IF9sb2dnZXIgPSBsb2dnZXIgPz8gZGkuZ2V0PExvZ2dlcj4oKSwKICAgICAgIF90b2tlbk1hbmFnZXIgPSB0b2tlbk1hbmFnZXIgPz8gZGkuZ2V0PE5vdGlmaWNhdGlvblRva2VuTWFuYWdlcj4oKSwKICAgICAgIF9wZXJtaXNzaW9uTWFuYWdlciA9CiAgICAgICAgICAgcGVybWlzc2lvbk1hbmFnZXIgPz8gZGkuZ2V0PE5vdGlmaWNhdGlvblBlcm1pc3Npb25NYW5hZ2VyPigpOwoKICBmaW5hbCBOb3RpZmljYXRpb25Db25maWcgY29uZmlnOwogIGZpbmFsIExvZ2dlciBfbG9nZ2VyOwogIGZpbmFsIE5vdGlmaWNhdGlvblRva2VuTWFuYWdlciBfdG9rZW5NYW5hZ2VyOwogIGZpbmFsIE5vdGlmaWNhdGlvblBlcm1pc3Npb25NYW5hZ2VyIF9wZXJtaXNzaW9uTWFuYWdlcjsKICBib29sIF9pc0dyYW50ZWQgPSBmYWxzZTsKCiAgRmlyZWJhc2VNZXNzYWdpbmc/IF9maXJlYmFzZU1lc3NhZ2luZzsKCiAgU3RyaW5nPyBfZmNtVG9rZW47CiAgU3RyaW5nPyBfZGV2aWNlSWQ7CgogIC8vIFN0b3JlIHN1YnNjcmlwdGlvbnMgZm9yIGNsZWFudXAKICBTdHJlYW1TdWJzY3JpcHRpb248UmVtb3RlTWVzc2FnZT4/IF9mb3JlZ3JvdW5kTWVzc2FnZVN1YnNjcmlwdGlvbjsKICBTdHJlYW1TdWJzY3JpcHRpb248UmVtb3RlTWVzc2FnZT4/IF9tZXNzYWdlT3BlbmVkQXBwU3Vic2NyaXB0aW9uOwogIFN0cmVhbVN1YnNjcmlwdGlvbjxTdHJpbmc+PyBfdG9rZW5SZWZyZXNoU3Vic2NyaXB0aW9uOwoKICBAb3ZlcnJpZGUKICBGdXR1cmU8dm9pZD4gaW5pdCgpIGFzeW5jIHsKICAgIHRyeSB7CiAgICAgIF9sb2dnZXIuaSgnJF90YWc6IEluaXRpYWxpemluZyBGaXJlYmFzZSBub3RpZmljYXRpb24gY2xpZW50Jyk7CgogICAgICAvLyBJbml0aWFsaXplIEZpcmViYXNlIE1lc3NhZ2luZwogICAgICBfZmlyZWJhc2VNZXNzYWdpbmcgPSBGaXJlYmFzZU1lc3NhZ2luZy5pbnN0YW5jZTsKCiAgICAgIGZpbmFsIGlzR3JhbnRlZCA9IGF3YWl0IHJlcXVlc3RQZXJtaXNzaW9ucygpOwogICAgICBpZiAoIWlzR3JhbnRlZCkgewogICAgICAgIF9sb2dnZXIudygnJF90YWc6IE5vdGlmaWNhdGlvbiBwZXJtaXNzaW9ucyBub3QgZ3JhbnRlZCcpOwogICAgICAgIHJldHVybjsKICAgICAgfQogICAgICBfaXNHcmFudGVkID0gaXNHcmFudGVkOwoKICAgICAgLy8gU2V0dXAgRkNNIGhhbmRsZXJzIGlmIGJhY2tncm91bmQgbm90aWZpY2F0aW9ucyBhcmUgZW5hYmxlZAogICAgICBhd2FpdCBfc2V0dXBGQ01IYW5kbGVycygpOwoKICAgICAgLy8gR2V0IEZDTSB0b2tlbiBhbmQgcmVnaXN0ZXIgaWYgYmFja2VuZCByZWdpc3RyYXRpb24gaXMgZW5hYmxlZAogICAgICBhd2FpdCBnZXRGQ01Ub2tlbigpOwoKICAgICAgLy8gQ2xlYXIgYmFkZ2UgY291bnQgYXMgdGhlIGxhc3Qgc3RlcCBvZiBpbml0aWFsaXphdGlvbgogICAgICBhd2FpdCBjbGVhckJhZGdlKCk7CgogICAgICBfbG9nZ2VyLmkoJyRfdGFnOiBGaXJlYmFzZSBub3RpZmljYXRpb24gY2xpZW50IGluaXRpYWxpemVkIHN1Y2Nlc3NmdWxseScpOwogICAgfSBjYXRjaCAoZSwgcykgewogICAgICBfbG9nZ2VyLmUoJyRfdGFnOiBFcnJvciBpbml0aWFsaXppbmcgRmlyZWJhc2Ugbm90aWZpY2F0aW9uIGNsaWVudCcsIGUsIHMpOwogICAgICB0aHJvdyBOb3RpZmljYXRpb25FeGNlcHRpb24obWVzc2FnZTogZS5tZXNzYWdlKCkpOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPGJvb2w+IHJlcXVlc3RQZXJtaXNzaW9ucyh7Ym9vbCBwcm92aXNpb25hbCA9IGZhbHNlfSkgYXN5bmMgewogICAgcmV0dXJuIGF3YWl0IF9wZXJtaXNzaW9uTWFuYWdlci5yZXF1ZXN0UGVybWlzc2lvbnMoKTsKICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTx2b2lkPiBjbGVhckJhZGdlKCkgYXN5bmMgewogICAgdHJ5IHsKICAgICAgX2xvZ2dlci5pKCfwn5SUIENsZWFyaW5nIGJhZGdlIGNvdW50Jyk7CiAgICAgIGlmICghX2lzR3JhbnRlZCkgewogICAgICAgIF9sb2dnZXIudygKICAgICAgICAgICfwn5SUIE5vdGlmaWNhdGlvbiBwZXJtaXNzaW9ucyAnCiAgICAgICAgICAnbm90IGdyYW50ZWQsIGNhbm5vdCBjbGVhciBiYWRnZScsCiAgICAgICAgKTsKICAgICAgICByZXR1cm47CiAgICAgIH0KICAgICAgaWYgKCFhd2FpdCBBcHBCYWRnZVBsdXMuaXNTdXBwb3J0ZWQoKSkgewogICAgICAgIF9sb2dnZXIudygn8J+UlCBBcHBCYWRnZVBsdXMgaXMgbm90IHN1cHBvcnRlZCBvbiB0aGlzIHBsYXRmb3JtJyk7CiAgICAgICAgcmV0dXJuOwogICAgICB9CiAgICAgIGF3YWl0IEFwcEJhZGdlUGx1cy51cGRhdGVCYWRnZSgwKTsKICAgICAgX2xvZ2dlci5pKCfwn5SUIEJhZGdlIGNvdW50IGNsZWFyZWQnKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgX2xvZ2dlci53KCfwn5SUIEZhaWxlZCB0byBjbGVhciBiYWRnZSBjb3VudDogJGUnKTsKICAgIH0KICB9CgogIEZ1dHVyZTx2b2lkPiBfc2V0dXBGQ01IYW5kbGVycygpIGFzeW5jIHsKICAgIC8vIEhhbmRsZSBiYWNrZ3JvdW5kIG1lc3NhZ2VzICh3aGVuIGFwcCBpcyB0ZXJtaW5hdGVkKQogICAgRmlyZWJhc2VNZXNzYWdpbmcub25CYWNrZ3JvdW5kTWVzc2FnZShfaGFuZGxlQmFja2dyb3VuZE5vdGlmaWNhdGlvbik7CgogICAgLy8gSGFuZGxlIGZvcmVncm91bmQgbWVzc2FnZXMKICAgIF9mb3JlZ3JvdW5kTWVzc2FnZVN1YnNjcmlwdGlvbiA9IEZpcmViYXNlTWVzc2FnaW5nLm9uTWVzc2FnZS5saXN0ZW4oCiAgICAgIChSZW1vdGVNZXNzYWdlIG1lc3NhZ2UpIHsKICAgICAgICBfbG9nZ2VyLmkoJyRfdGFnOiBSZWNlaXZlZCBmb3JlZ3JvdW5kIG1lc3NhZ2U6ICR7bWVzc2FnZS5tZXNzYWdlSWR9Jyk7CiAgICAgICAgaGFuZGxlRm9yZWdyb3VuZE5vdGlmaWNhdGlvbihtZXNzYWdlKTsKICAgICAgfSwKICAgICAgb25FcnJvcjogKGVycm9yKSB7CiAgICAgICAgX2xvZ2dlci5lKCckX3RhZzogRXJyb3IgaW4gZm9yZWdyb3VuZCBtZXNzYWdlIGhhbmRsZXInLCBlcnJvcik7CiAgICAgIH0sCiAgICApOwoKICAgIC8vIEhhbmRsZSBub3RpZmljYXRpb24gb3BlbmVkIChmcm9tIGJhY2tncm91bmQvdGVybWluYXRlZCkKICAgIF9tZXNzYWdlT3BlbmVkQXBwU3Vic2NyaXB0aW9uID0gRmlyZWJhc2VNZXNzYWdpbmcub25NZXNzYWdlT3BlbmVkQXBwLmxpc3RlbigKICAgICAgKFJlbW90ZU1lc3NhZ2UgbWVzc2FnZSkgewogICAgICAgIF9sb2dnZXIuaSgnJF90YWc6IE5vdGlmaWNhdGlvbiBvcGVuZWQgYXBwOiAke21lc3NhZ2UubWVzc2FnZUlkfScpOwogICAgICAgIGhhbmRsZU5vdGlmaWNhdGlvbk9wZW5lZChtZXNzYWdlLCBzb3VyY2U6ICdiYWNrZ3JvdW5kJyk7CiAgICAgIH0sCiAgICAgIG9uRXJyb3I6IChlcnJvcikgewogICAgICAgIF9sb2dnZXIuZSgnJF90YWc6IEVycm9yIGluIG5vdGlmaWNhdGlvbiBvcGVuZWQgaGFuZGxlcicsIGVycm9yKTsKICAgICAgfSwKICAgICk7CgogICAgLy8gSGFuZGxlIGluaXRpYWwgbWVzc2FnZSAoaWYgYXBwIHdhcyBvcGVuZWQgZnJvbSB0ZXJtaW5hdGVkIHN0YXRlKQogICAgaWYgKF9maXJlYmFzZU1lc3NhZ2luZyAhPSBudWxsKSB7CiAgICAgIGZpbmFsIGluaXRpYWxNZXNzYWdlID0gYXdhaXQgX2ZpcmViYXNlTWVzc2FnaW5nIS5nZXRJbml0aWFsTWVzc2FnZSgpOwogICAgICBpZiAoaW5pdGlhbE1lc3NhZ2UgIT0gbnVsbCkgewogICAgICAgIF9sb2dnZXIuaSgKICAgICAgICAgICckX3RhZzogQXBwIG9wZW5lZCBmcm9tIHRlcm1pbmF0ZWQgc3RhdGU6ICR7aW5pdGlhbE1lc3NhZ2UubWVzc2FnZUlkfScsCiAgICAgICAgKTsKICAgICAgICBoYW5kbGVOb3RpZmljYXRpb25PcGVuZWQoaW5pdGlhbE1lc3NhZ2UsIHNvdXJjZTogJ3Rlcm1pbmF0ZWQnKTsKICAgICAgfQoKICAgICAgLy8gSGFuZGxlIHRva2VuIHJlZnJlc2gKICAgICAgX3Rva2VuUmVmcmVzaFN1YnNjcmlwdGlvbiA9IF9maXJlYmFzZU1lc3NhZ2luZyEub25Ub2tlblJlZnJlc2gubGlzdGVuKAogICAgICAgIChTdHJpbmcgbmV3VG9rZW4pIHsKICAgICAgICAgIF9mY21Ub2tlbiA9IG5ld1Rva2VuOwogICAgICAgICAgX3Rva2VuTWFuYWdlci5oYW5kbGVUb2tlblJlZnJlc2gobmV3VG9rZW4pOwogICAgICAgIH0sCiAgICAgICAgb25FcnJvcjogKGVycm9yKSB7CiAgICAgICAgICBfbG9nZ2VyLmUoJyRfdGFnOiBFcnJvciBpbiB0b2tlbiByZWZyZXNoIGhhbmRsZXInLCBlcnJvcik7CiAgICAgICAgfSwKICAgICAgKTsKICAgIH0KCiAgICBfbG9nZ2VyLmkoJyRfdGFnOiBGQ00gaGFuZGxlcnMgc2V0dXAgY29tcGxldGUnKTsKICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTxTdHJpbmc/PiBnZXRGQ01Ub2tlbigpIGFzeW5jIHsKICAgIHRyeSB7CiAgICAgIF9mY21Ub2tlbiA9IGF3YWl0IF90b2tlbk1hbmFnZXIuZ2V0RkNNVG9rZW4oKTsKCiAgICAgIGlmIChfZmNtVG9rZW4gIT0gbnVsbCkgewogICAgICAgIF9kZXZpY2VJZCA9IGF3YWl0IF90b2tlbk1hbmFnZXIucmVnaXN0ZXJUb2tlbihfZmNtVG9rZW4hKTsKICAgICAgICBfbG9nZ2VyLmkoJyRfdGFnOiBUb2tlbiByZWdpc3RlcmVkIHdpdGggZGV2aWNlIElEOiAkX2RldmljZUlkJyk7CiAgICAgIH0KCiAgICAgIHJldHVybiBfZmNtVG9rZW47CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2dnZXIuZSgnJF90YWc6IEVycm9yIGdldHRpbmcgRkNNIHRva2VuJywgZSwgcyk7CiAgICAgIHJldHVybiBudWxsOwogICAgfQogIH0KCiAgLy8vIEhhbmRsZSBiYWNrZ3JvdW5kIG5vdGlmaWNhdGlvbiAod2hlbiBhcHAgaXMgbm90IHJ1bm5pbmcpCiAgQHByYWdtYSgndm06ZW50cnktcG9pbnQnKQogIHN0YXRpYyBGdXR1cmU8dm9pZD4gX2hhbmRsZUJhY2tncm91bmROb3RpZmljYXRpb24oCiAgICBSZW1vdGVNZXNzYWdlIG1lc3NhZ2UsCiAgKSBhc3luYyB7CiAgICB0cnkgewogICAgICBkZWJ1Z1ByaW50KAogICAgICAgICckX3RhZzogSGFuZGxpbmcgYmFja2dyb3VuZCBub3RpZmljYXRpb246ICR7bWVzc2FnZS5tZXNzYWdlSWR9JywKICAgICAgKTsKICAgIH0gY2F0Y2ggKGUsIHMpIHsKICAgICAgZGVidWdQcmludCgKICAgICAgICAnJF90YWc6IEVycm9yIGhhbmRsaW5nIGJhY2tncm91bmQgbm90aWZpY2F0aW9uOicKICAgICAgICAnICR7bWVzc2FnZS5tZXNzYWdlSWR9IC0gJGUgJHMnLAogICAgICApOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IGhhbmRsZUZvcmVncm91bmROb3RpZmljYXRpb24oUmVtb3RlTWVzc2FnZSBtZXNzYWdlKSBhc3luYyB7CiAgICB0cnkgewogICAgICBfbG9nZ2VyLmkoCiAgICAgICAgJyRfdGFnOiBIYW5kbGluZyBmb3JlZ3JvdW5kIG5vdGlmaWNhdGlvbjogJHttZXNzYWdlLm1lc3NhZ2VJZH0nLAogICAgICApOwoKICAgICAgZmluYWwgbm90aWZpY2F0aW9uID0gbWVzc2FnZS5ub3RpZmljYXRpb247CiAgICAgIGlmIChub3RpZmljYXRpb24gIT0gbnVsbCAmJgogICAgICAgICAgbm90aWZpY2F0aW9uLnRpdGxlICE9IG51bGwgJiYKICAgICAgICAgIG5vdGlmaWNhdGlvbi5ib2R5ICE9IG51bGwpIHsKICAgICAgICBUb2FzdC5ub3RpZmljYXRpb24oCiAgICAgICAgICB0aXRsZTogbm90aWZpY2F0aW9uLnRpdGxlISwKICAgICAgICAgIGJvZHk6IG5vdGlmaWNhdGlvbi5ib2R5ISwKICAgICAgICApOwogICAgICB9CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2dnZXIuZSgnJF90YWc6IEVycm9yIGhhbmRsaW5nIGZvcmVncm91bmQgbm90aWZpY2F0aW9uJywgZSwgcyk7CiAgICB9CiAgfQoKICBAb3ZlcnJpZGUKICBGdXR1cmU8dm9pZD4gaGFuZGxlTm90aWZpY2F0aW9uT3BlbmVkKAogICAgUmVtb3RlTWVzc2FnZSBtZXNzYWdlLCB7CiAgICBTdHJpbmc/IHNvdXJjZSwKICB9KSBhc3luYyB7CiAgICB0cnkgewogICAgICBfbG9nZ2VyLmkoJyRfdGFnOiBIYW5kbGluZyBub3RpZmljYXRpb24gb3BlbmVkOiAke21lc3NhZ2UubWVzc2FnZUlkfScpOwoKICAgICAgZmluYWwgbm90aWZpY2F0aW9uID0gbWVzc2FnZS5ub3RpZmljYXRpb247CgogICAgICBfbG9nZ2VyLmkoCiAgICAgICAgJyRfdGFnOiBOb3RpZmljYXRpb24gb3BlbmVkIGZyb20gJHNvdXJjZTogJHtub3RpZmljYXRpb24/LnRpdGxlfSAtICR7bm90aWZpY2F0aW9uPy5ib2R5fScsCiAgICAgICk7CgogICAgICBmaW5hbCBkYXRhID0gbWVzc2FnZS5kYXRhOwogICAgICBmaW5hbCBhY3Rpb25UeXBlID0gZGF0YVsndHlwZSddOwogICAgICBpZiAoYWN0aW9uVHlwZSA9PSBudWxsKSByZXR1cm47CgogICAgICBpZiAoYWN0aW9uVHlwZSA9PSAnc3VydmV5QXNzaWdubWVudHMnKSB7CiAgICAgICAgZmluYWwgaWQgPSBkYXRhWydpZCddOwogICAgICAgIGlmIChpZCA9PSBudWxsKSByZXR1cm47CiAgICAgICAgZmluYWwgcm91dGVyID0gZGkuZ2V0PEdvUm91dGVyPigpOwogICAgICAgIHJvdXRlci5wdXNoKCcvaG9tZS9zdXJ2ZXkvYXNzaWdubWVudC8kaWQnKTsKICAgICAgfQogICAgfSBjYXRjaCAoZSwgcykgewogICAgICBfbG9nZ2VyLmUoJyRfdGFnOiBFcnJvciBoYW5kbGluZyBub3RpZmljYXRpb24gb3BlbmVkJywgZSwgcyk7CiAgICB9CiAgfQoKICBAb3ZlcnJpZGUKICBTdHJpbmc/IGdldCBmY21Ub2tlbiA9PiBfZmNtVG9rZW47CgogIEBvdmVycmlkZQogIFN0cmluZz8gZ2V0IGRldmljZUlkID0+IF9kZXZpY2VJZDsKCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IGRpc3Bvc2UoKSBhc3luYyB7CiAgICBfbG9nZ2VyLmkoJyRfdGFnOiBEaXNwb3NpbmcgRmlyZWJhc2Ugbm90aWZpY2F0aW9uIGNsaWVudCcpOwoKICAgIC8vIENhbmNlbCBhbGwgc3Vic2NyaXB0aW9ucyB0byBwcmV2ZW50IG1lbW9yeSBsZWFrcwogICAgX2ZvcmVncm91bmRNZXNzYWdlU3Vic2NyaXB0aW9uPy5jYW5jZWwoKTsKICAgIF9tZXNzYWdlT3BlbmVkQXBwU3Vic2NyaXB0aW9uPy5jYW5jZWwoKTsKICAgIF90b2tlblJlZnJlc2hTdWJzY3JpcHRpb24/LmNhbmNlbCgpOwoKICAgIF9mb3JlZ3JvdW5kTWVzc2FnZVN1YnNjcmlwdGlvbiA9IG51bGw7CiAgICBfbWVzc2FnZU9wZW5lZEFwcFN1YnNjcmlwdGlvbiA9IG51bGw7CiAgICBfdG9rZW5SZWZyZXNoU3Vic2NyaXB0aW9uID0gbnVsbDsKCiAgICBfZmlyZWJhc2VNZXNzYWdpbmcgPSBudWxsOwogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/index.dart", + "data": + "ZXhwb3J0ICdmaXJlYmFzZV9ub3RpZmljYXRpb25fY2xpZW50LmRhcnQnOwpleHBvcnQgJ25vdGlmaWNhdGlvbl9jbGllbnQuZGFydCc7Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/notification_client.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmZpcmViYXNlX21lc3NhZ2luZy9maXJlYmFzZV9tZXNzYWdpbmcuZGFydCcKICAgIGhpZGUgTm90aWZpY2F0aW9uU2V0dGluZ3M7CgovLy8gQWJzdHJhY3Qgbm90aWZpY2F0aW9uIGNsaWVudCBpbnRlcmZhY2UKLy8vIFNpbXBsaWZpZWQgaW50ZXJmYWNlIGZvbGxvd2luZyBuZXR3b3JrIHBhY2thZ2UgcGF0dGVybnMKYWJzdHJhY3QgY2xhc3MgTm90aWZpY2F0aW9uQ2xpZW50IHsKICBGdXR1cmU8dm9pZD4gaW5pdCgpOwoKICBGdXR1cmU8Ym9vbD4gcmVxdWVzdFBlcm1pc3Npb25zKHtib29sIHByb3Zpc2lvbmFsID0gZmFsc2V9KTsKCiAgRnV0dXJlPFN0cmluZz8+IGdldEZDTVRva2VuKCk7CgogIEZ1dHVyZTx2b2lkPiBoYW5kbGVGb3JlZ3JvdW5kTm90aWZpY2F0aW9uKFJlbW90ZU1lc3NhZ2UgbWVzc2FnZSk7CgogIEZ1dHVyZTx2b2lkPiBoYW5kbGVOb3RpZmljYXRpb25PcGVuZWQoCiAgICBSZW1vdGVNZXNzYWdlIG1lc3NhZ2UsIHsKICAgIFN0cmluZz8gc291cmNlLAogIH0pOwoKICBGdXR1cmU8dm9pZD4gY2xlYXJCYWRnZSgpOwoKICBTdHJpbmc/IGdldCBmY21Ub2tlbjsKCiAgU3RyaW5nPyBnZXQgZGV2aWNlSWQ7CgogIEZ1dHVyZTx2b2lkPiBkaXNwb3NlKCk7Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/config/index.dart", + "data": "ZXhwb3J0ICdub3RpZmljYXRpb25fY29uZmlnLmRhcnQnOwo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/config/notification_config.dart", + "data": + "Y2xhc3MgTm90aWZpY2F0aW9uQ29uZmlnIHsKICBjb25zdCBOb3RpZmljYXRpb25Db25maWcoKTsKfQoKY2xhc3MgRGVmYXVsdE5vdGlmaWNhdGlvbkNvbmZpZyBleHRlbmRzIE5vdGlmaWNhdGlvbkNvbmZpZyB7CiAgY29uc3QgRGVmYXVsdE5vdGlmaWNhdGlvbkNvbmZpZygpIDogc3VwZXIoKTsKfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/device_info/device_info.dart", + "data": + "YWJzdHJhY3QgY2xhc3MgRGV2aWNlSW5mbyB7CiAgRnV0dXJlPFN0cmluZz4gZ2VuZXJhdGVEZXZpY2VJZCgpOwoKICBGdXR1cmU8U3RyaW5nPiBnZXREZXZpY2VOYW1lKCk7Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/device_info/device_info_impl.dart", + "data": + "aW1wb3J0ICdkYXJ0OmNvbnZlcnQnOwppbXBvcnQgJ2RhcnQ6aW8nOwoKaW1wb3J0ICdwYWNrYWdlOmNvcmUvbG9nZ2VyL2xvZ2dlci5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmNyeXB0by9jcnlwdG8uZGFydCc7CmltcG9ydCAncGFja2FnZTpkZXZpY2VfaW5mb19wbHVzL2RldmljZV9pbmZvX3BsdXMuZGFydCc7CmltcG9ydCAncGFja2FnZTpkaS9kaS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOm5vdGlmaWNhdGlvbnMvc3JjL2RldmljZV9pbmZvL2RldmljZV9pbmZvLmRhcnQnOwoKLy8vIENvbmNyZXRlIGltcGxlbWVudGF0aW9uIG9mIERldmljZUluZm8gZm9yIEFuZHJvaWQgYW5kIGlPUyBvbmx5Ci8vLyBHZW5lcmF0ZXMgY29uc2lzdGVudCBkZXZpY2UgaWRlbnRpZmllcnMgdXNpbmcgc3RhYmxlIGRldmljZSBjaGFyYWN0ZXJpc3RpY3MKY2xhc3MgRGV2aWNlSW5mb0ltcGwgaW1wbGVtZW50cyBEZXZpY2VJbmZvIHsKICBzdGF0aWMgY29uc3QgU3RyaW5nIF90YWcgPSAnRGV2aWNlSW5mb0ltcGwnOwoKICBmaW5hbCBMb2dnZXIgX2xvZ2dlcjsKICBmaW5hbCBEZXZpY2VJbmZvUGx1Z2luIF9kZXZpY2VJbmZvUGx1Z2luOwoKICBEZXZpY2VJbmZvSW1wbCh7TG9nZ2VyPyBsb2dnZXIsIERldmljZUluZm9QbHVnaW4/IGRldmljZUluZm9QbHVnaW59KQogICAgOiBfbG9nZ2VyID0gbG9nZ2VyID8/IGRpLmdldDxMb2dnZXI+KCksCiAgICAgIF9kZXZpY2VJbmZvUGx1Z2luID0gZGV2aWNlSW5mb1BsdWdpbiA/PyBEZXZpY2VJbmZvUGx1Z2luKCk7CgogIEBvdmVycmlkZQogIEZ1dHVyZTxTdHJpbmc+IGdlbmVyYXRlRGV2aWNlSWQoKSBhc3luYyB7CiAgICB0cnkgewogICAgICBpZiAoUGxhdGZvcm0uaXNBbmRyb2lkKSB7CiAgICAgICAgcmV0dXJuIGF3YWl0IF9nZW5lcmF0ZUFuZHJvaWREZXZpY2VJZCgpOwogICAgICB9IGVsc2UgaWYgKFBsYXRmb3JtLmlzSU9TKSB7CiAgICAgICAgcmV0dXJuIGF3YWl0IF9nZW5lcmF0ZUlPU0RldmljZUlkKCk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgX2xvZ2dlci53KCckX3RhZzogVW5zdXBwb3J0ZWQgcGxhdGZvcm06ICR7UGxhdGZvcm0ub3BlcmF0aW5nU3lzdGVtfScpOwogICAgICAgIHRocm93IFVuc3VwcG9ydGVkRXJyb3IoJ09ubHkgQW5kcm9pZCBhbmQgaU9TIHBsYXRmb3JtcyBhcmUgc3VwcG9ydGVkJyk7CiAgICAgIH0KICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgX2xvZ2dlci53KCckX3RhZzogRXJyb3IgZ2VuZXJhdGluZyBkZXZpY2UgSUQ6ICRlJyk7CiAgICAgIHJldHVybiBfZ2VuZXJhdGVGYWxsYmFja0lkKCk7CiAgICB9CiAgfQoKICBAb3ZlcnJpZGUKICBGdXR1cmU8U3RyaW5nPiBnZXREZXZpY2VOYW1lKCkgYXN5bmMgewogICAgdHJ5IHsKICAgICAgaWYgKFBsYXRmb3JtLmlzQW5kcm9pZCkgewogICAgICAgIHJldHVybiBhd2FpdCBfZ2V0QW5kcm9pZERldmljZU5hbWUoKTsKICAgICAgfSBlbHNlIGlmIChQbGF0Zm9ybS5pc0lPUykgewogICAgICAgIHJldHVybiBhd2FpdCBfZ2V0SU9TRGV2aWNlTmFtZSgpOwogICAgICB9IGVsc2UgewogICAgICAgIF9sb2dnZXIudygnJF90YWc6IFVuc3VwcG9ydGVkIHBsYXRmb3JtOiAke1BsYXRmb3JtLm9wZXJhdGluZ1N5c3RlbX0nKTsKICAgICAgICB0aHJvdyBVbnN1cHBvcnRlZEVycm9yKCdPbmx5IEFuZHJvaWQgYW5kIGlPUyBwbGF0Zm9ybXMgYXJlIHN1cHBvcnRlZCcpOwogICAgICB9CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIF9sb2dnZXIudygnJF90YWc6IEVycm9yIGdldHRpbmcgZGV2aWNlIG5hbWU6ICRlJyk7CiAgICAgIHJldHVybiBfZ2V0RmFsbGJhY2tEZXZpY2VOYW1lKCk7CiAgICB9CiAgfQoKICAvLy8gR2VuZXJhdGUgQW5kcm9pZCBkZXZpY2UgSUQgdXNpbmcgc3RhYmxlIGhhcmR3YXJlIGNoYXJhY3RlcmlzdGljcwogIEZ1dHVyZTxTdHJpbmc+IF9nZW5lcmF0ZUFuZHJvaWREZXZpY2VJZCgpIGFzeW5jIHsKICAgIHRyeSB7CiAgICAgIGZpbmFsIGFuZHJvaWRJbmZvID0gYXdhaXQgX2RldmljZUluZm9QbHVnaW4uYW5kcm9pZEluZm87CgogICAgICAvLyBVc2Ugb25seSB0aGUgbW9zdCBzdGFibGUgaGFyZHdhcmUgY2hhcmFjdGVyaXN0aWNzIHRoYXQgZG9uJ3QgY2hhbmdlCiAgICAgIGZpbmFsIHN0YWJsZUNoYXJhY3RlcmlzdGljcyA9IFsKICAgICAgICBhbmRyb2lkSW5mby5icmFuZCwKICAgICAgICBhbmRyb2lkSW5mby5tb2RlbCwKICAgICAgICBhbmRyb2lkSW5mby5kZXZpY2UsCiAgICAgICAgYW5kcm9pZEluZm8uaGFyZHdhcmUsCiAgICAgICAgYW5kcm9pZEluZm8uYm9hcmQsCiAgICAgICAgYW5kcm9pZEluZm8uYm9vdGxvYWRlciwKICAgICAgICBhbmRyb2lkSW5mby5wcm9kdWN0LAogICAgICAgIC8vIGZpbmdlcnByaW50IGNvbnRhaW5zIGJ1aWxkIGluZm8gYnV0IGlzIGdlbmVyYWxseSBzdGFibGUgZm9yIHNhbWUgZGV2aWNlCiAgICAgICAgYW5kcm9pZEluZm8uZmluZ2VycHJpbnQsCiAgICAgICAgLy8gUGh5c2ljYWwgZGV2aWNlIGluZGljYXRvcgogICAgICAgIGFuZHJvaWRJbmZvLmlzUGh5c2ljYWxEZXZpY2UudG9TdHJpbmcoKSwKICAgICAgICAvLyBIYXJkd2FyZSBmZWF0dXJlcyB0aGF0IGFyZSBzdGFibGUKICAgICAgICBhbmRyb2lkSW5mby5zdXBwb3J0ZWRBYmlzLmpvaW4oJywnKSwKICAgICAgICBhbmRyb2lkSW5mby5zdXBwb3J0ZWQzMkJpdEFiaXMuam9pbignLCcpLAogICAgICAgIGFuZHJvaWRJbmZvLnN1cHBvcnRlZDY0Qml0QWJpcy5qb2luKCcsJyksCiAgICAgIF07CgogICAgICAvLyBSZW1vdmUgZW1wdHkgdmFsdWVzIGFuZCBjcmVhdGUgYSBkZXRlcm1pbmlzdGljIHN0cmluZwogICAgICBmaW5hbCBjbGVhbkNoYXJhY3RlcmlzdGljcyA9IHN0YWJsZUNoYXJhY3RlcmlzdGljcwogICAgICAgICAgLndoZXJlKChjaGFyKSA9PiBjaGFyLmlzTm90RW1wdHkgJiYgY2hhciAhPSAnbnVsbCcpCiAgICAgICAgICAudG9MaXN0KCk7CgogICAgICBpZiAoY2xlYW5DaGFyYWN0ZXJpc3RpY3MuaXNFbXB0eSkgewogICAgICAgIHRocm93IEV4Y2VwdGlvbignTm8gc3RhYmxlIGNoYXJhY3RlcmlzdGljcyBmb3VuZCBmb3IgQW5kcm9pZCBkZXZpY2UnKTsKICAgICAgfQoKICAgICAgLy8gU29ydCB0byBlbnN1cmUgY29uc2lzdGVudCBvcmRlcmluZwogICAgICBjbGVhbkNoYXJhY3RlcmlzdGljcy5zb3J0KCk7CgogICAgICBmaW5hbCBkZXZpY2VTdHJpbmcgPSBjbGVhbkNoYXJhY3RlcmlzdGljcy5qb2luKCd8Jyk7CiAgICAgIGZpbmFsIGRldmljZUhhc2ggPSBfZ2VuZXJhdGVEZXRlcm1pbmlzdGljSGFzaChkZXZpY2VTdHJpbmcpOwoKICAgICAgX2xvZ2dlci5kKAogICAgICAgICckX3RhZzogR2VuZXJhdGVkIEFuZHJvaWQgZGV2aWNlIElEIGZyb20gJwogICAgICAgICcke2NsZWFuQ2hhcmFjdGVyaXN0aWNzLmxlbmd0aH0gY2hhcmFjdGVyaXN0aWNzJywKICAgICAgKTsKICAgICAgcmV0dXJuICdhbmRyb2lkXyRkZXZpY2VIYXNoJzsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgX2xvZ2dlci53KCckX3RhZzogQW5kcm9pZCBkZXZpY2UgSUQgZ2VuZXJhdGlvbiBmYWlsZWQ6ICRlJyk7CiAgICAgIHJldGhyb3c7CiAgICB9CiAgfQoKICAvLy8gR2VuZXJhdGUgaU9TIGRldmljZSBJRCB1c2luZyBpZGVudGlmaWVyRm9yVmVuZG9yIG9yIHN0YWJsZSBjaGFyYWN0ZXJpc3RpY3MKICBGdXR1cmU8U3RyaW5nPiBfZ2VuZXJhdGVJT1NEZXZpY2VJZCgpIGFzeW5jIHsKICAgIHRyeSB7CiAgICAgIGZpbmFsIGlvc0luZm8gPSBhd2FpdCBfZGV2aWNlSW5mb1BsdWdpbi5pb3NJbmZvOwoKICAgICAgLy8gRmlyc3QgcHJlZmVyZW5jZTogdXNlIGlkZW50aWZpZXJGb3JWZW5kb3IgYXMgaXQncyBkZXNpZ25lZCB0byBiZSBzdGFibGUKICAgICAgZmluYWwgaWRlbnRpZmllciA9IGlvc0luZm8uaWRlbnRpZmllckZvclZlbmRvcjsKICAgICAgaWYgKGlkZW50aWZpZXIgIT0gbnVsbCAmJiBpZGVudGlmaWVyLmlzTm90RW1wdHkgJiYgaWRlbnRpZmllciAhPSAnbnVsbCcpIHsKICAgICAgICBfbG9nZ2VyLmQoJyRfdGFnOiBVc2luZyBpT1MgaWRlbnRpZmllckZvclZlbmRvcicpOwogICAgICAgIHJldHVybiAnaW9zXyRpZGVudGlmaWVyJzsKICAgICAgfQoKICAgICAgLy8gRmFsbGJhY2s6IHVzZSBzdGFibGUgZGV2aWNlIGNoYXJhY3RlcmlzdGljcwogICAgICBfbG9nZ2VyLmkoCiAgICAgICAgJyRfdGFnOiBpT1MgaWRlbnRpZmllckZvclZlbmRvciBub3QgYXZhaWxhYmxlLCAnCiAgICAgICAgJ3VzaW5nIGRldmljZSBjaGFyYWN0ZXJpc3RpY3MnLAogICAgICApOwoKICAgICAgZmluYWwgc3RhYmxlQ2hhcmFjdGVyaXN0aWNzID0gWwogICAgICAgIGlvc0luZm8ubW9kZWwsCiAgICAgICAgaW9zSW5mby5sb2NhbGl6ZWRNb2RlbCwKICAgICAgICBpb3NJbmZvLnN5c3RlbU5hbWUsCiAgICAgICAgaW9zSW5mby5pc1BoeXNpY2FsRGV2aWNlLnRvU3RyaW5nKCksCiAgICAgICAgLy8gTWFjaGluZSBpZGVudGlmaWVyIGZyb20gdXRzbmFtZSAoaGFyZHdhcmUgbW9kZWwpCiAgICAgICAgaW9zSW5mby51dHNuYW1lLm1hY2hpbmUsCiAgICAgICAgaW9zSW5mby51dHNuYW1lLnN5c25hbWUsCiAgICAgICAgLy8gVGhlc2UgYXJlIGdlbmVyYWxseSBzdGFibGUgZm9yIHRoZSBzYW1lIGRldmljZSBtb2RlbAogICAgICAgIGlvc0luZm8udXRzbmFtZS5ub2RlbmFtZSwKICAgICAgXTsKCiAgICAgIC8vIFJlbW92ZSBlbXB0eSB2YWx1ZXMgYW5kIGNyZWF0ZSBhIGRldGVybWluaXN0aWMgc3RyaW5nCiAgICAgIGZpbmFsIGNsZWFuQ2hhcmFjdGVyaXN0aWNzID0gc3RhYmxlQ2hhcmFjdGVyaXN0aWNzCiAgICAgICAgICAud2hlcmUoKGNoYXIpID0+IGNoYXIuaXNOb3RFbXB0eSAmJiBjaGFyICE9ICdudWxsJykKICAgICAgICAgIC50b0xpc3QoKTsKCiAgICAgIGlmIChjbGVhbkNoYXJhY3RlcmlzdGljcy5pc0VtcHR5KSB7CiAgICAgICAgdGhyb3cgRXhjZXB0aW9uKCdObyBzdGFibGUgY2hhcmFjdGVyaXN0aWNzIGZvdW5kIGZvciBpT1MgZGV2aWNlJyk7CiAgICAgIH0KCiAgICAgIC8vIFNvcnQgdG8gZW5zdXJlIGNvbnNpc3RlbnQgb3JkZXJpbmcKICAgICAgY2xlYW5DaGFyYWN0ZXJpc3RpY3Muc29ydCgpOwoKICAgICAgZmluYWwgZGV2aWNlU3RyaW5nID0gY2xlYW5DaGFyYWN0ZXJpc3RpY3Muam9pbignfCcpOwogICAgICBmaW5hbCBkZXZpY2VIYXNoID0gX2dlbmVyYXRlRGV0ZXJtaW5pc3RpY0hhc2goZGV2aWNlU3RyaW5nKTsKCiAgICAgIF9sb2dnZXIuZCgKICAgICAgICAnJF90YWc6IEdlbmVyYXRlZCBpT1MgZGV2aWNlIElEIGZyb20gJwogICAgICAgICcke2NsZWFuQ2hhcmFjdGVyaXN0aWNzLmxlbmd0aH0gY2hhcmFjdGVyaXN0aWNzJywKICAgICAgKTsKICAgICAgcmV0dXJuICdpb3NfJGRldmljZUhhc2gnOwogICAgfSBjYXRjaCAoZSkgewogICAgICBfbG9nZ2VyLncoJyRfdGFnOiBpT1MgZGV2aWNlIElEIGdlbmVyYXRpb24gZmFpbGVkOiAkZScpOwogICAgICByZXRocm93OwogICAgfQogIH0KCiAgLy8vIEdldCBBbmRyb2lkIGRldmljZSBuYW1lIGluIGZvcm1hdCAiQnJhbmQgTW9kZWwiCiAgRnV0dXJlPFN0cmluZz4gX2dldEFuZHJvaWREZXZpY2VOYW1lKCkgYXN5bmMgewogICAgZmluYWwgYW5kcm9pZEluZm8gPSBhd2FpdCBfZGV2aWNlSW5mb1BsdWdpbi5hbmRyb2lkSW5mbzsKICAgIGZpbmFsIGJyYW5kID0gYW5kcm9pZEluZm8uYnJhbmQ7CiAgICBmaW5hbCBtb2RlbCA9IGFuZHJvaWRJbmZvLm1vZGVsOwoKICAgIHJldHVybiAnJGJyYW5kICRtb2RlbCc7CiAgfQoKICAvLy8gR2V0IGlPUyBkZXZpY2UgbmFtZSBpbiBmb3JtYXQgIk5hbWUgKE1vZGVsKSIKICBGdXR1cmU8U3RyaW5nPiBfZ2V0SU9TRGV2aWNlTmFtZSgpIGFzeW5jIHsKICAgIGZpbmFsIGlvc0luZm8gPSBhd2FpdCBfZGV2aWNlSW5mb1BsdWdpbi5pb3NJbmZvOwogICAgZmluYWwgbmFtZSA9IGlvc0luZm8ubmFtZTsKICAgIGZpbmFsIG1vZGVsID0gaW9zSW5mby5tb2RlbDsKCiAgICByZXR1cm4gJyRuYW1lICgkbW9kZWwpJzsKICB9CgogIC8vLyBHZW5lcmF0ZSBhIGRldGVybWluaXN0aWMgaGFzaCBmcm9tIGRldmljZSBjaGFyYWN0ZXJpc3RpY3MKICAvLy8gVXNlcyBTSEEtMjU2IHRvIGNyZWF0ZSBhIGNvbnNpc3RlbnQsIHN0YWJsZSBoYXNoCiAgU3RyaW5nIF9nZW5lcmF0ZURldGVybWluaXN0aWNIYXNoKFN0cmluZyBpbnB1dCkgewogICAgLy8gQWRkIGEgc2FsdCB0byBtYWtlIHRoZSBoYXNoIG1vcmUgdW5pcXVlIHdoaWxlIGtlZXBpbmcgaXQgZGV0ZXJtaW5pc3RpYwogICAgY29uc3Qgc2FsdCA9ICdkZXZpY2VfaWRlbnRpZmllcl9zYWx0XzIwMjQnOwogICAgZmluYWwgc2FsdGVkSW5wdXQgPSAnJHNhbHR8JGlucHV0JzsKCiAgICBmaW5hbCBieXRlcyA9IHV0ZjguZW5jb2RlKHNhbHRlZElucHV0KTsKICAgIGZpbmFsIGRpZ2VzdCA9IHNoYTI1Ni5jb252ZXJ0KGJ5dGVzKTsKCiAgICAvLyBSZXR1cm4gZmlyc3QgMTYgY2hhcmFjdGVycyBmb3IgcmVhc29uYWJsZSBsZW5ndGgKICAgIHJldHVybiBkaWdlc3QudG9TdHJpbmcoKS5zdWJzdHJpbmcoMCwgMTYpOwogIH0KCiAgLy8vIEdlbmVyYXRlIGEgZmFsbGJhY2sgZGV2aWNlIElEIHdoZW4gYWxsIG90aGVyIG1ldGhvZHMgZmFpbAogIFN0cmluZyBfZ2VuZXJhdGVGYWxsYmFja0lkKCkgewogICAgZmluYWwgcGxhdGZvcm0gPSBQbGF0Zm9ybS5pc0FuZHJvaWQgPyAnYW5kcm9pZCcgOiAnaW9zJzsKCiAgICAvLyBVc2UgYSBkZXRlcm1pbmlzdGljIGZhbGxiYWNrIGJhc2VkIG9uIHBsYXRmb3JtCiAgICBmaW5hbCBmYWxsYmFja1N0cmluZyA9ICcke3BsYXRmb3JtfV9mYWxsYmFja19kZXZpY2UnOwogICAgZmluYWwgZmFsbGJhY2tIYXNoID0gX2dlbmVyYXRlRGV0ZXJtaW5pc3RpY0hhc2goZmFsbGJhY2tTdHJpbmcpOwoKICAgIF9sb2dnZXIudygnJF90YWc6IFVzaW5nIGRldGVybWluaXN0aWMgZmFsbGJhY2sgSUQnKTsKICAgIHJldHVybiAnJHtwbGF0Zm9ybX1fJGZhbGxiYWNrSGFzaCc7CiAgfQoKICAvLy8gR2V0IGZhbGxiYWNrIGRldmljZSBuYW1lIHdoZW4gZGV0ZWN0aW9uIGZhaWxzCiAgU3RyaW5nIF9nZXRGYWxsYmFja0RldmljZU5hbWUoKSB7CiAgICByZXR1cm4gUGxhdGZvcm0uaXNBbmRyb2lkID8gJ0FuZHJvaWQgRGV2aWNlJyA6ICdpT1MgRGV2aWNlJzsKICB9Cn0K", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/device_info/index.dart", + "data": + "ZXhwb3J0ICdkZXZpY2VfaW5mby5kYXJ0JzsKZXhwb3J0ICdkZXZpY2VfaW5mb19pbXBsLmRhcnQnOwo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/di/index.dart", + "data": "ZXhwb3J0ICdub3RpZmljYXRpb25fZGkuZGFydCc7Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/di/notification_di.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmRpL2RpLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6bm90aWZpY2F0aW9ucy9zcmMvY2xpZW50L2luZGV4LmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6bm90aWZpY2F0aW9ucy9zcmMvY29uZmlnL2luZGV4LmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6bm90aWZpY2F0aW9ucy9zcmMvZGV2aWNlX2luZm8vaW5kZXguZGFydCc7CmltcG9ydCAncGFja2FnZTpub3RpZmljYXRpb25zL3NyYy9zZXJ2aWNlcy9ub3RpZmljYXRpb25fcGVybWlzc2lvbl9tYW5hZ2VyLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6bm90aWZpY2F0aW9ucy9zcmMvc2VydmljZXMvbm90aWZpY2F0aW9uX3Rva2VuX21hbmFnZXIuZGFydCc7CgovLy8gUmVnaXN0ZXIgbm90aWZpY2F0aW9uIHNlcnZpY2VzIHdpdGggZGVwZW5kZW5jeSBpbmplY3Rpb24KRnV0dXJlPHZvaWQ+IHJlZ2lzdGVyTm90aWZpY2F0aW9uV2l0aERJKE5vdGlmaWNhdGlvbkNvbmZpZyBjb25maWcpIGFzeW5jIHsKICAvLyBSZWdpc3RlciBjb25maWcKICBkaS5yZWdpc3RlcjxOb3RpZmljYXRpb25Db25maWc+KGNvbmZpZyk7CgogIC8vIFJlZ2lzdGVyIGRldmljZSBpbmZvIHNlcnZpY2UKICBkaS5yZWdpc3RlcjxEZXZpY2VJbmZvPihEZXZpY2VJbmZvSW1wbCgpKTsKCiAgLy8gUmVnaXN0ZXIgdG9rZW4gbWFuYWdlcgogIGRpLnJlZ2lzdGVyPE5vdGlmaWNhdGlvblRva2VuTWFuYWdlcj4oRmlyZWJhc2VUb2tlbk1hbmFnZXIoKSk7CgogIC8vIFJlZ2lzdGVyIHBlcm1pc3Npb24gbWFuYWdlcgogIGRpLnJlZ2lzdGVyPE5vdGlmaWNhdGlvblBlcm1pc3Npb25NYW5hZ2VyPihGaXJlYmFzZVBlcm1pc3Npb25NYW5hZ2VyKCkpOwoKICAvLyBSZWdpc3RlciBub3RpZmljYXRpb24gY2xpZW50IHdpdGggcHJvcGVyIGRpc3Bvc2FsCiAgZGkucmVnaXN0ZXI8Tm90aWZpY2F0aW9uQ2xpZW50PigKICAgIEZpcmViYXNlTm90aWZpY2F0aW9uQ2xpZW50KGNvbmZpZzogY29uZmlnKSwKICAgIGRpc3Bvc2U6IChjbGllbnQpID0+IGNsaWVudC5kaXNwb3NlKCksCiAgKTsKfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/exceptions/index.dart", + "data": "ZXhwb3J0ICdub3RpZmljYXRpb25fZXhjZXB0aW9ucy5kYXJ0JzsK", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/exceptions/notification_exceptions.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNvcmUvY29yZS5kYXJ0JzsKCmNsYXNzIE5vdGlmaWNhdGlvbkV4Y2VwdGlvbiBleHRlbmRzIENvcmVFeGNlcHRpb24gewogIE5vdGlmaWNhdGlvbkV4Y2VwdGlvbih7cmVxdWlyZWQgc3VwZXIubWVzc2FnZX0pOwp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/models/device_token_models.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmpzb25fYW5ub3RhdGlvbi9qc29uX2Fubm90YXRpb24uZGFydCc7CgpwYXJ0ICdkZXZpY2VfdG9rZW5fbW9kZWxzLmcuZGFydCc7CgpASnNvblNlcmlhbGl6YWJsZSgpCmNsYXNzIERldmljZVRva2VuUmVxdWVzdCB7CiAgZmluYWwgU3RyaW5nIGZjbVRva2VuOwogIGZpbmFsIFN0cmluZyBkZXZpY2VJZDsKICBmaW5hbCBTdHJpbmcgZGV2aWNlTmFtZTsKICBmaW5hbCBTdHJpbmcgZGV2aWNlVHlwZTsKCiAgY29uc3QgRGV2aWNlVG9rZW5SZXF1ZXN0KHsKICAgIHJlcXVpcmVkIHRoaXMuZmNtVG9rZW4sCiAgICByZXF1aXJlZCB0aGlzLmRldmljZUlkLAogICAgcmVxdWlyZWQgdGhpcy5kZXZpY2VOYW1lLAogICAgcmVxdWlyZWQgdGhpcy5kZXZpY2VUeXBlLAogIH0pOwoKICBmYWN0b3J5IERldmljZVRva2VuUmVxdWVzdC5mcm9tSnNvbihNYXA8U3RyaW5nLCBkeW5hbWljPiBqc29uKSA9PgogICAgICBfJERldmljZVRva2VuUmVxdWVzdEZyb21Kc29uKGpzb24pOwoKICBNYXA8U3RyaW5nLCBkeW5hbWljPiB0b0pzb24oKSA9PiBfJERldmljZVRva2VuUmVxdWVzdFRvSnNvbih0aGlzKTsKfQoKQEpzb25TZXJpYWxpemFibGUoKQpjbGFzcyBEZXZpY2VUb2tlblVwZGF0ZVJlcXVlc3QgewogIGZpbmFsIFN0cmluZyBmY21Ub2tlbjsKCiAgY29uc3QgRGV2aWNlVG9rZW5VcGRhdGVSZXF1ZXN0KHtyZXF1aXJlZCB0aGlzLmZjbVRva2VufSk7CgogIGZhY3RvcnkgRGV2aWNlVG9rZW5VcGRhdGVSZXF1ZXN0LmZyb21Kc29uKE1hcDxTdHJpbmcsIGR5bmFtaWM+IGpzb24pID0+CiAgICAgIF8kRGV2aWNlVG9rZW5VcGRhdGVSZXF1ZXN0RnJvbUpzb24oanNvbik7CgogIE1hcDxTdHJpbmcsIGR5bmFtaWM+IHRvSnNvbigpID0+IF8kRGV2aWNlVG9rZW5VcGRhdGVSZXF1ZXN0VG9Kc29uKHRoaXMpOwp9Cg==", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/models/device_token_models.g.dart", + "data": + "Ly8gR0VORVJBVEVEIENPREUgLSBETyBOT1QgTU9ESUZZIEJZIEhBTkQKCnBhcnQgb2YgJ2RldmljZV90b2tlbl9tb2RlbHMuZGFydCc7CgovLyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKgovLyBKc29uU2VyaWFsaXphYmxlR2VuZXJhdG9yCi8vICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqCgpEZXZpY2VUb2tlblJlcXVlc3QgXyREZXZpY2VUb2tlblJlcXVlc3RGcm9tSnNvbihNYXA8U3RyaW5nLCBkeW5hbWljPiBqc29uKSA9PgogICAgRGV2aWNlVG9rZW5SZXF1ZXN0KAogICAgICBmY21Ub2tlbjoganNvblsnZmNtVG9rZW4nXSBhcyBTdHJpbmcsCiAgICAgIGRldmljZUlkOiBqc29uWydkZXZpY2VJZCddIGFzIFN0cmluZywKICAgICAgZGV2aWNlTmFtZToganNvblsnZGV2aWNlTmFtZSddIGFzIFN0cmluZywKICAgICAgZGV2aWNlVHlwZToganNvblsnZGV2aWNlVHlwZSddIGFzIFN0cmluZywKICAgICk7CgpNYXA8U3RyaW5nLCBkeW5hbWljPiBfJERldmljZVRva2VuUmVxdWVzdFRvSnNvbihEZXZpY2VUb2tlblJlcXVlc3QgaW5zdGFuY2UpID0+CiAgICA8U3RyaW5nLCBkeW5hbWljPnsKICAgICAgJ2ZjbVRva2VuJzogaW5zdGFuY2UuZmNtVG9rZW4sCiAgICAgICdkZXZpY2VJZCc6IGluc3RhbmNlLmRldmljZUlkLAogICAgICAnZGV2aWNlTmFtZSc6IGluc3RhbmNlLmRldmljZU5hbWUsCiAgICAgICdkZXZpY2VUeXBlJzogaW5zdGFuY2UuZGV2aWNlVHlwZSwKICAgIH07CgpEZXZpY2VUb2tlblVwZGF0ZVJlcXVlc3QgXyREZXZpY2VUb2tlblVwZGF0ZVJlcXVlc3RGcm9tSnNvbigKICBNYXA8U3RyaW5nLCBkeW5hbWljPiBqc29uLAopID0+IERldmljZVRva2VuVXBkYXRlUmVxdWVzdChmY21Ub2tlbjoganNvblsnZmNtVG9rZW4nXSBhcyBTdHJpbmcpOwoKTWFwPFN0cmluZywgZHluYW1pYz4gXyREZXZpY2VUb2tlblVwZGF0ZVJlcXVlc3RUb0pzb24oCiAgRGV2aWNlVG9rZW5VcGRhdGVSZXF1ZXN0IGluc3RhbmNlLAopID0+IDxTdHJpbmcsIGR5bmFtaWM+eydmY21Ub2tlbic6IGluc3RhbmNlLmZjbVRva2VufTsK", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/models/index.dart", + "data": "ZXhwb3J0ICdkZXZpY2VfdG9rZW5fbW9kZWxzLmRhcnQnOwo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/services/index.dart", + "data": + "ZXhwb3J0ICdub3RpZmljYXRpb25fcGVybWlzc2lvbl9tYW5hZ2VyLmRhcnQnOwpleHBvcnQgJ25vdGlmaWNhdGlvbl90b2tlbl9tYW5hZ2VyLmRhcnQnOwo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/services/notification_permission_manager.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNvcmUvbG9nZ2VyL2xvZ2dlci5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRpL2RpLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZmlyZWJhc2VfbWVzc2FnaW5nL2ZpcmViYXNlX21lc3NhZ2luZy5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOm5ldHdvcmsvbmV0d29yay5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOm5vdGlmaWNhdGlvbnMvc3JjL2V4Y2VwdGlvbnMvbm90aWZpY2F0aW9uX2V4Y2VwdGlvbnMuZGFydCc7CgphYnN0cmFjdCBjbGFzcyBOb3RpZmljYXRpb25QZXJtaXNzaW9uTWFuYWdlciB7CiAgRnV0dXJlPGJvb2w+IHJlcXVlc3RQZXJtaXNzaW9ucygpOwp9CgpjbGFzcyBGaXJlYmFzZVBlcm1pc3Npb25NYW5hZ2VyIGltcGxlbWVudHMgTm90aWZpY2F0aW9uUGVybWlzc2lvbk1hbmFnZXIgewogIHN0YXRpYyBjb25zdCBTdHJpbmcgX3RhZyA9ICdGaXJlYmFzZVBlcm1pc3Npb25NYW5hZ2VyJzsKCiAgZmluYWwgTG9nZ2VyIF9sb2dnZXI7CiAgZmluYWwgRmlyZWJhc2VNZXNzYWdpbmcgX2ZpcmViYXNlTWVzc2FnaW5nOwoKICBGaXJlYmFzZVBlcm1pc3Npb25NYW5hZ2VyKHsKICAgIExvZ2dlcj8gbG9nZ2VyLAogICAgRmlyZWJhc2VNZXNzYWdpbmc/IGZpcmViYXNlTWVzc2FnaW5nLAogIH0pIDogX2xvZ2dlciA9IGxvZ2dlciA/PyBkaS5nZXQ8TG9nZ2VyPigpLAogICAgICAgX2ZpcmViYXNlTWVzc2FnaW5nID0gZmlyZWJhc2VNZXNzYWdpbmcgPz8gRmlyZWJhc2VNZXNzYWdpbmcuaW5zdGFuY2U7CgogIEBvdmVycmlkZQogIEZ1dHVyZTxib29sPiByZXF1ZXN0UGVybWlzc2lvbnMoKSBhc3luYyB7CiAgICB0cnkgewogICAgICBfbG9nZ2VyLmkoJyRfdGFnOiBSZXF1ZXN0aW5nIG5vdGlmaWNhdGlvbiBwZXJtaXNzaW9ucycpOwogICAgICBmaW5hbCBhdXRoU2V0dGluZ3MgPSBhd2FpdCBfZmlyZWJhc2VNZXNzYWdpbmcucmVxdWVzdFBlcm1pc3Npb24oKTsKCiAgICAgIGZpbmFsIGlzR3JhbnRlZCA9CiAgICAgICAgICBhdXRoU2V0dGluZ3MuYXV0aG9yaXphdGlvblN0YXR1cyA9PSBBdXRob3JpemF0aW9uU3RhdHVzLmF1dGhvcml6ZWQgfHwKICAgICAgICAgIGF1dGhTZXR0aW5ncy5hdXRob3JpemF0aW9uU3RhdHVzID09IEF1dGhvcml6YXRpb25TdGF0dXMucHJvdmlzaW9uYWw7CgogICAgICBfbG9nZ2VyLmkoCiAgICAgICAgJyRfdGFnOiBOb3RpZmljYXRpb24gcGVybWlzc2lvbnMgcmVxdWVzdGVkICcKICAgICAgICAnd2l0aCBzdGF0dXM6ICR7YXV0aFNldHRpbmdzLmF1dGhvcml6YXRpb25TdGF0dXN9LCAnCiAgICAgICAgJ2dyYW50ZWQ6ICRpc0dyYW50ZWQnLAogICAgICApOwoKICAgICAgcmV0dXJuIGlzR3JhbnRlZDsKICAgIH0gY2F0Y2ggKGUsIHMpIHsKICAgICAgX2xvZ2dlci5lKCckX3RhZzogRXJyb3IgcmVxdWVzdGluZyBwZXJtaXNzaW9ucycsIGUsIHMpOwogICAgICB0aHJvdyBOb3RpZmljYXRpb25FeGNlcHRpb24obWVzc2FnZTogZS5tZXNzYWdlKCkpOwogICAgfQogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/services/notification_token_manager.dart", + "data": + "aW1wb3J0ICdkYXJ0OmlvJzsKCmltcG9ydCAncGFja2FnZTpjb3JlL2xvZ2dlci9sb2dnZXIuZGFydCc7CmltcG9ydCAncGFja2FnZTpkaS9kaS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmZpcmViYXNlX21lc3NhZ2luZy9maXJlYmFzZV9tZXNzYWdpbmcuZGFydCc7CmltcG9ydCAncGFja2FnZTpuZXR3b3JrL25ldHdvcmsuZGFydCc7CmltcG9ydCAncGFja2FnZTpub3RpZmljYXRpb25zL25vdGlmaWNhdGlvbnMuZGFydCc7CgphYnN0cmFjdCBjbGFzcyBOb3RpZmljYXRpb25Ub2tlbk1hbmFnZXIgewogIEZ1dHVyZTxTdHJpbmc/PiBnZXRGQ01Ub2tlbigpOwoKICBGdXR1cmU8U3RyaW5nPiByZWdpc3RlclRva2VuKFN0cmluZyBmY21Ub2tlbik7CgogIEZ1dHVyZTx2b2lkPiB1blJlZ2lzdGVyVG9rZW4oU3RyaW5nIGRldmljZUlkKTsKCiAgRnV0dXJlPHZvaWQ+IGhhbmRsZVRva2VuUmVmcmVzaChTdHJpbmcgbmV3VG9rZW4pOwp9CgpjbGFzcyBGaXJlYmFzZVRva2VuTWFuYWdlciBpbXBsZW1lbnRzIE5vdGlmaWNhdGlvblRva2VuTWFuYWdlciB7CiAgc3RhdGljIGNvbnN0IFN0cmluZyBfdGFnID0gJ0ZpcmViYXNlVG9rZW5NYW5hZ2VyJzsKCiAgZmluYWwgTG9nZ2VyIF9sb2dnZXI7CiAgZmluYWwgTmV0d29ya0NsaWVudCBfbmV0d29ya0NsaWVudDsKICBmaW5hbCBEZXZpY2VJbmZvIF9kZXZpY2VJbmZvOwogIGZpbmFsIEZpcmViYXNlTWVzc2FnaW5nIF9maXJlYmFzZU1lc3NhZ2luZzsKCiAgRmlyZWJhc2VUb2tlbk1hbmFnZXIoewogICAgTG9nZ2VyPyBsb2dnZXIsCiAgICBOZXR3b3JrQ2xpZW50PyBuZXR3b3JrQ2xpZW50LAogICAgRGV2aWNlSW5mbz8gZGV2aWNlSW5mbywKICAgIEZpcmViYXNlTWVzc2FnaW5nPyBmaXJlYmFzZU1lc3NhZ2luZywKICB9KSA6IF9sb2dnZXIgPSBsb2dnZXIgPz8gZGkuZ2V0PExvZ2dlcj4oKSwKICAgICAgIF9uZXR3b3JrQ2xpZW50ID0gbmV0d29ya0NsaWVudCA/PyBkaS5nZXQ8TmV0d29ya0NsaWVudD4oKSwKICAgICAgIF9kZXZpY2VJbmZvID0gZGV2aWNlSW5mbyA/PyBkaS5nZXQ8RGV2aWNlSW5mbz4oKSwKICAgICAgIF9maXJlYmFzZU1lc3NhZ2luZyA9IGZpcmViYXNlTWVzc2FnaW5nID8/IEZpcmViYXNlTWVzc2FnaW5nLmluc3RhbmNlOwoKICBAb3ZlcnJpZGUKICBGdXR1cmU8U3RyaW5nPz4gZ2V0RkNNVG9rZW4oKSBhc3luYyB7CiAgICB0cnkgewogICAgICBmaW5hbCB0b2tlbiA9IGF3YWl0IF9maXJlYmFzZU1lc3NhZ2luZy5nZXRUb2tlbigpOwogICAgICBpZiAodG9rZW4gIT0gbnVsbCkgewogICAgICAgIF9sb2dnZXIuaSgnJF90YWc6IEZDTSBUb2tlbiBvYnRhaW5lZCBzdWNjZXNzZnVsbHknKTsKICAgICAgfSBlbHNlIHsKICAgICAgICBfbG9nZ2VyLncoJyRfdGFnOiBGYWlsZWQgdG8gb2J0YWluIEZDTSB0b2tlbicpOwogICAgICB9CiAgICAgIHJldHVybiB0b2tlbjsKICAgIH0gY2F0Y2ggKGUsIHMpIHsKICAgICAgX2xvZ2dlci5lKCckX3RhZzogRXJyb3IgZ2V0dGluZyBGQ00gdG9rZW4nLCBlLCBzKTsKICAgICAgcmV0aHJvdzsKICAgIH0KICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTxTdHJpbmc+IHJlZ2lzdGVyVG9rZW4oU3RyaW5nIGZjbVRva2VuKSBhc3luYyB7CiAgICB0cnkgewogICAgICBfbG9nZ2VyLmkoJyRfdGFnOiBSZWdpc3RlcmluZyBGQ00gdG9rZW4gd2l0aCBiYWNrZW5kJyk7CgogICAgICBmaW5hbCBkZXZpY2VJZCA9IGF3YWl0IF9kZXZpY2VJbmZvLmdlbmVyYXRlRGV2aWNlSWQoKTsKICAgICAgZmluYWwgZGV2aWNlTmFtZSA9IGF3YWl0IF9kZXZpY2VJbmZvLmdldERldmljZU5hbWUoKTsKICAgICAgZmluYWwgZGV2aWNlVHlwZSA9IFBsYXRmb3JtLmlzSU9TID8gJ2lvcycgOiAnYW5kcm9pZCc7CgogICAgICBmaW5hbCByZXF1ZXN0ID0gRGV2aWNlVG9rZW5SZXF1ZXN0KAogICAgICAgIGZjbVRva2VuOiBmY21Ub2tlbiwKICAgICAgICBkZXZpY2VJZDogZGV2aWNlSWQsCiAgICAgICAgZGV2aWNlTmFtZTogZGV2aWNlTmFtZSwKICAgICAgICBkZXZpY2VUeXBlOiBkZXZpY2VUeXBlLAogICAgICApOwoKICAgICAgYXdhaXQgX25ldHdvcmtDbGllbnQucG9zdDxNYXA8U3RyaW5nLCBkeW5hbWljPj4oCiAgICAgICAgJy9kZXZpY2UtdG9rZW5zL21lJywKICAgICAgICBkYXRhOiByZXF1ZXN0LnRvSnNvbigpLAogICAgICAgIGZyb21Kc29uVDogKGpzb24pID0+IGpzb24gYXMgTWFwPFN0cmluZywgZHluYW1pYz4sCiAgICAgICk7CiAgICAgIF9sb2dnZXIuaSgnJF90YWc6IEZDTSB0b2tlbiByZWdpc3RlcmVkIHN1Y2Nlc3NmdWxseScpOwogICAgICByZXR1cm4gZGV2aWNlSWQ7CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2dnZXIuZSgnJF90YWc6IEVycm9yIHJlZ2lzdGVyaW5nIEZDTSB0b2tlbicsIGUsIHMpOwogICAgICByZXRocm93OwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IHVuUmVnaXN0ZXJUb2tlbihTdHJpbmcgZGV2aWNlSWQpIGFzeW5jIHsKICAgIHRyeSB7CiAgICAgIF9sb2dnZXIuaSgnJF90YWc6IFJlbW92aW5nIEZDTSB0b2tlbiBmcm9tIGJhY2tlbmQnKTsKCiAgICAgIGF3YWl0IF9uZXR3b3JrQ2xpZW50LmRlbGV0ZTxNYXA8U3RyaW5nLCBkeW5hbWljPj4oCiAgICAgICAgJy9kZXZpY2UtdG9rZW5zL21lLyRkZXZpY2VJZCcsCiAgICAgICAgZnJvbUpzb25UOiAoanNvbikgPT4ganNvbiBhcyBNYXA8U3RyaW5nLCBkeW5hbWljPiwKICAgICAgKTsKICAgICAgX2xvZ2dlci5pKCckX3RhZzogRkNNIHRva2VuIHJlbW92ZWQgc3VjY2Vzc2Z1bGx5Jyk7CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2dnZXIuZSgnJF90YWc6IEVycm9yIHJlbW92aW5nIEZDTSB0b2tlbicsIGUsIHMpOwogICAgICByZXRocm93OwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IGhhbmRsZVRva2VuUmVmcmVzaChTdHJpbmcgbmV3VG9rZW4pIGFzeW5jIHsKICAgIF9sb2dnZXIuaSgnJF90YWc6IEZDTSB0b2tlbiByZWZyZXNoZWQnKTsKICAgIGF3YWl0IHJlZ2lzdGVyVG9rZW4obmV3VG9rZW4pOwogIH0KfQo=", + "type": "text" + }, + { + "path": + "{{ name.snakeCase() }}/packages/{{notifications_dir}}/pubspec.yaml", + "data": + "bmFtZTogbm90aWZpY2F0aW9ucwpkZXNjcmlwdGlvbjogIkZpcmViYXNlIENsb3VkIE1lc3NhZ2luZywgcGVybWlzc2lvbnMgYW5kIGRldmljZS10b2tlbiBtYW5hZ2VtZW50IGZvciB7e25hbWUudGl0bGVDYXNlKCl9fS4iCnZlcnNpb246IDAuMS4wCnB1Ymxpc2hfdG86ICdub25lJwoKZW52aXJvbm1lbnQ6CiAgc2RrOiBeMy44LjAKICBmbHV0dGVyOiAiPj0zLjI5LjAiCgpkZXBlbmRlbmNpZXM6CiAgZmx1dHRlcjoKICAgIHNkazogZmx1dHRlcgogIGFwcF9iYWRnZV9wbHVzOiBeMS4xLjIKICBjcnlwdG86IF4zLjAuNgogIGRldmljZV9pbmZvX3BsdXM6IF4xMS4xLjEKICBmaXJlYmFzZV9jb3JlOiBeMy4xNC4wCiAgZmlyZWJhc2VfbWVzc2FnaW5nOiBeMTUuMS42CiAgZ29fcm91dGVyOiBeMTUuMS4zCiAganNvbl9hbm5vdGF0aW9uOiBeNC45LjAKICBwZXJtaXNzaW9uX2hhbmRsZXI6IF4xMS4xLjAKICB0aW1lem9uZTogXjAuOS40CiAgY29yZToKICAgIHBhdGg6IC4uL2NvcmUKICBkZXNpZ25fc3lzdGVtOgogICAgcGF0aDogLi4vZGVzaWduX3N5c3RlbQogIGRpOgogICAgcGF0aDogLi4vZGkKe3sjbmV0d29ya319ICBuZXR3b3JrOgogICAgcGF0aDogLi4vbmV0d29yawp7ey9uZXR3b3JrfX17eyNhbmFseXRpY3N9fSAgYW5hbHl0aWNzOgogICAgcGF0aDogLi4vYW5hbHl0aWNzCnt7L2FuYWx5dGljc319CmRldl9kZXBlbmRlbmNpZXM6CiAgZmx1dHRlcl90ZXN0OgogICAgc2RrOiBmbHV0dGVyCiAgZmx1dHRlcl9saW50czogXjUuMC4wCiAgYnVpbGRfcnVubmVyOiBeMi40LjE1CiAganNvbl9zZXJpYWxpemFibGU6IF42LjkuNQoKZmx1dHRlcjoKICB1c2VzLW1hdGVyaWFsLWRlc2lnbjogdHJ1ZQo=", "type": "text" }, { "path": "{{ name.snakeCase() }}/plugins/README.md", - "data": "IyBQdXQgYWxsIHlvdXIgcGx1Z2lucyBpbiB0aGlzIGZvbGRlcgo=", + "data": + "IyBQbHVnaW5zCgpQbGF0Zm9ybSBjaGFubmVscyBhbmQgbmF0aXZlIGludGVncmF0aW9ucy4KCmBgYHNoCm5vbnN0b3AgY3JlYXRlIHBsdWdpbiBteV9wbHVnaW4gLW8gcGx1Z2lucwpgYGAK", "type": "text" }, { "path": "{{ name.snakeCase() }}/pubspec.yaml", "data": - "bmFtZToge3sgbmFtZS5zbmFrZUNhc2UoKSB9fV93b3Jrc3BhY2UKZGVzY3JpcHRpb246IHt7IGRlc2NyaXB0aW9uIH19CgplbnZpcm9ubWVudDoKICBzZGs6ICc+PTMuMC4wIDw0LjAuMCcKCmRldl9kZXBlbmRlbmNpZXM6CiAgZmx1dHRlcl9saW50czogXjUuMC4wCiAgbWVsb3M6IF42LjIuMAo=", + "bmFtZToge3sgbmFtZS5zbmFrZUNhc2UoKSB9fV93b3Jrc3BhY2UKZGVzY3JpcHRpb246ICJ7e3tkZXNjcmlwdGlvbn19fSIKcHVibGlzaF90bzogJ25vbmUnCgplbnZpcm9ubWVudDoKICBzZGs6ICc+PTMuOC4wIDw0LjAuMCcKCmRldl9kZXBlbmRlbmNpZXM6CiAgZmx1dHRlcl9saW50czogXjUuMC4wCiAgbWVsb3M6IF42LjIuMAo=", "type": "text" }, { "path": "{{ name.snakeCase() }}/README.md", "data": - "IyB7eyBuYW1lLnRpdGxlQ2FzZSgpIH19Cgp7eyBkZXNjcmlwdGlvbiB9fQoKWyFbbm9uc3RvcF9jbGldKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2Uvc3RhcnRlZCUyMHdpdGgtbm9uc3RvcF9jbGktMTY2QzRFLnN2Zz9zdHlsZT1mbGF0LXNxdWFyZSldKGh0dHBzOi8vcHViLmRldi9wYWNrYWdlcy9ub25zdG9wX2NsaSkKWyFbbWVsb3NdKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2UvbWFpbnRhaW5lZCUyMHdpdGgtbWVsb3MtZjcwMGZmLnN2Zz9zdHlsZT1mbGF0LXNxdWFyZSldKGh0dHBzOi8vZ2l0aHViLmNvbS9pbnZlcnRhc2UvbWVsb3MpCgoKIyMgVGVjaG5vbG9naWVzIGF0IHBsYXkKCi0gRGFydAotIEZsdXR0ZXIKLSBNZWxvcwoKIyMgR2V0dGluZyBTdGFydGVkCgotIEFsbCBhcHBzIGFyZSBpbiB0aGUgYC9hcHBzYCBkaXJlY3RvcnkuCi0gQWxsIGZlYXR1cmVzIGFyZSBpbiB0aGUgYC9mZWF0dXJlc2AgZGlyZWN0b3J5Ci0gQWxsIHNoYXJlZCBwYWNrYWdlcyBhcmUgaW4gdGhlIGAvcGFja2FnZXNgIGRpcmVjdG9yeQotIEFsbCBwbHVnaW5zIGFyZSBpbiB0aGUgYC9wbHVnaW5zYCBkaXJlY3Rvcnk=", + "IyB7eyBuYW1lLnRpdGxlQ2FzZSgpIH19Cgp7e3tkZXNjcmlwdGlvbn19fQoKWyFbbm9uc3RvcF9jbGldKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2Uvc3RhcnRlZCUyMHdpdGgtbm9uc3RvcF9jbGktMTY2QzRFLnN2Zz9zdHlsZT1mbGF0LXNxdWFyZSldKGh0dHBzOi8vcHViLmRldi9wYWNrYWdlcy9ub25zdG9wX2NsaSkKWyFbbWVsb3NdKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2UvbWFpbnRhaW5lZCUyMHdpdGgtbWVsb3MtZjcwMGZmLnN2Zz9zdHlsZT1mbGF0LXNxdWFyZSldKGh0dHBzOi8vZ2l0aHViLmNvbS9pbnZlcnRhc2UvbWVsb3MpCgojIyBMYXlvdXQKCmBgYAphcHBzL3t7bmFtZS5zbmFrZUNhc2UoKX19ICAgICAgdGhlIGFwcGxpY2F0aW9uIHNoZWxsOiBib290c3RyYXAsIHJvdXRlciwgc3BsYXNoCmZlYXR1cmVzLyAgICAgICAgICAgICAgICAgICAgICB2ZXJ0aWNhbCBzbGljZXMgKHVpICsgc3RhdGUgKyBkYXRhKQpwYWNrYWdlcy8gICAgICAgICAgICAgICAgICAgICAgc2hhcmVkIGNhcGFiaWxpdGllcwpwbHVnaW5zLyAgICAgICAgICAgICAgICAgICAgICAgbmF0aXZlIGludGVncmF0aW9ucwpgYGAKCkRlcGVuZGVuY2llcyBwb2ludCBpbndhcmQ6IGFwcHMgZGVwZW5kIG9uIGZlYXR1cmVzLCBmZWF0dXJlcyBkZXBlbmQgb24KcGFja2FnZXMsIHBhY2thZ2VzIGRlcGVuZCBvbiBub3RoaW5nIGFib3ZlIHRoZW0uCgojIyMgV2hhdCBpcyBhbHJlYWR5IHdpcmVkCgp8IE1vZHVsZSB8IFJvbGUgfAp8IC0tLSB8IC0tLSB8CnwgYHBhY2thZ2VzL2NvcmVgIHwgbG9nZ2VyLCBESSBib290c3RyYXAsIHJvdXRlIG9ic2VydmVycywgZXJyb3IgdHlwZXMsIEJMb0Mgb2JzZXJ2ZXIgfAp8IGBwYWNrYWdlcy9kaWAgfCBzZXJ2aWNlIGxvY2F0b3IgKEdldEl0KSBiZWhpbmQgYSBzbWFsbCBpbnRlcmZhY2UgfAp8IGBwYWNrYWdlcy9kZXNpZ25fc3lzdGVtYCB8IE1hdGVyaWFsIDMgdGhlbWUsIHNoYXJlZCBjb21wb25lbnRzLCB0b2FzdHMsIGxvYWRlcnMgfAp8IGBwYWNrYWdlcy9sb2NhbGl6YXRpb25gIHwgdHlwZS1zYWZlIHN0cmluZ3MgZ2VuZXJhdGVkIGZyb20gWUFNTCAoaTY5bikgfAp8IGBwYWNrYWdlcy91dGlsc2AgfCBkZXBlbmRlbmN5LWxpZ2h0IGhlbHBlcnMgfAp7eyNuZXR3b3JrfX18IGBwYWNrYWdlcy9uZXR3b3JrYCB8IERpbyBjbGllbnQsIGF1dGggKyBsb2dnaW5nIGludGVyY2VwdG9ycywgdHlwZWQgZXJyb3JzIHwKe3svbmV0d29ya319e3sjYW5hbHl0aWNzfX18IGBwYWNrYWdlcy9hbmFseXRpY3NgIHwgZXZlbnQgdHJhY2tpbmcgYmVoaW5kIGEgc3dhcHBhYmxlIGNsaWVudCB8Cnt7L2FuYWx5dGljc319e3sjY3Jhc2hseXRpY3N9fXwgYHBhY2thZ2VzL2NyYXNobHl0aWNzYCB8IGNyYXNoIGFuZCBub24tZmF0YWwgcmVwb3J0aW5nIHwKe3svY3Jhc2hseXRpY3N9fXt7I25vdGlmaWNhdGlvbnN9fXwgYHBhY2thZ2VzL25vdGlmaWNhdGlvbnNgIHwgRkNNLCBwZXJtaXNzaW9ucywgZGV2aWNlLXRva2VuIHJlZ2lzdHJhdGlvbiB8Cnt7L25vdGlmaWNhdGlvbnN9fXt7I2ZlYXR1cmVfZmxhZ3N9fXwgYHBhY2thZ2VzL2ZlYXR1cmVfZmxhZ3NgIHwgUmVtb3RlIENvbmZpZyBmbGFncyB3aXRoIGEgd2lkZ2V0IHdyYXBwZXIgfAp7ey9mZWF0dXJlX2ZsYWdzfX17eyNkZXZlbG9wZXJ9fXwgYHBhY2thZ2VzL2RldmVsb3BlcmAgfCBpbi1hcHAgZGV2IHRvb2xzLCByZWFjaGFibGUgYnkgYSBoaWRkZW4gNS10YXAgZ2VzdHVyZSB8Cnt7L2RldmVsb3Blcn19e3sjYXV0aH19fCBgZmVhdHVyZXMvYXV0aGAgfCBGaXJlYmFzZSBBdXRoIChlbWFpbCwgR29vZ2xlLCBBcHBsZSkgKyByb3V0ZSBndWFyZHMgfAp7ey9hdXRofX17eyNkYXNoYm9hcmR9fXwgYGZlYXR1cmVzL2Rhc2hib2FyZGAgfCBib3R0b20tbmF2aWdhdGlvbiBzaGVsbCB3aXRoIHN0YXJ0ZXIgdGFicyB8Cnt7L2Rhc2hib2FyZH19CgojIyBHZXR0aW5nIHN0YXJ0ZWQKCmBgYHNoCmRhcnQgcHViIGdsb2JhbCBhY3RpdmF0ZSBtZWxvcwptZWxvcyBib290c3RyYXAKYGBgCnt7I2ZpcmViYXNlfX0KIyMjIEZpcmViYXNlCgpUaGlzIHByb2plY3QgdXNlcyBGaXJlYmFzZSwgc28gY29uZmlndXJlIGl0IGJlZm9yZSB0aGUgZmlyc3QgcnVuIC0gdGhlCmdlbmVyYXRlZCBgbGliL2ZpcmViYXNlX29wdGlvbnMuZGFydGAgZGVsaWJlcmF0ZWx5IHRocm93cyB1bnRpbCB5b3UgZG86CgpgYGBzaApkYXJ0IHB1YiBnbG9iYWwgYWN0aXZhdGUgZmx1dHRlcmZpcmVfY2xpCmNkIGFwcHMve3tuYW1lLnNuYWtlQ2FzZSgpfX0KZmx1dHRlcmZpcmUgY29uZmlndXJlCmBgYAp7ey9maXJlYmFzZX19CiMjIyBSdW4KCmBgYHNoCmNkIGFwcHMve3tuYW1lLnNuYWtlQ2FzZSgpfX0KZmx1dHRlciBydW4gLS1kYXJ0LWRlZmluZT1CQVNFX1VSTD1odHRwczovL2FwaS5leGFtcGxlLmNvbQpgYGAKCkV2ZXJ5IGVudmlyb25tZW50IHZhbHVlIGlzIGEgYC0tZGFydC1kZWZpbmVgIHJlYWQgaW4KYHBhY2thZ2VzL2NvcmUvbGliL2NvbnN0YW50cy9lbnZpcm9ubWVudC5kYXJ0YC4gQWRkIG5ldyBvbmVzIHRoZXJlIHJhdGhlciB0aGFuCnNjYXR0ZXJpbmcgYFN0cmluZy5mcm9tRW52aXJvbm1lbnRgIGFyb3VuZCB0aGUgY29kZWJhc2UuCnt7I2ZpcmViYXNlfX0KVG8gcnVuIGFnYWluc3QgdGhlIEZpcmViYXNlIGVtdWxhdG9yIHN1aXRlLCBhZGQKYC0tZGFydC1kZWZpbmU9VVNFX0VNVUxBVE9SUz10cnVlYC4Ke3svZmlyZWJhc2V9fQojIyBEYXktdG8tZGF5CgpgYGBzaAptZWxvcyBsaW50ICAgICAgICAgICAgICMgZm9ybWF0ICsgYW5hbHl6ZSBldmVyeXRoaW5nCm1lbG9zIHRlc3QgICAgICAgICAgICAgIyBydW4gZXZlcnkgcGFja2FnZSdzIHRlc3RzCm1lbG9zIHJ1biBnZW5lcmF0ZSAgICAgIyBidWlsZF9ydW5uZXIgd2hlcmUgaXQgaXMgbmVlZGVkCm1lbG9zIHJ1biBnZW5lcmF0ZTppNjluICAjIHJlZ2VuZXJhdGUgc3RyaW5ncyBhZnRlciBlZGl0aW5nIG1lc3NhZ2VzLmk2OW4ueWFtbApgYGAKCiMjIEFkZGluZyB0byB0aGUgbW9ub3JlcG8KCmBgYHNoCm5vbnN0b3AgY3JlYXRlIHBhY2thZ2UgbXlfZmVhdHVyZSAtbyBmZWF0dXJlcwpub25zdG9wIGNyZWF0ZSBwYWNrYWdlIG15X3BhY2thZ2UgLW8gcGFja2FnZXMKbm9uc3RvcCBjcmVhdGUgYXBwIG15X3NlY29uZF9hcHAgLW8gYXBwcwpub25zdG9wIGNyZWF0ZSBwbHVnaW4gbXlfcGx1Z2luIC1vIHBsdWdpbnMKYGBgCgojIyBXaGVyZSB0byBzdGFydAoKMS4gUmVwbGFjZSB0aGUgcGxhY2Vob2xkZXIgdGFicyBpbiBgZmVhdHVyZXMvZGFzaGJvYXJkYC4KMi4gUHV0IHlvdXIgcHJvZHVjdCBzdHJpbmdzIGluIGBwYWNrYWdlcy9sb2NhbGl6YXRpb24vbGliL21lc3NhZ2VzLmk2OW4ueWFtbGAuCjMuIFNldCB0aGUgcGFsZXR0ZSBpbiBgcGFja2FnZXMvZGVzaWduX3N5c3RlbS9saWIvZ2VuZXJhdGVkL3RoZW1lLmRhcnRgLgo0LiBBZGQgZmVhdHVyZSBtb2R1bGVzIGFuZCBzcHJlYWQgdGhlaXIgcm91dGVzIGludG8KICAgYGFwcHMve3tuYW1lLnNuYWtlQ2FzZSgpfX0vbGliL3JvdXRlci9yb3V0ZXIuZGFydGAuCg==", "type": "text" } ], "hooks": [ + { + "path": "commands/checked_process.dart", + "data": + "aW1wb3J0ICdkYXJ0OmlvJzsKCmltcG9ydCAncGFja2FnZTptYXNvbi9tYXNvbi5kYXJ0JzsKCi8vLyBSdW5zIFtleGVjdXRhYmxlXSBhbmQgdGhyb3dzIHdoZW4gaXQgZmFpbHMuCi8vLwovLy8gYGNsaV9jb3JlYCdzIGB0cmFja09wZXJhdGlvbmAgcmVwb3J0cyBzdWNjZXNzIGFzIHNvb24gYXMgdGhlIGZ1dHVyZQovLy8gY29tcGxldGVzLCBzbyBhIG5vbi16ZXJvIGV4aXQgY29kZSBzaG93cyBhIGdyZWVuIHRpY2sgYW5kIGdlbmVyYXRpb24KLy8vIGNhcnJpZXMgb24gdG8gcHJvZHVjZSBhIGhhbGYtYnVpbHQgcHJvamVjdC4gRXZlcnkgc3RlcCBoZXJlIGlzIGxvYWQtYmVhcmluZwovLy8gLSBpZiBgZmx1dHRlciBjcmVhdGVgIG9yIGBtZWxvcyBib290c3RyYXBgIGZhaWxzIHRoZXJlIGlzIG5vdGhpbmcgd29ydGgKLy8vIGNvbnRpbnVpbmcgZm9yIC0gc28gdGhlIGV4aXQgY29kZSBpcyBjaGVja2VkIGFuZCB0aGUgY2hpbGQncyBzdGRlcnIgaXMKLy8vIHN1cmZhY2VkLgpGdXR1cmU8dm9pZD4gcnVuQ2hlY2tlZCgKICBIb29rQ29udGV4dCBjb250ZXh0LCB7CiAgcmVxdWlyZWQgU3RyaW5nIHN0YXJ0TWVzc2FnZSwKICByZXF1aXJlZCBTdHJpbmcgZW5kTWVzc2FnZSwKICByZXF1aXJlZCBTdHJpbmcgZXhlY3V0YWJsZSwKICByZXF1aXJlZCBMaXN0PFN0cmluZz4gYXJndW1lbnRzLAogIFN0cmluZz8gd29ya2luZ0RpcmVjdG9yeSwKfSkgYXN5bmMgewogIGZpbmFsIHByb2dyZXNzID0gY29udGV4dC5sb2dnZXIucHJvZ3Jlc3Moc3RhcnRNZXNzYWdlKTsKCiAgZmluYWwgUHJvY2Vzc1Jlc3VsdCByZXN1bHQ7CiAgdHJ5IHsKICAgIHJlc3VsdCA9IGF3YWl0IFByb2Nlc3MucnVuKAogICAgICBleGVjdXRhYmxlLAogICAgICBhcmd1bWVudHMsCiAgICAgIHdvcmtpbmdEaXJlY3Rvcnk6IHdvcmtpbmdEaXJlY3RvcnksCiAgICAgIHJ1bkluU2hlbGw6IHRydWUsCiAgICApOwogIH0gY2F0Y2ggKGVycm9yKSB7CiAgICBwcm9ncmVzcy5mYWlsKCckc3RhcnRNZXNzYWdlIGZhaWxlZDogJGVycm9yJyk7CiAgICByZXRocm93OwogIH0KCiAgaWYgKHJlc3VsdC5leGl0Q29kZSAhPSAwKSB7CiAgICBwcm9ncmVzcy5mYWlsKCckc3RhcnRNZXNzYWdlIGZhaWxlZCAoZXhpdCAke3Jlc3VsdC5leGl0Q29kZX0pJyk7CiAgICBmaW5hbCBkZXRhaWxzID0gJyR7cmVzdWx0LnN0ZGVycn0nLnRyaW0oKS5pc05vdEVtcHR5CiAgICAgICAgPyAnJHtyZXN1bHQuc3RkZXJyfScudHJpbSgpCiAgICAgICAgOiAnJHtyZXN1bHQuc3Rkb3V0fScudHJpbSgpOwogICAgaWYgKGRldGFpbHMuaXNOb3RFbXB0eSkgY29udGV4dC5sb2dnZXIuZXJyKGRldGFpbHMpOwogICAgdGhyb3cgUHJvY2Vzc0V4Y2VwdGlvbigKICAgICAgZXhlY3V0YWJsZSwKICAgICAgYXJndW1lbnRzLAogICAgICAnZXhpdGVkIHdpdGggJHtyZXN1bHQuZXhpdENvZGV9JywKICAgICAgcmVzdWx0LmV4aXRDb2RlLAogICAgKTsKICB9CgogIHByb2dyZXNzLmNvbXBsZXRlKGVuZE1lc3NhZ2UpOwp9Cg==", + "type": "text" + }, { "path": "commands/flutter_create_command.dart", "data": - "aW1wb3J0ICdkYXJ0OmFzeW5jJzsKCmltcG9ydCAncGFja2FnZTpjbGlfY29yZS9jbGlfY29yZS5kYXJ0JyBzaG93IEJhc2VGbHV0dGVyQ29tbWFuZDsKaW1wb3J0ICdwYWNrYWdlOm1hc29uL21hc29uLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6cGF0aC9wYXRoLmRhcnQnIGFzIHA7CgpmaW5hbCBjbGFzcyBGbHV0dGVyQ3JlYXRlQ29tbWFuZCBleHRlbmRzIEJhc2VGbHV0dGVyQ29tbWFuZCB7CiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IHJ1bihIb29rQ29udGV4dCBjb250ZXh0KSBhc3luYyB7CiAgICBmaW5hbCBTdHJpbmcgbmFtZSA9IGNvbnRleHQudmFyc1snbmFtZSddOwogICAgZmluYWwgU3RyaW5nIGRlc2NyaXB0aW9uID0gY29udGV4dC52YXJzWydkZXNjcmlwdGlvbiddOwogICAgZmluYWwgU3RyaW5nIG9yZ05hbWUgPSBjb250ZXh0LnZhcnNbJ29yZ19uYW1lJ10gPz8gJ2NvbS5leGFtcGxlJzsKICAgIGZpbmFsIGFwcE5hbWUgPSBuYW1lLnNuYWtlQ2FzZTsKICAgIGZpbmFsIG91dHB1dFBhdGggPSBwLm5vcm1hbGl6ZSgnJGFwcE5hbWUvYXBwcycpOwoKICAgIGF3YWl0IGNyZWF0ZUZsdXR0ZXJQcm9qZWN0KAogICAgICBjb250ZXh0OiBjb250ZXh0LAogICAgICBuYW1lOiBhcHBOYW1lLAogICAgICBkZXNjcmlwdGlvbjogZGVzY3JpcHRpb24sCiAgICAgIG91dHB1dFBhdGg6IG91dHB1dFBhdGgsCiAgICAgIG9yZ05hbWU6IG9yZ05hbWUsCiAgICApOwoKICAgIGF3YWl0IHJlbW92ZUFuYWx5c2lzT3B0aW9ucygKICAgICAgY29udGV4dDogY29udGV4dCwKICAgICAgcHJvamVjdFBhdGg6IHAuam9pbihvdXRwdXRQYXRoLCBhcHBOYW1lKSwKICAgICk7CiAgfQp9Cg==", + "aW1wb3J0ICdkYXJ0OmFzeW5jJzsKaW1wb3J0ICdkYXJ0OmlvJzsKCmltcG9ydCAncGFja2FnZTpjbGlfY29yZS9jbGlfY29yZS5kYXJ0JyBzaG93IENsaUNvbW1hbmQsIEZpbGVVdGlsczsKaW1wb3J0ICdwYWNrYWdlOm1hc29uL21hc29uLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6cGF0aC9wYXRoLmRhcnQnIGFzIHA7CgppbXBvcnQgJ2NoZWNrZWRfcHJvY2Vzcy5kYXJ0JzsKCi8vLyBTY2FmZm9sZHMgdGhlIGFwcGxpY2F0aW9uIHdpdGggYGZsdXR0ZXIgY3JlYXRlYC4KLy8vCi8vLyBUaGlzIHJ1bnMgaW4gcHJlLWdlbiwgc28gdGhlIGJyaWNrJ3Mgb3duIGBsaWIvYCBhbmQgYHB1YnNwZWMueWFtbGAgYXJlCi8vLyB3cml0dGVuIG92ZXIgdGhlIGNvdW50ZXItYXBwIHRoYXQgYGZsdXR0ZXIgY3JlYXRlYCBwcm9kdWNlcy4KZmluYWwgY2xhc3MgRmx1dHRlckNyZWF0ZUNvbW1hbmQgZXh0ZW5kcyBDbGlDb21tYW5kIHsKICBAb3ZlcnJpZGUKICBGdXR1cmU8dm9pZD4gcnVuKEhvb2tDb250ZXh0IGNvbnRleHQpIGFzeW5jIHsKICAgIGZpbmFsIFN0cmluZyBuYW1lID0gY29udGV4dC52YXJzWyduYW1lJ107CiAgICBmaW5hbCBTdHJpbmcgZGVzY3JpcHRpb24gPSBjb250ZXh0LnZhcnNbJ2Rlc2NyaXB0aW9uJ107CiAgICBmaW5hbCBTdHJpbmcgb3JnTmFtZSA9IGNvbnRleHQudmFyc1snb3JnX25hbWUnXSA/PyAnY29tLmV4YW1wbGUnOwogICAgZmluYWwgYXBwTmFtZSA9IG5hbWUuc25ha2VDYXNlOwogICAgZmluYWwgYXBwc1BhdGggPSBwLm5vcm1hbGl6ZSgnJGFwcE5hbWUvYXBwcycpOwoKICAgIGF3YWl0IEZpbGVVdGlscy5lbnN1cmVEaXJlY3RvcnkoYXBwc1BhdGgpOwoKICAgIGF3YWl0IHJ1bkNoZWNrZWQoCiAgICAgIGNvbnRleHQsCiAgICAgIHN0YXJ0TWVzc2FnZTogJ0NyZWF0aW5nIEZsdXR0ZXIgYXBwOiAkYXBwTmFtZScsCiAgICAgIGVuZE1lc3NhZ2U6ICdGbHV0dGVyIGFwcCBjcmVhdGVkJywKICAgICAgZXhlY3V0YWJsZTogJ2ZsdXR0ZXInLAogICAgICBhcmd1bWVudHM6IFsKICAgICAgICAnY3JlYXRlJywKICAgICAgICBhcHBOYW1lLAogICAgICAgICctLXRlbXBsYXRlPWFwcCcsCiAgICAgICAgJy0tcGxhdGZvcm1zPWlvcyxhbmRyb2lkLHdlYicsCiAgICAgICAgJy0tZGVzY3JpcHRpb249JGRlc2NyaXB0aW9uJywKICAgICAgICAnLS1vcmc9JG9yZ05hbWUnLAogICAgICBdLAogICAgICB3b3JraW5nRGlyZWN0b3J5OiBhcHBzUGF0aCwKICAgICk7CgogICAgZmluYWwgYXBwUGF0aCA9IHAuam9pbihhcHBzUGF0aCwgYXBwTmFtZSk7CgogICAgLy8gVGhlIHdvcmtzcGFjZSBvd25zIGxpbnQgY29uZmlndXJhdGlvbi4KICAgIGF3YWl0IEZpbGVVdGlscy5kZWxldGVGaWxlKHAuam9pbihhcHBQYXRoLCAnYW5hbHlzaXNfb3B0aW9ucy55YW1sJykpOwoKICAgIC8vIGBmbHV0dGVyIGNyZWF0ZWAgbGVhdmVzIGEgd2lkZ2V0IHRlc3QgZm9yIHRoZSBjb3VudGVyIGFwcCBpdCBqdXN0IHdyb3RlOwogICAgLy8gdGhlIGJyaWNrIHJlcGxhY2VzIHRoYXQgYXBwLCBzbyB0aGUgdGVzdCB3b3VsZCBub3QgY29tcGlsZS4KICAgIGF3YWl0IEZpbGVVdGlscy5kZWxldGVGaWxlKHAuam9pbihhcHBQYXRoLCAndGVzdCcsICd3aWRnZXRfdGVzdC5kYXJ0JykpOwogICAgZmluYWwgdGVzdERpciA9IERpcmVjdG9yeShwLmpvaW4oYXBwUGF0aCwgJ3Rlc3QnKSk7CiAgICBpZiAodGVzdERpci5leGlzdHNTeW5jKCkgJiYgdGVzdERpci5saXN0U3luYygpLmlzRW1wdHkpIHsKICAgICAgYXdhaXQgdGVzdERpci5kZWxldGUoKTsKICAgIH0KICB9Cn0K", + "type": "text" + }, + { + "path": "commands/format_command.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNsaV9jb3JlL2NsaV9jb3JlLmRhcnQnIHNob3cgQ2xpQ29tbWFuZDsKaW1wb3J0ICdwYWNrYWdlOm1hc29uL21hc29uLmRhcnQnOwoKaW1wb3J0ICdjaGVja2VkX3Byb2Nlc3MuZGFydCc7CgovLy8gRm9ybWF0cyB0aGUgZ2VuZXJhdGVkIHdvcmtzcGFjZS4KLy8vCi8vLyBUZW1wbGF0ZXMgYXJlIHJlbmRlcmVkIGZyb20gbXVzdGFjaGUsIHNvIHRoZSBleGFjdCBsaW5lIGJyZWFrcyBkZXBlbmQgb24KLy8vIHdoaWNoIG1vZHVsZXMgd2VyZSBzZWxlY3RlZC4gUnVubmluZyB0aGUgZm9ybWF0dGVyIG9uY2UgaGVyZSBtZWFucyBhIGZyZXNoCi8vLyBwcm9qZWN0IHBhc3NlcyBpdHMgb3duIGBtZWxvcyBsaW50YCB3aGF0ZXZlciB0aGUgYW5zd2VycyB3ZXJlLgpmaW5hbCBjbGFzcyBGb3JtYXRDb21tYW5kIGV4dGVuZHMgQ2xpQ29tbWFuZCB7CiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IHJ1bihIb29rQ29udGV4dCBjb250ZXh0KSBhc3luYyB7CiAgICBmaW5hbCBTdHJpbmcgbmFtZSA9IGNvbnRleHQudmFyc1snbmFtZSddOwoKICAgIGF3YWl0IHJ1bkNoZWNrZWQoCiAgICAgIGNvbnRleHQsCiAgICAgIHN0YXJ0TWVzc2FnZTogJ0Zvcm1hdHRpbmcgZ2VuZXJhdGVkIGNvZGUnLAogICAgICBlbmRNZXNzYWdlOiAnQ29kZSBmb3JtYXR0ZWQnLAogICAgICBleGVjdXRhYmxlOiAnZGFydCcsCiAgICAgIGFyZ3VtZW50czogWydmb3JtYXQnLCAnLiddLAogICAgICB3b3JraW5nRGlyZWN0b3J5OiBuYW1lLnNuYWtlQ2FzZSwKICAgICk7CiAgfQp9Cg==", "type": "text" }, { "path": "commands/melos_command.dart", "data": - "aW1wb3J0ICdkYXJ0OmlvJzsKCmltcG9ydCAncGFja2FnZTpjbGlfY29yZS9jbGlfY29yZS5kYXJ0JyBzaG93IEJhc2VNZWxvc0NvbW1hbmQ7CmltcG9ydCAncGFja2FnZTptYXNvbi9tYXNvbi5kYXJ0JzsKCmZpbmFsIGNsYXNzIE1lbG9zQ29tbWFuZCBleHRlbmRzIEJhc2VNZWxvc0NvbW1hbmQgewogIEBvdmVycmlkZQogIEZ1dHVyZTx2b2lkPiBydW4oSG9va0NvbnRleHQgY29udGV4dCkgYXN5bmMgewogICAgZmluYWwgU3RyaW5nIG5hbWUgPSBjb250ZXh0LnZhcnNbJ25hbWUnXTsKICAgIGZpbmFsIGFwcE5hbWUgPSBuYW1lLnNuYWtlQ2FzZTsKCiAgICBhd2FpdCB0cmFja09wZXJhdGlvbigKICAgICAgY29udGV4dCwKICAgICAgc3RhcnRNZXNzYWdlOiAnQWN0aXZhdGluZyBNZWxvcyBnbG9iYWxseScsCiAgICAgIGVuZE1lc3NhZ2U6ICdNZWxvcyBhY3RpdmF0ZWQgZ2xvYmFsbHknLAogICAgICBvcGVyYXRpb246ICgpID0+IFByb2Nlc3MucnVuKAogICAgICAgICdkYXJ0JywKICAgICAgICBbJ3B1YicsICdnbG9iYWwnLCAnYWN0aXZhdGUnLCAnbWVsb3MnXSwKICAgICAgICBydW5JblNoZWxsOiB0cnVlLAogICAgICApLAogICAgKTsKCiAgICBhd2FpdCBib290c3RyYXAoCiAgICAgIGNvbnRleHQ6IGNvbnRleHQsCiAgICAgIHdvcmtzcGFjZVBhdGg6IGFwcE5hbWUsCiAgICApOwogIH0KfQo=", + "aW1wb3J0ICdwYWNrYWdlOmNsaV9jb3JlL2NsaV9jb3JlLmRhcnQnIHNob3cgQ2xpQ29tbWFuZDsKaW1wb3J0ICdwYWNrYWdlOm1hc29uL21hc29uLmRhcnQnOwoKaW1wb3J0ICdjaGVja2VkX3Byb2Nlc3MuZGFydCc7CgpmaW5hbCBjbGFzcyBNZWxvc0NvbW1hbmQgZXh0ZW5kcyBDbGlDb21tYW5kIHsKICBAb3ZlcnJpZGUKICBGdXR1cmU8dm9pZD4gcnVuKEhvb2tDb250ZXh0IGNvbnRleHQpIGFzeW5jIHsKICAgIGZpbmFsIFN0cmluZyBuYW1lID0gY29udGV4dC52YXJzWyduYW1lJ107CiAgICBmaW5hbCBhcHBOYW1lID0gbmFtZS5zbmFrZUNhc2U7CgogICAgYXdhaXQgcnVuQ2hlY2tlZCgKICAgICAgY29udGV4dCwKICAgICAgc3RhcnRNZXNzYWdlOiAnQWN0aXZhdGluZyBNZWxvcyBnbG9iYWxseScsCiAgICAgIGVuZE1lc3NhZ2U6ICdNZWxvcyBhY3RpdmF0ZWQgZ2xvYmFsbHknLAogICAgICBleGVjdXRhYmxlOiAnZGFydCcsCiAgICAgIGFyZ3VtZW50czogWydwdWInLCAnZ2xvYmFsJywgJ2FjdGl2YXRlJywgJ21lbG9zJ10sCiAgICApOwoKICAgIGF3YWl0IHJ1bkNoZWNrZWQoCiAgICAgIGNvbnRleHQsCiAgICAgIHN0YXJ0TWVzc2FnZTogJ1J1bm5pbmcgbWVsb3MgYm9vdHN0cmFwJywKICAgICAgZW5kTWVzc2FnZTogJ0RlcGVuZGVuY2llcyBpbnN0YWxsZWQnLAogICAgICBleGVjdXRhYmxlOiAnbWVsb3MnLAogICAgICBhcmd1bWVudHM6IFsnYm9vdHN0cmFwJ10sCiAgICAgIHdvcmtpbmdEaXJlY3Rvcnk6IGFwcE5hbWUsCiAgICApOwogIH0KfQo=", + "type": "text" + }, + { + "path": "commands/module_vars.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOm1hc29uL21hc29uLmRhcnQnOwoKLy8vIE9wdGlvbmFsIG1vZHVsZXMsIGVhY2ggb2Ygd2hpY2ggbWFwcyB0byBhIGRpcmVjdG9yeSBpbiB0aGUgYnJpY2suCi8vLwovLy8gQSBkaXJlY3RvcnkgbmFtZWQgYHt7PG1vZHVsZT5fZGlyfX1gIHJlbmRlcnMgdG8gYW4gZW1wdHkgcGF0aCBzZWdtZW50IHdoZW4KLy8vIHRoZSBtb2R1bGUgaXMgb2ZmLCBhbmQgbWFzb24gc2tpcHMgZXZlcnkgZmlsZSB1bmRlcm5lYXRoIGl0Lgpjb25zdCBvcHRpb25hbE1vZHVsZXMgPSA8U3RyaW5nPlsKICAnbmV0d29yaycsCiAgJ25vdGlmaWNhdGlvbnMnLAogICdhbmFseXRpY3MnLAogICdjcmFzaGx5dGljcycsCiAgJ2ZlYXR1cmVfZmxhZ3MnLAogICdkZXZlbG9wZXInLAogICdhdXRoJywKICAnZGFzaGJvYXJkJywKXTsKCi8vLyBNb2R1bGVzIHRoYXQgY2Fubm90IHdvcmsgd2l0aG91dCBgRmlyZWJhc2UuaW5pdGlhbGl6ZUFwcGAuCmNvbnN0IF9maXJlYmFzZUJhY2tlZE1vZHVsZXMgPSA8U3RyaW5nPlsKICAnbm90aWZpY2F0aW9ucycsCiAgJ2FuYWx5dGljcycsCiAgJ2NyYXNobHl0aWNzJywKICAnZmVhdHVyZV9mbGFncycsCiAgJ2F1dGgnLAogICdmaXJlc3RvcmUnLApdOwoKYm9vbCBfZmxhZyhIb29rQ29udGV4dCBjb250ZXh0LCBTdHJpbmcga2V5KSA9PiBjb250ZXh0LnZhcnNba2V5XSA9PSB0cnVlOwoKLy8vIE5vcm1hbGlzZXMgdGhlIG1vZHVsZSBhbnN3ZXJzIGFuZCBkZXJpdmVzIGV2ZXJ5dGhpbmcgdGhlIHRlbXBsYXRlcyByZWFkLgp2b2lkIHJlc29sdmVNb2R1bGVWYXJzKEhvb2tDb250ZXh0IGNvbnRleHQpIHsKICAvLyBBIG1vZHVsZSBjYW4gYmUgaW1wbGllZCBieSBhbm90aGVyIChkZXYgdG9vbHMgcmVhZCBmZWF0dXJlIGZsYWdzKSwgc28KICAvLyByZXNvbHZlIGltcGxpY2F0aW9ucyBiZWZvcmUgZGVyaXZpbmcgYW55dGhpbmcgZnJvbSB0aGVtLiBgbm9uc3RvcCBjcmVhdGVgCiAgLy8gYWxyZWFkeSBkb2VzIHRoaXMgaW4gbGliL2NvbW1hbmRzL2NyZWF0ZS9tb2R1bGVzLmRhcnQ7IGl0IGlzIHJlcGVhdGVkIGhlcmUKICAvLyBzbyBgbWFzb24gbWFrZWAgb24gaXRzIG93biBzdGlsbCBwcm9kdWNlcyBhIHByb2plY3QgdGhhdCBjb21waWxlcy4KICBpZiAoX2ZsYWcoY29udGV4dCwgJ2RldmVsb3BlcicpKSBjb250ZXh0LnZhcnNbJ2ZlYXR1cmVfZmxhZ3MnXSA9IHRydWU7CiAgaWYgKF9mbGFnKGNvbnRleHQsICdub3RpZmljYXRpb25zJykpIGNvbnRleHQudmFyc1snbmV0d29yayddID0gdHJ1ZTsKCiAgZm9yIChmaW5hbCBtb2R1bGUgaW4gb3B0aW9uYWxNb2R1bGVzKSB7CiAgICBmaW5hbCBlbmFibGVkID0gX2ZsYWcoY29udGV4dCwgbW9kdWxlKTsKICAgIGNvbnRleHQudmFyc1ttb2R1bGVdID0gZW5hYmxlZDsKICAgIGNvbnRleHQudmFyc1snJHttb2R1bGV9X2RpciddID0gZW5hYmxlZCA/IG1vZHVsZSA6ICcnOwogIH0KCiAgY29udGV4dC52YXJzWydmaXJlc3RvcmUnXSA9IF9mbGFnKGNvbnRleHQsICdmaXJlc3RvcmUnKTsKICBmaW5hbCB1c2VzRmlyZWJhc2UgPQogICAgICBfZmlyZWJhc2VCYWNrZWRNb2R1bGVzLmFueSgobW9kdWxlKSA9PiBfZmxhZyhjb250ZXh0LCBtb2R1bGUpKTsKICBjb250ZXh0LnZhcnNbJ2ZpcmViYXNlJ10gPSB1c2VzRmlyZWJhc2U7CgogIC8vIFRoZSBib290IHRlc3QgcnVucyB0aGUgcmVhbCBib290c3RyYXAsIHdoaWNoIGEgRmlyZWJhc2UgcHJvamVjdCBjYW5ub3QgZG8KICAvLyB1bnRpbCBgZmx1dHRlcmZpcmUgY29uZmlndXJlYCBoYXMgYmVlbiBydW4uIEFuIGVtcHR5IGZpbGUgbmFtZSBtYWtlcyBtYXNvbgogIC8vIHNraXAgdGhlIGZpbGUsIHNvIHRob3NlIHByb2plY3RzIHNpbXBseSBzaGlwIHdpdGhvdXQgaXQuCiAgY29udGV4dC52YXJzWydzbW9rZV90ZXN0X2ZpbGUnXSA9IHVzZXNGaXJlYmFzZSA/ICcnIDogJ3Ntb2tlX3Rlc3QuZGFydCc7Cn0K", + "type": "text" + }, + { + "path": "commands/next_steps_command.dart", + "data": + "aW1wb3J0ICdwYWNrYWdlOmNsaV9jb3JlL2NsaV9jb3JlLmRhcnQnIHNob3cgQ2xpQ29tbWFuZDsKaW1wb3J0ICdwYWNrYWdlOm1hc29uL21hc29uLmRhcnQnOwoKLy8vIFByaW50cyB0aGUgaGFuZGZ1bCBvZiB0aGluZ3MgdGhlIGdlbmVyYXRvciBjYW5ub3QgZG8gZm9yIHlvdS4KZmluYWwgY2xhc3MgTmV4dFN0ZXBzQ29tbWFuZCBleHRlbmRzIENsaUNvbW1hbmQgewogIEBvdmVycmlkZQogIEZ1dHVyZTx2b2lkPiBydW4oSG9va0NvbnRleHQgY29udGV4dCkgYXN5bmMgewogICAgZmluYWwgU3RyaW5nIG5hbWUgPSBjb250ZXh0LnZhcnNbJ25hbWUnXTsKICAgIGZpbmFsIGFwcE5hbWUgPSBuYW1lLnNuYWtlQ2FzZTsKICAgIGZpbmFsIGxvZ2dlciA9IGNvbnRleHQubG9nZ2VyOwoKICAgIGlmIChjb250ZXh0LnZhcnNbJ2ZpcmViYXNlJ10gPT0gdHJ1ZSkgewogICAgICBsb2dnZXIKICAgICAgICAuLmluZm8oJycpCiAgICAgICAgLi53YXJuKCdGaXJlYmFzZSBtb2R1bGVzIGFyZSBlbmFibGVkIGJ1dCBub3QgY29uZmlndXJlZCB5ZXQuJykKICAgICAgICAuLmluZm8oJyAgZGFydCBwdWIgZ2xvYmFsIGFjdGl2YXRlIGZsdXR0ZXJmaXJlX2NsaScpCiAgICAgICAgLi5pbmZvKCcgIGNkICRhcHBOYW1lL2FwcHMvJGFwcE5hbWUgJiYgZmx1dHRlcmZpcmUgY29uZmlndXJlJykKICAgICAgICAuLmluZm8oCiAgICAgICAgICAnICAodW50aWwgdGhlbiBsaWIvZmlyZWJhc2Vfb3B0aW9ucy5kYXJ0IHRocm93cyBvbiBwdXJwb3NlKScsCiAgICAgICAgKTsKICAgIH0KCiAgICBsb2dnZXIKICAgICAgLi5pbmZvKCcnKQogICAgICAuLmluZm8oJ1J1biB0aGUgYXBwOicpCiAgICAgIC4uaW5mbygnICBjZCAkYXBwTmFtZS9hcHBzLyRhcHBOYW1lICYmIGZsdXR0ZXIgcnVuJyk7CiAgfQp9Cg==", "type": "text" }, { "path": "post_gen.dart", "data": - "aW1wb3J0ICdkYXJ0OmFzeW5jJzsKCmltcG9ydCAncGFja2FnZTptYXNvbi9tYXNvbi5kYXJ0JzsKCmltcG9ydCAnY29tbWFuZHMvZmx1dHRlcl9jcmVhdGVfY29tbWFuZC5kYXJ0JzsKaW1wb3J0ICdjb21tYW5kcy9tZWxvc19jb21tYW5kLmRhcnQnOwoKRnV0dXJlPHZvaWQ+IHJ1bihIb29rQ29udGV4dCBjb250ZXh0KSBhc3luYyB7CiAgZmluYWwgY29tbWFuZHMgPSBbCiAgICBGbHV0dGVyQ3JlYXRlQ29tbWFuZCgpLAogICAgTWVsb3NDb21tYW5kKCksCiAgXTsKCiAgZm9yIChmaW5hbCBjb21tYW5kIGluIGNvbW1hbmRzKSB7CiAgICBhd2FpdCBjb21tYW5kLnJ1bihjb250ZXh0KTsKICB9Cn0K", + "aW1wb3J0ICdkYXJ0OmFzeW5jJzsKCmltcG9ydCAncGFja2FnZTptYXNvbi9tYXNvbi5kYXJ0JzsKCmltcG9ydCAnY29tbWFuZHMvZm9ybWF0X2NvbW1hbmQuZGFydCc7CmltcG9ydCAnY29tbWFuZHMvbWVsb3NfY29tbWFuZC5kYXJ0JzsKaW1wb3J0ICdjb21tYW5kcy9uZXh0X3N0ZXBzX2NvbW1hbmQuZGFydCc7CgpGdXR1cmU8dm9pZD4gcnVuKEhvb2tDb250ZXh0IGNvbnRleHQpIGFzeW5jIHsKICBmaW5hbCBjb21tYW5kcyA9IFsKICAgIEZvcm1hdENvbW1hbmQoKSwKICAgIE1lbG9zQ29tbWFuZCgpLAogICAgTmV4dFN0ZXBzQ29tbWFuZCgpLAogIF07CgogIGZvciAoZmluYWwgY29tbWFuZCBpbiBjb21tYW5kcykgewogICAgYXdhaXQgY29tbWFuZC5ydW4oY29udGV4dCk7CiAgfQp9Cg==", "type": "text" }, { "path": "pre_gen.dart", "data": - "aW1wb3J0ICdkYXJ0OmFzeW5jJzsKCmltcG9ydCAncGFja2FnZTpjbGlfY29yZS9jbGlfY29yZS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOm1hc29uL21hc29uLmRhcnQnOwoKRnV0dXJlPHZvaWQ+IHJ1bihIb29rQ29udGV4dCBjb250ZXh0KSBhc3luYyB7CiAgLy8gQ2hlY2sgaWYgd2UncmUgaW4gYSBtb25vIHJlcG8gdXNpbmcgdGhlIHNoYXJlZCB1dGlsaXR5CiAgY29udGV4dC52YXJzWydpc19tb25vX3JlcG8nXSA9IGF3YWl0IEZpbGVVdGlscy5pc01vbm9SZXBvKCk7Cn0K", + "aW1wb3J0ICdkYXJ0OmFzeW5jJzsKCmltcG9ydCAncGFja2FnZTpjbGlfY29yZS9jbGlfY29yZS5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOm1hc29uL21hc29uLmRhcnQnOwoKaW1wb3J0ICdjb21tYW5kcy9mbHV0dGVyX2NyZWF0ZV9jb21tYW5kLmRhcnQnOwppbXBvcnQgJ2NvbW1hbmRzL21vZHVsZV92YXJzLmRhcnQnOwoKRnV0dXJlPHZvaWQ+IHJ1bihIb29rQ29udGV4dCBjb250ZXh0KSBhc3luYyB7CiAgY29udGV4dC52YXJzWydpc19tb25vX3JlcG8nXSA9IGF3YWl0IEZpbGVVdGlscy5pc01vbm9SZXBvKCk7CgogIC8vIFR1cm4gdGhlIG1vZHVsZSBhbnN3ZXJzIGludG8gdGhlIGAqX2RpcmAgcGF0aCB2YXJpYWJsZXMgdGhlIGJyaWNrIHVzZXMgdG8KICAvLyBpbmNsdWRlIG9yIHNraXAgd2hvbGUgZGlyZWN0b3JpZXMsIGFuZCBkZXJpdmUgYGZpcmViYXNlYC4KICByZXNvbHZlTW9kdWxlVmFycyhjb250ZXh0KTsKCiAgLy8gVGhlIGFwcCBpcyBzY2FmZm9sZGVkIGJlZm9yZSBnZW5lcmF0aW9uIHNvIHRoZSBicmljaydzIGBsaWIvYCBhbmQKICAvLyBgcHVic3BlYy55YW1sYCBsYW5kIG9uIHRvcCBvZiB3aGF0IGBmbHV0dGVyIGNyZWF0ZWAgcHJvZHVjZWQuCiAgYXdhaXQgRmx1dHRlckNyZWF0ZUNvbW1hbmQoKS5ydW4oY29udGV4dCk7Cn0K", "type": "text" }, { @@ -90,11 +1461,12 @@ final flutterProjectWithMonoRepoBundle = MasonBundle.fromJson({ } ], "name": "flutter_project_with_mono_repo", - "description": "A Flutter project within a Melos-managed mono-repo.", - "version": "1.0.0", + "description": + "A production-shaped Flutter mono-repo - app shell, design system, localization and the platform modules you pick.", + "version": "2.0.0", "environment": {"mason": ">=0.1.0-dev.52 <0.1.0"}, "repository": - "https://github.com/nonstopio/flutter_forge/tree/main/plugins/nonstop_cli", + "https://github.com/nonstopio/flutter_forge/tree/main/packages/nonstop_cli", "readme": { "path": "README.md", "data": "IyBmbHV0dGVyX3Byb2plY3Rfd2l0aF9tb25vX3JlcG8=", @@ -128,6 +1500,62 @@ final flutterProjectWithMonoRepoBundle = MasonBundle.fromJson({ "description": "Organization name", "default": "com.example", "prompt": "What is your organization name?" + }, + "network": { + "type": "boolean", + "description": "Dio-based HTTP client with auth and logging interceptors", + "default": true, + "prompt": "Include the network package?" + }, + "auth": { + "type": "boolean", + "description": + "Firebase Authentication with email, Google and Apple sign-in", + "default": true, + "prompt": "Include authentication?" + }, + "firestore": { + "type": "boolean", + "description": "Cloud Firestore and Cloud Functions wiring", + "default": false, + "prompt": "Include Cloud Firestore?" + }, + "notifications": { + "type": "boolean", + "description": + "Firebase Cloud Messaging, permissions and token registration", + "default": true, + "prompt": "Include push notifications?" + }, + "analytics": { + "type": "boolean", + "description": "Event tracking behind a swappable client", + "default": true, + "prompt": "Include analytics?" + }, + "crashlytics": { + "type": "boolean", + "description": "Crash and non-fatal error reporting", + "default": true, + "prompt": "Include crash reporting?" + }, + "feature_flags": { + "type": "boolean", + "description": "Firebase Remote Config feature flags", + "default": true, + "prompt": "Include feature flags?" + }, + "developer": { + "type": "boolean", + "description": "In-app developer tools behind a hidden gesture", + "default": true, + "prompt": "Include developer tools?" + }, + "dashboard": { + "type": "boolean", + "description": "Bottom-navigation shell with starter tabs", + "default": true, + "prompt": "Include the dashboard shell?" } } }); From 5eafd6badc061f608aec335f8d880c507e20e6ab Mon Sep 17 00:00:00 2001 From: Ajay Kumar Date: Sat, 5 Sep 2026 15:47:29 -0500 Subject: [PATCH 5/7] refactor(nonstop_cli): strip source-project domain from the template The brick was ported from a health app, and the rename pass only caught the product name. Its domain had also settled into shared code, where it would have shipped to every project generated from the template. Removed: survey and health event groups from analytics, along with the two survey helpers on AnalyticsHelper; a survey-assignment event on core's global event channel, replaced with a single generic example; and the source project's own examples throughout the analytics README. The notification client hardcoded one deep link - a `surveyAssignments` message type routed to `/home/survey/assignment/$id`, a path this template does not even define. It now reads a `route` from the message data and pushes that, which is the contract the docs can actually describe. The auth header is drawn from the theme instead of fetching a placeholder image, so a fresh project has no artwork to ship and nothing to fetch. Also corrects the CI package filter, which excluded a workspace named after the source project and so never matched anything here. Verified by decoding all 222 files embedded in the generated bundle: no product-specific term survives in what the CLI actually ships. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/dart.yml | 2 +- .../lib/ui/components/header_builder.dart | 41 ++++++++++--- .../packages/core/lib/bloc/global_event.dart | 14 +++-- .../localization/lib/messages.i69n.yaml | 3 - .../packages/{{analytics_dir}}/README.md | 36 +++--------- .../lib/src/models/analytics_events.dart | 58 ++----------------- .../lib/src/utils/analytics_helper.dart | 30 ---------- .../client/firebase_notification_client.dart | 17 +++--- 8 files changed, 61 insertions(+), 140 deletions(-) diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index fa41eac..04e8755 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -38,7 +38,7 @@ jobs: # Get packages that have changes using melos diff - packages=$(melos list --diff="$BASE_REF" --json | jq -c "[.[] | select(.name != \"biomedict_workspace\") | .name]" 2>/dev/null || echo '[]') + packages=$(melos list --diff="$BASE_REF" --json | jq -c "[.[] | select(.name != \"flutter_forge_workspace\") | .name]" 2>/dev/null || echo '[]') if [ "$packages" = "" ] || [ "$packages" = "null" ]; then packages="[]" fi diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/components/header_builder.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/components/header_builder.dart index 08b94e5..80eed4c 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/components/header_builder.dart +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/components/header_builder.dart @@ -1,15 +1,40 @@ -import 'package:design_system/design_system.dart' as ds; -import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart'; +import 'package:localization/localization.dart'; /// Banner shown above the sign-in and register forms. /// -/// Swap this for your own artwork: add it under the app's `assets:` in -/// pubspec.yaml and switch to `ds.HeaderType.asset` with an `assetPath`. +/// Deliberately drawn from the theme rather than an image, so a fresh project +/// has no asset to ship and nothing to fetch. Swap it for your own artwork: +/// add the file under the app's `assets:` and use `design_system`'s `Header` +/// with `HeaderType.asset`. Widget headerBuilder(BuildContext context) { - return const Padding( - padding: EdgeInsets.all(8), - child: ds.Header( - imageUrl: 'https://picsum.photos/1200/300', + final theme = Theme.of(context); + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 24), + child: Column( + children: [ + Container( + height: 72, + width: 72, + decoration: BoxDecoration( + color: theme.colorScheme.primaryContainer, + borderRadius: BorderRadius.circular(20), + ), + child: Icon( + Icons.bolt_outlined, + size: 40, + color: theme.colorScheme.onPrimaryContainer, + ), + ), + const SizedBox(height: 16), + Text( + strings.app.name, + style: theme.textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + ], ), ); } diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event.dart index 0e27e43..b65f666 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event.dart +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/core/lib/bloc/global_event.dart @@ -1,5 +1,10 @@ import 'package:equatable/equatable.dart'; +/// Base type for events broadcast on the app-wide event channel. +/// +/// Use this for the handful of things that genuinely cross feature +/// boundaries - "the signed-in user changed", "this list is stale" - and keep +/// everything else inside the feature that owns it. abstract class GlobalEventType extends Equatable { const GlobalEventType(); @@ -7,12 +12,9 @@ abstract class GlobalEventType extends Equatable { List get props => []; } -class RefreshSurveyAssignment extends GlobalEventType { - final String? id; - - const RefreshSurveyAssignment(this.id); -} - +/// Example event: something invalidated the current user's profile. +/// +/// Replace this with your own events; it is here to show the shape. class RefreshProfile extends GlobalEventType { const RefreshProfile(); } diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/messages.i69n.yaml b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/messages.i69n.yaml index 15beb7f..e807aa8 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/messages.i69n.yaml +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/localization/lib/messages.i69n.yaml @@ -146,6 +146,3 @@ validation: password_too_short: "Password must be at least 8 characters" passwords_dont_match: "Passwords do not match" invalid_key_config(String of, String key): "Invalid configuration for $key in $of. Please check your settings." - -# BioKit - diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/README.md b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/README.md index 3591060..5a7fc51 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/README.md +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/README.md @@ -57,14 +57,14 @@ await AnalyticsHelper.logEvent( // Feature usage await AnalyticsHelper.logEvent( AnalyticsEvents.feature.used, - parameters: {'feature_name': 'health_sync'}, + parameters: {'feature_name': 'export'}, ); -// Survey events +// A group of your own, added to AnalyticsEvents await AnalyticsHelper.logEvent( - AnalyticsEvents.survey.completed, + AnalyticsEvents.feature.tutorialCompleted, parameters: { - 'survey_id': 'wellness_assessment', + 'tutorial_id': 'getting_started', 'duration_seconds': 120, }, ); @@ -74,17 +74,13 @@ await AnalyticsHelper.logEvent( ```dart // Feature tracking -await AnalyticsHelper.logFeatureUsed('health_dashboard'); - -// Survey tracking -await AnalyticsHelper.logSurveyStarted('mood_tracker'); -await AnalyticsHelper.logSurveyCompleted('mood_tracker', duration: 45); +await AnalyticsHelper.logFeatureUsed('export'); // Error tracking await AnalyticsHelper.logAppError('network_timeout'); // Button interactions -await AnalyticsHelper.logButtonPressed('start_assessment'); +await AnalyticsHelper.logButtonPressed('get_started'); // Authentication events await AnalyticsHelper.logSignIn(method: 'google'); @@ -142,16 +138,6 @@ AnalyticsEvents.app.open AnalyticsEvents.app.background AnalyticsEvents.app.foreground -// Survey events -AnalyticsEvents.survey.started -AnalyticsEvents.survey.completed -AnalyticsEvents.survey.abandoned - -// Health data events -AnalyticsEvents.health.dataSynced -AnalyticsEvents.health.deviceConnected -AnalyticsEvents.health.goalAchieved - // Error events AnalyticsEvents.error.appError AnalyticsEvents.error.networkError @@ -237,13 +223,7 @@ await AnalyticsHelper.logEvent( ); // User uses a feature -await AnalyticsHelper.logFeatureUsed('health_sync'); - -// User completes a survey -await AnalyticsHelper.logSurveyCompleted( - 'wellness_assessment', - duration: 180, -); +await AnalyticsHelper.logFeatureUsed('export'); ``` ### Error Tracking @@ -256,7 +236,7 @@ try { 'api_call_failed', errorMessage: error.toString(), parameters: { - 'endpoint': '/api/health/sync', + 'endpoint': '/api/items', 'user_id': userId, }, ); diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/analytics_events.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/analytics_events.dart index 231b32e..ea381eb 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/analytics_events.dart +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/analytics_events.dart @@ -1,7 +1,8 @@ -/// Structured analytics event names organized by category +/// Structured analytics event names organized by category. /// -/// Usage: AnalyticsEvents.user.authenticatedRedirect -/// This provides type-safe, discoverable event names and prevents typos +/// Usage: `AnalyticsEvents.user.authenticatedRedirect`. Naming events here +/// instead of inline keeps them discoverable and typo-free, and gives you one +/// place to add the groups your own product needs. class AnalyticsEvents { AnalyticsEvents._(); @@ -23,12 +24,6 @@ class AnalyticsEvents { /// App lifecycle events static const app = _AppEvents(); - /// Survey and assessment events - static const survey = _SurveyEvents(); - - /// Health data and biokit events - static const health = _HealthEvents(); - /// Profile-related events static const profile = _ProfileEvents(); } @@ -150,52 +145,7 @@ class _AppEvents { String get uninstalled => 'app_uninstalled'; } -class _SurveyEvents { - const _SurveyEvents(); - - /// Survey started - String get started => 'survey_started'; - - /// Survey completed - String get completed => 'survey_completed'; - - /// Survey abandoned/cancelled - String get abandoned => 'survey_abandoned'; - - /// Survey question answered - String get questionAnswered => 'survey_question_answered'; - - /// Survey progress saved - String get progressSaved => 'survey_progress_saved'; - - /// Survey results viewed - String get resultsViewed => 'survey_results_viewed'; -} -class _HealthEvents { - const _HealthEvents(); - - /// Health data synced - String get dataSynced => 'health_data_synced'; - - /// Wearable device connected - String get deviceConnected => 'health_device_connected'; - - /// Wearable device disconnected - String get deviceDisconnected => 'health_device_disconnected'; - - /// Health metric recorded - String get metricRecorded => 'health_metric_recorded'; - - /// Health goal set - String get goalSet => 'health_goal_set'; - - /// Health goal achieved - String get goalAchieved => 'health_goal_achieved'; - - /// Health report generated - String get reportGenerated => 'health_report_generated'; -} class _ProfileEvents { const _ProfileEvents(); diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/utils/analytics_helper.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/utils/analytics_helper.dart index bd9ab4e..90509ed 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/utils/analytics_helper.dart +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/utils/analytics_helper.dart @@ -170,34 +170,4 @@ class AnalyticsHelper { ); } - /// Additional structured event helpers - static Future logSurveyStarted( - String surveyId, { - Map? parameters, - }) async { - await logEvent( - AnalyticsEvents.survey.started, - parameters: { - 'survey_id': surveyId, - 'timestamp': DateTime.now().toIso8601String(), - ...?parameters, - }, - ); - } - - static Future logSurveyCompleted( - String surveyId, { - int? duration, - Map? parameters, - }) async { - await logEvent( - AnalyticsEvents.survey.completed, - parameters: { - 'survey_id': surveyId, - if (duration != null) 'duration_seconds': duration, - 'timestamp': DateTime.now().toIso8601String(), - ...?parameters, - }, - ); - } } diff --git a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/firebase_notification_client.dart b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/firebase_notification_client.dart index babe212..cbe1cb7 100644 --- a/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/firebase_notification_client.dart +++ b/packages/nonstop_cli/bricks/flutter_project_with_mono_repo/__brick__/{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/firebase_notification_client.dart @@ -219,16 +219,13 @@ class FirebaseNotificationClient implements NotificationClient { '$_tag: Notification opened from $source: ${notification?.title} - ${notification?.body}', ); - final data = message.data; - final actionType = data['type']; - if (actionType == null) return; - - if (actionType == 'surveyAssignments') { - final id = data['id']; - if (id == null) return; - final router = di.get(); - router.push('/home/survey/assignment/$id'); - } + // Deep link contract: send a `route` in the message data and the app + // navigates there, e.g. {"route": "/home/orders/42"}. Branch on your own + // payload fields here if you need something richer than a path. + final route = message.data['route']; + if (route is! String || route.isEmpty) return; + + di.get().push(route); } catch (e, s) { _logger.e('$_tag: Error handling notification opened', e, s); } From 625dffbb747b88d1579a345ca49e6455bdd40f8a Mon Sep 17 00:00:00 2001 From: Ajay Kumar Date: Sat, 5 Sep 2026 15:47:29 -0500 Subject: [PATCH 6/7] chore(nonstop_cli): update flutter_project_with_mono_repo bundle Co-Authored-By: Claude Opus 5 (1M context) --- .../flutter_project_with_mono_repo_bundle.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/nonstop_cli/lib/commands/create/flutter_project_with_mono_repo_bundle.dart b/packages/nonstop_cli/lib/commands/create/flutter_project_with_mono_repo_bundle.dart index 23d7c76..1ab07a8 100644 --- a/packages/nonstop_cli/lib/commands/create/flutter_project_with_mono_repo_bundle.dart +++ b/packages/nonstop_cli/lib/commands/create/flutter_project_with_mono_repo_bundle.dart @@ -183,7 +183,7 @@ final flutterProjectWithMonoRepoBundle = MasonBundle.fromJson({ "path": "{{ name.snakeCase() }}/features/{{auth_dir}}/lib/ui/components/header_builder.dart", "data": - "aW1wb3J0ICdwYWNrYWdlOmRlc2lnbl9zeXN0ZW0vZGVzaWduX3N5c3RlbS5kYXJ0JyBhcyBkczsKaW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvd2lkZ2V0cy5kYXJ0JzsKCi8vLyBCYW5uZXIgc2hvd24gYWJvdmUgdGhlIHNpZ24taW4gYW5kIHJlZ2lzdGVyIGZvcm1zLgovLy8KLy8vIFN3YXAgdGhpcyBmb3IgeW91ciBvd24gYXJ0d29yazogYWRkIGl0IHVuZGVyIHRoZSBhcHAncyBgYXNzZXRzOmAgaW4KLy8vIHB1YnNwZWMueWFtbCBhbmQgc3dpdGNoIHRvIGBkcy5IZWFkZXJUeXBlLmFzc2V0YCB3aXRoIGFuIGBhc3NldFBhdGhgLgpXaWRnZXQgaGVhZGVyQnVpbGRlcihCdWlsZENvbnRleHQgY29udGV4dCkgewogIHJldHVybiBjb25zdCBQYWRkaW5nKAogICAgcGFkZGluZzogRWRnZUluc2V0cy5hbGwoOCksCiAgICBjaGlsZDogZHMuSGVhZGVyKAogICAgICBpbWFnZVVybDogJ2h0dHBzOi8vcGljc3VtLnBob3Rvcy8xMjAwLzMwMCcsCiAgICApLAogICk7Cn0K", + "aW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvbWF0ZXJpYWwuZGFydCc7CmltcG9ydCAncGFja2FnZTpsb2NhbGl6YXRpb24vbG9jYWxpemF0aW9uLmRhcnQnOwoKLy8vIEJhbm5lciBzaG93biBhYm92ZSB0aGUgc2lnbi1pbiBhbmQgcmVnaXN0ZXIgZm9ybXMuCi8vLwovLy8gRGVsaWJlcmF0ZWx5IGRyYXduIGZyb20gdGhlIHRoZW1lIHJhdGhlciB0aGFuIGFuIGltYWdlLCBzbyBhIGZyZXNoIHByb2plY3QKLy8vIGhhcyBubyBhc3NldCB0byBzaGlwIGFuZCBub3RoaW5nIHRvIGZldGNoLiBTd2FwIGl0IGZvciB5b3VyIG93biBhcnR3b3JrOgovLy8gYWRkIHRoZSBmaWxlIHVuZGVyIHRoZSBhcHAncyBgYXNzZXRzOmAgYW5kIHVzZSBgZGVzaWduX3N5c3RlbWAncyBgSGVhZGVyYAovLy8gd2l0aCBgSGVhZGVyVHlwZS5hc3NldGAuCldpZGdldCBoZWFkZXJCdWlsZGVyKEJ1aWxkQ29udGV4dCBjb250ZXh0KSB7CiAgZmluYWwgdGhlbWUgPSBUaGVtZS5vZihjb250ZXh0KTsKCiAgcmV0dXJuIFBhZGRpbmcoCiAgICBwYWRkaW5nOiBjb25zdCBFZGdlSW5zZXRzLnN5bW1ldHJpYyh2ZXJ0aWNhbDogMjQpLAogICAgY2hpbGQ6IENvbHVtbigKICAgICAgY2hpbGRyZW46IFsKICAgICAgICBDb250YWluZXIoCiAgICAgICAgICBoZWlnaHQ6IDcyLAogICAgICAgICAgd2lkdGg6IDcyLAogICAgICAgICAgZGVjb3JhdGlvbjogQm94RGVjb3JhdGlvbigKICAgICAgICAgICAgY29sb3I6IHRoZW1lLmNvbG9yU2NoZW1lLnByaW1hcnlDb250YWluZXIsCiAgICAgICAgICAgIGJvcmRlclJhZGl1czogQm9yZGVyUmFkaXVzLmNpcmN1bGFyKDIwKSwKICAgICAgICAgICksCiAgICAgICAgICBjaGlsZDogSWNvbigKICAgICAgICAgICAgSWNvbnMuYm9sdF9vdXRsaW5lZCwKICAgICAgICAgICAgc2l6ZTogNDAsCiAgICAgICAgICAgIGNvbG9yOiB0aGVtZS5jb2xvclNjaGVtZS5vblByaW1hcnlDb250YWluZXIsCiAgICAgICAgICApLAogICAgICAgICksCiAgICAgICAgY29uc3QgU2l6ZWRCb3goaGVpZ2h0OiAxNiksCiAgICAgICAgVGV4dCgKICAgICAgICAgIHN0cmluZ3MuYXBwLm5hbWUsCiAgICAgICAgICBzdHlsZTogdGhlbWUudGV4dFRoZW1lLnRpdGxlTGFyZ2U/LmNvcHlXaXRoKAogICAgICAgICAgICBmb250V2VpZ2h0OiBGb250V2VpZ2h0LmJvbGQsCiAgICAgICAgICApLAogICAgICAgICksCiAgICAgIF0sCiAgICApLAogICk7Cn0K", "type": "text" }, { @@ -309,7 +309,7 @@ final flutterProjectWithMonoRepoBundle = MasonBundle.fromJson({ { "path": "{{ name.snakeCase() }}/packages/core/lib/bloc/global_event.dart", "data": - "aW1wb3J0ICdwYWNrYWdlOmVxdWF0YWJsZS9lcXVhdGFibGUuZGFydCc7CgphYnN0cmFjdCBjbGFzcyBHbG9iYWxFdmVudFR5cGUgZXh0ZW5kcyBFcXVhdGFibGUgewogIGNvbnN0IEdsb2JhbEV2ZW50VHlwZSgpOwoKICBAb3ZlcnJpZGUKICBMaXN0PE9iamVjdD8+IGdldCBwcm9wcyA9PiBbXTsKfQoKY2xhc3MgUmVmcmVzaFN1cnZleUFzc2lnbm1lbnQgZXh0ZW5kcyBHbG9iYWxFdmVudFR5cGUgewogIGZpbmFsIFN0cmluZz8gaWQ7CgogIGNvbnN0IFJlZnJlc2hTdXJ2ZXlBc3NpZ25tZW50KHRoaXMuaWQpOwp9CgpjbGFzcyBSZWZyZXNoUHJvZmlsZSBleHRlbmRzIEdsb2JhbEV2ZW50VHlwZSB7CiAgY29uc3QgUmVmcmVzaFByb2ZpbGUoKTsKfQoKY2xhc3MgRmlyZUdsb2JhbEV2ZW50IGV4dGVuZHMgRXF1YXRhYmxlIHsKICBmaW5hbCBHbG9iYWxFdmVudFR5cGUgZXZlbnRUeXBlOwoKICBjb25zdCBGaXJlR2xvYmFsRXZlbnQoe3JlcXVpcmVkIHRoaXMuZXZlbnRUeXBlfSk7CgogIEBvdmVycmlkZQogIExpc3Q8T2JqZWN0Pz4gZ2V0IHByb3BzID0+IFtldmVudFR5cGVdOwp9Cg==", + "aW1wb3J0ICdwYWNrYWdlOmVxdWF0YWJsZS9lcXVhdGFibGUuZGFydCc7CgovLy8gQmFzZSB0eXBlIGZvciBldmVudHMgYnJvYWRjYXN0IG9uIHRoZSBhcHAtd2lkZSBldmVudCBjaGFubmVsLgovLy8KLy8vIFVzZSB0aGlzIGZvciB0aGUgaGFuZGZ1bCBvZiB0aGluZ3MgdGhhdCBnZW51aW5lbHkgY3Jvc3MgZmVhdHVyZQovLy8gYm91bmRhcmllcyAtICJ0aGUgc2lnbmVkLWluIHVzZXIgY2hhbmdlZCIsICJ0aGlzIGxpc3QgaXMgc3RhbGUiIC0gYW5kIGtlZXAKLy8vIGV2ZXJ5dGhpbmcgZWxzZSBpbnNpZGUgdGhlIGZlYXR1cmUgdGhhdCBvd25zIGl0LgphYnN0cmFjdCBjbGFzcyBHbG9iYWxFdmVudFR5cGUgZXh0ZW5kcyBFcXVhdGFibGUgewogIGNvbnN0IEdsb2JhbEV2ZW50VHlwZSgpOwoKICBAb3ZlcnJpZGUKICBMaXN0PE9iamVjdD8+IGdldCBwcm9wcyA9PiBbXTsKfQoKLy8vIEV4YW1wbGUgZXZlbnQ6IHNvbWV0aGluZyBpbnZhbGlkYXRlZCB0aGUgY3VycmVudCB1c2VyJ3MgcHJvZmlsZS4KLy8vCi8vLyBSZXBsYWNlIHRoaXMgd2l0aCB5b3VyIG93biBldmVudHM7IGl0IGlzIGhlcmUgdG8gc2hvdyB0aGUgc2hhcGUuCmNsYXNzIFJlZnJlc2hQcm9maWxlIGV4dGVuZHMgR2xvYmFsRXZlbnRUeXBlIHsKICBjb25zdCBSZWZyZXNoUHJvZmlsZSgpOwp9CgpjbGFzcyBGaXJlR2xvYmFsRXZlbnQgZXh0ZW5kcyBFcXVhdGFibGUgewogIGZpbmFsIEdsb2JhbEV2ZW50VHlwZSBldmVudFR5cGU7CgogIGNvbnN0IEZpcmVHbG9iYWxFdmVudCh7cmVxdWlyZWQgdGhpcy5ldmVudFR5cGV9KTsKCiAgQG92ZXJyaWRlCiAgTGlzdDxPYmplY3Q/PiBnZXQgcHJvcHMgPT4gW2V2ZW50VHlwZV07Cn0K", "type": "text" }, { @@ -718,7 +718,7 @@ final flutterProjectWithMonoRepoBundle = MasonBundle.fromJson({ "path": "{{ name.snakeCase() }}/packages/localization/lib/messages.i69n.yaml", "data": - "IyB7e25hbWUudGl0bGVDYXNlKCl9fSAtIEludGVybmF0aW9uYWxpemF0aW9uIE1lc3NhZ2VzCiMgQmFzZSB0cmFuc2xhdGlvbnMgZm9yIHRoZSBhcHBsaWNhdGlvbgoKIyBhcHBsaWNhdGlvbiBtZXRhZGF0YQphcHA6CiAgbmFtZTogInt7bmFtZS50aXRsZUNhc2UoKX19IgogIGRlc2NyaXB0aW9uOiAie3t7ZGVzY3JpcHRpb259fX0iCiAgd2VsY29tZV90b19hcHA6ICJXZWxjb21lIHRvIHt7bmFtZS50aXRsZUNhc2UoKX19ISIKCiMgQ29tbW9uL0dlbmVyaWMgbWVzc2FnZXMKZ2VuZXJpYzoKICBvazogIk9LIgogIGNhbmNlbDogIkNhbmNlbCIKICBzYXZlOiAiU2F2ZSIKICBkZWxldGU6ICJEZWxldGUiCiAgZWRpdDogIkVkaXQiCiAgdXBkYXRlOiAiVXBkYXRlIgogIHN1Ym1pdDogIlN1Ym1pdCIKICBjbG9zZTogIkNsb3NlIgogIGJhY2s6ICJCYWNrIgogIG5leHQ6ICJOZXh0IgogIHByZXZpb3VzOiAiUHJldmlvdXMiCiAgZG9uZTogIkRvbmUiCiAgbG9hZGluZzogIkxvYWRpbmcuLi4iCiAgZXJyb3I6ICJFcnJvciIKICBzdWNjZXNzOiAiU3VjY2VzcyIKICB3YXJuaW5nOiAiV2FybmluZyIKICBpbmZvOiAiSW5mbyIKICByZXRyeTogIlJldHJ5IgogIHJlZnJlc2g6ICJSZWZyZXNoIgogIHllczogIlllcyIKICBubzogIk5vIgogIGFkZDogIisgQWRkIgogIHRyeV9hZ2FpbjogIlRyeSBBZ2FpbiIKCiMgQ29tbW9uIG1lc3NhZ2VzICh1c2VkIGZvciBjb25zaXN0ZW5jeSBhY3Jvc3MgbW9kdWxlcykKY29tbW9uOgogIGNhbmNlbDogIkNhbmNlbCIKICBvazogIk9LIgogIHNhdmU6ICJTYXZlIgogIGRlbGV0ZTogIkRlbGV0ZSIKICBlZGl0OiAiRWRpdCIKICB0cnlfYWdhaW46ICJUcnkgQWdhaW4iCiAgd2VlazogIldlZWsiCiAgbW9udGg6ICJNb250aCIKCiMgQXV0aGVudGljYXRpb24KYXV0aDoKICByZWdpc3RlcjogIlJlZ2lzdGVyIgogIHNpZ25faW46ICJTaWduIEluIgogIHNpZ25fb3V0OiAiU2lnbiBPdXQiCiAgZG9udF9oYXZlX2FjY291bnQ6ICJEb24ndCBoYXZlIGFuIGFjY291bnQ/ICIKICBhbHJlYWR5X2hhdmVfYWNjb3VudDogIkFscmVhZHkgaGF2ZSBhbiBhY2NvdW50PyAiCiAgc2lnbl9vdXRfY29uZmlybWF0aW9uOiAiQXJlIHlvdSBzdXJlIHlvdSB3YW50IHRvIHNpZ24gb3V0PyIKCiMgUHJvZmlsZQojIFByb2ZpbGUKcHJvZmlsZToKICBwcm9maWxlOiAiUHJvZmlsZSIKICBzZXR0aW5nczogIlNldHRpbmdzIgogIGFjY291bnQ6ICJBY2NvdW50IgogIHBlcnNvbmFsX2luZm86ICJQZXJzb25hbCBJbmZvcm1hdGlvbiIKICBwcml2YWN5X3NldHRpbmdzOiAiUHJpdmFjeSBTZXR0aW5ncyIKICBuYW1lOiAiTmFtZSIKICBlbWFpbF9hZGRyZXNzOiAiRW1haWwgQWRkcmVzcyIKICBwaG9uZV9udW1iZXI6ICJQaG9uZSBOdW1iZXIiCiAgZGF0ZV9vZl9iaXJ0aDogIkRhdGUgb2YgQmlydGgiCiAgZGVsZXRlX2NvbmZpcm1hdGlvbjogIkRlbGV0ZSBDb25maXJtYXRpb24iCiAgZGVsZXRlX2NvbmZpcm1hdGlvbl9tZXNzYWdlOiAiQXJlIHlvdSBzdXJlIHlvdSB3YW50IHRvIGRlbGV0ZSB0aGlzIGl0ZW0/IFRoaXMgYWN0aW9uIGNhbm5vdCBiZSB1bmRvbmUuIgoKbmF2OgogIGhvbWU6ICJIb21lIgogIGRhc2hib2FyZDogIkRhc2hib2FyZCIKICBwcm9maWxlOiAiUHJvZmlsZSIKICBzZXR0aW5nczogIlNldHRpbmdzIgogIG5vdGlmaWNhdGlvbnM6ICJOb3RpZmljYXRpb25zIgoKIyBOb3RpZmljYXRpb25zCm5vdGlmaWNhdGlvbnM6CiAgdGl0bGU6ICJOb3RpZmljYXRpb25zIgogIG1hcmtfYXNfcmVhZDogIk1hcmsgYXMgcmVhZCIKICBtYXJrX2FsbF9yZWFkOiAiTWFyayBhbGwgYXMgcmVhZCIKICBkZWxldGU6ICJEZWxldGUiCiAgCiAgIyBGaWx0ZXIgdGFicwogIGZpbHRlcl9hbGw6ICJBbGwiCiAgZmlsdGVyX3VucmVhZDogIlVucmVhZCIKICBmaWx0ZXJfcmVhZDogIlJlYWQiCiAgCiAgIyBOb3RpZmljYXRpb24gdHlwZXMKICB0eXBlX3JlbWluZGVyOiAiUmVtaW5kZXIiCiAgdHlwZV9hbGVydDogIkFsZXJ0IgogIHR5cGVfcHJvbW90aW9uOiAiUHJvbW90aW9uIgogIHR5cGVfc3lzdGVtOiAiU3lzdGVtIgogIHR5cGVfY3VzdG9tOiAiQ3VzdG9tIgogIAogICMgRW1wdHkgc3RhdGVzCiAgZW1wdHlfdGl0bGU6ICJObyBub3RpZmljYXRpb25zIgogIGVtcHR5X2Rlc2NyaXB0aW9uOiAiWW91J3JlIGFsbCBjYXVnaHQgdXAhIE5ldyBub3RpZmljYXRpb25zIHdpbGwgYXBwZWFyIGhlcmUuIgogIAogICMgRGVsZXRlIGNvbmZpcm1hdGlvbgogIGRlbGV0ZV9jb25maXJtYXRpb25fdGl0bGU6ICJEZWxldGUgbm90aWZpY2F0aW9uPyIKICBkZWxldGVfY29uZmlybWF0aW9uX21lc3NhZ2U6ICJUaGlzIG5vdGlmaWNhdGlvbiB3aWxsIGJlIHBlcm1hbmVudGx5IHJlbW92ZWQgZnJvbSB5b3VyIGxpc3QuIgoKIyBFcnJvcnMKZXJyb3JzOgogIG5ldHdvcmtfZXJyb3I6ICJOZXR3b3JrIGVycm9yLiBQbGVhc2UgY2hlY2sgeW91ciBjb25uZWN0aW9uLiIKICB1bmtub3duX2Vycm9yOiAiQW4gdW5rbm93biBlcnJvciBvY2N1cnJlZC4iCiAgdmFsaWRhdGlvbl9lcnJvcjogIlBsZWFzZSBjaGVjayB5b3VyIGlucHV0IGFuZCB0cnkgYWdhaW4uIgogIHNlcnZlcl9lcnJvcjogIlNlcnZlciBlcnJvci4gUGxlYXNlIHRyeSBhZ2FpbiBsYXRlci4iCiAgZGVmYXVsdF9lcnJvcl9tZXNzYWdlOiAiT29wcyEgU29tZXRoaW5nIHdlbnQgd3JvbmcuIFBsZWFzZSB0cnkgYWdhaW4uIgogIHVzZXJfbm90X2ZvdW5kOiAiVXNlciBub3QgZm91bmQuIFBsZWFzZSBjaGVjayB5b3VyIGNyZWRlbnRpYWxzLiIKICBkZWZhdWx0X2Vycm9yX2Rlc2NyaXB0aW9uOiAiV2UgZW5jb3VudGVyZWQgYW4gZXJyb3Igd2hpbGUgcHJvY2Vzc2luZyB5b3VyIHJlcXVlc3QuIFdlIGFwb2xvZ2l6ZSBmb3IgdGhlIGluY29udmVuaWVuY2UuIFBsZWFzZSB0cnkgYWdhaW4gbGF0ZXIgb3IgY29udGFjdCBzdXBwb3J0IGlmIHRoZSBpc3N1ZSBwZXJzaXN0cy4iCiAgcGFnZV9ub3RfZm91bmQ6ICJQYWdlIE5vdCBGb3VuZCIKICBwYWdlX25vdF9mb3VuZF9kZXNjcmlwdGlvbjogIlRoZSBwYWdlIHlvdSBhcmUgbG9va2luZyBmb3IgZG9lcyBub3QgZXhpc3QuIgogIHVuZXhwZWN0ZWRfZXJyb3I6ICJBbiB1bmV4cGVjdGVkIGVycm9yIG9jY3VycmVkLiIKICByZWRpcmVjdF9lcnJvcjogIlJlZGlyZWN0IEVycm9yIgogIGJhZF9yZXF1ZXN0OiAiSW52YWxpZCByZXF1ZXN0LiBQbGVhc2UgY2hlY2sgeW91ciBpbnB1dC4iCiAgdW5hdXRob3JpemVkOiAiQXV0aGVudGljYXRpb24gcmVxdWlyZWQuIFBsZWFzZSBzaWduIGluIGFnYWluLiIKICBmb3JiaWRkZW46ICJBY2Nlc3MgZGVuaWVkLiBZb3UgZG9uJ3QgaGF2ZSBwZXJtaXNzaW9uLiIKICBub3RfZm91bmQ6ICJSZXF1ZXN0ZWQgcmVzb3VyY2Ugbm90IGZvdW5kLiIKICBjb25mbGljdDogIkRhdGEgY29uZmxpY3QuIFBsZWFzZSByZWZyZXNoIGFuZCB0cnkgYWdhaW4uIgogIHVucHJvY2Vzc2FibGVfZW50aXR5OiAiSW52YWxpZCBkYXRhIGZvcm1hdC4gUGxlYXNlIGNoZWNrIHlvdXIgaW5wdXQuIgogIGludGVybmFsX3NlcnZlcl9lcnJvcjogIlNlcnZlciBlcnJvci4gUGxlYXNlIHRyeSBhZ2FpbiBsYXRlci4iCiAgY29ubmVjdGlvbl90aW1lb3V0OiAiQ29ubmVjdGlvbiB0aW1lb3V0LiBQbGVhc2UgY2hlY2sgeW91ciBpbnRlcm5ldC4iCiAgcmVjZWl2ZV90aW1lb3V0OiAiUmVxdWVzdCB0aW1lb3V0LiBQbGVhc2UgdHJ5IGFnYWluLiIKICBzZW5kX3RpbWVvdXQ6ICJVcGxvYWQgdGltZW91dC4gUGxlYXNlIHRyeSBhZ2Fpbi4iCiAgbm9faW50ZXJuZXQ6ICJObyBpbnRlcm5ldCBjb25uZWN0aW9uLiBQbGVhc2UgY2hlY2sgeW91ciBuZXR3b3JrLiIKICB1bmtub3duX25ldHdvcms6ICJOZXR3b3JrIGVycm9yIG9jY3VycmVkLiBQbGVhc2UgdHJ5IGFnYWluLiIKICBmb3JtYXRfZXhjZXB0aW9uX21lc3NhZ2UoU3RyaW5nIGNvZGUsIFN0cmluZyBwb3N0Zml4KTogIlRoaXMgZGF0YSBpcyB3ZWFyaW5nIHRoZSB3cm9uZyBjb3N0dW1lLCBJIGRvbid0IHJlY29nbml6ZSBpdCBbJGNvZGVdICRwb3N0Zml4IgogIHR5cGVfZXJyb3JfbWVzc2FnZShTdHJpbmcgY29kZSwgU3RyaW5nIHBvc3RmaXgpOiAiVGhpcyBkYXRhIGlzIG5vdCB3aGF0IEkgZXhwZWN0ZWQsIEkgY2FuJ3QgcHJvY2VzcyBpdCBbJGNvZGVdICRwb3N0Zml4IgogIGluZGV4X2Vycm9yX21lc3NhZ2UoU3RyaW5nIGNvZGUsIFN0cmluZyBwb3N0Zml4KTogIkhtbSwgSSBjYW4ndCBzZWVtIHRvIGZpbmQgdGhhdCBpdGVtIGluIHRoZSBsaXN0IFskY29kZV0gJHBvc3RmaXgiCiAgcmFuZ2VfZXJyb3JfbWVzc2FnZShTdHJpbmcgY29kZSwgU3RyaW5nIHBvc3RmaXgpOiAiT29wcyEgVGhhdCBudW1iZXIgaXMgd2F5IG91dCBvZiBteSBjb21mb3J0IHpvbmUgWyRjb2RlXSAkcG9zdGZpeCIKICBhcmd1bWVudF9lcnJvcl9tZXNzYWdlKFN0cmluZyBjb2RlLCBTdHJpbmcgcG9zdGZpeCk6ICJIZXkhIFNvbWV0aGluZydzIG5vdCByaWdodCB3aXRoIHdoYXQgeW91IGdhdmUgbWUgWyRjb2RlXSAkcG9zdGZpeCIKICBzdGF0ZV9lcnJvcl9tZXNzYWdlKFN0cmluZyBjb2RlLCBTdHJpbmcgcG9zdGZpeCk6ICJJJ20gYSBiaXQgY29uZnVzZWQgYWJvdXQgd2hhdCBJIHNob3VsZCBiZSBkb2luZyByaWdodCBub3cgWyRjb2RlXSAkcG9zdGZpeCIKICB1bmltcGxlbWVudGVkX2Vycm9yX21lc3NhZ2UoU3RyaW5nIGNvZGUsIFN0cmluZyBwb3N0Zml4KTogIlRoaXMgZmVhdHVyZSBpcyBzdGlsbCB1bmRlciBjb25zdHJ1Y3Rpb24gWyRjb2RlXSAkcG9zdGZpeCIKICB1bnN1cHBvcnRlZF9lcnJvcl9tZXNzYWdlKFN0cmluZyBjb2RlLCBTdHJpbmcgcG9zdGZpeCk6ICJTb3JyeSwgSSBkb24ndCBrbm93IGhvdyB0byBkbyB0aGF0IHlldCBbJGNvZGVdICRwb3N0Zml4IgogIGNvbmN1cnJlbnRfbW9kaWZpY2F0aW9uX2Vycm9yX21lc3NhZ2UoU3RyaW5nIGNvZGUsIFN0cmluZyBwb3N0Zml4KTogIldob2EhIFRvbyBtYW55IHRoaW5ncyBoYXBwZW5pbmcgYXQgb25jZSBbJGNvZGVdICRwb3N0Zml4IgogIG91dF9vZl9tZW1vcnlfZXJyb3JfbWVzc2FnZShTdHJpbmcgY29kZSwgU3RyaW5nIHBvc3RmaXgpOiAiTXkgYnJhaW4gaXMgZnVsbCEgTmVlZCB0byBjbGVhciBzb21lIHNwYWNlIFskY29kZV0gJHBvc3RmaXgiCiAgc3RhY2tfb3ZlcmZsb3dfZXJyb3JfbWVzc2FnZShTdHJpbmcgY29kZSwgU3RyaW5nIHBvc3RmaXgpOiAiSSdtIHN0dWNrIGluIGEgbG9vcCBhbmQgZ2V0dGluZyBkaXp6eSBbJGNvZGVdICRwb3N0Zml4IgogIHVua25vd25fZXJyb3JfbWVzc2FnZShTdHJpbmcgY29kZSwgU3RyaW5nIHBvc3RmaXgpOiAiU29tZXRoaW5nIHVuZXhwZWN0ZWQgaGFwcGVuZWQsIGJ1dCBkb24ndCB3b3JyeSBbJGNvZGVdICRwb3N0Zml4IgojIFZhbGlkYXRpb24gbWVzc2FnZXMKdmFsaWRhdGlvbjoKICByZXF1aXJlZF9maWVsZDogIlRoaXMgZmllbGQgaXMgcmVxdWlyZWQiCiAgaW52YWxpZF9lbWFpbDogIlBsZWFzZSBlbnRlciBhIHZhbGlkIGVtYWlsIGFkZHJlc3MiCiAgcGFzc3dvcmRfdG9vX3Nob3J0OiAiUGFzc3dvcmQgbXVzdCBiZSBhdCBsZWFzdCA4IGNoYXJhY3RlcnMiCiAgcGFzc3dvcmRzX2RvbnRfbWF0Y2g6ICJQYXNzd29yZHMgZG8gbm90IG1hdGNoIgogIGludmFsaWRfa2V5X2NvbmZpZyhTdHJpbmcgb2YsIFN0cmluZyBrZXkpOiAiSW52YWxpZCBjb25maWd1cmF0aW9uIGZvciAka2V5IGluICRvZi4gUGxlYXNlIGNoZWNrIHlvdXIgc2V0dGluZ3MuIgoKIyBCaW9LaXQKCg==", + "IyB7e25hbWUudGl0bGVDYXNlKCl9fSAtIEludGVybmF0aW9uYWxpemF0aW9uIE1lc3NhZ2VzCiMgQmFzZSB0cmFuc2xhdGlvbnMgZm9yIHRoZSBhcHBsaWNhdGlvbgoKIyBhcHBsaWNhdGlvbiBtZXRhZGF0YQphcHA6CiAgbmFtZTogInt7bmFtZS50aXRsZUNhc2UoKX19IgogIGRlc2NyaXB0aW9uOiAie3t7ZGVzY3JpcHRpb259fX0iCiAgd2VsY29tZV90b19hcHA6ICJXZWxjb21lIHRvIHt7bmFtZS50aXRsZUNhc2UoKX19ISIKCiMgQ29tbW9uL0dlbmVyaWMgbWVzc2FnZXMKZ2VuZXJpYzoKICBvazogIk9LIgogIGNhbmNlbDogIkNhbmNlbCIKICBzYXZlOiAiU2F2ZSIKICBkZWxldGU6ICJEZWxldGUiCiAgZWRpdDogIkVkaXQiCiAgdXBkYXRlOiAiVXBkYXRlIgogIHN1Ym1pdDogIlN1Ym1pdCIKICBjbG9zZTogIkNsb3NlIgogIGJhY2s6ICJCYWNrIgogIG5leHQ6ICJOZXh0IgogIHByZXZpb3VzOiAiUHJldmlvdXMiCiAgZG9uZTogIkRvbmUiCiAgbG9hZGluZzogIkxvYWRpbmcuLi4iCiAgZXJyb3I6ICJFcnJvciIKICBzdWNjZXNzOiAiU3VjY2VzcyIKICB3YXJuaW5nOiAiV2FybmluZyIKICBpbmZvOiAiSW5mbyIKICByZXRyeTogIlJldHJ5IgogIHJlZnJlc2g6ICJSZWZyZXNoIgogIHllczogIlllcyIKICBubzogIk5vIgogIGFkZDogIisgQWRkIgogIHRyeV9hZ2FpbjogIlRyeSBBZ2FpbiIKCiMgQ29tbW9uIG1lc3NhZ2VzICh1c2VkIGZvciBjb25zaXN0ZW5jeSBhY3Jvc3MgbW9kdWxlcykKY29tbW9uOgogIGNhbmNlbDogIkNhbmNlbCIKICBvazogIk9LIgogIHNhdmU6ICJTYXZlIgogIGRlbGV0ZTogIkRlbGV0ZSIKICBlZGl0OiAiRWRpdCIKICB0cnlfYWdhaW46ICJUcnkgQWdhaW4iCiAgd2VlazogIldlZWsiCiAgbW9udGg6ICJNb250aCIKCiMgQXV0aGVudGljYXRpb24KYXV0aDoKICByZWdpc3RlcjogIlJlZ2lzdGVyIgogIHNpZ25faW46ICJTaWduIEluIgogIHNpZ25fb3V0OiAiU2lnbiBPdXQiCiAgZG9udF9oYXZlX2FjY291bnQ6ICJEb24ndCBoYXZlIGFuIGFjY291bnQ/ICIKICBhbHJlYWR5X2hhdmVfYWNjb3VudDogIkFscmVhZHkgaGF2ZSBhbiBhY2NvdW50PyAiCiAgc2lnbl9vdXRfY29uZmlybWF0aW9uOiAiQXJlIHlvdSBzdXJlIHlvdSB3YW50IHRvIHNpZ24gb3V0PyIKCiMgUHJvZmlsZQojIFByb2ZpbGUKcHJvZmlsZToKICBwcm9maWxlOiAiUHJvZmlsZSIKICBzZXR0aW5nczogIlNldHRpbmdzIgogIGFjY291bnQ6ICJBY2NvdW50IgogIHBlcnNvbmFsX2luZm86ICJQZXJzb25hbCBJbmZvcm1hdGlvbiIKICBwcml2YWN5X3NldHRpbmdzOiAiUHJpdmFjeSBTZXR0aW5ncyIKICBuYW1lOiAiTmFtZSIKICBlbWFpbF9hZGRyZXNzOiAiRW1haWwgQWRkcmVzcyIKICBwaG9uZV9udW1iZXI6ICJQaG9uZSBOdW1iZXIiCiAgZGF0ZV9vZl9iaXJ0aDogIkRhdGUgb2YgQmlydGgiCiAgZGVsZXRlX2NvbmZpcm1hdGlvbjogIkRlbGV0ZSBDb25maXJtYXRpb24iCiAgZGVsZXRlX2NvbmZpcm1hdGlvbl9tZXNzYWdlOiAiQXJlIHlvdSBzdXJlIHlvdSB3YW50IHRvIGRlbGV0ZSB0aGlzIGl0ZW0/IFRoaXMgYWN0aW9uIGNhbm5vdCBiZSB1bmRvbmUuIgoKbmF2OgogIGhvbWU6ICJIb21lIgogIGRhc2hib2FyZDogIkRhc2hib2FyZCIKICBwcm9maWxlOiAiUHJvZmlsZSIKICBzZXR0aW5nczogIlNldHRpbmdzIgogIG5vdGlmaWNhdGlvbnM6ICJOb3RpZmljYXRpb25zIgoKIyBOb3RpZmljYXRpb25zCm5vdGlmaWNhdGlvbnM6CiAgdGl0bGU6ICJOb3RpZmljYXRpb25zIgogIG1hcmtfYXNfcmVhZDogIk1hcmsgYXMgcmVhZCIKICBtYXJrX2FsbF9yZWFkOiAiTWFyayBhbGwgYXMgcmVhZCIKICBkZWxldGU6ICJEZWxldGUiCiAgCiAgIyBGaWx0ZXIgdGFicwogIGZpbHRlcl9hbGw6ICJBbGwiCiAgZmlsdGVyX3VucmVhZDogIlVucmVhZCIKICBmaWx0ZXJfcmVhZDogIlJlYWQiCiAgCiAgIyBOb3RpZmljYXRpb24gdHlwZXMKICB0eXBlX3JlbWluZGVyOiAiUmVtaW5kZXIiCiAgdHlwZV9hbGVydDogIkFsZXJ0IgogIHR5cGVfcHJvbW90aW9uOiAiUHJvbW90aW9uIgogIHR5cGVfc3lzdGVtOiAiU3lzdGVtIgogIHR5cGVfY3VzdG9tOiAiQ3VzdG9tIgogIAogICMgRW1wdHkgc3RhdGVzCiAgZW1wdHlfdGl0bGU6ICJObyBub3RpZmljYXRpb25zIgogIGVtcHR5X2Rlc2NyaXB0aW9uOiAiWW91J3JlIGFsbCBjYXVnaHQgdXAhIE5ldyBub3RpZmljYXRpb25zIHdpbGwgYXBwZWFyIGhlcmUuIgogIAogICMgRGVsZXRlIGNvbmZpcm1hdGlvbgogIGRlbGV0ZV9jb25maXJtYXRpb25fdGl0bGU6ICJEZWxldGUgbm90aWZpY2F0aW9uPyIKICBkZWxldGVfY29uZmlybWF0aW9uX21lc3NhZ2U6ICJUaGlzIG5vdGlmaWNhdGlvbiB3aWxsIGJlIHBlcm1hbmVudGx5IHJlbW92ZWQgZnJvbSB5b3VyIGxpc3QuIgoKIyBFcnJvcnMKZXJyb3JzOgogIG5ldHdvcmtfZXJyb3I6ICJOZXR3b3JrIGVycm9yLiBQbGVhc2UgY2hlY2sgeW91ciBjb25uZWN0aW9uLiIKICB1bmtub3duX2Vycm9yOiAiQW4gdW5rbm93biBlcnJvciBvY2N1cnJlZC4iCiAgdmFsaWRhdGlvbl9lcnJvcjogIlBsZWFzZSBjaGVjayB5b3VyIGlucHV0IGFuZCB0cnkgYWdhaW4uIgogIHNlcnZlcl9lcnJvcjogIlNlcnZlciBlcnJvci4gUGxlYXNlIHRyeSBhZ2FpbiBsYXRlci4iCiAgZGVmYXVsdF9lcnJvcl9tZXNzYWdlOiAiT29wcyEgU29tZXRoaW5nIHdlbnQgd3JvbmcuIFBsZWFzZSB0cnkgYWdhaW4uIgogIHVzZXJfbm90X2ZvdW5kOiAiVXNlciBub3QgZm91bmQuIFBsZWFzZSBjaGVjayB5b3VyIGNyZWRlbnRpYWxzLiIKICBkZWZhdWx0X2Vycm9yX2Rlc2NyaXB0aW9uOiAiV2UgZW5jb3VudGVyZWQgYW4gZXJyb3Igd2hpbGUgcHJvY2Vzc2luZyB5b3VyIHJlcXVlc3QuIFdlIGFwb2xvZ2l6ZSBmb3IgdGhlIGluY29udmVuaWVuY2UuIFBsZWFzZSB0cnkgYWdhaW4gbGF0ZXIgb3IgY29udGFjdCBzdXBwb3J0IGlmIHRoZSBpc3N1ZSBwZXJzaXN0cy4iCiAgcGFnZV9ub3RfZm91bmQ6ICJQYWdlIE5vdCBGb3VuZCIKICBwYWdlX25vdF9mb3VuZF9kZXNjcmlwdGlvbjogIlRoZSBwYWdlIHlvdSBhcmUgbG9va2luZyBmb3IgZG9lcyBub3QgZXhpc3QuIgogIHVuZXhwZWN0ZWRfZXJyb3I6ICJBbiB1bmV4cGVjdGVkIGVycm9yIG9jY3VycmVkLiIKICByZWRpcmVjdF9lcnJvcjogIlJlZGlyZWN0IEVycm9yIgogIGJhZF9yZXF1ZXN0OiAiSW52YWxpZCByZXF1ZXN0LiBQbGVhc2UgY2hlY2sgeW91ciBpbnB1dC4iCiAgdW5hdXRob3JpemVkOiAiQXV0aGVudGljYXRpb24gcmVxdWlyZWQuIFBsZWFzZSBzaWduIGluIGFnYWluLiIKICBmb3JiaWRkZW46ICJBY2Nlc3MgZGVuaWVkLiBZb3UgZG9uJ3QgaGF2ZSBwZXJtaXNzaW9uLiIKICBub3RfZm91bmQ6ICJSZXF1ZXN0ZWQgcmVzb3VyY2Ugbm90IGZvdW5kLiIKICBjb25mbGljdDogIkRhdGEgY29uZmxpY3QuIFBsZWFzZSByZWZyZXNoIGFuZCB0cnkgYWdhaW4uIgogIHVucHJvY2Vzc2FibGVfZW50aXR5OiAiSW52YWxpZCBkYXRhIGZvcm1hdC4gUGxlYXNlIGNoZWNrIHlvdXIgaW5wdXQuIgogIGludGVybmFsX3NlcnZlcl9lcnJvcjogIlNlcnZlciBlcnJvci4gUGxlYXNlIHRyeSBhZ2FpbiBsYXRlci4iCiAgY29ubmVjdGlvbl90aW1lb3V0OiAiQ29ubmVjdGlvbiB0aW1lb3V0LiBQbGVhc2UgY2hlY2sgeW91ciBpbnRlcm5ldC4iCiAgcmVjZWl2ZV90aW1lb3V0OiAiUmVxdWVzdCB0aW1lb3V0LiBQbGVhc2UgdHJ5IGFnYWluLiIKICBzZW5kX3RpbWVvdXQ6ICJVcGxvYWQgdGltZW91dC4gUGxlYXNlIHRyeSBhZ2Fpbi4iCiAgbm9faW50ZXJuZXQ6ICJObyBpbnRlcm5ldCBjb25uZWN0aW9uLiBQbGVhc2UgY2hlY2sgeW91ciBuZXR3b3JrLiIKICB1bmtub3duX25ldHdvcms6ICJOZXR3b3JrIGVycm9yIG9jY3VycmVkLiBQbGVhc2UgdHJ5IGFnYWluLiIKICBmb3JtYXRfZXhjZXB0aW9uX21lc3NhZ2UoU3RyaW5nIGNvZGUsIFN0cmluZyBwb3N0Zml4KTogIlRoaXMgZGF0YSBpcyB3ZWFyaW5nIHRoZSB3cm9uZyBjb3N0dW1lLCBJIGRvbid0IHJlY29nbml6ZSBpdCBbJGNvZGVdICRwb3N0Zml4IgogIHR5cGVfZXJyb3JfbWVzc2FnZShTdHJpbmcgY29kZSwgU3RyaW5nIHBvc3RmaXgpOiAiVGhpcyBkYXRhIGlzIG5vdCB3aGF0IEkgZXhwZWN0ZWQsIEkgY2FuJ3QgcHJvY2VzcyBpdCBbJGNvZGVdICRwb3N0Zml4IgogIGluZGV4X2Vycm9yX21lc3NhZ2UoU3RyaW5nIGNvZGUsIFN0cmluZyBwb3N0Zml4KTogIkhtbSwgSSBjYW4ndCBzZWVtIHRvIGZpbmQgdGhhdCBpdGVtIGluIHRoZSBsaXN0IFskY29kZV0gJHBvc3RmaXgiCiAgcmFuZ2VfZXJyb3JfbWVzc2FnZShTdHJpbmcgY29kZSwgU3RyaW5nIHBvc3RmaXgpOiAiT29wcyEgVGhhdCBudW1iZXIgaXMgd2F5IG91dCBvZiBteSBjb21mb3J0IHpvbmUgWyRjb2RlXSAkcG9zdGZpeCIKICBhcmd1bWVudF9lcnJvcl9tZXNzYWdlKFN0cmluZyBjb2RlLCBTdHJpbmcgcG9zdGZpeCk6ICJIZXkhIFNvbWV0aGluZydzIG5vdCByaWdodCB3aXRoIHdoYXQgeW91IGdhdmUgbWUgWyRjb2RlXSAkcG9zdGZpeCIKICBzdGF0ZV9lcnJvcl9tZXNzYWdlKFN0cmluZyBjb2RlLCBTdHJpbmcgcG9zdGZpeCk6ICJJJ20gYSBiaXQgY29uZnVzZWQgYWJvdXQgd2hhdCBJIHNob3VsZCBiZSBkb2luZyByaWdodCBub3cgWyRjb2RlXSAkcG9zdGZpeCIKICB1bmltcGxlbWVudGVkX2Vycm9yX21lc3NhZ2UoU3RyaW5nIGNvZGUsIFN0cmluZyBwb3N0Zml4KTogIlRoaXMgZmVhdHVyZSBpcyBzdGlsbCB1bmRlciBjb25zdHJ1Y3Rpb24gWyRjb2RlXSAkcG9zdGZpeCIKICB1bnN1cHBvcnRlZF9lcnJvcl9tZXNzYWdlKFN0cmluZyBjb2RlLCBTdHJpbmcgcG9zdGZpeCk6ICJTb3JyeSwgSSBkb24ndCBrbm93IGhvdyB0byBkbyB0aGF0IHlldCBbJGNvZGVdICRwb3N0Zml4IgogIGNvbmN1cnJlbnRfbW9kaWZpY2F0aW9uX2Vycm9yX21lc3NhZ2UoU3RyaW5nIGNvZGUsIFN0cmluZyBwb3N0Zml4KTogIldob2EhIFRvbyBtYW55IHRoaW5ncyBoYXBwZW5pbmcgYXQgb25jZSBbJGNvZGVdICRwb3N0Zml4IgogIG91dF9vZl9tZW1vcnlfZXJyb3JfbWVzc2FnZShTdHJpbmcgY29kZSwgU3RyaW5nIHBvc3RmaXgpOiAiTXkgYnJhaW4gaXMgZnVsbCEgTmVlZCB0byBjbGVhciBzb21lIHNwYWNlIFskY29kZV0gJHBvc3RmaXgiCiAgc3RhY2tfb3ZlcmZsb3dfZXJyb3JfbWVzc2FnZShTdHJpbmcgY29kZSwgU3RyaW5nIHBvc3RmaXgpOiAiSSdtIHN0dWNrIGluIGEgbG9vcCBhbmQgZ2V0dGluZyBkaXp6eSBbJGNvZGVdICRwb3N0Zml4IgogIHVua25vd25fZXJyb3JfbWVzc2FnZShTdHJpbmcgY29kZSwgU3RyaW5nIHBvc3RmaXgpOiAiU29tZXRoaW5nIHVuZXhwZWN0ZWQgaGFwcGVuZWQsIGJ1dCBkb24ndCB3b3JyeSBbJGNvZGVdICRwb3N0Zml4IgojIFZhbGlkYXRpb24gbWVzc2FnZXMKdmFsaWRhdGlvbjoKICByZXF1aXJlZF9maWVsZDogIlRoaXMgZmllbGQgaXMgcmVxdWlyZWQiCiAgaW52YWxpZF9lbWFpbDogIlBsZWFzZSBlbnRlciBhIHZhbGlkIGVtYWlsIGFkZHJlc3MiCiAgcGFzc3dvcmRfdG9vX3Nob3J0OiAiUGFzc3dvcmQgbXVzdCBiZSBhdCBsZWFzdCA4IGNoYXJhY3RlcnMiCiAgcGFzc3dvcmRzX2RvbnRfbWF0Y2g6ICJQYXNzd29yZHMgZG8gbm90IG1hdGNoIgogIGludmFsaWRfa2V5X2NvbmZpZyhTdHJpbmcgb2YsIFN0cmluZyBrZXkpOiAiSW52YWxpZCBjb25maWd1cmF0aW9uIGZvciAka2V5IGluICRvZi4gUGxlYXNlIGNoZWNrIHlvdXIgc2V0dGluZ3MuIgo=", "type": "text" }, { @@ -831,7 +831,7 @@ final flutterProjectWithMonoRepoBundle = MasonBundle.fromJson({ "path": "{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/models/analytics_events.dart", "data": - "Ly8vIFN0cnVjdHVyZWQgYW5hbHl0aWNzIGV2ZW50IG5hbWVzIG9yZ2FuaXplZCBieSBjYXRlZ29yeQovLy8KLy8vIFVzYWdlOiBBbmFseXRpY3NFdmVudHMudXNlci5hdXRoZW50aWNhdGVkUmVkaXJlY3QKLy8vIFRoaXMgcHJvdmlkZXMgdHlwZS1zYWZlLCBkaXNjb3ZlcmFibGUgZXZlbnQgbmFtZXMgYW5kIHByZXZlbnRzIHR5cG9zCmNsYXNzIEFuYWx5dGljc0V2ZW50cyB7CiAgQW5hbHl0aWNzRXZlbnRzLl8oKTsKCiAgLy8vIFVzZXItcmVsYXRlZCBldmVudHMKICBzdGF0aWMgY29uc3QgdXNlciA9IF9Vc2VyRXZlbnRzKCk7CgogIC8vLyBBdXRoZW50aWNhdGlvbi1yZWxhdGVkIGV2ZW50cwogIHN0YXRpYyBjb25zdCBhdXRoID0gX0F1dGhFdmVudHMoKTsKCiAgLy8vIE5hdmlnYXRpb24tcmVsYXRlZCBldmVudHMKICBzdGF0aWMgY29uc3QgbmF2aWdhdGlvbiA9IF9OYXZpZ2F0aW9uRXZlbnRzKCk7CgogIC8vLyBGZWF0dXJlIHVzYWdlIGV2ZW50cwogIHN0YXRpYyBjb25zdCBmZWF0dXJlID0gX0ZlYXR1cmVFdmVudHMoKTsKCiAgLy8vIEVycm9yIGFuZCBkZWJ1Z2dpbmcgZXZlbnRzCiAgc3RhdGljIGNvbnN0IGVycm9yID0gX0Vycm9yRXZlbnRzKCk7CgogIC8vLyBBcHAgbGlmZWN5Y2xlIGV2ZW50cwogIHN0YXRpYyBjb25zdCBhcHAgPSBfQXBwRXZlbnRzKCk7CgogIC8vLyBTdXJ2ZXkgYW5kIGFzc2Vzc21lbnQgZXZlbnRzCiAgc3RhdGljIGNvbnN0IHN1cnZleSA9IF9TdXJ2ZXlFdmVudHMoKTsKCiAgLy8vIEhlYWx0aCBkYXRhIGFuZCBiaW9raXQgZXZlbnRzCiAgc3RhdGljIGNvbnN0IGhlYWx0aCA9IF9IZWFsdGhFdmVudHMoKTsKCiAgLy8vIFByb2ZpbGUtcmVsYXRlZCBldmVudHMKICBzdGF0aWMgY29uc3QgcHJvZmlsZSA9IF9Qcm9maWxlRXZlbnRzKCk7Cn0KCmNsYXNzIF9Vc2VyRXZlbnRzIHsKICBjb25zdCBfVXNlckV2ZW50cygpOwoKICAvLy8gVXNlciBzdWNjZXNzZnVsbHkgYXV0aGVudGljYXRlZCBhbmQgcmVkaXJlY3RlZAogIFN0cmluZyBnZXQgYXV0aGVudGljYXRlZFJlZGlyZWN0ID0+ICd1c2VyX2F1dGhlbnRpY2F0ZWRfcmVkaXJlY3QnOwoKICAvLy8gVXNlciBwcm9maWxlIHVwZGF0ZWQKICBTdHJpbmcgZ2V0IHByb2ZpbGVVcGRhdGVkID0+ICd1c2VyX3Byb2ZpbGVfdXBkYXRlZCc7CgogIC8vLyBVc2VyIHByZWZlcmVuY2VzIGNoYW5nZWQKICBTdHJpbmcgZ2V0IHByZWZlcmVuY2VzQ2hhbmdlZCA9PiAndXNlcl9wcmVmZXJlbmNlc19jaGFuZ2VkJzsKCiAgLy8vIFVzZXIgY29tcGxldGVkIG9uYm9hcmRpbmcKICBTdHJpbmcgZ2V0IG9uYm9hcmRpbmdDb21wbGV0ZWQgPT4gJ3VzZXJfb25ib2FyZGluZ19jb21wbGV0ZWQnOwoKICAvLy8gVXNlciBhY2NvdW50IGRlbGV0ZWQKICBTdHJpbmcgZ2V0IGFjY291bnREZWxldGVkID0+ICd1c2VyX2FjY291bnRfZGVsZXRlZCc7Cn0KCmNsYXNzIF9BdXRoRXZlbnRzIHsKICBjb25zdCBfQXV0aEV2ZW50cygpOwoKICAvLy8gVXNlciBzaWduZWQgaW4KICBTdHJpbmcgZ2V0IHNpZ25JbiA9PiAnYXV0aF9zaWduX2luJzsKCiAgLy8vIFVzZXIgc2lnbmVkIHVwCiAgU3RyaW5nIGdldCBzaWduVXAgPT4gJ2F1dGhfc2lnbl91cCc7CgogIC8vLyBVc2VyIHNpZ25lZCBvdXQKICBTdHJpbmcgZ2V0IHNpZ25PdXQgPT4gJ2F1dGhfc2lnbl9vdXQnOwoKICAvLy8gUGFzc3dvcmQgcmVzZXQgcmVxdWVzdGVkCiAgU3RyaW5nIGdldCBwYXNzd29yZFJlc2V0ID0+ICdhdXRoX3Bhc3N3b3JkX3Jlc2V0JzsKCiAgLy8vIEF1dGhlbnRpY2F0aW9uIGZhaWxlZAogIFN0cmluZyBnZXQgZmFpbGVkID0+ICdhdXRoX2ZhaWxlZCc7CgogIC8vLyBTb2NpYWwgYXV0aGVudGljYXRpb24gKEdvb2dsZSwgQXBwbGUsIGV0Yy4pCiAgU3RyaW5nIGdldCBzb2NpYWxMb2dpbiA9PiAnYXV0aF9zb2NpYWxfbG9naW4nOwp9CgpjbGFzcyBfTmF2aWdhdGlvbkV2ZW50cyB7CiAgY29uc3QgX05hdmlnYXRpb25FdmVudHMoKTsKCiAgLy8vIFNjcmVlbiB2aWV3ZWQgKGF1dG9tYXRpY2FsbHkgdHJhY2tlZCkKICBTdHJpbmcgZ2V0IHNjcmVlblZpZXcgPT4gJ3NjcmVlbl92aWV3JzsKCiAgLy8vIFRhYiBzd2l0Y2hlZCBpbiBib3R0b20gbmF2aWdhdGlvbgogIFN0cmluZyBnZXQgdGFiU3dpdGNoZWQgPT4gJ25hdmlnYXRpb25fdGFiX3N3aXRjaGVkJzsKCiAgLy8vIEJhY2sgYnV0dG9uIHByZXNzZWQKICBTdHJpbmcgZ2V0IGJhY2tQcmVzc2VkID0+ICduYXZpZ2F0aW9uX2JhY2tfcHJlc3NlZCc7CgogIC8vLyBEZWVwIGxpbmsgb3BlbmVkCiAgU3RyaW5nIGdldCBkZWVwTGlua09wZW5lZCA9PiAnbmF2aWdhdGlvbl9kZWVwX2xpbmtfb3BlbmVkJzsKfQoKY2xhc3MgX0ZlYXR1cmVFdmVudHMgewogIGNvbnN0IF9GZWF0dXJlRXZlbnRzKCk7CgogIC8vLyBGZWF0dXJlIHdhcyB1c2VkL2FjY2Vzc2VkCiAgU3RyaW5nIGdldCB1c2VkID0+ICdmZWF0dXJlX3VzZWQnOwoKICAvLy8gRmVhdHVyZSB0dXRvcmlhbCBzdGFydGVkCiAgU3RyaW5nIGdldCB0dXRvcmlhbFN0YXJ0ZWQgPT4gJ2ZlYXR1cmVfdHV0b3JpYWxfc3RhcnRlZCc7CgogIC8vLyBGZWF0dXJlIHR1dG9yaWFsIGNvbXBsZXRlZAogIFN0cmluZyBnZXQgdHV0b3JpYWxDb21wbGV0ZWQgPT4gJ2ZlYXR1cmVfdHV0b3JpYWxfY29tcGxldGVkJzsKCiAgLy8vIEZlYXR1cmUgZmVlZGJhY2sgZ2l2ZW4KICBTdHJpbmcgZ2V0IGZlZWRiYWNrR2l2ZW4gPT4gJ2ZlYXR1cmVfZmVlZGJhY2tfZ2l2ZW4nOwoKICAvLy8gQnV0dG9uIHByZXNzZWQKICBTdHJpbmcgZ2V0IGJ1dHRvblByZXNzZWQgPT4gJ2J1dHRvbl9wcmVzc2VkJzsKfQoKY2xhc3MgX0Vycm9yRXZlbnRzIHsKICBjb25zdCBfRXJyb3JFdmVudHMoKTsKCiAgLy8vIEdlbmVyYWwgYXBwIGVycm9yIG9jY3VycmVkCiAgU3RyaW5nIGdldCBhcHBFcnJvciA9PiAnYXBwX2Vycm9yJzsKCiAgLy8vIE5ldHdvcmsgZXJyb3Igb2NjdXJyZWQKICBTdHJpbmcgZ2V0IG5ldHdvcmtFcnJvciA9PiAnbmV0d29ya19lcnJvcic7CgogIC8vLyBDcmFzaCBvY2N1cnJlZAogIFN0cmluZyBnZXQgY3Jhc2ggPT4gJ2FwcF9jcmFzaCc7CgogIC8vLyBBUEkgZXJyb3Igb2NjdXJyZWQKICBTdHJpbmcgZ2V0IGFwaUVycm9yID0+ICdhcGlfZXJyb3InOwoKICAvLy8gVmFsaWRhdGlvbiBlcnJvcgogIFN0cmluZyBnZXQgdmFsaWRhdGlvbkVycm9yID0+ICd2YWxpZGF0aW9uX2Vycm9yJzsKfQoKY2xhc3MgX0FwcEV2ZW50cyB7CiAgY29uc3QgX0FwcEV2ZW50cygpOwoKICAvLy8gQXBwIG9wZW5lZC9sYXVuY2hlZAogIFN0cmluZyBnZXQgb3BlbiA9PiAnYXBwX29wZW4nOwoKICAvLy8gQXBwIHdlbnQgdG8gYmFja2dyb3VuZAogIFN0cmluZyBnZXQgYmFja2dyb3VuZCA9PiAnYXBwX2JhY2tncm91bmQnOwoKICAvLy8gQXBwIGNhbWUgdG8gZm9yZWdyb3VuZAogIFN0cmluZyBnZXQgZm9yZWdyb3VuZCA9PiAnYXBwX2ZvcmVncm91bmQnOwoKICAvLy8gQXBwIHVwZGF0ZWQgdG8gbmV3IHZlcnNpb24KICBTdHJpbmcgZ2V0IHVwZGF0ZWQgPT4gJ2FwcF91cGRhdGVkJzsKCiAgLy8vIEFwcCBpbnN0YWxsZWQgZm9yIGZpcnN0IHRpbWUKICBTdHJpbmcgZ2V0IGluc3RhbGxlZCA9PiAnYXBwX2luc3RhbGxlZCc7CgogIC8vLyBBcHAgdW5pbnN0YWxsZWQKICBTdHJpbmcgZ2V0IHVuaW5zdGFsbGVkID0+ICdhcHBfdW5pbnN0YWxsZWQnOwp9CgpjbGFzcyBfU3VydmV5RXZlbnRzIHsKICBjb25zdCBfU3VydmV5RXZlbnRzKCk7CgogIC8vLyBTdXJ2ZXkgc3RhcnRlZAogIFN0cmluZyBnZXQgc3RhcnRlZCA9PiAnc3VydmV5X3N0YXJ0ZWQnOwoKICAvLy8gU3VydmV5IGNvbXBsZXRlZAogIFN0cmluZyBnZXQgY29tcGxldGVkID0+ICdzdXJ2ZXlfY29tcGxldGVkJzsKCiAgLy8vIFN1cnZleSBhYmFuZG9uZWQvY2FuY2VsbGVkCiAgU3RyaW5nIGdldCBhYmFuZG9uZWQgPT4gJ3N1cnZleV9hYmFuZG9uZWQnOwoKICAvLy8gU3VydmV5IHF1ZXN0aW9uIGFuc3dlcmVkCiAgU3RyaW5nIGdldCBxdWVzdGlvbkFuc3dlcmVkID0+ICdzdXJ2ZXlfcXVlc3Rpb25fYW5zd2VyZWQnOwoKICAvLy8gU3VydmV5IHByb2dyZXNzIHNhdmVkCiAgU3RyaW5nIGdldCBwcm9ncmVzc1NhdmVkID0+ICdzdXJ2ZXlfcHJvZ3Jlc3Nfc2F2ZWQnOwoKICAvLy8gU3VydmV5IHJlc3VsdHMgdmlld2VkCiAgU3RyaW5nIGdldCByZXN1bHRzVmlld2VkID0+ICdzdXJ2ZXlfcmVzdWx0c192aWV3ZWQnOwp9CgpjbGFzcyBfSGVhbHRoRXZlbnRzIHsKICBjb25zdCBfSGVhbHRoRXZlbnRzKCk7CgogIC8vLyBIZWFsdGggZGF0YSBzeW5jZWQKICBTdHJpbmcgZ2V0IGRhdGFTeW5jZWQgPT4gJ2hlYWx0aF9kYXRhX3N5bmNlZCc7CgogIC8vLyBXZWFyYWJsZSBkZXZpY2UgY29ubmVjdGVkCiAgU3RyaW5nIGdldCBkZXZpY2VDb25uZWN0ZWQgPT4gJ2hlYWx0aF9kZXZpY2VfY29ubmVjdGVkJzsKCiAgLy8vIFdlYXJhYmxlIGRldmljZSBkaXNjb25uZWN0ZWQKICBTdHJpbmcgZ2V0IGRldmljZURpc2Nvbm5lY3RlZCA9PiAnaGVhbHRoX2RldmljZV9kaXNjb25uZWN0ZWQnOwoKICAvLy8gSGVhbHRoIG1ldHJpYyByZWNvcmRlZAogIFN0cmluZyBnZXQgbWV0cmljUmVjb3JkZWQgPT4gJ2hlYWx0aF9tZXRyaWNfcmVjb3JkZWQnOwoKICAvLy8gSGVhbHRoIGdvYWwgc2V0CiAgU3RyaW5nIGdldCBnb2FsU2V0ID0+ICdoZWFsdGhfZ29hbF9zZXQnOwoKICAvLy8gSGVhbHRoIGdvYWwgYWNoaWV2ZWQKICBTdHJpbmcgZ2V0IGdvYWxBY2hpZXZlZCA9PiAnaGVhbHRoX2dvYWxfYWNoaWV2ZWQnOwoKICAvLy8gSGVhbHRoIHJlcG9ydCBnZW5lcmF0ZWQKICBTdHJpbmcgZ2V0IHJlcG9ydEdlbmVyYXRlZCA9PiAnaGVhbHRoX3JlcG9ydF9nZW5lcmF0ZWQnOwp9CgpjbGFzcyBfUHJvZmlsZUV2ZW50cyB7CiAgY29uc3QgX1Byb2ZpbGVFdmVudHMoKTsKCiAgLy8vIFByb2ZpbGUgdmlld2VkCiAgU3RyaW5nIGdldCB2aWV3ZWQgPT4gJ3Byb2ZpbGVfdmlld2VkJzsKCiAgLy8vIFByb2ZpbGUgZWRpdGVkCiAgU3RyaW5nIGdldCBlZGl0ZWQgPT4gJ3Byb2ZpbGVfZWRpdGVkJzsKCiAgLy8vIFByb2ZpbGUgcGhvdG8gdXBkYXRlZAogIFN0cmluZyBnZXQgcGhvdG9VcGRhdGVkID0+ICdwcm9maWxlX3Bob3RvX3VwZGF0ZWQnOwoKICAvLy8gUHJvZmlsZSBzZXR0aW5ncyBjaGFuZ2VkCiAgU3RyaW5nIGdldCBzZXR0aW5nc0NoYW5nZWQgPT4gJ3Byb2ZpbGVfc2V0dGluZ3NfY2hhbmdlZCc7CgogIC8vLyBQcm9maWxlIHByaXZhY3kgc2V0dGluZ3MgdXBkYXRlZAogIFN0cmluZyBnZXQgcHJpdmFjeVVwZGF0ZWQgPT4gJ3Byb2ZpbGVfcHJpdmFjeV91cGRhdGVkJzsKfQo=", + "Ly8vIFN0cnVjdHVyZWQgYW5hbHl0aWNzIGV2ZW50IG5hbWVzIG9yZ2FuaXplZCBieSBjYXRlZ29yeS4KLy8vCi8vLyBVc2FnZTogYEFuYWx5dGljc0V2ZW50cy51c2VyLmF1dGhlbnRpY2F0ZWRSZWRpcmVjdGAuIE5hbWluZyBldmVudHMgaGVyZQovLy8gaW5zdGVhZCBvZiBpbmxpbmUga2VlcHMgdGhlbSBkaXNjb3ZlcmFibGUgYW5kIHR5cG8tZnJlZSwgYW5kIGdpdmVzIHlvdSBvbmUKLy8vIHBsYWNlIHRvIGFkZCB0aGUgZ3JvdXBzIHlvdXIgb3duIHByb2R1Y3QgbmVlZHMuCmNsYXNzIEFuYWx5dGljc0V2ZW50cyB7CiAgQW5hbHl0aWNzRXZlbnRzLl8oKTsKCiAgLy8vIFVzZXItcmVsYXRlZCBldmVudHMKICBzdGF0aWMgY29uc3QgdXNlciA9IF9Vc2VyRXZlbnRzKCk7CgogIC8vLyBBdXRoZW50aWNhdGlvbi1yZWxhdGVkIGV2ZW50cwogIHN0YXRpYyBjb25zdCBhdXRoID0gX0F1dGhFdmVudHMoKTsKCiAgLy8vIE5hdmlnYXRpb24tcmVsYXRlZCBldmVudHMKICBzdGF0aWMgY29uc3QgbmF2aWdhdGlvbiA9IF9OYXZpZ2F0aW9uRXZlbnRzKCk7CgogIC8vLyBGZWF0dXJlIHVzYWdlIGV2ZW50cwogIHN0YXRpYyBjb25zdCBmZWF0dXJlID0gX0ZlYXR1cmVFdmVudHMoKTsKCiAgLy8vIEVycm9yIGFuZCBkZWJ1Z2dpbmcgZXZlbnRzCiAgc3RhdGljIGNvbnN0IGVycm9yID0gX0Vycm9yRXZlbnRzKCk7CgogIC8vLyBBcHAgbGlmZWN5Y2xlIGV2ZW50cwogIHN0YXRpYyBjb25zdCBhcHAgPSBfQXBwRXZlbnRzKCk7CgogIC8vLyBQcm9maWxlLXJlbGF0ZWQgZXZlbnRzCiAgc3RhdGljIGNvbnN0IHByb2ZpbGUgPSBfUHJvZmlsZUV2ZW50cygpOwp9CgpjbGFzcyBfVXNlckV2ZW50cyB7CiAgY29uc3QgX1VzZXJFdmVudHMoKTsKCiAgLy8vIFVzZXIgc3VjY2Vzc2Z1bGx5IGF1dGhlbnRpY2F0ZWQgYW5kIHJlZGlyZWN0ZWQKICBTdHJpbmcgZ2V0IGF1dGhlbnRpY2F0ZWRSZWRpcmVjdCA9PiAndXNlcl9hdXRoZW50aWNhdGVkX3JlZGlyZWN0JzsKCiAgLy8vIFVzZXIgcHJvZmlsZSB1cGRhdGVkCiAgU3RyaW5nIGdldCBwcm9maWxlVXBkYXRlZCA9PiAndXNlcl9wcm9maWxlX3VwZGF0ZWQnOwoKICAvLy8gVXNlciBwcmVmZXJlbmNlcyBjaGFuZ2VkCiAgU3RyaW5nIGdldCBwcmVmZXJlbmNlc0NoYW5nZWQgPT4gJ3VzZXJfcHJlZmVyZW5jZXNfY2hhbmdlZCc7CgogIC8vLyBVc2VyIGNvbXBsZXRlZCBvbmJvYXJkaW5nCiAgU3RyaW5nIGdldCBvbmJvYXJkaW5nQ29tcGxldGVkID0+ICd1c2VyX29uYm9hcmRpbmdfY29tcGxldGVkJzsKCiAgLy8vIFVzZXIgYWNjb3VudCBkZWxldGVkCiAgU3RyaW5nIGdldCBhY2NvdW50RGVsZXRlZCA9PiAndXNlcl9hY2NvdW50X2RlbGV0ZWQnOwp9CgpjbGFzcyBfQXV0aEV2ZW50cyB7CiAgY29uc3QgX0F1dGhFdmVudHMoKTsKCiAgLy8vIFVzZXIgc2lnbmVkIGluCiAgU3RyaW5nIGdldCBzaWduSW4gPT4gJ2F1dGhfc2lnbl9pbic7CgogIC8vLyBVc2VyIHNpZ25lZCB1cAogIFN0cmluZyBnZXQgc2lnblVwID0+ICdhdXRoX3NpZ25fdXAnOwoKICAvLy8gVXNlciBzaWduZWQgb3V0CiAgU3RyaW5nIGdldCBzaWduT3V0ID0+ICdhdXRoX3NpZ25fb3V0JzsKCiAgLy8vIFBhc3N3b3JkIHJlc2V0IHJlcXVlc3RlZAogIFN0cmluZyBnZXQgcGFzc3dvcmRSZXNldCA9PiAnYXV0aF9wYXNzd29yZF9yZXNldCc7CgogIC8vLyBBdXRoZW50aWNhdGlvbiBmYWlsZWQKICBTdHJpbmcgZ2V0IGZhaWxlZCA9PiAnYXV0aF9mYWlsZWQnOwoKICAvLy8gU29jaWFsIGF1dGhlbnRpY2F0aW9uIChHb29nbGUsIEFwcGxlLCBldGMuKQogIFN0cmluZyBnZXQgc29jaWFsTG9naW4gPT4gJ2F1dGhfc29jaWFsX2xvZ2luJzsKfQoKY2xhc3MgX05hdmlnYXRpb25FdmVudHMgewogIGNvbnN0IF9OYXZpZ2F0aW9uRXZlbnRzKCk7CgogIC8vLyBTY3JlZW4gdmlld2VkIChhdXRvbWF0aWNhbGx5IHRyYWNrZWQpCiAgU3RyaW5nIGdldCBzY3JlZW5WaWV3ID0+ICdzY3JlZW5fdmlldyc7CgogIC8vLyBUYWIgc3dpdGNoZWQgaW4gYm90dG9tIG5hdmlnYXRpb24KICBTdHJpbmcgZ2V0IHRhYlN3aXRjaGVkID0+ICduYXZpZ2F0aW9uX3RhYl9zd2l0Y2hlZCc7CgogIC8vLyBCYWNrIGJ1dHRvbiBwcmVzc2VkCiAgU3RyaW5nIGdldCBiYWNrUHJlc3NlZCA9PiAnbmF2aWdhdGlvbl9iYWNrX3ByZXNzZWQnOwoKICAvLy8gRGVlcCBsaW5rIG9wZW5lZAogIFN0cmluZyBnZXQgZGVlcExpbmtPcGVuZWQgPT4gJ25hdmlnYXRpb25fZGVlcF9saW5rX29wZW5lZCc7Cn0KCmNsYXNzIF9GZWF0dXJlRXZlbnRzIHsKICBjb25zdCBfRmVhdHVyZUV2ZW50cygpOwoKICAvLy8gRmVhdHVyZSB3YXMgdXNlZC9hY2Nlc3NlZAogIFN0cmluZyBnZXQgdXNlZCA9PiAnZmVhdHVyZV91c2VkJzsKCiAgLy8vIEZlYXR1cmUgdHV0b3JpYWwgc3RhcnRlZAogIFN0cmluZyBnZXQgdHV0b3JpYWxTdGFydGVkID0+ICdmZWF0dXJlX3R1dG9yaWFsX3N0YXJ0ZWQnOwoKICAvLy8gRmVhdHVyZSB0dXRvcmlhbCBjb21wbGV0ZWQKICBTdHJpbmcgZ2V0IHR1dG9yaWFsQ29tcGxldGVkID0+ICdmZWF0dXJlX3R1dG9yaWFsX2NvbXBsZXRlZCc7CgogIC8vLyBGZWF0dXJlIGZlZWRiYWNrIGdpdmVuCiAgU3RyaW5nIGdldCBmZWVkYmFja0dpdmVuID0+ICdmZWF0dXJlX2ZlZWRiYWNrX2dpdmVuJzsKCiAgLy8vIEJ1dHRvbiBwcmVzc2VkCiAgU3RyaW5nIGdldCBidXR0b25QcmVzc2VkID0+ICdidXR0b25fcHJlc3NlZCc7Cn0KCmNsYXNzIF9FcnJvckV2ZW50cyB7CiAgY29uc3QgX0Vycm9yRXZlbnRzKCk7CgogIC8vLyBHZW5lcmFsIGFwcCBlcnJvciBvY2N1cnJlZAogIFN0cmluZyBnZXQgYXBwRXJyb3IgPT4gJ2FwcF9lcnJvcic7CgogIC8vLyBOZXR3b3JrIGVycm9yIG9jY3VycmVkCiAgU3RyaW5nIGdldCBuZXR3b3JrRXJyb3IgPT4gJ25ldHdvcmtfZXJyb3InOwoKICAvLy8gQ3Jhc2ggb2NjdXJyZWQKICBTdHJpbmcgZ2V0IGNyYXNoID0+ICdhcHBfY3Jhc2gnOwoKICAvLy8gQVBJIGVycm9yIG9jY3VycmVkCiAgU3RyaW5nIGdldCBhcGlFcnJvciA9PiAnYXBpX2Vycm9yJzsKCiAgLy8vIFZhbGlkYXRpb24gZXJyb3IKICBTdHJpbmcgZ2V0IHZhbGlkYXRpb25FcnJvciA9PiAndmFsaWRhdGlvbl9lcnJvcic7Cn0KCmNsYXNzIF9BcHBFdmVudHMgewogIGNvbnN0IF9BcHBFdmVudHMoKTsKCiAgLy8vIEFwcCBvcGVuZWQvbGF1bmNoZWQKICBTdHJpbmcgZ2V0IG9wZW4gPT4gJ2FwcF9vcGVuJzsKCiAgLy8vIEFwcCB3ZW50IHRvIGJhY2tncm91bmQKICBTdHJpbmcgZ2V0IGJhY2tncm91bmQgPT4gJ2FwcF9iYWNrZ3JvdW5kJzsKCiAgLy8vIEFwcCBjYW1lIHRvIGZvcmVncm91bmQKICBTdHJpbmcgZ2V0IGZvcmVncm91bmQgPT4gJ2FwcF9mb3JlZ3JvdW5kJzsKCiAgLy8vIEFwcCB1cGRhdGVkIHRvIG5ldyB2ZXJzaW9uCiAgU3RyaW5nIGdldCB1cGRhdGVkID0+ICdhcHBfdXBkYXRlZCc7CgogIC8vLyBBcHAgaW5zdGFsbGVkIGZvciBmaXJzdCB0aW1lCiAgU3RyaW5nIGdldCBpbnN0YWxsZWQgPT4gJ2FwcF9pbnN0YWxsZWQnOwoKICAvLy8gQXBwIHVuaW5zdGFsbGVkCiAgU3RyaW5nIGdldCB1bmluc3RhbGxlZCA9PiAnYXBwX3VuaW5zdGFsbGVkJzsKfQoKCgpjbGFzcyBfUHJvZmlsZUV2ZW50cyB7CiAgY29uc3QgX1Byb2ZpbGVFdmVudHMoKTsKCiAgLy8vIFByb2ZpbGUgdmlld2VkCiAgU3RyaW5nIGdldCB2aWV3ZWQgPT4gJ3Byb2ZpbGVfdmlld2VkJzsKCiAgLy8vIFByb2ZpbGUgZWRpdGVkCiAgU3RyaW5nIGdldCBlZGl0ZWQgPT4gJ3Byb2ZpbGVfZWRpdGVkJzsKCiAgLy8vIFByb2ZpbGUgcGhvdG8gdXBkYXRlZAogIFN0cmluZyBnZXQgcGhvdG9VcGRhdGVkID0+ICdwcm9maWxlX3Bob3RvX3VwZGF0ZWQnOwoKICAvLy8gUHJvZmlsZSBzZXR0aW5ncyBjaGFuZ2VkCiAgU3RyaW5nIGdldCBzZXR0aW5nc0NoYW5nZWQgPT4gJ3Byb2ZpbGVfc2V0dGluZ3NfY2hhbmdlZCc7CgogIC8vLyBQcm9maWxlIHByaXZhY3kgc2V0dGluZ3MgdXBkYXRlZAogIFN0cmluZyBnZXQgcHJpdmFjeVVwZGF0ZWQgPT4gJ3Byb2ZpbGVfcHJpdmFjeV91cGRhdGVkJzsKfQo=", "type": "text" }, { @@ -858,7 +858,7 @@ final flutterProjectWithMonoRepoBundle = MasonBundle.fromJson({ "path": "{{ name.snakeCase() }}/packages/{{analytics_dir}}/lib/src/utils/analytics_helper.dart", "data": - "aW1wb3J0ICdwYWNrYWdlOmFuYWx5dGljcy9zcmMvY2xpZW50L2FuYWx5dGljc19jbGllbnQuZGFydCc7CmltcG9ydCAncGFja2FnZTphbmFseXRpY3Mvc3JjL21vZGVscy9hbmFseXRpY3NfZXZlbnQuZGFydCc7CmltcG9ydCAncGFja2FnZTphbmFseXRpY3Mvc3JjL21vZGVscy9hbmFseXRpY3NfZXZlbnRzLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Y29yZS9jb3JlLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZGkvZGkuZGFydCc7CgpjbGFzcyBBbmFseXRpY3NIZWxwZXIgewogIHN0YXRpYyBBbmFseXRpY3NDbGllbnQ/IF9jbGllbnQ7CiAgc3RhdGljIExvZ2dlcj8gX2xvZ2dlcjsKCiAgc3RhdGljIEFuYWx5dGljc0NsaWVudD8gZ2V0IF9hbmFseXRpY3NDbGllbnQgewogICAgX2NsaWVudCA/Pz0gX2dldEFuYWx5dGljc0NsaWVudCgpOwogICAgcmV0dXJuIF9jbGllbnQ7CiAgfQoKICBzdGF0aWMgTG9nZ2VyIGdldCBfbG9nIHsKICAgIF9sb2dnZXIgPz89IGRpLmdldDxMb2dnZXI+KCk7CiAgICByZXR1cm4gX2xvZ2dlciE7CiAgfQoKICBzdGF0aWMgQW5hbHl0aWNzQ2xpZW50PyBfZ2V0QW5hbHl0aWNzQ2xpZW50KCkgewogICAgdHJ5IHsKICAgICAgcmV0dXJuIGRpLmdldDxBbmFseXRpY3NDbGllbnQ+KCk7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIF9sb2cudygnQW5hbHl0aWNzIGNsaWVudCBub3QgYXZhaWxhYmxlOiAkZScpOwogICAgICByZXR1cm4gbnVsbDsKICAgIH0KICB9CgogIC8vLyBMb2cgYSBjdXN0b20gZXZlbnQgd2l0aCBvcHRpb25hbCBwYXJhbWV0ZXJzCiAgc3RhdGljIEZ1dHVyZTx2b2lkPiBsb2dFdmVudCgKICAgIFN0cmluZyBuYW1lLCB7CiAgICBNYXA8U3RyaW5nLCBkeW5hbWljPj8gcGFyYW1ldGVycywKICB9KSBhc3luYyB7CiAgICBmaW5hbCBjbGllbnQgPSBfYW5hbHl0aWNzQ2xpZW50OwogICAgaWYgKGNsaWVudCA9PSBudWxsKSByZXR1cm47CgogICAgdHJ5IHsKICAgICAgYXdhaXQgY2xpZW50LmxvZ0V2ZW50KG5hbWU6IG5hbWUsIHBhcmFtZXRlcnM6IHBhcmFtZXRlcnMpOwogICAgfSBjYXRjaCAoZSwgcykgewogICAgICBfbG9nLmUoJ0ZhaWxlZCB0byBsb2cgYW5hbHl0aWNzIGV2ZW50OiAkbmFtZScsIGUsIHMpOwogICAgfQogIH0KCiAgLy8vIExvZyB1c2VyIHNpZ24gaW4KICBzdGF0aWMgRnV0dXJlPHZvaWQ+IGxvZ1NpZ25Jbih7CiAgICBTdHJpbmc/IG1ldGhvZCwKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBwYXJhbWV0ZXJzLAogIH0pIGFzeW5jIHsKICAgIGZpbmFsIGNsaWVudCA9IF9hbmFseXRpY3NDbGllbnQ7CiAgICBpZiAoY2xpZW50ID09IG51bGwpIHJldHVybjsKCiAgICB0cnkgewogICAgICBhd2FpdCBjbGllbnQubG9nTG9naW4obG9naW5NZXRob2Q6IG1ldGhvZCwgcGFyYW1ldGVyczogcGFyYW1ldGVycyk7CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2cuZSgnRmFpbGVkIHRvIGxvZyBzaWduIGluIGV2ZW50JywgZSwgcyk7CiAgICB9CiAgfQoKICAvLy8gTG9nIHVzZXIgc2lnbiB1cAogIHN0YXRpYyBGdXR1cmU8dm9pZD4gbG9nU2lnblVwKHsKICAgIFN0cmluZz8gbWV0aG9kLAogICAgTWFwPFN0cmluZywgZHluYW1pYz4/IHBhcmFtZXRlcnMsCiAgfSkgYXN5bmMgewogICAgZmluYWwgY2xpZW50ID0gX2FuYWx5dGljc0NsaWVudDsKICAgIGlmIChjbGllbnQgPT0gbnVsbCkgcmV0dXJuOwoKICAgIHRyeSB7CiAgICAgIGF3YWl0IGNsaWVudC5sb2dTaWduVXAoc2lnblVwTWV0aG9kOiBtZXRob2QsIHBhcmFtZXRlcnM6IHBhcmFtZXRlcnMpOwogICAgfSBjYXRjaCAoZSwgcykgewogICAgICBfbG9nLmUoJ0ZhaWxlZCB0byBsb2cgc2lnbiB1cCBldmVudCcsIGUsIHMpOwogICAgfQogIH0KCiAgLy8vIFNldCB1c2VyIElEIGZvciBhbmFseXRpY3MKICBzdGF0aWMgRnV0dXJlPHZvaWQ+IHNldFVzZXJJZChTdHJpbmc/IHVzZXJJZCkgYXN5bmMgewogICAgZmluYWwgY2xpZW50ID0gX2FuYWx5dGljc0NsaWVudDsKICAgIGlmIChjbGllbnQgPT0gbnVsbCkgcmV0dXJuOwoKICAgIHRyeSB7CiAgICAgIGF3YWl0IGNsaWVudC5zZXRVc2VySWQodXNlcklkKTsKICAgIH0gY2F0Y2ggKGUsIHMpIHsKICAgICAgX2xvZy5lKCdGYWlsZWQgdG8gc2V0IGFuYWx5dGljcyB1c2VyIElEJywgZSwgcyk7CiAgICB9CiAgfQoKICAvLy8gU2V0IHVzZXIgcHJvcGVydHkKICBzdGF0aWMgRnV0dXJlPHZvaWQ+IHNldFVzZXJQcm9wZXJ0eSh7CiAgICByZXF1aXJlZCBTdHJpbmcgbmFtZSwKICAgIHJlcXVpcmVkIFN0cmluZz8gdmFsdWUsCiAgfSkgYXN5bmMgewogICAgZmluYWwgY2xpZW50ID0gX2FuYWx5dGljc0NsaWVudDsKICAgIGlmIChjbGllbnQgPT0gbnVsbCkgcmV0dXJuOwoKICAgIHRyeSB7CiAgICAgIGF3YWl0IGNsaWVudC5zZXRVc2VyUHJvcGVydHkobmFtZTogbmFtZSwgdmFsdWU6IHZhbHVlKTsKICAgIH0gY2F0Y2ggKGUsIHMpIHsKICAgICAgX2xvZy5lKCdGYWlsZWQgdG8gc2V0IGFuYWx5dGljcyB1c2VyIHByb3BlcnR5OiAkbmFtZScsIGUsIHMpOwogICAgfQogIH0KCiAgLy8vIExvZyBhcHAgb3BlbiBldmVudAogIHN0YXRpYyBGdXR1cmU8dm9pZD4gbG9nQXBwT3Blbih7TWFwPFN0cmluZywgZHluYW1pYz4/IHBhcmFtZXRlcnN9KSBhc3luYyB7CiAgICBmaW5hbCBjbGllbnQgPSBfYW5hbHl0aWNzQ2xpZW50OwogICAgaWYgKGNsaWVudCA9PSBudWxsKSByZXR1cm47CgogICAgdHJ5IHsKICAgICAgYXdhaXQgY2xpZW50LmxvZ0FwcE9wZW4ocGFyYW1ldGVyczogcGFyYW1ldGVycyk7CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2cuZSgnRmFpbGVkIHRvIGxvZyBhcHAgb3BlbiBldmVudCcsIGUsIHMpOwogICAgfQogIH0KCiAgLy8vIExvZyBjdXN0b20gYW5hbHl0aWNzIGV2ZW50CiAgc3RhdGljIEZ1dHVyZTx2b2lkPiBsb2dDdXN0b21FdmVudChBbmFseXRpY3NFdmVudCBldmVudCkgYXN5bmMgewogICAgZmluYWwgY2xpZW50ID0gX2FuYWx5dGljc0NsaWVudDsKICAgIGlmIChjbGllbnQgPT0gbnVsbCkgcmV0dXJuOwoKICAgIHRyeSB7CiAgICAgIGF3YWl0IGNsaWVudC5sb2dDdXN0b21FdmVudChldmVudCk7CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2cuZSgnRmFpbGVkIHRvIGxvZyBjdXN0b20gYW5hbHl0aWNzIGV2ZW50OiAke2V2ZW50Lm5hbWV9JywgZSwgcyk7CiAgICB9CiAgfQoKICAvLy8gQ29tbW9uIGJ1c2luZXNzIGV2ZW50cwogIHN0YXRpYyBGdXR1cmU8dm9pZD4gbG9nRmVhdHVyZVVzZWQoCiAgICBTdHJpbmcgZmVhdHVyZU5hbWUsIHsKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBwYXJhbWV0ZXJzLAogIH0pIGFzeW5jIHsKICAgIGF3YWl0IGxvZ0V2ZW50KAogICAgICBBbmFseXRpY3NFdmVudHMuZmVhdHVyZS51c2VkLAogICAgICBwYXJhbWV0ZXJzOiB7CiAgICAgICAgJ2ZlYXR1cmVfbmFtZSc6IGZlYXR1cmVOYW1lLAogICAgICAgICd0aW1lc3RhbXAnOiBEYXRlVGltZS5ub3coKS50b0lzbzg2MDFTdHJpbmcoKSwKICAgICAgICAuLi4/cGFyYW1ldGVycywKICAgICAgfSwKICAgICk7CiAgfQoKICBzdGF0aWMgRnV0dXJlPHZvaWQ+IGxvZ0J1dHRvblByZXNzZWQoCiAgICBTdHJpbmcgYnV0dG9uTmFtZSwgewogICAgU3RyaW5nPyBzY3JlZW5OYW1lLAogICAgTWFwPFN0cmluZywgZHluYW1pYz4/IHBhcmFtZXRlcnMsCiAgfSkgYXN5bmMgewogICAgYXdhaXQgbG9nRXZlbnQoCiAgICAgIEFuYWx5dGljc0V2ZW50cy5mZWF0dXJlLmJ1dHRvblByZXNzZWQsCiAgICAgIHBhcmFtZXRlcnM6IHsKICAgICAgICAnYnV0dG9uX25hbWUnOiBidXR0b25OYW1lLAogICAgICAgIGlmIChzY3JlZW5OYW1lICE9IG51bGwpICdzY3JlZW5fbmFtZSc6IHNjcmVlbk5hbWUsCiAgICAgICAgJ3RpbWVzdGFtcCc6IERhdGVUaW1lLm5vdygpLnRvSXNvODYwMVN0cmluZygpLAogICAgICAgIC4uLj9wYXJhbWV0ZXJzLAogICAgICB9LAogICAgKTsKICB9CgogIHN0YXRpYyBGdXR1cmU8dm9pZD4gbG9nQXBwRXJyb3IoCiAgICBTdHJpbmcgZXJyb3JUeXBlLCB7CiAgICBTdHJpbmc/IGVycm9yTWVzc2FnZSwKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBwYXJhbWV0ZXJzLAogIH0pIGFzeW5jIHsKICAgIGF3YWl0IGxvZ0V2ZW50KAogICAgICBBbmFseXRpY3NFdmVudHMuZXJyb3IuYXBwRXJyb3IsCiAgICAgIHBhcmFtZXRlcnM6IHsKICAgICAgICAnZXJyb3JfdHlwZSc6IGVycm9yVHlwZSwKICAgICAgICBpZiAoZXJyb3JNZXNzYWdlICE9IG51bGwpICdlcnJvcl9tZXNzYWdlJzogZXJyb3JNZXNzYWdlLAogICAgICAgICd0aW1lc3RhbXAnOiBEYXRlVGltZS5ub3coKS50b0lzbzg2MDFTdHJpbmcoKSwKICAgICAgICAuLi4/cGFyYW1ldGVycywKICAgICAgfSwKICAgICk7CiAgfQoKICAvLy8gQWRkaXRpb25hbCBzdHJ1Y3R1cmVkIGV2ZW50IGhlbHBlcnMKICBzdGF0aWMgRnV0dXJlPHZvaWQ+IGxvZ1N1cnZleVN0YXJ0ZWQoCiAgICBTdHJpbmcgc3VydmV5SWQsIHsKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBwYXJhbWV0ZXJzLAogIH0pIGFzeW5jIHsKICAgIGF3YWl0IGxvZ0V2ZW50KAogICAgICBBbmFseXRpY3NFdmVudHMuc3VydmV5LnN0YXJ0ZWQsCiAgICAgIHBhcmFtZXRlcnM6IHsKICAgICAgICAnc3VydmV5X2lkJzogc3VydmV5SWQsCiAgICAgICAgJ3RpbWVzdGFtcCc6IERhdGVUaW1lLm5vdygpLnRvSXNvODYwMVN0cmluZygpLAogICAgICAgIC4uLj9wYXJhbWV0ZXJzLAogICAgICB9LAogICAgKTsKICB9CgogIHN0YXRpYyBGdXR1cmU8dm9pZD4gbG9nU3VydmV5Q29tcGxldGVkKAogICAgU3RyaW5nIHN1cnZleUlkLCB7CiAgICBpbnQ/IGR1cmF0aW9uLAogICAgTWFwPFN0cmluZywgZHluYW1pYz4/IHBhcmFtZXRlcnMsCiAgfSkgYXN5bmMgewogICAgYXdhaXQgbG9nRXZlbnQoCiAgICAgIEFuYWx5dGljc0V2ZW50cy5zdXJ2ZXkuY29tcGxldGVkLAogICAgICBwYXJhbWV0ZXJzOiB7CiAgICAgICAgJ3N1cnZleV9pZCc6IHN1cnZleUlkLAogICAgICAgIGlmIChkdXJhdGlvbiAhPSBudWxsKSAnZHVyYXRpb25fc2Vjb25kcyc6IGR1cmF0aW9uLAogICAgICAgICd0aW1lc3RhbXAnOiBEYXRlVGltZS5ub3coKS50b0lzbzg2MDFTdHJpbmcoKSwKICAgICAgICAuLi4/cGFyYW1ldGVycywKICAgICAgfSwKICAgICk7CiAgfQp9Cg==", + "aW1wb3J0ICdwYWNrYWdlOmFuYWx5dGljcy9zcmMvY2xpZW50L2FuYWx5dGljc19jbGllbnQuZGFydCc7CmltcG9ydCAncGFja2FnZTphbmFseXRpY3Mvc3JjL21vZGVscy9hbmFseXRpY3NfZXZlbnQuZGFydCc7CmltcG9ydCAncGFja2FnZTphbmFseXRpY3Mvc3JjL21vZGVscy9hbmFseXRpY3NfZXZlbnRzLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6Y29yZS9jb3JlLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZGkvZGkuZGFydCc7CgpjbGFzcyBBbmFseXRpY3NIZWxwZXIgewogIHN0YXRpYyBBbmFseXRpY3NDbGllbnQ/IF9jbGllbnQ7CiAgc3RhdGljIExvZ2dlcj8gX2xvZ2dlcjsKCiAgc3RhdGljIEFuYWx5dGljc0NsaWVudD8gZ2V0IF9hbmFseXRpY3NDbGllbnQgewogICAgX2NsaWVudCA/Pz0gX2dldEFuYWx5dGljc0NsaWVudCgpOwogICAgcmV0dXJuIF9jbGllbnQ7CiAgfQoKICBzdGF0aWMgTG9nZ2VyIGdldCBfbG9nIHsKICAgIF9sb2dnZXIgPz89IGRpLmdldDxMb2dnZXI+KCk7CiAgICByZXR1cm4gX2xvZ2dlciE7CiAgfQoKICBzdGF0aWMgQW5hbHl0aWNzQ2xpZW50PyBfZ2V0QW5hbHl0aWNzQ2xpZW50KCkgewogICAgdHJ5IHsKICAgICAgcmV0dXJuIGRpLmdldDxBbmFseXRpY3NDbGllbnQ+KCk7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIF9sb2cudygnQW5hbHl0aWNzIGNsaWVudCBub3QgYXZhaWxhYmxlOiAkZScpOwogICAgICByZXR1cm4gbnVsbDsKICAgIH0KICB9CgogIC8vLyBMb2cgYSBjdXN0b20gZXZlbnQgd2l0aCBvcHRpb25hbCBwYXJhbWV0ZXJzCiAgc3RhdGljIEZ1dHVyZTx2b2lkPiBsb2dFdmVudCgKICAgIFN0cmluZyBuYW1lLCB7CiAgICBNYXA8U3RyaW5nLCBkeW5hbWljPj8gcGFyYW1ldGVycywKICB9KSBhc3luYyB7CiAgICBmaW5hbCBjbGllbnQgPSBfYW5hbHl0aWNzQ2xpZW50OwogICAgaWYgKGNsaWVudCA9PSBudWxsKSByZXR1cm47CgogICAgdHJ5IHsKICAgICAgYXdhaXQgY2xpZW50LmxvZ0V2ZW50KG5hbWU6IG5hbWUsIHBhcmFtZXRlcnM6IHBhcmFtZXRlcnMpOwogICAgfSBjYXRjaCAoZSwgcykgewogICAgICBfbG9nLmUoJ0ZhaWxlZCB0byBsb2cgYW5hbHl0aWNzIGV2ZW50OiAkbmFtZScsIGUsIHMpOwogICAgfQogIH0KCiAgLy8vIExvZyB1c2VyIHNpZ24gaW4KICBzdGF0aWMgRnV0dXJlPHZvaWQ+IGxvZ1NpZ25Jbih7CiAgICBTdHJpbmc/IG1ldGhvZCwKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBwYXJhbWV0ZXJzLAogIH0pIGFzeW5jIHsKICAgIGZpbmFsIGNsaWVudCA9IF9hbmFseXRpY3NDbGllbnQ7CiAgICBpZiAoY2xpZW50ID09IG51bGwpIHJldHVybjsKCiAgICB0cnkgewogICAgICBhd2FpdCBjbGllbnQubG9nTG9naW4obG9naW5NZXRob2Q6IG1ldGhvZCwgcGFyYW1ldGVyczogcGFyYW1ldGVycyk7CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2cuZSgnRmFpbGVkIHRvIGxvZyBzaWduIGluIGV2ZW50JywgZSwgcyk7CiAgICB9CiAgfQoKICAvLy8gTG9nIHVzZXIgc2lnbiB1cAogIHN0YXRpYyBGdXR1cmU8dm9pZD4gbG9nU2lnblVwKHsKICAgIFN0cmluZz8gbWV0aG9kLAogICAgTWFwPFN0cmluZywgZHluYW1pYz4/IHBhcmFtZXRlcnMsCiAgfSkgYXN5bmMgewogICAgZmluYWwgY2xpZW50ID0gX2FuYWx5dGljc0NsaWVudDsKICAgIGlmIChjbGllbnQgPT0gbnVsbCkgcmV0dXJuOwoKICAgIHRyeSB7CiAgICAgIGF3YWl0IGNsaWVudC5sb2dTaWduVXAoc2lnblVwTWV0aG9kOiBtZXRob2QsIHBhcmFtZXRlcnM6IHBhcmFtZXRlcnMpOwogICAgfSBjYXRjaCAoZSwgcykgewogICAgICBfbG9nLmUoJ0ZhaWxlZCB0byBsb2cgc2lnbiB1cCBldmVudCcsIGUsIHMpOwogICAgfQogIH0KCiAgLy8vIFNldCB1c2VyIElEIGZvciBhbmFseXRpY3MKICBzdGF0aWMgRnV0dXJlPHZvaWQ+IHNldFVzZXJJZChTdHJpbmc/IHVzZXJJZCkgYXN5bmMgewogICAgZmluYWwgY2xpZW50ID0gX2FuYWx5dGljc0NsaWVudDsKICAgIGlmIChjbGllbnQgPT0gbnVsbCkgcmV0dXJuOwoKICAgIHRyeSB7CiAgICAgIGF3YWl0IGNsaWVudC5zZXRVc2VySWQodXNlcklkKTsKICAgIH0gY2F0Y2ggKGUsIHMpIHsKICAgICAgX2xvZy5lKCdGYWlsZWQgdG8gc2V0IGFuYWx5dGljcyB1c2VyIElEJywgZSwgcyk7CiAgICB9CiAgfQoKICAvLy8gU2V0IHVzZXIgcHJvcGVydHkKICBzdGF0aWMgRnV0dXJlPHZvaWQ+IHNldFVzZXJQcm9wZXJ0eSh7CiAgICByZXF1aXJlZCBTdHJpbmcgbmFtZSwKICAgIHJlcXVpcmVkIFN0cmluZz8gdmFsdWUsCiAgfSkgYXN5bmMgewogICAgZmluYWwgY2xpZW50ID0gX2FuYWx5dGljc0NsaWVudDsKICAgIGlmIChjbGllbnQgPT0gbnVsbCkgcmV0dXJuOwoKICAgIHRyeSB7CiAgICAgIGF3YWl0IGNsaWVudC5zZXRVc2VyUHJvcGVydHkobmFtZTogbmFtZSwgdmFsdWU6IHZhbHVlKTsKICAgIH0gY2F0Y2ggKGUsIHMpIHsKICAgICAgX2xvZy5lKCdGYWlsZWQgdG8gc2V0IGFuYWx5dGljcyB1c2VyIHByb3BlcnR5OiAkbmFtZScsIGUsIHMpOwogICAgfQogIH0KCiAgLy8vIExvZyBhcHAgb3BlbiBldmVudAogIHN0YXRpYyBGdXR1cmU8dm9pZD4gbG9nQXBwT3Blbih7TWFwPFN0cmluZywgZHluYW1pYz4/IHBhcmFtZXRlcnN9KSBhc3luYyB7CiAgICBmaW5hbCBjbGllbnQgPSBfYW5hbHl0aWNzQ2xpZW50OwogICAgaWYgKGNsaWVudCA9PSBudWxsKSByZXR1cm47CgogICAgdHJ5IHsKICAgICAgYXdhaXQgY2xpZW50LmxvZ0FwcE9wZW4ocGFyYW1ldGVyczogcGFyYW1ldGVycyk7CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2cuZSgnRmFpbGVkIHRvIGxvZyBhcHAgb3BlbiBldmVudCcsIGUsIHMpOwogICAgfQogIH0KCiAgLy8vIExvZyBjdXN0b20gYW5hbHl0aWNzIGV2ZW50CiAgc3RhdGljIEZ1dHVyZTx2b2lkPiBsb2dDdXN0b21FdmVudChBbmFseXRpY3NFdmVudCBldmVudCkgYXN5bmMgewogICAgZmluYWwgY2xpZW50ID0gX2FuYWx5dGljc0NsaWVudDsKICAgIGlmIChjbGllbnQgPT0gbnVsbCkgcmV0dXJuOwoKICAgIHRyeSB7CiAgICAgIGF3YWl0IGNsaWVudC5sb2dDdXN0b21FdmVudChldmVudCk7CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2cuZSgnRmFpbGVkIHRvIGxvZyBjdXN0b20gYW5hbHl0aWNzIGV2ZW50OiAke2V2ZW50Lm5hbWV9JywgZSwgcyk7CiAgICB9CiAgfQoKICAvLy8gQ29tbW9uIGJ1c2luZXNzIGV2ZW50cwogIHN0YXRpYyBGdXR1cmU8dm9pZD4gbG9nRmVhdHVyZVVzZWQoCiAgICBTdHJpbmcgZmVhdHVyZU5hbWUsIHsKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBwYXJhbWV0ZXJzLAogIH0pIGFzeW5jIHsKICAgIGF3YWl0IGxvZ0V2ZW50KAogICAgICBBbmFseXRpY3NFdmVudHMuZmVhdHVyZS51c2VkLAogICAgICBwYXJhbWV0ZXJzOiB7CiAgICAgICAgJ2ZlYXR1cmVfbmFtZSc6IGZlYXR1cmVOYW1lLAogICAgICAgICd0aW1lc3RhbXAnOiBEYXRlVGltZS5ub3coKS50b0lzbzg2MDFTdHJpbmcoKSwKICAgICAgICAuLi4/cGFyYW1ldGVycywKICAgICAgfSwKICAgICk7CiAgfQoKICBzdGF0aWMgRnV0dXJlPHZvaWQ+IGxvZ0J1dHRvblByZXNzZWQoCiAgICBTdHJpbmcgYnV0dG9uTmFtZSwgewogICAgU3RyaW5nPyBzY3JlZW5OYW1lLAogICAgTWFwPFN0cmluZywgZHluYW1pYz4/IHBhcmFtZXRlcnMsCiAgfSkgYXN5bmMgewogICAgYXdhaXQgbG9nRXZlbnQoCiAgICAgIEFuYWx5dGljc0V2ZW50cy5mZWF0dXJlLmJ1dHRvblByZXNzZWQsCiAgICAgIHBhcmFtZXRlcnM6IHsKICAgICAgICAnYnV0dG9uX25hbWUnOiBidXR0b25OYW1lLAogICAgICAgIGlmIChzY3JlZW5OYW1lICE9IG51bGwpICdzY3JlZW5fbmFtZSc6IHNjcmVlbk5hbWUsCiAgICAgICAgJ3RpbWVzdGFtcCc6IERhdGVUaW1lLm5vdygpLnRvSXNvODYwMVN0cmluZygpLAogICAgICAgIC4uLj9wYXJhbWV0ZXJzLAogICAgICB9LAogICAgKTsKICB9CgogIHN0YXRpYyBGdXR1cmU8dm9pZD4gbG9nQXBwRXJyb3IoCiAgICBTdHJpbmcgZXJyb3JUeXBlLCB7CiAgICBTdHJpbmc/IGVycm9yTWVzc2FnZSwKICAgIE1hcDxTdHJpbmcsIGR5bmFtaWM+PyBwYXJhbWV0ZXJzLAogIH0pIGFzeW5jIHsKICAgIGF3YWl0IGxvZ0V2ZW50KAogICAgICBBbmFseXRpY3NFdmVudHMuZXJyb3IuYXBwRXJyb3IsCiAgICAgIHBhcmFtZXRlcnM6IHsKICAgICAgICAnZXJyb3JfdHlwZSc6IGVycm9yVHlwZSwKICAgICAgICBpZiAoZXJyb3JNZXNzYWdlICE9IG51bGwpICdlcnJvcl9tZXNzYWdlJzogZXJyb3JNZXNzYWdlLAogICAgICAgICd0aW1lc3RhbXAnOiBEYXRlVGltZS5ub3coKS50b0lzbzg2MDFTdHJpbmcoKSwKICAgICAgICAuLi4/cGFyYW1ldGVycywKICAgICAgfSwKICAgICk7CiAgfQoKfQo=", "type": "text" }, { @@ -876,7 +876,7 @@ final flutterProjectWithMonoRepoBundle = MasonBundle.fromJson({ { "path": "{{ name.snakeCase() }}/packages/{{analytics_dir}}/README.md", "data": - "IyBBbmFseXRpY3MgUGFja2FnZQoKQSBzdHJ1Y3R1cmVkIGFuYWx5dGljcyBwYWNrYWdlIGZvciB7e25hbWUudGl0bGVDYXNlKCl9fSB3aXRoIEZpcmViYXNlIEFuYWx5dGljcyBpbnRlZ3JhdGlvbiBhbmQgcGx1Z2dhYmxlIHByb3ZpZGVyIGFyY2hpdGVjdHVyZS4KCiMjIEZlYXR1cmVzCgotIOKchSAqKkludGVyZmFjZS1iYXNlZCBkZXNpZ24qKiAtIEVhc3kgdG8gc3dpdGNoIGFuYWx5dGljcyBwcm92aWRlcnMKLSDinIUgKipBdXRvbWF0aWMgc2NyZWVuIHRyYWNraW5nKiogLSBSb3V0ZSBvYnNlcnZlciB0cmFja3MgbmF2aWdhdGlvbiBhdXRvbWF0aWNhbGx5Ci0g4pyFICoqU3RydWN0dXJlZCBldmVudCBuYW1lcyoqIC0gVHlwZS1zYWZlLCBvcmdhbml6ZWQgZXZlbnQgY29uc3RhbnRzCi0g4pyFICoqRGVwZW5kZW5jeSBpbmplY3Rpb24qKiAtIEludGVncmF0ZWQgd2l0aCBESSBjb250YWluZXIKLSDinIUgKipFcnJvciBoYW5kbGluZyoqIC0gR3JhY2VmdWwgZmFpbHVyZXMgZG9uJ3QgY3Jhc2ggdGhlIGFwcAotIOKchSAqKkRlYnVnIGxvZ2dpbmcqKiAtIENvbmZpZ3VyYWJsZSBsb2dnaW5nIGZvciBkZXZlbG9wbWVudAoKIyMgUXVpY2sgU3RhcnQKCiMjIyAxLiBJbml0aWFsaXplIEFuYWx5dGljcwoKYGBgZGFydAppbXBvcnQgJ3BhY2thZ2U6YW5hbHl0aWNzL2FuYWx5dGljcy5kYXJ0JzsKCmZpbmFsIGFuYWx5dGljc0NvbmZpZyA9IERlZmF1bHRBbmFseXRpY3NDb25maWcoCiAgZW5hYmxlQW5hbHl0aWNzOiB0cnVlLAogIGVuYWJsZURlYnVnTG9nZ2luZzogdHJ1ZSwgLy8gU2V0IHRvIGZhbHNlIGluIHByb2R1Y3Rpb24KKTsKYXdhaXQgaW5pdChjb25maWc6IGFuYWx5dGljc0NvbmZpZyk7CmBgYAoKIyMjIDIuIEF1dG9tYXRpYyBTY3JlZW4gVHJhY2tpbmcKCkFkZCB0aGUgYEFuYWx5dGljc1JvdXRlT2JzZXJ2ZXJgIHRvIHlvdXIgcm91dGVyOgoKYGBgZGFydApHb1JvdXRlcigKICBvYnNlcnZlcnM6IFsKICAgIEFuYWx5dGljc1JvdXRlT2JzZXJ2ZXIoKSwgLy8gVXNlcyBGaXJlYmFzZUFuYWx5dGljc09ic2VydmVyIGludGVybmFsbHkKICBdLAogIC8vIC4uLiByb3V0ZXMKKTsKYGBgCgpUaGUgYEFuYWx5dGljc1JvdXRlT2JzZXJ2ZXJgIGludGVybmFsbHkgdXNlcyBGaXJlYmFzZSdzIG5hdGl2ZSBgRmlyZWJhc2VBbmFseXRpY3NPYnNlcnZlcmAgZm9yIHByb3BlciBzY3JlZW4gdHJhY2tpbmcgd2hpbGUgYWRkaW5nIGFkZGl0aW9uYWwgbG9nZ2luZyBhbmQgZXJyb3IgaGFuZGxpbmcuCgojIyMgMy4gTWFudWFsIEV2ZW50IFRyYWNraW5nCgojIyMjIFVzaW5nIFN0cnVjdHVyZWQgRXZlbnQgTmFtZXMgKFJlY29tbWVuZGVkKQoKYGBgZGFydAovLyBVc2VyIGV2ZW50cwphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nRXZlbnQoQW5hbHl0aWNzRXZlbnRzLnVzZXIuYXV0aGVudGljYXRlZFJlZGlyZWN0KTsKCi8vIEF1dGhlbnRpY2F0aW9uIGV2ZW50cwphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nRXZlbnQoCiAgQW5hbHl0aWNzRXZlbnRzLmF1dGguc2lnbkluLAogIHBhcmFtZXRlcnM6IHsnbWV0aG9kJzogJ2dvb2dsZSd9LAopOwoKLy8gRmVhdHVyZSB1c2FnZQphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nRXZlbnQoCiAgQW5hbHl0aWNzRXZlbnRzLmZlYXR1cmUudXNlZCwKICBwYXJhbWV0ZXJzOiB7J2ZlYXR1cmVfbmFtZSc6ICdoZWFsdGhfc3luYyd9LAopOwoKLy8gU3VydmV5IGV2ZW50cwphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nRXZlbnQoCiAgQW5hbHl0aWNzRXZlbnRzLnN1cnZleS5jb21wbGV0ZWQsCiAgcGFyYW1ldGVyczogewogICAgJ3N1cnZleV9pZCc6ICd3ZWxsbmVzc19hc3Nlc3NtZW50JywKICAgICdkdXJhdGlvbl9zZWNvbmRzJzogMTIwLAogIH0sCik7CmBgYAoKIyMjIyBVc2luZyBIZWxwZXIgTWV0aG9kcwoKYGBgZGFydAovLyBGZWF0dXJlIHRyYWNraW5nCmF3YWl0IEFuYWx5dGljc0hlbHBlci5sb2dGZWF0dXJlVXNlZCgnaGVhbHRoX2Rhc2hib2FyZCcpOwoKLy8gU3VydmV5IHRyYWNraW5nCmF3YWl0IEFuYWx5dGljc0hlbHBlci5sb2dTdXJ2ZXlTdGFydGVkKCdtb29kX3RyYWNrZXInKTsKYXdhaXQgQW5hbHl0aWNzSGVscGVyLmxvZ1N1cnZleUNvbXBsZXRlZCgnbW9vZF90cmFja2VyJywgZHVyYXRpb246IDQ1KTsKCi8vIEVycm9yIHRyYWNraW5nCmF3YWl0IEFuYWx5dGljc0hlbHBlci5sb2dBcHBFcnJvcignbmV0d29ya190aW1lb3V0Jyk7CgovLyBCdXR0b24gaW50ZXJhY3Rpb25zCmF3YWl0IEFuYWx5dGljc0hlbHBlci5sb2dCdXR0b25QcmVzc2VkKCdzdGFydF9hc3Nlc3NtZW50Jyk7CgovLyBBdXRoZW50aWNhdGlvbiBldmVudHMKYXdhaXQgQW5hbHl0aWNzSGVscGVyLmxvZ1NpZ25JbihtZXRob2Q6ICdnb29nbGUnKTsKYXdhaXQgQW5hbHl0aWNzSGVscGVyLmxvZ1NpZ25VcChtZXRob2Q6ICdlbWFpbCcpOwoKLy8gVXNlciBtYW5hZ2VtZW50CmF3YWl0IEFuYWx5dGljc0hlbHBlci5zZXRVc2VySWQoJ3VzZXIxMjMnKTsKYXdhaXQgQW5hbHl0aWNzSGVscGVyLnNldFVzZXJQcm9wZXJ0eShuYW1lOiAncGxhbicsIHZhbHVlOiAncHJlbWl1bScpOwoKLy8gQXBwIGxpZmVjeWNsZQphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nQXBwT3BlbigpOwpgYGAKCiMjIyMgRGlyZWN0IENsaWVudCBVc2FnZQoKYGBgZGFydApmaW5hbCBhbmFseXRpY3NDbGllbnQgPSBkaS5nZXQ8QW5hbHl0aWNzQ2xpZW50PigpOwoKYXdhaXQgYW5hbHl0aWNzQ2xpZW50LmxvZ0V2ZW50KAogIG5hbWU6ICdjdXN0b21fZXZlbnQnLAogIHBhcmFtZXRlcnM6IHsna2V5JzogJ3ZhbHVlJ30sCik7Cgphd2FpdCBhbmFseXRpY3NDbGllbnQuc2V0VXNlcklkKCd1c2VyMTIzJyk7CmF3YWl0IGFuYWx5dGljc0NsaWVudC5zZXRVc2VyUHJvcGVydHkobmFtZTogJ3BsYW4nLCB2YWx1ZTogJ3ByZW1pdW0nKTsKYGBgCgojIyBTdHJ1Y3R1cmVkIEV2ZW50IE5hbWVzCgpUaGUgcGFja2FnZSBwcm92aWRlcyBvcmdhbml6ZWQsIHR5cGUtc2FmZSBldmVudCBuYW1lcyB0aHJvdWdoIGBBbmFseXRpY3NFdmVudHNgOgoKIyMjIEF2YWlsYWJsZSBFdmVudCBDYXRlZ29yaWVzCgpgYGBkYXJ0Ci8vIFVzZXItcmVsYXRlZCBldmVudHMKQW5hbHl0aWNzRXZlbnRzLnVzZXIuYXV0aGVudGljYXRlZFJlZGlyZWN0CkFuYWx5dGljc0V2ZW50cy51c2VyLnByb2ZpbGVVcGRhdGVkCkFuYWx5dGljc0V2ZW50cy51c2VyLm9uYm9hcmRpbmdDb21wbGV0ZWQKCi8vIEF1dGhlbnRpY2F0aW9uIGV2ZW50cwpBbmFseXRpY3NFdmVudHMuYXV0aC5zaWduSW4KQW5hbHl0aWNzRXZlbnRzLmF1dGguc2lnblVwCkFuYWx5dGljc0V2ZW50cy5hdXRoLnNpZ25PdXQKCi8vIE5hdmlnYXRpb24gZXZlbnRzCkFuYWx5dGljc0V2ZW50cy5uYXZpZ2F0aW9uLnNjcmVlblZpZXcKQW5hbHl0aWNzRXZlbnRzLm5hdmlnYXRpb24udGFiU3dpdGNoZWQKCi8vIEZlYXR1cmUgdXNhZ2UKQW5hbHl0aWNzRXZlbnRzLmZlYXR1cmUudXNlZApBbmFseXRpY3NFdmVudHMuZmVhdHVyZS50dXRvcmlhbENvbXBsZXRlZAoKLy8gQXBwIGxpZmVjeWNsZQpBbmFseXRpY3NFdmVudHMuYXBwLm9wZW4KQW5hbHl0aWNzRXZlbnRzLmFwcC5iYWNrZ3JvdW5kCkFuYWx5dGljc0V2ZW50cy5hcHAuZm9yZWdyb3VuZAoKLy8gU3VydmV5IGV2ZW50cwpBbmFseXRpY3NFdmVudHMuc3VydmV5LnN0YXJ0ZWQKQW5hbHl0aWNzRXZlbnRzLnN1cnZleS5jb21wbGV0ZWQKQW5hbHl0aWNzRXZlbnRzLnN1cnZleS5hYmFuZG9uZWQKCi8vIEhlYWx0aCBkYXRhIGV2ZW50cwpBbmFseXRpY3NFdmVudHMuaGVhbHRoLmRhdGFTeW5jZWQKQW5hbHl0aWNzRXZlbnRzLmhlYWx0aC5kZXZpY2VDb25uZWN0ZWQKQW5hbHl0aWNzRXZlbnRzLmhlYWx0aC5nb2FsQWNoaWV2ZWQKCi8vIEVycm9yIGV2ZW50cwpBbmFseXRpY3NFdmVudHMuZXJyb3IuYXBwRXJyb3IKQW5hbHl0aWNzRXZlbnRzLmVycm9yLm5ldHdvcmtFcnJvcgpgYGAKCiMjIENvbmZpZ3VyYXRpb24KCiMjIyBBbmFseXRpY3NDb25maWcgT3B0aW9ucwoKYGBgZGFydApmaW5hbCBjb25maWcgPSBEZWZhdWx0QW5hbHl0aWNzQ29uZmlnKAogIGVuYWJsZUFuYWx5dGljczogdHJ1ZSwgICAgICAgIC8vIEVuYWJsZS9kaXNhYmxlIGFuYWx5dGljcyBjb2xsZWN0aW9uCiAgZW5hYmxlRGVidWdMb2dnaW5nOiBmYWxzZSwgICAgLy8gRGVidWcgbG9nZ2luZyAoZGV2ZWxvcG1lbnQgb25seSkKICB1c2VySWQ6ICd1c2VyMTIzJywgICAgICAgICAgIC8vIE9wdGlvbmFsOiBTZXQgaW5pdGlhbCB1c2VyIElECiAgZGVmYXVsdFVzZXJQcm9wZXJ0aWVzOiB7ICAgICAgLy8gT3B0aW9uYWw6IERlZmF1bHQgdXNlciBwcm9wZXJ0aWVzCiAgICAndXNlcl90eXBlJzogJ3ByZW1pdW0nLAogICAgJ2FwcF92ZXJzaW9uJzogJzEuMC4wJywKICB9LAopOwpgYGAKCiMjIyBFbnZpcm9ubWVudC1iYXNlZCBDb25maWd1cmF0aW9uCgpgYGBkYXJ0CmltcG9ydCAncGFja2FnZTpmbHV0dGVyL2ZvdW5kYXRpb24uZGFydCc7CgpmaW5hbCBjb25maWcgPSBEZWZhdWx0QW5hbHl0aWNzQ29uZmlnKAogIGVuYWJsZUFuYWx5dGljczogIWtEZWJ1Z01vZGUsICAgICAgICAgIC8vIERpc2FibGUgaW4gZGVidWcgYnVpbGRzCiAgZW5hYmxlRGVidWdMb2dnaW5nOiBrRGVidWdNb2RlLCAgICAgICAgLy8gRGVidWcgbG9nZ2luZyBpbiBkZXZlbG9wbWVudAopOwpgYGAKCiMjIEFyY2hpdGVjdHVyZQoKIyMjIENvbXBvbmVudHMKCi0gKipBbmFseXRpY3NDbGllbnQqKiAtIEFic3RyYWN0IGludGVyZmFjZSBmb3IgYW5hbHl0aWNzIG9wZXJhdGlvbnMKLSAqKkZpcmViYXNlQW5hbHl0aWNzQ2xpZW50KiogLSBGaXJlYmFzZSBBbmFseXRpY3MgaW1wbGVtZW50YXRpb24KLSAqKkFuYWx5dGljc1JvdXRlT2JzZXJ2ZXIqKiAtIEF1dG9tYXRpYyBzY3JlZW4gdHJhY2tpbmcKLSAqKkFuYWx5dGljc0hlbHBlcioqIC0gQ29udmVuaWVuY2UgbWV0aG9kcyBmb3IgY29tbW9uIGV2ZW50cwotICoqQW5hbHl0aWNzRXZlbnRzKiogLSBTdHJ1Y3R1cmVkIGV2ZW50IG5hbWUgY29uc3RhbnRzCgojIyMgUGx1Z2dhYmxlIERlc2lnbgoKU3dpdGNoIGFuYWx5dGljcyBwcm92aWRlcnMgYnkgaW1wbGVtZW50aW5nIGBBbmFseXRpY3NDbGllbnRgOgoKYGBgZGFydApjbGFzcyBNaXhwYW5lbEFuYWx5dGljc0NsaWVudCBpbXBsZW1lbnRzIEFuYWx5dGljc0NsaWVudCB7CiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IGxvZ0V2ZW50KHtyZXF1aXJlZCBTdHJpbmcgbmFtZSwgTWFwPFN0cmluZywgZHluYW1pYz4/IHBhcmFtZXRlcnN9KSB7CiAgICAvLyBNaXhwYW5lbCBpbXBsZW1lbnRhdGlvbgogIH0KICAvLyAuLi4gb3RoZXIgbWV0aG9kcwp9CgovLyBSZWdpc3RlciB3aXRoIERJCmRpLnJlZ2lzdGVyPEFuYWx5dGljc0NsaWVudD4oTWl4cGFuZWxBbmFseXRpY3NDbGllbnQoY29uZmlnKSk7CmBgYAoKIyMgQmVzdCBQcmFjdGljZXMKCjEuICoqVXNlIFN0cnVjdHVyZWQgRXZlbnRzKio6IEFsd2F5cyBwcmVmZXIgYEFuYWx5dGljc0V2ZW50cy5jYXRlZ29yeS5ldmVudE5hbWVgIG92ZXIgc3RyaW5nIGxpdGVyYWxzCjIuICoqSW5jbHVkZSBDb250ZXh0Kio6IEFkZCByZWxldmFudCBwYXJhbWV0ZXJzIGxpa2UgYHNjcmVlbl9uYW1lYCwgYHVzZXJfaWRgLCBldGMuCjMuICoqQ29uc2lzdGVudCBOYW1pbmcqKjogVXNlIHNuYWtlX2Nhc2UgZm9yIGV2ZW50IG5hbWVzIGFuZCBwYXJhbWV0ZXJzCjQuICoqRXJyb3IgSGFuZGxpbmcqKjogQW5hbHl0aWNzIGZhaWx1cmVzIHNob3VsZCBuZXZlciBjcmFzaCB0aGUgYXBwCjUuICoqUHJpdmFjeSoqOiBSZXNwZWN0IHVzZXIgcHJpdmFjeSBzZXR0aW5ncyBhbmQgZGF0YSByZWd1bGF0aW9ucwoKIyMgRXhhbXBsZXMKCiMjIyBUcmFjayBVc2VyIEpvdXJuZXkKCmBgYGRhcnQKLy8gQXBwIGxhdW5jaAphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nQXBwT3BlbigpOwoKLy8gVXNlciBzaWducyBpbgphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nU2lnbkluKG1ldGhvZDogJ2dvb2dsZScpOwoKLy8gVXNlciBjb21wbGV0ZXMgb25ib2FyZGluZwphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nRXZlbnQoCiAgQW5hbHl0aWNzRXZlbnRzLnVzZXIub25ib2FyZGluZ0NvbXBsZXRlZCwKICBwYXJhbWV0ZXJzOiB7J3N0ZXBzX2NvbXBsZXRlZCc6IDV9LAopOwoKLy8gVXNlciB1c2VzIGEgZmVhdHVyZQphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nRmVhdHVyZVVzZWQoJ2hlYWx0aF9zeW5jJyk7CgovLyBVc2VyIGNvbXBsZXRlcyBhIHN1cnZleQphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nU3VydmV5Q29tcGxldGVkKAogICd3ZWxsbmVzc19hc3Nlc3NtZW50JywKICBkdXJhdGlvbjogMTgwLAopOwpgYGAKCiMjIyBFcnJvciBUcmFja2luZwoKYGBgZGFydAp0cnkgewogIC8vIFNvbWUgb3BlcmF0aW9uCn0gY2F0Y2ggKGVycm9yKSB7CiAgYXdhaXQgQW5hbHl0aWNzSGVscGVyLmxvZ0FwcEVycm9yKAogICAgJ2FwaV9jYWxsX2ZhaWxlZCcsCiAgICBlcnJvck1lc3NhZ2U6IGVycm9yLnRvU3RyaW5nKCksCiAgICBwYXJhbWV0ZXJzOiB7CiAgICAgICdlbmRwb2ludCc6ICcvYXBpL2hlYWx0aC9zeW5jJywKICAgICAgJ3VzZXJfaWQnOiB1c2VySWQsCiAgICB9LAogICk7CiAgcmV0aHJvdzsKfQpgYGAKClRoaXMgc3RydWN0dXJlZCBhcHByb2FjaCBlbnN1cmVzIGNvbnNpc3RlbnQsIGRpc2NvdmVyYWJsZSwgYW5kIG1haW50YWluYWJsZSBhbmFseXRpY3MgdGhyb3VnaG91dCB0aGUge3tuYW1lLnRpdGxlQ2FzZSgpfX0gYXBwLg==", + "IyBBbmFseXRpY3MgUGFja2FnZQoKQSBzdHJ1Y3R1cmVkIGFuYWx5dGljcyBwYWNrYWdlIGZvciB7e25hbWUudGl0bGVDYXNlKCl9fSB3aXRoIEZpcmViYXNlIEFuYWx5dGljcyBpbnRlZ3JhdGlvbiBhbmQgcGx1Z2dhYmxlIHByb3ZpZGVyIGFyY2hpdGVjdHVyZS4KCiMjIEZlYXR1cmVzCgotIOKchSAqKkludGVyZmFjZS1iYXNlZCBkZXNpZ24qKiAtIEVhc3kgdG8gc3dpdGNoIGFuYWx5dGljcyBwcm92aWRlcnMKLSDinIUgKipBdXRvbWF0aWMgc2NyZWVuIHRyYWNraW5nKiogLSBSb3V0ZSBvYnNlcnZlciB0cmFja3MgbmF2aWdhdGlvbiBhdXRvbWF0aWNhbGx5Ci0g4pyFICoqU3RydWN0dXJlZCBldmVudCBuYW1lcyoqIC0gVHlwZS1zYWZlLCBvcmdhbml6ZWQgZXZlbnQgY29uc3RhbnRzCi0g4pyFICoqRGVwZW5kZW5jeSBpbmplY3Rpb24qKiAtIEludGVncmF0ZWQgd2l0aCBESSBjb250YWluZXIKLSDinIUgKipFcnJvciBoYW5kbGluZyoqIC0gR3JhY2VmdWwgZmFpbHVyZXMgZG9uJ3QgY3Jhc2ggdGhlIGFwcAotIOKchSAqKkRlYnVnIGxvZ2dpbmcqKiAtIENvbmZpZ3VyYWJsZSBsb2dnaW5nIGZvciBkZXZlbG9wbWVudAoKIyMgUXVpY2sgU3RhcnQKCiMjIyAxLiBJbml0aWFsaXplIEFuYWx5dGljcwoKYGBgZGFydAppbXBvcnQgJ3BhY2thZ2U6YW5hbHl0aWNzL2FuYWx5dGljcy5kYXJ0JzsKCmZpbmFsIGFuYWx5dGljc0NvbmZpZyA9IERlZmF1bHRBbmFseXRpY3NDb25maWcoCiAgZW5hYmxlQW5hbHl0aWNzOiB0cnVlLAogIGVuYWJsZURlYnVnTG9nZ2luZzogdHJ1ZSwgLy8gU2V0IHRvIGZhbHNlIGluIHByb2R1Y3Rpb24KKTsKYXdhaXQgaW5pdChjb25maWc6IGFuYWx5dGljc0NvbmZpZyk7CmBgYAoKIyMjIDIuIEF1dG9tYXRpYyBTY3JlZW4gVHJhY2tpbmcKCkFkZCB0aGUgYEFuYWx5dGljc1JvdXRlT2JzZXJ2ZXJgIHRvIHlvdXIgcm91dGVyOgoKYGBgZGFydApHb1JvdXRlcigKICBvYnNlcnZlcnM6IFsKICAgIEFuYWx5dGljc1JvdXRlT2JzZXJ2ZXIoKSwgLy8gVXNlcyBGaXJlYmFzZUFuYWx5dGljc09ic2VydmVyIGludGVybmFsbHkKICBdLAogIC8vIC4uLiByb3V0ZXMKKTsKYGBgCgpUaGUgYEFuYWx5dGljc1JvdXRlT2JzZXJ2ZXJgIGludGVybmFsbHkgdXNlcyBGaXJlYmFzZSdzIG5hdGl2ZSBgRmlyZWJhc2VBbmFseXRpY3NPYnNlcnZlcmAgZm9yIHByb3BlciBzY3JlZW4gdHJhY2tpbmcgd2hpbGUgYWRkaW5nIGFkZGl0aW9uYWwgbG9nZ2luZyBhbmQgZXJyb3IgaGFuZGxpbmcuCgojIyMgMy4gTWFudWFsIEV2ZW50IFRyYWNraW5nCgojIyMjIFVzaW5nIFN0cnVjdHVyZWQgRXZlbnQgTmFtZXMgKFJlY29tbWVuZGVkKQoKYGBgZGFydAovLyBVc2VyIGV2ZW50cwphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nRXZlbnQoQW5hbHl0aWNzRXZlbnRzLnVzZXIuYXV0aGVudGljYXRlZFJlZGlyZWN0KTsKCi8vIEF1dGhlbnRpY2F0aW9uIGV2ZW50cwphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nRXZlbnQoCiAgQW5hbHl0aWNzRXZlbnRzLmF1dGguc2lnbkluLAogIHBhcmFtZXRlcnM6IHsnbWV0aG9kJzogJ2dvb2dsZSd9LAopOwoKLy8gRmVhdHVyZSB1c2FnZQphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nRXZlbnQoCiAgQW5hbHl0aWNzRXZlbnRzLmZlYXR1cmUudXNlZCwKICBwYXJhbWV0ZXJzOiB7J2ZlYXR1cmVfbmFtZSc6ICdleHBvcnQnfSwKKTsKCi8vIEEgZ3JvdXAgb2YgeW91ciBvd24sIGFkZGVkIHRvIEFuYWx5dGljc0V2ZW50cwphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nRXZlbnQoCiAgQW5hbHl0aWNzRXZlbnRzLmZlYXR1cmUudHV0b3JpYWxDb21wbGV0ZWQsCiAgcGFyYW1ldGVyczogewogICAgJ3R1dG9yaWFsX2lkJzogJ2dldHRpbmdfc3RhcnRlZCcsCiAgICAnZHVyYXRpb25fc2Vjb25kcyc6IDEyMCwKICB9LAopOwpgYGAKCiMjIyMgVXNpbmcgSGVscGVyIE1ldGhvZHMKCmBgYGRhcnQKLy8gRmVhdHVyZSB0cmFja2luZwphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nRmVhdHVyZVVzZWQoJ2V4cG9ydCcpOwoKLy8gRXJyb3IgdHJhY2tpbmcKYXdhaXQgQW5hbHl0aWNzSGVscGVyLmxvZ0FwcEVycm9yKCduZXR3b3JrX3RpbWVvdXQnKTsKCi8vIEJ1dHRvbiBpbnRlcmFjdGlvbnMKYXdhaXQgQW5hbHl0aWNzSGVscGVyLmxvZ0J1dHRvblByZXNzZWQoJ2dldF9zdGFydGVkJyk7CgovLyBBdXRoZW50aWNhdGlvbiBldmVudHMKYXdhaXQgQW5hbHl0aWNzSGVscGVyLmxvZ1NpZ25JbihtZXRob2Q6ICdnb29nbGUnKTsKYXdhaXQgQW5hbHl0aWNzSGVscGVyLmxvZ1NpZ25VcChtZXRob2Q6ICdlbWFpbCcpOwoKLy8gVXNlciBtYW5hZ2VtZW50CmF3YWl0IEFuYWx5dGljc0hlbHBlci5zZXRVc2VySWQoJ3VzZXIxMjMnKTsKYXdhaXQgQW5hbHl0aWNzSGVscGVyLnNldFVzZXJQcm9wZXJ0eShuYW1lOiAncGxhbicsIHZhbHVlOiAncHJlbWl1bScpOwoKLy8gQXBwIGxpZmVjeWNsZQphd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nQXBwT3BlbigpOwpgYGAKCiMjIyMgRGlyZWN0IENsaWVudCBVc2FnZQoKYGBgZGFydApmaW5hbCBhbmFseXRpY3NDbGllbnQgPSBkaS5nZXQ8QW5hbHl0aWNzQ2xpZW50PigpOwoKYXdhaXQgYW5hbHl0aWNzQ2xpZW50LmxvZ0V2ZW50KAogIG5hbWU6ICdjdXN0b21fZXZlbnQnLAogIHBhcmFtZXRlcnM6IHsna2V5JzogJ3ZhbHVlJ30sCik7Cgphd2FpdCBhbmFseXRpY3NDbGllbnQuc2V0VXNlcklkKCd1c2VyMTIzJyk7CmF3YWl0IGFuYWx5dGljc0NsaWVudC5zZXRVc2VyUHJvcGVydHkobmFtZTogJ3BsYW4nLCB2YWx1ZTogJ3ByZW1pdW0nKTsKYGBgCgojIyBTdHJ1Y3R1cmVkIEV2ZW50IE5hbWVzCgpUaGUgcGFja2FnZSBwcm92aWRlcyBvcmdhbml6ZWQsIHR5cGUtc2FmZSBldmVudCBuYW1lcyB0aHJvdWdoIGBBbmFseXRpY3NFdmVudHNgOgoKIyMjIEF2YWlsYWJsZSBFdmVudCBDYXRlZ29yaWVzCgpgYGBkYXJ0Ci8vIFVzZXItcmVsYXRlZCBldmVudHMKQW5hbHl0aWNzRXZlbnRzLnVzZXIuYXV0aGVudGljYXRlZFJlZGlyZWN0CkFuYWx5dGljc0V2ZW50cy51c2VyLnByb2ZpbGVVcGRhdGVkCkFuYWx5dGljc0V2ZW50cy51c2VyLm9uYm9hcmRpbmdDb21wbGV0ZWQKCi8vIEF1dGhlbnRpY2F0aW9uIGV2ZW50cwpBbmFseXRpY3NFdmVudHMuYXV0aC5zaWduSW4KQW5hbHl0aWNzRXZlbnRzLmF1dGguc2lnblVwCkFuYWx5dGljc0V2ZW50cy5hdXRoLnNpZ25PdXQKCi8vIE5hdmlnYXRpb24gZXZlbnRzCkFuYWx5dGljc0V2ZW50cy5uYXZpZ2F0aW9uLnNjcmVlblZpZXcKQW5hbHl0aWNzRXZlbnRzLm5hdmlnYXRpb24udGFiU3dpdGNoZWQKCi8vIEZlYXR1cmUgdXNhZ2UKQW5hbHl0aWNzRXZlbnRzLmZlYXR1cmUudXNlZApBbmFseXRpY3NFdmVudHMuZmVhdHVyZS50dXRvcmlhbENvbXBsZXRlZAoKLy8gQXBwIGxpZmVjeWNsZQpBbmFseXRpY3NFdmVudHMuYXBwLm9wZW4KQW5hbHl0aWNzRXZlbnRzLmFwcC5iYWNrZ3JvdW5kCkFuYWx5dGljc0V2ZW50cy5hcHAuZm9yZWdyb3VuZAoKLy8gRXJyb3IgZXZlbnRzCkFuYWx5dGljc0V2ZW50cy5lcnJvci5hcHBFcnJvcgpBbmFseXRpY3NFdmVudHMuZXJyb3IubmV0d29ya0Vycm9yCmBgYAoKIyMgQ29uZmlndXJhdGlvbgoKIyMjIEFuYWx5dGljc0NvbmZpZyBPcHRpb25zCgpgYGBkYXJ0CmZpbmFsIGNvbmZpZyA9IERlZmF1bHRBbmFseXRpY3NDb25maWcoCiAgZW5hYmxlQW5hbHl0aWNzOiB0cnVlLCAgICAgICAgLy8gRW5hYmxlL2Rpc2FibGUgYW5hbHl0aWNzIGNvbGxlY3Rpb24KICBlbmFibGVEZWJ1Z0xvZ2dpbmc6IGZhbHNlLCAgICAvLyBEZWJ1ZyBsb2dnaW5nIChkZXZlbG9wbWVudCBvbmx5KQogIHVzZXJJZDogJ3VzZXIxMjMnLCAgICAgICAgICAgLy8gT3B0aW9uYWw6IFNldCBpbml0aWFsIHVzZXIgSUQKICBkZWZhdWx0VXNlclByb3BlcnRpZXM6IHsgICAgICAvLyBPcHRpb25hbDogRGVmYXVsdCB1c2VyIHByb3BlcnRpZXMKICAgICd1c2VyX3R5cGUnOiAncHJlbWl1bScsCiAgICAnYXBwX3ZlcnNpb24nOiAnMS4wLjAnLAogIH0sCik7CmBgYAoKIyMjIEVudmlyb25tZW50LWJhc2VkIENvbmZpZ3VyYXRpb24KCmBgYGRhcnQKaW1wb3J0ICdwYWNrYWdlOmZsdXR0ZXIvZm91bmRhdGlvbi5kYXJ0JzsKCmZpbmFsIGNvbmZpZyA9IERlZmF1bHRBbmFseXRpY3NDb25maWcoCiAgZW5hYmxlQW5hbHl0aWNzOiAha0RlYnVnTW9kZSwgICAgICAgICAgLy8gRGlzYWJsZSBpbiBkZWJ1ZyBidWlsZHMKICBlbmFibGVEZWJ1Z0xvZ2dpbmc6IGtEZWJ1Z01vZGUsICAgICAgICAvLyBEZWJ1ZyBsb2dnaW5nIGluIGRldmVsb3BtZW50Cik7CmBgYAoKIyMgQXJjaGl0ZWN0dXJlCgojIyMgQ29tcG9uZW50cwoKLSAqKkFuYWx5dGljc0NsaWVudCoqIC0gQWJzdHJhY3QgaW50ZXJmYWNlIGZvciBhbmFseXRpY3Mgb3BlcmF0aW9ucwotICoqRmlyZWJhc2VBbmFseXRpY3NDbGllbnQqKiAtIEZpcmViYXNlIEFuYWx5dGljcyBpbXBsZW1lbnRhdGlvbgotICoqQW5hbHl0aWNzUm91dGVPYnNlcnZlcioqIC0gQXV0b21hdGljIHNjcmVlbiB0cmFja2luZwotICoqQW5hbHl0aWNzSGVscGVyKiogLSBDb252ZW5pZW5jZSBtZXRob2RzIGZvciBjb21tb24gZXZlbnRzCi0gKipBbmFseXRpY3NFdmVudHMqKiAtIFN0cnVjdHVyZWQgZXZlbnQgbmFtZSBjb25zdGFudHMKCiMjIyBQbHVnZ2FibGUgRGVzaWduCgpTd2l0Y2ggYW5hbHl0aWNzIHByb3ZpZGVycyBieSBpbXBsZW1lbnRpbmcgYEFuYWx5dGljc0NsaWVudGA6CgpgYGBkYXJ0CmNsYXNzIE1peHBhbmVsQW5hbHl0aWNzQ2xpZW50IGltcGxlbWVudHMgQW5hbHl0aWNzQ2xpZW50IHsKICBAb3ZlcnJpZGUKICBGdXR1cmU8dm9pZD4gbG9nRXZlbnQoe3JlcXVpcmVkIFN0cmluZyBuYW1lLCBNYXA8U3RyaW5nLCBkeW5hbWljPj8gcGFyYW1ldGVyc30pIHsKICAgIC8vIE1peHBhbmVsIGltcGxlbWVudGF0aW9uCiAgfQogIC8vIC4uLiBvdGhlciBtZXRob2RzCn0KCi8vIFJlZ2lzdGVyIHdpdGggREkKZGkucmVnaXN0ZXI8QW5hbHl0aWNzQ2xpZW50PihNaXhwYW5lbEFuYWx5dGljc0NsaWVudChjb25maWcpKTsKYGBgCgojIyBCZXN0IFByYWN0aWNlcwoKMS4gKipVc2UgU3RydWN0dXJlZCBFdmVudHMqKjogQWx3YXlzIHByZWZlciBgQW5hbHl0aWNzRXZlbnRzLmNhdGVnb3J5LmV2ZW50TmFtZWAgb3ZlciBzdHJpbmcgbGl0ZXJhbHMKMi4gKipJbmNsdWRlIENvbnRleHQqKjogQWRkIHJlbGV2YW50IHBhcmFtZXRlcnMgbGlrZSBgc2NyZWVuX25hbWVgLCBgdXNlcl9pZGAsIGV0Yy4KMy4gKipDb25zaXN0ZW50IE5hbWluZyoqOiBVc2Ugc25ha2VfY2FzZSBmb3IgZXZlbnQgbmFtZXMgYW5kIHBhcmFtZXRlcnMKNC4gKipFcnJvciBIYW5kbGluZyoqOiBBbmFseXRpY3MgZmFpbHVyZXMgc2hvdWxkIG5ldmVyIGNyYXNoIHRoZSBhcHAKNS4gKipQcml2YWN5Kio6IFJlc3BlY3QgdXNlciBwcml2YWN5IHNldHRpbmdzIGFuZCBkYXRhIHJlZ3VsYXRpb25zCgojIyBFeGFtcGxlcwoKIyMjIFRyYWNrIFVzZXIgSm91cm5leQoKYGBgZGFydAovLyBBcHAgbGF1bmNoCmF3YWl0IEFuYWx5dGljc0hlbHBlci5sb2dBcHBPcGVuKCk7CgovLyBVc2VyIHNpZ25zIGluCmF3YWl0IEFuYWx5dGljc0hlbHBlci5sb2dTaWduSW4obWV0aG9kOiAnZ29vZ2xlJyk7CgovLyBVc2VyIGNvbXBsZXRlcyBvbmJvYXJkaW5nCmF3YWl0IEFuYWx5dGljc0hlbHBlci5sb2dFdmVudCgKICBBbmFseXRpY3NFdmVudHMudXNlci5vbmJvYXJkaW5nQ29tcGxldGVkLAogIHBhcmFtZXRlcnM6IHsnc3RlcHNfY29tcGxldGVkJzogNX0sCik7CgovLyBVc2VyIHVzZXMgYSBmZWF0dXJlCmF3YWl0IEFuYWx5dGljc0hlbHBlci5sb2dGZWF0dXJlVXNlZCgnZXhwb3J0Jyk7CmBgYAoKIyMjIEVycm9yIFRyYWNraW5nCgpgYGBkYXJ0CnRyeSB7CiAgLy8gU29tZSBvcGVyYXRpb24KfSBjYXRjaCAoZXJyb3IpIHsKICBhd2FpdCBBbmFseXRpY3NIZWxwZXIubG9nQXBwRXJyb3IoCiAgICAnYXBpX2NhbGxfZmFpbGVkJywKICAgIGVycm9yTWVzc2FnZTogZXJyb3IudG9TdHJpbmcoKSwKICAgIHBhcmFtZXRlcnM6IHsKICAgICAgJ2VuZHBvaW50JzogJy9hcGkvaXRlbXMnLAogICAgICAndXNlcl9pZCc6IHVzZXJJZCwKICAgIH0sCiAgKTsKICByZXRocm93Owp9CmBgYAoKVGhpcyBzdHJ1Y3R1cmVkIGFwcHJvYWNoIGVuc3VyZXMgY29uc2lzdGVudCwgZGlzY292ZXJhYmxlLCBhbmQgbWFpbnRhaW5hYmxlIGFuYWx5dGljcyB0aHJvdWdob3V0IHRoZSB7e25hbWUudGl0bGVDYXNlKCl9fSBhcHAu", "type": "text" }, { @@ -1260,7 +1260,7 @@ final flutterProjectWithMonoRepoBundle = MasonBundle.fromJson({ "path": "{{ name.snakeCase() }}/packages/{{notifications_dir}}/lib/src/client/firebase_notification_client.dart", "data": - "aW1wb3J0ICdkYXJ0OmFzeW5jJzsKCmltcG9ydCAncGFja2FnZTphcHBfYmFkZ2VfcGx1cy9hcHBfYmFkZ2VfcGx1cy5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmNvcmUvbG9nZ2VyL2xvZ2dlci5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRlc2lnbl9zeXN0ZW0vdG9hc3QvdG9hc3RzLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZGkvZGkuZGFydCc7CmltcG9ydCAncGFja2FnZTpmaXJlYmFzZV9tZXNzYWdpbmcvZmlyZWJhc2VfbWVzc2FnaW5nLmRhcnQnCiAgICBoaWRlIE5vdGlmaWNhdGlvblNldHRpbmdzOwppbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlci9tYXRlcmlhbC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmdvX3JvdXRlci9nb19yb3V0ZXIuZGFydCc7CmltcG9ydCAncGFja2FnZTpuZXR3b3JrL25ldHdvcmsuZGFydCc7CmltcG9ydCAncGFja2FnZTpub3RpZmljYXRpb25zL25vdGlmaWNhdGlvbnMuZGFydCc7Cgpjb25zdCBTdHJpbmcgX3RhZyA9ICdGaXJlYmFzZU5vdGlmaWNhdGlvbkNsaWVudCc7CgovLy8gRmlyZWJhc2UgaW1wbGVtZW50YXRpb24gb2YgdGhlIG5vdGlmaWNhdGlvbiBjbGllbnQKY2xhc3MgRmlyZWJhc2VOb3RpZmljYXRpb25DbGllbnQgaW1wbGVtZW50cyBOb3RpZmljYXRpb25DbGllbnQgewogIEZpcmViYXNlTm90aWZpY2F0aW9uQ2xpZW50KHsKICAgIHJlcXVpcmVkIHRoaXMuY29uZmlnLAogICAgTG9nZ2VyPyBsb2dnZXIsCiAgICBOb3RpZmljYXRpb25Ub2tlbk1hbmFnZXI/IHRva2VuTWFuYWdlciwKICAgIE5vdGlmaWNhdGlvblBlcm1pc3Npb25NYW5hZ2VyPyBwZXJtaXNzaW9uTWFuYWdlciwKICB9KSA6IF9sb2dnZXIgPSBsb2dnZXIgPz8gZGkuZ2V0PExvZ2dlcj4oKSwKICAgICAgIF90b2tlbk1hbmFnZXIgPSB0b2tlbk1hbmFnZXIgPz8gZGkuZ2V0PE5vdGlmaWNhdGlvblRva2VuTWFuYWdlcj4oKSwKICAgICAgIF9wZXJtaXNzaW9uTWFuYWdlciA9CiAgICAgICAgICAgcGVybWlzc2lvbk1hbmFnZXIgPz8gZGkuZ2V0PE5vdGlmaWNhdGlvblBlcm1pc3Npb25NYW5hZ2VyPigpOwoKICBmaW5hbCBOb3RpZmljYXRpb25Db25maWcgY29uZmlnOwogIGZpbmFsIExvZ2dlciBfbG9nZ2VyOwogIGZpbmFsIE5vdGlmaWNhdGlvblRva2VuTWFuYWdlciBfdG9rZW5NYW5hZ2VyOwogIGZpbmFsIE5vdGlmaWNhdGlvblBlcm1pc3Npb25NYW5hZ2VyIF9wZXJtaXNzaW9uTWFuYWdlcjsKICBib29sIF9pc0dyYW50ZWQgPSBmYWxzZTsKCiAgRmlyZWJhc2VNZXNzYWdpbmc/IF9maXJlYmFzZU1lc3NhZ2luZzsKCiAgU3RyaW5nPyBfZmNtVG9rZW47CiAgU3RyaW5nPyBfZGV2aWNlSWQ7CgogIC8vIFN0b3JlIHN1YnNjcmlwdGlvbnMgZm9yIGNsZWFudXAKICBTdHJlYW1TdWJzY3JpcHRpb248UmVtb3RlTWVzc2FnZT4/IF9mb3JlZ3JvdW5kTWVzc2FnZVN1YnNjcmlwdGlvbjsKICBTdHJlYW1TdWJzY3JpcHRpb248UmVtb3RlTWVzc2FnZT4/IF9tZXNzYWdlT3BlbmVkQXBwU3Vic2NyaXB0aW9uOwogIFN0cmVhbVN1YnNjcmlwdGlvbjxTdHJpbmc+PyBfdG9rZW5SZWZyZXNoU3Vic2NyaXB0aW9uOwoKICBAb3ZlcnJpZGUKICBGdXR1cmU8dm9pZD4gaW5pdCgpIGFzeW5jIHsKICAgIHRyeSB7CiAgICAgIF9sb2dnZXIuaSgnJF90YWc6IEluaXRpYWxpemluZyBGaXJlYmFzZSBub3RpZmljYXRpb24gY2xpZW50Jyk7CgogICAgICAvLyBJbml0aWFsaXplIEZpcmViYXNlIE1lc3NhZ2luZwogICAgICBfZmlyZWJhc2VNZXNzYWdpbmcgPSBGaXJlYmFzZU1lc3NhZ2luZy5pbnN0YW5jZTsKCiAgICAgIGZpbmFsIGlzR3JhbnRlZCA9IGF3YWl0IHJlcXVlc3RQZXJtaXNzaW9ucygpOwogICAgICBpZiAoIWlzR3JhbnRlZCkgewogICAgICAgIF9sb2dnZXIudygnJF90YWc6IE5vdGlmaWNhdGlvbiBwZXJtaXNzaW9ucyBub3QgZ3JhbnRlZCcpOwogICAgICAgIHJldHVybjsKICAgICAgfQogICAgICBfaXNHcmFudGVkID0gaXNHcmFudGVkOwoKICAgICAgLy8gU2V0dXAgRkNNIGhhbmRsZXJzIGlmIGJhY2tncm91bmQgbm90aWZpY2F0aW9ucyBhcmUgZW5hYmxlZAogICAgICBhd2FpdCBfc2V0dXBGQ01IYW5kbGVycygpOwoKICAgICAgLy8gR2V0IEZDTSB0b2tlbiBhbmQgcmVnaXN0ZXIgaWYgYmFja2VuZCByZWdpc3RyYXRpb24gaXMgZW5hYmxlZAogICAgICBhd2FpdCBnZXRGQ01Ub2tlbigpOwoKICAgICAgLy8gQ2xlYXIgYmFkZ2UgY291bnQgYXMgdGhlIGxhc3Qgc3RlcCBvZiBpbml0aWFsaXphdGlvbgogICAgICBhd2FpdCBjbGVhckJhZGdlKCk7CgogICAgICBfbG9nZ2VyLmkoJyRfdGFnOiBGaXJlYmFzZSBub3RpZmljYXRpb24gY2xpZW50IGluaXRpYWxpemVkIHN1Y2Nlc3NmdWxseScpOwogICAgfSBjYXRjaCAoZSwgcykgewogICAgICBfbG9nZ2VyLmUoJyRfdGFnOiBFcnJvciBpbml0aWFsaXppbmcgRmlyZWJhc2Ugbm90aWZpY2F0aW9uIGNsaWVudCcsIGUsIHMpOwogICAgICB0aHJvdyBOb3RpZmljYXRpb25FeGNlcHRpb24obWVzc2FnZTogZS5tZXNzYWdlKCkpOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPGJvb2w+IHJlcXVlc3RQZXJtaXNzaW9ucyh7Ym9vbCBwcm92aXNpb25hbCA9IGZhbHNlfSkgYXN5bmMgewogICAgcmV0dXJuIGF3YWl0IF9wZXJtaXNzaW9uTWFuYWdlci5yZXF1ZXN0UGVybWlzc2lvbnMoKTsKICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTx2b2lkPiBjbGVhckJhZGdlKCkgYXN5bmMgewogICAgdHJ5IHsKICAgICAgX2xvZ2dlci5pKCfwn5SUIENsZWFyaW5nIGJhZGdlIGNvdW50Jyk7CiAgICAgIGlmICghX2lzR3JhbnRlZCkgewogICAgICAgIF9sb2dnZXIudygKICAgICAgICAgICfwn5SUIE5vdGlmaWNhdGlvbiBwZXJtaXNzaW9ucyAnCiAgICAgICAgICAnbm90IGdyYW50ZWQsIGNhbm5vdCBjbGVhciBiYWRnZScsCiAgICAgICAgKTsKICAgICAgICByZXR1cm47CiAgICAgIH0KICAgICAgaWYgKCFhd2FpdCBBcHBCYWRnZVBsdXMuaXNTdXBwb3J0ZWQoKSkgewogICAgICAgIF9sb2dnZXIudygn8J+UlCBBcHBCYWRnZVBsdXMgaXMgbm90IHN1cHBvcnRlZCBvbiB0aGlzIHBsYXRmb3JtJyk7CiAgICAgICAgcmV0dXJuOwogICAgICB9CiAgICAgIGF3YWl0IEFwcEJhZGdlUGx1cy51cGRhdGVCYWRnZSgwKTsKICAgICAgX2xvZ2dlci5pKCfwn5SUIEJhZGdlIGNvdW50IGNsZWFyZWQnKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgX2xvZ2dlci53KCfwn5SUIEZhaWxlZCB0byBjbGVhciBiYWRnZSBjb3VudDogJGUnKTsKICAgIH0KICB9CgogIEZ1dHVyZTx2b2lkPiBfc2V0dXBGQ01IYW5kbGVycygpIGFzeW5jIHsKICAgIC8vIEhhbmRsZSBiYWNrZ3JvdW5kIG1lc3NhZ2VzICh3aGVuIGFwcCBpcyB0ZXJtaW5hdGVkKQogICAgRmlyZWJhc2VNZXNzYWdpbmcub25CYWNrZ3JvdW5kTWVzc2FnZShfaGFuZGxlQmFja2dyb3VuZE5vdGlmaWNhdGlvbik7CgogICAgLy8gSGFuZGxlIGZvcmVncm91bmQgbWVzc2FnZXMKICAgIF9mb3JlZ3JvdW5kTWVzc2FnZVN1YnNjcmlwdGlvbiA9IEZpcmViYXNlTWVzc2FnaW5nLm9uTWVzc2FnZS5saXN0ZW4oCiAgICAgIChSZW1vdGVNZXNzYWdlIG1lc3NhZ2UpIHsKICAgICAgICBfbG9nZ2VyLmkoJyRfdGFnOiBSZWNlaXZlZCBmb3JlZ3JvdW5kIG1lc3NhZ2U6ICR7bWVzc2FnZS5tZXNzYWdlSWR9Jyk7CiAgICAgICAgaGFuZGxlRm9yZWdyb3VuZE5vdGlmaWNhdGlvbihtZXNzYWdlKTsKICAgICAgfSwKICAgICAgb25FcnJvcjogKGVycm9yKSB7CiAgICAgICAgX2xvZ2dlci5lKCckX3RhZzogRXJyb3IgaW4gZm9yZWdyb3VuZCBtZXNzYWdlIGhhbmRsZXInLCBlcnJvcik7CiAgICAgIH0sCiAgICApOwoKICAgIC8vIEhhbmRsZSBub3RpZmljYXRpb24gb3BlbmVkIChmcm9tIGJhY2tncm91bmQvdGVybWluYXRlZCkKICAgIF9tZXNzYWdlT3BlbmVkQXBwU3Vic2NyaXB0aW9uID0gRmlyZWJhc2VNZXNzYWdpbmcub25NZXNzYWdlT3BlbmVkQXBwLmxpc3RlbigKICAgICAgKFJlbW90ZU1lc3NhZ2UgbWVzc2FnZSkgewogICAgICAgIF9sb2dnZXIuaSgnJF90YWc6IE5vdGlmaWNhdGlvbiBvcGVuZWQgYXBwOiAke21lc3NhZ2UubWVzc2FnZUlkfScpOwogICAgICAgIGhhbmRsZU5vdGlmaWNhdGlvbk9wZW5lZChtZXNzYWdlLCBzb3VyY2U6ICdiYWNrZ3JvdW5kJyk7CiAgICAgIH0sCiAgICAgIG9uRXJyb3I6IChlcnJvcikgewogICAgICAgIF9sb2dnZXIuZSgnJF90YWc6IEVycm9yIGluIG5vdGlmaWNhdGlvbiBvcGVuZWQgaGFuZGxlcicsIGVycm9yKTsKICAgICAgfSwKICAgICk7CgogICAgLy8gSGFuZGxlIGluaXRpYWwgbWVzc2FnZSAoaWYgYXBwIHdhcyBvcGVuZWQgZnJvbSB0ZXJtaW5hdGVkIHN0YXRlKQogICAgaWYgKF9maXJlYmFzZU1lc3NhZ2luZyAhPSBudWxsKSB7CiAgICAgIGZpbmFsIGluaXRpYWxNZXNzYWdlID0gYXdhaXQgX2ZpcmViYXNlTWVzc2FnaW5nIS5nZXRJbml0aWFsTWVzc2FnZSgpOwogICAgICBpZiAoaW5pdGlhbE1lc3NhZ2UgIT0gbnVsbCkgewogICAgICAgIF9sb2dnZXIuaSgKICAgICAgICAgICckX3RhZzogQXBwIG9wZW5lZCBmcm9tIHRlcm1pbmF0ZWQgc3RhdGU6ICR7aW5pdGlhbE1lc3NhZ2UubWVzc2FnZUlkfScsCiAgICAgICAgKTsKICAgICAgICBoYW5kbGVOb3RpZmljYXRpb25PcGVuZWQoaW5pdGlhbE1lc3NhZ2UsIHNvdXJjZTogJ3Rlcm1pbmF0ZWQnKTsKICAgICAgfQoKICAgICAgLy8gSGFuZGxlIHRva2VuIHJlZnJlc2gKICAgICAgX3Rva2VuUmVmcmVzaFN1YnNjcmlwdGlvbiA9IF9maXJlYmFzZU1lc3NhZ2luZyEub25Ub2tlblJlZnJlc2gubGlzdGVuKAogICAgICAgIChTdHJpbmcgbmV3VG9rZW4pIHsKICAgICAgICAgIF9mY21Ub2tlbiA9IG5ld1Rva2VuOwogICAgICAgICAgX3Rva2VuTWFuYWdlci5oYW5kbGVUb2tlblJlZnJlc2gobmV3VG9rZW4pOwogICAgICAgIH0sCiAgICAgICAgb25FcnJvcjogKGVycm9yKSB7CiAgICAgICAgICBfbG9nZ2VyLmUoJyRfdGFnOiBFcnJvciBpbiB0b2tlbiByZWZyZXNoIGhhbmRsZXInLCBlcnJvcik7CiAgICAgICAgfSwKICAgICAgKTsKICAgIH0KCiAgICBfbG9nZ2VyLmkoJyRfdGFnOiBGQ00gaGFuZGxlcnMgc2V0dXAgY29tcGxldGUnKTsKICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTxTdHJpbmc/PiBnZXRGQ01Ub2tlbigpIGFzeW5jIHsKICAgIHRyeSB7CiAgICAgIF9mY21Ub2tlbiA9IGF3YWl0IF90b2tlbk1hbmFnZXIuZ2V0RkNNVG9rZW4oKTsKCiAgICAgIGlmIChfZmNtVG9rZW4gIT0gbnVsbCkgewogICAgICAgIF9kZXZpY2VJZCA9IGF3YWl0IF90b2tlbk1hbmFnZXIucmVnaXN0ZXJUb2tlbihfZmNtVG9rZW4hKTsKICAgICAgICBfbG9nZ2VyLmkoJyRfdGFnOiBUb2tlbiByZWdpc3RlcmVkIHdpdGggZGV2aWNlIElEOiAkX2RldmljZUlkJyk7CiAgICAgIH0KCiAgICAgIHJldHVybiBfZmNtVG9rZW47CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2dnZXIuZSgnJF90YWc6IEVycm9yIGdldHRpbmcgRkNNIHRva2VuJywgZSwgcyk7CiAgICAgIHJldHVybiBudWxsOwogICAgfQogIH0KCiAgLy8vIEhhbmRsZSBiYWNrZ3JvdW5kIG5vdGlmaWNhdGlvbiAod2hlbiBhcHAgaXMgbm90IHJ1bm5pbmcpCiAgQHByYWdtYSgndm06ZW50cnktcG9pbnQnKQogIHN0YXRpYyBGdXR1cmU8dm9pZD4gX2hhbmRsZUJhY2tncm91bmROb3RpZmljYXRpb24oCiAgICBSZW1vdGVNZXNzYWdlIG1lc3NhZ2UsCiAgKSBhc3luYyB7CiAgICB0cnkgewogICAgICBkZWJ1Z1ByaW50KAogICAgICAgICckX3RhZzogSGFuZGxpbmcgYmFja2dyb3VuZCBub3RpZmljYXRpb246ICR7bWVzc2FnZS5tZXNzYWdlSWR9JywKICAgICAgKTsKICAgIH0gY2F0Y2ggKGUsIHMpIHsKICAgICAgZGVidWdQcmludCgKICAgICAgICAnJF90YWc6IEVycm9yIGhhbmRsaW5nIGJhY2tncm91bmQgbm90aWZpY2F0aW9uOicKICAgICAgICAnICR7bWVzc2FnZS5tZXNzYWdlSWR9IC0gJGUgJHMnLAogICAgICApOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IGhhbmRsZUZvcmVncm91bmROb3RpZmljYXRpb24oUmVtb3RlTWVzc2FnZSBtZXNzYWdlKSBhc3luYyB7CiAgICB0cnkgewogICAgICBfbG9nZ2VyLmkoCiAgICAgICAgJyRfdGFnOiBIYW5kbGluZyBmb3JlZ3JvdW5kIG5vdGlmaWNhdGlvbjogJHttZXNzYWdlLm1lc3NhZ2VJZH0nLAogICAgICApOwoKICAgICAgZmluYWwgbm90aWZpY2F0aW9uID0gbWVzc2FnZS5ub3RpZmljYXRpb247CiAgICAgIGlmIChub3RpZmljYXRpb24gIT0gbnVsbCAmJgogICAgICAgICAgbm90aWZpY2F0aW9uLnRpdGxlICE9IG51bGwgJiYKICAgICAgICAgIG5vdGlmaWNhdGlvbi5ib2R5ICE9IG51bGwpIHsKICAgICAgICBUb2FzdC5ub3RpZmljYXRpb24oCiAgICAgICAgICB0aXRsZTogbm90aWZpY2F0aW9uLnRpdGxlISwKICAgICAgICAgIGJvZHk6IG5vdGlmaWNhdGlvbi5ib2R5ISwKICAgICAgICApOwogICAgICB9CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2dnZXIuZSgnJF90YWc6IEVycm9yIGhhbmRsaW5nIGZvcmVncm91bmQgbm90aWZpY2F0aW9uJywgZSwgcyk7CiAgICB9CiAgfQoKICBAb3ZlcnJpZGUKICBGdXR1cmU8dm9pZD4gaGFuZGxlTm90aWZpY2F0aW9uT3BlbmVkKAogICAgUmVtb3RlTWVzc2FnZSBtZXNzYWdlLCB7CiAgICBTdHJpbmc/IHNvdXJjZSwKICB9KSBhc3luYyB7CiAgICB0cnkgewogICAgICBfbG9nZ2VyLmkoJyRfdGFnOiBIYW5kbGluZyBub3RpZmljYXRpb24gb3BlbmVkOiAke21lc3NhZ2UubWVzc2FnZUlkfScpOwoKICAgICAgZmluYWwgbm90aWZpY2F0aW9uID0gbWVzc2FnZS5ub3RpZmljYXRpb247CgogICAgICBfbG9nZ2VyLmkoCiAgICAgICAgJyRfdGFnOiBOb3RpZmljYXRpb24gb3BlbmVkIGZyb20gJHNvdXJjZTogJHtub3RpZmljYXRpb24/LnRpdGxlfSAtICR7bm90aWZpY2F0aW9uPy5ib2R5fScsCiAgICAgICk7CgogICAgICBmaW5hbCBkYXRhID0gbWVzc2FnZS5kYXRhOwogICAgICBmaW5hbCBhY3Rpb25UeXBlID0gZGF0YVsndHlwZSddOwogICAgICBpZiAoYWN0aW9uVHlwZSA9PSBudWxsKSByZXR1cm47CgogICAgICBpZiAoYWN0aW9uVHlwZSA9PSAnc3VydmV5QXNzaWdubWVudHMnKSB7CiAgICAgICAgZmluYWwgaWQgPSBkYXRhWydpZCddOwogICAgICAgIGlmIChpZCA9PSBudWxsKSByZXR1cm47CiAgICAgICAgZmluYWwgcm91dGVyID0gZGkuZ2V0PEdvUm91dGVyPigpOwogICAgICAgIHJvdXRlci5wdXNoKCcvaG9tZS9zdXJ2ZXkvYXNzaWdubWVudC8kaWQnKTsKICAgICAgfQogICAgfSBjYXRjaCAoZSwgcykgewogICAgICBfbG9nZ2VyLmUoJyRfdGFnOiBFcnJvciBoYW5kbGluZyBub3RpZmljYXRpb24gb3BlbmVkJywgZSwgcyk7CiAgICB9CiAgfQoKICBAb3ZlcnJpZGUKICBTdHJpbmc/IGdldCBmY21Ub2tlbiA9PiBfZmNtVG9rZW47CgogIEBvdmVycmlkZQogIFN0cmluZz8gZ2V0IGRldmljZUlkID0+IF9kZXZpY2VJZDsKCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IGRpc3Bvc2UoKSBhc3luYyB7CiAgICBfbG9nZ2VyLmkoJyRfdGFnOiBEaXNwb3NpbmcgRmlyZWJhc2Ugbm90aWZpY2F0aW9uIGNsaWVudCcpOwoKICAgIC8vIENhbmNlbCBhbGwgc3Vic2NyaXB0aW9ucyB0byBwcmV2ZW50IG1lbW9yeSBsZWFrcwogICAgX2ZvcmVncm91bmRNZXNzYWdlU3Vic2NyaXB0aW9uPy5jYW5jZWwoKTsKICAgIF9tZXNzYWdlT3BlbmVkQXBwU3Vic2NyaXB0aW9uPy5jYW5jZWwoKTsKICAgIF90b2tlblJlZnJlc2hTdWJzY3JpcHRpb24/LmNhbmNlbCgpOwoKICAgIF9mb3JlZ3JvdW5kTWVzc2FnZVN1YnNjcmlwdGlvbiA9IG51bGw7CiAgICBfbWVzc2FnZU9wZW5lZEFwcFN1YnNjcmlwdGlvbiA9IG51bGw7CiAgICBfdG9rZW5SZWZyZXNoU3Vic2NyaXB0aW9uID0gbnVsbDsKCiAgICBfZmlyZWJhc2VNZXNzYWdpbmcgPSBudWxsOwogIH0KfQo=", + "aW1wb3J0ICdkYXJ0OmFzeW5jJzsKCmltcG9ydCAncGFja2FnZTphcHBfYmFkZ2VfcGx1cy9hcHBfYmFkZ2VfcGx1cy5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmNvcmUvbG9nZ2VyL2xvZ2dlci5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmRlc2lnbl9zeXN0ZW0vdG9hc3QvdG9hc3RzLmRhcnQnOwppbXBvcnQgJ3BhY2thZ2U6ZGkvZGkuZGFydCc7CmltcG9ydCAncGFja2FnZTpmaXJlYmFzZV9tZXNzYWdpbmcvZmlyZWJhc2VfbWVzc2FnaW5nLmRhcnQnCiAgICBoaWRlIE5vdGlmaWNhdGlvblNldHRpbmdzOwppbXBvcnQgJ3BhY2thZ2U6Zmx1dHRlci9tYXRlcmlhbC5kYXJ0JzsKaW1wb3J0ICdwYWNrYWdlOmdvX3JvdXRlci9nb19yb3V0ZXIuZGFydCc7CmltcG9ydCAncGFja2FnZTpuZXR3b3JrL25ldHdvcmsuZGFydCc7CmltcG9ydCAncGFja2FnZTpub3RpZmljYXRpb25zL25vdGlmaWNhdGlvbnMuZGFydCc7Cgpjb25zdCBTdHJpbmcgX3RhZyA9ICdGaXJlYmFzZU5vdGlmaWNhdGlvbkNsaWVudCc7CgovLy8gRmlyZWJhc2UgaW1wbGVtZW50YXRpb24gb2YgdGhlIG5vdGlmaWNhdGlvbiBjbGllbnQKY2xhc3MgRmlyZWJhc2VOb3RpZmljYXRpb25DbGllbnQgaW1wbGVtZW50cyBOb3RpZmljYXRpb25DbGllbnQgewogIEZpcmViYXNlTm90aWZpY2F0aW9uQ2xpZW50KHsKICAgIHJlcXVpcmVkIHRoaXMuY29uZmlnLAogICAgTG9nZ2VyPyBsb2dnZXIsCiAgICBOb3RpZmljYXRpb25Ub2tlbk1hbmFnZXI/IHRva2VuTWFuYWdlciwKICAgIE5vdGlmaWNhdGlvblBlcm1pc3Npb25NYW5hZ2VyPyBwZXJtaXNzaW9uTWFuYWdlciwKICB9KSA6IF9sb2dnZXIgPSBsb2dnZXIgPz8gZGkuZ2V0PExvZ2dlcj4oKSwKICAgICAgIF90b2tlbk1hbmFnZXIgPSB0b2tlbk1hbmFnZXIgPz8gZGkuZ2V0PE5vdGlmaWNhdGlvblRva2VuTWFuYWdlcj4oKSwKICAgICAgIF9wZXJtaXNzaW9uTWFuYWdlciA9CiAgICAgICAgICAgcGVybWlzc2lvbk1hbmFnZXIgPz8gZGkuZ2V0PE5vdGlmaWNhdGlvblBlcm1pc3Npb25NYW5hZ2VyPigpOwoKICBmaW5hbCBOb3RpZmljYXRpb25Db25maWcgY29uZmlnOwogIGZpbmFsIExvZ2dlciBfbG9nZ2VyOwogIGZpbmFsIE5vdGlmaWNhdGlvblRva2VuTWFuYWdlciBfdG9rZW5NYW5hZ2VyOwogIGZpbmFsIE5vdGlmaWNhdGlvblBlcm1pc3Npb25NYW5hZ2VyIF9wZXJtaXNzaW9uTWFuYWdlcjsKICBib29sIF9pc0dyYW50ZWQgPSBmYWxzZTsKCiAgRmlyZWJhc2VNZXNzYWdpbmc/IF9maXJlYmFzZU1lc3NhZ2luZzsKCiAgU3RyaW5nPyBfZmNtVG9rZW47CiAgU3RyaW5nPyBfZGV2aWNlSWQ7CgogIC8vIFN0b3JlIHN1YnNjcmlwdGlvbnMgZm9yIGNsZWFudXAKICBTdHJlYW1TdWJzY3JpcHRpb248UmVtb3RlTWVzc2FnZT4/IF9mb3JlZ3JvdW5kTWVzc2FnZVN1YnNjcmlwdGlvbjsKICBTdHJlYW1TdWJzY3JpcHRpb248UmVtb3RlTWVzc2FnZT4/IF9tZXNzYWdlT3BlbmVkQXBwU3Vic2NyaXB0aW9uOwogIFN0cmVhbVN1YnNjcmlwdGlvbjxTdHJpbmc+PyBfdG9rZW5SZWZyZXNoU3Vic2NyaXB0aW9uOwoKICBAb3ZlcnJpZGUKICBGdXR1cmU8dm9pZD4gaW5pdCgpIGFzeW5jIHsKICAgIHRyeSB7CiAgICAgIF9sb2dnZXIuaSgnJF90YWc6IEluaXRpYWxpemluZyBGaXJlYmFzZSBub3RpZmljYXRpb24gY2xpZW50Jyk7CgogICAgICAvLyBJbml0aWFsaXplIEZpcmViYXNlIE1lc3NhZ2luZwogICAgICBfZmlyZWJhc2VNZXNzYWdpbmcgPSBGaXJlYmFzZU1lc3NhZ2luZy5pbnN0YW5jZTsKCiAgICAgIGZpbmFsIGlzR3JhbnRlZCA9IGF3YWl0IHJlcXVlc3RQZXJtaXNzaW9ucygpOwogICAgICBpZiAoIWlzR3JhbnRlZCkgewogICAgICAgIF9sb2dnZXIudygnJF90YWc6IE5vdGlmaWNhdGlvbiBwZXJtaXNzaW9ucyBub3QgZ3JhbnRlZCcpOwogICAgICAgIHJldHVybjsKICAgICAgfQogICAgICBfaXNHcmFudGVkID0gaXNHcmFudGVkOwoKICAgICAgLy8gU2V0dXAgRkNNIGhhbmRsZXJzIGlmIGJhY2tncm91bmQgbm90aWZpY2F0aW9ucyBhcmUgZW5hYmxlZAogICAgICBhd2FpdCBfc2V0dXBGQ01IYW5kbGVycygpOwoKICAgICAgLy8gR2V0IEZDTSB0b2tlbiBhbmQgcmVnaXN0ZXIgaWYgYmFja2VuZCByZWdpc3RyYXRpb24gaXMgZW5hYmxlZAogICAgICBhd2FpdCBnZXRGQ01Ub2tlbigpOwoKICAgICAgLy8gQ2xlYXIgYmFkZ2UgY291bnQgYXMgdGhlIGxhc3Qgc3RlcCBvZiBpbml0aWFsaXphdGlvbgogICAgICBhd2FpdCBjbGVhckJhZGdlKCk7CgogICAgICBfbG9nZ2VyLmkoJyRfdGFnOiBGaXJlYmFzZSBub3RpZmljYXRpb24gY2xpZW50IGluaXRpYWxpemVkIHN1Y2Nlc3NmdWxseScpOwogICAgfSBjYXRjaCAoZSwgcykgewogICAgICBfbG9nZ2VyLmUoJyRfdGFnOiBFcnJvciBpbml0aWFsaXppbmcgRmlyZWJhc2Ugbm90aWZpY2F0aW9uIGNsaWVudCcsIGUsIHMpOwogICAgICB0aHJvdyBOb3RpZmljYXRpb25FeGNlcHRpb24obWVzc2FnZTogZS5tZXNzYWdlKCkpOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPGJvb2w+IHJlcXVlc3RQZXJtaXNzaW9ucyh7Ym9vbCBwcm92aXNpb25hbCA9IGZhbHNlfSkgYXN5bmMgewogICAgcmV0dXJuIGF3YWl0IF9wZXJtaXNzaW9uTWFuYWdlci5yZXF1ZXN0UGVybWlzc2lvbnMoKTsKICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTx2b2lkPiBjbGVhckJhZGdlKCkgYXN5bmMgewogICAgdHJ5IHsKICAgICAgX2xvZ2dlci5pKCfwn5SUIENsZWFyaW5nIGJhZGdlIGNvdW50Jyk7CiAgICAgIGlmICghX2lzR3JhbnRlZCkgewogICAgICAgIF9sb2dnZXIudygKICAgICAgICAgICfwn5SUIE5vdGlmaWNhdGlvbiBwZXJtaXNzaW9ucyAnCiAgICAgICAgICAnbm90IGdyYW50ZWQsIGNhbm5vdCBjbGVhciBiYWRnZScsCiAgICAgICAgKTsKICAgICAgICByZXR1cm47CiAgICAgIH0KICAgICAgaWYgKCFhd2FpdCBBcHBCYWRnZVBsdXMuaXNTdXBwb3J0ZWQoKSkgewogICAgICAgIF9sb2dnZXIudygn8J+UlCBBcHBCYWRnZVBsdXMgaXMgbm90IHN1cHBvcnRlZCBvbiB0aGlzIHBsYXRmb3JtJyk7CiAgICAgICAgcmV0dXJuOwogICAgICB9CiAgICAgIGF3YWl0IEFwcEJhZGdlUGx1cy51cGRhdGVCYWRnZSgwKTsKICAgICAgX2xvZ2dlci5pKCfwn5SUIEJhZGdlIGNvdW50IGNsZWFyZWQnKTsKICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgX2xvZ2dlci53KCfwn5SUIEZhaWxlZCB0byBjbGVhciBiYWRnZSBjb3VudDogJGUnKTsKICAgIH0KICB9CgogIEZ1dHVyZTx2b2lkPiBfc2V0dXBGQ01IYW5kbGVycygpIGFzeW5jIHsKICAgIC8vIEhhbmRsZSBiYWNrZ3JvdW5kIG1lc3NhZ2VzICh3aGVuIGFwcCBpcyB0ZXJtaW5hdGVkKQogICAgRmlyZWJhc2VNZXNzYWdpbmcub25CYWNrZ3JvdW5kTWVzc2FnZShfaGFuZGxlQmFja2dyb3VuZE5vdGlmaWNhdGlvbik7CgogICAgLy8gSGFuZGxlIGZvcmVncm91bmQgbWVzc2FnZXMKICAgIF9mb3JlZ3JvdW5kTWVzc2FnZVN1YnNjcmlwdGlvbiA9IEZpcmViYXNlTWVzc2FnaW5nLm9uTWVzc2FnZS5saXN0ZW4oCiAgICAgIChSZW1vdGVNZXNzYWdlIG1lc3NhZ2UpIHsKICAgICAgICBfbG9nZ2VyLmkoJyRfdGFnOiBSZWNlaXZlZCBmb3JlZ3JvdW5kIG1lc3NhZ2U6ICR7bWVzc2FnZS5tZXNzYWdlSWR9Jyk7CiAgICAgICAgaGFuZGxlRm9yZWdyb3VuZE5vdGlmaWNhdGlvbihtZXNzYWdlKTsKICAgICAgfSwKICAgICAgb25FcnJvcjogKGVycm9yKSB7CiAgICAgICAgX2xvZ2dlci5lKCckX3RhZzogRXJyb3IgaW4gZm9yZWdyb3VuZCBtZXNzYWdlIGhhbmRsZXInLCBlcnJvcik7CiAgICAgIH0sCiAgICApOwoKICAgIC8vIEhhbmRsZSBub3RpZmljYXRpb24gb3BlbmVkIChmcm9tIGJhY2tncm91bmQvdGVybWluYXRlZCkKICAgIF9tZXNzYWdlT3BlbmVkQXBwU3Vic2NyaXB0aW9uID0gRmlyZWJhc2VNZXNzYWdpbmcub25NZXNzYWdlT3BlbmVkQXBwLmxpc3RlbigKICAgICAgKFJlbW90ZU1lc3NhZ2UgbWVzc2FnZSkgewogICAgICAgIF9sb2dnZXIuaSgnJF90YWc6IE5vdGlmaWNhdGlvbiBvcGVuZWQgYXBwOiAke21lc3NhZ2UubWVzc2FnZUlkfScpOwogICAgICAgIGhhbmRsZU5vdGlmaWNhdGlvbk9wZW5lZChtZXNzYWdlLCBzb3VyY2U6ICdiYWNrZ3JvdW5kJyk7CiAgICAgIH0sCiAgICAgIG9uRXJyb3I6IChlcnJvcikgewogICAgICAgIF9sb2dnZXIuZSgnJF90YWc6IEVycm9yIGluIG5vdGlmaWNhdGlvbiBvcGVuZWQgaGFuZGxlcicsIGVycm9yKTsKICAgICAgfSwKICAgICk7CgogICAgLy8gSGFuZGxlIGluaXRpYWwgbWVzc2FnZSAoaWYgYXBwIHdhcyBvcGVuZWQgZnJvbSB0ZXJtaW5hdGVkIHN0YXRlKQogICAgaWYgKF9maXJlYmFzZU1lc3NhZ2luZyAhPSBudWxsKSB7CiAgICAgIGZpbmFsIGluaXRpYWxNZXNzYWdlID0gYXdhaXQgX2ZpcmViYXNlTWVzc2FnaW5nIS5nZXRJbml0aWFsTWVzc2FnZSgpOwogICAgICBpZiAoaW5pdGlhbE1lc3NhZ2UgIT0gbnVsbCkgewogICAgICAgIF9sb2dnZXIuaSgKICAgICAgICAgICckX3RhZzogQXBwIG9wZW5lZCBmcm9tIHRlcm1pbmF0ZWQgc3RhdGU6ICR7aW5pdGlhbE1lc3NhZ2UubWVzc2FnZUlkfScsCiAgICAgICAgKTsKICAgICAgICBoYW5kbGVOb3RpZmljYXRpb25PcGVuZWQoaW5pdGlhbE1lc3NhZ2UsIHNvdXJjZTogJ3Rlcm1pbmF0ZWQnKTsKICAgICAgfQoKICAgICAgLy8gSGFuZGxlIHRva2VuIHJlZnJlc2gKICAgICAgX3Rva2VuUmVmcmVzaFN1YnNjcmlwdGlvbiA9IF9maXJlYmFzZU1lc3NhZ2luZyEub25Ub2tlblJlZnJlc2gubGlzdGVuKAogICAgICAgIChTdHJpbmcgbmV3VG9rZW4pIHsKICAgICAgICAgIF9mY21Ub2tlbiA9IG5ld1Rva2VuOwogICAgICAgICAgX3Rva2VuTWFuYWdlci5oYW5kbGVUb2tlblJlZnJlc2gobmV3VG9rZW4pOwogICAgICAgIH0sCiAgICAgICAgb25FcnJvcjogKGVycm9yKSB7CiAgICAgICAgICBfbG9nZ2VyLmUoJyRfdGFnOiBFcnJvciBpbiB0b2tlbiByZWZyZXNoIGhhbmRsZXInLCBlcnJvcik7CiAgICAgICAgfSwKICAgICAgKTsKICAgIH0KCiAgICBfbG9nZ2VyLmkoJyRfdGFnOiBGQ00gaGFuZGxlcnMgc2V0dXAgY29tcGxldGUnKTsKICB9CgogIEBvdmVycmlkZQogIEZ1dHVyZTxTdHJpbmc/PiBnZXRGQ01Ub2tlbigpIGFzeW5jIHsKICAgIHRyeSB7CiAgICAgIF9mY21Ub2tlbiA9IGF3YWl0IF90b2tlbk1hbmFnZXIuZ2V0RkNNVG9rZW4oKTsKCiAgICAgIGlmIChfZmNtVG9rZW4gIT0gbnVsbCkgewogICAgICAgIF9kZXZpY2VJZCA9IGF3YWl0IF90b2tlbk1hbmFnZXIucmVnaXN0ZXJUb2tlbihfZmNtVG9rZW4hKTsKICAgICAgICBfbG9nZ2VyLmkoJyRfdGFnOiBUb2tlbiByZWdpc3RlcmVkIHdpdGggZGV2aWNlIElEOiAkX2RldmljZUlkJyk7CiAgICAgIH0KCiAgICAgIHJldHVybiBfZmNtVG9rZW47CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2dnZXIuZSgnJF90YWc6IEVycm9yIGdldHRpbmcgRkNNIHRva2VuJywgZSwgcyk7CiAgICAgIHJldHVybiBudWxsOwogICAgfQogIH0KCiAgLy8vIEhhbmRsZSBiYWNrZ3JvdW5kIG5vdGlmaWNhdGlvbiAod2hlbiBhcHAgaXMgbm90IHJ1bm5pbmcpCiAgQHByYWdtYSgndm06ZW50cnktcG9pbnQnKQogIHN0YXRpYyBGdXR1cmU8dm9pZD4gX2hhbmRsZUJhY2tncm91bmROb3RpZmljYXRpb24oCiAgICBSZW1vdGVNZXNzYWdlIG1lc3NhZ2UsCiAgKSBhc3luYyB7CiAgICB0cnkgewogICAgICBkZWJ1Z1ByaW50KAogICAgICAgICckX3RhZzogSGFuZGxpbmcgYmFja2dyb3VuZCBub3RpZmljYXRpb246ICR7bWVzc2FnZS5tZXNzYWdlSWR9JywKICAgICAgKTsKICAgIH0gY2F0Y2ggKGUsIHMpIHsKICAgICAgZGVidWdQcmludCgKICAgICAgICAnJF90YWc6IEVycm9yIGhhbmRsaW5nIGJhY2tncm91bmQgbm90aWZpY2F0aW9uOicKICAgICAgICAnICR7bWVzc2FnZS5tZXNzYWdlSWR9IC0gJGUgJHMnLAogICAgICApOwogICAgfQogIH0KCiAgQG92ZXJyaWRlCiAgRnV0dXJlPHZvaWQ+IGhhbmRsZUZvcmVncm91bmROb3RpZmljYXRpb24oUmVtb3RlTWVzc2FnZSBtZXNzYWdlKSBhc3luYyB7CiAgICB0cnkgewogICAgICBfbG9nZ2VyLmkoCiAgICAgICAgJyRfdGFnOiBIYW5kbGluZyBmb3JlZ3JvdW5kIG5vdGlmaWNhdGlvbjogJHttZXNzYWdlLm1lc3NhZ2VJZH0nLAogICAgICApOwoKICAgICAgZmluYWwgbm90aWZpY2F0aW9uID0gbWVzc2FnZS5ub3RpZmljYXRpb247CiAgICAgIGlmIChub3RpZmljYXRpb24gIT0gbnVsbCAmJgogICAgICAgICAgbm90aWZpY2F0aW9uLnRpdGxlICE9IG51bGwgJiYKICAgICAgICAgIG5vdGlmaWNhdGlvbi5ib2R5ICE9IG51bGwpIHsKICAgICAgICBUb2FzdC5ub3RpZmljYXRpb24oCiAgICAgICAgICB0aXRsZTogbm90aWZpY2F0aW9uLnRpdGxlISwKICAgICAgICAgIGJvZHk6IG5vdGlmaWNhdGlvbi5ib2R5ISwKICAgICAgICApOwogICAgICB9CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2dnZXIuZSgnJF90YWc6IEVycm9yIGhhbmRsaW5nIGZvcmVncm91bmQgbm90aWZpY2F0aW9uJywgZSwgcyk7CiAgICB9CiAgfQoKICBAb3ZlcnJpZGUKICBGdXR1cmU8dm9pZD4gaGFuZGxlTm90aWZpY2F0aW9uT3BlbmVkKAogICAgUmVtb3RlTWVzc2FnZSBtZXNzYWdlLCB7CiAgICBTdHJpbmc/IHNvdXJjZSwKICB9KSBhc3luYyB7CiAgICB0cnkgewogICAgICBfbG9nZ2VyLmkoJyRfdGFnOiBIYW5kbGluZyBub3RpZmljYXRpb24gb3BlbmVkOiAke21lc3NhZ2UubWVzc2FnZUlkfScpOwoKICAgICAgZmluYWwgbm90aWZpY2F0aW9uID0gbWVzc2FnZS5ub3RpZmljYXRpb247CgogICAgICBfbG9nZ2VyLmkoCiAgICAgICAgJyRfdGFnOiBOb3RpZmljYXRpb24gb3BlbmVkIGZyb20gJHNvdXJjZTogJHtub3RpZmljYXRpb24/LnRpdGxlfSAtICR7bm90aWZpY2F0aW9uPy5ib2R5fScsCiAgICAgICk7CgogICAgICAvLyBEZWVwIGxpbmsgY29udHJhY3Q6IHNlbmQgYSBgcm91dGVgIGluIHRoZSBtZXNzYWdlIGRhdGEgYW5kIHRoZSBhcHAKICAgICAgLy8gbmF2aWdhdGVzIHRoZXJlLCBlLmcuIHsicm91dGUiOiAiL2hvbWUvb3JkZXJzLzQyIn0uIEJyYW5jaCBvbiB5b3VyIG93bgogICAgICAvLyBwYXlsb2FkIGZpZWxkcyBoZXJlIGlmIHlvdSBuZWVkIHNvbWV0aGluZyByaWNoZXIgdGhhbiBhIHBhdGguCiAgICAgIGZpbmFsIHJvdXRlID0gbWVzc2FnZS5kYXRhWydyb3V0ZSddOwogICAgICBpZiAocm91dGUgaXMhIFN0cmluZyB8fCByb3V0ZS5pc0VtcHR5KSByZXR1cm47CgogICAgICBkaS5nZXQ8R29Sb3V0ZXI+KCkucHVzaChyb3V0ZSk7CiAgICB9IGNhdGNoIChlLCBzKSB7CiAgICAgIF9sb2dnZXIuZSgnJF90YWc6IEVycm9yIGhhbmRsaW5nIG5vdGlmaWNhdGlvbiBvcGVuZWQnLCBlLCBzKTsKICAgIH0KICB9CgogIEBvdmVycmlkZQogIFN0cmluZz8gZ2V0IGZjbVRva2VuID0+IF9mY21Ub2tlbjsKCiAgQG92ZXJyaWRlCiAgU3RyaW5nPyBnZXQgZGV2aWNlSWQgPT4gX2RldmljZUlkOwoKICBAb3ZlcnJpZGUKICBGdXR1cmU8dm9pZD4gZGlzcG9zZSgpIGFzeW5jIHsKICAgIF9sb2dnZXIuaSgnJF90YWc6IERpc3Bvc2luZyBGaXJlYmFzZSBub3RpZmljYXRpb24gY2xpZW50Jyk7CgogICAgLy8gQ2FuY2VsIGFsbCBzdWJzY3JpcHRpb25zIHRvIHByZXZlbnQgbWVtb3J5IGxlYWtzCiAgICBfZm9yZWdyb3VuZE1lc3NhZ2VTdWJzY3JpcHRpb24/LmNhbmNlbCgpOwogICAgX21lc3NhZ2VPcGVuZWRBcHBTdWJzY3JpcHRpb24/LmNhbmNlbCgpOwogICAgX3Rva2VuUmVmcmVzaFN1YnNjcmlwdGlvbj8uY2FuY2VsKCk7CgogICAgX2ZvcmVncm91bmRNZXNzYWdlU3Vic2NyaXB0aW9uID0gbnVsbDsKICAgIF9tZXNzYWdlT3BlbmVkQXBwU3Vic2NyaXB0aW9uID0gbnVsbDsKICAgIF90b2tlblJlZnJlc2hTdWJzY3JpcHRpb24gPSBudWxsOwoKICAgIF9maXJlYmFzZU1lc3NhZ2luZyA9IG51bGw7CiAgfQp9Cg==", "type": "text" }, { From bc6f7dfce5336f125c0e8463fc7a72dde489aa1e Mon Sep 17 00:00:00 2001 From: Ajay Kumar Date: Sat, 5 Sep 2026 15:52:42 -0500 Subject: [PATCH 7/7] ci: skip brick templates in the per-package format check The workflow runs `dart format .` directly rather than the melos lint script, so excluding __brick__ there was not enough - the job still parsed the mustache templates and failed with exit 65. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/dart.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 04e8755..e796a3a 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -66,7 +66,12 @@ jobs: melos bootstrap --scope="${{ matrix.package }}" - name: Format package - run: melos exec --scope="${{ matrix.package }}" -- dart format . --set-exit-if-changed + # Brick templates are mustache, not parseable Dart, and `dart format` + # has no exclude of its own - so filter the paths before it sees them. + run: | + melos exec --scope="${{ matrix.package }}" -- \ + "find . -name '*.dart' -not -path '*/__brick__/*' -not -path '*/.dart_tool/*' \ + -exec dart format --set-exit-if-changed {} +" - name: Analyze package run: melos exec --scope="${{ matrix.package }}" -- dart analyze --fatal-infos