chore: scaffold the Serverpod 4 build

Deviations from a stock `serverpod create`:
- Dropped Flutter web support, for two reasons:
  - Appflowy editor had issues in the past, potentially related to the
    Flutter-Web IME bridge. Upstream has also open bugs in relation to
that:
    - AppFlowy-IO/appflowy-editor#1029 [Bug][Web] Unwanted space on click
       and overall space bugs (open)
    - AppFlowy-IO/appflowy-editor#782 [Bug] Editor auto-scrolling when
       pressing space too quickly if placed inside a scrollable (open)
  - Offline-first is a required feature. serverpod_offline_sync's SQLite
    support in browsers, maybe via sqlite3.wasm + OPFS, is not
documented.
- SDK constraints raised to ^3.13.0, instead of ^3.12.2, so enjoy the
  newest language features.
- Pinned to Flutter 3.47.4 via FVM.

Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-09-16 19:24:59 +02:00
commit e17d99fa51
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
11 changed files with 17563 additions and 0 deletions

13
halkyon_learn_client/.gitignore vendored Normal file
View file

@ -0,0 +1,13 @@
# Files and directories created by pub
.dart_tool/
.packages
# Omit committing pubspec.lock for library packages:
# https://dart.dev/guides/libraries/private-files#pubspeclock
pubspec.lock
# Conventional directory for build outputs
build/
# Directory created by dartdoc
doc/api/

View file

@ -0,0 +1,3 @@
## 1.0.0
- Initial version, created by Stagehand

View file

@ -0,0 +1,5 @@
# halkyon_learn_client
This is your Serverpod client. The code in here is mostly generated by
Serverpod, but you may want to make changes if you are adding modules to your
project.

View file

@ -0,0 +1,16 @@
# Defines a default set of lint rules enforced for
# projects at Google. For details and rationale,
# see https://github.com/dart-lang/pedantic#enabled-lints.
# For lint rules and documentation, see http://dart-lang.github.io/linter/lints.
# Uncomment to specify additional rules.
# linter:
# rules:
# - camel_case_types
analyzer:
exclude:
- lib/src/protocol/**
formatter:
trailing_commas: preserve

View file

@ -0,0 +1,5 @@
dartdoc:
categories:
"Endpoint":
markdown: doc/endpoint.md
name: Endpoint

View file

@ -0,0 +1,15 @@
# Callable endpoints
Each class contains callable methods that will call a method on the server side. These are normally defined in the `endpoint` directory in your server project. This client sends requests to these endpoints and returns the result.
Example usage:
```dart
// How to use GreetingEndpoint.
client.greeting.hello("world!");
// Generic format.
client.<endpoint>.<method>(...);
```
Please see the full official documentation [here](https://docs.serverpod.dev)

View file

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

View file

@ -0,0 +1,331 @@
/* 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_learn_client/src/protocol/greetings/greeting.dart'
as _ili0bmg9;
import 'package:http/http.dart' as _i85jenna;
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 'protocol.dart' as _il2as5qe;
/// By extending [EmailIdpBaseEndpoint], the email identity provider endpoints
/// are made available on the server and enable the corresponding sign-in widget
/// on the client.
/// {@category Endpoint}
class EndpointEmailIdp extends _iaic.EndpointEmailIdpBase {
EndpointEmailIdp(_isc.EndpointCaller caller) : super(caller);
@override
String get name => 'emailIdp';
/// Logs in the user and returns a new session.
///
/// Throws an [EmailAccountLoginException] in case of errors, with reason:
/// - [EmailAccountLoginExceptionReason.invalidCredentials] if the email or
/// password is incorrect.
/// - [EmailAccountLoginExceptionReason.tooManyAttempts] if there have been
/// too many failed login attempts.
///
/// Throws an [AuthUserBlockedException] if the auth user is blocked.
@override
_ida.Future<_iacc.AuthSuccess> login({
required String email,
required String password,
}) => caller.callServerEndpoint<_iacc.AuthSuccess>(
'emailIdp',
'login',
{
'email': email,
'password': password,
},
);
/// Starts the registration for a new user account with an email-based login
/// associated to it.
///
/// Upon successful completion of this method, an email will have been
/// sent to [email] with a verification link, which the user must open to
/// complete the registration.
///
/// Always returns a account request ID, which can be used to complete the
/// registration. If the email is already registered, the returned ID will not
/// be valid.
@override
_ida.Future<_isc.UuidValue> startRegistration({required String email}) =>
caller.callServerEndpoint<_isc.UuidValue>(
'emailIdp',
'startRegistration',
{'email': email},
);
/// Verifies an account request code and returns a token
/// that can be used to complete the account creation.
///
/// Throws an [EmailAccountRequestException] in case of errors, with reason:
/// - [EmailAccountRequestExceptionReason.expired] if the account request has
/// already expired.
/// - [EmailAccountRequestExceptionReason.policyViolation] if the password
/// does not comply with the password policy.
/// - [EmailAccountRequestExceptionReason.invalid] if no request exists
/// for the given [accountRequestId] or [verificationCode] is invalid.
@override
_ida.Future<String> verifyRegistrationCode({
required _isc.UuidValue accountRequestId,
required String verificationCode,
}) => caller.callServerEndpoint<String>(
'emailIdp',
'verifyRegistrationCode',
{
'accountRequestId': accountRequestId,
'verificationCode': verificationCode,
},
);
/// Completes a new account registration, creating a new auth user with a
/// profile and attaching the given email account to it.
///
/// Throws an [EmailAccountRequestException] in case of errors, with reason:
/// - [EmailAccountRequestExceptionReason.expired] if the account request has
/// already expired.
/// - [EmailAccountRequestExceptionReason.policyViolation] if the password
/// does not comply with the password policy.
/// - [EmailAccountRequestExceptionReason.invalid] if the [registrationToken]
/// is invalid.
///
/// Throws an [AuthUserBlockedException] if the auth user is blocked.
///
/// Returns a session for the newly created user.
@override
_ida.Future<_iacc.AuthSuccess> finishRegistration({
required String registrationToken,
required String password,
}) => caller.callServerEndpoint<_iacc.AuthSuccess>(
'emailIdp',
'finishRegistration',
{
'registrationToken': registrationToken,
'password': password,
},
);
/// Requests a password reset for [email].
///
/// If the email address is registered, an email with reset instructions will
/// be send out. If the email is unknown, this method will have no effect.
///
/// Always returns a password reset request ID, which can be used to complete
/// the reset. If the email is not registered, the returned ID will not be
/// valid.
///
/// Throws an [EmailAccountPasswordResetException] in case of errors, with reason:
/// - [EmailAccountPasswordResetExceptionReason.tooManyAttempts] if the user has
/// made too many attempts trying to request a password reset.
///
@override
_ida.Future<_isc.UuidValue> startPasswordReset({required String email}) =>
caller.callServerEndpoint<_isc.UuidValue>(
'emailIdp',
'startPasswordReset',
{'email': email},
);
/// Verifies a password reset code and returns a finishPasswordResetToken
/// that can be used to finish the password reset.
///
/// Throws an [EmailAccountPasswordResetException] in case of errors, with reason:
/// - [EmailAccountPasswordResetExceptionReason.expired] if the password reset
/// request has already expired.
/// - [EmailAccountPasswordResetExceptionReason.tooManyAttempts] if the user has
/// made too many attempts trying to verify the password reset.
/// - [EmailAccountPasswordResetExceptionReason.invalid] if no request exists
/// for the given [passwordResetRequestId] or [verificationCode] is invalid.
///
/// If multiple steps are required to complete the password reset, this endpoint
/// should be overridden to return credentials for the next step instead
/// of the credentials for setting the password.
@override
_ida.Future<String> verifyPasswordResetCode({
required _isc.UuidValue passwordResetRequestId,
required String verificationCode,
}) => caller.callServerEndpoint<String>(
'emailIdp',
'verifyPasswordResetCode',
{
'passwordResetRequestId': passwordResetRequestId,
'verificationCode': verificationCode,
},
);
/// Completes a password reset request by setting a new password.
///
/// The [verificationCode] returned from [verifyPasswordResetCode] is used to
/// validate the password reset request.
///
/// Throws an [EmailAccountPasswordResetException] in case of errors, with reason:
/// - [EmailAccountPasswordResetExceptionReason.expired] if the password reset
/// request has already expired.
/// - [EmailAccountPasswordResetExceptionReason.policyViolation] if the new
/// password does not comply with the password policy.
/// - [EmailAccountPasswordResetExceptionReason.invalid] if no request exists
/// for the given [passwordResetRequestId] or [verificationCode] is invalid.
///
/// Throws an [AuthUserBlockedException] if the auth user is blocked.
@override
_ida.Future<void> finishPasswordReset({
required String finishPasswordResetToken,
required String newPassword,
}) => caller.callServerEndpoint<void>(
'emailIdp',
'finishPasswordReset',
{
'finishPasswordResetToken': finishPasswordResetToken,
'newPassword': newPassword,
},
);
@override
_ida.Future<bool> hasAccount() => caller.callServerEndpoint<bool>(
'emailIdp',
'hasAccount',
{},
);
}
/// By extending [RefreshJwtTokensEndpoint], the JWT token refresh endpoint
/// is made available on the server and enables automatic token refresh on the client.
/// {@category Endpoint}
class EndpointJwtRefresh extends _iacc.EndpointRefreshJwtTokens {
EndpointJwtRefresh(_isc.EndpointCaller caller) : super(caller);
@override
String get name => 'jwtRefresh';
/// Creates a new token pair for the given [refreshToken].
///
/// If [refreshToken] is omitted, cookie-mode web clients fall back to the
/// configured HttpOnly refresh cookie. When neither source is present this
/// throws [RefreshTokenNotFoundException], the same public "no usable refresh
/// credential" exception used for unknown refresh tokens.
///
/// Can throw the following exceptions:
/// -[RefreshTokenMalformedException]: refresh token is malformed and could
/// not be parsed. Not expected to happen for tokens issued by the server.
/// -[RefreshTokenNotFoundException]: refresh token is unknown to the server.
/// Either the token was deleted or generated by a different server.
/// -[RefreshTokenExpiredException]: refresh token has expired. Will happen
/// only if it has not been used within configured `refreshTokenLifetime`.
/// -[RefreshTokenInvalidSecretException]: refresh token is incorrect, meaning
/// it does not refer to the current secret refresh token. This indicates
/// either a malfunctioning client or a malicious attempt by someone who has
/// obtained the refresh token. In this case the underlying refresh token
/// will be deleted, and access to it will expire fully when the last access
/// token is elapsed.
///
/// This endpoint is unauthenticated, meaning the client won't include any
/// authentication information with the call.
@override
_ida.Future<_iacc.AuthSuccess> refreshAccessToken({String? refreshToken}) =>
caller.callServerEndpoint<_iacc.AuthSuccess>(
'jwtRefresh',
'refreshAccessToken',
{'refreshToken': refreshToken},
authenticated: false,
);
}
/// This is an example endpoint that returns a greeting message through
/// its [hello] method.
/// {@category Endpoint}
class EndpointGreeting extends _isc.EndpointRef {
EndpointGreeting(_isc.EndpointCaller caller) : super(caller);
@override
String get name => 'greeting';
/// Returns a personalized greeting message: "Hello {name}".
_ida.Future<_ili0bmg9.Greeting> hello(String name) =>
caller.callServerEndpoint<_ili0bmg9.Greeting>(
'greeting',
'hello',
{'name': name},
);
}
class Modules {
Modules(Client client) {
serverpod_auth_idp = _iaic.Caller(client);
serverpod_auth_core = _iacc.Caller(client);
}
late final _iaic.Caller serverpod_auth_idp;
late final _iacc.Caller serverpod_auth_core;
}
class Client extends _isc.ServerpodClientShared {
Client(
String host, {
dynamic securityContext,
Duration? streamingConnectionTimeout,
Duration? connectionTimeout,
Function(
_isc.MethodCallContext,
Object,
StackTrace,
)?
onFailedCall,
Function(_isc.MethodCallContext)? onSucceededCall,
bool? disconnectStreamsOnLostInternetConnection,
_i85jenna.Client? httpClientOverride,
}) : super(
host,
_il2as5qe.Protocol(),
securityContext: securityContext,
streamingConnectionTimeout: streamingConnectionTimeout,
connectionTimeout: connectionTimeout,
onFailedCall: onFailedCall,
onSucceededCall: onSucceededCall,
disconnectStreamsOnLostInternetConnection:
disconnectStreamsOnLostInternetConnection,
httpClientOverride: httpClientOverride,
) {
emailIdp = EndpointEmailIdp(this);
jwtRefresh = EndpointJwtRefresh(this);
greeting = EndpointGreeting(this);
modules = Modules(this);
}
late final EndpointEmailIdp emailIdp;
late final EndpointJwtRefresh jwtRefresh;
late final EndpointGreeting greeting;
late final Modules modules;
@override
Map<String, _isc.EndpointRef> get endpointRefLookup => {
'emailIdp': emailIdp,
'jwtRefresh': jwtRefresh,
'greeting': greeting,
};
@override
Map<String, _isc.ModuleEndpointCaller> get moduleLookup => {
'serverpod_auth_idp': modules.serverpod_auth_idp,
'serverpod_auth_core': modules.serverpod_auth_core,
};
}

View file

@ -0,0 +1,109 @@
/* 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 greeting message which can be sent to or from the server.
abstract class Greeting
implements _isc.SerializableModel, _isc.ProtocolSerialization {
Greeting._({
required this.message,
required this.author,
required this.timestamp,
});
factory Greeting({
required String message,
required String author,
required DateTime timestamp,
}) = _GreetingImpl;
factory Greeting.fromJson(Map<String, dynamic> jsonSerialization) {
return Greeting(
message: jsonSerialization['message'] as String,
author: jsonSerialization['author'] as String,
timestamp: _isc.DateTimeJsonExtension.fromJson(
jsonSerialization['timestamp'],
),
);
}
/// The greeting message.
String message;
/// The author of the greeting message.
String author;
/// The time when the message was created.
DateTime timestamp;
/// Returns a shallow copy of this [Greeting]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
Greeting copyWith({
String? message,
String? author,
DateTime? timestamp,
});
@override
Map<String, dynamic> toJson() {
return {
'__className__': 'Greeting',
'message': message,
'author': author,
'timestamp': timestamp.toJson(),
};
}
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'__className__': 'Greeting',
'message': message,
'author': author,
'timestamp': timestamp.toJson(),
};
}
@override
String toString() {
return _isc.SerializationManager.encode(this);
}
}
class _GreetingImpl extends Greeting {
_GreetingImpl({
required String message,
required String author,
required DateTime timestamp,
}) : super._(
message: message,
author: author,
timestamp: timestamp,
);
/// Returns a shallow copy of this [Greeting]
/// with some or all fields replaced by the given arguments.
@_isc.useResult
@override
Greeting copyWith({
String? message,
String? author,
DateTime? timestamp,
}) {
return Greeting(
message: message ?? this.message,
author: author ?? this.author,
timestamp: timestamp ?? this.timestamp,
);
}
}

View file

@ -0,0 +1,155 @@
/* 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 'greetings/greeting.dart' as _izw8z7ou;
export 'greetings/greeting.dart';
export 'client.dart';
class Protocol extends _isc.SerializationManager {
Protocol._();
factory Protocol() => _instance;
static final Protocol _instance = Protocol._().._registerHostProtocols();
static String? getClassNameFromObjectJson(dynamic data) {
if (data is! Map) return null;
final className = data['__className__'] as String?;
return className;
}
@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 == _izw8z7ou.Greeting) {
return _izw8z7ou.Greeting.fromJson(data) as T;
}
if (t == _isc.getType<_izw8z7ou.Greeting?>()) {
return (data != null ? _izw8z7ou.Greeting.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) {
_izw8z7ou.Greeting => 'Greeting',
_ => 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_learn.',
'',
);
}
switch (data) {
case _izw8z7ou.Greeting():
return 'Greeting';
}
className = _iaic.Protocol().getClassNameForObject(data);
if (className != null) {
return className.contains('.')
? className
: 'serverpod_auth_idp.$className';
}
className = _iacc.Protocol().getClassNameForObject(data);
if (className != null) {
return className.contains('.')
? className
: 'serverpod_auth_core.$className';
}
return null;
}
@override
dynamic deserializeByClassName(Map<String, dynamic> data) {
var dataClassName = data['className'];
if (dataClassName is! String) {
return super.deserializeByClassName(data);
}
if (dataClassName == 'Greeting') {
return deserialize<_izw8z7ou.Greeting>(data['data']);
}
if (dataClassName.startsWith('serverpod_auth_idp.')) {
data['className'] = dataClassName.substring(19);
return _iaic.Protocol().deserializeByClassName(data);
}
if (dataClassName.startsWith('serverpod_auth_core.')) {
data['className'] = dataClassName.substring(20);
return _iacc.Protocol().deserializeByClassName(data);
}
return super.deserializeByClassName(data);
}
void _registerHostProtocols() {
_iaic.Protocol().registerHostProtocol('halkyon_learn', this);
_iacc.Protocol().registerHostProtocol('halkyon_learn', this);
}
@override
String getModuleName() => 'halkyon_learn';
/// 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}');
}
}

View file

@ -0,0 +1,11 @@
name: halkyon_learn_client
description: Starting point for a Serverpod client.
environment:
sdk: '^3.13.0'
resolution: workspace
dependencies:
serverpod_auth_idp_client: 4.0.0
serverpod_client: 4.0.0