@absolutejs/voice 0.0.22-beta.271 → 0.0.22-beta.272

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -160,12 +160,12 @@ export type { VoiceSQLiteRuntimeStorage, VoiceSQLiteStoreOptions } from './sqlit
160
160
  export type { StoredVoiceIntegrationEvent, StoredVoiceExternalObjectMap, StoredVoiceOpsTask, VoiceExternalObjectMap, VoiceExternalObjectMapStore, VoiceOpsTaskAgeBucket, VoiceOpsTaskAnalyticsOptions, VoiceOpsTaskAnalyticsSummary, VoiceOpsTaskAssignmentRule, VoiceOpsTaskAssignmentRuleCondition, VoiceOpsTaskAssignmentRules, VoiceOpsTaskAssigneeAnalytics, VoiceOpsDispositionTaskPolicies, VoiceOpsSLABreachPolicy, VoiceIntegrationDeliveryStatus, VoiceIntegrationEvent, VoiceIntegrationEventStore, VoiceIntegrationSinkDelivery, VoiceIntegrationEventType, VoiceIntegrationWebhookConfig, VoiceOpsTask, VoiceOpsTaskHistoryEntry, VoiceOpsTaskKind, VoiceOpsTaskPolicy, VoiceOpsTaskPriority, VoiceOpsTaskStatus, VoiceOpsTaskStore, VoiceOpsTaskSummary, VoiceOpsTaskWorkerAnalytics } from './ops';
161
161
  export { createTwilioMediaStreamBridge, createTwilioVoiceRoutes, createTwilioVoiceResponse, decodeTwilioMulawBase64, encodeTwilioMulawBase64, transcodePCMToTwilioOutboundPayload, transcodeTwilioInboundPayloadToPCM16 } from './telephony/twilio';
162
162
  export { evaluateVoiceTelephonyContract } from './telephony/contract';
163
- export { createTelnyxMediaStreamBridge, createTelnyxVoiceResponse, createTelnyxVoiceRoutes, verifyVoiceTelnyxWebhookSignature } from './telephony/telnyx';
163
+ export { createMemoryVoiceTelnyxWebhookEventStore, createTelnyxMediaStreamBridge, createTelnyxVoiceResponse, createTelnyxVoiceRoutes, createVoicePostgresTelnyxWebhookEventStore, createVoiceRedisTelnyxWebhookEventStore, createVoiceSQLiteTelnyxWebhookEventStore, createVoiceTelnyxWebhookVerifier, verifyVoiceTelnyxWebhookSignature } from './telephony/telnyx';
164
164
  export { createMemoryVoicePlivoWebhookNonceStore, createPlivoMediaStreamBridge, createPlivoVoiceResponse, createPlivoVoiceRoutes, createVoicePostgresPlivoWebhookNonceStore, createVoicePlivoWebhookVerifier, createVoiceRedisPlivoWebhookNonceStore, createVoiceSQLitePlivoWebhookNonceStore, signVoicePlivoWebhook, verifyVoicePlivoWebhookSignature } from './telephony/plivo';
165
165
  export { createVoiceTelephonyCarrierMatrix, createVoiceTelephonyCarrierMatrixRoutes, renderVoiceTelephonyCarrierMatrixHTML } from './telephony/matrix';
166
166
  export type { TwilioInboundMessage, TwilioMediaStreamBridge, TwilioMediaStreamBridgeOptions, TwilioMediaStreamSocket, TwilioOutboundClearMessage, TwilioOutboundMarkMessage, TwilioOutboundMediaMessage, TwilioOutboundMessage, TwilioVoiceRouteParameters, TwilioVoiceResponseOptions, TwilioVoiceSmokeCheck, TwilioVoiceSmokeOptions, TwilioVoiceSmokeReport, TwilioVoiceSetupOptions, TwilioVoiceSetupStatus, TwilioVoiceRoutesOptions } from './telephony/twilio';
167
167
  export type { VoiceTelephonyContractIssue, VoiceTelephonyContractOptions, VoiceTelephonyContractReport, VoiceTelephonyContractRequirement, VoiceTelephonyProvider, VoiceTelephonySetupStatus, VoiceTelephonySmokeCheck, VoiceTelephonySmokeReport } from './telephony/contract';
168
- export type { TelnyxInboundMessage, TelnyxMediaPayload, TelnyxMediaStreamBridge, TelnyxMediaStreamBridgeOptions, TelnyxMediaStreamSocket, TelnyxOutboundClearMessage, TelnyxOutboundMarkMessage, TelnyxOutboundMediaMessage, TelnyxOutboundMessage, TelnyxVoiceResponseOptions, TelnyxVoiceRoutesOptions, TelnyxVoiceSetupOptions, TelnyxVoiceSetupStatus, TelnyxVoiceSmokeCheck, TelnyxVoiceSmokeOptions, TelnyxVoiceSmokeReport } from './telephony/telnyx';
168
+ export type { TelnyxInboundMessage, TelnyxMediaPayload, TelnyxMediaStreamBridge, TelnyxMediaStreamBridgeOptions, TelnyxMediaStreamSocket, TelnyxOutboundClearMessage, TelnyxOutboundMarkMessage, TelnyxOutboundMediaMessage, TelnyxOutboundMessage, TelnyxVoiceResponseOptions, TelnyxVoiceRoutesOptions, TelnyxVoiceSetupOptions, TelnyxVoiceSetupStatus, TelnyxVoiceSmokeCheck, TelnyxVoiceSmokeOptions, TelnyxVoiceSmokeReport, VoicePostgresTelnyxWebhookEventStoreOptions, VoiceRedisTelnyxWebhookEventClient, VoiceRedisTelnyxWebhookEventStoreOptions, VoiceSQLiteTelnyxWebhookEventStoreOptions, VoiceTelnyxWebhookEventStore, VoiceTelnyxWebhookEventStoreOptions, VoiceTelnyxWebhookVerifierOptions } from './telephony/telnyx';
169
169
  export type { PlivoInboundMessage, PlivoMediaStreamBridge, PlivoMediaStreamBridgeOptions, PlivoMediaStreamSocket, PlivoOutboundCheckpointMessage, PlivoOutboundClearAudioMessage, PlivoOutboundMessage, PlivoOutboundPlayAudioMessage, PlivoVoiceResponseOptions, PlivoVoiceRoutesOptions, PlivoVoiceSetupOptions, PlivoVoiceSetupStatus, PlivoVoiceSmokeCheck, PlivoVoiceSmokeOptions, PlivoVoiceSmokeReport, VoicePostgresPlivoWebhookNonceStoreOptions, VoicePlivoWebhookNonceStore, VoicePlivoWebhookNonceStoreOptions, VoicePlivoWebhookVerifierOptions, VoiceRedisPlivoWebhookNonceClient, VoiceRedisPlivoWebhookNonceStoreOptions, VoiceSQLitePlivoWebhookNonceStoreOptions } from './telephony/plivo';
170
170
  export type { VoiceTelephonyCarrierMatrix, VoiceTelephonyCarrierMatrixEntry, VoiceTelephonyCarrierMatrixInput, VoiceTelephonyCarrierMatrixOptions, VoiceTelephonyCarrierMatrixRoutesOptions, VoiceTelephonyCarrierMatrixStatus } from './telephony/matrix';
171
171
  export { shapeTelephonyAssistantText } from './telephony/response';
package/dist/index.js CHANGED
@@ -19549,6 +19549,7 @@ var createPlivoVoiceRoutes = (options = {}) => {
19549
19549
 
19550
19550
  // src/telephony/telnyx.ts
19551
19551
  import { Buffer as Buffer6 } from "buffer";
19552
+ import { Database as Database2 } from "bun:sqlite";
19552
19553
  import { Elysia as Elysia31 } from "elysia";
19553
19554
  var escapeXml4 = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("'", "&apos;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
19554
19555
  var escapeHtml30 = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("'", "&#39;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
@@ -19721,6 +19722,198 @@ var verifyVoiceTelnyxWebhookSignature = async (input) => {
19721
19722
  return { ok: false, reason: "invalid-signature" };
19722
19723
  }
19723
19724
  };
19725
+ var createMemoryVoiceTelnyxWebhookEventStore = () => {
19726
+ const eventIds = new Set;
19727
+ return {
19728
+ claim: (eventId) => {
19729
+ if (eventIds.has(eventId)) {
19730
+ return false;
19731
+ }
19732
+ eventIds.add(eventId);
19733
+ return true;
19734
+ },
19735
+ has: (eventId) => eventIds.has(eventId),
19736
+ set: (eventId) => {
19737
+ eventIds.add(eventId);
19738
+ }
19739
+ };
19740
+ };
19741
+ var normalizeTelnyxStoreIdentifierSegment = (value) => value.trim().replace(/[^a-zA-Z0-9_]+/g, "_").replace(/^_+|_+$/g, "") || "voice";
19742
+ var quoteTelnyxStoreIdentifier = (value) => `"${value.replace(/"/g, '""')}"`;
19743
+ var resolveTelnyxEventTableName = (input) => {
19744
+ if (input.tableName) {
19745
+ return normalizeTelnyxStoreIdentifierSegment(input.tableName);
19746
+ }
19747
+ return `${normalizeTelnyxStoreIdentifierSegment(input.tablePrefix ?? "voice")}_${normalizeTelnyxStoreIdentifierSegment(input.fallback)}`;
19748
+ };
19749
+ var getTelnyxEventExpiresAt = (ttlSeconds) => typeof ttlSeconds === "number" && ttlSeconds > 0 ? Date.now() + Math.ceil(ttlSeconds * 1000) : null;
19750
+ var createVoiceSQLiteTelnyxWebhookEventStore = (options) => {
19751
+ const database = options.database ?? new Database2(options.path ?? ":memory:", {
19752
+ create: true
19753
+ });
19754
+ const tableName = resolveTelnyxEventTableName({
19755
+ fallback: "telnyx_webhook_events",
19756
+ tableName: options.tableName,
19757
+ tablePrefix: options.tablePrefix
19758
+ });
19759
+ database.exec(`CREATE TABLE IF NOT EXISTS "${tableName}" (
19760
+ event_id TEXT PRIMARY KEY,
19761
+ created_at INTEGER NOT NULL,
19762
+ expires_at INTEGER
19763
+ )`);
19764
+ const pruneExpired = database.query(`DELETE FROM "${tableName}" WHERE expires_at IS NOT NULL AND expires_at <= ?1`);
19765
+ const select = database.query(`SELECT event_id FROM "${tableName}" WHERE event_id = ?1 AND (expires_at IS NULL OR expires_at > ?2) LIMIT 1`);
19766
+ const insert = database.query(`INSERT OR IGNORE INTO "${tableName}" (event_id, created_at, expires_at) VALUES (?1, ?2, ?3)`);
19767
+ const upsert = database.query(`INSERT INTO "${tableName}" (event_id, created_at, expires_at) VALUES (?1, ?2, ?3)
19768
+ ON CONFLICT(event_id) DO UPDATE SET expires_at = excluded.expires_at`);
19769
+ return {
19770
+ claim: (eventId) => {
19771
+ const now = Date.now();
19772
+ pruneExpired.run(now);
19773
+ const result = insert.run(eventId, now, getTelnyxEventExpiresAt(options.ttlSeconds));
19774
+ return result.changes > 0;
19775
+ },
19776
+ has: (eventId) => Boolean(select.get(eventId, Date.now())),
19777
+ set: (eventId) => {
19778
+ upsert.run(eventId, Date.now(), getTelnyxEventExpiresAt(options.ttlSeconds));
19779
+ }
19780
+ };
19781
+ };
19782
+ var createVoiceTelnyxPostgresClient = async (options) => {
19783
+ if (options.sql) {
19784
+ return options.sql;
19785
+ }
19786
+ if (!options.connectionString) {
19787
+ throw new Error("createVoicePostgresTelnyxWebhookEventStore requires either options.sql or options.connectionString.");
19788
+ }
19789
+ const sql = new Bun.SQL(options.connectionString);
19790
+ return {
19791
+ unsafe: sql.unsafe.bind(sql)
19792
+ };
19793
+ };
19794
+ var resolveTelnyxEventQualifiedTableName = (options) => {
19795
+ const schema = normalizeTelnyxStoreIdentifierSegment(options.schemaName ?? "public");
19796
+ const table = resolveTelnyxEventTableName({
19797
+ fallback: "telnyx_webhook_events",
19798
+ tableName: options.tableName,
19799
+ tablePrefix: options.tablePrefix
19800
+ });
19801
+ return `${quoteTelnyxStoreIdentifier(schema)}.${quoteTelnyxStoreIdentifier(table)}`;
19802
+ };
19803
+ var createVoicePostgresTelnyxWebhookEventStore = (options = {}) => {
19804
+ const qualifiedTableName = resolveTelnyxEventQualifiedTableName(options);
19805
+ const schemaMatch = qualifiedTableName.match(/^"([^"]+)"\./);
19806
+ const client = createVoiceTelnyxPostgresClient(options);
19807
+ const initialized = (async () => {
19808
+ const sql = await client;
19809
+ if (schemaMatch?.[1]) {
19810
+ await sql.unsafe(`CREATE SCHEMA IF NOT EXISTS ${quoteTelnyxStoreIdentifier(schemaMatch[1])}`);
19811
+ }
19812
+ await sql.unsafe(`CREATE TABLE IF NOT EXISTS ${qualifiedTableName} (
19813
+ event_id TEXT PRIMARY KEY,
19814
+ created_at BIGINT NOT NULL,
19815
+ expires_at BIGINT
19816
+ )`);
19817
+ })();
19818
+ const pruneExpired = async () => {
19819
+ await initialized;
19820
+ const sql = await client;
19821
+ await sql.unsafe(`DELETE FROM ${qualifiedTableName} WHERE expires_at IS NOT NULL AND expires_at <= $1`, [Date.now()]);
19822
+ };
19823
+ return {
19824
+ claim: async (eventId) => {
19825
+ await pruneExpired();
19826
+ const sql = await client;
19827
+ const rows = await sql.unsafe(`INSERT INTO ${qualifiedTableName} (event_id, created_at, expires_at)
19828
+ VALUES ($1, $2, $3)
19829
+ ON CONFLICT (event_id) DO NOTHING
19830
+ RETURNING event_id`, [eventId, Date.now(), getTelnyxEventExpiresAt(options.ttlSeconds)]);
19831
+ return rows.length > 0;
19832
+ },
19833
+ has: async (eventId) => {
19834
+ await initialized;
19835
+ const sql = await client;
19836
+ const rows = await sql.unsafe(`SELECT event_id FROM ${qualifiedTableName}
19837
+ WHERE event_id = $1 AND (expires_at IS NULL OR expires_at > $2)
19838
+ LIMIT 1`, [eventId, Date.now()]);
19839
+ return rows.length > 0;
19840
+ },
19841
+ set: async (eventId) => {
19842
+ await initialized;
19843
+ const sql = await client;
19844
+ await sql.unsafe(`INSERT INTO ${qualifiedTableName} (event_id, created_at, expires_at)
19845
+ VALUES ($1, $2, $3)
19846
+ ON CONFLICT (event_id) DO UPDATE SET expires_at = EXCLUDED.expires_at`, [eventId, Date.now(), getTelnyxEventExpiresAt(options.ttlSeconds)]);
19847
+ }
19848
+ };
19849
+ };
19850
+ var getTelnyxRedisEventKey = (keyPrefix, eventId) => `${keyPrefix}:${eventId}`;
19851
+ var createVoiceRedisTelnyxWebhookEventStore = (options = {}) => {
19852
+ const client = options.client ?? new Bun.RedisClient(options.url);
19853
+ const keyPrefix = options.keyPrefix?.trim() || "voice:telnyx-webhook-event";
19854
+ const ttlSeconds = options.ttlSeconds;
19855
+ const setEvent = async (eventId, nx) => {
19856
+ const key = getTelnyxRedisEventKey(keyPrefix, eventId);
19857
+ if (typeof ttlSeconds === "number" && ttlSeconds > 0) {
19858
+ return client.set(key, "1", "EX", String(Math.ceil(ttlSeconds)), ...nx ? ["NX"] : []);
19859
+ }
19860
+ return client.set(key, "1", ...nx ? ["NX"] : []);
19861
+ };
19862
+ return {
19863
+ claim: async (eventId) => await setEvent(eventId, true) === "OK",
19864
+ has: async (eventId) => Boolean(await client.exists(getTelnyxRedisEventKey(keyPrefix, eventId))),
19865
+ set: async (eventId) => {
19866
+ await setEvent(eventId, false);
19867
+ }
19868
+ };
19869
+ };
19870
+ var readTelnyxWebhookEventId = (rawBody) => {
19871
+ try {
19872
+ const body = JSON.parse(rawBody);
19873
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
19874
+ return;
19875
+ }
19876
+ const record = body;
19877
+ const data = record.data;
19878
+ if (data && typeof data === "object" && !Array.isArray(data)) {
19879
+ const eventId2 = data.id;
19880
+ if (typeof eventId2 === "string" && eventId2.trim()) {
19881
+ return eventId2;
19882
+ }
19883
+ }
19884
+ const eventId = record.id ?? record.event_id;
19885
+ return typeof eventId === "string" && eventId.trim() ? eventId : undefined;
19886
+ } catch {
19887
+ return;
19888
+ }
19889
+ };
19890
+ var createVoiceTelnyxWebhookVerifier = (options) => async (input) => {
19891
+ const verification = await verifyVoiceTelnyxWebhookSignature({
19892
+ body: input.rawBody,
19893
+ headers: input.headers,
19894
+ publicKey: options.publicKey,
19895
+ toleranceSeconds: options.toleranceSeconds
19896
+ });
19897
+ if (!verification.ok) {
19898
+ return verification;
19899
+ }
19900
+ const eventStore = options.eventStore;
19901
+ if (!eventStore) {
19902
+ return verification;
19903
+ }
19904
+ const eventId = readTelnyxWebhookEventId(input.rawBody);
19905
+ if (!eventId) {
19906
+ return { ok: false, reason: "invalid-signature" };
19907
+ }
19908
+ if (eventStore.claim) {
19909
+ return await eventStore.claim(eventId) ? verification : { ok: false, reason: "invalid-signature" };
19910
+ }
19911
+ if (await eventStore.has(eventId)) {
19912
+ return { ok: false, reason: "invalid-signature" };
19913
+ }
19914
+ await eventStore.set(eventId);
19915
+ return verification;
19916
+ };
19724
19917
  var buildTelnyxVoiceSetupStatus = async (options, input) => {
19725
19918
  const origin = resolveRequestOrigin3(input.request);
19726
19919
  const stream = await resolveTelnyxStreamUrl(options, input);
@@ -19854,11 +20047,10 @@ var createTelnyxVoiceRoutes = (options = {}) => {
19854
20047
  const smokePath = options.smoke?.path === false ? false : options.smoke?.path ?? "/api/voice/telnyx/smoke";
19855
20048
  const bridges = new WeakMap;
19856
20049
  const webhookPolicy = options.webhook?.policy ?? options.outcomePolicy ?? createVoiceTelephonyOutcomePolicy();
19857
- const verify = options.webhook?.verify ?? (options.webhook?.publicKey ? (input) => verifyVoiceTelnyxWebhookSignature({
19858
- body: input.rawBody,
19859
- headers: input.headers,
19860
- publicKey: options.webhook?.publicKey,
19861
- toleranceSeconds: options.webhook?.toleranceSeconds
20050
+ const verify = options.webhook?.verify ?? (options.webhook?.publicKey ? createVoiceTelnyxWebhookVerifier({
20051
+ eventStore: options.webhook.eventStore,
20052
+ publicKey: options.webhook.publicKey,
20053
+ toleranceSeconds: options.webhook.toleranceSeconds
19862
20054
  }) : undefined);
19863
20055
  const app = new Elysia31({
19864
20056
  name: options.name ?? "absolutejs-voice-telnyx"
@@ -23990,7 +24182,7 @@ var createVoiceOpsRecoveryRoutes = (options = {}) => {
23990
24182
 
23991
24183
  // src/observabilityExport.ts
23992
24184
  import { Elysia as Elysia41 } from "elysia";
23993
- import { Database as Database2 } from "bun:sqlite";
24185
+ import { Database as Database3 } from "bun:sqlite";
23994
24186
  import { createHash } from "crypto";
23995
24187
  import { mkdir as mkdir4, readFile as readFile2, stat, unlink } from "fs/promises";
23996
24188
  import { join as join3 } from "path";
@@ -25220,7 +25412,7 @@ var loadVoiceObservabilityExportReplaySource = async (source) => {
25220
25412
  if (!source.database && !source.path) {
25221
25413
  throw new Error("SQLite observability export replay requires source.database or source.path.");
25222
25414
  }
25223
- const database = source.database ?? new Database2(source.path, { create: false });
25415
+ const database = source.database ?? new Database3(source.path, { create: false });
25224
25416
  const table2 = quoteObservabilityIdentifier(normalizeObservabilityIdentifier(source.tableName));
25225
25417
  const row2 = database.query(`SELECT manifest_json, artifact_index_json, payload_json FROM ${table2} WHERE run_id = $runId`).get({ $runId: source.runId });
25226
25418
  if (!row2) {
@@ -25297,7 +25489,7 @@ var deliverObservabilityExportToSQLite = async (input) => {
25297
25489
  if (!input.destination.database && !input.destination.path) {
25298
25490
  throw new Error("SQLite observability export delivery requires destination.database or destination.path.");
25299
25491
  }
25300
- const database = input.destination.database ?? new Database2(input.destination.path, { create: true });
25492
+ const database = input.destination.database ?? new Database3(input.destination.path, { create: true });
25301
25493
  const table = quoteObservabilityIdentifier(normalizeObservabilityIdentifier(input.destination.tableName));
25302
25494
  const record = buildObservabilityExportDatabaseRecord(input);
25303
25495
  database.exec(`CREATE TABLE IF NOT EXISTS ${table} (
@@ -29407,7 +29599,7 @@ var createVoiceTraceDeliveryRoutes = (options) => {
29407
29599
  return routes;
29408
29600
  };
29409
29601
  // src/sqliteStore.ts
29410
- import { Database as Database3 } from "bun:sqlite";
29602
+ import { Database as Database4 } from "bun:sqlite";
29411
29603
  var normalizeTableNameSegment = (value) => value.trim().replace(/[^a-zA-Z0-9_]+/g, "_").replace(/^_+|_+$/g, "") || "voice";
29412
29604
  var resolveTableName = (input) => {
29413
29605
  if (input.options.tableName) {
@@ -29418,7 +29610,7 @@ var resolveTableName = (input) => {
29418
29610
  return `${prefix}_${fallback}`;
29419
29611
  };
29420
29612
  var openVoiceSQLiteDatabase = (path) => {
29421
- const database = new Database3(path, {
29613
+ const database = new Database4(path, {
29422
29614
  create: true
29423
29615
  });
29424
29616
  database.exec("PRAGMA journal_mode = WAL;");
@@ -31780,6 +31972,7 @@ export {
31780
31972
  createVoiceToolContractJSONHandler,
31781
31973
  createVoiceToolContractHTMLHandler,
31782
31974
  createVoiceToolContract,
31975
+ createVoiceTelnyxWebhookVerifier,
31783
31976
  createVoiceTelnyxCampaignDialer,
31784
31977
  createVoiceTelephonyWebhookRoutes,
31785
31978
  createVoiceTelephonyWebhookHandler,
@@ -31803,6 +31996,7 @@ export {
31803
31996
  createVoiceSTTProviderRouter,
31804
31997
  createVoiceSQLiteTraceSinkDeliveryStore,
31805
31998
  createVoiceSQLiteTraceEventStore,
31999
+ createVoiceSQLiteTelnyxWebhookEventStore,
31806
32000
  createVoiceSQLiteTelephonyWebhookIdempotencyStore,
31807
32001
  createVoiceSQLiteTaskStore,
31808
32002
  createVoiceSQLiteSessionStore,
@@ -31820,6 +32014,7 @@ export {
31820
32014
  createVoiceRoutingDecisionSummary,
31821
32015
  createVoiceReviewSavedEvent,
31822
32016
  createVoiceResilienceRoutes,
32017
+ createVoiceRedisTelnyxWebhookEventStore,
31823
32018
  createVoiceRedisTelephonyWebhookIdempotencyStore,
31824
32019
  createVoiceRedisTaskLeaseCoordinator,
31825
32020
  createVoiceRedisPlivoWebhookNonceStore,
@@ -31843,6 +32038,7 @@ export {
31843
32038
  createVoiceProductionReadinessRoutes,
31844
32039
  createVoicePostgresTraceSinkDeliveryStore,
31845
32040
  createVoicePostgresTraceEventStore,
32041
+ createVoicePostgresTelnyxWebhookEventStore,
31846
32042
  createVoicePostgresTelephonyWebhookIdempotencyStore,
31847
32043
  createVoicePostgresTaskStore,
31848
32044
  createVoicePostgresSessionStore,
@@ -31998,6 +32194,7 @@ export {
31998
32194
  createOpenAIVoiceTTS,
31999
32195
  createOpenAIVoiceAssistantModel,
32000
32196
  createOpenAIRealtimeAdapter,
32197
+ createMemoryVoiceTelnyxWebhookEventStore,
32001
32198
  createMemoryVoiceTelephonyWebhookIdempotencyStore,
32002
32199
  createMemoryVoicePlivoWebhookNonceStore,
32003
32200
  createJSONVoiceAssistantModel,
@@ -1,8 +1,11 @@
1
+ import { Database } from 'bun:sqlite';
2
+ import type { RedisClient } from 'bun';
1
3
  import { Elysia } from 'elysia';
2
4
  import { type VoiceTelephonyContractReport, type VoiceTelephonySetupStatus, type VoiceTelephonySmokeCheck, type VoiceTelephonySmokeReport } from './contract';
3
5
  import { type VoiceTelephonyOutcomePolicy, type VoiceTelephonyWebhookRoutesOptions, type VoiceTelephonyWebhookVerificationResult } from '../telephonyOutcome';
4
6
  import type { VoiceServerMessage, VoiceSessionRecord } from '../types';
5
7
  import { type TwilioMediaStreamBridgeOptions } from './twilio';
8
+ import type { VoicePostgresClient } from '../postgresStore';
6
9
  export type TelnyxMediaPayload = {
7
10
  chunk?: string;
8
11
  payload: string;
@@ -151,12 +154,45 @@ export type TelnyxVoiceRoutesOptions<TContext = unknown, TSession extends VoiceS
151
154
  }) => Promise<string> | string);
152
155
  };
153
156
  webhook?: Omit<VoiceTelephonyWebhookRoutesOptions<TContext, TSession, TResult>, 'context' | 'path' | 'policy' | 'provider'> & {
157
+ eventStore?: VoiceTelnyxWebhookEventStore;
154
158
  path?: string;
155
159
  policy?: VoiceTelephonyOutcomePolicy;
156
160
  publicKey?: string;
157
161
  toleranceSeconds?: number;
158
162
  };
159
163
  };
164
+ export type VoiceTelnyxWebhookEventStore = {
165
+ claim?: (eventId: string) => Promise<boolean> | boolean;
166
+ has: (eventId: string) => Promise<boolean> | boolean;
167
+ set: (eventId: string) => Promise<void> | void;
168
+ };
169
+ export type VoiceTelnyxWebhookEventStoreOptions = {
170
+ ttlSeconds?: number;
171
+ };
172
+ export type VoiceSQLiteTelnyxWebhookEventStoreOptions = VoiceTelnyxWebhookEventStoreOptions & {
173
+ database?: Database;
174
+ path?: string;
175
+ tableName?: string;
176
+ tablePrefix?: string;
177
+ };
178
+ export type VoicePostgresTelnyxWebhookEventStoreOptions = VoiceTelnyxWebhookEventStoreOptions & {
179
+ connectionString?: string;
180
+ schemaName?: string;
181
+ sql?: VoicePostgresClient;
182
+ tableName?: string;
183
+ tablePrefix?: string;
184
+ };
185
+ export type VoiceRedisTelnyxWebhookEventClient = Pick<RedisClient, 'exists' | 'set'>;
186
+ export type VoiceRedisTelnyxWebhookEventStoreOptions = VoiceTelnyxWebhookEventStoreOptions & {
187
+ client?: VoiceRedisTelnyxWebhookEventClient;
188
+ keyPrefix?: string;
189
+ url?: string;
190
+ };
191
+ export type VoiceTelnyxWebhookVerifierOptions = {
192
+ eventStore?: VoiceTelnyxWebhookEventStore;
193
+ publicKey?: string;
194
+ toleranceSeconds?: number;
195
+ };
160
196
  export declare const createTelnyxVoiceResponse: (options: TelnyxVoiceResponseOptions) => string;
161
197
  export declare const createTelnyxMediaStreamBridge: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(socket: TelnyxMediaStreamSocket, options: TelnyxMediaStreamBridgeOptions<TContext, TSession, TResult>) => TelnyxMediaStreamBridge;
162
198
  export declare const verifyVoiceTelnyxWebhookSignature: (input: {
@@ -165,6 +201,14 @@ export declare const verifyVoiceTelnyxWebhookSignature: (input: {
165
201
  publicKey?: string;
166
202
  toleranceSeconds?: number;
167
203
  }) => Promise<VoiceTelephonyWebhookVerificationResult>;
204
+ export declare const createMemoryVoiceTelnyxWebhookEventStore: () => VoiceTelnyxWebhookEventStore;
205
+ export declare const createVoiceSQLiteTelnyxWebhookEventStore: (options: VoiceSQLiteTelnyxWebhookEventStoreOptions) => VoiceTelnyxWebhookEventStore;
206
+ export declare const createVoicePostgresTelnyxWebhookEventStore: (options?: VoicePostgresTelnyxWebhookEventStoreOptions) => VoiceTelnyxWebhookEventStore;
207
+ export declare const createVoiceRedisTelnyxWebhookEventStore: (options?: VoiceRedisTelnyxWebhookEventStoreOptions) => VoiceTelnyxWebhookEventStore;
208
+ export declare const createVoiceTelnyxWebhookVerifier: (options: VoiceTelnyxWebhookVerifierOptions) => (input: {
209
+ rawBody: string;
210
+ headers: Headers;
211
+ }) => Promise<VoiceTelephonyWebhookVerificationResult>;
168
212
  export declare const createTelnyxVoiceRoutes: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(options?: TelnyxVoiceRoutesOptions<TContext, TSession, TResult>) => Elysia<"", {
169
213
  decorator: {};
170
214
  store: {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.271",
3
+ "version": "0.0.22-beta.272",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",