Sendi is a Webex to Twilio SMS gateway written in TypeScript that runs directly on Node 22.18+ via native type-stripping (no bundler/transpiler). Each Webex space represents one external SMS contact; inbound MMS is proxied both ways, with delivery-status cards for outbound sends. Highlights: - Express 5 HTTP surface for Twilio /sms and /callback (signature-validated) - webex-node-bot-framework in websocket transport mode for bot control plane - Prisma 6 + SQLite via better-sqlite3 driver adapter - Zod-validated env with fail-fast startup and pino secret redaction - Pino logging: pretty in dev, structured JSON in prod - 13 vitest unit tests (phone normalization, Twilio signature validation, card action dispatch) - Native --require preload patches Node 22's read-only globalThis.navigator so @webex/internal-media-core (transitive) can load Co-authored-by: Cursor <cursoragent@cursor.com>
51 lines
1.7 KiB
TypeScript
51 lines
1.7 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { toE164, isE164, formatForDisplay } from '../../src/lib/phone.ts';
|
|
|
|
describe('toE164', () => {
|
|
it('normalizes 10-digit NANP', () => {
|
|
expect(toE164('289-206-7080')).toBe('+12892067080');
|
|
expect(toE164('(289) 206-7080')).toBe('+12892067080');
|
|
expect(toE164('289.206.7080')).toBe('+12892067080');
|
|
expect(toE164('2892067080')).toBe('+12892067080');
|
|
});
|
|
|
|
it('normalizes 11-digit NANP starting with 1', () => {
|
|
expect(toE164('12892067080')).toBe('+12892067080');
|
|
expect(toE164('1-289-206-7080')).toBe('+12892067080');
|
|
});
|
|
|
|
it('preserves +-prefixed E.164', () => {
|
|
expect(toE164('+442071838750')).toBe('+442071838750');
|
|
expect(toE164('+1 289 206 7080')).toBe('+12892067080');
|
|
});
|
|
|
|
it('throws on inputs that cannot be normalized', () => {
|
|
expect(() => toE164('abc')).toThrow();
|
|
expect(() => toE164('123')).toThrow();
|
|
expect(() => toE164('')).toThrow();
|
|
});
|
|
});
|
|
|
|
describe('isE164', () => {
|
|
it('recognizes valid E.164', () => {
|
|
expect(isE164('+12892067080')).toBe(true);
|
|
expect(isE164('+442071838750')).toBe(true);
|
|
});
|
|
|
|
it('rejects invalid inputs', () => {
|
|
expect(isE164('2892067080')).toBe(false);
|
|
expect(isE164('+0123456789')).toBe(false);
|
|
expect(isE164(undefined)).toBe(false);
|
|
expect(isE164(12345)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('formatForDisplay', () => {
|
|
it('formats NANP numbers', () => {
|
|
expect(formatForDisplay('+12892067080')).toBe('(289) 206-7080');
|
|
});
|
|
|
|
it('returns non-NANP numbers unchanged', () => {
|
|
expect(formatForDisplay('+442071838750')).toBe('+442071838750');
|
|
});
|
|
});
|