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>
34 lines
1.4 KiB
JavaScript
34 lines
1.4 KiB
JavaScript
// scripts/navigator-patch.cjs
|
|
//
|
|
// Workaround for Node 22 + @webex/internal-media-core.
|
|
//
|
|
// The bot framework transitively depends on `@webex/internal-media-core`, whose
|
|
// pre-compiled CJS bundle does `commonjsGlobal.navigator = { ... }` at import time.
|
|
// Node 22+ exposes `globalThis.navigator` as a non-writable property, so that
|
|
// assignment throws:
|
|
//
|
|
// TypeError: Cannot set property navigator of #<Object> which has only a getter
|
|
//
|
|
// `--require` runs synchronously before any ESM resolution, which is the only
|
|
// hook early enough to fix the descriptor before the framework loads.
|
|
//
|
|
// We make the property writable + configurable, preserving any existing value
|
|
// so anything that reads from it later still works.
|
|
'use strict';
|
|
|
|
try {
|
|
const desc = Object.getOwnPropertyDescriptor(globalThis, 'navigator');
|
|
if (desc && desc.writable !== true) {
|
|
const current = 'value' in desc ? desc.value : (desc.get ? desc.get() : undefined);
|
|
Object.defineProperty(globalThis, 'navigator', {
|
|
value: current,
|
|
writable: true,
|
|
configurable: true,
|
|
});
|
|
}
|
|
} catch (err) {
|
|
// If the patch can't apply, log loudly so the bot startup failure is at
|
|
// least traceable; the framework import will then fail with the original error.
|
|
// eslint-disable-next-line no-console
|
|
console.warn('[navigator-patch] failed to make globalThis.navigator writable:', err && err.message);
|
|
}
|