chore: standardize protocol

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-02-14 11:45:34 -05:00
parent 169b7b4c91
commit fefa0a0384
18 changed files with 3090 additions and 1239 deletions

View File

@@ -1,276 +1,61 @@
/**
* Helper functions for working with service identity and location types
* Helper functions for working with service types
*/
import type {
ServiceIdentity,
ServiceIdentityNone,
ServiceIdentityBasic,
ServiceIdentityToken,
ServiceIdentityOAuth,
ServiceIdentityCertificate,
ServiceLocation,
ServiceLocationUri,
ServiceLocationSocketSole,
ServiceLocationSocketSplit,
ServiceLocationFile
} from '@/types/service';
// ==================== Identity Helpers ====================
import type { ServiceListFilterDefinition } from '@/types/service';
import { ListFilterComparisonOperator } from '@/types/common';
/**
* Create a "None" identity (no authentication)
* Parse a filter specification string into its components
*
* @param spec - Filter specification string (e.g., "s:200:256:771")
* @returns Parsed filter specification object
*
* @example
* parseFilterSpec("s:200:256:771")
* // Returns: {
* // type: 'string',
* // length: 200,
* // defaultComparator: 256 (LIKE),
* // supportedComparators: [1, 2, 256, 512] (EQ, NEQ, LIKE, NLIKE)
* // }
*/
export function createIdentityNone(): ServiceIdentityNone {
return { type: 'NA' };
}
/**
* Create a Basic Auth identity
*/
export function createIdentityBasic(identity: string, secret: string): ServiceIdentityBasic {
return {
type: 'BA',
identity,
secret
export function parseFilterSpec(spec: string): ServiceListFilterDefinition {
const [typeCode, lengthStr, defaultComparatorStr, supportedComparatorsStr] = spec.split(':');
const typeMap: Record<string, ServiceListFilterDefinition['type']> = {
's': 'string',
'i': 'integer',
'd': 'date',
'b': 'boolean',
'a': 'array',
};
}
/**
* Create a Token Auth identity
*/
export function createIdentityToken(token: string): ServiceIdentityToken {
return {
type: 'TA',
token
};
}
/**
* Create an OAuth identity
*/
export function createIdentityOAuth(
accessToken: string,
options?: {
accessScope?: string[];
accessExpiry?: number;
refreshToken?: string;
refreshLocation?: string;
const type = typeMap[typeCode];
if (!type) {
throw new Error(`Invalid filter type code: ${typeCode}`);
}
): ServiceIdentityOAuth {
const length = parseInt(lengthStr, 10);
const defaultComparator = parseInt(defaultComparatorStr, 10) as ListFilterComparisonOperator;
// Parse supported comparators from bitmask
const supportedComparators: ListFilterComparisonOperator[] = [];
const supportedBitmask = parseInt(supportedComparatorsStr, 10);
if (supportedBitmask !== 0) {
const allComparators = Object.values(ListFilterComparisonOperator).filter(v => typeof v === 'number') as number[];
for (const comparator of allComparators) {
if ((supportedBitmask & comparator) === comparator) {
supportedComparators.push(comparator as ListFilterComparisonOperator);
}
}
}
return {
type: 'OA',
accessToken,
...options
type,
length,
defaultComparator,
supportedComparators,
};
}
/**
* Create a Certificate identity
*/
export function createIdentityCertificate(
certificate: string,
privateKey: string,
passphrase?: string
): ServiceIdentityCertificate {
return {
type: 'CC',
certificate,
privateKey,
...(passphrase && { passphrase })
};
}
// ==================== Location Helpers ====================
/**
* Create a URI-based location
*/
export function createLocationUri(
host: string,
options?: {
scheme?: 'http' | 'https';
port?: number;
path?: string;
verifyPeer?: boolean;
verifyHost?: boolean;
}
): ServiceLocationUri {
return {
type: 'URI',
scheme: options?.scheme || 'https',
host,
port: options?.port || (options?.scheme === 'http' ? 80 : 443),
...(options?.path && { path: options.path }),
verifyPeer: options?.verifyPeer ?? true,
verifyHost: options?.verifyHost ?? true
};
}
/**
* Create a URI location from a full URL string
*/
export function createLocationFromUrl(url: string): ServiceLocationUri {
try {
const parsed = new URL(url);
return {
type: 'URI',
scheme: parsed.protocol.replace(':', '') as 'http' | 'https',
host: parsed.hostname,
port: parsed.port ? parseInt(parsed.port) : (parsed.protocol === 'https:' ? 443 : 80),
path: parsed.pathname,
verifyPeer: true,
verifyHost: true
};
} catch (error) {
throw new Error(`Invalid URL: ${url}`);
}
}
/**
* Create a single socket location (IMAP, SMTP on same server)
*/
export function createLocationSocketSole(
host: string,
port: number,
encryption: 'none' | 'ssl' | 'tls' | 'starttls' = 'ssl',
options?: {
verifyPeer?: boolean;
verifyHost?: boolean;
}
): ServiceLocationSocketSole {
return {
type: 'SOCKET_SOLE',
host,
port,
encryption,
verifyPeer: options?.verifyPeer ?? true,
verifyHost: options?.verifyHost ?? true
};
}
/**
* Create a split socket location (separate IMAP/SMTP servers)
*/
export function createLocationSocketSplit(
config: {
inboundHost: string;
inboundPort: number;
inboundEncryption?: 'none' | 'ssl' | 'tls' | 'starttls';
outboundHost: string;
outboundPort: number;
outboundEncryption?: 'none' | 'ssl' | 'tls' | 'starttls';
inboundVerifyPeer?: boolean;
inboundVerifyHost?: boolean;
outboundVerifyPeer?: boolean;
outboundVerifyHost?: boolean;
}
): ServiceLocationSocketSplit {
return {
type: 'SOCKET_SPLIT',
inboundHost: config.inboundHost,
inboundPort: config.inboundPort,
inboundEncryption: config.inboundEncryption || 'ssl',
outboundHost: config.outboundHost,
outboundPort: config.outboundPort,
outboundEncryption: config.outboundEncryption || 'ssl',
inboundVerifyPeer: config.inboundVerifyPeer ?? true,
inboundVerifyHost: config.inboundVerifyHost ?? true,
outboundVerifyPeer: config.outboundVerifyPeer ?? true,
outboundVerifyHost: config.outboundVerifyHost ?? true
};
}
/**
* Create a file-based location
*/
export function createLocationFile(path: string): ServiceLocationFile {
return {
type: 'FILE',
path
};
}
// ==================== Validation Helpers ====================
/**
* Validate that an identity object is properly formed
*/
export function validateIdentity(identity: ServiceIdentity): boolean {
switch (identity.type) {
case 'NA':
return true;
case 'BA':
return !!(identity as ServiceIdentityBasic).identity &&
!!(identity as ServiceIdentityBasic).secret;
case 'TA':
return !!(identity as ServiceIdentityToken).token;
case 'OA':
return !!(identity as ServiceIdentityOAuth).accessToken;
case 'CC':
return !!(identity as ServiceIdentityCertificate).certificate &&
!!(identity as ServiceIdentityCertificate).privateKey;
default:
return false;
}
}
/**
* Validate that a location object is properly formed
*/
export function validateLocation(location: ServiceLocation): boolean {
switch (location.type) {
case 'URI':
return !!(location as ServiceLocationUri).host &&
!!(location as ServiceLocationUri).port;
case 'SOCKET_SOLE':
return !!(location as ServiceLocationSocketSole).host &&
!!(location as ServiceLocationSocketSole).port;
case 'SOCKET_SPLIT':
const split = location as ServiceLocationSocketSplit;
return !!split.inboundHost && !!split.inboundPort &&
!!split.outboundHost && !!split.outboundPort;
case 'FILE':
return !!(location as ServiceLocationFile).path;
default:
return false;
}
}
// ==================== Type Guards ====================
export function isIdentityNone(identity: ServiceIdentity): identity is ServiceIdentityNone {
return identity.type === 'NA';
}
export function isIdentityBasic(identity: ServiceIdentity): identity is ServiceIdentityBasic {
return identity.type === 'BA';
}
export function isIdentityToken(identity: ServiceIdentity): identity is ServiceIdentityToken {
return identity.type === 'TA';
}
export function isIdentityOAuth(identity: ServiceIdentity): identity is ServiceIdentityOAuth {
return identity.type === 'OA';
}
export function isIdentityCertificate(identity: ServiceIdentity): identity is ServiceIdentityCertificate {
return identity.type === 'CC';
}
export function isLocationUri(location: ServiceLocation): location is ServiceLocationUri {
return location.type === 'URI';
}
export function isLocationSocketSole(location: ServiceLocation): location is ServiceLocationSocketSole {
return location.type === 'SOCKET_SOLE';
}
export function isLocationSocketSplit(location: ServiceLocation): location is ServiceLocationSocketSplit {
return location.type === 'SOCKET_SPLIT';
}
export function isLocationFile(location: ServiceLocation): location is ServiceLocationFile {
return location.type === 'FILE';
}