mirror of https://github.com/AMT-Cheif/drift.git
Add multi-package example
This commit is contained in:
parent
b58e97f99c
commit
af55bd0f92
|
@ -26,6 +26,8 @@ drift features:
|
|||
- The [migration] example makes use of advanced schema migrations and shows how
|
||||
to test migrations between different database schemas by using drift's
|
||||
[dedicated tooling][migration tooling] for this purpose.
|
||||
- There's an example showing how to share drift database definitions between a
|
||||
[server and a client][multi_package] in different packages.
|
||||
- [Another example][with_built_value] shows how to use drift-generated code in
|
||||
other builders (here, `built_value`).
|
||||
|
||||
|
@ -53,3 +55,5 @@ Additional patterns are also shown and explained on this website:
|
|||
[migration]: https://github.com/simolus3/drift/tree/develop/examples/migrations_example
|
||||
[migration tooling]: {{ '../Migrations/tests.md#verifying-migrations' | pageUrl }}
|
||||
[with_built_value]: https://github.com/simolus3/drift/tree/develop/examples/with_built_value
|
||||
[multi_package]: https://github.com/simolus3/drift/tree/develop/examples/multi_package
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ This collection of examples demonstrates how to use some advanced drift features
|
|||
- `migrations_example`: Example showing to how to generate test utilities to verify schema migration.
|
||||
- `modular`: Example using drift's upcoming modular generation mode.
|
||||
- `with_built_value`: Configure `build_runner` so that drift-generated classes can be used by `build_runner`.
|
||||
- `multi_package`: This example shows how to share drift definitions between packages.
|
||||
|
||||
These two examples exist to highlight a feature of `package:drift/web.dart` and `package:drift/web/workers.dart`.
|
||||
However, the setup shown here is now a core drift feature thanks to `WasmDatabase.open`, which means that this
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
This example shows how to use drift declarations across packages.
|
||||
It is structured as follows:
|
||||
|
||||
- `shared/` contains table definitions. This package does not define a database
|
||||
on its own (although that could be useful for testing), instead it declares
|
||||
tables used by the server and the client.
|
||||
- `server/` is a simple shelf server using Postgres with drift.
|
||||
- `client/` is a simple CLI client using a local sqlite3 database
|
||||
while also communicating with the server.
|
||||
|
||||
As the main point of this example is to demonstrate how the build
|
||||
setup could look like, the client and server are kept minimal.
|
||||
|
||||
To fully build the code, `build_runner run` needs to be run in all three
|
||||
packages.
|
||||
However, after making changes to the database code in one of the packages, only
|
||||
that package needs to be rebuilt.
|
||||
|
||||
## Starting
|
||||
|
||||
To run the server, first start a postgres database server:
|
||||
|
||||
```
|
||||
docker run -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres postgres
|
||||
```
|
||||
|
||||
Then, run the example by starting a server and a client:
|
||||
|
||||
```
|
||||
dart run server/bin/server.dart
|
||||
dart run client/bin/client.dart
|
||||
```
|
|
@ -0,0 +1,3 @@
|
|||
# https://dart.dev/guides/libraries/private-files
|
||||
# Created by `dart pub`
|
||||
.dart_tool/
|
|
@ -0,0 +1,30 @@
|
|||
# This file configures the static analysis results for your project (errors,
|
||||
# warnings, and lints).
|
||||
#
|
||||
# This enables the 'recommended' set of lints from `package:lints`.
|
||||
# This set helps identify many issues that may lead to problems when running
|
||||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||
# style and format.
|
||||
#
|
||||
# If you want a smaller set of lints you can change this to specify
|
||||
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||
# (the recommended set includes the core lints).
|
||||
# The core lints are also what is used by pub.dev for scoring packages.
|
||||
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
# Uncomment the following section to specify additional rules.
|
||||
|
||||
# linter:
|
||||
# rules:
|
||||
# - camel_case_types
|
||||
|
||||
# analyzer:
|
||||
# exclude:
|
||||
# - path/to/excluded/files/**
|
||||
|
||||
# For more information about the core and recommended set of lints, see
|
||||
# https://dart.dev/go/core-lints
|
||||
|
||||
# For additional information about configuring this file, see
|
||||
# https://dart.dev/guides/language/analysis-options
|
|
@ -0,0 +1,33 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:client/database.dart';
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift/native.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:shared/tables.dart';
|
||||
|
||||
void main(List<String> arguments) async {
|
||||
final database = ClientDatabase(NativeDatabase.memory());
|
||||
final client = http.Client();
|
||||
|
||||
// Fetch posts from server and save them in the local database.
|
||||
final fromServer =
|
||||
await client.get(Uri.parse('http://localhost:8080/posts/latest'));
|
||||
|
||||
await database.batch((batch) {
|
||||
final entries = json.decode(fromServer.body) as List;
|
||||
|
||||
for (final entry in entries) {
|
||||
final post = Post.fromJson(entry['post']);
|
||||
final user = User.fromJson(entry['author']);
|
||||
|
||||
batch.insert(database.posts, post);
|
||||
batch.insert(database.users, user, onConflict: DoUpdate((old) => user));
|
||||
}
|
||||
});
|
||||
|
||||
final localPosts = await database.locallySavedPosts;
|
||||
print('Saved local posts: $localPosts');
|
||||
|
||||
await database.close();
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
targets:
|
||||
$default:
|
||||
builders:
|
||||
drift_dev:
|
||||
# disable drift's default builder, we're using the modular setup
|
||||
# instead.
|
||||
enabled: false
|
||||
|
||||
# Instead, enable drift_dev:analyzer and drift_dev:modular manually:
|
||||
drift_dev:analyzer:
|
||||
enabled: true
|
||||
options: &options
|
||||
# Drift build options, as per https://drift.simonbinder.eu/docs/advanced-features/builder_options/
|
||||
store_date_time_values_as_text: true
|
||||
named_parameters: true
|
||||
sql:
|
||||
dialects:
|
||||
- sqlite
|
||||
options:
|
||||
version: "3.45"
|
||||
modules: [fts5]
|
||||
drift_dev:modular:
|
||||
enabled: true
|
||||
# We use yaml anchors to give the two builders the same options
|
||||
options: *options
|
|
@ -0,0 +1,17 @@
|
|||
import 'package:drift/drift.dart';
|
||||
|
||||
import 'database.drift.dart';
|
||||
|
||||
@DriftDatabase(include: {'package:shared/shared.drift'})
|
||||
class ClientDatabase extends $ClientDatabase {
|
||||
ClientDatabase(super.e);
|
||||
|
||||
@override
|
||||
int get schemaVersion => 1;
|
||||
|
||||
Future<int> get locallySavedPosts async {
|
||||
final count = countAll();
|
||||
final query = selectOnly(posts)..addColumns([count]);
|
||||
return query.map((row) => row.read(count)!).getSingle();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:shared/src/users.drift.dart' as i1;
|
||||
import 'package:shared/src/posts.drift.dart' as i2;
|
||||
import 'package:shared/shared.drift.dart' as i3;
|
||||
import 'package:drift/internal/modular.dart' as i4;
|
||||
|
||||
abstract class $ClientDatabase extends i0.GeneratedDatabase {
|
||||
$ClientDatabase(i0.QueryExecutor e) : super(e);
|
||||
late final i1.$UsersTable users = i1.$UsersTable(this);
|
||||
late final i2.Posts posts = i2.Posts(this);
|
||||
i3.SharedDrift get sharedDrift => i4.ReadDatabaseContainer(this)
|
||||
.accessor<i3.SharedDrift>(i3.SharedDrift.new);
|
||||
@override
|
||||
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
|
||||
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
|
||||
@override
|
||||
List<i0.DatabaseSchemaEntity> get allSchemaEntities => [users, posts];
|
||||
@override
|
||||
i0.DriftDatabaseOptions get options =>
|
||||
const i0.DriftDatabaseOptions(storeDateTimeAsText: true);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
name: client
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ^3.2.5
|
||||
|
||||
dependencies:
|
||||
drift: ^2.15.0
|
||||
http: ^1.2.0
|
||||
shared:
|
||||
path: ../shared/
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^2.1.0
|
||||
test: ^1.24.0
|
||||
drift_dev: ^2.15.0
|
||||
build_runner: ^2.4.8
|
|
@ -0,0 +1,3 @@
|
|||
# https://dart.dev/guides/libraries/private-files
|
||||
# Created by `dart pub`
|
||||
.dart_tool/
|
|
@ -0,0 +1,30 @@
|
|||
# This file configures the static analysis results for your project (errors,
|
||||
# warnings, and lints).
|
||||
#
|
||||
# This enables the 'recommended' set of lints from `package:lints`.
|
||||
# This set helps identify many issues that may lead to problems when running
|
||||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||
# style and format.
|
||||
#
|
||||
# If you want a smaller set of lints you can change this to specify
|
||||
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||
# (the recommended set includes the core lints).
|
||||
# The core lints are also what is used by pub.dev for scoring packages.
|
||||
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
# Uncomment the following section to specify additional rules.
|
||||
|
||||
# linter:
|
||||
# rules:
|
||||
# - camel_case_types
|
||||
|
||||
# analyzer:
|
||||
# exclude:
|
||||
# - path/to/excluded/files/**
|
||||
|
||||
# For more information about the core and recommended set of lints, see
|
||||
# https://dart.dev/go/core-lints
|
||||
|
||||
# For additional information about configuring this file, see
|
||||
# https://dart.dev/guides/language/analysis-options
|
|
@ -0,0 +1,75 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift_postgres/drift_postgres.dart';
|
||||
import 'package:postgres/postgres.dart';
|
||||
import 'package:server/database.dart';
|
||||
import 'package:shared/tables.dart';
|
||||
import 'package:shelf/shelf.dart';
|
||||
import 'package:shelf/shelf_io.dart';
|
||||
import 'package:shelf_router/shelf_router.dart';
|
||||
|
||||
// To run this server, first start a local postgres server with
|
||||
//
|
||||
// docker run -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres postgres
|
||||
//
|
||||
void main(List<String> args) async {
|
||||
// Use any available host or container IP (usually `0.0.0.0`).
|
||||
final ip = InternetAddress.anyIPv4;
|
||||
final database = ServerDatabase(PgDatabase(
|
||||
endpoint: Endpoint(
|
||||
host: 'localhost',
|
||||
database: 'postgres',
|
||||
username: 'postgres',
|
||||
password: 'postgres',
|
||||
),
|
||||
settings: ConnectionSettings(
|
||||
// Disable because this example is talking to a local postgres container.
|
||||
sslMode: SslMode.disable,
|
||||
),
|
||||
));
|
||||
|
||||
final router = Router()
|
||||
..post('/post', (Request request) async {
|
||||
final header = request.headers['Authorization'];
|
||||
if (header == null || !header.startsWith('Bearer ')) {
|
||||
return Response.unauthorized('Missing Authorization header');
|
||||
}
|
||||
|
||||
final user =
|
||||
await database.authenticateUser(header.substring('Bearer '.length));
|
||||
if (user == null) {
|
||||
return Response.unauthorized('Invalid token');
|
||||
}
|
||||
|
||||
database.posts.insertOne(PostsCompanion.insert(
|
||||
author: user.id, content: Value(await request.readAsString())));
|
||||
|
||||
return Response(201);
|
||||
})
|
||||
..get('/posts/latest', (req) async {
|
||||
final somePosts = await database.sharedDrift
|
||||
.allPosts(limit: (_, __) => Limit(10, null))
|
||||
.get();
|
||||
|
||||
return Response.ok(
|
||||
json.encode([
|
||||
for (final post in somePosts)
|
||||
{
|
||||
'author': post.author,
|
||||
'post': post.posts,
|
||||
}
|
||||
]),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
);
|
||||
});
|
||||
|
||||
// Configure a pipeline that logs requests.
|
||||
final handler = Pipeline().addMiddleware(logRequests()).addHandler(router);
|
||||
|
||||
// For running in containers, we respect the PORT environment variable.
|
||||
final port = int.parse(Platform.environment['PORT'] ?? '8080');
|
||||
final server = await serve(handler, ip, port);
|
||||
print('Server listening on port ${server.port}');
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
targets:
|
||||
$default:
|
||||
builders:
|
||||
drift_dev:
|
||||
# disable drift's default builder, we're using the modular setup
|
||||
# instead.
|
||||
enabled: false
|
||||
|
||||
# Instead, enable drift_dev:analyzer and drift_dev:modular manually:
|
||||
drift_dev:analyzer:
|
||||
enabled: true
|
||||
options: &options
|
||||
# Drift build options, as per https://drift.simonbinder.eu/docs/advanced-features/builder_options/
|
||||
store_date_time_values_as_text: true
|
||||
named_parameters: true
|
||||
sql:
|
||||
# The server itself will only support postgres
|
||||
dialects:
|
||||
- postgres
|
||||
options:
|
||||
version: "3.45"
|
||||
modules: [fts5]
|
||||
drift_dev:modular:
|
||||
enabled: true
|
||||
# We use yaml anchors to give the two builders the same options
|
||||
options: *options
|
|
@ -0,0 +1,43 @@
|
|||
import 'package:drift/drift.dart';
|
||||
import 'package:shared/tables.dart';
|
||||
|
||||
import 'database.drift.dart';
|
||||
|
||||
// Additional table we only need on the server
|
||||
class ActiveSessions extends Table {
|
||||
IntColumn get user => integer().references(Users, #id)();
|
||||
TextColumn get bearerToken => text()();
|
||||
}
|
||||
|
||||
@DriftDatabase(
|
||||
tables: [ActiveSessions],
|
||||
include: {'package:shared/shared.drift'},
|
||||
)
|
||||
class ServerDatabase extends $ServerDatabase {
|
||||
ServerDatabase(super.e);
|
||||
|
||||
@override
|
||||
int get schemaVersion => 1;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration {
|
||||
return MigrationStrategy(
|
||||
beforeOpen: (details) async {
|
||||
if (details.wasCreated) {
|
||||
await users.insertOne(UsersCompanion.insert(name: 'Demo user'));
|
||||
await posts.insertOne(
|
||||
PostsCompanion.insert(author: 1, content: Value('Test post')));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<User?> authenticateUser(String token) async {
|
||||
final query = select(users).join(
|
||||
[innerJoin(activeSessions, activeSessions.user.equalsExp(users.id))]);
|
||||
query.where(activeSessions.bearerToken.equals(token));
|
||||
|
||||
final row = await query.getSingleOrNull();
|
||||
return row?.readTable(users);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:shared/src/users.drift.dart' as i1;
|
||||
import 'package:shared/src/posts.drift.dart' as i2;
|
||||
import 'package:server/database.drift.dart' as i3;
|
||||
import 'package:shared/shared.drift.dart' as i4;
|
||||
import 'package:drift/internal/modular.dart' as i5;
|
||||
import 'package:server/database.dart' as i6;
|
||||
|
||||
abstract class $ServerDatabase extends i0.GeneratedDatabase {
|
||||
$ServerDatabase(i0.QueryExecutor e) : super(e);
|
||||
late final i1.$UsersTable users = i1.$UsersTable(this);
|
||||
late final i2.Posts posts = i2.Posts(this);
|
||||
late final i3.$ActiveSessionsTable activeSessions =
|
||||
i3.$ActiveSessionsTable(this);
|
||||
i4.SharedDrift get sharedDrift => i5.ReadDatabaseContainer(this)
|
||||
.accessor<i4.SharedDrift>(i4.SharedDrift.new);
|
||||
@override
|
||||
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
|
||||
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
|
||||
@override
|
||||
List<i0.DatabaseSchemaEntity> get allSchemaEntities =>
|
||||
[users, posts, activeSessions];
|
||||
@override
|
||||
i0.DriftDatabaseOptions get options =>
|
||||
const i0.DriftDatabaseOptions(storeDateTimeAsText: true);
|
||||
}
|
||||
|
||||
class $ActiveSessionsTable extends i6.ActiveSessions
|
||||
with i0.TableInfo<$ActiveSessionsTable, i3.ActiveSession> {
|
||||
@override
|
||||
final i0.GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
$ActiveSessionsTable(this.attachedDatabase, [this._alias]);
|
||||
static const i0.VerificationMeta _userMeta =
|
||||
const i0.VerificationMeta('user');
|
||||
@override
|
||||
late final i0.GeneratedColumn<int> user = i0.GeneratedColumn<int>(
|
||||
'user', aliasedName, false,
|
||||
type: i0.DriftSqlType.int,
|
||||
requiredDuringInsert: true,
|
||||
defaultConstraints:
|
||||
i0.GeneratedColumn.constraintIsAlways('REFERENCES users (id)'));
|
||||
static const i0.VerificationMeta _bearerTokenMeta =
|
||||
const i0.VerificationMeta('bearerToken');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> bearerToken =
|
||||
i0.GeneratedColumn<String>('bearer_token', aliasedName, false,
|
||||
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
||||
@override
|
||||
List<i0.GeneratedColumn> get $columns => [user, bearerToken];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'active_sessions';
|
||||
@override
|
||||
i0.VerificationContext validateIntegrity(
|
||||
i0.Insertable<i3.ActiveSession> instance,
|
||||
{bool isInserting = false}) {
|
||||
final context = i0.VerificationContext();
|
||||
final data = instance.toColumns(true);
|
||||
if (data.containsKey('user')) {
|
||||
context.handle(
|
||||
_userMeta, user.isAcceptableOrUnknown(data['user']!, _userMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_userMeta);
|
||||
}
|
||||
if (data.containsKey('bearer_token')) {
|
||||
context.handle(
|
||||
_bearerTokenMeta,
|
||||
bearerToken.isAcceptableOrUnknown(
|
||||
data['bearer_token']!, _bearerTokenMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_bearerTokenMeta);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<i0.GeneratedColumn> get $primaryKey => const {};
|
||||
@override
|
||||
i3.ActiveSession map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return i3.ActiveSession(
|
||||
user: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.int, data['${effectivePrefix}user'])!,
|
||||
bearerToken: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.string, data['${effectivePrefix}bearer_token'])!,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
$ActiveSessionsTable createAlias(String alias) {
|
||||
return $ActiveSessionsTable(attachedDatabase, alias);
|
||||
}
|
||||
}
|
||||
|
||||
class ActiveSession extends i0.DataClass
|
||||
implements i0.Insertable<i3.ActiveSession> {
|
||||
final int user;
|
||||
final String bearerToken;
|
||||
const ActiveSession({required this.user, required this.bearerToken});
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
map['user'] = i0.Variable<int>(user);
|
||||
map['bearer_token'] = i0.Variable<String>(bearerToken);
|
||||
return map;
|
||||
}
|
||||
|
||||
i3.ActiveSessionsCompanion toCompanion(bool nullToAbsent) {
|
||||
return i3.ActiveSessionsCompanion(
|
||||
user: i0.Value(user),
|
||||
bearerToken: i0.Value(bearerToken),
|
||||
);
|
||||
}
|
||||
|
||||
factory ActiveSession.fromJson(Map<String, dynamic> json,
|
||||
{i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return ActiveSession(
|
||||
user: serializer.fromJson<int>(json['user']),
|
||||
bearerToken: serializer.fromJson<String>(json['bearerToken']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'user': serializer.toJson<int>(user),
|
||||
'bearerToken': serializer.toJson<String>(bearerToken),
|
||||
};
|
||||
}
|
||||
|
||||
i3.ActiveSession copyWith({int? user, String? bearerToken}) =>
|
||||
i3.ActiveSession(
|
||||
user: user ?? this.user,
|
||||
bearerToken: bearerToken ?? this.bearerToken,
|
||||
);
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('ActiveSession(')
|
||||
..write('user: $user, ')
|
||||
..write('bearerToken: $bearerToken')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(user, bearerToken);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is i3.ActiveSession &&
|
||||
other.user == this.user &&
|
||||
other.bearerToken == this.bearerToken);
|
||||
}
|
||||
|
||||
class ActiveSessionsCompanion extends i0.UpdateCompanion<i3.ActiveSession> {
|
||||
final i0.Value<int> user;
|
||||
final i0.Value<String> bearerToken;
|
||||
final i0.Value<int> rowid;
|
||||
const ActiveSessionsCompanion({
|
||||
this.user = const i0.Value.absent(),
|
||||
this.bearerToken = const i0.Value.absent(),
|
||||
this.rowid = const i0.Value.absent(),
|
||||
});
|
||||
ActiveSessionsCompanion.insert({
|
||||
required int user,
|
||||
required String bearerToken,
|
||||
this.rowid = const i0.Value.absent(),
|
||||
}) : user = i0.Value(user),
|
||||
bearerToken = i0.Value(bearerToken);
|
||||
static i0.Insertable<i3.ActiveSession> custom({
|
||||
i0.Expression<int>? user,
|
||||
i0.Expression<String>? bearerToken,
|
||||
i0.Expression<int>? rowid,
|
||||
}) {
|
||||
return i0.RawValuesInsertable({
|
||||
if (user != null) 'user': user,
|
||||
if (bearerToken != null) 'bearer_token': bearerToken,
|
||||
if (rowid != null) 'rowid': rowid,
|
||||
});
|
||||
}
|
||||
|
||||
i3.ActiveSessionsCompanion copyWith(
|
||||
{i0.Value<int>? user,
|
||||
i0.Value<String>? bearerToken,
|
||||
i0.Value<int>? rowid}) {
|
||||
return i3.ActiveSessionsCompanion(
|
||||
user: user ?? this.user,
|
||||
bearerToken: bearerToken ?? this.bearerToken,
|
||||
rowid: rowid ?? this.rowid,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
if (user.present) {
|
||||
map['user'] = i0.Variable<int>(user.value);
|
||||
}
|
||||
if (bearerToken.present) {
|
||||
map['bearer_token'] = i0.Variable<String>(bearerToken.value);
|
||||
}
|
||||
if (rowid.present) {
|
||||
map['rowid'] = i0.Variable<int>(rowid.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('ActiveSessionsCompanion(')
|
||||
..write('user: $user, ')
|
||||
..write('bearerToken: $bearerToken, ')
|
||||
..write('rowid: $rowid')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
name: server
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ^3.2.5
|
||||
|
||||
dependencies:
|
||||
args: ^2.4.0
|
||||
drift: ^2.15.0
|
||||
drift_postgres: ^1.1.0
|
||||
shelf: ^1.4.0
|
||||
shelf_router: ^1.1.0
|
||||
|
||||
shared:
|
||||
path: ../shared/
|
||||
postgres: ^3.0.8
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.4.8
|
||||
http: ^1.1.0
|
||||
lints: ^2.1.0
|
||||
test: ^1.24.0
|
||||
drift_dev: ^2.15.0
|
|
@ -0,0 +1,7 @@
|
|||
# https://dart.dev/guides/libraries/private-files
|
||||
# Created by `dart pub`
|
||||
.dart_tool/
|
||||
|
||||
# Avoid committing pubspec.lock for library packages; see
|
||||
# https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
pubspec.lock
|
|
@ -0,0 +1,30 @@
|
|||
# This file configures the static analysis results for your project (errors,
|
||||
# warnings, and lints).
|
||||
#
|
||||
# This enables the 'recommended' set of lints from `package:lints`.
|
||||
# This set helps identify many issues that may lead to problems when running
|
||||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||
# style and format.
|
||||
#
|
||||
# If you want a smaller set of lints you can change this to specify
|
||||
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||
# (the recommended set includes the core lints).
|
||||
# The core lints are also what is used by pub.dev for scoring packages.
|
||||
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
# Uncomment the following section to specify additional rules.
|
||||
|
||||
# linter:
|
||||
# rules:
|
||||
# - camel_case_types
|
||||
|
||||
# analyzer:
|
||||
# exclude:
|
||||
# - path/to/excluded/files/**
|
||||
|
||||
# For more information about the core and recommended set of lints, see
|
||||
# https://dart.dev/go/core-lints
|
||||
|
||||
# For additional information about configuring this file, see
|
||||
# https://dart.dev/guides/language/analysis-options
|
|
@ -0,0 +1,28 @@
|
|||
targets:
|
||||
$default:
|
||||
builders:
|
||||
drift_dev:
|
||||
# disable drift's default builder, we're using the modular setup
|
||||
# instead.
|
||||
enabled: false
|
||||
|
||||
# Instead, enable drift_dev:analyzer and drift_dev:modular manually:
|
||||
drift_dev:analyzer:
|
||||
enabled: true
|
||||
options: &options
|
||||
# Drift build options, as per https://drift.simonbinder.eu/docs/advanced-features/builder_options/
|
||||
store_date_time_values_as_text: true
|
||||
named_parameters: true
|
||||
sql:
|
||||
# This package will be embedded into a server (using postgres) and a client (using sqlite).
|
||||
# So we need to generate code for both.
|
||||
dialects:
|
||||
- sqlite
|
||||
- postgres
|
||||
options:
|
||||
version: "3.45"
|
||||
modules: [fts5]
|
||||
drift_dev:modular:
|
||||
enabled: true
|
||||
# We use yaml anchors to give the two builders the same options
|
||||
options: *options
|
|
@ -0,0 +1,11 @@
|
|||
-- Imports in drift files are transitive, so this is the drift equivalent
|
||||
-- of exporting files in `src/`.
|
||||
-- If we only had Dart-defined tables we wanted to expose, a Dart file exporting
|
||||
-- `users.dart` would have worked as well.
|
||||
|
||||
import 'src/posts.drift';
|
||||
import 'src/users.dart';
|
||||
|
||||
allPosts: SELECT posts.**, author.** FROM posts
|
||||
INNER JOIN users AS author ON author.id = posts.author
|
||||
LIMIT $limit;
|
|
@ -0,0 +1,45 @@
|
|||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:drift/internal/modular.dart' as i1;
|
||||
import 'package:shared/src/posts.drift.dart' as i2;
|
||||
import 'package:shared/src/users.drift.dart' as i3;
|
||||
|
||||
class SharedDrift extends i1.ModularAccessor {
|
||||
SharedDrift(i0.GeneratedDatabase db) : super(db);
|
||||
i0.Selectable<AllPostsResult> allPosts({required AllPosts$limit limit}) {
|
||||
var $arrayStartIndex = 1;
|
||||
final generatedlimit = $write(
|
||||
limit(this.posts, alias(this.users, 'author')),
|
||||
hasMultipleTables: true,
|
||||
startIndex: $arrayStartIndex);
|
||||
$arrayStartIndex += generatedlimit.amountOfVariables;
|
||||
return customSelect(
|
||||
'SELECT"posts"."author" AS "nested_0.author", "posts"."content" AS "nested_0.content","author"."id" AS "nested_1.id", "author"."name" AS "nested_1.name" FROM posts INNER JOIN users AS author ON author.id = posts.author ${generatedlimit.sql}',
|
||||
variables: [
|
||||
...generatedlimit.introducedVariables
|
||||
],
|
||||
readsFrom: {
|
||||
posts,
|
||||
users,
|
||||
...generatedlimit.watchedTables,
|
||||
}).asyncMap((i0.QueryRow row) async => AllPostsResult(
|
||||
posts: await posts.mapFromRow(row, tablePrefix: 'nested_0'),
|
||||
author: await users.mapFromRow(row, tablePrefix: 'nested_1'),
|
||||
));
|
||||
}
|
||||
|
||||
i2.Posts get posts => this.resultSet<i2.Posts>('posts');
|
||||
i3.$UsersTable get users => this.resultSet<i3.$UsersTable>('users');
|
||||
}
|
||||
|
||||
class AllPostsResult {
|
||||
final i2.Post posts;
|
||||
final i3.User author;
|
||||
AllPostsResult({
|
||||
required this.posts,
|
||||
required this.author,
|
||||
});
|
||||
}
|
||||
|
||||
typedef AllPosts$limit = i0.Limit Function(
|
||||
i2.Posts posts, i3.$UsersTable author);
|
|
@ -0,0 +1,6 @@
|
|||
import 'users.dart';
|
||||
|
||||
CREATE TABLE posts (
|
||||
author INTEGER NOT NULL REFERENCES users (id),
|
||||
content TEXT
|
||||
);
|
|
@ -0,0 +1,197 @@
|
|||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:shared/src/posts.drift.dart' as i1;
|
||||
|
||||
class Posts extends i0.Table with i0.TableInfo<Posts, i1.Post> {
|
||||
@override
|
||||
final i0.GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
Posts(this.attachedDatabase, [this._alias]);
|
||||
static const i0.VerificationMeta _authorMeta =
|
||||
const i0.VerificationMeta('author');
|
||||
late final i0.GeneratedColumn<int> author = i0.GeneratedColumn<int>(
|
||||
'author', aliasedName, false,
|
||||
type: i0.DriftSqlType.int,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'NOT NULL REFERENCES users(id)');
|
||||
static const i0.VerificationMeta _contentMeta =
|
||||
const i0.VerificationMeta('content');
|
||||
late final i0.GeneratedColumn<String> content = i0.GeneratedColumn<String>(
|
||||
'content', aliasedName, true,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: false,
|
||||
$customConstraints: '');
|
||||
@override
|
||||
List<i0.GeneratedColumn> get $columns => [author, content];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'posts';
|
||||
@override
|
||||
i0.VerificationContext validateIntegrity(i0.Insertable<i1.Post> instance,
|
||||
{bool isInserting = false}) {
|
||||
final context = i0.VerificationContext();
|
||||
final data = instance.toColumns(true);
|
||||
if (data.containsKey('author')) {
|
||||
context.handle(_authorMeta,
|
||||
author.isAcceptableOrUnknown(data['author']!, _authorMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_authorMeta);
|
||||
}
|
||||
if (data.containsKey('content')) {
|
||||
context.handle(_contentMeta,
|
||||
content.isAcceptableOrUnknown(data['content']!, _contentMeta));
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<i0.GeneratedColumn> get $primaryKey => const {};
|
||||
@override
|
||||
i1.Post map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return i1.Post(
|
||||
author: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.int, data['${effectivePrefix}author'])!,
|
||||
content: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}content']),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Posts createAlias(String alias) {
|
||||
return Posts(attachedDatabase, alias);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get dontWriteConstraints => true;
|
||||
}
|
||||
|
||||
class Post extends i0.DataClass implements i0.Insertable<i1.Post> {
|
||||
final int author;
|
||||
final String? content;
|
||||
const Post({required this.author, this.content});
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
map['author'] = i0.Variable<int>(author);
|
||||
if (!nullToAbsent || content != null) {
|
||||
map['content'] = i0.Variable<String>(content);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
i1.PostsCompanion toCompanion(bool nullToAbsent) {
|
||||
return i1.PostsCompanion(
|
||||
author: i0.Value(author),
|
||||
content: content == null && nullToAbsent
|
||||
? const i0.Value.absent()
|
||||
: i0.Value(content),
|
||||
);
|
||||
}
|
||||
|
||||
factory Post.fromJson(Map<String, dynamic> json,
|
||||
{i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return Post(
|
||||
author: serializer.fromJson<int>(json['author']),
|
||||
content: serializer.fromJson<String?>(json['content']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'author': serializer.toJson<int>(author),
|
||||
'content': serializer.toJson<String?>(content),
|
||||
};
|
||||
}
|
||||
|
||||
i1.Post copyWith(
|
||||
{int? author, i0.Value<String?> content = const i0.Value.absent()}) =>
|
||||
i1.Post(
|
||||
author: author ?? this.author,
|
||||
content: content.present ? content.value : this.content,
|
||||
);
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('Post(')
|
||||
..write('author: $author, ')
|
||||
..write('content: $content')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(author, content);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is i1.Post &&
|
||||
other.author == this.author &&
|
||||
other.content == this.content);
|
||||
}
|
||||
|
||||
class PostsCompanion extends i0.UpdateCompanion<i1.Post> {
|
||||
final i0.Value<int> author;
|
||||
final i0.Value<String?> content;
|
||||
final i0.Value<int> rowid;
|
||||
const PostsCompanion({
|
||||
this.author = const i0.Value.absent(),
|
||||
this.content = const i0.Value.absent(),
|
||||
this.rowid = const i0.Value.absent(),
|
||||
});
|
||||
PostsCompanion.insert({
|
||||
required int author,
|
||||
this.content = const i0.Value.absent(),
|
||||
this.rowid = const i0.Value.absent(),
|
||||
}) : author = i0.Value(author);
|
||||
static i0.Insertable<i1.Post> custom({
|
||||
i0.Expression<int>? author,
|
||||
i0.Expression<String>? content,
|
||||
i0.Expression<int>? rowid,
|
||||
}) {
|
||||
return i0.RawValuesInsertable({
|
||||
if (author != null) 'author': author,
|
||||
if (content != null) 'content': content,
|
||||
if (rowid != null) 'rowid': rowid,
|
||||
});
|
||||
}
|
||||
|
||||
i1.PostsCompanion copyWith(
|
||||
{i0.Value<int>? author,
|
||||
i0.Value<String?>? content,
|
||||
i0.Value<int>? rowid}) {
|
||||
return i1.PostsCompanion(
|
||||
author: author ?? this.author,
|
||||
content: content ?? this.content,
|
||||
rowid: rowid ?? this.rowid,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
if (author.present) {
|
||||
map['author'] = i0.Variable<int>(author.value);
|
||||
}
|
||||
if (content.present) {
|
||||
map['content'] = i0.Variable<String>(content.value);
|
||||
}
|
||||
if (rowid.present) {
|
||||
map['rowid'] = i0.Variable<int>(rowid.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('PostsCompanion(')
|
||||
..write('author: $author, ')
|
||||
..write('content: $content, ')
|
||||
..write('rowid: $rowid')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import 'package:drift/drift.dart';
|
||||
|
||||
class Users extends Table {
|
||||
IntColumn get id => integer().autoIncrement()();
|
||||
TextColumn get name => text()();
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:shared/src/users.drift.dart' as i1;
|
||||
import 'package:shared/src/users.dart' as i2;
|
||||
|
||||
class $UsersTable extends i2.Users with i0.TableInfo<$UsersTable, i1.User> {
|
||||
@override
|
||||
final i0.GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
$UsersTable(this.attachedDatabase, [this._alias]);
|
||||
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
|
||||
@override
|
||||
late final i0.GeneratedColumn<int> id = i0.GeneratedColumn<int>(
|
||||
'id', aliasedName, false,
|
||||
hasAutoIncrement: true,
|
||||
type: i0.DriftSqlType.int,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints:
|
||||
i0.GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
||||
static const i0.VerificationMeta _nameMeta =
|
||||
const i0.VerificationMeta('name');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> name = i0.GeneratedColumn<String>(
|
||||
'name', aliasedName, false,
|
||||
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
||||
@override
|
||||
List<i0.GeneratedColumn> get $columns => [id, name];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'users';
|
||||
@override
|
||||
i0.VerificationContext validateIntegrity(i0.Insertable<i1.User> instance,
|
||||
{bool isInserting = false}) {
|
||||
final context = i0.VerificationContext();
|
||||
final data = instance.toColumns(true);
|
||||
if (data.containsKey('id')) {
|
||||
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
|
||||
}
|
||||
if (data.containsKey('name')) {
|
||||
context.handle(
|
||||
_nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_nameMeta);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<i0.GeneratedColumn> get $primaryKey => {id};
|
||||
@override
|
||||
i1.User map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return i1.User(
|
||||
id: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.int, data['${effectivePrefix}id'])!,
|
||||
name: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
$UsersTable createAlias(String alias) {
|
||||
return $UsersTable(attachedDatabase, alias);
|
||||
}
|
||||
}
|
||||
|
||||
class User extends i0.DataClass implements i0.Insertable<i1.User> {
|
||||
final int id;
|
||||
final String name;
|
||||
const User({required this.id, required this.name});
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
map['id'] = i0.Variable<int>(id);
|
||||
map['name'] = i0.Variable<String>(name);
|
||||
return map;
|
||||
}
|
||||
|
||||
i1.UsersCompanion toCompanion(bool nullToAbsent) {
|
||||
return i1.UsersCompanion(
|
||||
id: i0.Value(id),
|
||||
name: i0.Value(name),
|
||||
);
|
||||
}
|
||||
|
||||
factory User.fromJson(Map<String, dynamic> json,
|
||||
{i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return User(
|
||||
id: serializer.fromJson<int>(json['id']),
|
||||
name: serializer.fromJson<String>(json['name']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'id': serializer.toJson<int>(id),
|
||||
'name': serializer.toJson<String>(name),
|
||||
};
|
||||
}
|
||||
|
||||
i1.User copyWith({int? id, String? name}) => i1.User(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
);
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('User(')
|
||||
..write('id: $id, ')
|
||||
..write('name: $name')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(id, name);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is i1.User && other.id == this.id && other.name == this.name);
|
||||
}
|
||||
|
||||
class UsersCompanion extends i0.UpdateCompanion<i1.User> {
|
||||
final i0.Value<int> id;
|
||||
final i0.Value<String> name;
|
||||
const UsersCompanion({
|
||||
this.id = const i0.Value.absent(),
|
||||
this.name = const i0.Value.absent(),
|
||||
});
|
||||
UsersCompanion.insert({
|
||||
this.id = const i0.Value.absent(),
|
||||
required String name,
|
||||
}) : name = i0.Value(name);
|
||||
static i0.Insertable<i1.User> custom({
|
||||
i0.Expression<int>? id,
|
||||
i0.Expression<String>? name,
|
||||
}) {
|
||||
return i0.RawValuesInsertable({
|
||||
if (id != null) 'id': id,
|
||||
if (name != null) 'name': name,
|
||||
});
|
||||
}
|
||||
|
||||
i1.UsersCompanion copyWith({i0.Value<int>? id, i0.Value<String>? name}) {
|
||||
return i1.UsersCompanion(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
if (id.present) {
|
||||
map['id'] = i0.Variable<int>(id.value);
|
||||
}
|
||||
if (name.present) {
|
||||
map['name'] = i0.Variable<String>(name.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('UsersCompanion(')
|
||||
..write('id: $id, ')
|
||||
..write('name: $name')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export 'src/users.dart';
|
||||
export 'src/users.drift.dart';
|
||||
export 'src/posts.drift.dart';
|
|
@ -0,0 +1,16 @@
|
|||
name: shared
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ^3.2.5
|
||||
|
||||
# Add regular dependencies here.
|
||||
dependencies:
|
||||
drift: ^2.15.0
|
||||
# it is important that drift_dev is a regular dependency here.
|
||||
drift_dev: ^2.15.0
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^2.1.0
|
||||
test: ^1.24.0
|
||||
build_runner: ^2.4.8
|
|
@ -8,6 +8,7 @@ packages:
|
|||
- drift_dev
|
||||
- sqlparser
|
||||
- examples/*
|
||||
- examples/multi_package/*
|
||||
- extras/benchmarks
|
||||
- extras/drift_devtools_extension
|
||||
- extras/drift_mariadb
|
||||
|
|
Loading…
Reference in New Issue