refactor(core): extract shared dedup helpers

This commit is contained in:
Peter Steinberger
2026-03-07 10:40:49 +00:00
parent 14c61bb33f
commit 3c71e2bd48
114 changed files with 3400 additions and 2040 deletions

View File

@@ -1,3 +1,5 @@
import { isAllowedParsedChatSender } from "../plugin-sdk/allow-from.js";
export type ServicePrefix<TService extends string> = { prefix: string; service: TService };
export type ChatTargetPrefixesParams = {
@@ -13,10 +15,24 @@ export type ParsedChatTarget =
| { kind: "chat_guid"; chatGuid: string }
| { kind: "chat_identifier"; chatIdentifier: string };
export type ParsedChatAllowTarget = ParsedChatTarget | { kind: "handle"; handle: string };
export type ChatSenderAllowParams = {
allowFrom: Array<string | number>;
sender: string;
chatId?: number | null;
chatGuid?: string | null;
chatIdentifier?: string | null;
};
function stripPrefix(value: string, prefix: string): string {
return value.slice(prefix.length).trim();
}
function startsWithAnyPrefix(value: string, prefixes: readonly string[]): boolean {
return prefixes.some((prefix) => value.startsWith(prefix));
}
export function resolveServicePrefixedTarget<TService extends string, TTarget>(params: {
trimmed: string;
lower: string;
@@ -41,6 +57,31 @@ export function resolveServicePrefixedTarget<TService extends string, TTarget>(p
return null;
}
export function resolveServicePrefixedChatTarget<TService extends string, TTarget>(params: {
trimmed: string;
lower: string;
servicePrefixes: Array<ServicePrefix<TService>>;
chatIdPrefixes: string[];
chatGuidPrefixes: string[];
chatIdentifierPrefixes: string[];
extraChatPrefixes?: string[];
parseTarget: (remainder: string) => TTarget;
}): ({ kind: "handle"; to: string; service: TService } | TTarget) | null {
const chatPrefixes = [
...params.chatIdPrefixes,
...params.chatGuidPrefixes,
...params.chatIdentifierPrefixes,
...(params.extraChatPrefixes ?? []),
];
return resolveServicePrefixedTarget({
trimmed: params.trimmed,
lower: params.lower,
servicePrefixes: params.servicePrefixes,
isChatTarget: (remainderLower) => startsWithAnyPrefix(remainderLower, chatPrefixes),
parseTarget: params.parseTarget,
});
}
export function parseChatTargetPrefixesOrThrow(
params: ChatTargetPrefixesParams,
): ParsedChatTarget | null {
@@ -97,6 +138,56 @@ export function resolveServicePrefixedAllowTarget<TAllowTarget>(params: {
return null;
}
export function resolveServicePrefixedOrChatAllowTarget<
TAllowTarget extends ParsedChatAllowTarget,
>(params: {
trimmed: string;
lower: string;
servicePrefixes: Array<{ prefix: string }>;
parseAllowTarget: (remainder: string) => TAllowTarget;
chatIdPrefixes: string[];
chatGuidPrefixes: string[];
chatIdentifierPrefixes: string[];
}): TAllowTarget | null {
const servicePrefixed = resolveServicePrefixedAllowTarget({
trimmed: params.trimmed,
lower: params.lower,
servicePrefixes: params.servicePrefixes,
parseAllowTarget: params.parseAllowTarget,
});
if (servicePrefixed) {
return servicePrefixed as TAllowTarget;
}
const chatTarget = parseChatAllowTargetPrefixes({
trimmed: params.trimmed,
lower: params.lower,
chatIdPrefixes: params.chatIdPrefixes,
chatGuidPrefixes: params.chatGuidPrefixes,
chatIdentifierPrefixes: params.chatIdentifierPrefixes,
});
if (chatTarget) {
return chatTarget as TAllowTarget;
}
return null;
}
export function createAllowedChatSenderMatcher<TParsed extends ParsedChatAllowTarget>(params: {
normalizeSender: (sender: string) => string;
parseAllowTarget: (entry: string) => TParsed;
}): (input: ChatSenderAllowParams) => boolean {
return (input) =>
isAllowedParsedChatSender({
allowFrom: input.allowFrom,
sender: input.sender,
chatId: input.chatId,
chatGuid: input.chatGuid,
chatIdentifier: input.chatIdentifier,
normalizeSender: params.normalizeSender,
parseAllowTarget: params.parseAllowTarget,
});
}
export function parseChatAllowTargetPrefixes(
params: ChatTargetPrefixesParams,
): ParsedChatTarget | null {

View File

@@ -1,11 +1,11 @@
import { isAllowedParsedChatSender } from "../plugin-sdk/allow-from.js";
import { normalizeE164 } from "../utils.js";
import {
createAllowedChatSenderMatcher,
type ChatSenderAllowParams,
type ParsedChatTarget,
parseChatAllowTargetPrefixes,
parseChatTargetPrefixesOrThrow,
resolveServicePrefixedAllowTarget,
resolveServicePrefixedTarget,
resolveServicePrefixedChatTarget,
resolveServicePrefixedOrChatAllowTarget,
} from "./target-parsing-helpers.js";
export type IMessageService = "imessage" | "sms" | "auto";
@@ -80,14 +80,13 @@ export function parseIMessageTarget(raw: string): IMessageTarget {
}
const lower = trimmed.toLowerCase();
const servicePrefixed = resolveServicePrefixedTarget({
const servicePrefixed = resolveServicePrefixedChatTarget({
trimmed,
lower,
servicePrefixes: SERVICE_PREFIXES,
isChatTarget: (remainderLower) =>
CHAT_ID_PREFIXES.some((p) => remainderLower.startsWith(p)) ||
CHAT_GUID_PREFIXES.some((p) => remainderLower.startsWith(p)) ||
CHAT_IDENTIFIER_PREFIXES.some((p) => remainderLower.startsWith(p)),
chatIdPrefixes: CHAT_ID_PREFIXES,
chatGuidPrefixes: CHAT_GUID_PREFIXES,
chatIdentifierPrefixes: CHAT_IDENTIFIER_PREFIXES,
parseTarget: parseIMessageTarget,
});
if (servicePrefixed) {
@@ -115,46 +114,29 @@ export function parseIMessageAllowTarget(raw: string): IMessageAllowTarget {
}
const lower = trimmed.toLowerCase();
const servicePrefixed = resolveServicePrefixedAllowTarget({
const servicePrefixed = resolveServicePrefixedOrChatAllowTarget({
trimmed,
lower,
servicePrefixes: SERVICE_PREFIXES,
parseAllowTarget: parseIMessageAllowTarget,
chatIdPrefixes: CHAT_ID_PREFIXES,
chatGuidPrefixes: CHAT_GUID_PREFIXES,
chatIdentifierPrefixes: CHAT_IDENTIFIER_PREFIXES,
});
if (servicePrefixed) {
return servicePrefixed;
}
const chatTarget = parseChatAllowTargetPrefixes({
trimmed,
lower,
chatIdPrefixes: CHAT_ID_PREFIXES,
chatGuidPrefixes: CHAT_GUID_PREFIXES,
chatIdentifierPrefixes: CHAT_IDENTIFIER_PREFIXES,
});
if (chatTarget) {
return chatTarget;
}
return { kind: "handle", handle: normalizeIMessageHandle(trimmed) };
}
export function isAllowedIMessageSender(params: {
allowFrom: Array<string | number>;
sender: string;
chatId?: number | null;
chatGuid?: string | null;
chatIdentifier?: string | null;
}): boolean {
return isAllowedParsedChatSender({
allowFrom: params.allowFrom,
sender: params.sender,
chatId: params.chatId,
chatGuid: params.chatGuid,
chatIdentifier: params.chatIdentifier,
normalizeSender: normalizeIMessageHandle,
parseAllowTarget: parseIMessageAllowTarget,
});
const isAllowedIMessageSenderMatcher = createAllowedChatSenderMatcher({
normalizeSender: normalizeIMessageHandle,
parseAllowTarget: parseIMessageAllowTarget,
});
export function isAllowedIMessageSender(params: ChatSenderAllowParams): boolean {
return isAllowedIMessageSenderMatcher(params);
}
export function formatIMessageChatTarget(chatId?: number | null): string {