@sinch/functions-runtime 0.3.4 → 0.3.6

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
@@ -812,6 +812,8 @@ export interface FunctionContext {
812
812
  sms?: SmsService;
813
813
  /** Sinch Numbers SDK client (if available) */
814
814
  numbers?: NumbersService;
815
+ /** Read a file from the assets/ directory (private, not served over HTTP) */
816
+ assets(filename: string): Promise<string>;
815
817
  }
816
818
  /**
817
819
  * Application credentials structure
package/dist/index.js CHANGED
@@ -896,6 +896,7 @@ function setupJsonParsing(app, options = {}) {
896
896
  import { createRequire as createRequire2 } from "module";
897
897
  import { pathToFileURL } from "url";
898
898
  import * as nodePath from "path";
899
+ import * as nodeFs from "fs";
899
900
  var requireCjs2 = createRequire2(import.meta.url);
900
901
  var LANDING_PAGE_HTML = `<!DOCTYPE html>
901
902
  <html lang="en">
@@ -982,7 +983,8 @@ function extractFunctionName(path5, body) {
982
983
  if (body?.event && isVoiceCallback(body.event)) {
983
984
  return body.event;
984
985
  }
985
- const segments = path5.split("/").filter((s) => s && s !== "*");
986
+ const pathname = path5.split("?")[0];
987
+ const segments = pathname.split("/").filter((s) => s && s !== "*");
986
988
  if (segments.length === 1 && isVoiceCallback(segments[0])) {
987
989
  return segments[0];
988
990
  }
@@ -1084,7 +1086,11 @@ function buildBaseContext(req, config = {}) {
1084
1086
  environment: config.environment || "development",
1085
1087
  variables: config.variables
1086
1088
  },
1087
- cache: noOpCache
1089
+ cache: noOpCache,
1090
+ assets: (filename) => {
1091
+ const filePath = nodePath.join(process.cwd(), "assets", filename);
1092
+ return nodeFs.promises.readFile(filePath, "utf-8");
1093
+ }
1088
1094
  };
1089
1095
  }
1090
1096
  async function handleVoiceCallback(functionName, userFunction, context, callbackData, logger) {
@@ -1144,10 +1150,11 @@ async function handleCustomEndpoint(functionName, userFunction, context, request
1144
1150
  function createApp(options = {}) {
1145
1151
  const express = requireCjs2("express");
1146
1152
  const app = express();
1147
- if (options.staticDir) {
1148
- app.use(express.static(options.staticDir, {
1153
+ const staticDir = options.staticDir ?? (nodeFs.existsSync("./public") ? "./public" : void 0);
1154
+ if (staticDir) {
1155
+ app.use(express.static(staticDir, {
1149
1156
  index: false,
1150
- // Don't serve index.html for /, landing page handles that
1157
+ // Don't serve index.html for /, we handle that below
1151
1158
  dotfiles: "ignore"
1152
1159
  }));
1153
1160
  }
@@ -1180,7 +1187,7 @@ function setupRequestHandler(app, options = {}) {
1180
1187
  res.type("image/svg+xml").send(svg);
1181
1188
  return;
1182
1189
  }
1183
- if (req.method === "GET" && req.path === "/" && landingPageEnabled) {
1190
+ if (req.method === "GET" && req.originalUrl === "/" && landingPageEnabled) {
1184
1191
  const acceptHeader = req.headers.accept || "";
1185
1192
  if (acceptHeader.includes("text/html")) {
1186
1193
  const html = getLandingPageHtml();
@@ -1188,8 +1195,15 @@ function setupRequestHandler(app, options = {}) {
1188
1195
  return;
1189
1196
  }
1190
1197
  }
1198
+ if (req.method === "GET" && req.originalUrl === "/" && !landingPageEnabled) {
1199
+ const indexPath = nodePath.join(process.cwd(), "public", "index.html");
1200
+ if (nodeFs.existsSync(indexPath)) {
1201
+ res.type("html").sendFile(indexPath);
1202
+ return;
1203
+ }
1204
+ }
1191
1205
  try {
1192
- const functionName = extractFunctionName(req.baseUrl, req.body);
1206
+ const functionName = extractFunctionName(req.originalUrl, req.body);
1193
1207
  logger(`[${(/* @__PURE__ */ new Date()).toISOString()}] ${req.method} ${req.path} -> ${functionName}`);
1194
1208
  onRequestStart({ functionName, req });
1195
1209
  const context = buildContext(req);
@@ -1248,7 +1262,7 @@ function setupRequestHandler(app, options = {}) {
1248
1262
  logger("Function execution error:", {
1249
1263
  error: error.message,
1250
1264
  stack: error.stack,
1251
- function: extractFunctionName(req.path, req.body)
1265
+ function: extractFunctionName(req.originalUrl, req.body)
1252
1266
  });
1253
1267
  res.status(500).json({
1254
1268
  error: "Internal server error",
@@ -1256,7 +1270,7 @@ function setupRequestHandler(app, options = {}) {
1256
1270
  ...process.env.NODE_ENV === "development" && { stack: error.stack }
1257
1271
  });
1258
1272
  onRequestEnd({
1259
- functionName: extractFunctionName(req.path, req.body),
1273
+ functionName: extractFunctionName(req.originalUrl, req.body),
1260
1274
  req,
1261
1275
  error,
1262
1276
  duration,
@@ -1644,7 +1658,7 @@ async function configureConversationWebhooks(tunnelUrl, config) {
1644
1658
  console.log("\u{1F4A1} Conversation API not fully configured - skipping webhook setup");
1645
1659
  return;
1646
1660
  }
1647
- const webhookUrl = `${tunnelUrl}/conversation`;
1661
+ const webhookUrl = `${tunnelUrl}/webhook`;
1648
1662
  console.log(`\u{1F4AC} Conversation webhook URL: ${webhookUrl}`);
1649
1663
  const sinchClient = new SinchClient2({
1650
1664
  projectId,
@@ -1906,8 +1920,11 @@ var TunnelClient = class {
1906
1920
  * Configure all webhooks (Voice, Conversation, ElevenLabs)
1907
1921
  */
1908
1922
  async configureWebhooks() {
1909
- const autoConfigVoice = process.env.AUTO_CONFIGURE_VOICE !== "false";
1910
- const autoConfigConversation = process.env.AUTO_CONFIGURE_CONVERSATION !== "false";
1923
+ const hasNewFormat = "AUTO_CONFIGURE" in process.env;
1924
+ const autoEnabled = process.env.AUTO_CONFIGURE === "true";
1925
+ const sinchServices = (process.env.SINCH_SERVICES ?? "").split(",").map((s) => s.trim());
1926
+ const autoConfigVoice = hasNewFormat ? autoEnabled && sinchServices.includes("voice") : process.env.AUTO_CONFIGURE_VOICE !== "false" && !!process.env.VOICE_APPLICATION_KEY;
1927
+ const autoConfigConversation = hasNewFormat ? autoEnabled && sinchServices.includes("conversation") : process.env.AUTO_CONFIGURE_CONVERSATION !== "false" && !!process.env.CONVERSATION_APP_ID;
1911
1928
  if (autoConfigVoice && process.env.VOICE_APPLICATION_KEY) {
1912
1929
  await this.configureVoiceWebhooks();
1913
1930
  }