@absolutejs/voice 0.0.22-beta.270 → 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 +4 -4
- package/dist/index.js +357 -11
- package/dist/telephony/plivo.d.ts +29 -0
- package/dist/telephony/telnyx.d.ts +44 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -160,13 +160,13 @@ 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';
|
|
164
|
-
export { createMemoryVoicePlivoWebhookNonceStore, createPlivoMediaStreamBridge, createPlivoVoiceResponse, createPlivoVoiceRoutes, createVoicePlivoWebhookVerifier, signVoicePlivoWebhook, verifyVoicePlivoWebhookSignature } from './telephony/plivo';
|
|
163
|
+
export { createMemoryVoiceTelnyxWebhookEventStore, createTelnyxMediaStreamBridge, createTelnyxVoiceResponse, createTelnyxVoiceRoutes, createVoicePostgresTelnyxWebhookEventStore, createVoiceRedisTelnyxWebhookEventStore, createVoiceSQLiteTelnyxWebhookEventStore, createVoiceTelnyxWebhookVerifier, verifyVoiceTelnyxWebhookSignature } from './telephony/telnyx';
|
|
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';
|
|
169
|
-
export type { PlivoInboundMessage, PlivoMediaStreamBridge, PlivoMediaStreamBridgeOptions, PlivoMediaStreamSocket, PlivoOutboundCheckpointMessage, PlivoOutboundClearAudioMessage, PlivoOutboundMessage, PlivoOutboundPlayAudioMessage, PlivoVoiceResponseOptions, PlivoVoiceRoutesOptions, PlivoVoiceSetupOptions, PlivoVoiceSetupStatus, PlivoVoiceSmokeCheck, PlivoVoiceSmokeOptions, PlivoVoiceSmokeReport, VoicePlivoWebhookNonceStore, VoicePlivoWebhookVerifierOptions } from './telephony/plivo';
|
|
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
|
+
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';
|
|
172
172
|
export type { TelephonyResponseShapeMode, TelephonyResponseShapeOptions } from './telephony/response';
|
package/dist/index.js
CHANGED
|
@@ -18109,6 +18109,7 @@ import { Elysia as Elysia34 } from "elysia";
|
|
|
18109
18109
|
|
|
18110
18110
|
// src/telephony/plivo.ts
|
|
18111
18111
|
import { Buffer as Buffer5 } from "buffer";
|
|
18112
|
+
import { Database } from "bun:sqlite";
|
|
18112
18113
|
import { Elysia as Elysia30 } from "elysia";
|
|
18113
18114
|
|
|
18114
18115
|
// src/telephony/contract.ts
|
|
@@ -19125,12 +19126,148 @@ var verifyVoicePlivoWebhookSignature = async (input) => {
|
|
|
19125
19126
|
var createMemoryVoicePlivoWebhookNonceStore = () => {
|
|
19126
19127
|
const nonces = new Set;
|
|
19127
19128
|
return {
|
|
19129
|
+
claim: (nonce) => {
|
|
19130
|
+
if (nonces.has(nonce)) {
|
|
19131
|
+
return false;
|
|
19132
|
+
}
|
|
19133
|
+
nonces.add(nonce);
|
|
19134
|
+
return true;
|
|
19135
|
+
},
|
|
19128
19136
|
has: (nonce) => nonces.has(nonce),
|
|
19129
19137
|
set: (nonce) => {
|
|
19130
19138
|
nonces.add(nonce);
|
|
19131
19139
|
}
|
|
19132
19140
|
};
|
|
19133
19141
|
};
|
|
19142
|
+
var normalizePlivoStoreIdentifierSegment = (value) => value.trim().replace(/[^a-zA-Z0-9_]+/g, "_").replace(/^_+|_+$/g, "") || "voice";
|
|
19143
|
+
var quotePlivoStoreIdentifier = (value) => `"${value.replace(/"/g, '""')}"`;
|
|
19144
|
+
var resolvePlivoNonceTableName = (input) => {
|
|
19145
|
+
if (input.tableName) {
|
|
19146
|
+
return normalizePlivoStoreIdentifierSegment(input.tableName);
|
|
19147
|
+
}
|
|
19148
|
+
return `${normalizePlivoStoreIdentifierSegment(input.tablePrefix ?? "voice")}_${normalizePlivoStoreIdentifierSegment(input.fallback)}`;
|
|
19149
|
+
};
|
|
19150
|
+
var getPlivoNonceExpiresAt = (ttlSeconds) => typeof ttlSeconds === "number" && ttlSeconds > 0 ? Date.now() + Math.ceil(ttlSeconds * 1000) : null;
|
|
19151
|
+
var createVoiceSQLitePlivoWebhookNonceStore = (options) => {
|
|
19152
|
+
const database = options.database ?? new Database(options.path ?? ":memory:", {
|
|
19153
|
+
create: true
|
|
19154
|
+
});
|
|
19155
|
+
const tableName = resolvePlivoNonceTableName({
|
|
19156
|
+
fallback: "plivo_webhook_nonces",
|
|
19157
|
+
tableName: options.tableName,
|
|
19158
|
+
tablePrefix: options.tablePrefix
|
|
19159
|
+
});
|
|
19160
|
+
database.exec(`CREATE TABLE IF NOT EXISTS "${tableName}" (
|
|
19161
|
+
nonce TEXT PRIMARY KEY,
|
|
19162
|
+
created_at INTEGER NOT NULL,
|
|
19163
|
+
expires_at INTEGER
|
|
19164
|
+
)`);
|
|
19165
|
+
const pruneExpired = database.query(`DELETE FROM "${tableName}" WHERE expires_at IS NOT NULL AND expires_at <= ?1`);
|
|
19166
|
+
const select = database.query(`SELECT nonce FROM "${tableName}" WHERE nonce = ?1 AND (expires_at IS NULL OR expires_at > ?2) LIMIT 1`);
|
|
19167
|
+
const insert = database.query(`INSERT OR IGNORE INTO "${tableName}" (nonce, created_at, expires_at) VALUES (?1, ?2, ?3)`);
|
|
19168
|
+
const upsert = database.query(`INSERT INTO "${tableName}" (nonce, created_at, expires_at) VALUES (?1, ?2, ?3)
|
|
19169
|
+
ON CONFLICT(nonce) DO UPDATE SET expires_at = excluded.expires_at`);
|
|
19170
|
+
return {
|
|
19171
|
+
claim: (nonce) => {
|
|
19172
|
+
const now = Date.now();
|
|
19173
|
+
pruneExpired.run(now);
|
|
19174
|
+
const result = insert.run(nonce, now, getPlivoNonceExpiresAt(options.ttlSeconds));
|
|
19175
|
+
return result.changes > 0;
|
|
19176
|
+
},
|
|
19177
|
+
has: (nonce) => Boolean(select.get(nonce, Date.now())),
|
|
19178
|
+
set: (nonce) => {
|
|
19179
|
+
upsert.run(nonce, Date.now(), getPlivoNonceExpiresAt(options.ttlSeconds));
|
|
19180
|
+
}
|
|
19181
|
+
};
|
|
19182
|
+
};
|
|
19183
|
+
var createVoicePlivoPostgresClient = async (options) => {
|
|
19184
|
+
if (options.sql) {
|
|
19185
|
+
return options.sql;
|
|
19186
|
+
}
|
|
19187
|
+
if (!options.connectionString) {
|
|
19188
|
+
throw new Error("createVoicePostgresPlivoWebhookNonceStore requires either options.sql or options.connectionString.");
|
|
19189
|
+
}
|
|
19190
|
+
const sql = new Bun.SQL(options.connectionString);
|
|
19191
|
+
return {
|
|
19192
|
+
unsafe: sql.unsafe.bind(sql)
|
|
19193
|
+
};
|
|
19194
|
+
};
|
|
19195
|
+
var resolvePlivoNonceQualifiedTableName = (options) => {
|
|
19196
|
+
const schema = normalizePlivoStoreIdentifierSegment(options.schemaName ?? "public");
|
|
19197
|
+
const table = resolvePlivoNonceTableName({
|
|
19198
|
+
fallback: "plivo_webhook_nonces",
|
|
19199
|
+
tableName: options.tableName,
|
|
19200
|
+
tablePrefix: options.tablePrefix
|
|
19201
|
+
});
|
|
19202
|
+
return `${quotePlivoStoreIdentifier(schema)}.${quotePlivoStoreIdentifier(table)}`;
|
|
19203
|
+
};
|
|
19204
|
+
var createVoicePostgresPlivoWebhookNonceStore = (options = {}) => {
|
|
19205
|
+
const qualifiedTableName = resolvePlivoNonceQualifiedTableName(options);
|
|
19206
|
+
const schemaMatch = qualifiedTableName.match(/^"([^"]+)"\./);
|
|
19207
|
+
const client = createVoicePlivoPostgresClient(options);
|
|
19208
|
+
const initialized = (async () => {
|
|
19209
|
+
const sql = await client;
|
|
19210
|
+
if (schemaMatch?.[1]) {
|
|
19211
|
+
await sql.unsafe(`CREATE SCHEMA IF NOT EXISTS ${quotePlivoStoreIdentifier(schemaMatch[1])}`);
|
|
19212
|
+
}
|
|
19213
|
+
await sql.unsafe(`CREATE TABLE IF NOT EXISTS ${qualifiedTableName} (
|
|
19214
|
+
nonce TEXT PRIMARY KEY,
|
|
19215
|
+
created_at BIGINT NOT NULL,
|
|
19216
|
+
expires_at BIGINT
|
|
19217
|
+
)`);
|
|
19218
|
+
})();
|
|
19219
|
+
const pruneExpired = async () => {
|
|
19220
|
+
await initialized;
|
|
19221
|
+
const sql = await client;
|
|
19222
|
+
await sql.unsafe(`DELETE FROM ${qualifiedTableName} WHERE expires_at IS NOT NULL AND expires_at <= $1`, [Date.now()]);
|
|
19223
|
+
};
|
|
19224
|
+
return {
|
|
19225
|
+
claim: async (nonce) => {
|
|
19226
|
+
await pruneExpired();
|
|
19227
|
+
const sql = await client;
|
|
19228
|
+
const rows = await sql.unsafe(`INSERT INTO ${qualifiedTableName} (nonce, created_at, expires_at)
|
|
19229
|
+
VALUES ($1, $2, $3)
|
|
19230
|
+
ON CONFLICT (nonce) DO NOTHING
|
|
19231
|
+
RETURNING nonce`, [nonce, Date.now(), getPlivoNonceExpiresAt(options.ttlSeconds)]);
|
|
19232
|
+
return rows.length > 0;
|
|
19233
|
+
},
|
|
19234
|
+
has: async (nonce) => {
|
|
19235
|
+
await initialized;
|
|
19236
|
+
const sql = await client;
|
|
19237
|
+
const rows = await sql.unsafe(`SELECT nonce FROM ${qualifiedTableName}
|
|
19238
|
+
WHERE nonce = $1 AND (expires_at IS NULL OR expires_at > $2)
|
|
19239
|
+
LIMIT 1`, [nonce, Date.now()]);
|
|
19240
|
+
return rows.length > 0;
|
|
19241
|
+
},
|
|
19242
|
+
set: async (nonce) => {
|
|
19243
|
+
await initialized;
|
|
19244
|
+
const sql = await client;
|
|
19245
|
+
await sql.unsafe(`INSERT INTO ${qualifiedTableName} (nonce, created_at, expires_at)
|
|
19246
|
+
VALUES ($1, $2, $3)
|
|
19247
|
+
ON CONFLICT (nonce) DO UPDATE SET expires_at = EXCLUDED.expires_at`, [nonce, Date.now(), getPlivoNonceExpiresAt(options.ttlSeconds)]);
|
|
19248
|
+
}
|
|
19249
|
+
};
|
|
19250
|
+
};
|
|
19251
|
+
var getPlivoRedisNonceKey = (keyPrefix, nonce) => `${keyPrefix}:${nonce}`;
|
|
19252
|
+
var createVoiceRedisPlivoWebhookNonceStore = (options = {}) => {
|
|
19253
|
+
const client = options.client ?? new Bun.RedisClient(options.url);
|
|
19254
|
+
const keyPrefix = options.keyPrefix?.trim() || "voice:plivo-webhook-nonce";
|
|
19255
|
+
const ttlSeconds = options.ttlSeconds;
|
|
19256
|
+
const setNonce = async (nonce, nx) => {
|
|
19257
|
+
const key = getPlivoRedisNonceKey(keyPrefix, nonce);
|
|
19258
|
+
if (typeof ttlSeconds === "number" && ttlSeconds > 0) {
|
|
19259
|
+
return client.set(key, "1", "EX", String(Math.ceil(ttlSeconds)), ...nx ? ["NX"] : []);
|
|
19260
|
+
}
|
|
19261
|
+
return client.set(key, "1", ...nx ? ["NX"] : []);
|
|
19262
|
+
};
|
|
19263
|
+
return {
|
|
19264
|
+
claim: async (nonce) => await setNonce(nonce, true) === "OK",
|
|
19265
|
+
has: async (nonce) => Boolean(await client.exists(getPlivoRedisNonceKey(keyPrefix, nonce))),
|
|
19266
|
+
set: async (nonce) => {
|
|
19267
|
+
await setNonce(nonce, false);
|
|
19268
|
+
}
|
|
19269
|
+
};
|
|
19270
|
+
};
|
|
19134
19271
|
var createVoicePlivoWebhookVerifier = (options) => async (input) => {
|
|
19135
19272
|
const verificationUrl = options.verificationUrl;
|
|
19136
19273
|
const verification = await verifyVoicePlivoWebhookSignature({
|
|
@@ -19150,7 +19287,16 @@ var createVoicePlivoWebhookVerifier = (options) => async (input) => {
|
|
|
19150
19287
|
return verification;
|
|
19151
19288
|
}
|
|
19152
19289
|
const nonce = input.headers.get("x-plivo-signature-v3-nonce");
|
|
19153
|
-
if (!nonce
|
|
19290
|
+
if (!nonce) {
|
|
19291
|
+
return { ok: false, reason: "invalid-signature" };
|
|
19292
|
+
}
|
|
19293
|
+
if (nonceStore.claim) {
|
|
19294
|
+
if (!await nonceStore.claim(nonce)) {
|
|
19295
|
+
return { ok: false, reason: "invalid-signature" };
|
|
19296
|
+
}
|
|
19297
|
+
return verification;
|
|
19298
|
+
}
|
|
19299
|
+
if (await nonceStore.has(nonce)) {
|
|
19154
19300
|
return { ok: false, reason: "invalid-signature" };
|
|
19155
19301
|
}
|
|
19156
19302
|
await nonceStore.set(nonce);
|
|
@@ -19403,6 +19549,7 @@ var createPlivoVoiceRoutes = (options = {}) => {
|
|
|
19403
19549
|
|
|
19404
19550
|
// src/telephony/telnyx.ts
|
|
19405
19551
|
import { Buffer as Buffer6 } from "buffer";
|
|
19552
|
+
import { Database as Database2 } from "bun:sqlite";
|
|
19406
19553
|
import { Elysia as Elysia31 } from "elysia";
|
|
19407
19554
|
var escapeXml4 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
19408
19555
|
var escapeHtml30 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
@@ -19575,6 +19722,198 @@ var verifyVoiceTelnyxWebhookSignature = async (input) => {
|
|
|
19575
19722
|
return { ok: false, reason: "invalid-signature" };
|
|
19576
19723
|
}
|
|
19577
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
|
+
};
|
|
19578
19917
|
var buildTelnyxVoiceSetupStatus = async (options, input) => {
|
|
19579
19918
|
const origin = resolveRequestOrigin3(input.request);
|
|
19580
19919
|
const stream = await resolveTelnyxStreamUrl(options, input);
|
|
@@ -19708,11 +20047,10 @@ var createTelnyxVoiceRoutes = (options = {}) => {
|
|
|
19708
20047
|
const smokePath = options.smoke?.path === false ? false : options.smoke?.path ?? "/api/voice/telnyx/smoke";
|
|
19709
20048
|
const bridges = new WeakMap;
|
|
19710
20049
|
const webhookPolicy = options.webhook?.policy ?? options.outcomePolicy ?? createVoiceTelephonyOutcomePolicy();
|
|
19711
|
-
const verify = options.webhook?.verify ?? (options.webhook?.publicKey ? (
|
|
19712
|
-
|
|
19713
|
-
|
|
19714
|
-
|
|
19715
|
-
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
|
|
19716
20054
|
}) : undefined);
|
|
19717
20055
|
const app = new Elysia31({
|
|
19718
20056
|
name: options.name ?? "absolutejs-voice-telnyx"
|
|
@@ -23844,7 +24182,7 @@ var createVoiceOpsRecoveryRoutes = (options = {}) => {
|
|
|
23844
24182
|
|
|
23845
24183
|
// src/observabilityExport.ts
|
|
23846
24184
|
import { Elysia as Elysia41 } from "elysia";
|
|
23847
|
-
import { Database } from "bun:sqlite";
|
|
24185
|
+
import { Database as Database3 } from "bun:sqlite";
|
|
23848
24186
|
import { createHash } from "crypto";
|
|
23849
24187
|
import { mkdir as mkdir4, readFile as readFile2, stat, unlink } from "fs/promises";
|
|
23850
24188
|
import { join as join3 } from "path";
|
|
@@ -25074,7 +25412,7 @@ var loadVoiceObservabilityExportReplaySource = async (source) => {
|
|
|
25074
25412
|
if (!source.database && !source.path) {
|
|
25075
25413
|
throw new Error("SQLite observability export replay requires source.database or source.path.");
|
|
25076
25414
|
}
|
|
25077
|
-
const database = source.database ?? new
|
|
25415
|
+
const database = source.database ?? new Database3(source.path, { create: false });
|
|
25078
25416
|
const table2 = quoteObservabilityIdentifier(normalizeObservabilityIdentifier(source.tableName));
|
|
25079
25417
|
const row2 = database.query(`SELECT manifest_json, artifact_index_json, payload_json FROM ${table2} WHERE run_id = $runId`).get({ $runId: source.runId });
|
|
25080
25418
|
if (!row2) {
|
|
@@ -25151,7 +25489,7 @@ var deliverObservabilityExportToSQLite = async (input) => {
|
|
|
25151
25489
|
if (!input.destination.database && !input.destination.path) {
|
|
25152
25490
|
throw new Error("SQLite observability export delivery requires destination.database or destination.path.");
|
|
25153
25491
|
}
|
|
25154
|
-
const database = input.destination.database ?? new
|
|
25492
|
+
const database = input.destination.database ?? new Database3(input.destination.path, { create: true });
|
|
25155
25493
|
const table = quoteObservabilityIdentifier(normalizeObservabilityIdentifier(input.destination.tableName));
|
|
25156
25494
|
const record = buildObservabilityExportDatabaseRecord(input);
|
|
25157
25495
|
database.exec(`CREATE TABLE IF NOT EXISTS ${table} (
|
|
@@ -29261,7 +29599,7 @@ var createVoiceTraceDeliveryRoutes = (options) => {
|
|
|
29261
29599
|
return routes;
|
|
29262
29600
|
};
|
|
29263
29601
|
// src/sqliteStore.ts
|
|
29264
|
-
import { Database as
|
|
29602
|
+
import { Database as Database4 } from "bun:sqlite";
|
|
29265
29603
|
var normalizeTableNameSegment = (value) => value.trim().replace(/[^a-zA-Z0-9_]+/g, "_").replace(/^_+|_+$/g, "") || "voice";
|
|
29266
29604
|
var resolveTableName = (input) => {
|
|
29267
29605
|
if (input.options.tableName) {
|
|
@@ -29272,7 +29610,7 @@ var resolveTableName = (input) => {
|
|
|
29272
29610
|
return `${prefix}_${fallback}`;
|
|
29273
29611
|
};
|
|
29274
29612
|
var openVoiceSQLiteDatabase = (path) => {
|
|
29275
|
-
const database = new
|
|
29613
|
+
const database = new Database4(path, {
|
|
29276
29614
|
create: true
|
|
29277
29615
|
});
|
|
29278
29616
|
database.exec("PRAGMA journal_mode = WAL;");
|
|
@@ -31634,6 +31972,7 @@ export {
|
|
|
31634
31972
|
createVoiceToolContractJSONHandler,
|
|
31635
31973
|
createVoiceToolContractHTMLHandler,
|
|
31636
31974
|
createVoiceToolContract,
|
|
31975
|
+
createVoiceTelnyxWebhookVerifier,
|
|
31637
31976
|
createVoiceTelnyxCampaignDialer,
|
|
31638
31977
|
createVoiceTelephonyWebhookRoutes,
|
|
31639
31978
|
createVoiceTelephonyWebhookHandler,
|
|
@@ -31657,11 +31996,13 @@ export {
|
|
|
31657
31996
|
createVoiceSTTProviderRouter,
|
|
31658
31997
|
createVoiceSQLiteTraceSinkDeliveryStore,
|
|
31659
31998
|
createVoiceSQLiteTraceEventStore,
|
|
31999
|
+
createVoiceSQLiteTelnyxWebhookEventStore,
|
|
31660
32000
|
createVoiceSQLiteTelephonyWebhookIdempotencyStore,
|
|
31661
32001
|
createVoiceSQLiteTaskStore,
|
|
31662
32002
|
createVoiceSQLiteSessionStore,
|
|
31663
32003
|
createVoiceSQLiteRuntimeStorage,
|
|
31664
32004
|
createVoiceSQLiteReviewStore,
|
|
32005
|
+
createVoiceSQLitePlivoWebhookNonceStore,
|
|
31665
32006
|
createVoiceSQLiteIntegrationEventStore,
|
|
31666
32007
|
createVoiceSQLiteExternalObjectMapStore,
|
|
31667
32008
|
createVoiceSQLiteDeliverySink,
|
|
@@ -31673,8 +32014,10 @@ export {
|
|
|
31673
32014
|
createVoiceRoutingDecisionSummary,
|
|
31674
32015
|
createVoiceReviewSavedEvent,
|
|
31675
32016
|
createVoiceResilienceRoutes,
|
|
32017
|
+
createVoiceRedisTelnyxWebhookEventStore,
|
|
31676
32018
|
createVoiceRedisTelephonyWebhookIdempotencyStore,
|
|
31677
32019
|
createVoiceRedisTaskLeaseCoordinator,
|
|
32020
|
+
createVoiceRedisPlivoWebhookNonceStore,
|
|
31678
32021
|
createVoiceRedisIdempotencyStore,
|
|
31679
32022
|
createVoiceReconnectContractRoutes,
|
|
31680
32023
|
createVoiceReadinessProfile,
|
|
@@ -31695,11 +32038,13 @@ export {
|
|
|
31695
32038
|
createVoiceProductionReadinessRoutes,
|
|
31696
32039
|
createVoicePostgresTraceSinkDeliveryStore,
|
|
31697
32040
|
createVoicePostgresTraceEventStore,
|
|
32041
|
+
createVoicePostgresTelnyxWebhookEventStore,
|
|
31698
32042
|
createVoicePostgresTelephonyWebhookIdempotencyStore,
|
|
31699
32043
|
createVoicePostgresTaskStore,
|
|
31700
32044
|
createVoicePostgresSessionStore,
|
|
31701
32045
|
createVoicePostgresRuntimeStorage,
|
|
31702
32046
|
createVoicePostgresReviewStore,
|
|
32047
|
+
createVoicePostgresPlivoWebhookNonceStore,
|
|
31703
32048
|
createVoicePostgresIntegrationEventStore,
|
|
31704
32049
|
createVoicePostgresExternalObjectMapStore,
|
|
31705
32050
|
createVoicePostgresDeliverySink,
|
|
@@ -31849,6 +32194,7 @@ export {
|
|
|
31849
32194
|
createOpenAIVoiceTTS,
|
|
31850
32195
|
createOpenAIVoiceAssistantModel,
|
|
31851
32196
|
createOpenAIRealtimeAdapter,
|
|
32197
|
+
createMemoryVoiceTelnyxWebhookEventStore,
|
|
31852
32198
|
createMemoryVoiceTelephonyWebhookIdempotencyStore,
|
|
31853
32199
|
createMemoryVoicePlivoWebhookNonceStore,
|
|
31854
32200
|
createJSONVoiceAssistantModel,
|
|
@@ -1,7 +1,10 @@
|
|
|
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';
|
|
7
|
+
import type { VoicePostgresClient } from '../postgresStore';
|
|
5
8
|
import { type TwilioMediaStreamBridgeOptions } from './twilio';
|
|
6
9
|
export type PlivoInboundMessage = {
|
|
7
10
|
event: 'start';
|
|
@@ -132,9 +135,32 @@ export type PlivoVoiceSmokeOptions = {
|
|
|
132
135
|
title?: string;
|
|
133
136
|
};
|
|
134
137
|
export type VoicePlivoWebhookNonceStore = {
|
|
138
|
+
claim?: (nonce: string) => Promise<boolean> | boolean;
|
|
135
139
|
has: (nonce: string) => Promise<boolean> | boolean;
|
|
136
140
|
set: (nonce: string) => Promise<void> | void;
|
|
137
141
|
};
|
|
142
|
+
export type VoicePlivoWebhookNonceStoreOptions = {
|
|
143
|
+
ttlSeconds?: number;
|
|
144
|
+
};
|
|
145
|
+
export type VoiceSQLitePlivoWebhookNonceStoreOptions = VoicePlivoWebhookNonceStoreOptions & {
|
|
146
|
+
database?: Database;
|
|
147
|
+
path?: string;
|
|
148
|
+
tableName?: string;
|
|
149
|
+
tablePrefix?: string;
|
|
150
|
+
};
|
|
151
|
+
export type VoicePostgresPlivoWebhookNonceStoreOptions = VoicePlivoWebhookNonceStoreOptions & {
|
|
152
|
+
connectionString?: string;
|
|
153
|
+
schemaName?: string;
|
|
154
|
+
sql?: VoicePostgresClient;
|
|
155
|
+
tableName?: string;
|
|
156
|
+
tablePrefix?: string;
|
|
157
|
+
};
|
|
158
|
+
export type VoiceRedisPlivoWebhookNonceClient = Pick<RedisClient, 'exists' | 'set'>;
|
|
159
|
+
export type VoiceRedisPlivoWebhookNonceStoreOptions = VoicePlivoWebhookNonceStoreOptions & {
|
|
160
|
+
client?: VoiceRedisPlivoWebhookNonceClient;
|
|
161
|
+
keyPrefix?: string;
|
|
162
|
+
url?: string;
|
|
163
|
+
};
|
|
138
164
|
export type VoicePlivoWebhookVerifierOptions = {
|
|
139
165
|
authToken?: string;
|
|
140
166
|
nonceStore?: VoicePlivoWebhookNonceStore;
|
|
@@ -186,6 +212,9 @@ export declare const verifyVoicePlivoWebhookSignature: (input: {
|
|
|
186
212
|
url: string;
|
|
187
213
|
}) => Promise<VoiceTelephonyWebhookVerificationResult>;
|
|
188
214
|
export declare const createMemoryVoicePlivoWebhookNonceStore: () => VoicePlivoWebhookNonceStore;
|
|
215
|
+
export declare const createVoiceSQLitePlivoWebhookNonceStore: (options: VoiceSQLitePlivoWebhookNonceStoreOptions) => VoicePlivoWebhookNonceStore;
|
|
216
|
+
export declare const createVoicePostgresPlivoWebhookNonceStore: (options?: VoicePostgresPlivoWebhookNonceStoreOptions) => VoicePlivoWebhookNonceStore;
|
|
217
|
+
export declare const createVoiceRedisPlivoWebhookNonceStore: (options?: VoiceRedisPlivoWebhookNonceStoreOptions) => VoicePlivoWebhookNonceStore;
|
|
189
218
|
export declare const createVoicePlivoWebhookVerifier: (options: VoicePlivoWebhookVerifierOptions) => (input: {
|
|
190
219
|
body: unknown;
|
|
191
220
|
headers: Headers;
|
|
@@ -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: {};
|