sendi/scripts/navigator-patch.cjs
jmcqueen d0a0dda2b8 Initial commit: Sendi 2.0 SMS gateway (Webex + Twilio)
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>
2026-07-06 11:10:44 -04:00

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);
}