social bookmarking for atproto

[appview] generate database schema for clips and tags

hexmani.ac d0f63cb2 5fcd944d

verified
+12
.idea/dataSources.xml
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <project version="4"> 3 + <component name="DataSourceManagerImpl" format="xml" multifile-model="true"> 4 + <data-source source="LOCAL" name="Backend DB" read-only="true" uuid="befffb07-c652-4b9d-8f1c-31a01590f686"> 5 + <driver-ref>sqlite.xerial</driver-ref> 6 + <synchronize>true</synchronize> 7 + <jdbc-driver>org.sqlite.JDBC</jdbc-driver> 8 + <jdbc-url>jdbc:sqlite:backend/clippr.db</jdbc-url> 9 + <working-dir>$ProjectFileDir$</working-dir> 10 + </data-source> 11 + </component> 12 + </project>
+2
.idea/dictionaries/project.xml
··· 8 8 <w>clippr</w> 9 9 <w>dids</w> 10 10 <w>jetstream</w> 11 + <w>multiformats</w> 12 + <w>rkey</w> 11 13 </words> 12 14 </dictionary> 13 15 </component>
+8 -8
.idea/libraries/Dart_Packages.xml
··· 89 89 <entry key="build"> 90 90 <value> 91 91 <list> 92 - <option value="$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/build-2.5.0/lib" /> 92 + <option value="$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/build-2.5.1/lib" /> 93 93 </list> 94 94 </value> 95 95 </entry> ··· 110 110 <entry key="build_resolvers"> 111 111 <value> 112 112 <list> 113 - <option value="$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/build_resolvers-2.5.0/lib" /> 113 + <option value="$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/build_resolvers-2.5.1/lib" /> 114 114 </list> 115 115 </value> 116 116 </entry> 117 117 <entry key="build_runner"> 118 118 <value> 119 119 <list> 120 - <option value="$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/build_runner-2.5.0/lib" /> 120 + <option value="$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/build_runner-2.5.1/lib" /> 121 121 </list> 122 122 </value> 123 123 </entry> 124 124 <entry key="build_runner_core"> 125 125 <value> 126 126 <list> 127 - <option value="$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/build_runner_core-9.0.0/lib" /> 127 + <option value="$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/build_runner_core-9.0.1/lib" /> 128 128 </list> 129 129 </value> 130 130 </entry> ··· 710 710 <root url="file://$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/base_codecs-1.0.1/lib" /> 711 711 <root url="file://$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/boolean_selector-2.1.2/lib" /> 712 712 <root url="file://$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/buffer-1.2.3/lib" /> 713 - <root url="file://$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/build-2.5.0/lib" /> 713 + <root url="file://$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/build-2.5.1/lib" /> 714 714 <root url="file://$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/build_config-1.1.2/lib" /> 715 715 <root url="file://$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/build_daemon-4.0.4/lib" /> 716 - <root url="file://$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/build_resolvers-2.5.0/lib" /> 717 - <root url="file://$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/build_runner-2.5.0/lib" /> 718 - <root url="file://$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/build_runner_core-9.0.0/lib" /> 716 + <root url="file://$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/build_resolvers-2.5.1/lib" /> 717 + <root url="file://$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/build_runner-2.5.1/lib" /> 718 + <root url="file://$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/build_runner_core-9.0.1/lib" /> 719 719 <root url="file://$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/built_collection-5.1.1/lib" /> 720 720 <root url="file://$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/built_value-8.10.1/lib" /> 721 721 <root url="file://$PROJECT_DIR$/../.pub-cache/hosted/pub.dev/cbor-6.3.7/lib" />
+6
.idea/sqldialects.xml
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <project version="4"> 3 + <component name="SqlDialectMappings"> 4 + <file url="PROJECT" dialect="SQLite" /> 5 + </component> 6 + </project>
+1
backend/.gitignore
··· 2 2 # Created by `dart pub` 3 3 .dart_tool/ 4 4 config.yaml 5 + *.db
+2 -2
backend/CHANGELOG.md
··· 1 - ## 1.0.0 2 - 1 + # clippr-be changelog 2 + ## 0.1.0 3 3 - Initial version.
+2 -1
backend/README.md
··· 5 5 ```bash 6 6 cp config.example.yaml config.yaml 7 7 vi config.yaml # modify settings here 8 - dart run 8 + chmod +x tools/build_and_run.sh 9 + ./tools/build_and_run.sh 9 10 ```
+5 -1
backend/bin/clippr.dart
··· 15 15 Logger.logInfo("${ClipprPubspec.getName()} ${ClipprPubspec.getVersion()}"); 16 16 Logger.logInfo("Initializing config..."); 17 17 Config(); // initialize config 18 + Logger.logInfo("Initializing database at ${Config.getDatabaseName()}..."); 19 + launchDatabase(); 20 + Logger.logInfo("Initializing firehose..."); 21 + launchFirehose(); 18 22 Logger.logInfo("Starting server at ${Config.getHostname()}:${Config.getPort()}"); 19 - launchServer(); 23 + launchWebServer(); 20 24 Logger.logInfo("Server launched!"); 21 25 }
+1
backend/config.example.yaml
··· 6 6 server: 7 7 hostname: localhost 8 8 port: 9090 9 + database_name: "clippr.db" 9 10 10 11 ## General AT Protocol settings (relay communication for the moment) 11 12 network:
+15 -4
backend/lib/config/config.dart
··· 15 15 File file = File('config.yaml'); 16 16 if (file.existsSync()) { 17 17 yamlDoc = loadYaml(file.readAsStringSync()) as Map; 18 - } else { 19 - Logger.logSevere("Failed to read config.yaml"); 20 - exit(1); 18 + return; 19 + } 20 + 21 + file = File('config.yml'); 22 + if (file.existsSync()) { 23 + yamlDoc = loadYaml(file.readAsStringSync()) as Map; 24 + return; 21 25 } 26 + 27 + Logger.logSevere("Failed to read config.yaml"); 28 + exit(1); 22 29 } 23 30 24 31 static int getPort() { ··· 29 36 return yamlDoc?['server']['hostname']; 30 37 } 31 38 39 + static String getDatabaseName() { 40 + return yamlDoc?['server']['database_name']; 41 + } 42 + 32 43 static String getFirehoseProvider() { 33 44 return yamlDoc?['network']['firehose_provider']; 34 45 } 35 - } 46 + }
+89
backend/lib/db/database.dart
··· 1 + /* 2 + * clippr: a social bookmarking service for the AT Protocol 3 + * Copyright (c) 2025 clippr contributors. 4 + * SPDX-License-Identifier: AGPL-3.0-only 5 + */ 6 + 7 + import 'dart:convert'; 8 + import 'dart:io'; 9 + 10 + import 'package:atproto/atproto.dart'; 11 + import 'package:clippr/config/config.dart'; 12 + import 'package:drift/drift.dart'; 13 + import 'package:drift/native.dart'; 14 + 15 + part 'database.g.dart'; 16 + 17 + class Clips extends Table { 18 + DateTimeColumn get timestamp => 19 + dateTime().named("time_us").withDefault(currentDateAndTime)(); 20 + TextColumn get did => text()(); 21 + TextColumn get recordKey => text().named("rkey")(); 22 + TextColumn get url => text()(); 23 + TextColumn get title => text()(); 24 + TextColumn get description => text()(); 25 + TextColumn get notes => text().nullable()(); 26 + TextColumn get tags => text().map(const StrongRefConverter()).nullable()(); 27 + BoolColumn get unlisted => boolean()(); 28 + BoolColumn get unread => boolean().nullable()(); 29 + TextColumn get languages => 30 + text().map(const StringArrayConverter()).nullable()(); 31 + DateTimeColumn get createdAt => dateTime()(); 32 + @override 33 + Set<Column<Object>> get primaryKey => {timestamp}; 34 + } 35 + 36 + class Tags extends Table { 37 + DateTimeColumn get timestamp => 38 + dateTime().named("time_us").withDefault(currentDateAndTime)(); 39 + TextColumn get did => text()(); 40 + TextColumn get recordKey => text().named('rkey')(); 41 + TextColumn get name => text()(); 42 + TextColumn get color => text().nullable()(); 43 + DateTimeColumn get createdAt => dateTime()(); 44 + @override 45 + Set<Column<Object>> get primaryKey => {timestamp}; 46 + } 47 + 48 + @DriftDatabase(tables: [Clips]) 49 + class AppDatabase extends _$AppDatabase { 50 + AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection()); 51 + 52 + @override 53 + int get schemaVersion => 1; 54 + 55 + static QueryExecutor _openConnection() { 56 + return NativeDatabase.createInBackground(File(Config.getDatabaseName())); 57 + } 58 + } 59 + 60 + /// Convert an array of strings to and from JSON for Drift. 61 + class StringArrayConverter extends TypeConverter<List<String>, String> { 62 + const StringArrayConverter(); 63 + 64 + @override 65 + List<String> fromSql(String fromDb) { 66 + return (jsonDecode(fromDb) as List<dynamic>).cast<String>(); 67 + } 68 + 69 + @override 70 + String toSql(List<String> value) { 71 + return jsonEncode(value); 72 + } 73 + } 74 + 75 + /// Convert [StrongRef] to and from JSON for Drift. 76 + class StrongRefConverter extends TypeConverter<StrongRef, String> { 77 + const StrongRefConverter(); 78 + 79 + @override 80 + StrongRef fromSql(String fromDb) { 81 + final map = jsonDecode(fromDb); 82 + return StrongRef.fromJson(map); 83 + } 84 + 85 + @override 86 + String toSql(StrongRef value) { 87 + return jsonEncode(value.toJson()); 88 + } 89 + }
+1065
backend/lib/db/database.g.dart
··· 1 + // GENERATED CODE - DO NOT MODIFY BY HAND 2 + 3 + part of 'database.dart'; 4 + 5 + // ignore_for_file: type=lint 6 + class $ClipsTable extends Clips with TableInfo<$ClipsTable, Clip> { 7 + @override 8 + final GeneratedDatabase attachedDatabase; 9 + final String? _alias; 10 + $ClipsTable(this.attachedDatabase, [this._alias]); 11 + static const VerificationMeta _timestampMeta = const VerificationMeta( 12 + 'timestamp', 13 + ); 14 + @override 15 + late final GeneratedColumn<DateTime> timestamp = GeneratedColumn<DateTime>( 16 + 'time_us', 17 + aliasedName, 18 + false, 19 + type: DriftSqlType.dateTime, 20 + requiredDuringInsert: false, 21 + defaultValue: currentDateAndTime, 22 + ); 23 + static const VerificationMeta _didMeta = const VerificationMeta('did'); 24 + @override 25 + late final GeneratedColumn<String> did = GeneratedColumn<String>( 26 + 'did', 27 + aliasedName, 28 + false, 29 + type: DriftSqlType.string, 30 + requiredDuringInsert: true, 31 + ); 32 + static const VerificationMeta _recordKeyMeta = const VerificationMeta( 33 + 'recordKey', 34 + ); 35 + @override 36 + late final GeneratedColumn<String> recordKey = GeneratedColumn<String>( 37 + 'rkey', 38 + aliasedName, 39 + false, 40 + type: DriftSqlType.string, 41 + requiredDuringInsert: true, 42 + ); 43 + static const VerificationMeta _urlMeta = const VerificationMeta('url'); 44 + @override 45 + late final GeneratedColumn<String> url = GeneratedColumn<String>( 46 + 'url', 47 + aliasedName, 48 + false, 49 + type: DriftSqlType.string, 50 + requiredDuringInsert: true, 51 + ); 52 + static const VerificationMeta _titleMeta = const VerificationMeta('title'); 53 + @override 54 + late final GeneratedColumn<String> title = GeneratedColumn<String>( 55 + 'title', 56 + aliasedName, 57 + false, 58 + type: DriftSqlType.string, 59 + requiredDuringInsert: true, 60 + ); 61 + static const VerificationMeta _descriptionMeta = const VerificationMeta( 62 + 'description', 63 + ); 64 + @override 65 + late final GeneratedColumn<String> description = GeneratedColumn<String>( 66 + 'description', 67 + aliasedName, 68 + false, 69 + type: DriftSqlType.string, 70 + requiredDuringInsert: true, 71 + ); 72 + static const VerificationMeta _notesMeta = const VerificationMeta('notes'); 73 + @override 74 + late final GeneratedColumn<String> notes = GeneratedColumn<String>( 75 + 'notes', 76 + aliasedName, 77 + true, 78 + type: DriftSqlType.string, 79 + requiredDuringInsert: false, 80 + ); 81 + @override 82 + late final GeneratedColumnWithTypeConverter<StrongRef?, String> tags = 83 + GeneratedColumn<String>( 84 + 'tags', 85 + aliasedName, 86 + true, 87 + type: DriftSqlType.string, 88 + requiredDuringInsert: false, 89 + ).withConverter<StrongRef?>($ClipsTable.$convertertagsn); 90 + static const VerificationMeta _unlistedMeta = const VerificationMeta( 91 + 'unlisted', 92 + ); 93 + @override 94 + late final GeneratedColumn<bool> unlisted = GeneratedColumn<bool>( 95 + 'unlisted', 96 + aliasedName, 97 + false, 98 + type: DriftSqlType.bool, 99 + requiredDuringInsert: true, 100 + defaultConstraints: GeneratedColumn.constraintIsAlways( 101 + 'CHECK ("unlisted" IN (0, 1))', 102 + ), 103 + ); 104 + static const VerificationMeta _unreadMeta = const VerificationMeta('unread'); 105 + @override 106 + late final GeneratedColumn<bool> unread = GeneratedColumn<bool>( 107 + 'unread', 108 + aliasedName, 109 + true, 110 + type: DriftSqlType.bool, 111 + requiredDuringInsert: false, 112 + defaultConstraints: GeneratedColumn.constraintIsAlways( 113 + 'CHECK ("unread" IN (0, 1))', 114 + ), 115 + ); 116 + @override 117 + late final GeneratedColumnWithTypeConverter<List<String>?, String> languages = 118 + GeneratedColumn<String>( 119 + 'languages', 120 + aliasedName, 121 + true, 122 + type: DriftSqlType.string, 123 + requiredDuringInsert: false, 124 + ).withConverter<List<String>?>($ClipsTable.$converterlanguagesn); 125 + static const VerificationMeta _createdAtMeta = const VerificationMeta( 126 + 'createdAt', 127 + ); 128 + @override 129 + late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>( 130 + 'created_at', 131 + aliasedName, 132 + false, 133 + type: DriftSqlType.dateTime, 134 + requiredDuringInsert: true, 135 + ); 136 + @override 137 + List<GeneratedColumn> get $columns => [ 138 + timestamp, 139 + did, 140 + recordKey, 141 + url, 142 + title, 143 + description, 144 + notes, 145 + tags, 146 + unlisted, 147 + unread, 148 + languages, 149 + createdAt, 150 + ]; 151 + @override 152 + String get aliasedName => _alias ?? actualTableName; 153 + @override 154 + String get actualTableName => $name; 155 + static const String $name = 'clips'; 156 + @override 157 + VerificationContext validateIntegrity( 158 + Insertable<Clip> instance, { 159 + bool isInserting = false, 160 + }) { 161 + final context = VerificationContext(); 162 + final data = instance.toColumns(true); 163 + if (data.containsKey('time_us')) { 164 + context.handle( 165 + _timestampMeta, 166 + timestamp.isAcceptableOrUnknown(data['time_us']!, _timestampMeta), 167 + ); 168 + } 169 + if (data.containsKey('did')) { 170 + context.handle( 171 + _didMeta, 172 + did.isAcceptableOrUnknown(data['did']!, _didMeta), 173 + ); 174 + } else if (isInserting) { 175 + context.missing(_didMeta); 176 + } 177 + if (data.containsKey('rkey')) { 178 + context.handle( 179 + _recordKeyMeta, 180 + recordKey.isAcceptableOrUnknown(data['rkey']!, _recordKeyMeta), 181 + ); 182 + } else if (isInserting) { 183 + context.missing(_recordKeyMeta); 184 + } 185 + if (data.containsKey('url')) { 186 + context.handle( 187 + _urlMeta, 188 + url.isAcceptableOrUnknown(data['url']!, _urlMeta), 189 + ); 190 + } else if (isInserting) { 191 + context.missing(_urlMeta); 192 + } 193 + if (data.containsKey('title')) { 194 + context.handle( 195 + _titleMeta, 196 + title.isAcceptableOrUnknown(data['title']!, _titleMeta), 197 + ); 198 + } else if (isInserting) { 199 + context.missing(_titleMeta); 200 + } 201 + if (data.containsKey('description')) { 202 + context.handle( 203 + _descriptionMeta, 204 + description.isAcceptableOrUnknown( 205 + data['description']!, 206 + _descriptionMeta, 207 + ), 208 + ); 209 + } else if (isInserting) { 210 + context.missing(_descriptionMeta); 211 + } 212 + if (data.containsKey('notes')) { 213 + context.handle( 214 + _notesMeta, 215 + notes.isAcceptableOrUnknown(data['notes']!, _notesMeta), 216 + ); 217 + } 218 + if (data.containsKey('unlisted')) { 219 + context.handle( 220 + _unlistedMeta, 221 + unlisted.isAcceptableOrUnknown(data['unlisted']!, _unlistedMeta), 222 + ); 223 + } else if (isInserting) { 224 + context.missing(_unlistedMeta); 225 + } 226 + if (data.containsKey('unread')) { 227 + context.handle( 228 + _unreadMeta, 229 + unread.isAcceptableOrUnknown(data['unread']!, _unreadMeta), 230 + ); 231 + } 232 + if (data.containsKey('created_at')) { 233 + context.handle( 234 + _createdAtMeta, 235 + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), 236 + ); 237 + } else if (isInserting) { 238 + context.missing(_createdAtMeta); 239 + } 240 + return context; 241 + } 242 + 243 + @override 244 + Set<GeneratedColumn> get $primaryKey => {timestamp}; 245 + @override 246 + Clip map(Map<String, dynamic> data, {String? tablePrefix}) { 247 + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; 248 + return Clip( 249 + timestamp: attachedDatabase.typeMapping.read( 250 + DriftSqlType.dateTime, 251 + data['${effectivePrefix}time_us'], 252 + )!, 253 + did: attachedDatabase.typeMapping.read( 254 + DriftSqlType.string, 255 + data['${effectivePrefix}did'], 256 + )!, 257 + recordKey: attachedDatabase.typeMapping.read( 258 + DriftSqlType.string, 259 + data['${effectivePrefix}rkey'], 260 + )!, 261 + url: attachedDatabase.typeMapping.read( 262 + DriftSqlType.string, 263 + data['${effectivePrefix}url'], 264 + )!, 265 + title: attachedDatabase.typeMapping.read( 266 + DriftSqlType.string, 267 + data['${effectivePrefix}title'], 268 + )!, 269 + description: attachedDatabase.typeMapping.read( 270 + DriftSqlType.string, 271 + data['${effectivePrefix}description'], 272 + )!, 273 + notes: attachedDatabase.typeMapping.read( 274 + DriftSqlType.string, 275 + data['${effectivePrefix}notes'], 276 + ), 277 + tags: $ClipsTable.$convertertagsn.fromSql( 278 + attachedDatabase.typeMapping.read( 279 + DriftSqlType.string, 280 + data['${effectivePrefix}tags'], 281 + ), 282 + ), 283 + unlisted: attachedDatabase.typeMapping.read( 284 + DriftSqlType.bool, 285 + data['${effectivePrefix}unlisted'], 286 + )!, 287 + unread: attachedDatabase.typeMapping.read( 288 + DriftSqlType.bool, 289 + data['${effectivePrefix}unread'], 290 + ), 291 + languages: $ClipsTable.$converterlanguagesn.fromSql( 292 + attachedDatabase.typeMapping.read( 293 + DriftSqlType.string, 294 + data['${effectivePrefix}languages'], 295 + ), 296 + ), 297 + createdAt: attachedDatabase.typeMapping.read( 298 + DriftSqlType.dateTime, 299 + data['${effectivePrefix}created_at'], 300 + )!, 301 + ); 302 + } 303 + 304 + @override 305 + $ClipsTable createAlias(String alias) { 306 + return $ClipsTable(attachedDatabase, alias); 307 + } 308 + 309 + static TypeConverter<StrongRef, String> $convertertags = 310 + const StrongRefConverter(); 311 + static TypeConverter<StrongRef?, String?> $convertertagsn = 312 + NullAwareTypeConverter.wrap($convertertags); 313 + static TypeConverter<List<String>, String> $converterlanguages = 314 + const StringArrayConverter(); 315 + static TypeConverter<List<String>?, String?> $converterlanguagesn = 316 + NullAwareTypeConverter.wrap($converterlanguages); 317 + } 318 + 319 + class Clip extends DataClass implements Insertable<Clip> { 320 + final DateTime timestamp; 321 + final String did; 322 + final String recordKey; 323 + final String url; 324 + final String title; 325 + final String description; 326 + final String? notes; 327 + final StrongRef? tags; 328 + final bool unlisted; 329 + final bool? unread; 330 + final List<String>? languages; 331 + final DateTime createdAt; 332 + const Clip({ 333 + required this.timestamp, 334 + required this.did, 335 + required this.recordKey, 336 + required this.url, 337 + required this.title, 338 + required this.description, 339 + this.notes, 340 + this.tags, 341 + required this.unlisted, 342 + this.unread, 343 + this.languages, 344 + required this.createdAt, 345 + }); 346 + @override 347 + Map<String, Expression> toColumns(bool nullToAbsent) { 348 + final map = <String, Expression>{}; 349 + map['time_us'] = Variable<DateTime>(timestamp); 350 + map['did'] = Variable<String>(did); 351 + map['rkey'] = Variable<String>(recordKey); 352 + map['url'] = Variable<String>(url); 353 + map['title'] = Variable<String>(title); 354 + map['description'] = Variable<String>(description); 355 + if (!nullToAbsent || notes != null) { 356 + map['notes'] = Variable<String>(notes); 357 + } 358 + if (!nullToAbsent || tags != null) { 359 + map['tags'] = Variable<String>($ClipsTable.$convertertagsn.toSql(tags)); 360 + } 361 + map['unlisted'] = Variable<bool>(unlisted); 362 + if (!nullToAbsent || unread != null) { 363 + map['unread'] = Variable<bool>(unread); 364 + } 365 + if (!nullToAbsent || languages != null) { 366 + map['languages'] = Variable<String>( 367 + $ClipsTable.$converterlanguagesn.toSql(languages), 368 + ); 369 + } 370 + map['created_at'] = Variable<DateTime>(createdAt); 371 + return map; 372 + } 373 + 374 + ClipsCompanion toCompanion(bool nullToAbsent) { 375 + return ClipsCompanion( 376 + timestamp: Value(timestamp), 377 + did: Value(did), 378 + recordKey: Value(recordKey), 379 + url: Value(url), 380 + title: Value(title), 381 + description: Value(description), 382 + notes: notes == null && nullToAbsent 383 + ? const Value.absent() 384 + : Value(notes), 385 + tags: tags == null && nullToAbsent ? const Value.absent() : Value(tags), 386 + unlisted: Value(unlisted), 387 + unread: unread == null && nullToAbsent 388 + ? const Value.absent() 389 + : Value(unread), 390 + languages: languages == null && nullToAbsent 391 + ? const Value.absent() 392 + : Value(languages), 393 + createdAt: Value(createdAt), 394 + ); 395 + } 396 + 397 + factory Clip.fromJson( 398 + Map<String, dynamic> json, { 399 + ValueSerializer? serializer, 400 + }) { 401 + serializer ??= driftRuntimeOptions.defaultSerializer; 402 + return Clip( 403 + timestamp: serializer.fromJson<DateTime>(json['timestamp']), 404 + did: serializer.fromJson<String>(json['did']), 405 + recordKey: serializer.fromJson<String>(json['recordKey']), 406 + url: serializer.fromJson<String>(json['url']), 407 + title: serializer.fromJson<String>(json['title']), 408 + description: serializer.fromJson<String>(json['description']), 409 + notes: serializer.fromJson<String?>(json['notes']), 410 + tags: serializer.fromJson<StrongRef?>(json['tags']), 411 + unlisted: serializer.fromJson<bool>(json['unlisted']), 412 + unread: serializer.fromJson<bool?>(json['unread']), 413 + languages: serializer.fromJson<List<String>?>(json['languages']), 414 + createdAt: serializer.fromJson<DateTime>(json['createdAt']), 415 + ); 416 + } 417 + @override 418 + Map<String, dynamic> toJson({ValueSerializer? serializer}) { 419 + serializer ??= driftRuntimeOptions.defaultSerializer; 420 + return <String, dynamic>{ 421 + 'timestamp': serializer.toJson<DateTime>(timestamp), 422 + 'did': serializer.toJson<String>(did), 423 + 'recordKey': serializer.toJson<String>(recordKey), 424 + 'url': serializer.toJson<String>(url), 425 + 'title': serializer.toJson<String>(title), 426 + 'description': serializer.toJson<String>(description), 427 + 'notes': serializer.toJson<String?>(notes), 428 + 'tags': serializer.toJson<StrongRef?>(tags), 429 + 'unlisted': serializer.toJson<bool>(unlisted), 430 + 'unread': serializer.toJson<bool?>(unread), 431 + 'languages': serializer.toJson<List<String>?>(languages), 432 + 'createdAt': serializer.toJson<DateTime>(createdAt), 433 + }; 434 + } 435 + 436 + Clip copyWith({ 437 + DateTime? timestamp, 438 + String? did, 439 + String? recordKey, 440 + String? url, 441 + String? title, 442 + String? description, 443 + Value<String?> notes = const Value.absent(), 444 + Value<StrongRef?> tags = const Value.absent(), 445 + bool? unlisted, 446 + Value<bool?> unread = const Value.absent(), 447 + Value<List<String>?> languages = const Value.absent(), 448 + DateTime? createdAt, 449 + }) => Clip( 450 + timestamp: timestamp ?? this.timestamp, 451 + did: did ?? this.did, 452 + recordKey: recordKey ?? this.recordKey, 453 + url: url ?? this.url, 454 + title: title ?? this.title, 455 + description: description ?? this.description, 456 + notes: notes.present ? notes.value : this.notes, 457 + tags: tags.present ? tags.value : this.tags, 458 + unlisted: unlisted ?? this.unlisted, 459 + unread: unread.present ? unread.value : this.unread, 460 + languages: languages.present ? languages.value : this.languages, 461 + createdAt: createdAt ?? this.createdAt, 462 + ); 463 + Clip copyWithCompanion(ClipsCompanion data) { 464 + return Clip( 465 + timestamp: data.timestamp.present ? data.timestamp.value : this.timestamp, 466 + did: data.did.present ? data.did.value : this.did, 467 + recordKey: data.recordKey.present ? data.recordKey.value : this.recordKey, 468 + url: data.url.present ? data.url.value : this.url, 469 + title: data.title.present ? data.title.value : this.title, 470 + description: data.description.present 471 + ? data.description.value 472 + : this.description, 473 + notes: data.notes.present ? data.notes.value : this.notes, 474 + tags: data.tags.present ? data.tags.value : this.tags, 475 + unlisted: data.unlisted.present ? data.unlisted.value : this.unlisted, 476 + unread: data.unread.present ? data.unread.value : this.unread, 477 + languages: data.languages.present ? data.languages.value : this.languages, 478 + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, 479 + ); 480 + } 481 + 482 + @override 483 + String toString() { 484 + return (StringBuffer('Clip(') 485 + ..write('timestamp: $timestamp, ') 486 + ..write('did: $did, ') 487 + ..write('recordKey: $recordKey, ') 488 + ..write('url: $url, ') 489 + ..write('title: $title, ') 490 + ..write('description: $description, ') 491 + ..write('notes: $notes, ') 492 + ..write('tags: $tags, ') 493 + ..write('unlisted: $unlisted, ') 494 + ..write('unread: $unread, ') 495 + ..write('languages: $languages, ') 496 + ..write('createdAt: $createdAt') 497 + ..write(')')) 498 + .toString(); 499 + } 500 + 501 + @override 502 + int get hashCode => Object.hash( 503 + timestamp, 504 + did, 505 + recordKey, 506 + url, 507 + title, 508 + description, 509 + notes, 510 + tags, 511 + unlisted, 512 + unread, 513 + languages, 514 + createdAt, 515 + ); 516 + @override 517 + bool operator ==(Object other) => 518 + identical(this, other) || 519 + (other is Clip && 520 + other.timestamp == this.timestamp && 521 + other.did == this.did && 522 + other.recordKey == this.recordKey && 523 + other.url == this.url && 524 + other.title == this.title && 525 + other.description == this.description && 526 + other.notes == this.notes && 527 + other.tags == this.tags && 528 + other.unlisted == this.unlisted && 529 + other.unread == this.unread && 530 + other.languages == this.languages && 531 + other.createdAt == this.createdAt); 532 + } 533 + 534 + class ClipsCompanion extends UpdateCompanion<Clip> { 535 + final Value<DateTime> timestamp; 536 + final Value<String> did; 537 + final Value<String> recordKey; 538 + final Value<String> url; 539 + final Value<String> title; 540 + final Value<String> description; 541 + final Value<String?> notes; 542 + final Value<StrongRef?> tags; 543 + final Value<bool> unlisted; 544 + final Value<bool?> unread; 545 + final Value<List<String>?> languages; 546 + final Value<DateTime> createdAt; 547 + final Value<int> rowid; 548 + const ClipsCompanion({ 549 + this.timestamp = const Value.absent(), 550 + this.did = const Value.absent(), 551 + this.recordKey = const Value.absent(), 552 + this.url = const Value.absent(), 553 + this.title = const Value.absent(), 554 + this.description = const Value.absent(), 555 + this.notes = const Value.absent(), 556 + this.tags = const Value.absent(), 557 + this.unlisted = const Value.absent(), 558 + this.unread = const Value.absent(), 559 + this.languages = const Value.absent(), 560 + this.createdAt = const Value.absent(), 561 + this.rowid = const Value.absent(), 562 + }); 563 + ClipsCompanion.insert({ 564 + this.timestamp = const Value.absent(), 565 + required String did, 566 + required String recordKey, 567 + required String url, 568 + required String title, 569 + required String description, 570 + this.notes = const Value.absent(), 571 + this.tags = const Value.absent(), 572 + required bool unlisted, 573 + this.unread = const Value.absent(), 574 + this.languages = const Value.absent(), 575 + required DateTime createdAt, 576 + this.rowid = const Value.absent(), 577 + }) : did = Value(did), 578 + recordKey = Value(recordKey), 579 + url = Value(url), 580 + title = Value(title), 581 + description = Value(description), 582 + unlisted = Value(unlisted), 583 + createdAt = Value(createdAt); 584 + static Insertable<Clip> custom({ 585 + Expression<DateTime>? timestamp, 586 + Expression<String>? did, 587 + Expression<String>? recordKey, 588 + Expression<String>? url, 589 + Expression<String>? title, 590 + Expression<String>? description, 591 + Expression<String>? notes, 592 + Expression<String>? tags, 593 + Expression<bool>? unlisted, 594 + Expression<bool>? unread, 595 + Expression<String>? languages, 596 + Expression<DateTime>? createdAt, 597 + Expression<int>? rowid, 598 + }) { 599 + return RawValuesInsertable({ 600 + if (timestamp != null) 'time_us': timestamp, 601 + if (did != null) 'did': did, 602 + if (recordKey != null) 'rkey': recordKey, 603 + if (url != null) 'url': url, 604 + if (title != null) 'title': title, 605 + if (description != null) 'description': description, 606 + if (notes != null) 'notes': notes, 607 + if (tags != null) 'tags': tags, 608 + if (unlisted != null) 'unlisted': unlisted, 609 + if (unread != null) 'unread': unread, 610 + if (languages != null) 'languages': languages, 611 + if (createdAt != null) 'created_at': createdAt, 612 + if (rowid != null) 'rowid': rowid, 613 + }); 614 + } 615 + 616 + ClipsCompanion copyWith({ 617 + Value<DateTime>? timestamp, 618 + Value<String>? did, 619 + Value<String>? recordKey, 620 + Value<String>? url, 621 + Value<String>? title, 622 + Value<String>? description, 623 + Value<String?>? notes, 624 + Value<StrongRef?>? tags, 625 + Value<bool>? unlisted, 626 + Value<bool?>? unread, 627 + Value<List<String>?>? languages, 628 + Value<DateTime>? createdAt, 629 + Value<int>? rowid, 630 + }) { 631 + return ClipsCompanion( 632 + timestamp: timestamp ?? this.timestamp, 633 + did: did ?? this.did, 634 + recordKey: recordKey ?? this.recordKey, 635 + url: url ?? this.url, 636 + title: title ?? this.title, 637 + description: description ?? this.description, 638 + notes: notes ?? this.notes, 639 + tags: tags ?? this.tags, 640 + unlisted: unlisted ?? this.unlisted, 641 + unread: unread ?? this.unread, 642 + languages: languages ?? this.languages, 643 + createdAt: createdAt ?? this.createdAt, 644 + rowid: rowid ?? this.rowid, 645 + ); 646 + } 647 + 648 + @override 649 + Map<String, Expression> toColumns(bool nullToAbsent) { 650 + final map = <String, Expression>{}; 651 + if (timestamp.present) { 652 + map['time_us'] = Variable<DateTime>(timestamp.value); 653 + } 654 + if (did.present) { 655 + map['did'] = Variable<String>(did.value); 656 + } 657 + if (recordKey.present) { 658 + map['rkey'] = Variable<String>(recordKey.value); 659 + } 660 + if (url.present) { 661 + map['url'] = Variable<String>(url.value); 662 + } 663 + if (title.present) { 664 + map['title'] = Variable<String>(title.value); 665 + } 666 + if (description.present) { 667 + map['description'] = Variable<String>(description.value); 668 + } 669 + if (notes.present) { 670 + map['notes'] = Variable<String>(notes.value); 671 + } 672 + if (tags.present) { 673 + map['tags'] = Variable<String>( 674 + $ClipsTable.$convertertagsn.toSql(tags.value), 675 + ); 676 + } 677 + if (unlisted.present) { 678 + map['unlisted'] = Variable<bool>(unlisted.value); 679 + } 680 + if (unread.present) { 681 + map['unread'] = Variable<bool>(unread.value); 682 + } 683 + if (languages.present) { 684 + map['languages'] = Variable<String>( 685 + $ClipsTable.$converterlanguagesn.toSql(languages.value), 686 + ); 687 + } 688 + if (createdAt.present) { 689 + map['created_at'] = Variable<DateTime>(createdAt.value); 690 + } 691 + if (rowid.present) { 692 + map['rowid'] = Variable<int>(rowid.value); 693 + } 694 + return map; 695 + } 696 + 697 + @override 698 + String toString() { 699 + return (StringBuffer('ClipsCompanion(') 700 + ..write('timestamp: $timestamp, ') 701 + ..write('did: $did, ') 702 + ..write('recordKey: $recordKey, ') 703 + ..write('url: $url, ') 704 + ..write('title: $title, ') 705 + ..write('description: $description, ') 706 + ..write('notes: $notes, ') 707 + ..write('tags: $tags, ') 708 + ..write('unlisted: $unlisted, ') 709 + ..write('unread: $unread, ') 710 + ..write('languages: $languages, ') 711 + ..write('createdAt: $createdAt, ') 712 + ..write('rowid: $rowid') 713 + ..write(')')) 714 + .toString(); 715 + } 716 + } 717 + 718 + abstract class _$AppDatabase extends GeneratedDatabase { 719 + _$AppDatabase(QueryExecutor e) : super(e); 720 + $AppDatabaseManager get managers => $AppDatabaseManager(this); 721 + late final $ClipsTable clips = $ClipsTable(this); 722 + @override 723 + Iterable<TableInfo<Table, Object?>> get allTables => 724 + allSchemaEntities.whereType<TableInfo<Table, Object?>>(); 725 + @override 726 + List<DatabaseSchemaEntity> get allSchemaEntities => [clips]; 727 + } 728 + 729 + typedef $$ClipsTableCreateCompanionBuilder = 730 + ClipsCompanion Function({ 731 + Value<DateTime> timestamp, 732 + required String did, 733 + required String recordKey, 734 + required String url, 735 + required String title, 736 + required String description, 737 + Value<String?> notes, 738 + Value<StrongRef?> tags, 739 + required bool unlisted, 740 + Value<bool?> unread, 741 + Value<List<String>?> languages, 742 + required DateTime createdAt, 743 + Value<int> rowid, 744 + }); 745 + typedef $$ClipsTableUpdateCompanionBuilder = 746 + ClipsCompanion Function({ 747 + Value<DateTime> timestamp, 748 + Value<String> did, 749 + Value<String> recordKey, 750 + Value<String> url, 751 + Value<String> title, 752 + Value<String> description, 753 + Value<String?> notes, 754 + Value<StrongRef?> tags, 755 + Value<bool> unlisted, 756 + Value<bool?> unread, 757 + Value<List<String>?> languages, 758 + Value<DateTime> createdAt, 759 + Value<int> rowid, 760 + }); 761 + 762 + class $$ClipsTableFilterComposer extends Composer<_$AppDatabase, $ClipsTable> { 763 + $$ClipsTableFilterComposer({ 764 + required super.$db, 765 + required super.$table, 766 + super.joinBuilder, 767 + super.$addJoinBuilderToRootComposer, 768 + super.$removeJoinBuilderFromRootComposer, 769 + }); 770 + ColumnFilters<DateTime> get timestamp => $composableBuilder( 771 + column: $table.timestamp, 772 + builder: (column) => ColumnFilters(column), 773 + ); 774 + 775 + ColumnFilters<String> get did => $composableBuilder( 776 + column: $table.did, 777 + builder: (column) => ColumnFilters(column), 778 + ); 779 + 780 + ColumnFilters<String> get recordKey => $composableBuilder( 781 + column: $table.recordKey, 782 + builder: (column) => ColumnFilters(column), 783 + ); 784 + 785 + ColumnFilters<String> get url => $composableBuilder( 786 + column: $table.url, 787 + builder: (column) => ColumnFilters(column), 788 + ); 789 + 790 + ColumnFilters<String> get title => $composableBuilder( 791 + column: $table.title, 792 + builder: (column) => ColumnFilters(column), 793 + ); 794 + 795 + ColumnFilters<String> get description => $composableBuilder( 796 + column: $table.description, 797 + builder: (column) => ColumnFilters(column), 798 + ); 799 + 800 + ColumnFilters<String> get notes => $composableBuilder( 801 + column: $table.notes, 802 + builder: (column) => ColumnFilters(column), 803 + ); 804 + 805 + ColumnWithTypeConverterFilters<StrongRef?, StrongRef, String> get tags => 806 + $composableBuilder( 807 + column: $table.tags, 808 + builder: (column) => ColumnWithTypeConverterFilters(column), 809 + ); 810 + 811 + ColumnFilters<bool> get unlisted => $composableBuilder( 812 + column: $table.unlisted, 813 + builder: (column) => ColumnFilters(column), 814 + ); 815 + 816 + ColumnFilters<bool> get unread => $composableBuilder( 817 + column: $table.unread, 818 + builder: (column) => ColumnFilters(column), 819 + ); 820 + 821 + ColumnWithTypeConverterFilters<List<String>?, List<String>, String> 822 + get languages => $composableBuilder( 823 + column: $table.languages, 824 + builder: (column) => ColumnWithTypeConverterFilters(column), 825 + ); 826 + 827 + ColumnFilters<DateTime> get createdAt => $composableBuilder( 828 + column: $table.createdAt, 829 + builder: (column) => ColumnFilters(column), 830 + ); 831 + } 832 + 833 + class $$ClipsTableOrderingComposer 834 + extends Composer<_$AppDatabase, $ClipsTable> { 835 + $$ClipsTableOrderingComposer({ 836 + required super.$db, 837 + required super.$table, 838 + super.joinBuilder, 839 + super.$addJoinBuilderToRootComposer, 840 + super.$removeJoinBuilderFromRootComposer, 841 + }); 842 + ColumnOrderings<DateTime> get timestamp => $composableBuilder( 843 + column: $table.timestamp, 844 + builder: (column) => ColumnOrderings(column), 845 + ); 846 + 847 + ColumnOrderings<String> get did => $composableBuilder( 848 + column: $table.did, 849 + builder: (column) => ColumnOrderings(column), 850 + ); 851 + 852 + ColumnOrderings<String> get recordKey => $composableBuilder( 853 + column: $table.recordKey, 854 + builder: (column) => ColumnOrderings(column), 855 + ); 856 + 857 + ColumnOrderings<String> get url => $composableBuilder( 858 + column: $table.url, 859 + builder: (column) => ColumnOrderings(column), 860 + ); 861 + 862 + ColumnOrderings<String> get title => $composableBuilder( 863 + column: $table.title, 864 + builder: (column) => ColumnOrderings(column), 865 + ); 866 + 867 + ColumnOrderings<String> get description => $composableBuilder( 868 + column: $table.description, 869 + builder: (column) => ColumnOrderings(column), 870 + ); 871 + 872 + ColumnOrderings<String> get notes => $composableBuilder( 873 + column: $table.notes, 874 + builder: (column) => ColumnOrderings(column), 875 + ); 876 + 877 + ColumnOrderings<String> get tags => $composableBuilder( 878 + column: $table.tags, 879 + builder: (column) => ColumnOrderings(column), 880 + ); 881 + 882 + ColumnOrderings<bool> get unlisted => $composableBuilder( 883 + column: $table.unlisted, 884 + builder: (column) => ColumnOrderings(column), 885 + ); 886 + 887 + ColumnOrderings<bool> get unread => $composableBuilder( 888 + column: $table.unread, 889 + builder: (column) => ColumnOrderings(column), 890 + ); 891 + 892 + ColumnOrderings<String> get languages => $composableBuilder( 893 + column: $table.languages, 894 + builder: (column) => ColumnOrderings(column), 895 + ); 896 + 897 + ColumnOrderings<DateTime> get createdAt => $composableBuilder( 898 + column: $table.createdAt, 899 + builder: (column) => ColumnOrderings(column), 900 + ); 901 + } 902 + 903 + class $$ClipsTableAnnotationComposer 904 + extends Composer<_$AppDatabase, $ClipsTable> { 905 + $$ClipsTableAnnotationComposer({ 906 + required super.$db, 907 + required super.$table, 908 + super.joinBuilder, 909 + super.$addJoinBuilderToRootComposer, 910 + super.$removeJoinBuilderFromRootComposer, 911 + }); 912 + GeneratedColumn<DateTime> get timestamp => 913 + $composableBuilder(column: $table.timestamp, builder: (column) => column); 914 + 915 + GeneratedColumn<String> get did => 916 + $composableBuilder(column: $table.did, builder: (column) => column); 917 + 918 + GeneratedColumn<String> get recordKey => 919 + $composableBuilder(column: $table.recordKey, builder: (column) => column); 920 + 921 + GeneratedColumn<String> get url => 922 + $composableBuilder(column: $table.url, builder: (column) => column); 923 + 924 + GeneratedColumn<String> get title => 925 + $composableBuilder(column: $table.title, builder: (column) => column); 926 + 927 + GeneratedColumn<String> get description => $composableBuilder( 928 + column: $table.description, 929 + builder: (column) => column, 930 + ); 931 + 932 + GeneratedColumn<String> get notes => 933 + $composableBuilder(column: $table.notes, builder: (column) => column); 934 + 935 + GeneratedColumnWithTypeConverter<StrongRef?, String> get tags => 936 + $composableBuilder(column: $table.tags, builder: (column) => column); 937 + 938 + GeneratedColumn<bool> get unlisted => 939 + $composableBuilder(column: $table.unlisted, builder: (column) => column); 940 + 941 + GeneratedColumn<bool> get unread => 942 + $composableBuilder(column: $table.unread, builder: (column) => column); 943 + 944 + GeneratedColumnWithTypeConverter<List<String>?, String> get languages => 945 + $composableBuilder(column: $table.languages, builder: (column) => column); 946 + 947 + GeneratedColumn<DateTime> get createdAt => 948 + $composableBuilder(column: $table.createdAt, builder: (column) => column); 949 + } 950 + 951 + class $$ClipsTableTableManager 952 + extends 953 + RootTableManager< 954 + _$AppDatabase, 955 + $ClipsTable, 956 + Clip, 957 + $$ClipsTableFilterComposer, 958 + $$ClipsTableOrderingComposer, 959 + $$ClipsTableAnnotationComposer, 960 + $$ClipsTableCreateCompanionBuilder, 961 + $$ClipsTableUpdateCompanionBuilder, 962 + (Clip, BaseReferences<_$AppDatabase, $ClipsTable, Clip>), 963 + Clip, 964 + PrefetchHooks Function() 965 + > { 966 + $$ClipsTableTableManager(_$AppDatabase db, $ClipsTable table) 967 + : super( 968 + TableManagerState( 969 + db: db, 970 + table: table, 971 + createFilteringComposer: () => 972 + $$ClipsTableFilterComposer($db: db, $table: table), 973 + createOrderingComposer: () => 974 + $$ClipsTableOrderingComposer($db: db, $table: table), 975 + createComputedFieldComposer: () => 976 + $$ClipsTableAnnotationComposer($db: db, $table: table), 977 + updateCompanionCallback: 978 + ({ 979 + Value<DateTime> timestamp = const Value.absent(), 980 + Value<String> did = const Value.absent(), 981 + Value<String> recordKey = const Value.absent(), 982 + Value<String> url = const Value.absent(), 983 + Value<String> title = const Value.absent(), 984 + Value<String> description = const Value.absent(), 985 + Value<String?> notes = const Value.absent(), 986 + Value<StrongRef?> tags = const Value.absent(), 987 + Value<bool> unlisted = const Value.absent(), 988 + Value<bool?> unread = const Value.absent(), 989 + Value<List<String>?> languages = const Value.absent(), 990 + Value<DateTime> createdAt = const Value.absent(), 991 + Value<int> rowid = const Value.absent(), 992 + }) => ClipsCompanion( 993 + timestamp: timestamp, 994 + did: did, 995 + recordKey: recordKey, 996 + url: url, 997 + title: title, 998 + description: description, 999 + notes: notes, 1000 + tags: tags, 1001 + unlisted: unlisted, 1002 + unread: unread, 1003 + languages: languages, 1004 + createdAt: createdAt, 1005 + rowid: rowid, 1006 + ), 1007 + createCompanionCallback: 1008 + ({ 1009 + Value<DateTime> timestamp = const Value.absent(), 1010 + required String did, 1011 + required String recordKey, 1012 + required String url, 1013 + required String title, 1014 + required String description, 1015 + Value<String?> notes = const Value.absent(), 1016 + Value<StrongRef?> tags = const Value.absent(), 1017 + required bool unlisted, 1018 + Value<bool?> unread = const Value.absent(), 1019 + Value<List<String>?> languages = const Value.absent(), 1020 + required DateTime createdAt, 1021 + Value<int> rowid = const Value.absent(), 1022 + }) => ClipsCompanion.insert( 1023 + timestamp: timestamp, 1024 + did: did, 1025 + recordKey: recordKey, 1026 + url: url, 1027 + title: title, 1028 + description: description, 1029 + notes: notes, 1030 + tags: tags, 1031 + unlisted: unlisted, 1032 + unread: unread, 1033 + languages: languages, 1034 + createdAt: createdAt, 1035 + rowid: rowid, 1036 + ), 1037 + withReferenceMapper: (p0) => p0 1038 + .map((e) => (e.readTable(table), BaseReferences(db, table, e))) 1039 + .toList(), 1040 + prefetchHooksCallback: null, 1041 + ), 1042 + ); 1043 + } 1044 + 1045 + typedef $$ClipsTableProcessedTableManager = 1046 + ProcessedTableManager< 1047 + _$AppDatabase, 1048 + $ClipsTable, 1049 + Clip, 1050 + $$ClipsTableFilterComposer, 1051 + $$ClipsTableOrderingComposer, 1052 + $$ClipsTableAnnotationComposer, 1053 + $$ClipsTableCreateCompanionBuilder, 1054 + $$ClipsTableUpdateCompanionBuilder, 1055 + (Clip, BaseReferences<_$AppDatabase, $ClipsTable, Clip>), 1056 + Clip, 1057 + PrefetchHooks Function() 1058 + >; 1059 + 1060 + class $AppDatabaseManager { 1061 + final _$AppDatabase _db; 1062 + $AppDatabaseManager(this._db); 1063 + $$ClipsTableTableManager get clips => 1064 + $$ClipsTableTableManager(_db, _db.clips); 1065 + }
backend/lib/lexicons/social/clippr/actor/defs.json backend/lexicons/social/clippr/actor/defs.json
backend/lib/lexicons/social/clippr/actor/profile.json backend/lexicons/social/clippr/actor/profile.json
backend/lib/lexicons/social/clippr/clips/defs.json backend/lexicons/social/clippr/clips/defs.json
+1 -1
backend/lib/lexicons/social/clippr/clips/item.json backend/lexicons/social/clippr/clips/item.json
··· 45 45 "description": "An array of tags. A format of solely alphanumeric characters and dashes should be used.", 46 46 "items": { 47 47 "type": "ref", 48 - "ref": "social.clippr.clips.tag" 48 + "ref": "com.atproto.repo.strongRef" 49 49 } 50 50 }, 51 51 "unlisted": {
backend/lib/lexicons/social/clippr/clips/tag.json backend/lexicons/social/clippr/clips/tag.json
backend/lib/lexicons/social/clippr/feed/defs.json backend/lexicons/social/clippr/feed/defs.json
backend/lib/lexicons/social/clippr/reacts/defs.json backend/lexicons/social/clippr/reacts/defs.json
backend/lib/lexicons/social/clippr/reacts/like.json backend/lexicons/social/clippr/reacts/like.json
backend/lib/lexicons/social/clippr/reacts/post.json backend/lexicons/social/clippr/reacts/post.json
backend/lib/lexicons/social/clippr/reacts/reply.json backend/lexicons/social/clippr/reacts/reply.json
+4 -4
backend/lib/server/firehose.dart
··· 16 16 /// As such, new instances should be created as little as possible. 17 17 class Firehose { 18 18 /// [Uri] containing a link to the firehose's WebSocket subscription link. 19 - late final Uri firehoseProvider; 19 + static late final Uri firehoseProvider; 20 20 /// Raw WebSocket stream 21 - late final WebSocketChannel _channel; 21 + static late final WebSocketChannel _channel; 22 22 /// Controllable version of stream 23 - final StreamController<Map<String, dynamic>> _messageController = 23 + static final StreamController<Map<String, dynamic>> _messageController = 24 24 StreamController.broadcast(); 25 25 /// Stream of messages to access from other places 26 - Stream<Map<String, dynamic>> get messageStream => _messageController.stream; 26 + static Stream<Map<String, dynamic>> get messageStream => _messageController.stream; 27 27 28 28 /// Create a new [Firehose] instance using a provided URL string. 29 29 Firehose(String firehoseUrl) {
+20 -7
backend/lib/server/server.dart
··· 5 5 */ 6 6 7 7 import 'dart:io'; 8 + import 'package:clippr/db/database.dart'; 8 9 import 'package:clippr/server/firehose.dart'; 9 10 import 'package:clippr/server/logger.dart'; 10 11 import 'package:clippr/server/routes.dart'; ··· 14 15 import 'package:shelf_router/shelf_router.dart'; 15 16 import 'package:shelf_router_classes/shelf_router_classes.dart'; 16 17 17 - void launchServer() async { 18 + void launchWebServer() async { 18 19 Router router = getRoutersByClass([MiscRouter, OAuthRouter, XrpcRouter]); 19 20 final app = const Pipeline() 20 21 .addMiddleware(serverLogger()) 21 22 .addHandler(router.call); 22 23 23 24 HttpServer _ = await io.serve(app, Config.getHostname(), Config.getPort()); 25 + } 24 26 27 + void launchDatabase() { 28 + var database = AppDatabase(); 29 + } 30 + 31 + void launchFirehose() { 25 32 var firehose = Firehose(Config.getFirehoseProvider()); 26 33 firehose.connect(); 27 - firehose.messageStream.listen((message) { 28 - print(message); 29 - }); 34 + // Firehose.messageStream.listen((message) { 35 + // print(message); 36 + // }); 30 37 } 31 38 32 39 Middleware serverLogger() { ··· 37 44 final response = await innerHandler(request); 38 45 final endTime = DateTime.now(); 39 46 final duration = endTime.difference(startTime); // Time to serve route 40 - Logger.logFine('${request.method} ${request.requestedUri.path} [${response.statusCode}] in $duration'); 47 + Logger.logFine( 48 + '${request.method} ${request.requestedUri.path} [${response.statusCode}] in $duration', 49 + ); 41 50 return response; 42 51 } catch (e, stackTrace) { 43 - Logger.logSevere('Error handling ${request.method} ${request.requestedUri.path}', e, stackTrace); 52 + Logger.logSevere( 53 + 'Error handling ${request.method} ${request.requestedUri.path}', 54 + e, 55 + stackTrace, 56 + ); 44 57 rethrow; 45 58 } 46 59 }; 47 60 }; 48 - } 61 + }
+10 -10
backend/pubspec.lock
··· 101 101 dependency: transitive 102 102 description: 103 103 name: build 104 - sha256: "8295b0b6dfe00499b786718f2936a56b5e0d56d169c528472c8c1908f2b1e3ee" 104 + sha256: "486337d40a48d75049f2a01efceefc1412e3a6d6342d39624753e7d5a4e70016" 105 105 url: "https://pub.dev" 106 106 source: hosted 107 - version: "2.5.0" 107 + version: "2.5.1" 108 108 build_config: 109 109 dependency: transitive 110 110 description: ··· 125 125 dependency: transitive 126 126 description: 127 127 name: build_resolvers 128 - sha256: "57fe2f9149b01d52fcd0ea7de17083739b5cf9e040dedb4e24a17c628c1e9caf" 128 + sha256: abe6e4b5b36ce2bf01aec8f2a59424d1ecfc3cb340e7145a64359adc7233a0d1 129 129 url: "https://pub.dev" 130 130 source: hosted 131 - version: "2.5.0" 131 + version: "2.5.1" 132 132 build_runner: 133 133 dependency: "direct dev" 134 134 description: 135 135 name: build_runner 136 - sha256: "9b196d7b629c5317dff3ec83c7e98840b773cee3b9339b646fe487048d2ebc74" 136 + sha256: "398ec7898b9b60be126067835a8202240b26dc54aa34d91d0198a539dcd5942e" 137 137 url: "https://pub.dev" 138 138 source: hosted 139 - version: "2.5.0" 139 + version: "2.5.1" 140 140 build_runner_core: 141 141 dependency: transitive 142 142 description: 143 143 name: build_runner_core 144 - sha256: dae6a8a5cbef6866cdfa0d96df4b0d85b9086897096270a405a44d946dafffba 144 + sha256: "9821dbf604ed74a6dabeaba0c33ac58190332ea238862a62cdf25fc8eba226b8" 145 145 url: "https://pub.dev" 146 146 source: hosted 147 - version: "9.0.0" 147 + version: "9.0.1" 148 148 built_collection: 149 149 dependency: transitive 150 150 description: ··· 402 402 source: hosted 403 403 version: "0.7.2" 404 404 json_annotation: 405 - dependency: transitive 405 + dependency: "direct main" 406 406 description: 407 407 name: json_annotation 408 408 sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" ··· 458 458 source: hosted 459 459 version: "1.0.6" 460 460 multiformats: 461 - dependency: transitive 461 + dependency: "direct main" 462 462 description: 463 463 name: multiformats 464 464 sha256: aa2fa36d2e4d0069dac993b35ee52e5165d67f15b995d68f797466065a6d05a5
+2
backend/pubspec.yaml
··· 11 11 dependencies: 12 12 atproto: ^0.13.3 13 13 drift: ^2.27.0 14 + json_annotation: ^4.9.0 14 15 lexicon: ^0.2.5 15 16 logging: ^1.3.0 17 + multiformats: ^0.2.3 16 18 oauth2: ^2.0.3 17 19 pubspec_parse: ^1.5.0 18 20 shelf: ^1.4.2
+8
backend/tools/build_and_run.sh
··· 1 + #!/bin/sh 2 + 3 + # clippr: a social bookmarking service for the AT Protocol 4 + # Copyright (c) 2025 clippr contributors. 5 + # SPDX-License-Identifier: AGPL-3.0-only 6 + 7 + dart run build_runner build 8 + dart run bin/clippr.dart