From 32a94d7f49b6e414fdf33ef48d89af836f0c6b53 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 11:34:25 +0000 Subject: [PATCH 01/10] fix: resolve isolatedDeclarations errors (TS9021, TS9038) Extract Data.TaggedClass/TaggedError/Context.Tag expressions from extends clauses into intermediate const variables with explicit type annotations, following the same pattern used by Effect's own declaration output. For Data.TaggedClass: use ReturnType> For Data.TaggedError: use ReturnType> For Context.Tag: use Context.TagClass For computed [TypeId] properties in BotApiError classes: use interface merging + Object.defineProperty on prototype to satisfy both isolatedDeclarations and runtime type-checking. Closes #31 Co-authored-by: Vladislav Deryabkin --- src/Bot.ts | 6 ++--- src/BotApi.ts | 6 ++--- src/BotApiError.ts | 53 +++++++++++++++++++++++++++--------------- src/BotApiTransport.ts | 6 ++--- src/BotApiUrl.ts | 6 ++--- src/Content.ts | 39 ++++++++++++++++++++----------- src/Dialog.ts | 21 +++++++++++------ src/File.ts | 3 ++- src/Markup.ts | 12 ++++++---- src/Send.ts | 6 ++--- src/Text.ts | 12 ++++++---- 11 files changed, 102 insertions(+), 68 deletions(-) diff --git a/src/Bot.ts b/src/Bot.ts index 2029db5..f906906 100644 --- a/src/Bot.ts +++ b/src/Bot.ts @@ -4,10 +4,8 @@ import * as Context from 'effect/Context' export type Bot = Effect.Effect -export class Update extends Context.Tag('@grom.js/effect-tg/Bot/Update')< - Update, - BotApi.Types.Update ->() {} +const _Update: Context.TagClass = Context.Tag('@grom.js/effect-tg/Bot/Update')() +export class Update extends _Update {} export interface Middleware { (self: Bot): Bot diff --git a/src/BotApi.ts b/src/BotApi.ts index 4addbe3..5f34706 100644 --- a/src/BotApi.ts +++ b/src/BotApi.ts @@ -19,10 +19,8 @@ import * as internal from './internal/botApi.ts' export type { MethodParams, MethodResults, Service, Types } -export class BotApi extends Context.Tag('@grom.js/effect-tg/BotApi')< - BotApi, - Service ->() {} +const _BotApi: Context.TagClass = Context.Tag('@grom.js/effect-tg/BotApi')() +export class BotApi extends _BotApi {} export interface BotApiMethod { (...args: MethodArgs): Effect.Effect< diff --git a/src/BotApiError.ts b/src/BotApiError.ts index b0a0561..5887a84 100644 --- a/src/BotApiError.ts +++ b/src/BotApiError.ts @@ -25,13 +25,15 @@ export type BotApiError = /** * Error caused by the transport when accessing Bot API. */ -export class TransportError extends Data.TaggedError('TransportError')<{ +const _TransportError: ReturnType> = Data.TaggedError('TransportError') +// eslint-disable-next-line ts/no-unsafe-declaration-merging +export interface TransportError { readonly [TypeId]: TypeId } +// eslint-disable-next-line ts/no-unsafe-declaration-merging +export class TransportError extends _TransportError<{ cause: | HttpClientError.HttpClientError | HttpBody.HttpBodyError }> { - readonly [TypeId]: TypeId = TypeId - override get message(): string { return Match.value(this.cause).pipe( Match.tagsExhaustive({ @@ -47,45 +49,58 @@ export class TransportError extends Data.TaggedError('TransportError')<{ ) } } +Object.defineProperty(TransportError.prototype, TypeId, { value: TypeId }) -export class MethodFailed extends Data.TaggedError('MethodFailed')<{ +const _MethodFailed: ReturnType> = Data.TaggedError('MethodFailed') +// eslint-disable-next-line ts/no-unsafe-declaration-merging +export interface MethodFailed { readonly [TypeId]: TypeId } +// eslint-disable-next-line ts/no-unsafe-declaration-merging +export class MethodFailed extends _MethodFailed<{ response: FailureResponse possibleReason: MethodFailureReason }> { - readonly [TypeId]: TypeId = TypeId - - override get message() { + override get message(): string { return `(${this.response.error_code}) ${this.response.description}` } } +Object.defineProperty(MethodFailed.prototype, TypeId, { value: TypeId }) -export class GroupUpgraded extends Data.TaggedError('GroupUpgraded')<{ +const _GroupUpgraded: ReturnType> = Data.TaggedError('GroupUpgraded') +// eslint-disable-next-line ts/no-unsafe-declaration-merging +export interface GroupUpgraded { readonly [TypeId]: TypeId } +// eslint-disable-next-line ts/no-unsafe-declaration-merging +export class GroupUpgraded extends _GroupUpgraded<{ response: FailureResponse supergroup: Dialog.Supergroup }> { - readonly [TypeId]: TypeId = TypeId - - override get message() { + override get message(): string { return `Group has been upgraded to a supergroup with ID ${this.supergroup.id}.` } } +Object.defineProperty(GroupUpgraded.prototype, TypeId, { value: TypeId }) -export class RateLimited extends Data.TaggedError('RateLimited')<{ +const _RateLimited: ReturnType> = Data.TaggedError('RateLimited') +// eslint-disable-next-line ts/no-unsafe-declaration-merging +export interface RateLimited { readonly [TypeId]: TypeId } +// eslint-disable-next-line ts/no-unsafe-declaration-merging +export class RateLimited extends _RateLimited<{ response: FailureResponse retryAfter: Duration.Duration }> { - readonly [TypeId]: TypeId = TypeId - - override get message() { + override get message(): string { return `Flood limit exceeded. Should wait for ${Duration.format(this.retryAfter)} before retrying.` } } +Object.defineProperty(RateLimited.prototype, TypeId, { value: TypeId }) -export class InternalServerError extends Data.TaggedError('InternalServerError')<{ +const _InternalServerError: ReturnType> = Data.TaggedError('InternalServerError') +// eslint-disable-next-line ts/no-unsafe-declaration-merging +export interface InternalServerError { readonly [TypeId]: TypeId } +// eslint-disable-next-line ts/no-unsafe-declaration-merging +export class InternalServerError extends _InternalServerError<{ response: FailureResponse -}> { - readonly [TypeId]: TypeId = TypeId -} +}> {} +Object.defineProperty(InternalServerError.prototype, TypeId, { value: TypeId }) export const fromResponse = (response: FailureResponse): BotApiError => { if (response.error_code === 429 && response.parameters?.retry_after != null) { diff --git a/src/BotApiTransport.ts b/src/BotApiTransport.ts index a1cfdae..78ba293 100644 --- a/src/BotApiTransport.ts +++ b/src/BotApiTransport.ts @@ -7,10 +7,8 @@ import * as Layer from 'effect/Layer' import * as BotApiUrl from './BotApiUrl.ts' import * as internal from './internal/botApiTransport.ts' -export class BotApiTransport extends Context.Tag('@grom.js/effect-tg/BotApiTransport')< - BotApiTransport, - Service ->() {} +const _BotApiTransport: Context.TagClass = Context.Tag('@grom.js/effect-tg/BotApiTransport')() +export class BotApiTransport extends _BotApiTransport {} export interface Service { sendRequest: ( diff --git a/src/BotApiUrl.ts b/src/BotApiUrl.ts index b70297b..7aefdc2 100644 --- a/src/BotApiUrl.ts +++ b/src/BotApiUrl.ts @@ -1,9 +1,7 @@ import * as Context from 'effect/Context' -export class BotApiUrl extends Context.Tag('@grom.js/effect-tg/BotApiUrl')< - BotApiUrl, - Service ->() {} +const _BotApiUrl: Context.TagClass = Context.Tag('@grom.js/effect-tg/BotApiUrl')() +export class BotApiUrl extends _BotApiUrl {} export interface Service { toMethod: (method: string) => URL diff --git a/src/Content.ts b/src/Content.ts index 8e5bc7d..63526d4 100644 --- a/src/Content.ts +++ b/src/Content.ts @@ -33,7 +33,8 @@ export type Content = * @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_text.html TDLib • td_api.inputMessageText} * @see {@link https://core.telegram.org/bots/api#sendmessage Bot API • sendMessage} */ -export class Text extends Data.TaggedClass('Text')<{ +const _Text: ReturnType> = Data.TaggedClass('Text') +export class Text extends _Text<{ text: Text_.Text linkPreview: Option.Option }> {} @@ -44,7 +45,8 @@ export class Text extends Data.TaggedClass('Text')<{ * @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_photo.html TDLib • td_api.inputMessagePhoto} * @see {@link https://core.telegram.org/bots/api#sendphoto Bot API • sendPhoto} */ -export class Photo extends Data.TaggedClass('Photo')<{ +const _Photo: ReturnType> = Data.TaggedClass('Photo') +export class Photo extends _Photo<{ file: File.FileId | File.External | File.InputFile caption: Option.Option layout: 'caption-above' | 'caption-below' @@ -57,7 +59,8 @@ export class Photo extends Data.TaggedClass('Photo')<{ * @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_audio.html TDLib • td_api.inputMessageAudio} * @see {@link https://core.telegram.org/bots/api#sendaudio Bot API • sendAudio} */ -export class Audio extends Data.TaggedClass('Audio')<{ +const _Audio: ReturnType> = Data.TaggedClass('Audio') +export class Audio extends _Audio<{ file: File.FileId | File.External | File.InputFile caption: Option.Option duration: Option.Option @@ -72,7 +75,8 @@ export class Audio extends Data.TaggedClass('Audio')<{ * @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_document.html TDLib • td_api.inputMessageDocument} * @see {@link https://core.telegram.org/bots/api#senddocument Bot API • sendDocument} */ -export class Document extends Data.TaggedClass('Document')<{ +const _Document: ReturnType> = Data.TaggedClass('Document') +export class Document extends _Document<{ file: File.FileId | File.External | File.InputFile caption: Option.Option thumbnail: Option.Option @@ -85,7 +89,8 @@ export class Document extends Data.TaggedClass('Document')<{ * @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_video.html TDLib • td_api.inputMessageVideo} * @see {@link https://core.telegram.org/bots/api#sendvideo Bot API • sendVideo} */ -export class Video extends Data.TaggedClass('Video')<{ +const _Video: ReturnType> = Data.TaggedClass('Video') +export class Video extends _Video<{ file: File.FileId | File.External | File.InputFile caption: Option.Option layout: 'caption-above' | 'caption-below' @@ -105,7 +110,8 @@ export class Video extends Data.TaggedClass('Video')<{ * @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_animation.html TDLib • td_api.inputMessageAnimation} * @see {@link https://core.telegram.org/bots/api#sendanimation Bot API • sendAnimation} */ -export class Animation extends Data.TaggedClass('Animation')<{ +const _Animation: ReturnType> = Data.TaggedClass('Animation') +export class Animation extends _Animation<{ file: File.FileId | File.External | File.InputFile caption: Option.Option layout: 'caption-above' | 'caption-below' @@ -122,7 +128,8 @@ export class Animation extends Data.TaggedClass('Animation')<{ * @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_voice_note.html TDLib • td_api.inputMessageVoiceNote} * @see {@link https://core.telegram.org/bots/api#sendvoice Bot API • sendVoice} */ -export class Voice extends Data.TaggedClass('Voice')<{ +const _Voice: ReturnType> = Data.TaggedClass('Voice') +export class Voice extends _Voice<{ file: File.FileId | File.External | File.InputFile caption: Option.Option duration: Option.Option @@ -134,7 +141,8 @@ export class Voice extends Data.TaggedClass('Voice')<{ * @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_video_note.html TDLib • td_api.inputMessageVideoNote} * @see {@link https://core.telegram.org/bots/api#sendvideonote Bot API • sendVideoNote} */ -export class VideoNote extends Data.TaggedClass('VideoNote')<{ +const _VideoNote: ReturnType> = Data.TaggedClass('VideoNote') +export class VideoNote extends _VideoNote<{ file: File.FileId | File.InputFile duration: Option.Option diameter: Option.Option @@ -147,7 +155,8 @@ export class VideoNote extends Data.TaggedClass('VideoNote')<{ * @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_location.html TDLib • td_api.inputMessageLocation} * @see {@link https://core.telegram.org/bots/api#sendlocation Bot API • sendLocation} */ -export class Location extends Data.TaggedClass('Location')<{ +const _Location: ReturnType> = Data.TaggedClass('Location') +export class Location extends _Location<{ latitude: number longitude: number uncertaintyRadius: Option.Option @@ -162,7 +171,8 @@ export class Location extends Data.TaggedClass('Location')<{ * @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_venue.html TDLib • td_api.inputMessageVenue} * @see {@link https://core.telegram.org/bots/api#sendvenue Bot API • sendVenue} */ -export class Venue extends Data.TaggedClass('Venue')<{ +const _Venue: ReturnType> = Data.TaggedClass('Venue') +export class Venue extends _Venue<{ latitude: number longitude: number title: string @@ -179,7 +189,8 @@ export class Venue extends Data.TaggedClass('Venue')<{ * @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_contact.html TDLib • td_api.inputMessageContact} * @see {@link https://core.telegram.org/bots/api#sendcontact Bot API • sendContact} */ -export class Contact extends Data.TaggedClass('Contact')<{ +const _Contact: ReturnType> = Data.TaggedClass('Contact') +export class Contact extends _Contact<{ phoneNumber: string firstName: string lastName: Option.Option @@ -192,7 +203,8 @@ export class Contact extends Data.TaggedClass('Contact')<{ * @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_dice.html TDLib • td_api.inputMessageDice} * @see {@link https://core.telegram.org/bots/api#senddice Bot API • sendDice} */ -export class Dice extends Data.TaggedClass('Dice')<{ +const _Dice: ReturnType> = Data.TaggedClass('Dice') +export class Dice extends _Dice<{ emoji: '🎲' | '🎯' | '🏀' | '⚽' | '🎳' | '🎰' }> {} @@ -202,7 +214,8 @@ export class Dice extends Data.TaggedClass('Dice')<{ * @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_sticker.html TDLib • td_api.inputMessageSticker} * @see {@link https://core.telegram.org/bots/api#sendsticker Bot API • sendSticker} */ -export class Sticker extends Data.TaggedClass('Sticker')<{ +const _Sticker: ReturnType> = Data.TaggedClass('Sticker') +export class Sticker extends _Sticker<{ file: File.FileId | File.External | File.InputFile emoji: Option.Option }> {} diff --git a/src/Dialog.ts b/src/Dialog.ts index 48b3d52..868d2c4 100644 --- a/src/Dialog.ts +++ b/src/Dialog.ts @@ -14,17 +14,20 @@ export type Dialog = | ForumTopic | ChannelDm -export class PrivateTopic extends Data.TaggedClass('PrivateTopic')<{ +const _PrivateTopic: ReturnType> = Data.TaggedClass('PrivateTopic') +export class PrivateTopic extends _PrivateTopic<{ user: User topicId: number }> {} -export class ForumTopic extends Data.TaggedClass('ForumTopic')<{ +const _ForumTopic: ReturnType> = Data.TaggedClass('ForumTopic') +export class ForumTopic extends _ForumTopic<{ supergroup: Supergroup topicId: number }> {} -export class ChannelDm extends Data.TaggedClass('ChannelDm')<{ +const _ChannelDm: ReturnType> = Data.TaggedClass('ChannelDm') +export class ChannelDm extends _ChannelDm<{ channel: Channel topicId: number }> {} @@ -39,7 +42,8 @@ export type Peer = | Channel | Supergroup -export class User extends Data.TaggedClass('User')<{ +const _User: ReturnType> = Data.TaggedClass('User') +export class User extends _User<{ id: UserId }> { public dialogId(): DialogId { @@ -51,7 +55,8 @@ export class User extends Data.TaggedClass('User')<{ } } -export class Group extends Data.TaggedClass('Group')<{ +const _Group: ReturnType> = Data.TaggedClass('Group') +export class Group extends _Group<{ id: GroupId }> { public dialogId(): DialogId { @@ -59,7 +64,8 @@ export class Group extends Data.TaggedClass('Group')<{ } } -export class Channel extends Data.TaggedClass('Channel')<{ +const _Channel: ReturnType> = Data.TaggedClass('Channel') +export class Channel extends _Channel<{ id: ChannelId }> { public dialogId(): DialogId { @@ -71,7 +77,8 @@ export class Channel extends Data.TaggedClass('Channel')<{ } } -export class Supergroup extends Data.TaggedClass('Supergroup')<{ +const _Supergroup: ReturnType> = Data.TaggedClass('Supergroup') +export class Supergroup extends _Supergroup<{ id: SupergroupId }> { public dialogId(): DialogId { diff --git a/src/File.ts b/src/File.ts index 1332757..78bb082 100644 --- a/src/File.ts +++ b/src/File.ts @@ -16,7 +16,8 @@ export const FileId: Brand.Brand.Constructor = Brand.nominal() export type External = URL & Brand.Brand<'External'> export const External: Brand.Brand.Constructor = Brand.nominal() -export class InputFile extends Data.TaggedClass('InputFile')<{ +const _InputFile: ReturnType> = Data.TaggedClass('InputFile') +export class InputFile extends _InputFile<{ stream: Stream.Stream filename: string mimeType?: string diff --git a/src/Markup.ts b/src/Markup.ts index dc04d9a..dcb8114 100644 --- a/src/Markup.ts +++ b/src/Markup.ts @@ -14,11 +14,13 @@ export type Markup = | ReplyKeyboardRemove | ForceReply -export class InlineKeyboard extends Data.TaggedClass('InlineKeyboard')<{ +const _InlineKeyboard: ReturnType> = Data.TaggedClass('InlineKeyboard') +export class InlineKeyboard extends _InlineKeyboard<{ readonly rows: ReadonlyArray> }> {} -export class ReplyKeyboard extends Data.TaggedClass('ReplyKeyboard')<{ +const _ReplyKeyboard: ReturnType> = Data.TaggedClass('ReplyKeyboard') +export class ReplyKeyboard extends _ReplyKeyboard<{ readonly rows: ReadonlyArray> readonly persistent: boolean readonly resizable: boolean @@ -27,11 +29,13 @@ export class ReplyKeyboard extends Data.TaggedClass('ReplyKeyboard')<{ readonly inputPlaceholder: Option.Option }> {} -export class ReplyKeyboardRemove extends Data.TaggedClass('ReplyKeyboardRemove')<{ +const _ReplyKeyboardRemove: ReturnType> = Data.TaggedClass('ReplyKeyboardRemove') +export class ReplyKeyboardRemove extends _ReplyKeyboardRemove<{ readonly selective: boolean }> {} -export class ForceReply extends Data.TaggedClass('ForceReply')<{ +const _ForceReply: ReturnType> = Data.TaggedClass('ForceReply') +export class ForceReply extends _ForceReply<{ readonly selective: boolean readonly inputPlaceholder: Option.Option }> {} diff --git a/src/Send.ts b/src/Send.ts index 0a5f04a..aebcec3 100644 --- a/src/Send.ts +++ b/src/Send.ts @@ -116,10 +116,8 @@ export const message = (content: Content.Content, params?: { /** * Target dialog for sending messages. */ -export class TargetDialog extends Context.Tag('@grom.js/effect-tg/Send/TargetDialog')< - TargetDialog, - Dialog.Dialog | Dialog.DialogId ->() {} +const _TargetDialog: Context.TagClass = Context.Tag('@grom.js/effect-tg/Send/TargetDialog')() +export class TargetDialog extends _TargetDialog {} /** * Provides the target dialog for sending messages. diff --git a/src/Text.ts b/src/Text.ts index 02a5dd3..0e79d06 100644 --- a/src/Text.ts +++ b/src/Text.ts @@ -11,20 +11,24 @@ export type Text = | Markdown | Tgx -export class Plain extends Data.TaggedClass('Plain')<{ +const _Plain: ReturnType> = Data.TaggedClass('Plain') +export class Plain extends _Plain<{ text: string entities?: Array }> {} -export class Html extends Data.TaggedClass('Html')<{ +const _Html: ReturnType> = Data.TaggedClass('Html') +export class Html extends _Html<{ html: string }> {} -export class Markdown extends Data.TaggedClass('Markdown')<{ +const _Markdown: ReturnType> = Data.TaggedClass('Markdown') +export class Markdown extends _Markdown<{ markdown: string }> {} -export class Tgx extends Data.TaggedClass('Tgx')<{ +const _Tgx: ReturnType> = Data.TaggedClass('Tgx') +export class Tgx extends _Tgx<{ tgx: TgxElement }> {} From 302d55b1a1e1cd4fc50e5426339bc47f7e0a8c44 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 11:57:12 +0000 Subject: [PATCH 02/10] refactor: use Effect-style patterns for isolatedDeclarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace class-based patterns with idiomatic Effect alternatives: - Data.TaggedClass → interface + Data.tagged (Content, Dialog, File, Markup, Text modules) - Context.Tag class → Context.GenericTag (Bot, BotApi, BotApiTransport, BotApiUrl, Send modules) - Data.TaggedError classes use _base naming convention matching Effect's own declaration output (BotApiError) Additional changes: - Dialog peer methods (dialogId, topic, directMessages) converted to standalone functions following Effect's functional style - File.isInputFile guard replaces instanceof checks - Constructor calls changed from new Foo({}) to Foo({}) Co-authored-by: Vladislav Deryabkin --- src/Bot.ts | 4 +- src/BotApi.ts | 4 +- src/BotApiError.ts | 20 +-- src/BotApiTransport.ts | 4 +- src/BotApiUrl.ts | 4 +- src/Content.ts | 271 +++++++++++++++----------------- src/Dialog.ts | 111 ++++++------- src/File.ts | 16 +- src/Markup.ts | 36 +++-- src/Send.ts | 20 +-- src/Text.ts | 52 +++--- src/internal/botApiTransport.ts | 4 +- src/internal/dialog.ts | 14 +- src/internal/send.ts | 18 +-- 14 files changed, 285 insertions(+), 293 deletions(-) diff --git a/src/Bot.ts b/src/Bot.ts index f906906..bc9c74e 100644 --- a/src/Bot.ts +++ b/src/Bot.ts @@ -4,8 +4,8 @@ import * as Context from 'effect/Context' export type Bot = Effect.Effect -const _Update: Context.TagClass = Context.Tag('@grom.js/effect-tg/Bot/Update')() -export class Update extends _Update {} +export interface Update { readonly _: unique symbol } +export const Update: Context.Tag = Context.GenericTag('@grom.js/effect-tg/Bot/Update') export interface Middleware { (self: Bot): Bot diff --git a/src/BotApi.ts b/src/BotApi.ts index 5f34706..e189a1a 100644 --- a/src/BotApi.ts +++ b/src/BotApi.ts @@ -19,8 +19,8 @@ import * as internal from './internal/botApi.ts' export type { MethodParams, MethodResults, Service, Types } -const _BotApi: Context.TagClass = Context.Tag('@grom.js/effect-tg/BotApi')() -export class BotApi extends _BotApi {} +export interface BotApi { readonly _: unique symbol } +export const BotApi: Context.Tag = Context.GenericTag('@grom.js/effect-tg/BotApi') export interface BotApiMethod { (...args: MethodArgs): Effect.Effect< diff --git a/src/BotApiError.ts b/src/BotApiError.ts index 5887a84..25cb291 100644 --- a/src/BotApiError.ts +++ b/src/BotApiError.ts @@ -25,11 +25,11 @@ export type BotApiError = /** * Error caused by the transport when accessing Bot API. */ -const _TransportError: ReturnType> = Data.TaggedError('TransportError') +const TransportError_base: ReturnType> = Data.TaggedError('TransportError') // eslint-disable-next-line ts/no-unsafe-declaration-merging export interface TransportError { readonly [TypeId]: TypeId } // eslint-disable-next-line ts/no-unsafe-declaration-merging -export class TransportError extends _TransportError<{ +export class TransportError extends TransportError_base<{ cause: | HttpClientError.HttpClientError | HttpBody.HttpBodyError @@ -51,11 +51,11 @@ export class TransportError extends _TransportError<{ } Object.defineProperty(TransportError.prototype, TypeId, { value: TypeId }) -const _MethodFailed: ReturnType> = Data.TaggedError('MethodFailed') +const MethodFailed_base: ReturnType> = Data.TaggedError('MethodFailed') // eslint-disable-next-line ts/no-unsafe-declaration-merging export interface MethodFailed { readonly [TypeId]: TypeId } // eslint-disable-next-line ts/no-unsafe-declaration-merging -export class MethodFailed extends _MethodFailed<{ +export class MethodFailed extends MethodFailed_base<{ response: FailureResponse possibleReason: MethodFailureReason }> { @@ -65,11 +65,11 @@ export class MethodFailed extends _MethodFailed<{ } Object.defineProperty(MethodFailed.prototype, TypeId, { value: TypeId }) -const _GroupUpgraded: ReturnType> = Data.TaggedError('GroupUpgraded') +const GroupUpgraded_base: ReturnType> = Data.TaggedError('GroupUpgraded') // eslint-disable-next-line ts/no-unsafe-declaration-merging export interface GroupUpgraded { readonly [TypeId]: TypeId } // eslint-disable-next-line ts/no-unsafe-declaration-merging -export class GroupUpgraded extends _GroupUpgraded<{ +export class GroupUpgraded extends GroupUpgraded_base<{ response: FailureResponse supergroup: Dialog.Supergroup }> { @@ -79,11 +79,11 @@ export class GroupUpgraded extends _GroupUpgraded<{ } Object.defineProperty(GroupUpgraded.prototype, TypeId, { value: TypeId }) -const _RateLimited: ReturnType> = Data.TaggedError('RateLimited') +const RateLimited_base: ReturnType> = Data.TaggedError('RateLimited') // eslint-disable-next-line ts/no-unsafe-declaration-merging export interface RateLimited { readonly [TypeId]: TypeId } // eslint-disable-next-line ts/no-unsafe-declaration-merging -export class RateLimited extends _RateLimited<{ +export class RateLimited extends RateLimited_base<{ response: FailureResponse retryAfter: Duration.Duration }> { @@ -93,11 +93,11 @@ export class RateLimited extends _RateLimited<{ } Object.defineProperty(RateLimited.prototype, TypeId, { value: TypeId }) -const _InternalServerError: ReturnType> = Data.TaggedError('InternalServerError') +const InternalServerError_base: ReturnType> = Data.TaggedError('InternalServerError') // eslint-disable-next-line ts/no-unsafe-declaration-merging export interface InternalServerError { readonly [TypeId]: TypeId } // eslint-disable-next-line ts/no-unsafe-declaration-merging -export class InternalServerError extends _InternalServerError<{ +export class InternalServerError extends InternalServerError_base<{ response: FailureResponse }> {} Object.defineProperty(InternalServerError.prototype, TypeId, { value: TypeId }) diff --git a/src/BotApiTransport.ts b/src/BotApiTransport.ts index 78ba293..53af030 100644 --- a/src/BotApiTransport.ts +++ b/src/BotApiTransport.ts @@ -7,8 +7,8 @@ import * as Layer from 'effect/Layer' import * as BotApiUrl from './BotApiUrl.ts' import * as internal from './internal/botApiTransport.ts' -const _BotApiTransport: Context.TagClass = Context.Tag('@grom.js/effect-tg/BotApiTransport')() -export class BotApiTransport extends _BotApiTransport {} +export interface BotApiTransport { readonly _: unique symbol } +export const BotApiTransport: Context.Tag = Context.GenericTag('@grom.js/effect-tg/BotApiTransport') export interface Service { sendRequest: ( diff --git a/src/BotApiUrl.ts b/src/BotApiUrl.ts index 7aefdc2..9e4d766 100644 --- a/src/BotApiUrl.ts +++ b/src/BotApiUrl.ts @@ -1,7 +1,7 @@ import * as Context from 'effect/Context' -const _BotApiUrl: Context.TagClass = Context.Tag('@grom.js/effect-tg/BotApiUrl')() -export class BotApiUrl extends _BotApiUrl {} +export interface BotApiUrl { readonly _: unique symbol } +export const BotApiUrl: Context.Tag = Context.GenericTag('@grom.js/effect-tg/BotApiUrl') export interface Service { toMethod: (method: string) => URL diff --git a/src/Content.ts b/src/Content.ts index 63526d4..c22dd38 100644 --- a/src/Content.ts +++ b/src/Content.ts @@ -28,197 +28,184 @@ export type Content = | Sticker /** - * Content of a text message. - * * @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_text.html TDLib • td_api.inputMessageText} * @see {@link https://core.telegram.org/bots/api#sendmessage Bot API • sendMessage} */ -const _Text: ReturnType> = Data.TaggedClass('Text') -export class Text extends _Text<{ - text: Text_.Text - linkPreview: Option.Option -}> {} +export interface Text { + readonly _tag: 'Text' + readonly text: Text_.Text + readonly linkPreview: Option.Option +} +export const Text: Data.Case.Constructor = Data.tagged('Text') /** - * Content of a photo message. - * * @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_photo.html TDLib • td_api.inputMessagePhoto} * @see {@link https://core.telegram.org/bots/api#sendphoto Bot API • sendPhoto} */ -const _Photo: ReturnType> = Data.TaggedClass('Photo') -export class Photo extends _Photo<{ - file: File.FileId | File.External | File.InputFile - caption: Option.Option - layout: 'caption-above' | 'caption-below' - spoiler: boolean -}> {} +export interface Photo { + readonly _tag: 'Photo' + readonly file: File.FileId | File.External | File.InputFile + readonly caption: Option.Option + readonly layout: 'caption-above' | 'caption-below' + readonly spoiler: boolean +} +export const Photo: Data.Case.Constructor = Data.tagged('Photo') /** - * Content of an audio message. - * * @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_audio.html TDLib • td_api.inputMessageAudio} * @see {@link https://core.telegram.org/bots/api#sendaudio Bot API • sendAudio} */ -const _Audio: ReturnType> = Data.TaggedClass('Audio') -export class Audio extends _Audio<{ - file: File.FileId | File.External | File.InputFile - caption: Option.Option - duration: Option.Option - performer: Option.Option - title: Option.Option - thumbnail: Option.Option -}> {} +export interface Audio { + readonly _tag: 'Audio' + readonly file: File.FileId | File.External | File.InputFile + readonly caption: Option.Option + readonly duration: Option.Option + readonly performer: Option.Option + readonly title: Option.Option + readonly thumbnail: Option.Option +} +export const Audio: Data.Case.Constructor = Data.tagged