@absolutejs/voice 0.0.22-beta.75 → 0.0.22-beta.76
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 +1 -1
- package/dist/index.js +144 -2
- package/dist/telephony/twilio.d.ts +31 -0
- package/dist/testing/index.js +144 -2
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -81,7 +81,7 @@ export type { VoiceS3ReviewStoreClient, VoiceS3ReviewStoreFile, VoiceS3ReviewSto
|
|
|
81
81
|
export type { VoiceSQLiteRuntimeStorage, VoiceSQLiteStoreOptions } from './sqliteStore';
|
|
82
82
|
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';
|
|
83
83
|
export { createTwilioMediaStreamBridge, createTwilioVoiceRoutes, createTwilioVoiceResponse, decodeTwilioMulawBase64, encodeTwilioMulawBase64, transcodePCMToTwilioOutboundPayload, transcodeTwilioInboundPayloadToPCM16 } from './telephony/twilio';
|
|
84
|
-
export type { TwilioInboundMessage, TwilioMediaStreamBridge, TwilioMediaStreamBridgeOptions, TwilioMediaStreamSocket, TwilioOutboundClearMessage, TwilioOutboundMarkMessage, TwilioOutboundMediaMessage, TwilioOutboundMessage, TwilioVoiceRouteParameters, TwilioVoiceResponseOptions, TwilioVoiceSetupOptions, TwilioVoiceSetupStatus, TwilioVoiceRoutesOptions } from './telephony/twilio';
|
|
84
|
+
export type { TwilioInboundMessage, TwilioMediaStreamBridge, TwilioMediaStreamBridgeOptions, TwilioMediaStreamSocket, TwilioOutboundClearMessage, TwilioOutboundMarkMessage, TwilioOutboundMediaMessage, TwilioOutboundMessage, TwilioVoiceRouteParameters, TwilioVoiceResponseOptions, TwilioVoiceSmokeCheck, TwilioVoiceSmokeOptions, TwilioVoiceSmokeReport, TwilioVoiceSetupOptions, TwilioVoiceSetupStatus, TwilioVoiceRoutesOptions } from './telephony/twilio';
|
|
85
85
|
export { shapeTelephonyAssistantText } from './telephony/response';
|
|
86
86
|
export type { TelephonyResponseShapeMode, TelephonyResponseShapeOptions } from './telephony/response';
|
|
87
87
|
export * from './types';
|
package/dist/index.js
CHANGED
|
@@ -15288,6 +15288,104 @@ ${status.signing.verificationUrl ? `<p>Verification URL: <code>${escapeHtml17(st
|
|
|
15288
15288
|
${status.missing.length ? `<section><h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml17(name)}</code></li>`).join("")}</ul></section>` : ""}
|
|
15289
15289
|
${status.warnings.length ? `<section><h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml17(warning)}</li>`).join("")}</ul></section>` : ""}
|
|
15290
15290
|
</main>`;
|
|
15291
|
+
var extractTwilioStreamUrl = (twiml) => twiml.match(/<Stream\b[^>]*\surl="([^"]+)"/i)?.[1]?.replaceAll("&", "&");
|
|
15292
|
+
var createSmokeCheck = (name, status, message, details) => ({
|
|
15293
|
+
details,
|
|
15294
|
+
message,
|
|
15295
|
+
name,
|
|
15296
|
+
status
|
|
15297
|
+
});
|
|
15298
|
+
var renderTwilioVoiceSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
15299
|
+
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Twilio smoke test</p>
|
|
15300
|
+
<h1>${escapeHtml17(title)}</h1>
|
|
15301
|
+
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
15302
|
+
<section>
|
|
15303
|
+
<h2>Checks</h2>
|
|
15304
|
+
<ul>
|
|
15305
|
+
${report.checks.map((check) => `<li><strong>${escapeHtml17(check.name)}</strong>: ${escapeHtml17(check.status)}${check.message ? ` - ${escapeHtml17(check.message)}` : ""}</li>`).join("")}
|
|
15306
|
+
</ul>
|
|
15307
|
+
</section>
|
|
15308
|
+
<section>
|
|
15309
|
+
<h2>Observed URLs</h2>
|
|
15310
|
+
<ul>
|
|
15311
|
+
<li><strong>TwiML:</strong> <code>${escapeHtml17(report.setup.urls.twiml)}</code></li>
|
|
15312
|
+
<li><strong>Stream:</strong> <code>${escapeHtml17(report.twiml?.streamUrl ?? report.setup.urls.stream)}</code></li>
|
|
15313
|
+
<li><strong>Webhook:</strong> <code>${escapeHtml17(report.setup.urls.webhook)}</code></li>
|
|
15314
|
+
</ul>
|
|
15315
|
+
</section>
|
|
15316
|
+
</main>`;
|
|
15317
|
+
var runTwilioVoiceSmokeTest = async (input) => {
|
|
15318
|
+
const setup = await buildTwilioVoiceSetupStatus(input.options, input);
|
|
15319
|
+
const checks = [];
|
|
15320
|
+
const twimlUrl = new URL(setup.urls.twiml);
|
|
15321
|
+
twimlUrl.searchParams.set("scenarioId", input.options.smoke?.scenarioId ?? "smoke");
|
|
15322
|
+
twimlUrl.searchParams.set("sessionId", input.options.smoke?.sessionId ?? "smoke-session");
|
|
15323
|
+
const twimlResponse = await input.app.handle(new Request(twimlUrl, {
|
|
15324
|
+
headers: input.request.headers
|
|
15325
|
+
}));
|
|
15326
|
+
const twiml = await twimlResponse.text();
|
|
15327
|
+
const streamUrl = extractTwilioStreamUrl(twiml);
|
|
15328
|
+
checks.push(createSmokeCheck("twiml", twimlResponse.ok && Boolean(streamUrl) ? "pass" : "fail", streamUrl ? "TwiML includes a media stream URL." : 'TwiML is missing <Stream url="...">.', {
|
|
15329
|
+
status: twimlResponse.status,
|
|
15330
|
+
streamUrl
|
|
15331
|
+
}));
|
|
15332
|
+
checks.push(createSmokeCheck("stream-url", streamUrl?.startsWith("wss://") ? "pass" : "fail", streamUrl?.startsWith("wss://") ? "Media stream URL uses wss://." : "Media stream URL should use wss:// for Twilio.", {
|
|
15333
|
+
streamUrl
|
|
15334
|
+
}));
|
|
15335
|
+
const webhookBody = {
|
|
15336
|
+
CallSid: input.options.smoke?.callSid ?? "CA_SMOKE_TEST",
|
|
15337
|
+
CallStatus: input.options.smoke?.status ?? "busy",
|
|
15338
|
+
SipResponseCode: String(input.options.smoke?.sipCode ?? 486)
|
|
15339
|
+
};
|
|
15340
|
+
const webhookHeaders = new Headers({
|
|
15341
|
+
"content-type": "application/x-www-form-urlencoded"
|
|
15342
|
+
});
|
|
15343
|
+
const verificationUrl = setup.signing.verificationUrl ?? setup.urls.webhook;
|
|
15344
|
+
if (input.options.webhook?.signingSecret) {
|
|
15345
|
+
webhookHeaders.set("x-twilio-signature", await signVoiceTwilioWebhook({
|
|
15346
|
+
authToken: input.options.webhook.signingSecret,
|
|
15347
|
+
body: webhookBody,
|
|
15348
|
+
url: verificationUrl
|
|
15349
|
+
}));
|
|
15350
|
+
}
|
|
15351
|
+
const webhookResponse = await input.app.handle(new Request(setup.urls.webhook, {
|
|
15352
|
+
body: new URLSearchParams(webhookBody),
|
|
15353
|
+
headers: webhookHeaders,
|
|
15354
|
+
method: "POST"
|
|
15355
|
+
}));
|
|
15356
|
+
const webhookText = await webhookResponse.text();
|
|
15357
|
+
const webhookPayload = (() => {
|
|
15358
|
+
try {
|
|
15359
|
+
return JSON.parse(webhookText);
|
|
15360
|
+
} catch {
|
|
15361
|
+
return webhookText;
|
|
15362
|
+
}
|
|
15363
|
+
})();
|
|
15364
|
+
checks.push(createSmokeCheck("webhook", webhookResponse.ok ? "pass" : "fail", webhookResponse.ok ? "Synthetic Twilio status callback was accepted." : "Synthetic Twilio status callback failed.", {
|
|
15365
|
+
status: webhookResponse.status
|
|
15366
|
+
}));
|
|
15367
|
+
for (const warning of setup.warnings) {
|
|
15368
|
+
checks.push(createSmokeCheck("setup-warning", "warn", warning));
|
|
15369
|
+
}
|
|
15370
|
+
for (const name of setup.missing) {
|
|
15371
|
+
checks.push(createSmokeCheck("missing-env", "fail", `${name} is missing.`));
|
|
15372
|
+
}
|
|
15373
|
+
return {
|
|
15374
|
+
checks,
|
|
15375
|
+
generatedAt: Date.now(),
|
|
15376
|
+
pass: checks.every((check) => check.status !== "fail"),
|
|
15377
|
+
provider: "twilio",
|
|
15378
|
+
setup,
|
|
15379
|
+
twiml: {
|
|
15380
|
+
status: twimlResponse.status,
|
|
15381
|
+
streamUrl
|
|
15382
|
+
},
|
|
15383
|
+
webhook: {
|
|
15384
|
+
body: webhookPayload,
|
|
15385
|
+
status: webhookResponse.status
|
|
15386
|
+
}
|
|
15387
|
+
};
|
|
15388
|
+
};
|
|
15291
15389
|
var normalizeOnTurn2 = (handler) => {
|
|
15292
15390
|
if (handler.length > 1) {
|
|
15293
15391
|
const directHandler = handler;
|
|
@@ -15669,6 +15767,7 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
15669
15767
|
const twimlPath = options.twiml?.path ?? "/api/voice/twilio";
|
|
15670
15768
|
const webhookPath = options.webhook?.path ?? "/api/voice/twilio/webhook";
|
|
15671
15769
|
const setupPath = options.setup?.path === false ? false : options.setup?.path ?? "/api/voice/twilio/setup";
|
|
15770
|
+
const smokePath = options.smoke?.path === false ? false : options.smoke?.path ?? "/api/voice/twilio/smoke";
|
|
15672
15771
|
const bridges = new WeakMap;
|
|
15673
15772
|
const webhookPolicy = options.webhook?.policy ?? options.outcomePolicy ?? createVoiceTelephonyOutcomePolicy();
|
|
15674
15773
|
const app = new Elysia18({
|
|
@@ -15742,9 +15841,30 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
15742
15841
|
provider: "twilio"
|
|
15743
15842
|
}));
|
|
15744
15843
|
if (!setupPath) {
|
|
15745
|
-
|
|
15844
|
+
if (!smokePath) {
|
|
15845
|
+
return app;
|
|
15846
|
+
}
|
|
15847
|
+
return app.get(smokePath, async ({ query, request }) => {
|
|
15848
|
+
const report = await runTwilioVoiceSmokeTest({
|
|
15849
|
+
app,
|
|
15850
|
+
options,
|
|
15851
|
+
query,
|
|
15852
|
+
request,
|
|
15853
|
+
streamPath,
|
|
15854
|
+
twimlPath,
|
|
15855
|
+
webhookPath
|
|
15856
|
+
});
|
|
15857
|
+
if (query.format === "html") {
|
|
15858
|
+
return new Response(renderTwilioVoiceSmokeHTML(report, options.smoke?.title ?? "AbsoluteJS Twilio Voice Smoke Test"), {
|
|
15859
|
+
headers: {
|
|
15860
|
+
"content-type": "text/html; charset=utf-8"
|
|
15861
|
+
}
|
|
15862
|
+
});
|
|
15863
|
+
}
|
|
15864
|
+
return report;
|
|
15865
|
+
});
|
|
15746
15866
|
}
|
|
15747
|
-
|
|
15867
|
+
const withSetup = app.get(setupPath, async ({ query, request }) => {
|
|
15748
15868
|
const status = await buildTwilioVoiceSetupStatus(options, {
|
|
15749
15869
|
query,
|
|
15750
15870
|
request,
|
|
@@ -15761,6 +15881,28 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
15761
15881
|
}
|
|
15762
15882
|
return status;
|
|
15763
15883
|
});
|
|
15884
|
+
if (!smokePath) {
|
|
15885
|
+
return withSetup;
|
|
15886
|
+
}
|
|
15887
|
+
return withSetup.get(smokePath, async ({ query, request }) => {
|
|
15888
|
+
const report = await runTwilioVoiceSmokeTest({
|
|
15889
|
+
app,
|
|
15890
|
+
options,
|
|
15891
|
+
query,
|
|
15892
|
+
request,
|
|
15893
|
+
streamPath,
|
|
15894
|
+
twimlPath,
|
|
15895
|
+
webhookPath
|
|
15896
|
+
});
|
|
15897
|
+
if (query.format === "html") {
|
|
15898
|
+
return new Response(renderTwilioVoiceSmokeHTML(report, options.smoke?.title ?? "AbsoluteJS Twilio Voice Smoke Test"), {
|
|
15899
|
+
headers: {
|
|
15900
|
+
"content-type": "text/html; charset=utf-8"
|
|
15901
|
+
}
|
|
15902
|
+
});
|
|
15903
|
+
}
|
|
15904
|
+
return report;
|
|
15905
|
+
});
|
|
15764
15906
|
};
|
|
15765
15907
|
// src/telephony/response.ts
|
|
15766
15908
|
var normalizeWhitespace = (value) => value.replace(/\s+/g, " ").trim();
|
|
@@ -135,9 +135,40 @@ export type TwilioVoiceSetupOptions = {
|
|
|
135
135
|
requiredEnv?: Record<string, string | undefined>;
|
|
136
136
|
title?: string;
|
|
137
137
|
};
|
|
138
|
+
export type TwilioVoiceSmokeCheck = {
|
|
139
|
+
details?: Record<string, unknown>;
|
|
140
|
+
message?: string;
|
|
141
|
+
name: string;
|
|
142
|
+
status: 'fail' | 'pass' | 'warn';
|
|
143
|
+
};
|
|
144
|
+
export type TwilioVoiceSmokeReport = {
|
|
145
|
+
checks: TwilioVoiceSmokeCheck[];
|
|
146
|
+
generatedAt: number;
|
|
147
|
+
pass: boolean;
|
|
148
|
+
provider: 'twilio';
|
|
149
|
+
setup: TwilioVoiceSetupStatus;
|
|
150
|
+
twiml?: {
|
|
151
|
+
status: number;
|
|
152
|
+
streamUrl?: string;
|
|
153
|
+
};
|
|
154
|
+
webhook?: {
|
|
155
|
+
body?: unknown;
|
|
156
|
+
status: number;
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
export type TwilioVoiceSmokeOptions = {
|
|
160
|
+
callSid?: string;
|
|
161
|
+
path?: false | string;
|
|
162
|
+
scenarioId?: string;
|
|
163
|
+
sessionId?: string;
|
|
164
|
+
sipCode?: number;
|
|
165
|
+
status?: string;
|
|
166
|
+
title?: string;
|
|
167
|
+
};
|
|
138
168
|
export type TwilioVoiceRoutesOptions<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = TwilioMediaStreamBridgeOptions<TContext, TSession, TResult> & {
|
|
139
169
|
name?: string;
|
|
140
170
|
outcomePolicy?: VoiceTelephonyOutcomePolicy;
|
|
171
|
+
smoke?: TwilioVoiceSmokeOptions;
|
|
141
172
|
setup?: TwilioVoiceSetupOptions;
|
|
142
173
|
streamPath?: string;
|
|
143
174
|
twiml?: {
|
package/dist/testing/index.js
CHANGED
|
@@ -8627,6 +8627,104 @@ ${status.signing.verificationUrl ? `<p>Verification URL: <code>${escapeHtml2(sta
|
|
|
8627
8627
|
${status.missing.length ? `<section><h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml2(name)}</code></li>`).join("")}</ul></section>` : ""}
|
|
8628
8628
|
${status.warnings.length ? `<section><h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml2(warning)}</li>`).join("")}</ul></section>` : ""}
|
|
8629
8629
|
</main>`;
|
|
8630
|
+
var extractTwilioStreamUrl = (twiml) => twiml.match(/<Stream\b[^>]*\surl="([^"]+)"/i)?.[1]?.replaceAll("&", "&");
|
|
8631
|
+
var createSmokeCheck = (name, status, message, details) => ({
|
|
8632
|
+
details,
|
|
8633
|
+
message,
|
|
8634
|
+
name,
|
|
8635
|
+
status
|
|
8636
|
+
});
|
|
8637
|
+
var renderTwilioVoiceSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
8638
|
+
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Twilio smoke test</p>
|
|
8639
|
+
<h1>${escapeHtml2(title)}</h1>
|
|
8640
|
+
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
8641
|
+
<section>
|
|
8642
|
+
<h2>Checks</h2>
|
|
8643
|
+
<ul>
|
|
8644
|
+
${report.checks.map((check) => `<li><strong>${escapeHtml2(check.name)}</strong>: ${escapeHtml2(check.status)}${check.message ? ` - ${escapeHtml2(check.message)}` : ""}</li>`).join("")}
|
|
8645
|
+
</ul>
|
|
8646
|
+
</section>
|
|
8647
|
+
<section>
|
|
8648
|
+
<h2>Observed URLs</h2>
|
|
8649
|
+
<ul>
|
|
8650
|
+
<li><strong>TwiML:</strong> <code>${escapeHtml2(report.setup.urls.twiml)}</code></li>
|
|
8651
|
+
<li><strong>Stream:</strong> <code>${escapeHtml2(report.twiml?.streamUrl ?? report.setup.urls.stream)}</code></li>
|
|
8652
|
+
<li><strong>Webhook:</strong> <code>${escapeHtml2(report.setup.urls.webhook)}</code></li>
|
|
8653
|
+
</ul>
|
|
8654
|
+
</section>
|
|
8655
|
+
</main>`;
|
|
8656
|
+
var runTwilioVoiceSmokeTest = async (input) => {
|
|
8657
|
+
const setup = await buildTwilioVoiceSetupStatus(input.options, input);
|
|
8658
|
+
const checks = [];
|
|
8659
|
+
const twimlUrl = new URL(setup.urls.twiml);
|
|
8660
|
+
twimlUrl.searchParams.set("scenarioId", input.options.smoke?.scenarioId ?? "smoke");
|
|
8661
|
+
twimlUrl.searchParams.set("sessionId", input.options.smoke?.sessionId ?? "smoke-session");
|
|
8662
|
+
const twimlResponse = await input.app.handle(new Request(twimlUrl, {
|
|
8663
|
+
headers: input.request.headers
|
|
8664
|
+
}));
|
|
8665
|
+
const twiml = await twimlResponse.text();
|
|
8666
|
+
const streamUrl = extractTwilioStreamUrl(twiml);
|
|
8667
|
+
checks.push(createSmokeCheck("twiml", twimlResponse.ok && Boolean(streamUrl) ? "pass" : "fail", streamUrl ? "TwiML includes a media stream URL." : 'TwiML is missing <Stream url="...">.', {
|
|
8668
|
+
status: twimlResponse.status,
|
|
8669
|
+
streamUrl
|
|
8670
|
+
}));
|
|
8671
|
+
checks.push(createSmokeCheck("stream-url", streamUrl?.startsWith("wss://") ? "pass" : "fail", streamUrl?.startsWith("wss://") ? "Media stream URL uses wss://." : "Media stream URL should use wss:// for Twilio.", {
|
|
8672
|
+
streamUrl
|
|
8673
|
+
}));
|
|
8674
|
+
const webhookBody = {
|
|
8675
|
+
CallSid: input.options.smoke?.callSid ?? "CA_SMOKE_TEST",
|
|
8676
|
+
CallStatus: input.options.smoke?.status ?? "busy",
|
|
8677
|
+
SipResponseCode: String(input.options.smoke?.sipCode ?? 486)
|
|
8678
|
+
};
|
|
8679
|
+
const webhookHeaders = new Headers({
|
|
8680
|
+
"content-type": "application/x-www-form-urlencoded"
|
|
8681
|
+
});
|
|
8682
|
+
const verificationUrl = setup.signing.verificationUrl ?? setup.urls.webhook;
|
|
8683
|
+
if (input.options.webhook?.signingSecret) {
|
|
8684
|
+
webhookHeaders.set("x-twilio-signature", await signVoiceTwilioWebhook({
|
|
8685
|
+
authToken: input.options.webhook.signingSecret,
|
|
8686
|
+
body: webhookBody,
|
|
8687
|
+
url: verificationUrl
|
|
8688
|
+
}));
|
|
8689
|
+
}
|
|
8690
|
+
const webhookResponse = await input.app.handle(new Request(setup.urls.webhook, {
|
|
8691
|
+
body: new URLSearchParams(webhookBody),
|
|
8692
|
+
headers: webhookHeaders,
|
|
8693
|
+
method: "POST"
|
|
8694
|
+
}));
|
|
8695
|
+
const webhookText = await webhookResponse.text();
|
|
8696
|
+
const webhookPayload = (() => {
|
|
8697
|
+
try {
|
|
8698
|
+
return JSON.parse(webhookText);
|
|
8699
|
+
} catch {
|
|
8700
|
+
return webhookText;
|
|
8701
|
+
}
|
|
8702
|
+
})();
|
|
8703
|
+
checks.push(createSmokeCheck("webhook", webhookResponse.ok ? "pass" : "fail", webhookResponse.ok ? "Synthetic Twilio status callback was accepted." : "Synthetic Twilio status callback failed.", {
|
|
8704
|
+
status: webhookResponse.status
|
|
8705
|
+
}));
|
|
8706
|
+
for (const warning of setup.warnings) {
|
|
8707
|
+
checks.push(createSmokeCheck("setup-warning", "warn", warning));
|
|
8708
|
+
}
|
|
8709
|
+
for (const name of setup.missing) {
|
|
8710
|
+
checks.push(createSmokeCheck("missing-env", "fail", `${name} is missing.`));
|
|
8711
|
+
}
|
|
8712
|
+
return {
|
|
8713
|
+
checks,
|
|
8714
|
+
generatedAt: Date.now(),
|
|
8715
|
+
pass: checks.every((check) => check.status !== "fail"),
|
|
8716
|
+
provider: "twilio",
|
|
8717
|
+
setup,
|
|
8718
|
+
twiml: {
|
|
8719
|
+
status: twimlResponse.status,
|
|
8720
|
+
streamUrl
|
|
8721
|
+
},
|
|
8722
|
+
webhook: {
|
|
8723
|
+
body: webhookPayload,
|
|
8724
|
+
status: webhookResponse.status
|
|
8725
|
+
}
|
|
8726
|
+
};
|
|
8727
|
+
};
|
|
8630
8728
|
var normalizeOnTurn = (handler) => {
|
|
8631
8729
|
if (handler.length > 1) {
|
|
8632
8730
|
const directHandler = handler;
|
|
@@ -9008,6 +9106,7 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
9008
9106
|
const twimlPath = options.twiml?.path ?? "/api/voice/twilio";
|
|
9009
9107
|
const webhookPath = options.webhook?.path ?? "/api/voice/twilio/webhook";
|
|
9010
9108
|
const setupPath = options.setup?.path === false ? false : options.setup?.path ?? "/api/voice/twilio/setup";
|
|
9109
|
+
const smokePath = options.smoke?.path === false ? false : options.smoke?.path ?? "/api/voice/twilio/smoke";
|
|
9011
9110
|
const bridges = new WeakMap;
|
|
9012
9111
|
const webhookPolicy = options.webhook?.policy ?? options.outcomePolicy ?? createVoiceTelephonyOutcomePolicy();
|
|
9013
9112
|
const app = new Elysia2({
|
|
@@ -9081,9 +9180,30 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
9081
9180
|
provider: "twilio"
|
|
9082
9181
|
}));
|
|
9083
9182
|
if (!setupPath) {
|
|
9084
|
-
|
|
9183
|
+
if (!smokePath) {
|
|
9184
|
+
return app;
|
|
9185
|
+
}
|
|
9186
|
+
return app.get(smokePath, async ({ query, request }) => {
|
|
9187
|
+
const report = await runTwilioVoiceSmokeTest({
|
|
9188
|
+
app,
|
|
9189
|
+
options,
|
|
9190
|
+
query,
|
|
9191
|
+
request,
|
|
9192
|
+
streamPath,
|
|
9193
|
+
twimlPath,
|
|
9194
|
+
webhookPath
|
|
9195
|
+
});
|
|
9196
|
+
if (query.format === "html") {
|
|
9197
|
+
return new Response(renderTwilioVoiceSmokeHTML(report, options.smoke?.title ?? "AbsoluteJS Twilio Voice Smoke Test"), {
|
|
9198
|
+
headers: {
|
|
9199
|
+
"content-type": "text/html; charset=utf-8"
|
|
9200
|
+
}
|
|
9201
|
+
});
|
|
9202
|
+
}
|
|
9203
|
+
return report;
|
|
9204
|
+
});
|
|
9085
9205
|
}
|
|
9086
|
-
|
|
9206
|
+
const withSetup = app.get(setupPath, async ({ query, request }) => {
|
|
9087
9207
|
const status = await buildTwilioVoiceSetupStatus(options, {
|
|
9088
9208
|
query,
|
|
9089
9209
|
request,
|
|
@@ -9100,6 +9220,28 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
9100
9220
|
}
|
|
9101
9221
|
return status;
|
|
9102
9222
|
});
|
|
9223
|
+
if (!smokePath) {
|
|
9224
|
+
return withSetup;
|
|
9225
|
+
}
|
|
9226
|
+
return withSetup.get(smokePath, async ({ query, request }) => {
|
|
9227
|
+
const report = await runTwilioVoiceSmokeTest({
|
|
9228
|
+
app,
|
|
9229
|
+
options,
|
|
9230
|
+
query,
|
|
9231
|
+
request,
|
|
9232
|
+
streamPath,
|
|
9233
|
+
twimlPath,
|
|
9234
|
+
webhookPath
|
|
9235
|
+
});
|
|
9236
|
+
if (query.format === "html") {
|
|
9237
|
+
return new Response(renderTwilioVoiceSmokeHTML(report, options.smoke?.title ?? "AbsoluteJS Twilio Voice Smoke Test"), {
|
|
9238
|
+
headers: {
|
|
9239
|
+
"content-type": "text/html; charset=utf-8"
|
|
9240
|
+
}
|
|
9241
|
+
});
|
|
9242
|
+
}
|
|
9243
|
+
return report;
|
|
9244
|
+
});
|
|
9103
9245
|
};
|
|
9104
9246
|
|
|
9105
9247
|
// src/testing/telephony.ts
|