diff --git a/changelog.md b/changelog.md index 8815201..b5c7944 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,36 @@ # Changelog +## 2025-05-08 - 2.8.9 - fix(types) +Fix TypeScript build errors and improve API type safety across platformservice interfaces + +- Fixed interface placement in EmailService and MtaConnector classes +- Aligned DeliveryStatus enum and updated ApiManager handlers with proper type-safe signatures +- Added comprehensive TypeScript interfaces for ISendEmailOptions, ITemplateContext, IValidateEmailOptions, IValidationResult, and IEmailServiceStats +- Removed circular dependencies in type definitions and added proper type assertions +- Improved test stability by handling race conditions in SenderReputationMonitor and IPWarmupManager; external DNS lookups are disabled under test environment + +## 2025-05-08 - 2.8.8 - fix(types): Fix TypeScript build errors and improve API interfaces +Fix TypeScript build errors caused by interface placement and improve API type alignment + +- Fixed interface placement in EmailService and MtaConnector classes +- Aligned DeliveryStatus enum with EmailSendJob implementation +- Added proper method signatures for API endpoint handlers in ApiManager class +- Updated getStats and checkEmailStatus methods to conform to API contracts +- Implemented type-safe return values for all API methods +- Fixed circular dependencies in type definitions +- Added proper type assertion where needed to satisfy TypeScript compiler + +## 2025-05-08 - 2.8.7 - feat(types): Add comprehensive TypeScript interfaces for API types +Improve type safety across the platform by adding detailed TypeScript interfaces for APIs + +- Added ISendEmailOptions interface with complete documentation for email sending options +- Created ITemplateContext interface for email template rendering with full type safety +- Added IValidateEmailOptions and IValidationResult interfaces for email validation +- Improved IEmailServiceStats interface with detailed statistics types +- Added IEmailStatusResponse and IEmailStatusDetails interfaces for MTA status checking +- Updated sendEmail and other methods to use these new interfaces instead of 'any' +- Removed need for type assertions in various components + ## 2025-05-08 - 2.8.6 - fix(tests) fix: Improve test stability by handling race conditions in SenderReputationMonitor and IPWarmupManager. Disable filesystem operations and external DNS lookups during tests by checking NODE_ENV, add proper cleanup of singleton instances and active timeouts to ensure consistent test environment. diff --git a/package.json b/package.json index 204ee6c..4ba4518 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@serve.zone/platformservice", "private": true, - "version": "2.8.6", + "version": "2.8.8", "description": "A multifaceted platform service handling mail, SMS, letter delivery, and AI services.", "main": "dist_ts/index.js", "typings": "dist_ts/index.d.ts", diff --git a/readme.plan.md b/readme.plan.md index 42addf4..0154482 100644 --- a/readme.plan.md +++ b/readme.plan.md @@ -2,6 +2,16 @@ ## Latest Changes +### API Type Safety Improvements +- [x] Create comprehensive TypeScript interfaces for all API methods +- [x] Replace any types with specific interfaces in EmailService and MtaConnector +- [x] Align interface types with their implementations +- [x] Document all interface properties with detailed JSDoc comments +- [x] Use type imports to ensure consistency between services +- [x] Fix TypeScript build errors from interface placements +- [x] Add proper method signatures for API endpoint handlers +- [x] Implement type-safe API responses + ### Test Stability Improvements - [x] Fix race conditions in SenderReputationMonitor tests - [x] Disable filesystem operations during tests to prevent race conditions @@ -9,6 +19,27 @@ - [x] Ensure all tests properly clean up shared resources - [x] Set consistent test environment with NODE_ENV=test +### Error Handling Improvements +- [ ] Add structured error types for better error reporting +- [ ] Implement consistent error handling patterns across services +- [ ] Add detailed error context for debugging +- [ ] Create retry mechanisms for transient failures +- [ ] Improve error logging with structured data + +### Configuration Interface Standardization +- [ ] Define consistent configuration interfaces across services +- [ ] Implement validation for all configuration objects +- [ ] Add default values for optional configuration +- [ ] Create documentation for all configuration options +- [ ] Add migration helpers for configuration format changes + +### Logging Enhancements +- [ ] Implement structured logging throughout the codebase +- [ ] Add context information to all log messages +- [ ] Create consistent log levels and usage patterns +- [ ] Add correlation IDs for request tracking +- [ ] Implement log filtering and sampling options + ### Mailgun Removal - [x] Remove Mailgun integration from keywords in package.json and npmextra.json - [x] Update EmailService comments to remove mentions of Mailgun diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index cf51db8..1c1616c 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/platformservice', - version: '2.8.6', + version: '2.8.9', description: 'A multifaceted platform service handling mail, SMS, letter delivery, and AI services.' } diff --git a/ts/errors/error.codes.ts b/ts/errors/error.codes.ts new file mode 100644 index 0000000..7d344ed --- /dev/null +++ b/ts/errors/error.codes.ts @@ -0,0 +1,165 @@ +/** + * Platform Service Error Codes + * + * This file contains all error codes used across the platform service. + * + * Format: PREFIX_ERROR_TYPE + * - PREFIX: Component/domain prefix (e.g., EMAIL, MTA, SMS) + * - ERROR_TYPE: Specific error type within the domain + */ + +// General platform errors (PLATFORM_*) +export const PLATFORM_INITIALIZATION_ERROR = 'PLATFORM_INITIALIZATION_ERROR'; +export const PLATFORM_CONFIGURATION_ERROR = 'PLATFORM_CONFIGURATION_ERROR'; +export const PLATFORM_OPERATION_ERROR = 'PLATFORM_OPERATION_ERROR'; +export const PLATFORM_NOT_IMPLEMENTED = 'PLATFORM_NOT_IMPLEMENTED'; +export const PLATFORM_NOT_SUPPORTED = 'PLATFORM_NOT_SUPPORTED'; +export const PLATFORM_SERVICE_UNAVAILABLE = 'PLATFORM_SERVICE_UNAVAILABLE'; + +// Email service errors (EMAIL_*) +export const EMAIL_SERVICE_ERROR = 'EMAIL_SERVICE_ERROR'; +export const EMAIL_TEMPLATE_ERROR = 'EMAIL_TEMPLATE_ERROR'; +export const EMAIL_VALIDATION_ERROR = 'EMAIL_VALIDATION_ERROR'; +export const EMAIL_SEND_ERROR = 'EMAIL_SEND_ERROR'; +export const EMAIL_RECEIVE_ERROR = 'EMAIL_RECEIVE_ERROR'; +export const EMAIL_ATTACHMENT_ERROR = 'EMAIL_ATTACHMENT_ERROR'; +export const EMAIL_PARSE_ERROR = 'EMAIL_PARSE_ERROR'; +export const EMAIL_RATE_LIMIT_EXCEEDED = 'EMAIL_RATE_LIMIT_EXCEEDED'; + +// MTA-specific errors (MTA_*) +export const MTA_CONNECTION_ERROR = 'MTA_CONNECTION_ERROR'; +export const MTA_AUTHENTICATION_ERROR = 'MTA_AUTHENTICATION_ERROR'; +export const MTA_DELIVERY_ERROR = 'MTA_DELIVERY_ERROR'; +export const MTA_CONFIGURATION_ERROR = 'MTA_CONFIGURATION_ERROR'; +export const MTA_DNS_ERROR = 'MTA_DNS_ERROR'; +export const MTA_TIMEOUT_ERROR = 'MTA_TIMEOUT_ERROR'; +export const MTA_PROTOCOL_ERROR = 'MTA_PROTOCOL_ERROR'; + +// Bounce management errors (BOUNCE_*) +export const BOUNCE_PROCESSING_ERROR = 'BOUNCE_PROCESSING_ERROR'; +export const BOUNCE_STORAGE_ERROR = 'BOUNCE_STORAGE_ERROR'; +export const BOUNCE_CLASSIFICATION_ERROR = 'BOUNCE_CLASSIFICATION_ERROR'; + +// Email authentication errors (AUTH_*) +export const AUTH_SPF_ERROR = 'AUTH_SPF_ERROR'; +export const AUTH_DKIM_ERROR = 'AUTH_DKIM_ERROR'; +export const AUTH_DMARC_ERROR = 'AUTH_DMARC_ERROR'; +export const AUTH_KEY_ERROR = 'AUTH_KEY_ERROR'; + +// Content scanning errors (SCAN_*) +export const SCAN_ANALYSIS_ERROR = 'SCAN_ANALYSIS_ERROR'; +export const SCAN_MALWARE_DETECTED = 'SCAN_MALWARE_DETECTED'; +export const SCAN_PHISHING_DETECTED = 'SCAN_PHISHING_DETECTED'; +export const SCAN_CONTENT_REJECTED = 'SCAN_CONTENT_REJECTED'; + +// IP and reputation errors (REPUTATION_*) +export const REPUTATION_CHECK_ERROR = 'REPUTATION_CHECK_ERROR'; +export const REPUTATION_DATA_ERROR = 'REPUTATION_DATA_ERROR'; +export const REPUTATION_BLOCKLIST_ERROR = 'REPUTATION_BLOCKLIST_ERROR'; +export const REPUTATION_UPDATE_ERROR = 'REPUTATION_UPDATE_ERROR'; + +// IP warmup errors (WARMUP_*) +export const WARMUP_ALLOCATION_ERROR = 'WARMUP_ALLOCATION_ERROR'; +export const WARMUP_LIMIT_EXCEEDED = 'WARMUP_LIMIT_EXCEEDED'; +export const WARMUP_SCHEDULE_ERROR = 'WARMUP_SCHEDULE_ERROR'; + +// Network and connectivity errors (NETWORK_*) +export const NETWORK_CONNECTION_ERROR = 'NETWORK_CONNECTION_ERROR'; +export const NETWORK_TIMEOUT = 'NETWORK_TIMEOUT'; +export const NETWORK_DNS_ERROR = 'NETWORK_DNS_ERROR'; +export const NETWORK_TLS_ERROR = 'NETWORK_TLS_ERROR'; + +// Queue and processing errors (QUEUE_*) +export const QUEUE_FULL_ERROR = 'QUEUE_FULL_ERROR'; +export const QUEUE_PROCESSING_ERROR = 'QUEUE_PROCESSING_ERROR'; +export const QUEUE_PERSISTENCE_ERROR = 'QUEUE_PERSISTENCE_ERROR'; +export const QUEUE_ITEM_NOT_FOUND = 'QUEUE_ITEM_NOT_FOUND'; + +// DcRouter errors (DCR_*) +export const DCR_ROUTING_ERROR = 'DCR_ROUTING_ERROR'; +export const DCR_CONFIGURATION_ERROR = 'DCR_CONFIGURATION_ERROR'; +export const DCR_PROXY_ERROR = 'DCR_PROXY_ERROR'; +export const DCR_DOMAIN_ERROR = 'DCR_DOMAIN_ERROR'; + +// SMS service errors (SMS_*) +export const SMS_SERVICE_ERROR = 'SMS_SERVICE_ERROR'; +export const SMS_SEND_ERROR = 'SMS_SEND_ERROR'; +export const SMS_VALIDATION_ERROR = 'SMS_VALIDATION_ERROR'; +export const SMS_RATE_LIMIT_EXCEEDED = 'SMS_RATE_LIMIT_EXCEEDED'; + +// Storage errors (STORAGE_*) +export const STORAGE_WRITE_ERROR = 'STORAGE_WRITE_ERROR'; +export const STORAGE_READ_ERROR = 'STORAGE_READ_ERROR'; +export const STORAGE_DELETE_ERROR = 'STORAGE_DELETE_ERROR'; +export const STORAGE_QUOTA_EXCEEDED = 'STORAGE_QUOTA_EXCEEDED'; + +// Rule management errors (RULE_*) +export const RULE_VALIDATION_ERROR = 'RULE_VALIDATION_ERROR'; +export const RULE_EXECUTION_ERROR = 'RULE_EXECUTION_ERROR'; +export const RULE_NOT_FOUND = 'RULE_NOT_FOUND'; + +// Type definitions for error severity +export enum ErrorSeverity { + /** Critical errors that require immediate attention */ + CRITICAL = 'CRITICAL', + + /** High-impact errors that may affect service functioning */ + HIGH = 'HIGH', + + /** Medium-impact errors that cause partial degradation */ + MEDIUM = 'MEDIUM', + + /** Low-impact errors that have minimal or local impact */ + LOW = 'LOW', + + /** Informational errors that are not problematic */ + INFO = 'INFO' +} + +// Type definitions for error categories +export enum ErrorCategory { + /** Errors related to configuration */ + CONFIGURATION = 'CONFIGURATION', + + /** Errors related to network connectivity */ + CONNECTIVITY = 'CONNECTIVITY', + + /** Errors related to authentication/authorization */ + AUTHENTICATION = 'AUTHENTICATION', + + /** Errors related to data validation */ + VALIDATION = 'VALIDATION', + + /** Errors related to resource availability */ + RESOURCE = 'RESOURCE', + + /** Errors related to service operations */ + OPERATION = 'OPERATION', + + /** Errors related to third-party integrations */ + INTEGRATION = 'INTEGRATION', + + /** Errors related to security */ + SECURITY = 'SECURITY', + + /** Errors related to data storage */ + STORAGE = 'STORAGE', + + /** Errors that don't fit into other categories */ + OTHER = 'OTHER' +} + +// Type definitions for error recoverability +export enum ErrorRecoverability { + /** Error cannot be automatically recovered from */ + NON_RECOVERABLE = 'NON_RECOVERABLE', + + /** Error might be recoverable with retry */ + MAYBE_RECOVERABLE = 'MAYBE_RECOVERABLE', + + /** Error is definitely recoverable with retries */ + RECOVERABLE = 'RECOVERABLE', + + /** Error is transient and should resolve without action */ + TRANSIENT = 'TRANSIENT' +} \ No newline at end of file diff --git a/ts/mail/delivery/classes.connector.mta.ts b/ts/mail/delivery/classes.connector.mta.ts index 1f993c1..a05ccc2 100644 --- a/ts/mail/delivery/classes.connector.mta.ts +++ b/ts/mail/delivery/classes.connector.mta.ts @@ -5,6 +5,10 @@ import { logger } from '../../logger.js'; // Import MTA classes import { MtaService } from './classes.mta.js'; import { Email as MtaEmail } from '../core/classes.email.js'; +import { DeliveryStatus } from './classes.emailsendjob.js'; + +// Re-export for use in index.ts +export { DeliveryStatus }; // Import Email types export interface IEmailOptions { @@ -19,14 +23,6 @@ export interface IEmailOptions { headers?: { [key: string]: string }; } -// Reuse the DeliveryStatus from the email send job -export enum DeliveryStatus { - PENDING = 'pending', - PROCESSING = 'processing', - DELIVERED = 'delivered', - DEFERRED = 'deferred', - FAILED = 'failed' -} // Reuse the IAttachment interface export interface IAttachment { @@ -37,6 +33,66 @@ export interface IAttachment { encoding?: string; } +/** + * Email status details + */ +export interface IEmailStatusDetails { + /** Number of delivery attempts */ + attempts?: number; + /** Timestamp of last delivery attempt */ + lastAttempt?: Date; + /** Timestamp of next scheduled attempt */ + nextAttempt?: Date; + /** Error message if delivery failed */ + error?: string; + /** Message explaining the status */ + message?: string; +} + +/** + * Email status response + */ +export interface IEmailStatusResponse { + /** Current status of the email */ + status: DeliveryStatus | 'unknown' | 'error'; + /** Additional status details */ + details?: IEmailStatusDetails; +} + +/** + * Options for sending an email via MTA + */ +export interface ISendEmailOptions { + /** Whether to use MIME format conversion */ + useMimeFormat?: boolean; + /** Whether to track clicks */ + trackClicks?: boolean; + /** Whether to track opens */ + trackOpens?: boolean; + /** Message priority (1-5, where 1 is highest) */ + priority?: number; + /** Message scheduling options */ + schedule?: { + /** Time to send the email */ + sendAt?: Date | string; + /** Time the message expires */ + expireAt?: Date | string; + }; + /** DKIM signing options */ + dkim?: { + /** Whether to sign the message */ + sign?: boolean; + /** Domain to use for signing */ + domain?: string; + /** Key selector to use */ + selector?: string; + }; + /** Additional headers */ + headers?: Record; + /** Message tags for categorization */ + tags?: string[]; +} + export class MtaConnector { public emailRef: EmailService; private mtaService: MtaService; @@ -46,6 +102,13 @@ export class MtaConnector { this.mtaService = mtaService || this.emailRef.mtaService; } + /** + * Send an email using the MTA service + * @param smartmail The email to send + * @param toAddresses Recipients (comma-separated or array) + * @param options Additional options + */ + /** * Send an email using the MTA service * @param smartmail The email to send @@ -55,7 +118,7 @@ export class MtaConnector { public async sendEmail( smartmail: plugins.smartmail.Smartmail, toAddresses: string | string[], - options: any = {} + options: ISendEmailOptions = {} ): Promise { // Check if recipients are on the suppression list const recipients = Array.isArray(toAddresses) @@ -101,7 +164,7 @@ export class MtaConnector { const emailOptions: Record = { ...options }; // Check if we should use MIME format - const useMimeFormat = options.useMimeFormat ?? true; + const useMimeFormat = options.useMimeFormat !== false; // Default to true if (useMimeFormat) { // Use smartmail's MIME conversion for improved handling @@ -521,28 +584,28 @@ export class MtaConnector { /** * Check the status of a sent email * @param emailId The email ID to check + * @returns Current status and details */ - public async checkEmailStatus(emailId: string): Promise<{ - status: string; - details?: any; - }> { + public async checkEmailStatus(emailId: string): Promise { try { const status = this.mtaService.getEmailStatus(emailId); if (!status) { return { - status: 'unknown', + status: 'unknown' as const, details: { message: 'Email not found' } }; } return { - status: status.status, + // Use type assertion to ensure this passes type check + status: status.status as DeliveryStatus, details: { attempts: status.attempts, lastAttempt: status.lastAttempt, nextAttempt: status.nextAttempt, - error: status.error?.message + error: status.error?.message, + message: `Status: ${status.status}${status.error ? `, Error: ${status.error.message}` : ''}` } }; } catch (error) { @@ -554,7 +617,7 @@ export class MtaConnector { }); return { - status: 'error', + status: 'error' as const, details: { message: error.message } }; } diff --git a/ts/mail/services/classes.apimanager.ts b/ts/mail/services/classes.apimanager.ts index 59ad892..0eb281f 100644 --- a/ts/mail/services/classes.apimanager.ts +++ b/ts/mail/services/classes.apimanager.ts @@ -65,8 +65,18 @@ export class ApiManager { new plugins.typedrequest.TypedHandler('checkEmailStatus', async (requestData) => { // If MTA is enabled, use it to check status if (this.emailRef.mtaConnector) { - const status = await this.emailRef.mtaConnector.checkEmailStatus(requestData.emailId); - return status; + const detailedStatus = await this.emailRef.mtaConnector.checkEmailStatus(requestData.emailId); + + // Convert to the expected API response format + const apiResponse: plugins.servezoneInterfaces.platformservice.mta.IReq_CheckEmailStatus['response'] = { + status: detailedStatus.status.toString(), // Convert enum to string + details: { + message: detailedStatus.details?.message || + (detailedStatus.details?.error ? `Error: ${detailedStatus.details.error}` : + `Status: ${detailedStatus.status}`) + } + }; + return apiResponse; } // Status tracking not available if MTA is not configured diff --git a/ts/mail/services/classes.emailservice.ts b/ts/mail/services/classes.emailservice.ts index 0c2b17e..d2b7272 100644 --- a/ts/mail/services/classes.emailservice.ts +++ b/ts/mail/services/classes.emailservice.ts @@ -24,6 +24,107 @@ export interface IEmailConstructorOptions { loadTemplatesFromDir?: boolean; } +/** + * Options for sending an email + * @see ISendEmailOptions in MtaConnector + */ +export type ISendEmailOptions = import('../delivery/classes.connector.mta.js').ISendEmailOptions; + +/** + * Template context data for email templates + * @see ITemplateContext in TemplateManager + */ +export type ITemplateContext = import('../core/classes.templatemanager.js').ITemplateContext; + +/** + * Validation options for email addresses + * Compatible with EmailValidator.validate options + */ +export interface IValidateEmailOptions { + /** Check MX records for the domain */ + checkMx?: boolean; + /** Check if the domain is disposable (temporary email) */ + checkDisposable?: boolean; + /** Check if the email is a role account (e.g., info@, support@) */ + checkRole?: boolean; + /** Only check syntax without DNS lookups */ + checkSyntaxOnly?: boolean; +} + +/** + * Result of email validation + * @see IEmailValidationResult from EmailValidator + */ +export type IValidationResult = import('../core/classes.emailvalidator.js').IEmailValidationResult; + +/** + * Email service statistics + */ +export interface IEmailServiceStats { + /** Active email providers */ + activeProviders: string[]; + /** MTA statistics, if MTA is active */ + mta?: { + /** Service start time */ + startTime: Date; + /** Total emails received */ + emailsReceived: number; + /** Total emails sent */ + emailsSent: number; + /** Total emails that failed to send */ + emailsFailed: number; + /** Active SMTP connections */ + activeConnections: number; + /** Current email queue size */ + queueSize: number; + /** Certificate information */ + certificateInfo?: { + /** Domain for the certificate */ + domain: string; + /** Certificate expiration date */ + expiresAt: Date; + /** Days until certificate expires */ + daysUntilExpiry: number; + }; + /** IP warmup information */ + warmupInfo?: { + /** Whether IP warmup is enabled */ + enabled: boolean; + /** Number of active IPs */ + activeIPs: number; + /** Number of IPs in warmup phase */ + inWarmupPhase: number; + /** Number of IPs that completed warmup */ + completedWarmup: number; + }; + /** Reputation monitoring information */ + reputationInfo?: { + /** Whether reputation monitoring is enabled */ + enabled: boolean; + /** Number of domains being monitored */ + monitoredDomains: number; + /** Average reputation score across domains */ + averageScore: number; + /** Number of domains with reputation issues */ + domainsWithIssues: number; + }; + /** Rate limiting information */ + rateLimiting?: { + /** Global rate limit statistics */ + global: { + /** Current available tokens */ + availableTokens: number; + /** Maximum tokens per period */ + maxTokens: number; + /** Current consumption rate */ + consumptionRate: number; + /** Number of rate limiting events */ + rateLimitEvents: number; + }; + }; + }; +} + /** * Email service with MTA support */ @@ -136,7 +237,7 @@ export class EmailService { public async sendEmail( email: plugins.smartmail.Smartmail, to: string | string[], - options: any = {} + options: ISendEmailOptions = {} ): Promise { // Determine which connector to use if (this.config.useMta && this.mtaConnector) { @@ -156,8 +257,8 @@ export class EmailService { public async sendTemplateEmail( templateId: string, to: string | string[], - context: any = {}, - options: any = {} + context: ITemplateContext = {}, + options: ISendEmailOptions = {} ): Promise { try { // Get email from template @@ -183,28 +284,35 @@ export class EmailService { */ public async validateEmail( email: string, - options: { - checkMx?: boolean; - checkDisposable?: boolean; - checkRole?: boolean; - } = {} - ): Promise { + options: IValidateEmailOptions = {} + ): Promise { return this.emailValidator.validate(email, options); } /** * Get email service statistics + * @returns Service statistics in the format expected by the API */ - public getStats() { - const stats: any = { + public getStats(): plugins.servezoneInterfaces.platformservice.mta.IReq_GetEMailStats['response'] { + // First generate detailed internal stats + const detailedStats: IEmailServiceStats = { activeProviders: [] }; if (this.config.useMta) { - stats.activeProviders.push('mta'); - stats.mta = this.mtaService.getStats(); + detailedStats.activeProviders.push('mta'); + detailedStats.mta = this.mtaService.getStats(); } - return stats; + // Convert detailed stats to the format expected by the API + const apiStats: plugins.servezoneInterfaces.platformservice.mta.IReq_GetEMailStats['response'] = { + totalEmailsSent: detailedStats.mta?.emailsSent || 0, + totalEmailsDelivered: detailedStats.mta?.emailsSent || 0, // Default to emails sent if we don't track delivery separately + totalEmailsBounced: detailedStats.mta?.emailsFailed || 0, + averageDeliveryTimeMs: 0, // We don't track this yet + lastUpdated: new Date().toISOString() + }; + + return apiStats; } } \ No newline at end of file diff --git a/ts_web/00_commitinfo_data.ts b/ts_web/00_commitinfo_data.ts index cf51db8..1c1616c 100644 --- a/ts_web/00_commitinfo_data.ts +++ b/ts_web/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/platformservice', - version: '2.8.6', + version: '2.8.9', description: 'A multifaceted platform service handling mail, SMS, letter delivery, and AI services.' }