halkyon_base: a Serverpod module for sign-in, accounts and instance basics

What every Halkyon server has, taken from Halkyon Learn's: anonymous,
GitHub and email-code sign-in with their endpoints, an account's ways
in and its deletion, the pruning of abandoned anonymous people, of
sign-up attempts and of expired codes, and the instance's basics (the
sign-in ways offered, the source link, the minimum app build, where
people's data and its backups are). Endpoints sit under the nickname
`base`. A host configures it in one place before its pod starts: its
name for the mails, its source link, its minimum build, and hooks that
delete its own data with a person.

The tables keep Learn's names, columns and indexes, so a host moving
onto the module migrates nothing but version rows. Server package under
the SSPL, client under the AGPL, as Learn's are.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-19 14:28:09 +02:00
commit af1de799f4
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
10 changed files with 17704 additions and 3 deletions

View file

@ -0,0 +1,2 @@
export 'src/protocol/protocol.dart';
export 'package:serverpod_client/serverpod_client.dart';

View file

@ -0,0 +1,75 @@
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
/* To generate run: "serverpod generate" */
// ignore_for_file: implementation_imports
// ignore_for_file: library_private_types_in_public_api
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: public_member_api_docs
// ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_client/serverpod_client.dart' as _isc;
/// A code accepted: the address is now the signed-in person's (or already
/// was), or it is another person's and [switchToken] signs in as them.
abstract class EmailCodeAccepted
implements _isc.SerializableModel, _isc.ProtocolSerialization {
EmailCodeAccepted._({this.switchToken});
factory EmailCodeAccepted({String? switchToken}) = _EmailCodeAcceptedImpl;
factory EmailCodeAccepted.fromJson(Map<String, dynamic> jsonSerialization) {
return EmailCodeAccepted(
switchToken: jsonSerialization['switchToken'] as String?,
);
}
/// Null when the address is the signed-in person's; else what signs in as
/// the person who has it, once, within the code's 15 minutes
/// (`EmailSignInEndpoint.signIn`).
String? switchToken;
/// Returns a shallow copy of this [EmailCodeAccepted]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
EmailCodeAccepted copyWith({String? switchToken});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'halkyon_base.EmailCodeAccepted',
if (switchToken != null) 'switchToken': switchToken,
};
}
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'halkyon_base.EmailCodeAccepted',
if (switchToken != null) 'switchToken': switchToken,
};
}
@override
String toString() {
return _isc.SerializationManager.encode(this);
}
}
class _Undefined {}
class _EmailCodeAcceptedImpl extends EmailCodeAccepted {
_EmailCodeAcceptedImpl({String? switchToken})
: super._(switchToken: switchToken);
/// Returns a shallow copy of this [EmailCodeAccepted]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@override
EmailCodeAccepted copyWith({Object? switchToken = _Undefined}) {
return EmailCodeAccepted(
switchToken: switchToken is String? ? switchToken : this.switchToken,
);
}
}

View file

@ -0,0 +1,75 @@
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
/* To generate run: "serverpod generate" */
// ignore_for_file: implementation_imports
// ignore_for_file: library_private_types_in_public_api
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: public_member_api_docs
// ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_client/serverpod_client.dart' as _isc;
/// Why mailing a code, accepting it, or signing in with an email address
/// was refused.
enum EmailSignInRefusal implements _isc.SerializableModel {
/// This instance has no email sign-in (no emailSignInPepper in its
/// passwords, or no way to send mail).
unavailable,
/// What was typed isn't an email address.
notAnAddress,
/// Too many codes asked for, for this address or from this network, in
/// the last hour.
tooManyCodes,
/// The code is wrong.
wrongCode,
/// The code was typed wrong too often; a new one is needed.
tooManyTries,
/// The code is no longer valid: past its time, used, or replaced by a
/// newer one.
codeExpired,
/// The person already signs in with another address.
otherAddressLinked,
/// The mail couldn't be sent; nothing was kept of the request.
mailFailed;
static EmailSignInRefusal fromJson(String name) {
switch (name) {
case 'unavailable':
return EmailSignInRefusal.unavailable;
case 'notAnAddress':
return EmailSignInRefusal.notAnAddress;
case 'tooManyCodes':
return EmailSignInRefusal.tooManyCodes;
case 'wrongCode':
return EmailSignInRefusal.wrongCode;
case 'tooManyTries':
return EmailSignInRefusal.tooManyTries;
case 'codeExpired':
return EmailSignInRefusal.codeExpired;
case 'otherAddressLinked':
return EmailSignInRefusal.otherAddressLinked;
case 'mailFailed':
return EmailSignInRefusal.mailFailed;
default:
throw ArgumentError(
'Value "$name" cannot be converted to "EmailSignInRefusal"',
);
}
}
@override
String toJson() => name;
@override
String toString() => name;
}

View file

@ -0,0 +1,80 @@
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
/* To generate run: "serverpod generate" */
// ignore_for_file: implementation_imports
// ignore_for_file: library_private_types_in_public_api
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: public_member_api_docs
// ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_client/serverpod_client.dart' as _isc;
import '../auth/email_sign_in_refusal.dart' as _in9x16lz;
/// Mailing a code, accepting it, or signing in with an email address was
/// refused; nothing changed.
abstract class EmailSignInRefusedException
implements
_isc.SerializableException,
_isc.SerializableModel,
_isc.ProtocolSerialization {
EmailSignInRefusedException._({required this.reason});
factory EmailSignInRefusedException({
required _in9x16lz.EmailSignInRefusal reason,
}) = _EmailSignInRefusedExceptionImpl;
factory EmailSignInRefusedException.fromJson(
Map<String, dynamic> jsonSerialization,
) {
return EmailSignInRefusedException(
reason: _in9x16lz.EmailSignInRefusal.fromJson(
(jsonSerialization['reason'] as String),
),
);
}
/// Why it was refused.
_in9x16lz.EmailSignInRefusal reason;
/// Returns a shallow copy of this [EmailSignInRefusedException]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
EmailSignInRefusedException copyWith({_in9x16lz.EmailSignInRefusal? reason});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'halkyon_base.EmailSignInRefusedException',
'reason': reason.toJson(),
};
}
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'halkyon_base.EmailSignInRefusedException',
'reason': reason.toJson(),
};
}
@override
String toString() {
return 'EmailSignInRefusedException(reason: $reason)';
}
}
class _EmailSignInRefusedExceptionImpl extends EmailSignInRefusedException {
_EmailSignInRefusedExceptionImpl({
required _in9x16lz.EmailSignInRefusal reason,
}) : super._(reason: reason);
/// Returns a shallow copy of this [EmailSignInRefusedException]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@override
EmailSignInRefusedException copyWith({_in9x16lz.EmailSignInRefusal? reason}) {
return EmailSignInRefusedException(reason: reason ?? this.reason);
}
}

View file

@ -0,0 +1,52 @@
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
/* To generate run: "serverpod generate" */
// ignore_for_file: implementation_imports
// ignore_for_file: library_private_types_in_public_api
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: public_member_api_docs
// ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_client/serverpod_client.dart' as _isc;
/// Why signing in with GitHub, or linking a GitHub account, was refused.
enum GitHubSignInRefusal implements _isc.SerializableModel {
/// This instance has no GitHub OAuth app (no githubClientId and
/// githubClientSecret in its passwords).
unavailable,
/// The GitHub account already belongs to another person.
takenByAnotherPerson,
/// The person already has a different GitHub account.
otherGitHubAccountLinked,
/// No person has this GitHub account; only linking one creates it.
unknownAccount;
static GitHubSignInRefusal fromJson(String name) {
switch (name) {
case 'unavailable':
return GitHubSignInRefusal.unavailable;
case 'takenByAnotherPerson':
return GitHubSignInRefusal.takenByAnotherPerson;
case 'otherGitHubAccountLinked':
return GitHubSignInRefusal.otherGitHubAccountLinked;
case 'unknownAccount':
return GitHubSignInRefusal.unknownAccount;
default:
throw ArgumentError(
'Value "$name" cannot be converted to "GitHubSignInRefusal"',
);
}
}
@override
String toJson() => name;
@override
String toString() => name;
}

View file

@ -0,0 +1,84 @@
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
/* To generate run: "serverpod generate" */
// ignore_for_file: implementation_imports
// ignore_for_file: library_private_types_in_public_api
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: public_member_api_docs
// ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_client/serverpod_client.dart' as _isc;
import '../auth/github_sign_in_refusal.dart' as _irgbwy1y;
/// Signing in with GitHub, or linking a GitHub account, was refused; nothing
/// changed.
abstract class GitHubSignInRefusedException
implements
_isc.SerializableException,
_isc.SerializableModel,
_isc.ProtocolSerialization {
GitHubSignInRefusedException._({required this.reason});
factory GitHubSignInRefusedException({
required _irgbwy1y.GitHubSignInRefusal reason,
}) = _GitHubSignInRefusedExceptionImpl;
factory GitHubSignInRefusedException.fromJson(
Map<String, dynamic> jsonSerialization,
) {
return GitHubSignInRefusedException(
reason: _irgbwy1y.GitHubSignInRefusal.fromJson(
(jsonSerialization['reason'] as String),
),
);
}
/// Why it was refused.
_irgbwy1y.GitHubSignInRefusal reason;
/// Returns a shallow copy of this [GitHubSignInRefusedException]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
GitHubSignInRefusedException copyWith({
_irgbwy1y.GitHubSignInRefusal? reason,
});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'halkyon_base.GitHubSignInRefusedException',
'reason': reason.toJson(),
};
}
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'halkyon_base.GitHubSignInRefusedException',
'reason': reason.toJson(),
};
}
@override
String toString() {
return 'GitHubSignInRefusedException(reason: $reason)';
}
}
class _GitHubSignInRefusedExceptionImpl extends GitHubSignInRefusedException {
_GitHubSignInRefusedExceptionImpl({
required _irgbwy1y.GitHubSignInRefusal reason,
}) : super._(reason: reason);
/// Returns a shallow copy of this [GitHubSignInRefusedException]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@override
GitHubSignInRefusedException copyWith({
_irgbwy1y.GitHubSignInRefusal? reason,
}) {
return GitHubSignInRefusedException(reason: reason ?? this.reason);
}
}

View file

@ -0,0 +1,92 @@
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
/* To generate run: "serverpod generate" */
// ignore_for_file: implementation_imports
// ignore_for_file: library_private_types_in_public_api
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: public_member_api_docs
// ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_client/serverpod_client.dart' as _isc;
/// The ways a person signs in, besides an anonymous account's session.
abstract class SignInWays
implements _isc.SerializableModel, _isc.ProtocolSerialization {
SignInWays._({
required this.gitHub,
required this.email,
});
factory SignInWays({
required bool gitHub,
required bool email,
}) = _SignInWaysImpl;
factory SignInWays.fromJson(Map<String, dynamic> jsonSerialization) {
return SignInWays(
gitHub: _isc.BoolJsonExtension.fromJson(jsonSerialization['gitHub']),
email: _isc.BoolJsonExtension.fromJson(jsonSerialization['email']),
);
}
bool gitHub;
bool email;
/// Returns a shallow copy of this [SignInWays]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
SignInWays copyWith({
bool? gitHub,
bool? email,
});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'halkyon_base.SignInWays',
'gitHub': gitHub,
'email': email,
};
}
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'halkyon_base.SignInWays',
'gitHub': gitHub,
'email': email,
};
}
@override
String toString() {
return _isc.SerializationManager.encode(this);
}
}
class _SignInWaysImpl extends SignInWays {
_SignInWaysImpl({
required bool gitHub,
required bool email,
}) : super._(
gitHub: gitHub,
email: email,
);
/// Returns a shallow copy of this [SignInWays]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@override
SignInWays copyWith({
bool? gitHub,
bool? email,
}) {
return SignInWays(
gitHub: gitHub ?? this.gitHub,
email: email ?? this.email,
);
}
}

View file

@ -0,0 +1,247 @@
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
/* To generate run: "serverpod generate" */
// ignore_for_file: implementation_imports
// ignore_for_file: library_private_types_in_public_api
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: public_member_api_docs
// ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _ida;
import 'package:halkyon_base_client/src/protocol/auth/email_code_accepted.dart'
as _ioeosfv3;
import 'package:halkyon_base_client/src/protocol/auth/sign_in_ways.dart'
as _ifld8s4r;
import 'package:halkyon_base_client/src/protocol/instance/instance_basics.dart'
as _isnyur2i;
import 'package:serverpod_auth_core_client/serverpod_auth_core_client.dart'
as _iacc;
import 'package:serverpod_auth_idp_client/serverpod_auth_idp_client.dart'
as _iaic;
import 'package:serverpod_client/serverpod_client.dart' as _isc;
/// The signed-in person's account.
/// {@category Endpoint}
class EndpointAccount extends _isc.EndpointRef {
EndpointAccount(_isc.EndpointCaller caller) : super(caller);
@override
String get name => 'halkyon_base.account';
/// Deletes the caller with everything they have (see [deletePerson]). The
/// app also calls it when an anonymous account is given up (decided
/// 2026-09-30). Their other devices learn at once that their sessions
/// ended.
_ida.Future<void> delete() => caller.callServerEndpoint<void>(
'halkyon_base.account',
'delete',
{},
);
/// How the caller signs in besides their sessions: with GitHub, with an
/// email address, both, or neither (anonymous).
_ida.Future<_ifld8s4r.SignInWays> signInWays() =>
caller.callServerEndpoint<_ifld8s4r.SignInWays>(
'halkyon_base.account',
'signInWays',
{},
);
}
/// Signs people in anonymously; the app calls it on first launch. Creating an
/// account is rate-limited per IP address (see `auth_config.dart`).
/// {@category Endpoint}
class EndpointAnonymousIdp extends _iaic.EndpointAnonymousIdpBase {
EndpointAnonymousIdp(_isc.EndpointCaller caller) : super(caller);
@override
String get name => 'halkyon_base.anonymousIdp';
/// Creates a new anonymous account and returns its session.
///
/// Invokes the [AnonymousIdp.beforeAnonymousAccount] callback if configured,
/// which may prevent account creation if the endpoint is protected.
@override
_ida.Future<_iacc.AuthSuccess> login({String? token}) =>
caller.callServerEndpoint<_iacc.AuthSuccess>(
'halkyon_base.anonymousIdp',
'login',
{'token': token},
);
}
/// Signing in with an email address, by a code mailed to it:
/// the way to keep an anonymous account, as linking GitHub is, and a second
/// way in for someone who signs in with GitHub. Signed in only, as every
/// device is from its first start.
/// {@category Endpoint}
class EndpointEmailCode extends _isc.EndpointRef {
EndpointEmailCode(_isc.EndpointCaller caller) : super(caller);
@override
String get name => 'halkyon_base.emailCode';
/// Mails a code to [address], worded in [language] (`en`, `de`), and
/// returns the request it answers; refusals as in [sendSignInCode].
_ida.Future<_isc.UuidValue> sendCode({
required String address,
required String language,
}) => caller.callServerEndpoint<_isc.UuidValue>(
'halkyon_base.emailCode',
'sendCode',
{
'address': address,
'language': language,
},
);
/// Accepts [code] for [request]: the address becomes the caller's unless
/// it is another person's, whose token then signs in as them
/// (`EmailSignInEndpoint.signIn`); refusals as in [acceptSignInCode].
_ida.Future<_ioeosfv3.EmailCodeAccepted> verifyCode({
required _isc.UuidValue request,
required String code,
}) => caller.callServerEndpoint<_ioeosfv3.EmailCodeAccepted>(
'halkyon_base.emailCode',
'verifyCode',
{
'request': request,
'code': code,
},
);
}
/// Signs in the person whose address a mailed code proved, when the device
/// switches to them. Called without a session: Serverpod issues no session
/// for another person inside a signed-in call.
/// {@category Endpoint}
class EndpointEmailSignIn extends _isc.EndpointRef {
EndpointEmailSignIn(_isc.EndpointCaller caller) : super(caller);
@override
String get name => 'halkyon_base.emailSignIn';
/// A session for the person [switchToken] names; refusals as in
/// [signInWithEmail].
_ida.Future<_iacc.AuthSuccess> signIn({required String switchToken}) =>
caller.callServerEndpoint<_iacc.AuthSuccess>(
'halkyon_base.emailSignIn',
'signIn',
{'switchToken': switchToken},
);
}
/// Signs in the person who already has a GitHub account, e.g. when they switch
/// to it on another device. Called without a session: Serverpod issues no
/// session for another person inside a signed-in call.
/// {@category Endpoint}
class EndpointGitHubSignIn extends _isc.EndpointRef {
EndpointGitHubSignIn(_isc.EndpointCaller caller) : super(caller);
@override
String get name => 'halkyon_base.gitHubSignIn';
/// A session for the person with the GitHub account that [code] proves;
/// refusals as in [signInWithGitHubAccount].
_ida.Future<_iacc.AuthSuccess> signIn({
required String code,
required String codeVerifier,
required String redirectUri,
}) => caller.callServerEndpoint<_iacc.AuthSuccess>(
'halkyon_base.gitHubSignIn',
'signIn',
{
'code': code,
'codeVerifier': codeVerifier,
'redirectUri': redirectUri,
},
);
}
/// Links a GitHub account to the signed-in person: the lossless upgrade from
/// an anonymous account. The person and everything they have stay
/// where they are; only the way they sign in changes.
/// {@category Endpoint}
class EndpointGitHubUpgrade extends _isc.EndpointRef {
EndpointGitHubUpgrade(_isc.EndpointCaller caller) : super(caller);
@override
String get name => 'halkyon_base.gitHubUpgrade';
/// Links the GitHub account that [code] proves to the caller; refusals as in
/// [linkGitHubAccount].
_ida.Future<void> upgrade({
required String code,
required String codeVerifier,
required String redirectUri,
}) => caller.callServerEndpoint<void>(
'halkyon_base.gitHubUpgrade',
'upgrade',
{
'code': code,
'codeVerifier': codeVerifier,
'redirectUri': redirectUri,
},
);
}
/// What every Halkyon instance tells its apps. Public: an app asks before
/// anyone is signed in.
/// {@category Endpoint}
class EndpointInstance extends _isc.EndpointRef {
EndpointInstance(_isc.EndpointCaller caller) : super(caller);
@override
String get name => 'halkyon_base.instance';
/// This instance's basics: GitHub sign-in only with the operator's OAuth
/// app, email sign-in only where it can mail codes, where its source is,
/// the oldest app build it works with, and where it keeps people's data
/// and its backups.
_ida.Future<_isnyur2i.InstanceBasics> basics() =>
caller.callServerEndpoint<_isnyur2i.InstanceBasics>(
'halkyon_base.instance',
'basics',
{},
);
}
class Caller extends _isc.ModuleEndpointCaller {
Caller(_isc.ServerpodClientShared client) : super(client) {
account = EndpointAccount(this);
anonymousIdp = EndpointAnonymousIdp(this);
emailCode = EndpointEmailCode(this);
emailSignIn = EndpointEmailSignIn(this);
gitHubSignIn = EndpointGitHubSignIn(this);
gitHubUpgrade = EndpointGitHubUpgrade(this);
instance = EndpointInstance(this);
}
late final EndpointAccount account;
late final EndpointAnonymousIdp anonymousIdp;
late final EndpointEmailCode emailCode;
late final EndpointEmailSignIn emailSignIn;
late final EndpointGitHubSignIn gitHubSignIn;
late final EndpointGitHubUpgrade gitHubUpgrade;
late final EndpointInstance instance;
@override
Map<String, _isc.EndpointRef> get endpointRefLookup => {
'halkyon_base.account': account,
'halkyon_base.anonymousIdp': anonymousIdp,
'halkyon_base.emailCode': emailCode,
'halkyon_base.emailSignIn': emailSignIn,
'halkyon_base.gitHubSignIn': gitHubSignIn,
'halkyon_base.gitHubUpgrade': gitHubUpgrade,
'halkyon_base.instance': instance,
};
}

View file

@ -0,0 +1,165 @@
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
/* To generate run: "serverpod generate" */
// ignore_for_file: implementation_imports
// ignore_for_file: library_private_types_in_public_api
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: public_member_api_docs
// ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_client/serverpod_client.dart' as _isc;
/// What every Halkyon instance tells its apps, to anyone, before anyone signs
/// in: how people sign in here, where the source of what it runs is, the
/// oldest app build it works with, and where it keeps people's data. Every
/// instance is run by someone else, with its own configuration.
abstract class InstanceBasics
implements _isc.SerializableModel, _isc.ProtocolSerialization {
InstanceBasics._({
this.gitHubClientId,
required this.emailSignIn,
required this.sourceCodeUrl,
required this.minimumAppBuild,
this.dataLocation,
this.backupLocation,
});
factory InstanceBasics({
String? gitHubClientId,
required bool emailSignIn,
required String sourceCodeUrl,
required int minimumAppBuild,
String? dataLocation,
String? backupLocation,
}) = _InstanceBasicsImpl;
factory InstanceBasics.fromJson(Map<String, dynamic> jsonSerialization) {
return InstanceBasics(
gitHubClientId: jsonSerialization['gitHubClientId'] as String?,
emailSignIn: _isc.BoolJsonExtension.fromJson(
jsonSerialization['emailSignIn'],
),
sourceCodeUrl: jsonSerialization['sourceCodeUrl'] as String,
minimumAppBuild: jsonSerialization['minimumAppBuild'] as int,
dataLocation: jsonSerialization['dataLocation'] as String?,
backupLocation: jsonSerialization['backupLocation'] as String?,
);
}
/// The client ID of this instance's GitHub OAuth app, or null when GitHub
/// sign-in is off here.
String? gitHubClientId;
/// Whether people can sign in with an email address here: a code mailed
/// to it.
bool emailSignIn;
/// Where the source of what this instance runs is; the licences require
/// offering it to the people who use it.
String sourceCodeUrl;
/// The oldest build of the app (the number after `+` in its version) this
/// server still works with; older apps ask to be updated.
int minimumAppBuild;
/// Where this instance keeps people's data, as its operator says
/// (DATA_LOCATION): a country's code (ISO 3166-1 alpha-2, `CH`) or `EU`;
/// where the disks are, not which law governs. Null where the operator
/// says nothing.
String? dataLocation;
/// Where it keeps its backups (BACKUP_LOCATION): as [dataLocation], or
/// `none` where it keeps none. Null where the operator says nothing.
String? backupLocation;
/// Returns a shallow copy of this [InstanceBasics]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
InstanceBasics copyWith({
String? gitHubClientId,
bool? emailSignIn,
String? sourceCodeUrl,
int? minimumAppBuild,
String? dataLocation,
String? backupLocation,
});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'halkyon_base.InstanceBasics',
if (gitHubClientId != null) 'gitHubClientId': gitHubClientId,
'emailSignIn': emailSignIn,
'sourceCodeUrl': sourceCodeUrl,
'minimumAppBuild': minimumAppBuild,
if (dataLocation != null) 'dataLocation': dataLocation,
if (backupLocation != null) 'backupLocation': backupLocation,
};
}
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'halkyon_base.InstanceBasics',
if (gitHubClientId != null) 'gitHubClientId': gitHubClientId,
'emailSignIn': emailSignIn,
'sourceCodeUrl': sourceCodeUrl,
'minimumAppBuild': minimumAppBuild,
if (dataLocation != null) 'dataLocation': dataLocation,
if (backupLocation != null) 'backupLocation': backupLocation,
};
}
@override
String toString() {
return _isc.SerializationManager.encode(this);
}
}
class _Undefined {}
class _InstanceBasicsImpl extends InstanceBasics {
_InstanceBasicsImpl({
String? gitHubClientId,
required bool emailSignIn,
required String sourceCodeUrl,
required int minimumAppBuild,
String? dataLocation,
String? backupLocation,
}) : super._(
gitHubClientId: gitHubClientId,
emailSignIn: emailSignIn,
sourceCodeUrl: sourceCodeUrl,
minimumAppBuild: minimumAppBuild,
dataLocation: dataLocation,
backupLocation: backupLocation,
);
/// Returns a shallow copy of this [InstanceBasics]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@override
InstanceBasics copyWith({
Object? gitHubClientId = _Undefined,
bool? emailSignIn,
String? sourceCodeUrl,
int? minimumAppBuild,
Object? dataLocation = _Undefined,
Object? backupLocation = _Undefined,
}) {
return InstanceBasics(
gitHubClientId: gitHubClientId is String?
? gitHubClientId
: this.gitHubClientId,
emailSignIn: emailSignIn ?? this.emailSignIn,
sourceCodeUrl: sourceCodeUrl ?? this.sourceCodeUrl,
minimumAppBuild: minimumAppBuild ?? this.minimumAppBuild,
dataLocation: dataLocation is String? ? dataLocation : this.dataLocation,
backupLocation: backupLocation is String?
? backupLocation
: this.backupLocation,
);
}
}

View file

@ -0,0 +1,296 @@
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
/* To generate run: "serverpod generate" */
// ignore_for_file: implementation_imports
// ignore_for_file: library_private_types_in_public_api
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: public_member_api_docs
// ignore_for_file: type_literal_in_constant_pattern
// ignore_for_file: use_super_parameters
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: dead_code, unnecessary_type_check
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod_auth_core_client/serverpod_auth_core_client.dart'
as _iacc;
import 'package:serverpod_auth_idp_client/serverpod_auth_idp_client.dart'
as _iaic;
import 'package:serverpod_client/serverpod_client.dart' as _isc;
import 'auth/email_code_accepted.dart' as _iys46ui1;
import 'auth/email_sign_in_refusal.dart' as _ieyvcag1;
import 'auth/email_sign_in_refused_exception.dart' as _iztfgag5;
import 'auth/github_sign_in_refusal.dart' as _ir1gd9p5;
import 'auth/github_sign_in_refused_exception.dart' as _i0xfcu5n;
import 'auth/sign_in_ways.dart' as _ie6tbh85;
import 'instance/instance_basics.dart' as _i8jixqx6;
export 'auth/email_code_accepted.dart';
export 'auth/email_sign_in_refusal.dart';
export 'auth/email_sign_in_refused_exception.dart';
export 'auth/github_sign_in_refusal.dart';
export 'auth/github_sign_in_refused_exception.dart';
export 'auth/sign_in_ways.dart';
export 'instance/instance_basics.dart';
export 'client.dart';
class Protocol extends _isc.SerializationManager {
Protocol._();
factory Protocol() => _instance;
static final Protocol _instance = Protocol._();
final Set<_isc.SerializationManager> _hostProtocols = {};
void registerHostProtocol(
String projectName,
_isc.SerializationManager protocol,
) {
_hostProtocols.add(protocol);
}
static String? getClassNameFromObjectJson(dynamic data) {
if (data is! Map) return null;
final className = data['__className__'] as String?;
if (className == null) return null;
if (!className.startsWith('halkyon_base.')) return className;
return className.substring(13);
}
@override
T deserialize<T>(
dynamic data, [
Type? t,
]) {
t ??= T;
final dataClassName = getClassNameFromObjectJson(data);
if (dataClassName != null && dataClassName != getClassNameForType(t)) {
try {
return deserializeByClassName({
'className': dataClassName,
'data': data,
});
} on _isc.DeserializationClassNameNotFoundException catch (_) {
// If the className is not recognized (e.g., older client receiving
// data with a new subtype), fall back to deserializing without the
// className, using the expected type T.
}
}
if (t == _iys46ui1.EmailCodeAccepted) {
return _iys46ui1.EmailCodeAccepted.fromJson(data) as T;
}
if (t == _ieyvcag1.EmailSignInRefusal) {
return _ieyvcag1.EmailSignInRefusal.fromJson(data) as T;
}
if (t == _iztfgag5.EmailSignInRefusedException) {
return _iztfgag5.EmailSignInRefusedException.fromJson(data) as T;
}
if (t == _ir1gd9p5.GitHubSignInRefusal) {
return _ir1gd9p5.GitHubSignInRefusal.fromJson(data) as T;
}
if (t == _i0xfcu5n.GitHubSignInRefusedException) {
return _i0xfcu5n.GitHubSignInRefusedException.fromJson(data) as T;
}
if (t == _ie6tbh85.SignInWays) {
return _ie6tbh85.SignInWays.fromJson(data) as T;
}
if (t == _i8jixqx6.InstanceBasics) {
return _i8jixqx6.InstanceBasics.fromJson(data) as T;
}
if (t == _isc.getType<_iys46ui1.EmailCodeAccepted?>()) {
return (data != null ? _iys46ui1.EmailCodeAccepted.fromJson(data) : null)
as T;
}
if (t == _isc.getType<_ieyvcag1.EmailSignInRefusal?>()) {
return (data != null ? _ieyvcag1.EmailSignInRefusal.fromJson(data) : null)
as T;
}
if (t == _isc.getType<_iztfgag5.EmailSignInRefusedException?>()) {
return (data != null
? _iztfgag5.EmailSignInRefusedException.fromJson(data)
: null)
as T;
}
if (t == _isc.getType<_ir1gd9p5.GitHubSignInRefusal?>()) {
return (data != null
? _ir1gd9p5.GitHubSignInRefusal.fromJson(data)
: null)
as T;
}
if (t == _isc.getType<_i0xfcu5n.GitHubSignInRefusedException?>()) {
return (data != null
? _i0xfcu5n.GitHubSignInRefusedException.fromJson(data)
: null)
as T;
}
if (t == _isc.getType<_ie6tbh85.SignInWays?>()) {
return (data != null ? _ie6tbh85.SignInWays.fromJson(data) : null) as T;
}
if (t == _isc.getType<_i8jixqx6.InstanceBasics?>()) {
return (data != null ? _i8jixqx6.InstanceBasics.fromJson(data) : null)
as T;
}
try {
return _iaic.Protocol().deserialize<T>(data, t);
} on _isc.DeserializationTypeNotFoundException catch (_) {}
try {
return _iacc.Protocol().deserialize<T>(data, t);
} on _isc.DeserializationTypeNotFoundException catch (_) {}
return super.deserialize<T>(data, t);
}
static String? getClassNameForType(Type type) {
return switch (type) {
_iys46ui1.EmailCodeAccepted => 'EmailCodeAccepted',
_ieyvcag1.EmailSignInRefusal => 'EmailSignInRefusal',
_iztfgag5.EmailSignInRefusedException => 'EmailSignInRefusedException',
_ir1gd9p5.GitHubSignInRefusal => 'GitHubSignInRefusal',
_i0xfcu5n.GitHubSignInRefusedException => 'GitHubSignInRefusedException',
_ie6tbh85.SignInWays => 'SignInWays',
_i8jixqx6.InstanceBasics => 'InstanceBasics',
_ => null,
};
}
@override
String? getClassNameForObject(Object? data) {
String? className = super.getClassNameForObject(data);
if (className != null) return className;
if (data is Map<String, dynamic> && data['__className__'] is String) {
return (data['__className__'] as String).replaceFirst(
'halkyon_base.',
'',
);
}
switch (data) {
case _iys46ui1.EmailCodeAccepted():
return 'EmailCodeAccepted';
case _ieyvcag1.EmailSignInRefusal():
return 'EmailSignInRefusal';
case _iztfgag5.EmailSignInRefusedException():
return 'EmailSignInRefusedException';
case _ir1gd9p5.GitHubSignInRefusal():
return 'GitHubSignInRefusal';
case _i0xfcu5n.GitHubSignInRefusedException():
return 'GitHubSignInRefusedException';
case _ie6tbh85.SignInWays():
return 'SignInWays';
case _i8jixqx6.InstanceBasics():
return 'InstanceBasics';
}
return null;
}
@override
dynamic deserializeByClassName(Map<String, dynamic> data) {
var dataClassName = data['className'];
if (dataClassName is! String) {
return super.deserializeByClassName(data);
}
if (dataClassName == 'EmailCodeAccepted') {
return deserialize<_iys46ui1.EmailCodeAccepted>(data['data']);
}
if (dataClassName == 'EmailSignInRefusal') {
return deserialize<_ieyvcag1.EmailSignInRefusal>(data['data']);
}
if (dataClassName == 'EmailSignInRefusedException') {
return deserialize<_iztfgag5.EmailSignInRefusedException>(data['data']);
}
if (dataClassName == 'GitHubSignInRefusal') {
return deserialize<_ir1gd9p5.GitHubSignInRefusal>(data['data']);
}
if (dataClassName == 'GitHubSignInRefusedException') {
return deserialize<_i0xfcu5n.GitHubSignInRefusedException>(data['data']);
}
if (dataClassName == 'SignInWays') {
return deserialize<_ie6tbh85.SignInWays>(data['data']);
}
if (dataClassName == 'InstanceBasics') {
return deserialize<_i8jixqx6.InstanceBasics>(data['data']);
}
return super.deserializeByClassName(data);
}
@override
Object? dynamicFieldToJson(
Object? object, {
bool forProtocol = false,
}) {
if ((object is List || object is Set || object is Map) ||
getClassNameForObject(object) != null) {
return super.dynamicFieldToJson(object, forProtocol: forProtocol);
}
for (final protocol in _hostProtocols) {
final className = protocol.getClassNameForObject(object);
if (className == null) continue;
final host = protocol.getModuleName();
final wrapped = {
'className': className.contains('.') ? className : '$host.$className',
'data': object,
};
return forProtocol
? _isc.SerializationManager.toEncodableForProtocol(wrapped)
: _isc.SerializationManager.toEncodable(wrapped);
}
return super.dynamicFieldToJson(object, forProtocol: forProtocol);
}
@override
dynamic deserializeDynamicFieldValue(Object? value) {
if (value == null) return null;
if (value is! Map<String, dynamic> || value['className'] is! String) {
throw FormatException(
'Dynamic fields are encoded as a Map with className and data, but got '
'${value.runtimeType} instead.',
);
}
final className = value['className'] as String;
for (final protocol in _hostProtocols) {
final host = protocol.getModuleName();
final hostPrefix = '$host.';
if (className.startsWith(hostPrefix)) {
final strippedClassName = className.substring(hostPrefix.length);
if (strippedClassName.contains('.')) {
throw FormatException(
'Dynamic field className must not use multiple prefixes: $className',
);
}
final hostData = Map<String, dynamic>.from(value);
hostData['className'] = strippedClassName;
return protocol.deserializeByClassName(hostData);
}
}
if (className.contains('.')) {
for (final protocol in _hostProtocols) {
try {
return protocol.deserializeByClassName(value);
} on _isc.DeserializationClassNameNotFoundException catch (_) {}
}
}
return deserializeByClassName(value);
}
@override
String getModuleName() => 'halkyon_base';
/// Maps any `Record`s known to this [Protocol] to their JSON representation
///
/// Throws in case the record type is not known.
///
/// This method will return `null` (only) for `null` inputs.
Map<String, dynamic>? mapRecordToJson(Record? record) {
if (record == null) {
return null;
}
try {
return _iaic.Protocol().mapRecordToJson(record);
} catch (_) {}
try {
return _iacc.Protocol().mapRecordToJson(record);
} catch (_) {}
throw Exception('Unsupported record type ${record.runtimeType}');
}
}