gencow 0.1.78 → 0.1.80

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/bin/gencow.mjs CHANGED
@@ -1738,17 +1738,15 @@ ${BOLD}Examples:${RESET}
1738
1738
  process.exit(1);
1739
1739
  }
1740
1740
 
1741
- // 1-0. Pre-deploy dependency audit
1741
+ // 1-0. Pre-deploy dependency audit (informational — user deps auto-installed)
1742
1742
  if (!forceDeploy) {
1743
1743
  try {
1744
1744
  const { auditDeployDependencies, formatAuditError } = await import("../lib/deploy-auditor.mjs");
1745
1745
  const entryPoint = resolve(process.cwd(), "gencow", "index.ts");
1746
1746
  if (existsSync(entryPoint)) {
1747
1747
  const auditResult = await auditDeployDependencies(entryPoint);
1748
- if (!auditResult.passed) {
1749
- log(formatAuditError(auditResult));
1750
- process.exit(1);
1751
- }
1748
+ const auditMsg = formatAuditError(auditResult);
1749
+ if (auditMsg) log(auditMsg);
1752
1750
  }
1753
1751
  } catch (auditErr) {
1754
1752
  warn(`의존성 검사 스킵: ${auditErr.message}`);
@@ -2013,17 +2011,15 @@ ${BOLD}Examples:${RESET}
2013
2011
  if (shouldDeployBackend) {
2014
2012
  log(` ${BOLD}── 백엔드 배포 ──────────────────────${RESET}\n`);
2015
2013
 
2016
- // 1-0. Pre-deploy dependency audit
2014
+ // 1-0. Pre-deploy dependency audit (informational — user deps auto-installed)
2017
2015
  if (!forceDeploy) {
2018
2016
  try {
2019
2017
  const { auditDeployDependencies, formatAuditError } = await import("../lib/deploy-auditor.mjs");
2020
2018
  const entryPoint = resolve(backendRoot, "gencow", "index.ts");
2021
2019
  if (existsSync(entryPoint)) {
2022
2020
  const auditResult = await auditDeployDependencies(entryPoint);
2023
- if (!auditResult.passed) {
2024
- log(formatAuditError(auditResult));
2025
- process.exit(1);
2026
- }
2021
+ const auditMsg = formatAuditError(auditResult);
2022
+ if (auditMsg) log(auditMsg);
2027
2023
  }
2028
2024
  } catch (auditErr) {
2029
2025
  warn(`의존성 검사 스킵: ${auditErr.message}`);
@@ -78,36 +78,38 @@ describe("isNodeBuiltin", () => {
78
78
  });
79
79
 
80
80
  describe("formatAuditError", () => {
81
- it("returns empty string when passed", () => {
82
- expect(formatAuditError({ passed: true, unsupported: [] })).toBe("");
81
+ it("returns empty string when no user deps", () => {
82
+ expect(formatAuditError({ passed: true, unsupported: [], hasUserDependencies: false })).toBe("");
83
83
  });
84
84
 
85
- it("formats error with unsupported packages", () => {
85
+ it("formats info message with user dependencies (auto-install)", () => {
86
86
  const result = {
87
- passed: false,
87
+ passed: true,
88
88
  unsupported: [
89
89
  { packageName: "langfuse", importedFrom: "gencow/llmActions.ts" },
90
90
  { packageName: "cheerio", importedFrom: "gencow/crawlPipeline.ts" },
91
91
  ],
92
+ hasUserDependencies: true,
92
93
  };
93
94
  const output = formatAuditError(result);
94
- expect(output).toContain("DEPLOY BLOCKED");
95
+ // 이제 차단이 아닌 안내 메시지
95
96
  expect(output).toContain("langfuse");
96
- expect(output).toContain("gencow/llmActions.ts");
97
97
  expect(output).toContain("cheerio");
98
- expect(output).toContain("gencow/crawlPipeline.ts");
99
- expect(output).toContain("--force");
98
+ expect(output).toContain("자동 설치");
99
+ // 차단 메시지가 없어야 함
100
+ expect(output).not.toContain("DEPLOY BLOCKED");
101
+ expect(output).not.toContain("--force");
100
102
  });
101
103
 
102
- it("includes platform package list in error", () => {
104
+ it("shows rollback info in message", () => {
103
105
  const result = {
104
- passed: false,
106
+ passed: true,
105
107
  unsupported: [{ packageName: "moment", importedFrom: "gencow/utils.ts" }],
108
+ hasUserDependencies: true,
106
109
  };
107
110
  const output = formatAuditError(result);
108
- expect(output).toContain("@gencow/core");
109
- expect(output).toContain("drizzle-orm");
110
- expect(output).toContain("better-auth");
111
+ expect(output).toContain("moment");
112
+ expect(output).toContain("롤백");
111
113
  });
112
114
  });
113
115
 
@@ -1,18 +1,18 @@
1
1
  /**
2
- * Deploy Auditor — Pure function for pre-deploy dependency validation.
2
+ * Deploy Auditor — Pre-deploy dependency analysis (informational).
3
3
  *
4
- * Checks if user code imports packages that are not available
5
- * in the Gencow cloud runtime. This prevents "new version unhealthy"
6
- * deploy failures caused by missing third-party dependencies.
4
+ * Detects user third-party dependencies that need auto-install on the
5
+ * Gencow cloud runtime. User deps are installed via `bun install` on
6
+ * the server during provisioning.
7
7
  *
8
8
  * How it works:
9
9
  * 1. Uses esbuild metafile to extract all external imports
10
10
  * 2. Filters out relative imports, Node built-ins, and platform packages
11
- * 3. Returns list of unsupported packages with source file info
11
+ * 3. Returns list of user dependencies with source file info
12
12
  *
13
13
  * Usage:
14
14
  * const result = await auditDeployDependencies("./gencow/index.ts");
15
- * if (!result.passed) { ... block deploy ... }
15
+ * if (result.hasUserDependencies) { ... show info ... }
16
16
  */
17
17
  import { build } from "esbuild";
18
18
 
@@ -61,8 +61,9 @@ const NODE_BUILTINS = new Set([
61
61
 
62
62
  /**
63
63
  * @typedef {Object} AuditResult
64
- * @property {boolean} passed - true if no unsupported deps found
65
- * @property {UnsupportedDep[]} unsupported - List of unsupported packages
64
+ * @property {boolean} passed - Always true (audit is informational, not blocking)
65
+ * @property {UnsupportedDep[]} unsupported - List of user third-party packages
66
+ * @property {boolean} hasUserDependencies - true if user deps detected (will be auto-installed)
66
67
  */
67
68
 
68
69
  /**
@@ -178,43 +179,31 @@ export async function auditDeployDependencies(entryPoint) {
178
179
  }
179
180
 
180
181
  return {
181
- passed: unsupported.length === 0,
182
+ passed: true, // Always pass — user deps are auto-installed on server
182
183
  unsupported,
184
+ hasUserDependencies: unsupported.length > 0,
183
185
  };
184
186
  }
185
187
 
186
188
  /**
187
- * Format audit result as a human-readable error message for CLI output.
189
+ * Format audit result as a human-readable info message for CLI output.
190
+ * Shows which user dependencies will be auto-installed on the cloud.
188
191
  *
189
192
  * @param {AuditResult} result
190
- * @returns {string} Formatted message (empty string if passed)
193
+ * @returns {string} Formatted message (empty string if no user deps)
191
194
  */
192
195
  export function formatAuditError(result) {
193
- if (result.passed) return "";
196
+ if (!result.hasUserDependencies) return "";
194
197
 
198
+ const pkgNames = result.unsupported.map(d => d.packageName);
195
199
  const lines = [
196
200
  "",
197
- "╔═══════════════════════════════════════════════════════════════╗",
198
- "║ ⛔ DEPLOY BLOCKED Unsupported Dependencies Detected ║",
199
- "╚═══════════════════════════════════════════════════════════════╝",
200
- "",
201
- " The following packages are NOT available in the Gencow cloud runtime:",
201
+ ` 📦 서드파티 패키지 감지: ${pkgNames.join(", ")}`,
202
+ ` → 클라우드 배포 자동 설치됩니다.`,
203
+ ` → 설치 실패 시 배포가 롤백됩니다.`,
202
204
  "",
203
205
  ];
204
206
 
205
- for (const dep of result.unsupported) {
206
- lines.push(` ✗ ${dep.packageName} (imported in ${dep.importedFrom})`);
207
- }
208
-
209
- lines.push("");
210
- lines.push(" Gencow 클라우드에서 사용 가능한 패키지:");
211
- lines.push(" @gencow/core, drizzle-orm, better-auth, postgres, hono, ai, zod");
212
- lines.push("");
213
- lines.push(" 해결 방법:");
214
- lines.push(" 1. 해당 패키지 사용을 제거하거나 동적 import (try/catch)로 변경");
215
- lines.push(" 2. gencow deploy --force 로 강제 배포 (서버 크래시 위험)");
216
- lines.push("");
217
-
218
207
  return lines.join("\n");
219
208
  }
220
209
 
@@ -307,18 +307,16 @@ export function buildAiPrompt(apiObj, namespaces) {
307
307
  md += `- .env 파일은 로컬 개발 전용이야. 클라우드에는 gencow env push로 올려.\n`;
308
308
  md += `- 로컬 dev 서버에 설정하려면 \`gencow env set --local KEY=VALUE\`를 써.\n`;
309
309
  md += `\n`;
310
- md += `⚠️ 패키지 제한 (중요):\n`;
311
- md += `- gencow 클라우드에서 사용 가능한 npm 패키지는 아래만 해당돼:\n`;
312
- md += ` @gencow/core, drizzle-orm, better-auth, postgres, hono, ai, @ai-sdk/openai, zod, esbuild\n`;
313
- md += `- npm install로 추가한 서드파티 패키지(langfuse, openai, axios, cheerio 등)는 배포 시 사용 불가.\n`;
314
- md += ` 배포하면 "new version unhealthy" 에러가 발생해.\n`;
310
+ md += `⚠️ 패키지 정보:\n`;
311
+ md += `- npm install로 추가한 서드파티 패키지(langfuse, axios, cheerio 등)는 배포 시 자동 설치돼.\n`;
312
+ md += `- 단, child_process, vm, os, cluster, worker_threads 모듈은 보안상 차단됨.\n`;
315
313
  md += `\n`;
316
314
  md += `⚠️ 해싱/암호화:\n`;
317
- md += `- node:crypto 모듈은 사용 불가. 해싱이 필요하면 Web Crypto API 사용해:\n`;
315
+ md += `- node:crypto 모듈도 사용 가능하지만, 가볍게 Web Crypto API(crypto.subtle)도 추천:\n`;
318
316
  md += ` const hash = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));\n`;
319
317
  md += ` const hex = [...new Uint8Array(hash)].map(b => b.toString(16).padStart(2, "0")).join("");\n`;
320
318
  md += `- 직접 구현한 simpleHash 같은 32비트 해시는 충돌 위험이 있으니 SHA-256을 사용해.\n`;
321
- md += `- AI 호출이 필요하면 ctx.ai.chat()을 사용해. OpenAI SDK를 직접 설치하지 마.\n`;
319
+ md += `- AI 호출이 필요하면 ctx.ai.chat()을 사용해. OpenAI SDK를 직접 설치해도 되지만 ctx.ai.chat()이 더 간편해.\n`;
322
320
  md += `\`\`\`\n\n`;
323
321
  return md;
324
322
  }
@@ -401,9 +399,10 @@ export function buildDeploySection() {
401
399
  md += `\`\`\`\n\n`;
402
400
  md += `> ⚠️ 프론트엔드에서 API를 호출하려면 빌드 시 \`VITE_API_URL\`을 반드시 설정하세요.\n\n`;
403
401
 
404
- // 패키지 제한 경고
405
- md += `### ⚠️ 서드파티 npm 패키지 제한\n\n`;
406
- md += `Gencow 클라우드 런타임에서 사용 가능한 패키지는 다음으로 제한됩니다:\n\n`;
402
+ // 서드파티 패키지 안내 (자동 설치)
403
+ md += `### 📦 서드파티 npm 패키지\n\n`;
404
+ md += `\`npm install\`로 추가한 서드파티 패키지는 배포 시 **자동으로 설치**됩니다.\n\n`;
405
+ md += `클라우드 기본 제공 패키지 (별도 설치 불필요):\n\n`;
407
406
  md += `| 패키지 | 설명 |\n`;
408
407
  md += `| :--- | :--- |\n`;
409
408
  md += `| \`@gencow/core\` | Gencow 핵심 프레임워크 |\n`;
@@ -413,9 +412,8 @@ export function buildDeploySection() {
413
412
  md += `| \`hono\` | HTTP 프레임워크 |\n`;
414
413
  md += `| \`ai\`, \`@ai-sdk/*\` | AI SDK |\n`;
415
414
  md += `| \`zod\` | 밸리데이션 |\n\n`;
416
- md += `> ⚠️ \`npm install\`로 추가한 서드파티 패키지(langfuse, openai, axios)는 배포 사용할 없습니다.\n`;
417
- md += `> 배포하면 "new version unhealthy" 에러가 발생합니다.\n`;
418
- md += `> AI 호출이 필요하면 \`ctx.ai.chat()\`을 사용하세요.\n\n`;
415
+ md += `> 📦 langfuse, axios, cheerio추가 패키지도 \`npm install\` 후 \`gencow deploy\`하면 자동 설치됩니다.\n`;
416
+ md += `> \`child_process\`, \`vm\`, \`os\`, \`cluster\`, \`worker_threads\` 모듈은 보안상 차단됩니다.\n\n`;
419
417
  md += `### CORS 설정\n\n`;
420
418
  md += `- \`*.gencow.app\` 서브도메인 간 요청은 **자동 허용**됩니다.\n`;
421
419
  md += `- 커스텀 도메인에서 API를 호출하려면 환경변수를 설정하세요:\n\n`;
@@ -424,23 +422,21 @@ export function buildDeploySection() {
424
422
  md += `\`\`\`\n\n`;
425
423
 
426
424
  // 해싱/암호화 대안
427
- md += `### 🔐 해싱/암호화 (node:crypto 대안)\n\n`;
428
- md += `Gencow 클라우드에서 \`node:crypto\` 모듈은 사용할 수 없습니다.\n`;
429
- md += `해싱이 필요하면 **Web Crypto API** (\`crypto.subtle\`)를 사용하세요:\n\n`;
425
+ md += `### 🔐 해싱/암호화\n\n`;
426
+ md += `\`node:crypto\` 모듈 사용 가능합니다. Web Crypto API도 대안으로 사용할 수 있습니다:\n\n`;
430
427
  md += `\`\`\`typescript\n`;
431
- md += `// SHA-256 해싱 (node:crypto 대신 Web Crypto API)\n`;
428
+ md += `// SHA-256 해싱 (Web Crypto API)
429
+ `;
432
430
  md += `async function sha256(text: string): Promise<string> {\n`;
433
431
  md += ` const data = new TextEncoder().encode(text);\n`;
434
432
  md += ` const hash = await crypto.subtle.digest("SHA-256", data);\n`;
435
433
  md += ` return [...new Uint8Array(hash)]\n`;
436
434
  md += ` .map(b => b.toString(16).padStart(2, "0"))\n`;
437
435
  md += ` .join("");\n`;
438
- md += `}\n\n`;
439
- md += `// 사용 예: 중복 탐지, 캐시 키, 콘텐츠 해싱\n`;
440
- md += `const articleHash = await sha256(article.url + article.title);\n`;
436
+ md += `}\n`;
441
437
  md += `\`\`\`\n\n`;
442
438
  md += `> ⚠️ 직접 구현한 \`simpleHash()\` 같은 32비트 해시는 대량 데이터에서 충돌 위험이 있습니다.\n`;
443
- md += `> 반드시 SHA-256을 사용하세요 (2^256 가능 값, 사실상 충돌 0).\n\n`;
439
+ md += `> 반드시 SHA-256을 사용하세요.\n\n`;
444
440
 
445
441
  return md;
446
442
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gencow",
3
- "version": "0.1.78",
3
+ "version": "0.1.80",
4
4
  "description": "Gencow — AI Backend Engine",
5
5
  "type": "module",
6
6
  "bin": {
package/server/index.js CHANGED
@@ -71681,16 +71681,8 @@ async function consumeInviteCode(code) {
71681
71681
  // ../server/src/auditor.ts
71682
71682
  var esbuild = __toESM(require_main(), 1);
71683
71683
  var FORBIDDEN_MODULES = /* @__PURE__ */ new Set([
71684
- "fs",
71685
- "fs/promises",
71686
- "node:fs",
71687
- "node:fs/promises",
71688
71684
  "child_process",
71689
71685
  "node:child_process",
71690
- "net",
71691
- "node:net",
71692
- "tls",
71693
- "node:tls",
71694
71686
  "dgram",
71695
71687
  "node:dgram",
71696
71688
  "os",
@@ -71699,14 +71691,6 @@ var FORBIDDEN_MODULES = /* @__PURE__ */ new Set([
71699
71691
  "node:cluster",
71700
71692
  "worker_threads",
71701
71693
  "node:worker_threads",
71702
- "http",
71703
- "node:http",
71704
- "https",
71705
- "node:https",
71706
- "http2",
71707
- "node:http2",
71708
- "readline",
71709
- "node:readline",
71710
71694
  "v8",
71711
71695
  "node:v8",
71712
71696
  "vm",
@@ -71766,6 +71750,15 @@ var ALLOWED_NODE_MODULES = /* @__PURE__ */ new Set([
71766
71750
  "node:timers",
71767
71751
  "node:timers/promises",
71768
71752
  "node:zlib",
71753
+ // 서드파티 패키지 호환을 위해 허용 (nsjail이 보안 담당)
71754
+ "node:fs",
71755
+ "node:fs/promises",
71756
+ "node:http",
71757
+ "node:https",
71758
+ "node:http2",
71759
+ "node:net",
71760
+ "node:tls",
71761
+ "node:readline",
71769
71762
  // non-prefixed equivalents
71770
71763
  "crypto",
71771
71764
  "path",
@@ -71778,7 +71771,15 @@ var ALLOWED_NODE_MODULES = /* @__PURE__ */ new Set([
71778
71771
  "querystring",
71779
71772
  "string_decoder",
71780
71773
  "timers",
71781
- "zlib"
71774
+ "zlib",
71775
+ "fs",
71776
+ "fs/promises",
71777
+ "http",
71778
+ "https",
71779
+ "http2",
71780
+ "net",
71781
+ "tls",
71782
+ "readline"
71782
71783
  ]);
71783
71784
  function isAllowedNodeModule(moduleName) {
71784
71785
  return ALLOWED_NODE_MODULES.has(moduleName);
@@ -72553,6 +72554,38 @@ function getOriginalEnvValue(key) {
72553
72554
  }
72554
72555
 
72555
72556
  // ../server/src/index.ts
72557
+ var FUNCTION_TIMEOUTS = {
72558
+ query: 3e4,
72559
+ // 30초
72560
+ mutation: 3e4,
72561
+ // 30초
72562
+ httpAction: 3e5,
72563
+ // 5분
72564
+ cron: 6e5
72565
+ // 10분
72566
+ };
72567
+ async function executeWithTimeout(fn, type, functionName) {
72568
+ const timeoutMs = FUNCTION_TIMEOUTS[type];
72569
+ const controller = new AbortController();
72570
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
72571
+ try {
72572
+ const result = await Promise.race([
72573
+ fn(),
72574
+ new Promise((_, reject) => {
72575
+ controller.signal.addEventListener("abort", () => {
72576
+ const err = new Error(
72577
+ `Function "${functionName}" exceeded ${timeoutMs / 1e3}s time limit. Use ctx.scheduler.runAfter() for long-running tasks.`
72578
+ );
72579
+ err.code = "FUNCTION_TIMEOUT";
72580
+ reject(err);
72581
+ });
72582
+ })
72583
+ ]);
72584
+ return result;
72585
+ } finally {
72586
+ clearTimeout(timer);
72587
+ }
72588
+ }
72556
72589
  var METERING_URL = process.env.GENCOW_METERING_URL;
72557
72590
  var IS_BAAS = !!METERING_URL;
72558
72591
  var INTERNAL_TOKEN = process.env.GENCOW_INTERNAL_TOKEN || "";
@@ -72930,7 +72963,9 @@ async function main() {
72930
72963
  }
72931
72964
  }
72932
72965
  const headers = new Headers();
72933
- headers.set("Content-Type", file3.type || "application/octet-stream");
72966
+ const rawType = file3.type || "application/octet-stream";
72967
+ const needsCharset = (rawType.startsWith("text/") || rawType === "application/json") && !rawType.includes("charset");
72968
+ headers.set("Content-Type", needsCharset ? `${rawType}; charset=utf-8` : rawType);
72934
72969
  const ext = resolved.split(".").pop()?.toLowerCase() || "";
72935
72970
  if (["js", "css", "png", "jpg", "jpeg", "gif", "svg", "ico", "woff", "woff2"].includes(ext)) {
72936
72971
  headers.set("Cache-Control", "public, max-age=31536000, immutable");
@@ -73207,7 +73242,11 @@ async function main() {
73207
73242
  for (const [name21, mut] of mutationMap) {
73208
73243
  scheduler.registerAction(name21, async (args) => {
73209
73244
  const ctx = buildCtx({ get: () => null });
73210
- await mut.handler(ctx, args ?? {});
73245
+ await executeWithTimeout(
73246
+ () => mut.handler(ctx, args ?? {}),
73247
+ "mutation",
73248
+ name21
73249
+ );
73211
73250
  });
73212
73251
  }
73213
73252
  console.log(`[scheduler] Registered ${mutationMap.size} mutation(s) as scheduler actions`);
@@ -73234,7 +73273,11 @@ async function main() {
73234
73273
  if (mut) {
73235
73274
  try {
73236
73275
  const ctx = buildCtx({ get: () => null });
73237
- await mut.handler(ctx, {});
73276
+ await executeWithTimeout(
73277
+ () => mut.handler(ctx, {}),
73278
+ "cron",
73279
+ actionName
73280
+ );
73238
73281
  console.log(`[scheduler] Cron action "${actionName}" (mutation) completed`);
73239
73282
  } catch (err) {
73240
73283
  const msg = err instanceof Error ? err.message : JSON.stringify(err, null, 2);
@@ -73272,7 +73315,11 @@ async function main() {
73272
73315
  }
73273
73316
  try {
73274
73317
  const validatedArgs = parseArgs(queryDef.argsSchema, args);
73275
- const result = await queryDef.handler(ctx, validatedArgs);
73318
+ const result = await executeWithTimeout(
73319
+ () => queryDef.handler(ctx, validatedArgs),
73320
+ "query",
73321
+ name21
73322
+ );
73276
73323
  if (queryDef.isPublic && !IS_BAAS && Array.isArray(result) && result.length >= 100) {
73277
73324
  if (!publicQueryWarned.has(name21)) {
73278
73325
  publicQueryWarned.add(name21);
@@ -73282,8 +73329,8 @@ async function main() {
73282
73329
  }
73283
73330
  return c.json(result);
73284
73331
  } catch (err) {
73285
- const status = err instanceof GencowValidationError ? 400 : 500;
73286
- return c.json({ error: err.message }, status);
73332
+ const status = err?.code === "FUNCTION_TIMEOUT" ? 408 : err instanceof GencowValidationError ? 400 : 500;
73333
+ return c.json({ error: err.message, ...err?.code ? { code: err.code } : {} }, status);
73287
73334
  }
73288
73335
  }).post("/mutation", async (c) => {
73289
73336
  let name21, args;
@@ -73309,12 +73356,16 @@ async function main() {
73309
73356
  }
73310
73357
  try {
73311
73358
  const validatedArgs = parseArgs(mut.argsSchema, args);
73312
- const result = await mut.handler(ctx, validatedArgs);
73359
+ const result = await executeWithTimeout(
73360
+ () => mut.handler(ctx, validatedArgs),
73361
+ "mutation",
73362
+ name21
73363
+ );
73313
73364
  await invalidateQueries(mut.invalidates, ctx);
73314
73365
  return c.json(result, 201);
73315
73366
  } catch (err) {
73316
- const status = err instanceof GencowValidationError ? 400 : 500;
73317
- return c.json({ error: err.message }, status);
73367
+ const status = err?.code === "FUNCTION_TIMEOUT" ? 408 : err instanceof GencowValidationError ? 400 : 500;
73368
+ return c.json({ error: err.message, ...err?.code ? { code: err.code } : {} }, status);
73318
73369
  }
73319
73370
  }).get("/storage/:id", storageRoutes(storage, rawSql, storageDir));
73320
73371
  const queryNames = getRegisteredQueries();
@@ -73379,7 +73430,11 @@ async function main() {
73379
73430
  text: () => c.req.text()
73380
73431
  };
73381
73432
  try {
73382
- const result = await action.handler(ctx, req);
73433
+ const result = await executeWithTimeout(
73434
+ () => action.handler(ctx, req),
73435
+ "httpAction",
73436
+ `${action.method} ${action.path}`
73437
+ );
73383
73438
  if (result instanceof Response) {
73384
73439
  return result;
73385
73440
  }