Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Haze <hazeone@users.noreply.github.com>
53 lines
1.9 KiB
TypeScript
53 lines
1.9 KiB
TypeScript
const BLOCKED_OBJECT_KEYS = new Set(['__proto__', 'prototype', 'constructor']);
|
|
const VALID_ID_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
|
|
const INVALID_CHARS_RE = /[^a-z0-9_-]+/g;
|
|
const LEADING_DASH_RE = /^-+/;
|
|
const TRAILING_DASH_RE = /-+$/;
|
|
|
|
export const UI_WECHAT_CHANNEL_TYPE = 'wechat';
|
|
export const OPENCLAW_WECHAT_CHANNEL_TYPE = 'openclaw-weixin';
|
|
|
|
export type QrChannelEvent = 'qr' | 'success' | 'error';
|
|
|
|
export function toOpenClawChannelType(channelType: string): string {
|
|
return channelType === UI_WECHAT_CHANNEL_TYPE ? OPENCLAW_WECHAT_CHANNEL_TYPE : channelType;
|
|
}
|
|
|
|
export function toUiChannelType(channelType: string): string {
|
|
return channelType === OPENCLAW_WECHAT_CHANNEL_TYPE ? UI_WECHAT_CHANNEL_TYPE : channelType;
|
|
}
|
|
|
|
export function isWechatChannelType(channelType: string | null | undefined): boolean {
|
|
return channelType === UI_WECHAT_CHANNEL_TYPE || channelType === OPENCLAW_WECHAT_CHANNEL_TYPE;
|
|
}
|
|
|
|
export function buildQrChannelEventName(channelType: string, event: QrChannelEvent): string {
|
|
return `channel:${toUiChannelType(channelType)}-${event}`;
|
|
}
|
|
|
|
function canonicalizeAccountId(value: string): string {
|
|
if (VALID_ID_RE.test(value)) return value.toLowerCase();
|
|
return value
|
|
.toLowerCase()
|
|
.replace(INVALID_CHARS_RE, '-')
|
|
.replace(LEADING_DASH_RE, '')
|
|
.replace(TRAILING_DASH_RE, '')
|
|
.slice(0, 64);
|
|
}
|
|
|
|
export function normalizeOpenClawAccountId(value: string | null | undefined, fallback = 'default'): string {
|
|
const trimmed = (value ?? '').trim();
|
|
if (!trimmed) return fallback;
|
|
const normalized = canonicalizeAccountId(trimmed);
|
|
if (!normalized || BLOCKED_OBJECT_KEYS.has(normalized)) {
|
|
return fallback;
|
|
}
|
|
return normalized;
|
|
}
|
|
|
|
export function isCanonicalOpenClawAccountId(value: string | null | undefined): boolean {
|
|
const trimmed = (value ?? '').trim();
|
|
if (!trimmed) return false;
|
|
return normalizeOpenClawAccountId(trimmed, '') === trimmed;
|
|
}
|