diff --git a/.github/workflows/celest_cloud_auth.yaml b/.github/workflows/celest_cloud_auth.yaml new file mode 100644 index 00000000..f253680c --- /dev/null +++ b/.github/workflows/celest_cloud_auth.yaml @@ -0,0 +1,51 @@ +name: celest_cloud_auth +on: + pull_request: + paths: + - ".github/workflows/celest_cloud_auth.yaml" + - "services/celest_cloud_auth/**" + +# Prevent duplicate runs due to Graphite +# https://graphite.dev/docs/troubleshooting#why-are-my-actions-running-twice +concurrency: + group: ${{ github.repository }}-${{ github.workflow }}-${{ github.ref }}-${{ github.ref == 'refs/heads/main' && github.sha || ''}} + cancel-in-progress: true + +jobs: + analyze_and_format: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Git Checkout + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # 4.1.7 + - name: Setup Flutter + uses: subosito/flutter-action@44ac965b96f18d999802d4b807e3256d5a3f9fa1 # 2.16.0 + with: + cache: true + - name: Setup Melos + run: dart pub global activate melos + - name: Get Packages + run: melos bootstrap + - name: Analyze + working-directory: services/celest_cloud_auth + run: dart analyze + - name: Format + working-directory: services/celest_cloud_auth + run: dart format --set-exit-if-changed . + test: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Git Checkout + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # 4.1.7 + - name: Setup Flutter + uses: subosito/flutter-action@44ac965b96f18d999802d4b807e3256d5a3f9fa1 # 2.16.0 + with: + cache: true + - name: Setup Melos + run: dart pub global activate melos + - name: Get Packages + run: melos bootstrap + - name: Test + working-directory: services/celest_cloud_auth + run: dart test diff --git a/README.md b/README.md index 6bb2374f..e00acc44 100644 --- a/README.md +++ b/README.md @@ -134,11 +134,17 @@ You have now set up your Celest project and integrated it into your Flutter app. | Package | Description | Pub | Checks | | -------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [celest](packages/celest/) | The main package for defining Celest backends. | | [![Celest](https://github.com/celest-dev/celest/actions/workflows/celest.yaml/badge.svg)](https://github.com/celest-dev/celest/actions/workflows/celest.yaml) | -| [celest_ast](packages/celest_ast/) | A structured representation of Celest projects. | | [![Celest AST](https://github.com/celest-dev/celest/actions/workflows/celest_ast.yaml/badge.svg)](https://github.com/celest-dev/celest/actions/workflows/celest_ast.yaml) | +| [celest_ast](packages/celest_ast/) | A structured representation of Celest projects. | | [![Celest AST](https://github.com/celest-dev/celest/actions/workflows/celest_ast.yaml/badge.svg)](https://github.com/celest-dev/celest/actions/workflows/celest_ast.yaml) | | [celest_auth](packages/celest_auth/) | The authentication and authorization runtimes for Celest. | | [![Celest Auth](https://github.com/celest-dev/celest/actions/workflows/celest_auth.yaml/badge.svg)](https://github.com/celest-dev/celest/actions/workflows/celest_auth.yaml) | | [celest_cloud](packages/celest_cloud/) | API contracts and Dart clients for the Celest Cloud platform. | | [![Celest Cloud](https://github.com/celest-dev/celest/actions/workflows/celest_cloud.yaml/badge.svg)](https://github.com/celest-dev/celest/actions/workflows/celest_cloud.yaml) | | [celest_core](packages/celest_core/) | Core types and utilities shared between Celest packages. | | [![Celest Core](https://github.com/celest-dev/celest/actions/workflows/celest_core.yaml/badge.svg)](https://github.com/celest-dev/celest/actions/workflows/celest_core.yaml) | +## Services + +| Service | Description | Pub | Checks | +| ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [celest_cloud_auth](packages/celest_cloud_auth/) | A Dart-native authentication and authorization solution built on [Cedar](https://github.com/celest-dev/cedar-dart) and Celest Data. | | [![Celest Cloud Auth](https://github.com/celest-dev/celest/actions/workflows/celest_cloud_auth.yaml/badge.svg)](https://github.com/celest-dev/celest/actions/workflows/celest_cloud_auth.yaml) | + ## License This repo is licensed under the [BSD-2-Clause-Patent](https://spdx.org/licenses/BSD-2-Clause-Patent.html) license. See [LICENSE.md](LICENSE.md) for the full text. diff --git a/assets/analyzer-plugin.gif b/assets/analyzer-plugin.gif new file mode 100644 index 00000000..02d0b831 Binary files /dev/null and b/assets/analyzer-plugin.gif differ diff --git a/examples/gemini/celest/client/pubspec.yaml b/examples/gemini/celest/client/pubspec.yaml index 6548f180..49ac9894 100644 --- a/examples/gemini/celest/client/pubspec.yaml +++ b/examples/gemini/celest/client/pubspec.yaml @@ -6,10 +6,10 @@ environment: sdk: ^3.4.0 dependencies: - celest: ^1.0.0-0 + celest: ^1.0.0 celest_backend: path: .. - celest_core: ^1.0.0-0 + celest_core: ^1.0.0 http: ^1.0.0 meta: ^1.12.0 native_storage: ^0.2.2 diff --git a/examples/gemini/celest/config/.gitkeep b/examples/gemini/celest/config/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/gemini/celest/functions b/examples/gemini/celest/functions deleted file mode 120000 index 6e1f1720..00000000 --- a/examples/gemini/celest/functions +++ /dev/null @@ -1 +0,0 @@ -/Users/dillonnys/celest/cloud/celest/examples/gemini/celest/lib/src/functions \ No newline at end of file diff --git a/examples/gemini/celest/project.dart b/examples/gemini/celest/project.dart deleted file mode 120000 index a0bad29d..00000000 --- a/examples/gemini/celest/project.dart +++ /dev/null @@ -1 +0,0 @@ -/Users/dillonnys/celest/cloud/celest/examples/gemini/celest/lib/src/project.dart \ No newline at end of file diff --git a/examples/gemini/celest/pubspec.yaml b/examples/gemini/celest/pubspec.yaml index f5553c00..0a71d916 100644 --- a/examples/gemini/celest/pubspec.yaml +++ b/examples/gemini/celest/pubspec.yaml @@ -6,9 +6,9 @@ environment: sdk: ^3.4.0 dependencies: - celest: ^1.0.0-0 + celest: ^1.0.0 celest_ast: ^0.1.0 - celest_core: ^1.0.0-0 + celest_core: ^1.0.0 google_generative_ai: ^0.2.0 http: '>=0.13.0 <2.0.0' meta: ^1.12.0 diff --git a/examples/openai/celest/client/pubspec.yaml b/examples/openai/celest/client/pubspec.yaml index 76164d3f..f835719c 100644 --- a/examples/openai/celest/client/pubspec.yaml +++ b/examples/openai/celest/client/pubspec.yaml @@ -6,10 +6,10 @@ environment: sdk: ^3.4.0 dependencies: - celest: ^1.0.0-0 + celest: ^1.0.0 celest_backend: path: .. - celest_core: ^1.0.0-0 + celest_core: ^1.0.0 http: ^1.0.0 meta: ^1.12.0 native_storage: ^0.2.2 diff --git a/examples/openai/celest/config/.gitkeep b/examples/openai/celest/config/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/openai/celest/functions b/examples/openai/celest/functions deleted file mode 120000 index 4bee9983..00000000 --- a/examples/openai/celest/functions +++ /dev/null @@ -1 +0,0 @@ -/Users/dillonnys/celest/cloud/celest/examples/openai/celest/lib/src/functions \ No newline at end of file diff --git a/examples/openai/celest/project.dart b/examples/openai/celest/project.dart deleted file mode 120000 index 85dce2c5..00000000 --- a/examples/openai/celest/project.dart +++ /dev/null @@ -1 +0,0 @@ -/Users/dillonnys/celest/cloud/celest/examples/openai/celest/lib/src/project.dart \ No newline at end of file diff --git a/examples/openai/celest/pubspec.yaml b/examples/openai/celest/pubspec.yaml index cdf101d0..3c8dfba7 100644 --- a/examples/openai/celest/pubspec.yaml +++ b/examples/openai/celest/pubspec.yaml @@ -6,9 +6,9 @@ environment: sdk: ^3.4.0 dependencies: - celest: ^1.0.0-0 + celest: ^1.0.0 celest_ast: ^0.1.0 - celest_core: ^1.0.0-0 + celest_core: ^1.0.0 chat_gpt_sdk: ^2.2.8 http: '>=0.13.0 <2.0.0' meta: ^1.12.0 diff --git a/examples/tasks/celest/client/lib/src/functions.dart b/examples/tasks/celest/client/lib/src/functions.dart index b6bc1184..bd715c1b 100644 --- a/examples/tasks/celest/client/lib/src/functions.dart +++ b/examples/tasks/celest/client/lib/src/functions.dart @@ -16,7 +16,6 @@ import 'package:celest_core/celest_core.dart' as _$celest; import 'package:celest_core/src/exception/cloud_exception.dart' as _$celest; import 'package:celest_core/src/exception/serialization_exception.dart' as _$celest; -import 'package:drift/src/remote/communication.dart' as _$drift_communication; import 'package:drift/src/runtime/cancellation_zone.dart' as _$drift_cancellation_zone; import 'package:drift/src/runtime/exceptions.dart' as _$drift_exceptions; @@ -27,8 +26,6 @@ import 'package:google_cloud/src/bad_request_exception.dart' import 'package:hrana/src/exception.dart' as _$hrana_exception; import 'package:http/src/exception.dart' as _$http_exception; import 'package:shelf/src/hijack_exception.dart' as _$shelf_hijack_exception; -import 'package:sqlite3/src/exception.dart' as _$sqlite3_exception; -import 'package:sqlite3/src/vfs.dart' as _$sqlite3_vfs; import 'package:tasks_client/tasks_client.dart'; class CelestFunctions { @@ -406,12 +403,6 @@ class CelestFunctionsTasks { errorValue), stackTrace, ); - case 'sqlite3.SqliteException': - Error.throwWithStackTrace( - _$celest.Serializers.instance - .deserialize<_$sqlite3_exception.SqliteException>(errorValue), - stackTrace, - ); case 'drift.CancellationException': Error.throwWithStackTrace( _$celest.Serializers.instance @@ -419,19 +410,6 @@ class CelestFunctionsTasks { errorValue), stackTrace, ); - case 'drift.ConnectionClosedException': - Error.throwWithStackTrace( - _$celest.Serializers.instance - .deserialize<_$drift_communication.ConnectionClosedException>( - errorValue), - stackTrace, - ); - case 'sqlite3.VfsException': - Error.throwWithStackTrace( - _$celest.Serializers.instance - .deserialize<_$sqlite3_vfs.VfsException>(errorValue), - stackTrace, - ); case 'hrana.ConnectionClosed': Error.throwWithStackTrace( _$celest.Serializers.instance diff --git a/examples/tasks/celest/client/lib/src/serializers.dart b/examples/tasks/celest/client/lib/src/serializers.dart index 77939ae4..9b1557ee 100644 --- a/examples/tasks/celest/client/lib/src/serializers.dart +++ b/examples/tasks/celest/client/lib/src/serializers.dart @@ -13,7 +13,6 @@ import 'package:celest_core/src/exception/cloud_exception.dart' as _$celest; import 'package:celest_core/src/exception/serialization_exception.dart' as _$celest; import 'package:celest_core/src/serialization/json_value.dart' as _$celest; -import 'package:drift/src/remote/communication.dart' as _$drift_communication; import 'package:drift/src/runtime/cancellation_zone.dart' as _$drift_cancellation_zone; import 'package:drift/src/runtime/exceptions.dart' as _$drift_exceptions; @@ -24,8 +23,6 @@ import 'package:google_cloud/src/bad_request_exception.dart' import 'package:hrana/src/exception.dart' as _$hrana_exception; import 'package:http/src/exception.dart' as _$http_exception; import 'package:shelf/src/hijack_exception.dart' as _$shelf_hijack_exception; -import 'package:sqlite3/src/exception.dart' as _$sqlite3_exception; -import 'package:sqlite3/src/vfs.dart' as _$sqlite3_vfs; void initSerializers({_$celest.Serializers? serializers}) { return runZoned( @@ -899,14 +896,6 @@ void initSerializers({_$celest.Serializers? serializers}) { ), const _$celest.TypeToken<_$celest.JsonValue?>('JsonValue'), ); - _$celest.Serializers.instance.put(_$celest.Serializer.define< - _$drift_communication.ConnectionClosedException, - Map?>( - serialize: ($value) => const {}, - deserialize: ($serialized) { - return _$drift_communication.ConnectionClosedException(); - }, - )); _$celest.Serializers.instance.put(_$celest.Serializer.define< _$drift_cancellation_zone.CancellationException, Map?>( @@ -1031,41 +1020,6 @@ void initSerializers({_$celest.Serializers? serializers}) { return _$shelf_hijack_exception.HijackException(); }, )); - _$celest.Serializers.instance.put(_$celest.Serializer.define< - _$sqlite3_exception.SqliteException, Map>( - serialize: ($value) => { - r'message': $value.message, - if ($value.explanation case final explanation?) - r'explanation': explanation, - r'extendedResultCode': $value.extendedResultCode, - r'resultCode': $value.resultCode, - if ($value.operation case final operation?) r'operation': operation, - if ($value.causingStatement case final causingStatement?) - r'causingStatement': causingStatement, - if ($value.parametersToStatement case final parametersToStatement?) - r'parametersToStatement': parametersToStatement, - }, - deserialize: ($serialized) { - return _$sqlite3_exception.SqliteException( - ($serialized[r'extendedResultCode'] as num).toInt(), - ($serialized[r'message'] as String), - ($serialized[r'explanation'] as String?), - ($serialized[r'causingStatement'] as String?), - ($serialized[r'parametersToStatement'] as Iterable?) - ?.toList(), - ($serialized[r'operation'] as String?), - ); - }, - )); - _$celest.Serializers.instance.put(_$celest.Serializer.define< - _$sqlite3_vfs.VfsException, Map>( - serialize: ($value) => - {r'returnCode': $value.returnCode}, - deserialize: ($serialized) { - return _$sqlite3_vfs.VfsException( - ($serialized[r'returnCode'] as num).toInt()); - }, - )); }, zoneValues: {_$celest.Serializers: serializers}, ); diff --git a/examples/tasks/celest/client/lib/tasks_client.dart b/examples/tasks/celest/client/lib/tasks_client.dart index 91456387..14cbf302 100644 --- a/examples/tasks/celest/client/lib/tasks_client.dart +++ b/examples/tasks/celest/client/lib/tasks_client.dart @@ -26,8 +26,8 @@ enum CelestEnvironment { Uri get baseUri => switch (this) { local => _$celest.kIsWeb || !Platform.isAndroid - ? Uri.parse('http://localhost:56902') - : Uri.parse('http://10.0.2.2:56902'), + ? Uri.parse('http://localhost:56903') + : Uri.parse('http://10.0.2.2:56903'), }; } diff --git a/examples/tasks/celest/client/pubspec.yaml b/examples/tasks/celest/client/pubspec.yaml index f489861c..bd9a3db9 100644 --- a/examples/tasks/celest/client/pubspec.yaml +++ b/examples/tasks/celest/client/pubspec.yaml @@ -6,10 +6,10 @@ environment: sdk: ^3.4.0 dependencies: - celest: ^1.0.0-0 + celest: ^1.0.0 celest_backend: path: .. - celest_core: ^1.0.0-0 + celest_core: ^1.0.0 http: ^1.0.0 native_storage: ^0.2.2 diff --git a/examples/tasks/celest/lib/src/generated/data.celest.dart b/examples/tasks/celest/lib/src/generated/data.celest.dart index 46109d52..35e04bdc 100644 --- a/examples/tasks/celest/lib/src/generated/data.celest.dart +++ b/examples/tasks/celest/lib/src/generated/data.celest.dart @@ -6,11 +6,8 @@ library; import 'package:celest/celest.dart'; import 'package:celest/src/core/context.dart'; +import 'package:celest/src/runtime/data/connect.dart'; import 'package:celest_backend/src/database/task_database.dart'; -import 'package:drift/native.dart'; -import 'package:drift/src/runtime/api/runtime_api.dart'; -import 'package:drift/src/runtime/executor/executor.dart'; -import 'package:drift_hrana/drift_hrana.dart'; /// The data services for the Celest backend. /// @@ -23,7 +20,7 @@ class CelestData { static Future init(Context context) async { context.put( _tasksDatabaseKey, - await _connect( + await connect( context, name: 'TaskDatabase', factory: TaskDatabase.new, @@ -40,40 +37,3 @@ class CelestData { static ContextKey get _tasksDatabaseKey => const ContextKey('TaskDatabase'); } - -/// Checks the connection to the database by running a simple query. -Future _checkConnection( - Database db) async { - await db.transaction(() async { - await db.customSelect('SELECT 1').get(); - }); - return db; -} - -/// Constructs a new [Database] and connects to it using the provided -/// [hostnameVariable] and [tokenSecret] configuration values. -Future _connect( - Context context, { - required String name, - required Database Function(QueryExecutor) factory, - required env hostnameVariable, - required secret tokenSecret, -}) async { - if (context.environment == Environment.local) { - return _checkConnection(factory(NativeDatabase.memory())); - } - final host = context.get(hostnameVariable); - final token = context.get(tokenSecret); - if (host == null || token == null) { - throw StateError( - 'Missing database hostname or token for $name. ' - 'Please set the `$hostnameVariable` and `$tokenSecret` values ' - 'in the environment or Celest configuration file.', - ); - } - final connector = HranaDatabase( - Uri(scheme: 'libsql', host: host), - jwtToken: token, - ); - return _checkConnection(factory(connector)); -} diff --git a/examples/tasks/celest/lib/src/project.dart b/examples/tasks/celest/lib/src/project.dart index 3cc251fe..473dff35 100644 --- a/examples/tasks/celest/lib/src/project.dart +++ b/examples/tasks/celest/lib/src/project.dart @@ -1,9 +1,7 @@ import 'package:celest/celest.dart'; import 'package:celest_backend/src/database/task_database.dart'; -const project = Project( - name: 'tasks', -); +const project = Project(name: 'tasks'); const tasksDatabase = Database( schema: Schema.drift(TaskDatabase), diff --git a/examples/tasks/celest/pubspec.yaml b/examples/tasks/celest/pubspec.yaml index 7ed68a7f..0c32fec3 100644 --- a/examples/tasks/celest/pubspec.yaml +++ b/examples/tasks/celest/pubspec.yaml @@ -6,8 +6,8 @@ environment: sdk: ^3.4.0 dependencies: - celest: ^1.0.0-0 - celest_core: ^1.0.0-0 + celest: ^1.0.0 + celest_core: ^1.0.0 drift: ^2.20.3 drift_hrana: ^1.0.2 meta: ^1.12.0 diff --git a/examples/tasks/web/sqlite3.wasm b/examples/tasks/web/sqlite3.wasm new file mode 100644 index 00000000..be71fe4b Binary files /dev/null and b/examples/tasks/web/sqlite3.wasm differ diff --git a/packages/celest/CHANGELOG.md b/packages/celest/CHANGELOG.md index f74a788f..10c49db1 100644 --- a/packages/celest/CHANGELOG.md +++ b/packages/celest/CHANGELOG.md @@ -1,4 +1,74 @@ -## 1.0.0-dev.0 +## 1.0.0 + +The first release of Celest V1! This release includes: + +- **Celest Data** + + Celest Data is a brand new database offering from Celest built off of SQLite and [Drift](https://pub.dev/packages/drift). + + To get started with Celest Data, add a new `Database` component to your Celest project and point it at your Drift schema. + + ```dart + import 'package:celest/celest.dart'; + import 'package:celest_backend/src/database/task_database.dart'; + + const project = Project(name: 'tasks'); + + const tasksDatabase = Database( + schema: Schema.drift(TaskDatabase), + ); + ``` + + When you run Celest locally, Celest Data will use SQLite to store your data. And when you deploy your Celest project, Celest Data + will automatically switch to using Celest Data backed by [Turso](https://turso.tech/). + +- **Celest Cloud Auth** + + [Celest Cloud Auth](https://pub.dev/packages/celest_cloud_auth) is an open-source backend service for authenticating users and authorizing access to resources. It is built on the [Cedar](https://www.cedarpolicy.com/en) policy engine and Celest Data. + + Now, when adding [Auth]() to your Celest project, Celest will automatically generate the necessary code to integrate Celest Cloud + Auth into your project so you can start authenticating users and controlling the resources they can access. + + ```dart + import 'package:celest/celest.dart'; + + const project = Project(name: 'my-project'); + + const auth = Auth( + providers: [ + AuthProvider.email(), + ], + ); + ``` + +- **Self-Hosting** + + Celest now supports self-hosting your Celest project. You can run your Celest project on your own server or cloud provider. + This allows you to have full control over your backend and data. + + The new `celest build` command will automatically generate a `Dockerfile` which you can deploy to any server of your choice. + +- **Improved Developer Experience** + + We have made several improvements to the developer experience, including better error messages, improved documentation, and a + new Dart analyzer plugin which helps you more seamlessly navigate your code. Now, when using the `Go To Definition` feature in + your IDE, you will be taken directly from your frontend to your backend code. + +
+ + ![Go To Definition](https://github.com/celest-dev/celest/blob/master/assets/analyzer-plugin.gif) + +
+ +- **...and much more on the Roadmap!** + + We are excited to continue building out the Celest platform and have many more features planned for the future. Our initial release + of Celest V1 provides the foundation for many new features to come include server-side rendering of Flutter app, better integration + of your Data models and Auth policies, Web Hosting, Storage, and much more! + + Stay tuned for more updates by following us on [X](https://x.com/Celest_Dev) and joining our [Discord](https://celest.dev/discord). + +Flutter is the future! 🚀 ## 0.4.2 diff --git a/packages/celest/example/celest/client/pubspec.yaml b/packages/celest/example/celest/client/pubspec.yaml index 1b718e74..d5b13127 100644 --- a/packages/celest/example/celest/client/pubspec.yaml +++ b/packages/celest/example/celest/client/pubspec.yaml @@ -6,10 +6,10 @@ environment: sdk: ^3.4.0 dependencies: - celest: ^1.0.0-0 + celest: ^1.0.0 celest_backend: path: ../ - celest_core: ^1.0.0-0 + celest_core: ^1.0.0 http: ^1.0.0 native_storage: ^0.2.2 diff --git a/packages/celest/example/celest/functions b/packages/celest/example/celest/functions deleted file mode 120000 index 5dacca80..00000000 --- a/packages/celest/example/celest/functions +++ /dev/null @@ -1 +0,0 @@ -/Users/dillonnys/celest/cloud/celest/packages/celest/example/celest/lib/src/functions \ No newline at end of file diff --git a/packages/celest/example/celest/generated b/packages/celest/example/celest/generated deleted file mode 120000 index 4b60e59d..00000000 --- a/packages/celest/example/celest/generated +++ /dev/null @@ -1 +0,0 @@ -/Users/dillonnys/celest/cloud/celest/packages/celest/example/celest/lib/src/generated \ No newline at end of file diff --git a/packages/celest/example/celest/project.dart b/packages/celest/example/celest/project.dart deleted file mode 120000 index 5e1a005c..00000000 --- a/packages/celest/example/celest/project.dart +++ /dev/null @@ -1 +0,0 @@ -/Users/dillonnys/celest/cloud/celest/packages/celest/example/celest/lib/src/project.dart \ No newline at end of file diff --git a/packages/celest/example/celest/pubspec.yaml b/packages/celest/example/celest/pubspec.yaml index 7a5b866d..339c618e 100644 --- a/packages/celest/example/celest/pubspec.yaml +++ b/packages/celest/example/celest/pubspec.yaml @@ -6,9 +6,9 @@ environment: sdk: ^3.4.0 dependencies: - celest: ^1.0.0-0 + celest: ^1.0.0 celest_ast: ^0.1.0 - celest_core: ^1.0.0-0 + celest_core: ^1.0.0 http: '>=0.13.0 <2.0.0' meta: ^1.12.0 diff --git a/packages/celest/example/celest/test/functions/greeting_test.dart b/packages/celest/example/celest/test/functions/greeting_test.dart index dc9ec6df..434ab085 100644 --- a/packages/celest/example/celest/test/functions/greeting_test.dart +++ b/packages/celest/example/celest/test/functions/greeting_test.dart @@ -1,9 +1,8 @@ import 'package:celest_backend/exceptions/bad_name_exception.dart'; import 'package:celest_backend/models/person.dart'; +import 'package:celest_backend/src/functions/greeting.dart'; import 'package:test/test.dart'; -import '../../functions/greeting.dart'; - void main() { group('greeting', () { test('sayHello', () async { diff --git a/packages/celest/lib/src/runtime/data/connect.dart b/packages/celest/lib/src/runtime/data/connect.dart new file mode 100644 index 00000000..64298db3 --- /dev/null +++ b/packages/celest/lib/src/runtime/data/connect.dart @@ -0,0 +1,46 @@ +import 'package:celest/src/config/config_values.dart'; +import 'package:celest/src/core/context.dart'; +import 'package:celest/src/core/environment.dart'; +import 'package:celest/src/runtime/data/connect.io.dart' + if (dart.library.js_interop) 'package:celest/src/runtime/data/connect.web.dart'; +import 'package:drift/drift.dart'; +import 'package:drift_hrana/drift_hrana.dart'; + +/// Checks the connection to the database by running a simple query. +Future _checkConnection( + Database db, +) async { + await db.transaction(() async { + await db.customSelect('SELECT 1').get(); + }); + return db; +} + +/// Constructs a new [Database] and connects to it using the provided +/// [hostnameVariable] and [tokenSecret] configuration values. +Future connect( + Context context, { + required String name, + required Database Function(QueryExecutor) factory, + required env hostnameVariable, + required secret tokenSecret, +}) async { + if (context.environment == Environment.local) { + final executor = await inMemoryExecutor(); + return _checkConnection(factory(executor)); + } + final host = context.get(hostnameVariable); + final token = context.get(tokenSecret); + if (host == null || token == null) { + throw StateError( + 'Missing database hostname or token for $name. ' + 'Please set the `$hostnameVariable` and `$tokenSecret` values ' + 'in the environment or Celest configuration file.', + ); + } + final connector = HranaDatabase( + Uri(scheme: 'libsql', host: host), + jwtToken: token, + ); + return _checkConnection(factory(connector)); +} diff --git a/packages/celest/lib/src/runtime/data/connect.io.dart b/packages/celest/lib/src/runtime/data/connect.io.dart new file mode 100644 index 00000000..0b95986e --- /dev/null +++ b/packages/celest/lib/src/runtime/data/connect.io.dart @@ -0,0 +1,5 @@ +import 'package:drift/drift.dart'; +import 'package:drift/native.dart'; + +/// A [QueryExecutor] for an in-memory database. +Future inMemoryExecutor() async => NativeDatabase.memory(); diff --git a/packages/celest/lib/src/runtime/data/connect.web.dart b/packages/celest/lib/src/runtime/data/connect.web.dart new file mode 100644 index 00000000..a2987134 --- /dev/null +++ b/packages/celest/lib/src/runtime/data/connect.web.dart @@ -0,0 +1,10 @@ +import 'package:drift/drift.dart'; +import 'package:drift/wasm.dart'; +import 'package:sqlite3/wasm.dart'; + +/// A [QueryExecutor] for an in-memory database. +Future inMemoryExecutor() async { + final sqlite3 = await WasmSqlite3.loadFromUrl(Uri.parse('./sqlite3.wasm')); + sqlite3.registerVirtualFileSystem(InMemoryFileSystem(), makeDefault: true); + return WasmDatabase.inMemory(sqlite3); +} diff --git a/packages/celest/lib/src/runtime/http/logging.dart b/packages/celest/lib/src/runtime/http/logging.dart index 0dd584e0..c51dba51 100644 --- a/packages/celest/lib/src/runtime/http/logging.dart +++ b/packages/celest/lib/src/runtime/http/logging.dart @@ -113,7 +113,7 @@ extension on Traceparent { extension on Frame { // https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogEntrySourceLocation Map get sourceLocation => { - // TODO: Will need to fix `package:` URIs to file paths when possible + // TODO(dnys1): Will need to fix `package:` URIs to file paths when possible // GoogleCloudPlatform/functions-framework-dart#40 'file': library, if (line != null) 'line': line.toString(), diff --git a/packages/celest/lib/src/runtime/http/middleware.dart b/packages/celest/lib/src/runtime/http/middleware.dart index 50492013..1a86ede0 100644 --- a/packages/celest/lib/src/runtime/http/middleware.dart +++ b/packages/celest/lib/src/runtime/http/middleware.dart @@ -1,7 +1,4 @@ -import 'package:celest/src/core/context.dart'; -import 'package:celest/src/functions/http/http_status.dart'; import 'package:celest/src/runtime/http/cloud_middleware.dart'; -import 'package:meta/meta.dart'; import 'package:shelf/shelf.dart' as shelf show Middleware; import 'package:shelf/shelf.dart'; @@ -16,35 +13,6 @@ abstract interface class Middleware { Handler call(Handler inner); } -abstract base class Listener implements Middleware { - const Listener(); - - Future handle(Request request) async { - final stopwatch = Stopwatch()..start(); - context.after((_) { - stopwatch.stop(); - context.log( - 'Request completed in ${stopwatch.elapsedMilliseconds}ms', - ); - }); - return const Handle.next(); - } - - @override - @nonVirtual - Handler call(Handler inner) { - return (request) async { - final handle = await this.handle(request); - return switch (handle) { - _HandleNext() => inner(request), - _HandleChange(:final change) => inner(change), - _HandleRedirect(:final to, :final status) => - Response(status, headers: {'location': to}), - }; - }; - } -} - // Interface guard // ignore: unused_element final shelf.Middleware _ = const _NoOpMiddleware().call; @@ -57,34 +25,3 @@ final class _NoOpMiddleware implements Middleware { return inner; } } - -sealed class Handle { - const Handle(); - - const factory Handle.next() = _HandleNext; - const factory Handle.change(Request change) = _HandleChange; - const factory Handle.redirect({ - required String to, - HttpStatus status, - }) = _HandleRedirect; -} - -final class _HandleNext extends Handle { - const _HandleNext(); -} - -final class _HandleChange extends Handle { - const _HandleChange(this.change); - - final Request change; -} - -final class _HandleRedirect extends Handle { - const _HandleRedirect({ - required this.to, - this.status = HttpStatus.found, - }); - - final String to; - final HttpStatus status; -} diff --git a/packages/celest/pubspec.yaml b/packages/celest/pubspec.yaml index c50a0b09..e0b53a2f 100644 --- a/packages/celest/pubspec.yaml +++ b/packages/celest/pubspec.yaml @@ -1,6 +1,6 @@ name: celest description: The Flutter cloud platform. Celest enables you to build your entire backend in Dart. -version: 1.0.0-dev.0 +version: 1.0.0 homepage: https://celest.dev repository: https://github.com/celest-dev/celest/tree/main/packages/celest @@ -11,15 +11,16 @@ environment: dependencies: async: ^2.11.0 celest_ast: ^0.1.0 - celest_auth: 1.0.0-dev.0 + celest_auth: ^1.0.0 celest_cloud: ^0.1.1 - celest_core: 1.0.0-dev.0 + celest_core: ^1.0.0 chunked_stream: ^1.4.2 cloud_http: ^0.1.0 collection: ^1.18.0 convert: ^3.1.1 crypto_keys: ^0.3.0 drift: ^2.20.0 + drift_hrana: ^1.0.2 file: ^7.0.1 fixnum: ^1.1.0 google_cloud: ^0.2.0 @@ -32,6 +33,7 @@ dependencies: shelf: ^1.4.1 shelf_router: ^1.1.4 shelf_web_socket: ^2.0.0 + sqlite3: ^2.4.6 stack_trace: ^1.11.1 stream_channel: ^2.1.2 web_socket_channel: ^3.0.0 diff --git a/packages/celest_auth/CHANGELOG.md b/packages/celest_auth/CHANGELOG.md index 561eb846..6e9a7334 100644 --- a/packages/celest_auth/CHANGELOG.md +++ b/packages/celest_auth/CHANGELOG.md @@ -1,4 +1,8 @@ -## 1.0.0-dev.0 +## 1.0.0 + +The first release of Celest V1! + +See the release notes for the [celest](https://pub.dev/packages/celest) package for more details. ## 0.4.1 diff --git a/packages/celest_auth/example/celest/client/pubspec.yaml b/packages/celest_auth/example/celest/client/pubspec.yaml index 7cea987d..f72359bc 100644 --- a/packages/celest_auth/example/celest/client/pubspec.yaml +++ b/packages/celest_auth/example/celest/client/pubspec.yaml @@ -6,10 +6,10 @@ environment: sdk: ^3.4.0 dependencies: - celest: ^1.0.0-0 + celest: ^1.0.0 celest_backend: path: ../ - celest_core: ^1.0.0-0 + celest_core: ^1.0.0 http: ^1.0.0 native_storage: ^0.2.2 diff --git a/packages/celest_auth/example/celest/functions b/packages/celest_auth/example/celest/functions deleted file mode 120000 index 477c423e..00000000 --- a/packages/celest_auth/example/celest/functions +++ /dev/null @@ -1 +0,0 @@ -/Users/dillonnys/celest/cloud/celest/packages/celest_auth/example/celest/lib/src/functions \ No newline at end of file diff --git a/packages/celest_auth/example/celest/generated b/packages/celest_auth/example/celest/generated deleted file mode 120000 index f49daae1..00000000 --- a/packages/celest_auth/example/celest/generated +++ /dev/null @@ -1 +0,0 @@ -/Users/dillonnys/celest/cloud/celest/packages/celest_auth/example/celest/lib/src/generated \ No newline at end of file diff --git a/packages/celest_auth/example/celest/project.dart b/packages/celest_auth/example/celest/project.dart deleted file mode 120000 index cd39dccd..00000000 --- a/packages/celest_auth/example/celest/project.dart +++ /dev/null @@ -1 +0,0 @@ -/Users/dillonnys/celest/cloud/celest/packages/celest_auth/example/celest/lib/src/project.dart \ No newline at end of file diff --git a/packages/celest_auth/example/celest/pubspec.yaml b/packages/celest_auth/example/celest/pubspec.yaml index b3769b41..81fda25b 100644 --- a/packages/celest_auth/example/celest/pubspec.yaml +++ b/packages/celest_auth/example/celest/pubspec.yaml @@ -6,12 +6,12 @@ environment: sdk: ^3.4.0 dependencies: - celest: ^1.0.0-0 + celest: ^1.0.0 celest_ast: ^0.1.0 celest_auth: 'any' celest_cloud_auth: path: ../../../../services/celest_cloud_auth - celest_core: ^1.0.0-0 + celest_core: ^1.0.0 drift_hrana: ^1.0.2 http: '>=0.13.0 <2.0.0' meta: ^1.12.0 diff --git a/packages/celest_auth/example/celest/test/functions/greeting_test.dart b/packages/celest_auth/example/celest/test/functions/greeting_test.dart index 6e2fc736..b6840d5c 100644 --- a/packages/celest_auth/example/celest/test/functions/greeting_test.dart +++ b/packages/celest_auth/example/celest/test/functions/greeting_test.dart @@ -1,8 +1,7 @@ import 'package:celest/celest.dart'; +import 'package:celest_backend/src/functions/greeting.dart'; import 'package:test/test.dart'; -import '../../functions/greeting.dart'; - void main() { group('greeting', () { test('sayHello', () async { diff --git a/packages/celest_auth/pubspec.yaml b/packages/celest_auth/pubspec.yaml index bc626a9f..8281a2ba 100644 --- a/packages/celest_auth/pubspec.yaml +++ b/packages/celest_auth/pubspec.yaml @@ -1,6 +1,6 @@ name: celest_auth description: The Auth runtime and client library for Celest, the Flutter cloud platform. -version: 1.0.0-dev.0 +version: 1.0.0 homepage: https://celest.dev repository: https://github.com/celest-dev/celest/tree/main/packages/celest_auth @@ -14,7 +14,7 @@ dependencies: built_value: ^8.9.1 cedar: ^0.2.2 celest_cloud: ^0.1.0 - celest_core: 1.0.0-dev.0 + celest_core: ^1.0.0 chunked_stream: ^1.4.2 corks_cedar: ^0.2.1 ffi: ^2.1.2 diff --git a/packages/celest_cloud/lib/src/util/operations.dart b/packages/celest_cloud/lib/src/util/operations.dart index d7bbfffa..22a1996f 100644 --- a/packages/celest_cloud/lib/src/util/operations.dart +++ b/packages/celest_cloud/lib/src/util/operations.dart @@ -88,7 +88,7 @@ extension on Status { extension on T { T unpack(Any any) { - return any.unpackInto(this.createEmptyInstance() as T); + return any.unpackInto(createEmptyInstance() as T); } } diff --git a/packages/celest_cloud/pubspec.yaml b/packages/celest_cloud/pubspec.yaml index 97d6aa66..29402cb3 100644 --- a/packages/celest_cloud/pubspec.yaml +++ b/packages/celest_cloud/pubspec.yaml @@ -1,6 +1,6 @@ name: celest_cloud description: API contracts and Dart clients for the Celest Cloud platform. -version: 0.1.4-wip +version: 0.1.4 repository: https://github.com/celest-dev/celest environment: @@ -8,7 +8,7 @@ environment: dependencies: async: ^2.11.0 - celest_core: ^0.5.0-dev.1 + celest_core: ^1.0.0 code_builder: ^4.10.0 collection: ^1.18.0 fixnum: ^1.1.0 diff --git a/packages/celest_core/CHANGELOG.md b/packages/celest_core/CHANGELOG.md index 451f36f2..652353f6 100644 --- a/packages/celest_core/CHANGELOG.md +++ b/packages/celest_core/CHANGELOG.md @@ -1,4 +1,8 @@ -## 1.0.0-dev.0 +## 1.0.0 + +The first release of Celest V1! + +See the release notes for the [celest](https://pub.dev/packages/celest) package for more details. ## 0.4.2 diff --git a/packages/celest_core/pubspec.yaml b/packages/celest_core/pubspec.yaml index 68d01412..c3cadc19 100644 --- a/packages/celest_core/pubspec.yaml +++ b/packages/celest_core/pubspec.yaml @@ -1,6 +1,6 @@ name: celest_core description: Celest types and utilities shared between the client and the cloud. -version: 1.0.0-dev.0 +version: 1.0.0 homepage: https://celest.dev repository: https://github.com/celest-dev/celest/tree/main/packages/celest_core diff --git a/services/celest_cloud_auth/LICENSE b/services/celest_cloud_auth/LICENSE new file mode 100644 index 00000000..d8101f5c --- /dev/null +++ b/services/celest_cloud_auth/LICENSE @@ -0,0 +1,46 @@ +Copyright (c) 2024 Teo, Inc. (Celest) + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Subject to the terms and conditions of this license, each copyright holder and +contributor hereby grants to those receiving rights under this license a +perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except for failure to satisfy the conditions of this license) patent license to +make, have made, use, offer to sell, sell, import, and otherwise transfer this +software, where such license applies only to those patent claims, already +acquired or hereafter acquired, licensable by such copyright holder or +contributor that are necessarily infringed by: + +(a) their Contribution(s) (the licensed copyrights of copyright holders and +non-copyrightable additions of contributors, in source or binary form) alone; or + +(b) combination of their Contribution(s) with the work of authorship to which +such Contribution(s) was added by such copyright holder or contributor, if, at +the time the Contribution is added, such addition causes such combination to be +necessarily infringed. The patent license shall not apply to any other +combinations which include the Contribution. + +Except as expressly stated above, no rights or licenses from any copyright +holder or contributor is granted under this license, whether expressly, by +implication, estoppel or otherwise. + +DISCLAIMER + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/services/celest_cloud_auth/README.md b/services/celest_cloud_auth/README.md index e695d9de..e457ad77 100644 --- a/services/celest_cloud_auth/README.md +++ b/services/celest_cloud_auth/README.md @@ -1,49 +1,52 @@ -A server app built using [Shelf](https://pub.dev/packages/shelf), -configured to enable running with [Docker](https://www.docker.com/). +# Celest Cloud Auth -This sample code handles HTTP GET requests to `/` and `/echo/` +A fully Dart-native solution for authenticating users and authorizing access to resources. Built on the [Cedar](https://www.cedarpolicy.com/en) policy engine and Celest Data. -# Running the sample +## Features -## Running with the Dart SDK +- **Authentication**: Securely authenticate users using email OTP, with support for passwordless and social logins coming soon. +- **Authorization**: Define and enforce access control policies using the [Cedar](https://www.cedarpolicy.com/en) policy engine. +- **Data**: Store user data and access control policies in SQLite or Celest Data. -You can run the example with the [Dart SDK](https://dart.dev/get-dart) -like this: +## Getting Started -``` -$ dart run bin/server.dart -Server listening on port 8080 -``` +The easiest way to get started with Celest Cloud Auth is to use the [Celest CLI](https://celest.dev/docs/get-started) and add an [AuthProvider](https://pub.dev/documentation/celest/latest/celest/AuthProvider-class.html) to your project. -And then from a second terminal: -``` -$ curl http://0.0.0.0:8080 -Hello, World! -$ curl http://0.0.0.0:8080/echo/I_love_Dart -I_love_Dart +```dart +import 'package:celest/celest.dart'; + +const project = Project(name: 'my-project'); + +const auth = Auth( + providers: [ + AuthProvider.email(), + ], +); ``` -## Running with Docker +Celest will automatically generate the necessary code to integrate Celest Cloud Auth into your project so you can start +authenticating users and controlling the resources they can access. -If you have [Docker Desktop](https://www.docker.com/get-started) installed, you -can build and run with the `docker` command: +Celest Cloud Auth can also be integrated into and Dart server, though, using the [shelf](https://pub.dev/packages/shelf) package. -``` -$ docker build . -t myserver -$ docker run -it -p 8080:8080 myserver -Server listening on port 8080 -``` +```dart +Future main() async { + final auth = await CelestCloudAuth.create( + database: AuthDatabase.memory(), + ); -And then from a second terminal: -``` -$ curl http://0.0.0.0:8080 -Hello, World! -$ curl http://0.0.0.0:8080/echo/I_love_Dart -I_love_Dart -``` + // Adds authentication routes. + final router = Router(); + router.mount('/v1alpha1/auth/', auth.handler); -You should see the logging printed in the first terminal: -``` -2021-05-06T15:47:04.620417 0:00:00.000158 GET [200] / -2021-05-06T15:47:08.392928 0:00:00.001216 GET [200] /echo/I_love_Dart + // Adds authorization middleware. + final pipeline = const Pipeline().addMiddleware(auth.middleware); + + final server = await serve( + pipeline.addHandler(router.call), + InternetAddress.anyIpV4, + 8080, + ); + print('Serving at http://${server.address.host}:${server.port}'); +} ``` diff --git a/services/celest_cloud_auth/bin/server.dart b/services/celest_cloud_auth/bin/server.dart index 87e8f26f..e090e135 100644 --- a/services/celest_cloud_auth/bin/server.dart +++ b/services/celest_cloud_auth/bin/server.dart @@ -24,7 +24,7 @@ void main(List args) async { // Configure a pipeline that logs requests. final handler = - Pipeline().addMiddleware(logRequests()).addHandler(_router.call); + const Pipeline().addMiddleware(logRequests()).addHandler(_router.call); // For running in containers, we respect the PORT environment variable. final port = int.parse(Platform.environment['PORT'] ?? '8080'); diff --git a/services/celest_cloud_auth/lib/src/authorization/policy_set.g.dart b/services/celest_cloud_auth/lib/src/authorization/policy_set.g.dart index 25be3684..968e5b93 100644 --- a/services/celest_cloud_auth/lib/src/authorization/policy_set.g.dart +++ b/services/celest_cloud_auth/lib/src/authorization/policy_set.g.dart @@ -4,7 +4,7 @@ library; import 'package:cedar/cedar.dart'; /// The core policy set. -/// +/// /// Included policies: /// - policies.cedar final corePolicySet = PolicySet.parse(corePolicySetCedar); diff --git a/services/celest_cloud_auth/lib/src/database/schema/converters/auth_converters.dart b/services/celest_cloud_auth/lib/src/database/schema/converters/auth_converters.dart index d48aaeba..6c51b3b2 100644 --- a/services/celest_cloud_auth/lib/src/database/schema/converters/auth_converters.dart +++ b/services/celest_cloud_auth/lib/src/database/schema/converters/auth_converters.dart @@ -1,5 +1,5 @@ -import 'package:celest_cloud_auth/src/authentication/authentication_model.dart'; import 'package:celest_cloud/src/proto.dart' as pb; +import 'package:celest_cloud_auth/src/authentication/authentication_model.dart'; import 'package:drift/drift.dart'; import 'package:protobuf/protobuf.dart'; diff --git a/services/celest_cloud_auth/lib/src/users/users_service.dart b/services/celest_cloud_auth/lib/src/users/users_service.dart index 11f70c05..11ffbc6e 100644 --- a/services/celest_cloud_auth/lib/src/users/users_service.dart +++ b/services/celest_cloud_auth/lib/src/users/users_service.dart @@ -3,6 +3,7 @@ import 'package:cedar/cedar.dart' hide Value; // ignore: invalid_use_of_internal_member import 'package:celest/src/runtime/http/cloud_middleware.dart'; import 'package:celest_ast/celest_ast.dart' hide Variable; +import 'package:celest_cloud/src/proto.dart' as pb; import 'package:celest_cloud_auth/src/authorization/authorization_middleware.dart'; import 'package:celest_cloud_auth/src/authorization/authorizer.dart'; import 'package:celest_cloud_auth/src/authorization/celest_action.dart'; @@ -15,7 +16,6 @@ import 'package:celest_cloud_auth/src/model/interop.dart'; import 'package:celest_cloud_auth/src/model/order_by.dart'; import 'package:celest_cloud_auth/src/model/page_token.dart'; import 'package:celest_cloud_auth/src/model/route_map.dart'; -import 'package:celest_cloud/src/proto.dart' as pb; import 'package:celest_core/celest_core.dart'; import 'package:drift/drift.dart' as drift; import 'package:drift/drift.dart'; diff --git a/services/celest_cloud_auth/pubspec.yaml b/services/celest_cloud_auth/pubspec.yaml index 0b75e26e..8ab70cea 100644 --- a/services/celest_cloud_auth/pubspec.yaml +++ b/services/celest_cloud_auth/pubspec.yaml @@ -1,5 +1,5 @@ name: celest_cloud_auth -description: The Celest authentication and authorization service. +description: A Dart-native authentication and authorization service built on Celest, Cedar, and SQLite. version: 0.1.0 environment: @@ -9,10 +9,10 @@ dependencies: args: ^2.4.0 async: ^2.11.0 cedar: ^0.2.2 - celest: ^1.0.0-0 + celest: ^1.0.0 celest_ast: ^0.1.0 celest_cloud: ^0.1.0 - celest_core: ^1.0.0-0 + celest_core: ^1.0.0 clock: ^1.1.1 collection: ^1.18.0 convert: ^3.1.1 diff --git a/services/celest_cloud_auth/tool/render_email.dart b/services/celest_cloud_auth/tool/render_email.dart index 9bad3b0d..04006952 100644 --- a/services/celest_cloud_auth/tool/render_email.dart +++ b/services/celest_cloud_auth/tool/render_email.dart @@ -43,7 +43,7 @@ Future _handleRequests(HttpServer server) async { final templates = { 'VerificationCode': ( - VerificationCodeEmail(), + const VerificationCodeEmail(), const VerificationCodeEmailParams( email: 'test@celest.dev', code: '123456',