mcp-scraper 0.32.1 → 0.32.3

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.
Files changed (41) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/api-server.cjs +434 -252
  3. package/dist/bin/api-server.cjs.map +1 -1
  4. package/dist/bin/api-server.js +2 -2
  5. package/dist/bin/mcp-scraper-cli.cjs +3 -1
  6. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  7. package/dist/bin/mcp-scraper-cli.js +3 -3
  8. package/dist/bin/mcp-scraper-install.cjs +1 -1
  9. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  10. package/dist/bin/mcp-scraper-install.js +1 -1
  11. package/dist/bin/mcp-stdio-server.cjs +44 -1
  12. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  13. package/dist/bin/mcp-stdio-server.js +3 -3
  14. package/dist/bin/paa-harvest.cjs +4 -2
  15. package/dist/bin/paa-harvest.cjs.map +1 -1
  16. package/dist/bin/paa-harvest.js +2 -2
  17. package/dist/{chunk-RDCCU7CQ.js → chunk-AEMABLEL.js} +5 -4
  18. package/dist/chunk-AEMABLEL.js.map +1 -0
  19. package/dist/{chunk-2FY4VBAY.js → chunk-GNRHBIYZ.js} +44 -3
  20. package/dist/chunk-GNRHBIYZ.js.map +1 -0
  21. package/dist/{chunk-55M5CE5L.js → chunk-MKZIL4EA.js} +2 -2
  22. package/dist/{chunk-PTHY7NYD.js → chunk-V73MPRU6.js} +3 -1
  23. package/dist/chunk-V73MPRU6.js.map +1 -0
  24. package/dist/chunk-XF46AB5C.js +7 -0
  25. package/dist/chunk-XF46AB5C.js.map +1 -0
  26. package/dist/index.cjs +4 -2
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.js +2 -2
  29. package/dist/{server-GXTYKIAX.js → server-NHLEIKDZ.js} +386 -246
  30. package/dist/server-NHLEIKDZ.js.map +1 -0
  31. package/dist/{worker-7DJYUACV.js → worker-ZZCZNY3R.js} +3 -3
  32. package/docs/mcp-tool-manifest.generated.json +165 -3
  33. package/package.json +1 -1
  34. package/dist/chunk-2FY4VBAY.js.map +0 -1
  35. package/dist/chunk-PTHY7NYD.js.map +0 -1
  36. package/dist/chunk-RDCCU7CQ.js.map +0 -1
  37. package/dist/chunk-VJBS6RPB.js +0 -7
  38. package/dist/chunk-VJBS6RPB.js.map +0 -1
  39. package/dist/server-GXTYKIAX.js.map +0 -1
  40. /package/dist/{chunk-55M5CE5L.js.map → chunk-MKZIL4EA.js.map} +0 -0
  41. /package/dist/{worker-7DJYUACV.js.map → worker-ZZCZNY3R.js.map} +0 -0
@@ -15,8 +15,9 @@ import {
15
15
  encodeUule,
16
16
  harvest,
17
17
  normalizeLocation,
18
- resolveKernelProxyId
19
- } from "./chunk-RDCCU7CQ.js";
18
+ resolveKernelProxyId,
19
+ serpArrivalUrl
20
+ } from "./chunk-AEMABLEL.js";
20
21
  import {
21
22
  HttpMcpToolExecutor,
22
23
  assessTranscriptSignal,
@@ -36,7 +37,7 @@ import {
36
37
  sanitizeAttempts,
37
38
  sanitizeHarvestResult,
38
39
  transcribeMediaUrl
39
- } from "./chunk-2FY4VBAY.js";
40
+ } from "./chunk-GNRHBIYZ.js";
40
41
  import {
41
42
  auditImages,
42
43
  buildLinkReport,
@@ -65,7 +66,7 @@ import {
65
66
  workflowOutputBaseDir,
66
67
  workflowStepCount,
67
68
  workflowSupportsSteps
68
- } from "./chunk-55M5CE5L.js";
69
+ } from "./chunk-MKZIL4EA.js";
69
70
  import "./chunk-K443GQY5.js";
70
71
  import {
71
72
  DEFAULT_MAPS_PROXY_MODE,
@@ -76,8 +77,8 @@ import {
76
77
  RawMapsHoursRowSchema,
77
78
  RawMapsOverviewSchema,
78
79
  RawMapsReviewStatsSchema
79
- } from "./chunk-PTHY7NYD.js";
80
- import "./chunk-VJBS6RPB.js";
80
+ } from "./chunk-V73MPRU6.js";
81
+ import "./chunk-XF46AB5C.js";
81
82
  import {
82
83
  completeExtractJob,
83
84
  countSuccessfulPages,
@@ -13083,6 +13084,7 @@ import { Hono as Hono11 } from "hono";
13083
13084
  // src/extractor/MapsSearchExtractor.ts
13084
13085
  var LOCAL_RESULTS_PAGE_LIMIT = 8;
13085
13086
  var LOCAL_RESULTS_WAIT_MS = 1e3;
13087
+ var MAPS_REDIRECT_BASE = "https://serp-redirector.vercel.app";
13086
13088
  function buildGoogleLocalResultsUrl(input) {
13087
13089
  const query = [input.query, input.location].filter(Boolean).join(" ");
13088
13090
  const params = new URLSearchParams({ q: query, udm: "1", gl: input.gl, hl: input.hl, pws: "0" });
@@ -13095,6 +13097,22 @@ function buildGoogleOrganicSearchUrl(input) {
13095
13097
  if (input.location) params.set("uule", encodeUule(normalizeLocation(input.location)));
13096
13098
  return `https://www.google.com/search?${params.toString()}`;
13097
13099
  }
13100
+ function mapsIdentifiersFromUrl(url) {
13101
+ const fid = url.match(/!1s(0x[0-9a-f]+):(0x[0-9a-f]+)/i) ?? url.match(/(0x[0-9a-f]+):(0x[0-9a-f]+)/i);
13102
+ if (fid) {
13103
+ try {
13104
+ return { cid: `${fid[1]}:${fid[2]}`, cidDecimal: BigInt(fid[2]).toString() };
13105
+ } catch {
13106
+ return { cid: `${fid[1]}:${fid[2]}`, cidDecimal: null };
13107
+ }
13108
+ }
13109
+ try {
13110
+ const decimal = new URL(url).searchParams.get("cid");
13111
+ return { cid: null, cidDecimal: decimal && /^\d+$/.test(decimal) ? decimal : null };
13112
+ } catch {
13113
+ return { cid: null, cidDecimal: null };
13114
+ }
13115
+ }
13098
13116
  var MapsSearchExtractor = class {
13099
13117
  constructor(driver) {
13100
13118
  this.driver = driver;
@@ -13104,21 +13122,23 @@ var MapsSearchExtractor = class {
13104
13122
  const startMs = Date.now();
13105
13123
  const searchQuery = [options.query, options.location].filter(Boolean).join(" ");
13106
13124
  const searchUrl = buildGoogleOrganicSearchUrl(options);
13125
+ const keepDefaultProxy = !options.forceDirectEgress && options.proxyMode !== "location" && options.proxyMode !== "configured" && !options.kernelProxyId;
13107
13126
  const config = {
13108
13127
  headless: options.headless,
13109
13128
  kernelApiKey: options.kernelApiKey,
13110
13129
  kernelProxyId: options.kernelProxyId,
13111
13130
  kernelProxyResolution: options.kernelProxyResolution,
13112
13131
  proxyMode: options.proxyMode,
13113
- keepDefaultProxy: options.proxyMode !== "location" && options.proxyMode !== "configured" && !options.kernelProxyId,
13132
+ keepDefaultProxy,
13114
13133
  viewport: { width: 1280, height: 900 },
13115
13134
  locale: `${options.hl}-${options.gl.toUpperCase()}`,
13116
13135
  debug: options.debug
13117
13136
  };
13137
+ const navUrl = options.serpRedirect ? serpArrivalUrl(searchUrl, process.env.SERP_REDIRECT_BASE?.trim() || MAPS_REDIRECT_BASE) : searchUrl;
13118
13138
  try {
13119
13139
  await this.driver.launch(config);
13120
13140
  const page = this.driver.getPage();
13121
- await page.goto(searchUrl, { waitUntil: "domcontentloaded", timeout: 6e4 });
13141
+ await page.goto(navUrl, { waitUntil: "domcontentloaded", timeout: 6e4 });
13122
13142
  await page.waitForTimeout(LOCAL_RESULTS_WAIT_MS);
13123
13143
  if (await this.detectBlock(page)) throw new CaptchaError(RECAPTCHA_INSTRUCTIONS);
13124
13144
  const reachedFinder = await this.clickIntoLocalFinder(page);
@@ -13168,13 +13188,15 @@ var MapsSearchExtractor = class {
13168
13188
  for (const card of cards) {
13169
13189
  if (results.length >= options.maxResults || seen.has(card.cardKey)) continue;
13170
13190
  seen.add(card.cardKey);
13191
+ const cardIds = mapsIdentifiersFromUrl(card.directionsUrl ?? "");
13192
+ const cardPlaceUrl = cardIds.cidDecimal ? `https://www.google.com/maps?cid=${cardIds.cidDecimal}` : card.placeUrl;
13171
13193
  const details = await this.openCardAndExtractDialog(page, card, options.includeServices);
13172
13194
  results.push({
13173
13195
  position: results.length + 1,
13174
13196
  name: card.name,
13175
- placeUrl: details?.placeUrl ?? card.placeUrl,
13176
- cid: details?.cid ?? card.cid,
13177
- cidDecimal: details?.cidDecimal ?? card.cidDecimal,
13197
+ placeUrl: details?.placeUrl ?? cardPlaceUrl,
13198
+ cid: details?.cid ?? cardIds.cid,
13199
+ cidDecimal: details?.cidDecimal ?? cardIds.cidDecimal,
13178
13200
  rating: details?.rating ?? card.rating,
13179
13201
  reviewCount: details?.reviewCount ?? card.reviewCount,
13180
13202
  category: details?.category ?? card.category,
@@ -13231,8 +13253,10 @@ var MapsSearchExtractor = class {
13231
13253
  const phone = lines.map((line) => line.match(phonePattern)?.[0]).find((value) => Boolean(value)) ?? null;
13232
13254
  const hoursStatus = normalize((lines.find((line) => /\b(?:open|closed|opens|closes)\b/i.test(line)) ?? "").split("\xB7")[0] ?? "");
13233
13255
  const address = lines.find((line) => addressPattern.test(line)) ?? null;
13234
- const websiteUrl = Array.from(card.querySelectorAll("a[href]")).map((anchor) => anchor.href).find((href) => /^https?:/i.test(href) && !/google\.|gstatic|googleusercontent/.test(href)) ?? null;
13256
+ const anchorHrefs = Array.from(card.querySelectorAll("a[href]")).map((anchor) => anchor.href);
13257
+ const websiteUrl = anchorHrefs.find((href) => /^https?:/i.test(href) && !/google\.|gstatic|googleusercontent/.test(href)) ?? null;
13235
13258
  if (!rating && !(phone && websiteUrl)) continue;
13259
+ const fidHref = anchorHrefs.find((href) => /(0x[0-9a-f]+):(0x[0-9a-f]+)/i.test(href)) ?? null;
13236
13260
  const key = `${name.toLowerCase()}|${lines.join(" ").toLowerCase()}`;
13237
13261
  if (seen.has(key)) continue;
13238
13262
  seen.add(key);
@@ -13252,7 +13276,7 @@ var MapsSearchExtractor = class {
13252
13276
  phone,
13253
13277
  hoursStatus,
13254
13278
  websiteUrl,
13255
- directionsUrl: `https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent([name, address].filter(Boolean).join(", "))}`,
13279
+ directionsUrl: fidHref ?? `https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent([name, address].filter(Boolean).join(", "))}`,
13256
13280
  metadata: lines.slice(0, 20)
13257
13281
  });
13258
13282
  }
@@ -13978,7 +14002,9 @@ async function runMapsSearchWithRotation(options) {
13978
14002
  ...options,
13979
14003
  proxyZip,
13980
14004
  kernelProxyId: options.proxyMode === "none" ? void 0 : resolution.kernelProxyId,
13981
- kernelProxyResolution: resolution.resolution
14005
+ kernelProxyResolution: resolution.resolution,
14006
+ serpRedirect: options.proxyMode === "none" && attemptIndex >= 1 && attemptIndex < maxAttempts - 1,
14007
+ forceDirectEgress: options.proxyMode === "none" && attemptIndex >= 1
13982
14008
  };
13983
14009
  const isHeadfulEscalation = attemptIndex === maxAttempts - 1;
13984
14010
  const baseCtx = currentCostContext();
@@ -19548,9 +19574,222 @@ if (state.key) { refreshSessions(); if (state.current) selectSession(state.curre
19548
19574
  }
19549
19575
 
19550
19576
  // src/api/stripe-routes.ts
19551
- import Stripe from "stripe";
19577
+ import Stripe2 from "stripe";
19552
19578
  import { Hono as Hono19 } from "hono";
19553
- var stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { apiVersion: "2026-02-25.clover" });
19579
+
19580
+ // src/api/connected-account-billing.ts
19581
+ import Stripe from "stripe";
19582
+ var DEFAULT_CONNECTED_ACCOUNT_PRICE_ID = "price_1TtYPAS8aAcsk3TGHOgrDZiQ";
19583
+ var STRIPE_API_VERSION = "2026-02-25.clover";
19584
+ async function ensureConnectedAccountBillingSchema() {
19585
+ await getDb().execute(`CREATE TABLE IF NOT EXISTS connected_account_billing (
19586
+ user_id INTEGER PRIMARY KEY,
19587
+ stripe_subscription_id TEXT,
19588
+ stripe_subscription_item_id TEXT,
19589
+ price_id TEXT,
19590
+ quantity INTEGER NOT NULL DEFAULT 0,
19591
+ status TEXT NOT NULL DEFAULT 'pending',
19592
+ last_error_code TEXT,
19593
+ synced_at TEXT,
19594
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
19595
+ )`);
19596
+ }
19597
+ async function getConnectedAccountBillingState(userId) {
19598
+ await ensureConnectedAccountBillingSchema();
19599
+ const result = await getDb().execute({
19600
+ sql: "SELECT * FROM connected_account_billing WHERE user_id = ? LIMIT 1",
19601
+ args: [Number(userId)]
19602
+ });
19603
+ return result.rows[0] ? result.rows[0] : null;
19604
+ }
19605
+ async function setConnectedAccountBillingState(input) {
19606
+ await ensureConnectedAccountBillingSchema();
19607
+ await getDb().execute({
19608
+ sql: `INSERT INTO connected_account_billing
19609
+ (user_id, stripe_subscription_id, stripe_subscription_item_id, price_id, quantity, status, last_error_code, synced_at, updated_at)
19610
+ VALUES (?, ?, ?, ?, ?, ?, ?, CASE WHEN ? THEN datetime('now') ELSE NULL END, datetime('now'))
19611
+ ON CONFLICT(user_id) DO UPDATE SET
19612
+ stripe_subscription_id = excluded.stripe_subscription_id,
19613
+ stripe_subscription_item_id = excluded.stripe_subscription_item_id,
19614
+ price_id = excluded.price_id,
19615
+ quantity = excluded.quantity,
19616
+ status = excluded.status,
19617
+ last_error_code = excluded.last_error_code,
19618
+ synced_at = CASE WHEN ? THEN datetime('now') ELSE connected_account_billing.synced_at END,
19619
+ updated_at = datetime('now')`,
19620
+ args: [
19621
+ Number(input.userId),
19622
+ input.stripeSubscriptionId,
19623
+ input.stripeSubscriptionItemId,
19624
+ input.priceId,
19625
+ input.quantity,
19626
+ input.status,
19627
+ input.lastErrorCode,
19628
+ input.synced ? 1 : 0,
19629
+ input.synced ? 1 : 0
19630
+ ]
19631
+ });
19632
+ const state = await getConnectedAccountBillingState(input.userId);
19633
+ if (!state) throw new Error("connected account billing state was not persisted");
19634
+ return state;
19635
+ }
19636
+ var ConnectedAccountBillingError = class extends Error {
19637
+ constructor(message, code, status = 409) {
19638
+ super(message);
19639
+ this.code = code;
19640
+ this.status = status;
19641
+ this.name = "ConnectedAccountBillingError";
19642
+ }
19643
+ code;
19644
+ status;
19645
+ };
19646
+ function connectedAccountPriceId() {
19647
+ const configured = process.env.CONNECTED_ACCOUNT_PRICE_ID?.trim();
19648
+ if (configured) return configured;
19649
+ return DEFAULT_CONNECTED_ACCOUNT_PRICE_ID || null;
19650
+ }
19651
+ function connectedAccountBillingView(actualQuantity, state) {
19652
+ const quantity = Math.max(0, Math.round(actualQuantity));
19653
+ return {
19654
+ provider: "nango",
19655
+ billingMode: "flat_recurring_usd_plus_credits",
19656
+ priceId: connectedAccountPriceId(),
19657
+ unitAmountUsd: CONNECTED_ACTIVE_CONNECTION_MONTHLY_USD,
19658
+ interval: "month",
19659
+ quantity,
19660
+ projectedMonthlyUsd: quantity * CONNECTED_ACTIVE_CONNECTION_MONTHLY_USD,
19661
+ status: state?.status ?? "pending",
19662
+ lastErrorCode: state?.last_error_code ?? null,
19663
+ syncedAt: state?.synced_at ?? null,
19664
+ usage: CONNECTED_USAGE_RATE_POLICY.usage
19665
+ };
19666
+ }
19667
+ function findBasePlanItem(subscription) {
19668
+ return subscription.items.data.find((item) => !!item.price?.id && item.price.id in SUBSCRIPTION_TIERS);
19669
+ }
19670
+ function findConnectedAccountItem(subscription, priceId = connectedAccountPriceId()) {
19671
+ return priceId ? subscription.items.data.find((item) => item.price?.id === priceId) : void 0;
19672
+ }
19673
+ function liveSubscription(subscription) {
19674
+ return subscription.status === "active" || subscription.status === "trialing";
19675
+ }
19676
+ function safeStripeErrorCode(error) {
19677
+ if (error instanceof ConnectedAccountBillingError) return error.code;
19678
+ return "stripe_connection_billing_failed";
19679
+ }
19680
+ async function persistFailure(user, actualQuantity, error, subscriptionItemId = null) {
19681
+ await setConnectedAccountBillingState({
19682
+ userId: user.id,
19683
+ stripeSubscriptionId: user.subscription_id,
19684
+ stripeSubscriptionItemId: subscriptionItemId,
19685
+ priceId: connectedAccountPriceId(),
19686
+ quantity: actualQuantity,
19687
+ status: error instanceof ConnectedAccountBillingError && error.code !== "stripe_connection_billing_failed" ? "blocked" : "error",
19688
+ lastErrorCode: safeStripeErrorCode(error),
19689
+ synced: false
19690
+ });
19691
+ }
19692
+ async function reconcileConnectedAccountBilling(user, actualQuantity, stripeClient) {
19693
+ if (!Number.isSafeInteger(actualQuantity) || actualQuantity < 0) {
19694
+ throw new Error("actualQuantity must be a non-negative safe integer");
19695
+ }
19696
+ const priceId = connectedAccountPriceId();
19697
+ if (!priceId) {
19698
+ const error = new ConnectedAccountBillingError(
19699
+ "Connected-account billing is not configured.",
19700
+ "connected_account_price_not_configured",
19701
+ 503
19702
+ );
19703
+ await persistFailure(user, actualQuantity, error);
19704
+ throw error;
19705
+ }
19706
+ if (!user.subscription_id) {
19707
+ if (actualQuantity === 0) {
19708
+ const state = await setConnectedAccountBillingState({
19709
+ userId: user.id,
19710
+ stripeSubscriptionId: null,
19711
+ stripeSubscriptionItemId: null,
19712
+ priceId,
19713
+ quantity: 0,
19714
+ status: "synced",
19715
+ lastErrorCode: null,
19716
+ synced: true
19717
+ });
19718
+ return connectedAccountBillingView(0, state);
19719
+ }
19720
+ const error = new ConnectedAccountBillingError(
19721
+ "An active paid plan is required before connected accounts can be billed.",
19722
+ "paid_plan_required",
19723
+ 403
19724
+ );
19725
+ await persistFailure(user, actualQuantity, error);
19726
+ throw error;
19727
+ }
19728
+ const client2 = stripeClient ?? (() => {
19729
+ const secret2 = process.env.STRIPE_SECRET_KEY?.trim();
19730
+ if (!secret2) {
19731
+ throw new ConnectedAccountBillingError(
19732
+ "Stripe is not configured.",
19733
+ "connected_account_price_not_configured",
19734
+ 503
19735
+ );
19736
+ }
19737
+ return new Stripe(secret2, { apiVersion: STRIPE_API_VERSION });
19738
+ })();
19739
+ let currentItem;
19740
+ try {
19741
+ const subscription = await client2.subscriptions.retrieve(user.subscription_id);
19742
+ currentItem = findConnectedAccountItem(subscription, priceId);
19743
+ if (!liveSubscription(subscription)) {
19744
+ throw new ConnectedAccountBillingError(
19745
+ "The plan subscription is not active.",
19746
+ "subscription_not_active",
19747
+ 402
19748
+ );
19749
+ }
19750
+ if (!findBasePlanItem(subscription)) {
19751
+ throw new ConnectedAccountBillingError(
19752
+ "The base plan item could not be found on this subscription.",
19753
+ "subscription_plan_item_missing",
19754
+ 409
19755
+ );
19756
+ }
19757
+ const currentQuantity = Math.max(0, currentItem?.quantity ?? 0);
19758
+ let resultingItem = currentItem;
19759
+ if (actualQuantity !== currentQuantity) {
19760
+ const updated = await client2.subscriptions.update(subscription.id, {
19761
+ items: actualQuantity === 0 ? currentItem ? [{ id: currentItem.id, deleted: true }] : [] : currentItem ? [{ id: currentItem.id, quantity: actualQuantity }] : [{ price: priceId, quantity: actualQuantity }],
19762
+ proration_behavior: "create_prorations"
19763
+ });
19764
+ resultingItem = findConnectedAccountItem(updated, priceId);
19765
+ }
19766
+ const state = await setConnectedAccountBillingState({
19767
+ userId: user.id,
19768
+ stripeSubscriptionId: subscription.id,
19769
+ stripeSubscriptionItemId: resultingItem?.id ?? null,
19770
+ priceId,
19771
+ quantity: actualQuantity,
19772
+ status: "synced",
19773
+ lastErrorCode: null,
19774
+ synced: true
19775
+ });
19776
+ return connectedAccountBillingView(actualQuantity, state);
19777
+ } catch (error) {
19778
+ await persistFailure(user, actualQuantity, error, currentItem?.id ?? null);
19779
+ if (error instanceof ConnectedAccountBillingError) throw error;
19780
+ throw new ConnectedAccountBillingError(
19781
+ "Unable to update connected-account billing in Stripe.",
19782
+ "stripe_connection_billing_failed",
19783
+ 503
19784
+ );
19785
+ }
19786
+ }
19787
+ async function currentConnectedAccountBillingView(userId, actualQuantity) {
19788
+ return connectedAccountBillingView(actualQuantity, await getConnectedAccountBillingState(userId));
19789
+ }
19790
+
19791
+ // src/api/stripe-routes.ts
19792
+ var stripe = new Stripe2(process.env.STRIPE_SECRET_KEY, { apiVersion: "2026-02-25.clover" });
19554
19793
  var stripeApp = new Hono19();
19555
19794
  stripeApp.post("/webhooks", async (c) => {
19556
19795
  const sig = c.req.header("stripe-signature");
@@ -19567,15 +19806,23 @@ stripeApp.post("/webhooks", async (c) => {
19567
19806
  const invoice = event.data.object;
19568
19807
  const memLineId = invoice.lines.data.map(linePriceId).find((id) => id && id in MEMORY_PLANS);
19569
19808
  if (memLineId) return c.json({ received: true });
19570
- const lineTierId = invoice.lines.data.map(linePriceId).find((id) => id && id in SUBSCRIPTION_TIERS);
19571
- if (lineTierId) {
19572
- const tier = SUBSCRIPTION_TIERS[lineTierId];
19573
- const user = await resolveUser2(invoice.customer, invoice.customer_email ?? void 0);
19574
- if (user && invoice.id && !await ledgerExistsForStripePI(invoice.id)) {
19575
- await creditMc(user.id, tier.credits_mc, LedgerOperation.SUBSCRIPTION, `${tier.label} subscription credits`, invoice.id);
19576
- await setSubscriptionTier(user.id, tier.tier, tier.concurrency, invoice.subscription ?? user.subscription_id);
19577
- const credentials = await syncScheduledActionCredentials(user);
19578
- if (!credentials.ok) console.warn("[stripe] scheduled-action credential sync failed:", credentials.error ?? "unknown error");
19809
+ const invoiceHasTierLine = invoice.lines.data.some((l) => {
19810
+ const id = linePriceId(l);
19811
+ return id && id in SUBSCRIPTION_TIERS;
19812
+ });
19813
+ if (invoiceHasTierLine) {
19814
+ const subId = invoice.subscription;
19815
+ const liveBasePlanPriceId = subId ? findBasePlanItem(await stripe.subscriptions.retrieve(subId))?.price?.id : void 0;
19816
+ const tierPriceId = liveBasePlanPriceId ?? invoice.lines.data.map(linePriceId).find((id) => id && id in SUBSCRIPTION_TIERS);
19817
+ const tier = tierPriceId ? SUBSCRIPTION_TIERS[tierPriceId] : void 0;
19818
+ if (tier) {
19819
+ const user = await resolveUser2(invoice.customer, invoice.customer_email ?? void 0);
19820
+ if (user && invoice.id && !await ledgerExistsForStripePI(invoice.id)) {
19821
+ await creditMc(user.id, tier.credits_mc, LedgerOperation.SUBSCRIPTION, `${tier.label} subscription credits`, invoice.id);
19822
+ await setSubscriptionTier(user.id, tier.tier, tier.concurrency, subId ?? user.subscription_id);
19823
+ const credentials = await syncScheduledActionCredentials(user);
19824
+ if (!credentials.ok) console.warn("[stripe] scheduled-action credential sync failed:", credentials.error ?? "unknown error");
19825
+ }
19579
19826
  }
19580
19827
  }
19581
19828
  }
@@ -22826,8 +23073,8 @@ async function reconcileDiscoveredNangoConnections(identity, discovered) {
22826
23073
  sql: `
22827
23074
  INSERT INTO service_connections (
22828
23075
  id, user_id, provider_config_key, provider, transport, upstream_connection_id,
22829
- label, lifecycle_status, reconnect_required, legacy_owner_identity, created_at, updated_at
22830
- ) VALUES (?, ?, ?, ?, 'nango', ?, ?, ?, ?, ?, COALESCE(?, datetime('now')), COALESCE(?, datetime('now')))
23076
+ label, lifecycle_status, reconnect_required, actions_enabled, legacy_owner_identity, created_at, updated_at
23077
+ ) VALUES (?, ?, ?, ?, 'nango', ?, ?, ?, ?, 1, ?, COALESCE(?, datetime('now')), COALESCE(?, datetime('now')))
22831
23078
  ON CONFLICT(user_id, provider_config_key, upstream_connection_id) DO UPDATE SET
22832
23079
  provider = excluded.provider,
22833
23080
  label = COALESCE(excluded.label, service_connections.label),
@@ -24896,217 +25143,6 @@ async function verifySchedulerIntegrationRequest(request, rawBody) {
24896
25143
  return { requestId };
24897
25144
  }
24898
25145
 
24899
- // src/api/connected-account-billing.ts
24900
- import Stripe2 from "stripe";
24901
- var DEFAULT_CONNECTED_ACCOUNT_PRICE_ID = "price_1TtYPAS8aAcsk3TGHOgrDZiQ";
24902
- var STRIPE_API_VERSION = "2026-02-25.clover";
24903
- async function ensureConnectedAccountBillingSchema() {
24904
- await getDb().execute(`CREATE TABLE IF NOT EXISTS connected_account_billing (
24905
- user_id INTEGER PRIMARY KEY,
24906
- stripe_subscription_id TEXT,
24907
- stripe_subscription_item_id TEXT,
24908
- price_id TEXT,
24909
- quantity INTEGER NOT NULL DEFAULT 0,
24910
- status TEXT NOT NULL DEFAULT 'pending',
24911
- last_error_code TEXT,
24912
- synced_at TEXT,
24913
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
24914
- )`);
24915
- }
24916
- async function getConnectedAccountBillingState(userId) {
24917
- await ensureConnectedAccountBillingSchema();
24918
- const result = await getDb().execute({
24919
- sql: "SELECT * FROM connected_account_billing WHERE user_id = ? LIMIT 1",
24920
- args: [Number(userId)]
24921
- });
24922
- return result.rows[0] ? result.rows[0] : null;
24923
- }
24924
- async function setConnectedAccountBillingState(input) {
24925
- await ensureConnectedAccountBillingSchema();
24926
- await getDb().execute({
24927
- sql: `INSERT INTO connected_account_billing
24928
- (user_id, stripe_subscription_id, stripe_subscription_item_id, price_id, quantity, status, last_error_code, synced_at, updated_at)
24929
- VALUES (?, ?, ?, ?, ?, ?, ?, CASE WHEN ? THEN datetime('now') ELSE NULL END, datetime('now'))
24930
- ON CONFLICT(user_id) DO UPDATE SET
24931
- stripe_subscription_id = excluded.stripe_subscription_id,
24932
- stripe_subscription_item_id = excluded.stripe_subscription_item_id,
24933
- price_id = excluded.price_id,
24934
- quantity = excluded.quantity,
24935
- status = excluded.status,
24936
- last_error_code = excluded.last_error_code,
24937
- synced_at = CASE WHEN ? THEN datetime('now') ELSE connected_account_billing.synced_at END,
24938
- updated_at = datetime('now')`,
24939
- args: [
24940
- Number(input.userId),
24941
- input.stripeSubscriptionId,
24942
- input.stripeSubscriptionItemId,
24943
- input.priceId,
24944
- input.quantity,
24945
- input.status,
24946
- input.lastErrorCode,
24947
- input.synced ? 1 : 0,
24948
- input.synced ? 1 : 0
24949
- ]
24950
- });
24951
- const state = await getConnectedAccountBillingState(input.userId);
24952
- if (!state) throw new Error("connected account billing state was not persisted");
24953
- return state;
24954
- }
24955
- var ConnectedAccountBillingError = class extends Error {
24956
- constructor(message, code, status = 409) {
24957
- super(message);
24958
- this.code = code;
24959
- this.status = status;
24960
- this.name = "ConnectedAccountBillingError";
24961
- }
24962
- code;
24963
- status;
24964
- };
24965
- function connectedAccountPriceId() {
24966
- const configured = process.env.CONNECTED_ACCOUNT_PRICE_ID?.trim();
24967
- if (configured) return configured;
24968
- return DEFAULT_CONNECTED_ACCOUNT_PRICE_ID || null;
24969
- }
24970
- function connectedAccountBillingView(actualQuantity, state) {
24971
- const quantity = Math.max(0, Math.round(actualQuantity));
24972
- return {
24973
- provider: "nango",
24974
- billingMode: "flat_recurring_usd_plus_credits",
24975
- priceId: connectedAccountPriceId(),
24976
- unitAmountUsd: CONNECTED_ACTIVE_CONNECTION_MONTHLY_USD,
24977
- interval: "month",
24978
- quantity,
24979
- projectedMonthlyUsd: quantity * CONNECTED_ACTIVE_CONNECTION_MONTHLY_USD,
24980
- status: state?.status ?? "pending",
24981
- lastErrorCode: state?.last_error_code ?? null,
24982
- syncedAt: state?.synced_at ?? null,
24983
- usage: CONNECTED_USAGE_RATE_POLICY.usage
24984
- };
24985
- }
24986
- function findBasePlanItem(subscription) {
24987
- return subscription.items.data.find((item) => !!item.price?.id && item.price.id in SUBSCRIPTION_TIERS);
24988
- }
24989
- function findConnectedAccountItem(subscription, priceId = connectedAccountPriceId()) {
24990
- return priceId ? subscription.items.data.find((item) => item.price?.id === priceId) : void 0;
24991
- }
24992
- function liveSubscription(subscription) {
24993
- return subscription.status === "active" || subscription.status === "trialing";
24994
- }
24995
- function safeStripeErrorCode(error) {
24996
- if (error instanceof ConnectedAccountBillingError) return error.code;
24997
- return "stripe_connection_billing_failed";
24998
- }
24999
- async function persistFailure(user, actualQuantity, error, subscriptionItemId = null) {
25000
- await setConnectedAccountBillingState({
25001
- userId: user.id,
25002
- stripeSubscriptionId: user.subscription_id,
25003
- stripeSubscriptionItemId: subscriptionItemId,
25004
- priceId: connectedAccountPriceId(),
25005
- quantity: actualQuantity,
25006
- status: error instanceof ConnectedAccountBillingError && error.code !== "stripe_connection_billing_failed" ? "blocked" : "error",
25007
- lastErrorCode: safeStripeErrorCode(error),
25008
- synced: false
25009
- });
25010
- }
25011
- async function reconcileConnectedAccountBilling(user, actualQuantity, stripeClient) {
25012
- if (!Number.isSafeInteger(actualQuantity) || actualQuantity < 0) {
25013
- throw new Error("actualQuantity must be a non-negative safe integer");
25014
- }
25015
- const priceId = connectedAccountPriceId();
25016
- if (!priceId) {
25017
- const error = new ConnectedAccountBillingError(
25018
- "Connected-account billing is not configured.",
25019
- "connected_account_price_not_configured",
25020
- 503
25021
- );
25022
- await persistFailure(user, actualQuantity, error);
25023
- throw error;
25024
- }
25025
- if (!user.subscription_id) {
25026
- if (actualQuantity === 0) {
25027
- const state = await setConnectedAccountBillingState({
25028
- userId: user.id,
25029
- stripeSubscriptionId: null,
25030
- stripeSubscriptionItemId: null,
25031
- priceId,
25032
- quantity: 0,
25033
- status: "synced",
25034
- lastErrorCode: null,
25035
- synced: true
25036
- });
25037
- return connectedAccountBillingView(0, state);
25038
- }
25039
- const error = new ConnectedAccountBillingError(
25040
- "An active paid plan is required before connected accounts can be billed.",
25041
- "paid_plan_required",
25042
- 403
25043
- );
25044
- await persistFailure(user, actualQuantity, error);
25045
- throw error;
25046
- }
25047
- const client2 = stripeClient ?? (() => {
25048
- const secret2 = process.env.STRIPE_SECRET_KEY?.trim();
25049
- if (!secret2) {
25050
- throw new ConnectedAccountBillingError(
25051
- "Stripe is not configured.",
25052
- "connected_account_price_not_configured",
25053
- 503
25054
- );
25055
- }
25056
- return new Stripe2(secret2, { apiVersion: STRIPE_API_VERSION });
25057
- })();
25058
- let currentItem;
25059
- try {
25060
- const subscription = await client2.subscriptions.retrieve(user.subscription_id);
25061
- currentItem = findConnectedAccountItem(subscription, priceId);
25062
- if (!liveSubscription(subscription)) {
25063
- throw new ConnectedAccountBillingError(
25064
- "The plan subscription is not active.",
25065
- "subscription_not_active",
25066
- 402
25067
- );
25068
- }
25069
- if (!findBasePlanItem(subscription)) {
25070
- throw new ConnectedAccountBillingError(
25071
- "The base plan item could not be found on this subscription.",
25072
- "subscription_plan_item_missing",
25073
- 409
25074
- );
25075
- }
25076
- const currentQuantity = Math.max(0, currentItem?.quantity ?? 0);
25077
- let resultingItem = currentItem;
25078
- if (actualQuantity !== currentQuantity) {
25079
- const updated = await client2.subscriptions.update(subscription.id, {
25080
- items: actualQuantity === 0 ? currentItem ? [{ id: currentItem.id, deleted: true }] : [] : currentItem ? [{ id: currentItem.id, quantity: actualQuantity }] : [{ price: priceId, quantity: actualQuantity }],
25081
- proration_behavior: "create_prorations"
25082
- });
25083
- resultingItem = findConnectedAccountItem(updated, priceId);
25084
- }
25085
- const state = await setConnectedAccountBillingState({
25086
- userId: user.id,
25087
- stripeSubscriptionId: subscription.id,
25088
- stripeSubscriptionItemId: resultingItem?.id ?? null,
25089
- priceId,
25090
- quantity: actualQuantity,
25091
- status: "synced",
25092
- lastErrorCode: null,
25093
- synced: true
25094
- });
25095
- return connectedAccountBillingView(actualQuantity, state);
25096
- } catch (error) {
25097
- await persistFailure(user, actualQuantity, error, currentItem?.id ?? null);
25098
- if (error instanceof ConnectedAccountBillingError) throw error;
25099
- throw new ConnectedAccountBillingError(
25100
- "Unable to update connected-account billing in Stripe.",
25101
- "stripe_connection_billing_failed",
25102
- 503
25103
- );
25104
- }
25105
- }
25106
- async function currentConnectedAccountBillingView(userId, actualQuantity) {
25107
- return connectedAccountBillingView(actualQuantity, await getConnectedAccountBillingState(userId));
25108
- }
25109
-
25110
25146
  // src/api/server.ts
25111
25147
  var secureCookies2 = process.env.NODE_ENV === "production" || process.env.VERCEL === "1";
25112
25148
  var isProduction2 = secureCookies2;
@@ -26091,7 +26127,7 @@ app.post("/schedule-connections/actions/slack/send-message", auth2, requireInteg
26091
26127
  user.email,
26092
26128
  connectionId,
26093
26129
  { channel: body.channel, text: body.text },
26094
- void 0,
26130
+ "send-message",
26095
26131
  connectedActionIdempotencyKey(c)
26096
26132
  );
26097
26133
  return c.json({ ok: true, result });
@@ -26111,7 +26147,7 @@ app.post("/schedule-connections/actions/gmail/send-message", auth2, requireInteg
26111
26147
  user.email,
26112
26148
  connectionId,
26113
26149
  { to: body.to, subject: body.subject, body: body.body },
26114
- void 0,
26150
+ "send-message",
26115
26151
  connectedActionIdempotencyKey(c)
26116
26152
  );
26117
26153
  return c.json({ ok: true, result });
@@ -26119,6 +26155,76 @@ app.post("/schedule-connections/actions/gmail/send-message", auth2, requireInteg
26119
26155
  return scheduleConnectionError(c, err, "Unable to send the email.");
26120
26156
  }
26121
26157
  });
26158
+ function toolResultText(result) {
26159
+ if (!result || typeof result !== "object") return null;
26160
+ const content = result.content;
26161
+ if (!Array.isArray(content)) return null;
26162
+ const textItem = content.find((item) => item && typeof item === "object" && item.type === "text");
26163
+ const text2 = textItem?.text;
26164
+ return typeof text2 === "string" ? text2 : null;
26165
+ }
26166
+ function parseEmailAddress(headerValue) {
26167
+ const value = headerValue?.trim();
26168
+ if (!value) return null;
26169
+ const angled = value.match(/^"?([^"<]*?)"?\s*<([^>]+)>$/);
26170
+ if (angled) return { email: angled[2].trim().toLowerCase(), name: angled[1].trim() || null };
26171
+ const bare = value.match(/^([^\s<>]+@[^\s<>]+)$/);
26172
+ return bare ? { email: bare[1].trim().toLowerCase(), name: null } : null;
26173
+ }
26174
+ app.post("/schedule-connections/actions/gmail/search-contacts", auth2, requireIntegrationsTier, async (c) => {
26175
+ const user = c.get("user");
26176
+ const body = await c.req.json().catch(() => ({}));
26177
+ const connectionId = providerConfigKeyFrom(body.connectionId);
26178
+ const query = typeof body.query === "string" ? body.query.trim() : "";
26179
+ const maxMessages = Math.min(150, Math.max(1, Number.isFinite(body.maxMessages) ? Number(body.maxMessages) : 50));
26180
+ const pageToken = typeof body.pageToken === "string" ? body.pageToken : void 0;
26181
+ if (!connectionId || !query) return c.json({ ok: false, error: "connectionId and query are required." }, 400);
26182
+ try {
26183
+ const listRaw = await callScheduleConnectionRead(user.email, connectionId, "list-messages", { q: query, maxResults: maxMessages, pageToken });
26184
+ const listText = toolResultText(listRaw);
26185
+ const list = listText ? JSON.parse(listText) : {};
26186
+ const ids = (list.messages ?? []).map((m) => m.id);
26187
+ const messages = [];
26188
+ const contacts = /* @__PURE__ */ new Map();
26189
+ for (const id of ids) {
26190
+ const msgRaw = await callScheduleConnectionRead(user.email, connectionId, "get-message", { id, format: "metadata" });
26191
+ const msgText = toolResultText(msgRaw);
26192
+ if (!msgText) continue;
26193
+ const msg = JSON.parse(msgText);
26194
+ const headers = new Map((msg.payload?.headers ?? []).map((h) => [h.name.toLowerCase(), h.value]));
26195
+ const date = headers.get("date") ?? null;
26196
+ const fromRaw = headers.get("from");
26197
+ const from = parseEmailAddress(fromRaw);
26198
+ const subject = headers.get("subject") ?? null;
26199
+ messages.push({ id, date, from: fromRaw ?? null, to: headers.get("to") ?? null, subject, snippet: msg.snippet ?? null });
26200
+ if (from) {
26201
+ const domain = from.email.split("@")[1] ?? null;
26202
+ const existing = contacts.get(from.email);
26203
+ if (existing) {
26204
+ existing.messageCount += 1;
26205
+ existing.name = existing.name ?? from.name;
26206
+ if (subject && existing.sampleSubjects.length < 5 && !existing.sampleSubjects.includes(subject)) existing.sampleSubjects.push(subject);
26207
+ if (date && (!existing.lastSeen || date > existing.lastSeen)) existing.lastSeen = date;
26208
+ if (date && (!existing.firstSeen || date < existing.firstSeen)) existing.firstSeen = date;
26209
+ } else {
26210
+ contacts.set(from.email, { email: from.email, name: from.name, domain, messageCount: 1, firstSeen: date, lastSeen: date, sampleSubjects: subject ? [subject] : [] });
26211
+ }
26212
+ }
26213
+ }
26214
+ const totalMatches = list.resultSizeEstimate;
26215
+ return c.json({
26216
+ ok: true,
26217
+ totalMatches,
26218
+ messagesFetched: messages.length,
26219
+ truncated: typeof totalMatches === "number" ? totalMatches > messages.length : Boolean(list.nextPageToken),
26220
+ nextPageToken: list.nextPageToken ?? null,
26221
+ messages,
26222
+ contacts: Array.from(contacts.values())
26223
+ });
26224
+ } catch (err) {
26225
+ return scheduleConnectionError(c, err, "Unable to search Gmail contacts.");
26226
+ }
26227
+ });
26122
26228
  app.post("/schedule-connections/actions/google-calendar/create-event", auth2, requireIntegrationsTier, async (c) => {
26123
26229
  const user = c.get("user");
26124
26230
  const body = await c.req.json().catch(() => ({}));
@@ -26137,7 +26243,7 @@ app.post("/schedule-connections/actions/google-calendar/create-event", auth2, re
26137
26243
  start: { dateTime: body.startDateTime, timeZone },
26138
26244
  end: { dateTime: body.endDateTime, timeZone },
26139
26245
  attendees
26140
- }, void 0, connectedActionIdempotencyKey(c));
26246
+ }, "create-event", connectedActionIdempotencyKey(c));
26141
26247
  return c.json({ ok: true, result });
26142
26248
  } catch (err) {
26143
26249
  return scheduleConnectionError(c, err, "Unable to create the calendar event.");
@@ -26157,7 +26263,7 @@ app.post("/schedule-connections/actions/zoom/create-meeting", auth2, requireInte
26157
26263
  durationMinutes: typeof body.durationMinutes === "number" ? body.durationMinutes : 30,
26158
26264
  timezone: typeof body.timezone === "string" ? body.timezone : void 0,
26159
26265
  agenda: body.agenda
26160
- }, void 0, connectedActionIdempotencyKey(c));
26266
+ }, "create-meeting", connectedActionIdempotencyKey(c));
26161
26267
  return c.json({ ok: true, result });
26162
26268
  } catch (err) {
26163
26269
  return scheduleConnectionError(c, err, "Unable to create the Zoom meeting.");
@@ -27185,6 +27291,22 @@ app.post("/billing/concurrency/checkout", requireAllowedOrigin, sessionAuth, asy
27185
27291
  return c.json({ error: message }, 500);
27186
27292
  }
27187
27293
  });
27294
+ async function chargeTierChangeNow(stripeClient, subscriptionId, customerId) {
27295
+ try {
27296
+ const draft = await stripeClient.invoices.create({
27297
+ customer: customerId,
27298
+ subscription: subscriptionId,
27299
+ auto_advance: true,
27300
+ collection_method: "charge_automatically"
27301
+ });
27302
+ const finalized = await stripeClient.invoices.finalizeInvoice(draft.id);
27303
+ if (finalized.amount_due <= 0 || finalized.status === "paid") return { ok: true, amountDue: finalized.amount_due };
27304
+ const paid = await stripeClient.invoices.pay(finalized.id);
27305
+ return { ok: paid.status === "paid", amountDue: paid.amount_due };
27306
+ } catch (err) {
27307
+ return { ok: false, amountDue: 0, error: err instanceof Error ? err.message : "Unable to charge the plan change immediately." };
27308
+ }
27309
+ }
27188
27310
  app.post("/billing/subscribe", requireAllowedOrigin, sessionAuth, async (c) => {
27189
27311
  try {
27190
27312
  const user = c.get("sessionUser");
@@ -27208,7 +27330,16 @@ app.post("/billing/subscribe", requireAllowedOrigin, sessionAuth, async (c) => {
27208
27330
  items: [{ id: itemId, price: tier.price_id }],
27209
27331
  proration_behavior: "create_prorations"
27210
27332
  });
27211
- return c.json({ updated: true, tier: tier.tier });
27333
+ const billed = await chargeTierChangeNow(stripeClient, user.subscription_id, customerId);
27334
+ if (!billed.ok) {
27335
+ return c.json({
27336
+ updated: true,
27337
+ tier: tier.tier,
27338
+ billed: false,
27339
+ error: billed.error ?? "Plan changed, but the prorated charge could not be collected. Update your payment method and it will retry automatically."
27340
+ });
27341
+ }
27342
+ return c.json({ updated: true, tier: tier.tier, billed: true });
27212
27343
  }
27213
27344
  }
27214
27345
  const session = await stripeClient.checkout.sessions.create({
@@ -27325,7 +27456,16 @@ app.post("/billing/subscribe/terminal-checkout", auth2, async (c) => {
27325
27456
  items: [{ id: itemId, price: tier.price_id }],
27326
27457
  proration_behavior: "create_prorations"
27327
27458
  });
27328
- return c.json({ updated: true, tier: tier.tier, message: `Switched to ${tier.label} (prorated). Credits and concurrency update on the next invoice.` });
27459
+ const billed = await chargeTierChangeNow(stripeClient, user.subscription_id, customerId);
27460
+ if (!billed.ok) {
27461
+ return c.json({
27462
+ updated: true,
27463
+ tier: tier.tier,
27464
+ billed: false,
27465
+ message: `Switched to ${tier.label} (prorated), but the prorated charge could not be collected: ${billed.error ?? "unknown error"}. Update your payment method and it will retry automatically.`
27466
+ });
27467
+ }
27468
+ return c.json({ updated: true, tier: tier.tier, billed: true, message: `Switched to ${tier.label} and charged the prorated difference immediately \u2014 credits and concurrency are live now.` });
27329
27469
  }
27330
27470
  }
27331
27471
  const session = await stripeClient.checkout.sessions.create({
@@ -27424,7 +27564,7 @@ app.get("/cron/tick", async (c) => {
27424
27564
  if (!process.env.CRON_SECRET || secret2 !== `Bearer ${process.env.CRON_SECRET}`) {
27425
27565
  return c.json({ error: "Unauthorized" }, 401);
27426
27566
  }
27427
- const { drainQueue } = await import("./worker-7DJYUACV.js");
27567
+ const { drainQueue } = await import("./worker-ZZCZNY3R.js");
27428
27568
  const budget = { maxJobs: 10, deadlineMs: Date.now() + 28e4 };
27429
27569
  const workflowDispatchResult = await dispatchDueWorkflowSchedules(`${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`);
27430
27570
  const [results, sweepResult, reapResult, expiredResult, blobCleanup, connectedDataArtifactCleanup] = await Promise.all([
@@ -27627,4 +27767,4 @@ app.get("/blog/:slug/", (c) => {
27627
27767
  export {
27628
27768
  app
27629
27769
  };
27630
- //# sourceMappingURL=server-GXTYKIAX.js.map
27770
+ //# sourceMappingURL=server-NHLEIKDZ.js.map