mcp-scraper 0.56.0 → 0.57.0

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 (37) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +2 -2
  3. package/dist/bin/api-server.cjs +564 -156
  4. package/dist/bin/api-server.cjs.map +1 -1
  5. package/dist/bin/api-server.js +2 -2
  6. package/dist/bin/mcp-scraper-cli.cjs +1 -1
  7. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  8. package/dist/bin/mcp-scraper-cli.js +1 -1
  9. package/dist/bin/mcp-scraper-install.cjs +2 -2
  10. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  11. package/dist/bin/mcp-scraper-install.js +2 -2
  12. package/dist/bin/mcp-stdio-server.cjs +25 -2
  13. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  14. package/dist/bin/mcp-stdio-server.js +4 -4
  15. package/dist/{chunk-NP66MZ5A.js → chunk-5XYBKE3X.js} +2 -2
  16. package/dist/{chunk-LUJYVNO7.js → chunk-HU4R6BP2.js} +2 -2
  17. package/dist/{chunk-LUJYVNO7.js.map → chunk-HU4R6BP2.js.map} +1 -1
  18. package/dist/chunk-QQ6ZBYZZ.js +7 -0
  19. package/dist/chunk-QQ6ZBYZZ.js.map +1 -0
  20. package/dist/{chunk-WO2SSENO.js → chunk-R6CO77BN.js} +3 -3
  21. package/dist/{chunk-WO2SSENO.js.map → chunk-R6CO77BN.js.map} +1 -1
  22. package/dist/{chunk-JEZW6N2X.js → chunk-ULAJAZQF.js} +26 -3
  23. package/dist/chunk-ULAJAZQF.js.map +1 -0
  24. package/dist/{extract-bundle-6KYTD55T.js → extract-bundle-H7JEELY7.js} +3 -3
  25. package/dist/{server-SETYJPYO.js → server-NBGZJX4P.js} +447 -72
  26. package/dist/server-NBGZJX4P.js.map +1 -0
  27. package/dist/{site-extract-repository-YGNYFTNE.js → site-extract-repository-CFMSWAVL.js} +3 -3
  28. package/dist/{worker-DTVBN3RQ.js → worker-S2WXELUC.js} +2 -2
  29. package/package.json +1 -1
  30. package/dist/chunk-245IAJ7R.js +0 -7
  31. package/dist/chunk-245IAJ7R.js.map +0 -1
  32. package/dist/chunk-JEZW6N2X.js.map +0 -1
  33. package/dist/server-SETYJPYO.js.map +0 -1
  34. /package/dist/{chunk-NP66MZ5A.js.map → chunk-5XYBKE3X.js.map} +0 -0
  35. /package/dist/{extract-bundle-6KYTD55T.js.map → extract-bundle-H7JEELY7.js.map} +0 -0
  36. /package/dist/{site-extract-repository-YGNYFTNE.js.map → site-extract-repository-CFMSWAVL.js.map} +0 -0
  37. /package/dist/{worker-DTVBN3RQ.js.map → worker-S2WXELUC.js.map} +0 -0
@@ -12455,8 +12455,8 @@ var init_product_contract_generated = __esm({
12455
12455
  "freeSignupCredits": 0
12456
12456
  },
12457
12457
  "inventory": {
12458
- "totalTools": 250,
12459
- "scraperTools": 149,
12458
+ "totalTools": 251,
12459
+ "scraperTools": 150,
12460
12460
  "memoryTools": 101
12461
12461
  },
12462
12462
  "concurrencyPack": {
@@ -44624,7 +44624,7 @@ var PACKAGE_VERSION;
44624
44624
  var init_version = __esm({
44625
44625
  "src/version.ts"() {
44626
44626
  "use strict";
44627
- PACKAGE_VERSION = "0.56.0";
44627
+ PACKAGE_VERSION = "0.57.0";
44628
44628
  }
44629
44629
  });
44630
44630
 
@@ -46453,7 +46453,7 @@ var init_contracts = __esm({
46453
46453
  });
46454
46454
 
46455
46455
  // src/mcp/mcp-tool-schemas.ts
46456
- var import_zod43, WEBSITE_URL_OR_DOMAIN_ERROR, WebsiteUrlOrDomainSchema, HarvestPaaInputSchema, ExtractUrlBaseInputSchema, ExtractUrlInputSchema, ExtractUrlLocalInputSchema, DiffPageBaseInputSchema, DiffPageInputSchema, DiffPageLocalInputSchema, MapSiteUrlsInputSchema, MapWaybackSnapshotsInputSchema, ExtractSiteInputSchema, AuditSiteInputSchema, CheckSiteExportInputSchema, ArchiveReadInputSchema, YoutubeHarvestInputSchema, YoutubeTranscribeInputSchema, FacebookPageIntelInputSchema, FacebookAdSearchInputSchema, RedditThreadInputSchema, RedditTrendingInputSchema, VideoFrameAnalysisInputSchema, VideoFrameAnalysisStatusInputSchema, FacebookAdTranscribeInputSchema, FacebookVideoTranscribeInputSchema, GoogleAdsSearchInputSchema, GoogleAdsPageIntelInputSchema, GoogleAdsTranscribeInputSchema, InstagramProfileContentInputSchema, InstagramMediaDownloadInputSchema, MapsPlaceIntelInputSchema, TrustpilotReviewsInputSchema, G2ReviewsInputSchema, ReviewCardSchema, MapsSearchInputSchema, DirectoryWorkflowInputSchema, LocationMarketsInputSchema, CommonsSearchEntitiesInputSchema, CommonsGetEntityInputSchema, CommonsGetEntityLinksetInputSchema, CommonsFeaturedImageInputSchema, CommonsMediaInputSchema, CommonsCitationInputSchema, CommonsSourceInputSchema, CommonsRelatedLinkInputSchema, CommonsClaimInputSchema, CommonsPrepareEntityInputSchema, CommonsSubmitEntityInputSchema, CommonsValidateEntityInputSchema, CommonsGetEntityLedgerInputSchema, CommonsGetProposalInputSchema, CommonsSaveFilterInputSchema, CommonsListFiltersInputSchema, CommonsListNeedsLinksInputSchema, CommonsGenericOutputSchema, DirectoryWorkflowStatusInputSchema, LocalSourcebookSubmitInputSchema, LocalSourcebookCategorySchema, LocalSourcebookSchemaTypeInputSchema, LocalSourcebookTagCandidateObjectSchema, LocalSourcebookTagDecisionObjectSchema, LocalSourcebookIdentityObjectSchema, GetLocalSourcebookContractInputSchema, ListLocalSourcebookTagsInputSchema, ResolveLocalSourcebookTagsInputSchema, PrepareLocalSourcebookWriteInputSchema, ValidateLocalSourcebookWriteInputSchema, LocalSourcebookCaptureInputSchema, LocalSourcebookSubmissionStatusInputSchema, LocalSourcebookRefreshInputSchema, LocalSourcebookOutputSchema, ArtifactPointerOutputSchema, EditorialReadingRoomSiteSchema, EditorialReadingRoomImageSchema, EditorialReadingRoomArticleSchema, EditorialReadingRoomGuideInputSchema, EditorialReadingRoomGuideOutputSchema, CreateEditorialReadingRoomInputSchema, EditorialReadingRoomArtifactSchema, CreateEditorialReadingRoomOutputSchema, RenewEditorialReadingRoomDownloadInputSchema, RenewEditorialReadingRoomDownloadOutputSchema, CommonsPublicationSubdomainSchema, CommonsPreparePublicationInputSchema, CommonsValidatePublicationInputSchema, CommonsClaimPublicationInputSchema, CommonsPublishEditorialInputSchema, CommonsGetPublicationInputSchema, RankTrackerModeSchema, RankTrackerBlueprintInputSchema, NullableString, MapsSearchAttemptOutput, MapsSearchOutputSchema, DirectoryMapsBusinessOutput, DirectoryCsvArtifactOutput, DirectoryWorkflowOutputSchema, LocationDatasetProvenanceOutput, LocationMarketsOutputSchema, RankTrackerToolPlanOutput, RankTrackerTableOutput, RankTrackerCronJobOutput, RankTrackerBlueprintOutputSchema, OrganicResultOutput, AiOverviewOutput, EntityIdsOutput, HarvestPaaOutputSchema, SearchSerpOutputSchema, PageMediaAssetOutput, PageMediaArtifactOutput, ExtractUrlOutputSchema, DiffPageOutputSchema, ExtractSiteOutputSchema, AuditSiteOutputSchema, CheckSiteExportOutputSchema, ArchiveEntryOutputSchema, ArchiveReadOutputSchema, MapsPlaceIntelOutputSchema, TrustpilotReviewsOutputSchema, G2ReviewsOutputSchema, CreditsInfoOutputSchema, MapSiteUrlsOutputSchema, WaybackCaptureOutputSchema, MapWaybackSnapshotsOutputSchema, YoutubeHarvestOutputSchema, FacebookAdSearchOutputSchema, VideoFrameAnalysisOutputSchema, VideoFrameAnalysisStatusOutputSchema, RedditThreadOutputSchema, RedditTrendingOutputSchema, FacebookPageIntelOutputSchema, GoogleAdsSearchOutputSchema, GoogleAdsPageIntelOutputSchema, TranscriptSignalOutput, FacebookVideoTranscribeOutputSchema, TranscriptChunkOutput, InstagramBrowserOutput, InstagramPaginationOutput, InstagramProfileContentOutputSchema, InstagramMediaTrackOutput, InstagramDownloadOutput, InstagramMediaDownloadOutputSchema, YoutubeTranscribeOutputSchema, FacebookAdTranscribeOutputSchema, GoogleAdsTranscribeOutputSchema, CaptureSerpSnapshotOutputSchema, CaptureSerpPageSnapshotsOutputSchema, CreditsInfoInputSchema, WorkflowIdSchema2, WorkflowListInputSchema, WorkflowSuggestInputSchema, WorkflowRunInputSchema, WorkflowStepInputSchema, WorkflowStatusInputSchema, WorkflowArtifactReadInputSchema, WorkflowRecipeOutput, WorkflowDefinitionOutput, WorkflowArtifactOutput, WorkflowListOutputSchema, WorkflowSuggestOutputSchema, WorkflowRunOutputSchema, WorkflowStepOutputSchema, WorkflowStatusOutputSchema, WorkflowArtifactReadOutputSchema, SearchSerpInputSchema, CaptureSerpSnapshotInputSchema, ScreenshotInputSchema, CaptureSerpPageSnapshotsInputSchema, ReportArtifactReadInputSchema, ReportArtifactReadOutputSchema, ListServiceConnectionsInputSchema, ListServiceConnectionsOutputSchema, TestServiceConnectionInputSchema, TestServiceConnectionOutputSchema, ReadServiceConnectionInputSchema, ReadServiceConnectionOutputSchema, MetaAdCreativeMediaInputSchema, MetaAdCreativeMediaOutputSchema, ImportServiceConnectionToMemoryInputSchema, ImportServiceConnectionToMemoryOutputSchema, DescribeServiceConnectionToolInputSchema, DescribeServiceConnectionToolOutputSchema, ConnectedDataContinuationSchema, ExportConnectedServiceDataInputSchema, ConnectedDataArtifactSchema, ExportConnectedServiceDataOutputSchema, SearchConsoleTableColumnSchema, SearchConsoleTableFilterSchema, ExportSearchConsoleTableDataInputSchema, ExportSearchConsoleTableDataOutputSchema, RenewConnectedDataExportDownloadInputSchema, RenewConnectedDataExportDownloadOutputSchema, CallServiceConnectionActionInputSchema, CallServiceConnectionActionOutputSchema, SetScheduledActionConnectionsInputSchema, SetScheduledActionConnectionsOutputSchema, SlackSendMessageInputSchema, SlackSendMessageOutputSchema, GmailSendMessageInputSchema, GmailSendMessageOutputSchema, GmailSearchContactsInputSchema, GmailSearchContactsOutputSchema, GoogleCalendarCreateEventInputSchema, GoogleCalendarCreateEventOutputSchema, ZoomCreateMeetingInputSchema, ZoomCreateMeetingOutputSchema;
46456
+ var import_zod43, WEBSITE_URL_OR_DOMAIN_ERROR, WebsiteUrlOrDomainSchema, HarvestPaaInputSchema, ExtractUrlBaseInputSchema, ExtractUrlInputSchema, ExtractUrlLocalInputSchema, DiffPageBaseInputSchema, DiffPageInputSchema, DiffPageLocalInputSchema, MapSiteUrlsInputSchema, MapWaybackSnapshotsInputSchema, ExtractSiteInputSchema, AuditSiteInputSchema, CheckSiteExportInputSchema, ArchiveReadInputSchema, YoutubeHarvestInputSchema, YoutubeTranscribeInputSchema, FacebookPageIntelInputSchema, FacebookAdSearchInputSchema, RedditThreadInputSchema, RedditTrendingInputSchema, VideoFrameAnalysisInputSchema, VideoFrameAnalysisStatusInputSchema, FacebookAdTranscribeInputSchema, FacebookVideoTranscribeInputSchema, GoogleAdsSearchInputSchema, GoogleAdsPageIntelInputSchema, GoogleAdsTranscribeInputSchema, InstagramProfileContentInputSchema, InstagramMediaDownloadInputSchema, MapsPlaceIntelInputSchema, TrustpilotReviewsInputSchema, G2ReviewsInputSchema, ReviewCardSchema, MapsSearchInputSchema, DirectoryWorkflowInputSchema, LocationMarketsInputSchema, CommonsSearchEntitiesInputSchema, CommonsGetEntityInputSchema, CommonsGetEntityLinksetInputSchema, CommonsFeaturedImageInputSchema, CommonsMediaInputSchema, CommonsCitationInputSchema, CommonsSourceInputSchema, CommonsRelatedLinkInputSchema, CommonsClaimInputSchema, CommonsPrepareEntityInputSchema, CommonsSubmitEntityInputSchema, CommonsValidateEntityInputSchema, CommonsGetEntityLedgerInputSchema, CommonsHostImageInputSchema, CommonsGetProposalInputSchema, CommonsSaveFilterInputSchema, CommonsListFiltersInputSchema, CommonsListNeedsLinksInputSchema, CommonsGenericOutputSchema, DirectoryWorkflowStatusInputSchema, LocalSourcebookSubmitInputSchema, LocalSourcebookCategorySchema, LocalSourcebookSchemaTypeInputSchema, LocalSourcebookTagCandidateObjectSchema, LocalSourcebookTagDecisionObjectSchema, LocalSourcebookIdentityObjectSchema, GetLocalSourcebookContractInputSchema, ListLocalSourcebookTagsInputSchema, ResolveLocalSourcebookTagsInputSchema, PrepareLocalSourcebookWriteInputSchema, ValidateLocalSourcebookWriteInputSchema, LocalSourcebookCaptureInputSchema, LocalSourcebookSubmissionStatusInputSchema, LocalSourcebookRefreshInputSchema, LocalSourcebookOutputSchema, ArtifactPointerOutputSchema, EditorialReadingRoomSiteSchema, EditorialReadingRoomImageSchema, EditorialReadingRoomArticleSchema, EditorialReadingRoomGuideInputSchema, EditorialReadingRoomGuideOutputSchema, CreateEditorialReadingRoomInputSchema, EditorialReadingRoomArtifactSchema, CreateEditorialReadingRoomOutputSchema, RenewEditorialReadingRoomDownloadInputSchema, RenewEditorialReadingRoomDownloadOutputSchema, CommonsPublicationSubdomainSchema, CommonsPreparePublicationInputSchema, CommonsValidatePublicationInputSchema, CommonsClaimPublicationInputSchema, CommonsPublishEditorialInputSchema, CommonsGetPublicationInputSchema, RankTrackerModeSchema, RankTrackerBlueprintInputSchema, NullableString, MapsSearchAttemptOutput, MapsSearchOutputSchema, DirectoryMapsBusinessOutput, DirectoryCsvArtifactOutput, DirectoryWorkflowOutputSchema, LocationDatasetProvenanceOutput, LocationMarketsOutputSchema, RankTrackerToolPlanOutput, RankTrackerTableOutput, RankTrackerCronJobOutput, RankTrackerBlueprintOutputSchema, OrganicResultOutput, AiOverviewOutput, EntityIdsOutput, HarvestPaaOutputSchema, SearchSerpOutputSchema, PageMediaAssetOutput, PageMediaArtifactOutput, ExtractUrlOutputSchema, DiffPageOutputSchema, ExtractSiteOutputSchema, AuditSiteOutputSchema, CheckSiteExportOutputSchema, ArchiveEntryOutputSchema, ArchiveReadOutputSchema, MapsPlaceIntelOutputSchema, TrustpilotReviewsOutputSchema, G2ReviewsOutputSchema, CreditsInfoOutputSchema, MapSiteUrlsOutputSchema, WaybackCaptureOutputSchema, MapWaybackSnapshotsOutputSchema, YoutubeHarvestOutputSchema, FacebookAdSearchOutputSchema, VideoFrameAnalysisOutputSchema, VideoFrameAnalysisStatusOutputSchema, RedditThreadOutputSchema, RedditTrendingOutputSchema, FacebookPageIntelOutputSchema, GoogleAdsSearchOutputSchema, GoogleAdsPageIntelOutputSchema, TranscriptSignalOutput, FacebookVideoTranscribeOutputSchema, TranscriptChunkOutput, InstagramBrowserOutput, InstagramPaginationOutput, InstagramProfileContentOutputSchema, InstagramMediaTrackOutput, InstagramDownloadOutput, InstagramMediaDownloadOutputSchema, YoutubeTranscribeOutputSchema, FacebookAdTranscribeOutputSchema, GoogleAdsTranscribeOutputSchema, CaptureSerpSnapshotOutputSchema, CaptureSerpPageSnapshotsOutputSchema, CreditsInfoInputSchema, WorkflowIdSchema2, WorkflowListInputSchema, WorkflowSuggestInputSchema, WorkflowRunInputSchema, WorkflowStepInputSchema, WorkflowStatusInputSchema, WorkflowArtifactReadInputSchema, WorkflowRecipeOutput, WorkflowDefinitionOutput, WorkflowArtifactOutput, WorkflowListOutputSchema, WorkflowSuggestOutputSchema, WorkflowRunOutputSchema, WorkflowStepOutputSchema, WorkflowStatusOutputSchema, WorkflowArtifactReadOutputSchema, SearchSerpInputSchema, CaptureSerpSnapshotInputSchema, ScreenshotInputSchema, CaptureSerpPageSnapshotsInputSchema, ReportArtifactReadInputSchema, ReportArtifactReadOutputSchema, ListServiceConnectionsInputSchema, ListServiceConnectionsOutputSchema, TestServiceConnectionInputSchema, TestServiceConnectionOutputSchema, ReadServiceConnectionInputSchema, ReadServiceConnectionOutputSchema, MetaAdCreativeMediaInputSchema, MetaAdCreativeMediaOutputSchema, ImportServiceConnectionToMemoryInputSchema, ImportServiceConnectionToMemoryOutputSchema, DescribeServiceConnectionToolInputSchema, DescribeServiceConnectionToolOutputSchema, ConnectedDataContinuationSchema, ExportConnectedServiceDataInputSchema, ConnectedDataArtifactSchema, ExportConnectedServiceDataOutputSchema, SearchConsoleTableColumnSchema, SearchConsoleTableFilterSchema, ExportSearchConsoleTableDataInputSchema, ExportSearchConsoleTableDataOutputSchema, RenewConnectedDataExportDownloadInputSchema, RenewConnectedDataExportDownloadOutputSchema, CallServiceConnectionActionInputSchema, CallServiceConnectionActionOutputSchema, SetScheduledActionConnectionsInputSchema, SetScheduledActionConnectionsOutputSchema, SlackSendMessageInputSchema, SlackSendMessageOutputSchema, GmailSendMessageInputSchema, GmailSendMessageOutputSchema, GmailSearchContactsInputSchema, GmailSearchContactsOutputSchema, GoogleCalendarCreateEventInputSchema, GoogleCalendarCreateEventOutputSchema, ZoomCreateMeetingInputSchema, ZoomCreateMeetingOutputSchema;
46457
46457
  var init_mcp_tool_schemas = __esm({
46458
46458
  "src/mcp/mcp-tool-schemas.ts"() {
46459
46459
  "use strict";
@@ -46935,6 +46935,13 @@ var init_mcp_tool_schemas = __esm({
46935
46935
  CommonsGetEntityLedgerInputSchema = {
46936
46936
  idOrSlug: import_zod43.z.string().trim().min(1).max(180).describe("Published entity id or /wiki/ slug whose contribution ledger should be read.")
46937
46937
  };
46938
+ CommonsHostImageInputSchema = {
46939
+ sourceUrl: import_zod43.z.string().url().optional().describe("Public https image URL to download and host. The wiki never serves a third-party URL directly, so pass the original source here rather than putting it on the entity."),
46940
+ imageBase64: import_zod43.z.string().min(16).optional().describe("Base64 image bytes for an image you already hold. Use for images under about 3 MB; larger files should be published from a URL."),
46941
+ alt: import_zod43.z.string().trim().max(500).optional().describe("Alt text describing the image for readers who cannot see it."),
46942
+ license: import_zod43.z.string().trim().max(240).optional().describe("License or usage terms of the original image, preserved with the hosted copy."),
46943
+ attribution: import_zod43.z.string().trim().max(500).optional().describe("Credit line for the original photographer, publication, or archive.")
46944
+ };
46938
46945
  CommonsGetProposalInputSchema = {
46939
46946
  proposalId: import_zod43.z.string().trim().min(1).max(120).describe("Proposal id returned by commons_submit_entity, in the form commons-proposal-<uuid>.")
46940
46947
  };
@@ -50477,6 +50484,19 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
50477
50484
  openWorldHint: false
50478
50485
  }
50479
50486
  }, async (input) => executor.commonsGetEntityLedger(input));
50487
+ server.registerTool("commons_host_image", {
50488
+ title: "Host a Transparent Commons Image",
50489
+ description: "Download an image from a public https URL, or accept base64 bytes, and store it as a permanent Commons-hosted image. Returns the hosted URL to use on an entity. Commons never serves a third-party image URL: submitting an entity with an external image hosts it automatically, and this tool exists for hosting an image before or independently of a write. JPEG, PNG, GIF, and WebP up to 10 MB; SVG is rejected because the wiki serves images to the public web. Identical bytes are stored once and reused.",
50490
+ inputSchema: CommonsHostImageInputSchema,
50491
+ outputSchema: recordOutputSchema("commons_host_image", CommonsGenericOutputSchema),
50492
+ annotations: {
50493
+ title: "Host a Transparent Commons Image",
50494
+ readOnlyHint: false,
50495
+ destructiveHint: false,
50496
+ idempotentHint: true,
50497
+ openWorldHint: true
50498
+ }
50499
+ }, async (input) => executor.commonsHostImage(input));
50480
50500
  server.registerTool("commons_get_proposal", {
50481
50501
  title: "Transparent Commons Proposal Status",
50482
50502
  description: "Read one commons_submit_entity proposal owned by the calling account. Use it when submit returned a proposal that was held for review instead of published, to see the review reason and whether it has since been applied.",
@@ -51540,6 +51560,9 @@ var init_http_mcp_tool_executor = __esm({
51540
51560
  commonsGetProposal(input) {
51541
51561
  return this.getJson(`/commons/proposals/${encodeURIComponent(input.proposalId)}`);
51542
51562
  }
51563
+ commonsHostImage(input) {
51564
+ return this.call("/commons/images", input);
51565
+ }
51543
51566
  commonsSaveFilter(input) {
51544
51567
  return this.call("/commons/filters", input);
51545
51568
  }
@@ -64276,6 +64299,231 @@ var init_editorial_reading_room_routes = __esm({
64276
64299
  }
64277
64300
  });
64278
64301
 
64302
+ // src/api/commons-image-store.ts
64303
+ async function ensureCommonsImageSchema() {
64304
+ if (imageSchemaReady) return;
64305
+ await getDb().execute(`
64306
+ CREATE TABLE IF NOT EXISTS commons_images (
64307
+ id TEXT PRIMARY KEY,
64308
+ user_id INTEGER NOT NULL,
64309
+ digest TEXT NOT NULL,
64310
+ storage_key TEXT NOT NULL,
64311
+ url TEXT NOT NULL,
64312
+ content_type TEXT NOT NULL,
64313
+ bytes INTEGER NOT NULL,
64314
+ source_url TEXT,
64315
+ alt TEXT,
64316
+ license TEXT,
64317
+ attribution TEXT,
64318
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
64319
+ )
64320
+ `);
64321
+ await getDb().execute(`CREATE UNIQUE INDEX IF NOT EXISTS commons_images_digest ON commons_images(digest)`);
64322
+ imageSchemaReady = true;
64323
+ }
64324
+ function sniffImageFormat(bytes) {
64325
+ if (bytes.length < 12) return null;
64326
+ if (bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) return "image/jpeg";
64327
+ if (bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) return "image/png";
64328
+ if (bytes.subarray(0, 6).toString("ascii") === "GIF87a" || bytes.subarray(0, 6).toString("ascii") === "GIF89a") return "image/gif";
64329
+ if (bytes.subarray(0, 4).toString("ascii") === "RIFF" && bytes.subarray(8, 12).toString("ascii") === "WEBP") return "image/webp";
64330
+ return null;
64331
+ }
64332
+ function isBlockedAddress(address) {
64333
+ if ((0, import_node_net2.isIP)(address) === 6) {
64334
+ const normalized = address.toLowerCase();
64335
+ return normalized === "::1" || normalized.startsWith("fc") || normalized.startsWith("fd") || normalized.startsWith("fe80");
64336
+ }
64337
+ const parts = address.split(".").map(Number);
64338
+ if (parts.length !== 4 || parts.some((part) => Number.isNaN(part))) return true;
64339
+ const [a, b] = parts;
64340
+ if (a === 10 || a === 127 || a === 0) return true;
64341
+ if (a === 172 && b >= 16 && b <= 31) return true;
64342
+ if (a === 192 && b === 168) return true;
64343
+ if (a === 169 && b === 254) return true;
64344
+ if (a === 100 && b >= 64 && b <= 127) return true;
64345
+ return false;
64346
+ }
64347
+ async function assertPublicHttpsUrl(rawUrl) {
64348
+ let url;
64349
+ try {
64350
+ url = new URL(rawUrl);
64351
+ } catch {
64352
+ throw new CommonsImageError("image_url_invalid", `"${rawUrl}" is not a valid URL.`);
64353
+ }
64354
+ if (url.protocol !== "https:") {
64355
+ throw new CommonsImageError("image_url_not_https", "Commons image sources must be https URLs.");
64356
+ }
64357
+ const hostname = url.hostname.replace(/^\[|\]$/g, "");
64358
+ const addresses = (0, import_node_net2.isIP)(hostname) ? [{ address: hostname }] : await (0, import_promises15.lookup)(hostname, { all: true }).catch(() => []);
64359
+ if (!addresses.length) {
64360
+ throw new CommonsImageError("image_host_unresolvable", `Could not resolve ${url.hostname}.`);
64361
+ }
64362
+ if (addresses.some((entry) => isBlockedAddress(entry.address))) {
64363
+ throw new CommonsImageError("image_host_blocked", "Commons image sources cannot resolve to private or loopback addresses.");
64364
+ }
64365
+ return url;
64366
+ }
64367
+ async function downloadImage(sourceUrl) {
64368
+ let current = sourceUrl;
64369
+ for (let redirect = 0; redirect <= COMMONS_IMAGE_MAX_REDIRECTS; redirect += 1) {
64370
+ const url = await assertPublicHttpsUrl(current);
64371
+ const response = await fetch(url, { redirect: "manual", signal: AbortSignal.timeout(COMMONS_IMAGE_FETCH_TIMEOUT_MS) });
64372
+ if (response.status >= 300 && response.status < 400) {
64373
+ const location2 = response.headers.get("location");
64374
+ if (!location2) throw new CommonsImageError("image_redirect_invalid", "Image source redirected without a destination.");
64375
+ current = new URL(location2, url).toString();
64376
+ continue;
64377
+ }
64378
+ if (!response.ok) {
64379
+ throw new CommonsImageError("image_source_unavailable", `Image source returned HTTP ${response.status}.`, 422);
64380
+ }
64381
+ const declaredLength = Number(response.headers.get("content-length") ?? "0");
64382
+ if (declaredLength > COMMONS_IMAGE_MAX_BYTES) {
64383
+ throw new CommonsImageError("image_too_large", `Image exceeds the ${COMMONS_IMAGE_MAX_BYTES} byte limit.`, 413);
64384
+ }
64385
+ const bytes = Buffer.from(await response.arrayBuffer());
64386
+ if (bytes.length > COMMONS_IMAGE_MAX_BYTES) {
64387
+ throw new CommonsImageError("image_too_large", `Image exceeds the ${COMMONS_IMAGE_MAX_BYTES} byte limit.`, 413);
64388
+ }
64389
+ return bytes;
64390
+ }
64391
+ throw new CommonsImageError("image_redirect_limit", "Image source exceeded the redirect limit.");
64392
+ }
64393
+ function decodeBase64Image(value) {
64394
+ const payload = value.includes(",") && value.trim().startsWith("data:") ? value.slice(value.indexOf(",") + 1) : value;
64395
+ const bytes = Buffer.from(payload, "base64");
64396
+ if (!bytes.length) throw new CommonsImageError("image_base64_invalid", "imageBase64 did not decode to any bytes.");
64397
+ if (bytes.length > COMMONS_IMAGE_MAX_BYTES) {
64398
+ throw new CommonsImageError("image_too_large", `Image exceeds the ${COMMONS_IMAGE_MAX_BYTES} byte limit.`, 413);
64399
+ }
64400
+ return bytes;
64401
+ }
64402
+ function isCommonsHostedImageUrl(url) {
64403
+ return /^https:\/\/[^/]*(\.public\.blob\.vercel-storage\.com|blob\.vercel-storage\.com)\//i.test(url) || url.startsWith("file://");
64404
+ }
64405
+ async function hostCommonsImage(input) {
64406
+ if (hostOverride) return hostOverride(input);
64407
+ await ensureCommonsImageSchema();
64408
+ if (!input.sourceUrl && !input.imageBase64) {
64409
+ throw new CommonsImageError("image_source_missing", "Provide either sourceUrl or imageBase64.");
64410
+ }
64411
+ const bytes = input.imageBase64 ? decodeBase64Image(input.imageBase64) : await downloadImage(String(input.sourceUrl));
64412
+ const contentType = sniffImageFormat(bytes);
64413
+ if (!contentType) {
64414
+ throw new CommonsImageError(
64415
+ "image_format_unsupported",
64416
+ "Commons hosts JPEG, PNG, GIF, and WebP. SVG and other formats are rejected because the wiki serves them to the public web.",
64417
+ 415
64418
+ );
64419
+ }
64420
+ const digest2 = (0, import_node_crypto37.createHash)("sha256").update(bytes).digest("hex");
64421
+ const existing = await getDb().execute({
64422
+ sql: "SELECT id, url, content_type, bytes, source_url FROM commons_images WHERE digest = ? LIMIT 1",
64423
+ args: [digest2]
64424
+ });
64425
+ const reusable = existing.rows[0];
64426
+ if (reusable) {
64427
+ return {
64428
+ id: String(reusable.id),
64429
+ url: String(reusable.url),
64430
+ contentType: String(reusable.content_type),
64431
+ bytes: Number(reusable.bytes),
64432
+ digest: digest2,
64433
+ sourceUrl: reusable.source_url ? String(reusable.source_url) : null,
64434
+ reused: true
64435
+ };
64436
+ }
64437
+ const extension = contentType.split("/")[1] === "jpeg" ? "jpg" : contentType.split("/")[1];
64438
+ const storageKey = `commons/images/${digest2.slice(0, 2)}/${digest2}.${extension}`;
64439
+ const stored = await getBlobStore().put(storageKey, bytes, contentType);
64440
+ const id = `TPW-IMG-${digest2.slice(0, 16).toUpperCase()}`;
64441
+ await getDb().execute({
64442
+ sql: `
64443
+ INSERT INTO commons_images (id, user_id, digest, storage_key, url, content_type, bytes, source_url, alt, license, attribution, created_at)
64444
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
64445
+ `,
64446
+ args: [
64447
+ id,
64448
+ input.userId,
64449
+ digest2,
64450
+ storageKey,
64451
+ stored.url,
64452
+ contentType,
64453
+ bytes.length,
64454
+ input.sourceUrl ?? null,
64455
+ input.alt ?? null,
64456
+ input.license ?? null,
64457
+ input.attribution ?? null,
64458
+ (/* @__PURE__ */ new Date()).toISOString()
64459
+ ]
64460
+ });
64461
+ return { id, url: stored.url, contentType, bytes: bytes.length, digest: digest2, sourceUrl: input.sourceUrl ?? null, reused: false };
64462
+ }
64463
+ async function hostEntityImages(input) {
64464
+ const rewrite = { mediaUrls: /* @__PURE__ */ new Map(), dropped: [] };
64465
+ const featuredUrl = input.featuredImage?.url;
64466
+ if (featuredUrl && !isCommonsHostedImageUrl(featuredUrl)) {
64467
+ const hosted = await hostCommonsImage({
64468
+ userId: input.userId,
64469
+ sourceUrl: featuredUrl,
64470
+ alt: input.featuredImage?.alt,
64471
+ license: input.featuredImage?.license,
64472
+ attribution: input.featuredImage?.sourceUrl
64473
+ }).catch((error) => {
64474
+ const message = error instanceof Error ? error.message : String(error);
64475
+ throw new CommonsImageError(
64476
+ error instanceof CommonsImageError ? error.code : "featured_image_unhostable",
64477
+ `The featured image could not be hosted, so this entity would publish with a link that can break: ${message}`,
64478
+ 422
64479
+ );
64480
+ });
64481
+ rewrite.featuredImageUrl = hosted.url;
64482
+ }
64483
+ for (const item of input.media ?? []) {
64484
+ if (!item.url || isCommonsHostedImageUrl(item.url)) continue;
64485
+ try {
64486
+ const hosted = await hostCommonsImage({
64487
+ userId: input.userId,
64488
+ sourceUrl: item.url,
64489
+ alt: item.alt,
64490
+ license: item.license,
64491
+ attribution: item.sourceUrl
64492
+ });
64493
+ rewrite.mediaUrls.set(item.url, hosted.url);
64494
+ } catch (error) {
64495
+ rewrite.dropped.push({ url: item.url, reason: error instanceof Error ? error.message : String(error) });
64496
+ }
64497
+ }
64498
+ return rewrite;
64499
+ }
64500
+ var import_node_crypto37, import_promises15, import_node_net2, COMMONS_IMAGE_MAX_BYTES, COMMONS_IMAGE_MAX_REDIRECTS, COMMONS_IMAGE_FETCH_TIMEOUT_MS, CommonsImageError, imageSchemaReady, hostOverride;
64501
+ var init_commons_image_store = __esm({
64502
+ "src/api/commons-image-store.ts"() {
64503
+ "use strict";
64504
+ import_node_crypto37 = require("crypto");
64505
+ import_promises15 = require("dns/promises");
64506
+ import_node_net2 = require("net");
64507
+ init_blob_store();
64508
+ init_db();
64509
+ COMMONS_IMAGE_MAX_BYTES = 10 * 1024 * 1024;
64510
+ COMMONS_IMAGE_MAX_REDIRECTS = 3;
64511
+ COMMONS_IMAGE_FETCH_TIMEOUT_MS = 2e4;
64512
+ CommonsImageError = class extends Error {
64513
+ constructor(code, message, httpStatus = 400) {
64514
+ super(message);
64515
+ this.code = code;
64516
+ this.httpStatus = httpStatus;
64517
+ this.name = "CommonsImageError";
64518
+ }
64519
+ code;
64520
+ httpStatus;
64521
+ };
64522
+ imageSchemaReady = false;
64523
+ hostOverride = null;
64524
+ }
64525
+ });
64526
+
64279
64527
  // src/api/schema-presence.ts
64280
64528
  async function loadSchemaObjects() {
64281
64529
  const res = await getDb().execute(
@@ -64378,7 +64626,7 @@ function buildCommonsLinkset(entity, claims) {
64378
64626
  context[claim.predicate] = targets;
64379
64627
  }
64380
64628
  const document2 = { linkset: [context] };
64381
- const etag = `"${(0, import_node_crypto37.createHash)("sha256").update(JSON.stringify(document2)).digest("hex")}"`;
64629
+ const etag = `"${(0, import_node_crypto38.createHash)("sha256").update(JSON.stringify(document2)).digest("hex")}"`;
64382
64630
  return { document: document2, etag, publicUrl, linksetUrl, profile: COMMONS_RELATIONSHIP_PROFILE };
64383
64631
  }
64384
64632
  function commonsLinksetDiscoveryHeader(idOrSlug) {
@@ -64446,11 +64694,11 @@ function normalizeHreflang(value) {
64446
64694
  const languages = [...new Set(value.map((item) => optionalText(item, 80)).filter((item) => Boolean(item)))];
64447
64695
  return languages.length ? languages : void 0;
64448
64696
  }
64449
- var import_node_crypto37, COMMONS_LINKSET_MEDIA_TYPE, COMMONS_RELATIONSHIP_PROFILE, REGISTERED_RELATIONS;
64697
+ var import_node_crypto38, COMMONS_LINKSET_MEDIA_TYPE, COMMONS_RELATIONSHIP_PROFILE, REGISTERED_RELATIONS;
64450
64698
  var init_commons_linksets = __esm({
64451
64699
  "src/api/commons-linksets.ts"() {
64452
64700
  "use strict";
64453
- import_node_crypto37 = require("crypto");
64701
+ import_node_crypto38 = require("crypto");
64454
64702
  COMMONS_LINKSET_MEDIA_TYPE = "application/linkset+json";
64455
64703
  COMMONS_RELATIONSHIP_PROFILE = "https://mcpscraper.dev/commons/profiles/relationships/v1";
64456
64704
  REGISTERED_RELATIONS = /* @__PURE__ */ new Set([
@@ -64781,23 +65029,99 @@ async function searchCommonsEntities(filters = {}, userId) {
64781
65029
  const pattern = `%${escapeLike(term)}%`;
64782
65030
  args.push(pattern, pattern, pattern, pattern, pattern);
64783
65031
  }
64784
- args.push(limit, offset);
64785
- const result = await getDb().execute({
65032
+ if (!terms.length) {
65033
+ args.push(limit, offset);
65034
+ const result = await getDb().execute({
65035
+ sql: `
65036
+ SELECT *
65037
+ FROM commons_entities
65038
+ WHERE ${clauses.join(" AND ")}
65039
+ ORDER BY updated_at DESC, title ASC
65040
+ LIMIT ? OFFSET ?
65041
+ `,
65042
+ args
65043
+ });
65044
+ const results2 = result.rows.map((row) => rowToEntity(row));
65045
+ await resolveRelatedLinksAgainstPublishedEntities(results2);
65046
+ return { results: results2, filters: mergedFilters, limit, offset };
65047
+ }
65048
+ const candidates = await getDb().execute({
64786
65049
  sql: `
64787
- SELECT *
65050
+ SELECT id, title, tags_json, keywords_json, description, search_text, updated_at,
65051
+ (
65052
+ SELECT group_concat(
65053
+ coalesce(commons_claims.title, '') || ' ' ||
65054
+ coalesce(commons_claims.predicate, '') || ' ' ||
65055
+ coalesce(commons_claims.predicate_label, '') || ' ' ||
65056
+ coalesce(commons_claims.object_url, ''), ' ')
65057
+ FROM commons_claims
65058
+ WHERE commons_claims.subject_entity_id = commons_entities.id
65059
+ ) AS claim_text
64788
65060
  FROM commons_entities
64789
65061
  WHERE ${clauses.join(" AND ")}
64790
65062
  ORDER BY updated_at DESC, title ASC
64791
- LIMIT ? OFFSET ?
65063
+ LIMIT ${SEARCH_CANDIDATE_LIMIT}
64792
65064
  `,
64793
65065
  args
64794
65066
  });
64795
- return {
64796
- results: result.rows.map((row) => rowToEntity(row)),
64797
- filters: mergedFilters,
64798
- limit,
64799
- offset
64800
- };
65067
+ const ranked = candidates.rows.map((row) => {
65068
+ const record = row;
65069
+ const score = relevanceScore(terms, {
65070
+ title: String(record.title ?? ""),
65071
+ description: String(record.description ?? ""),
65072
+ tags: parseJsonArray(record.tags_json),
65073
+ keywords: parseJsonArray(record.keywords_json),
65074
+ searchText: String(record.search_text ?? ""),
65075
+ claimText: String(record.claim_text ?? "")
65076
+ });
65077
+ return { id: String(record.id), updatedAt: String(record.updated_at ?? ""), title: String(record.title ?? ""), score };
65078
+ }).filter((candidate) => candidate.score > 0).sort((left, right) => right.score - left.score || right.updatedAt.localeCompare(left.updatedAt) || left.title.localeCompare(right.title));
65079
+ const pageIds = ranked.slice(offset, offset + limit).map((candidate) => candidate.id);
65080
+ if (!pageIds.length) return { results: [], filters: mergedFilters, limit, offset };
65081
+ const page = await getDb().execute({
65082
+ sql: `SELECT * FROM commons_entities WHERE id IN (${pageIds.map(() => "?").join(", ")})`,
65083
+ args: pageIds
65084
+ });
65085
+ const byId = new Map(page.rows.map((row) => {
65086
+ const entity = rowToEntity(row);
65087
+ return [entity.id, entity];
65088
+ }));
65089
+ const results = pageIds.map((id) => byId.get(id)).filter((entity) => Boolean(entity));
65090
+ await resolveRelatedLinksAgainstPublishedEntities(results);
65091
+ return { results, filters: mergedFilters, limit, offset };
65092
+ }
65093
+ function parseJsonArray(value) {
65094
+ try {
65095
+ const parsed = JSON.parse(String(value ?? "[]"));
65096
+ return Array.isArray(parsed) ? parsed.map((item) => String(item)) : [];
65097
+ } catch {
65098
+ return [];
65099
+ }
65100
+ }
65101
+ function containsWord(haystack, term) {
65102
+ if (!haystack || !term) return false;
65103
+ const pattern = new RegExp(`(^|[^a-z0-9])${term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}([^a-z0-9]|$)`, "i");
65104
+ return pattern.test(haystack);
65105
+ }
65106
+ function relevanceScore(terms, fields) {
65107
+ let score = 0;
65108
+ const tagText = fields.tags.join(" ");
65109
+ const keywordText = fields.keywords.join(" ");
65110
+ for (const term of terms) {
65111
+ const inTitle = containsWord(fields.title, term);
65112
+ const inTags = containsWord(tagText, term) || containsWord(keywordText, term);
65113
+ const inDescription = containsWord(fields.description, term);
65114
+ const inBody = containsWord(fields.searchText, term);
65115
+ const inClaims = containsWord(fields.claimText, term);
65116
+ if (!inTitle && !inTags && !inDescription && !inBody && !inClaims) return 0;
65117
+ if (inTitle) score += 100;
65118
+ if (inTags) score += 40;
65119
+ if (inDescription) score += 20;
65120
+ if (inClaims) score += 10;
65121
+ if (inBody) score += 5;
65122
+ }
65123
+ if (terms.every((term) => containsWord(fields.title, term))) score += 50;
65124
+ return score;
64801
65125
  }
64802
65126
  async function prepareCommonsEntity(input, user) {
64803
65127
  await ensureCommonsSchema();
@@ -64944,7 +65268,10 @@ async function getCommonsEntity(idOrSlug) {
64944
65268
  `,
64945
65269
  args: [lookup2, normalizeSlug(lookup2)]
64946
65270
  });
64947
- return result.rows[0] ? rowToEntity(result.rows[0]) : null;
65271
+ if (!result.rows[0]) return null;
65272
+ const entity = rowToEntity(result.rows[0]);
65273
+ await resolveRelatedLinksAgainstPublishedEntities([entity]);
65274
+ return entity;
64948
65275
  }
64949
65276
  async function classifyCommonsWrite(input, user) {
64950
65277
  await ensureCommonsSchema();
@@ -64967,14 +65294,30 @@ async function submitCommonsEntity(input, user) {
64967
65294
  applied: existingProposal.status === "accepted",
64968
65295
  reviewReason: existingProposal.reviewReason,
64969
65296
  conflictNote: null,
65297
+ droppedImages: [],
64970
65298
  publicUrl: entity2?.status === "published" ? publicUrlForSlug(entity2.slug) : null
64971
65299
  };
64972
65300
  }
65301
+ const hostedImages = await hostEntityImages({
65302
+ userId: Number(user.id),
65303
+ featuredImage: normalized.featuredImage,
65304
+ media: normalized.media
65305
+ });
65306
+ if (hostedImages.featuredImageUrl && normalized.featuredImage) {
65307
+ normalized.featuredImage = { ...normalized.featuredImage, url: hostedImages.featuredImageUrl };
65308
+ }
65309
+ if (hostedImages.mediaUrls.size || hostedImages.dropped.length) {
65310
+ const droppedUrls = new Set(hostedImages.dropped.map((item) => item.url));
65311
+ normalized.media = normalized.media.filter((item) => !droppedUrls.has(item.url)).map((item) => {
65312
+ const hostedUrl = hostedImages.mediaUrls.get(item.url);
65313
+ return hostedUrl ? { ...item, url: hostedUrl } : item;
65314
+ });
65315
+ }
64973
65316
  const existing = await findEntityBySlugOrIdAnyStatus(normalized.slug, normalized.entityId);
64974
65317
  const existingClaims = existing && normalized.claims !== void 0 ? await getCommonsClaimsByEntityId(existing.id) : [];
64975
65318
  const safety = evaluatePublishSafety(normalized, existing);
64976
65319
  const shouldApply = safety.safe && normalized.reviewPolicy !== "always_review";
64977
- const proposalId = `commons-proposal-${(0, import_node_crypto38.randomUUID)()}`;
65320
+ const proposalId = `commons-proposal-${(0, import_node_crypto39.randomUUID)()}`;
64978
65321
  const now = (/* @__PURE__ */ new Date()).toISOString();
64979
65322
  const entityId = existing?.id ?? normalized.entityId ?? allocateEntityId();
64980
65323
  const proposalStatus = shouldApply ? "accepted" : "pending_review";
@@ -65029,7 +65372,7 @@ async function submitCommonsEntity(input, user) {
65029
65372
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
65030
65373
  `,
65031
65374
  args: [
65032
- `commons-ledger-${(0, import_node_crypto38.randomUUID)()}`,
65375
+ `commons-ledger-${(0, import_node_crypto39.randomUUID)()}`,
65033
65376
  nextEntity.id,
65034
65377
  proposalId,
65035
65378
  user.id,
@@ -65064,6 +65407,7 @@ async function submitCommonsEntity(input, user) {
65064
65407
  applied: shouldApply,
65065
65408
  reviewReason: safety.reason,
65066
65409
  conflictNote: safety.conflictNote,
65410
+ droppedImages: hostedImages.dropped,
65067
65411
  publicUrl: entity?.status === "published" && shouldApply ? publicUrlForSlug(entity.slug) : null
65068
65412
  };
65069
65413
  }
@@ -65134,7 +65478,7 @@ async function saveCommonsFilter(input, user) {
65134
65478
  sql: "SELECT id FROM commons_saved_filters WHERE user_id = ? AND name = ? LIMIT 1",
65135
65479
  args: [user.id, name]
65136
65480
  });
65137
- const id = existing.rows[0]?.id != null ? String(existing.rows[0].id) : `commons-filter-${(0, import_node_crypto38.randomUUID)()}`;
65481
+ const id = existing.rows[0]?.id != null ? String(existing.rows[0].id) : `commons-filter-${(0, import_node_crypto39.randomUUID)()}`;
65138
65482
  await getDb().execute({
65139
65483
  sql: `
65140
65484
  INSERT INTO commons_saved_filters (id, user_id, name, description, filter_json, created_at, updated_at)
@@ -65202,8 +65546,9 @@ async function listCommonsNeedsLinks(filters = {}) {
65202
65546
  });
65203
65547
  const concepts = /* @__PURE__ */ new Map();
65204
65548
  const queryTerms = tokenize(cleanFilters.query);
65205
- for (const row of result.rows) {
65206
- const entity = rowToEntity(row);
65549
+ const scanned = result.rows.map((row) => rowToEntity(row));
65550
+ await resolveRelatedLinksAgainstPublishedEntities(scanned);
65551
+ for (const entity of scanned) {
65207
65552
  for (const link2 of entity.articleSections.relatedLinks) {
65208
65553
  if (!relatedLinkNeedsPage(link2)) continue;
65209
65554
  const haystack = [link2.title, link2.summary, link2.relationship, entity.title, entity.description, entity.tags.join(" ")].filter(Boolean).join(" ").toLowerCase();
@@ -65911,7 +66256,7 @@ function commonsIndexDocuments(entity, now) {
65911
66256
  return documents;
65912
66257
  }
65913
66258
  function commonsIndexDocumentId(entityId, documentType, documentKey) {
65914
- return `commons-index-${(0, import_node_crypto38.createHash)("sha256").update(`${entityId}
66259
+ return `commons-index-${(0, import_node_crypto39.createHash)("sha256").update(`${entityId}
65915
66260
  ${documentType}
65916
66261
  ${documentKey}`).digest("hex").slice(0, 32)}`;
65917
66262
  }
@@ -66246,6 +66591,44 @@ function sanitizeNeedsLinkFilters(filters) {
66246
66591
  offset: clampNumber(filters.offset, 0, 1e4, 0)
66247
66592
  };
66248
66593
  }
66594
+ async function publishedEntityIndexFor(titles) {
66595
+ const index = /* @__PURE__ */ new Map();
66596
+ const slugKeys = uniqueStrings(titles.map((title) => normalizeNeedsLinkTitle(title))).filter(Boolean);
66597
+ const titleKeys = uniqueStrings(titles.map((title) => String(title || "").trim().toLowerCase())).filter(Boolean);
66598
+ if (!slugKeys.length && !titleKeys.length) return index;
66599
+ const result = await getDb().execute({
66600
+ sql: `
66601
+ SELECT id, slug, title
66602
+ FROM commons_entities
66603
+ WHERE status = 'published'
66604
+ AND (slug IN (${slugKeys.map(() => "?").join(", ") || "NULL"})
66605
+ OR lower(title) IN (${titleKeys.map(() => "?").join(", ") || "NULL"}))
66606
+ `,
66607
+ args: [...slugKeys, ...titleKeys]
66608
+ });
66609
+ for (const row of result.rows) {
66610
+ const record = row;
66611
+ const entry = { id: String(record.id), slug: String(record.slug), title: String(record.title) };
66612
+ index.set(entry.slug, entry);
66613
+ index.set(normalizeNeedsLinkTitle(entry.title), entry);
66614
+ }
66615
+ return index;
66616
+ }
66617
+ function unresolvedRelatedLinkTitles(entities) {
66618
+ return entities.flatMap((entity) => entity.articleSections.relatedLinks.filter((link2) => relatedLinkNeedsPage(link2)).map((link2) => link2.title));
66619
+ }
66620
+ async function resolveRelatedLinksAgainstPublishedEntities(entities) {
66621
+ const index = await publishedEntityIndexFor(unresolvedRelatedLinkTitles(entities));
66622
+ if (!index.size) return;
66623
+ for (const entity of entities) {
66624
+ entity.articleSections.relatedLinks = entity.articleSections.relatedLinks.map((link2) => {
66625
+ if (!relatedLinkNeedsPage(link2)) return link2;
66626
+ const match = index.get(normalizeNeedsLinkTitle(link2.title));
66627
+ if (!match || match.id === entity.id) return link2;
66628
+ return { ...link2, slug: match.slug, entityId: match.id, needsLink: false };
66629
+ });
66630
+ }
66631
+ }
66249
66632
  function relatedLinkNeedsPage(link2) {
66250
66633
  if (link2.needsLink === true) return true;
66251
66634
  if (link2.entityId || link2.slug) return false;
@@ -66438,12 +66821,13 @@ function jsonLikeValue(value) {
66438
66821
  function escapeLike(value) {
66439
66822
  return value.replace(/[%_]/g, "");
66440
66823
  }
66441
- var import_node_crypto38, COMMONS_SCHEMA_VERSION, DEFAULT_COMMONS_BASE_URL, DEFAULT_ENTITY_TYPE, COMMONS_ENTITY_PROFILES, schemaReady, COMMONS_SCHEMA_OBJECTS, CommonsRepositoryError;
66824
+ var import_node_crypto39, COMMONS_SCHEMA_VERSION, DEFAULT_COMMONS_BASE_URL, DEFAULT_ENTITY_TYPE, COMMONS_ENTITY_PROFILES, schemaReady, COMMONS_SCHEMA_OBJECTS, SEARCH_CANDIDATE_LIMIT, CommonsRepositoryError;
66442
66825
  var init_commons_repository = __esm({
66443
66826
  "src/api/commons-repository.ts"() {
66444
66827
  "use strict";
66445
- import_node_crypto38 = require("crypto");
66828
+ import_node_crypto39 = require("crypto");
66446
66829
  init_db();
66830
+ init_commons_image_store();
66447
66831
  init_schema_presence();
66448
66832
  init_rates();
66449
66833
  init_commons_linksets();
@@ -66621,6 +67005,7 @@ var init_commons_repository = __esm({
66621
67005
  "commons_publications_updated",
66622
67006
  "commons_publication_editions_publication_published"
66623
67007
  ];
67008
+ SEARCH_CANDIDATE_LIMIT = 500;
66624
67009
  CommonsRepositoryError = class extends Error {
66625
67010
  constructor(code, message, httpStatus = 400) {
66626
67011
  super(message);
@@ -66723,7 +67108,7 @@ async function claimCommonsPublication(input, user) {
66723
67108
  }
66724
67109
  const existingName = await getCommonsPublicationBySubdomain(subdomain);
66725
67110
  if (existingName) throw new CommonsPublicationError("publication_name_unavailable", "That publication name is already claimed.", 409);
66726
- const id = `tcpub_${(0, import_node_crypto39.randomUUID)()}`;
67111
+ const id = `tcpub_${(0, import_node_crypto40.randomUUID)()}`;
66727
67112
  const now = (/* @__PURE__ */ new Date()).toISOString();
66728
67113
  try {
66729
67114
  await getDb().execute({
@@ -66775,7 +67160,7 @@ async function publishCommonsEditorial(input, user) {
66775
67160
  const canonicalUrl = editionPublicUrl(subdomain, editionSlug);
66776
67161
  const rendered = renderEditorialReadingRoom(editionInput);
66777
67162
  const html = addPublicMetadata(rendered.html, canonicalUrl, publication.title);
66778
- const editionId = `tced_${(0, import_node_crypto39.randomUUID)()}`;
67163
+ const editionId = `tced_${(0, import_node_crypto40.randomUUID)()}`;
66779
67164
  const revision = (latest?.revision ?? 0) + 1;
66780
67165
  const now = (/* @__PURE__ */ new Date()).toISOString();
66781
67166
  await getDb().batch([
@@ -66799,7 +67184,7 @@ async function publishCommonsEditorial(input, user) {
66799
67184
  JSON.stringify(editionInput.articles),
66800
67185
  html,
66801
67186
  rendered.filename,
66802
- (0, import_node_crypto39.createHash)("sha256").update(html).digest("hex"),
67187
+ (0, import_node_crypto40.createHash)("sha256").update(html).digest("hex"),
66803
67188
  rendered.articleCount,
66804
67189
  rendered.wordCount,
66805
67190
  Buffer.byteLength(html),
@@ -66985,11 +67370,11 @@ function addPublicMetadata(html, canonicalUrl, publicationTitle) {
66985
67370
  "</head>"
66986
67371
  ].join("\n"));
66987
67372
  }
66988
- var import_node_crypto39, PUBLICATION_ROOT_DOMAIN, RESERVED_SUBDOMAINS, CommonsPublicationError;
67373
+ var import_node_crypto40, PUBLICATION_ROOT_DOMAIN, RESERVED_SUBDOMAINS, CommonsPublicationError;
66989
67374
  var init_commons_publication_repository = __esm({
66990
67375
  "src/api/commons-publication-repository.ts"() {
66991
67376
  "use strict";
66992
- import_node_crypto39 = require("crypto");
67377
+ import_node_crypto40 = require("crypto");
66993
67378
  init_db();
66994
67379
  init_commons_repository();
66995
67380
  init_render();
@@ -67090,7 +67475,7 @@ function publicationError(c, error) {
67090
67475
  function xmlEscape(value) {
67091
67476
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
67092
67477
  }
67093
- var import_hono31, import_zod52, commonsApp, auth2, SearchBodySchema, FeaturedImageSchema, MediaSchema, CitationSchema, RelatedLinkSchema, SubmitEntitySchema, PrepareEntitySchema, ValidateEntitySchema, SaveFilterSchema, NeedsLinkBodySchema, PreparePublicationSchema, ValidatePublicationSchema, ClaimPublicationSchema, PublishEditorialSchema;
67478
+ var import_hono31, import_zod52, commonsApp, auth2, SearchBodySchema, FeaturedImageSchema, MediaSchema, CitationSchema, RelatedLinkSchema, SubmitEntitySchema, PrepareEntitySchema, ValidateEntitySchema, SaveFilterSchema, NeedsLinkBodySchema, PreparePublicationSchema, ValidatePublicationSchema, ClaimPublicationSchema, PublishEditorialSchema, HostImageSchema;
67094
67479
  var init_commons_routes = __esm({
67095
67480
  "src/api/commons-routes.ts"() {
67096
67481
  "use strict";
@@ -67098,6 +67483,7 @@ var init_commons_routes = __esm({
67098
67483
  import_zod52 = require("zod");
67099
67484
  init_api_auth();
67100
67485
  init_commons_repository();
67486
+ init_commons_image_store();
67101
67487
  init_commons_linksets();
67102
67488
  init_listing_write_billing();
67103
67489
  init_commons_publication_repository();
@@ -67351,6 +67737,28 @@ var init_commons_routes = __esm({
67351
67737
  return publicationError(c, error);
67352
67738
  }
67353
67739
  });
67740
+ HostImageSchema = import_zod52.z.object({
67741
+ sourceUrl: import_zod52.z.string().url().optional(),
67742
+ imageBase64: import_zod52.z.string().min(16).optional(),
67743
+ alt: import_zod52.z.string().max(500).optional(),
67744
+ license: import_zod52.z.string().max(240).optional(),
67745
+ attribution: import_zod52.z.string().max(500).optional()
67746
+ }).strict().refine((body) => Boolean(body.sourceUrl || body.imageBase64), {
67747
+ message: "Provide either sourceUrl or imageBase64."
67748
+ });
67749
+ commonsApp.post("/images", auth2, async (c) => {
67750
+ const parsed = HostImageSchema.safeParse(await c.req.json().catch(() => ({})));
67751
+ if (!parsed.success) return validationError(c, parsed.error);
67752
+ try {
67753
+ const hosted = await hostCommonsImage({ ...parsed.data, userId: Number(c.get("user").id) });
67754
+ return c.json({ ok: true, data: hosted }, hosted.reused ? 200 : 201);
67755
+ } catch (error) {
67756
+ if (error instanceof CommonsImageError) {
67757
+ return c.json({ ok: false, error: error.code, message: error.message }, error.httpStatus);
67758
+ }
67759
+ throw error;
67760
+ }
67761
+ });
67354
67762
  commonsApp.get("/entities", async (c) => {
67355
67763
  const result = await searchCommonsEntities(filtersFromQuery(c.req.query()));
67356
67764
  return c.json({ ok: true, data: result });
@@ -68152,7 +68560,7 @@ async function migrateAnalytics() {
68152
68560
  }
68153
68561
  function normalizeSlug2(value) {
68154
68562
  const slug4 = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
68155
- return slug4 || `site-${(0, import_node_crypto40.randomBytes)(4).toString("hex")}`;
68563
+ return slug4 || `site-${(0, import_node_crypto41.randomBytes)(4).toString("hex")}`;
68156
68564
  }
68157
68565
  function normalizeObservedHostname(origin) {
68158
68566
  if (!origin || origin === "null") return null;
@@ -68165,7 +68573,7 @@ function normalizeObservedHostname(origin) {
68165
68573
  }
68166
68574
  }
68167
68575
  function publicPixelId() {
68168
- return `px_${(0, import_node_crypto40.randomBytes)(18).toString("base64url")}`;
68576
+ return `px_${(0, import_node_crypto41.randomBytes)(18).toString("base64url")}`;
68169
68577
  }
68170
68578
  function mapRows(rows) {
68171
68579
  return rows;
@@ -68197,7 +68605,7 @@ async function requireEditor(client2, siteId, userId) {
68197
68605
  async function createAnalyticsSite(input) {
68198
68606
  const db = getAnalyticsPool();
68199
68607
  const client2 = await db.connect();
68200
- const id = (0, import_node_crypto40.randomUUID)();
68608
+ const id = (0, import_node_crypto41.randomUUID)();
68201
68609
  const baseSlug = normalizeSlug2(input.slug || input.name);
68202
68610
  try {
68203
68611
  await client2.query("BEGIN");
@@ -68213,7 +68621,7 @@ async function createAnalyticsSite(input) {
68213
68621
  } catch (error) {
68214
68622
  const code = error.code;
68215
68623
  if (code !== "23505" || attempt === 2) throw error;
68216
- slug4 = `${baseSlug}-${(0, import_node_crypto40.randomBytes)(2).toString("hex")}`;
68624
+ slug4 = `${baseSlug}-${(0, import_node_crypto41.randomBytes)(2).toString("hex")}`;
68217
68625
  }
68218
68626
  }
68219
68627
  await client2.query(
@@ -68428,7 +68836,7 @@ async function createAnalyticsPixel(input) {
68428
68836
  VALUES ($1, $2, $3, $4, $5)
68429
68837
  RETURNING id, site_id, public_id, name, environment, status, created_at::text, NULL::text AS last_event_at`,
68430
68838
  [
68431
- (0, import_node_crypto40.randomUUID)(),
68839
+ (0, import_node_crypto41.randomUUID)(),
68432
68840
  input.siteId,
68433
68841
  publicPixelId(),
68434
68842
  input.name.trim(),
@@ -68505,7 +68913,7 @@ async function setAnalyticsPixelDomainState(input) {
68505
68913
  SELECT $1, p.id, $4, $5 FROM analytics_pixels p WHERE p.id = $2 AND p.site_id = $3
68506
68914
  ON CONFLICT(pixel_id, hostname) DO UPDATE SET state = EXCLUDED.state, updated_at = now()
68507
68915
  RETURNING id`,
68508
- [(0, import_node_crypto40.randomUUID)(), input.pixelId, input.siteId, hostname, input.state]
68916
+ [(0, import_node_crypto41.randomUUID)(), input.pixelId, input.siteId, hostname, input.state]
68509
68917
  );
68510
68918
  if (!result.rowCount)
68511
68919
  throw new AnalyticsRepositoryError(
@@ -68555,7 +68963,7 @@ function sanitizeAnalyticsProperties(value) {
68555
68963
  }
68556
68964
  async function ingestAnalyticsEvents(input) {
68557
68965
  const db = getAnalyticsPool();
68558
- const requestId = `air_${(0, import_node_crypto40.randomUUID)()}`;
68966
+ const requestId = `air_${(0, import_node_crypto41.randomUUID)()}`;
68559
68967
  const hostname = normalizeObservedHostname(input.origin);
68560
68968
  const pixelResult = await db.query(
68561
68969
  `SELECT p.id, p.site_id, p.status,
@@ -68576,7 +68984,7 @@ async function ingestAnalyticsEvents(input) {
68576
68984
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
68577
68985
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
68578
68986
  [
68579
- (0, import_node_crypto40.randomUUID)(),
68987
+ (0, import_node_crypto41.randomUUID)(),
68580
68988
  requestId,
68581
68989
  pixel?.site_id ?? null,
68582
68990
  pixel?.id ?? null,
@@ -68601,7 +69009,7 @@ async function ingestAnalyticsEvents(input) {
68601
69009
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
68602
69010
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
68603
69011
  [
68604
- (0, import_node_crypto40.randomUUID)(),
69012
+ (0, import_node_crypto41.randomUUID)(),
68605
69013
  requestId,
68606
69014
  pixel.site_id,
68607
69015
  pixel.id,
@@ -68626,7 +69034,7 @@ async function ingestAnalyticsEvents(input) {
68626
69034
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, rejected_count, reason_codes)
68627
69035
  VALUES ($1, $2, $3, $4, $5, $6::jsonb)`,
68628
69036
  [
68629
- (0, import_node_crypto40.randomUUID)(),
69037
+ (0, import_node_crypto41.randomUUID)(),
68630
69038
  requestId,
68631
69039
  pixel.site_id,
68632
69040
  pixel.id,
@@ -68650,7 +69058,7 @@ async function ingestAnalyticsEvents(input) {
68650
69058
  ON CONFLICT(pixel_id, hostname) DO UPDATE
68651
69059
  SET last_seen_at = now(), updated_at = now()
68652
69060
  RETURNING state`,
68653
- [(0, import_node_crypto40.randomUUID)(), pixel.id, hostname]
69061
+ [(0, import_node_crypto41.randomUUID)(), pixel.id, hostname]
68654
69062
  );
68655
69063
  if (domainResult.rows[0]?.state !== "approved") {
68656
69064
  await db.query(
@@ -68664,7 +69072,7 @@ async function ingestAnalyticsEvents(input) {
68664
69072
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
68665
69073
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
68666
69074
  [
68667
- (0, import_node_crypto40.randomUUID)(),
69075
+ (0, import_node_crypto41.randomUUID)(),
68668
69076
  requestId,
68669
69077
  pixel.site_id,
68670
69078
  pixel.id,
@@ -68714,7 +69122,7 @@ async function ingestAnalyticsEvents(input) {
68714
69122
  $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33
68715
69123
  ) ON CONFLICT(site_id, event_id) DO NOTHING`,
68716
69124
  [
68717
- (0, import_node_crypto40.randomUUID)(),
69125
+ (0, import_node_crypto41.randomUUID)(),
68718
69126
  event2.eventId,
68719
69127
  pixel.site_id,
68720
69128
  pixel.id,
@@ -68772,7 +69180,7 @@ async function ingestAnalyticsEvents(input) {
68772
69180
  id, request_id, site_id, pixel_id, hostname, accepted_count, rejected_count, reason_codes
68773
69181
  ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb)`,
68774
69182
  [
68775
- (0, import_node_crypto40.randomUUID)(),
69183
+ (0, import_node_crypto41.randomUUID)(),
68776
69184
  requestId,
68777
69185
  pixel.site_id,
68778
69186
  pixel.id,
@@ -68804,7 +69212,7 @@ function normalizeGeographyCode(value, max) {
68804
69212
  return normalized && /^[A-Z0-9-]+$/.test(normalized) ? normalized.slice(0, max) : null;
68805
69213
  }
68806
69214
  function pageFingerprint(scope, siteId, filters) {
68807
- return (0, import_node_crypto40.createHash)("sha256").update(JSON.stringify({ scope, siteId, filters })).digest("base64url").slice(0, 18);
69215
+ return (0, import_node_crypto41.createHash)("sha256").update(JSON.stringify({ scope, siteId, filters })).digest("base64url").slice(0, 18);
68808
69216
  }
68809
69217
  function decodePageOffset(cursor, fingerprint) {
68810
69218
  if (!cursor) return 0;
@@ -68854,7 +69262,7 @@ async function createAnalyticsConversion(input) {
68854
69262
  404
68855
69263
  );
68856
69264
  }
68857
- const id = (0, import_node_crypto40.randomUUID)();
69265
+ const id = (0, import_node_crypto41.randomUUID)();
68858
69266
  const resolvedPerson = input.sessionId ? await db.query(
68859
69267
  `SELECT e.person_id FROM analytics_identity_nodes n JOIN analytics_identity_edges e ON e.identity_node_id = n.id
68860
69268
  WHERE n.site_id = $1 AND n.kind = 'session_id' AND n.value_hmac = $2 ORDER BY e.confidence DESC LIMIT 1`,
@@ -69603,7 +70011,7 @@ async function createAnalyticsCampaignLink(input) {
69603
70011
  404
69604
70012
  );
69605
70013
  }
69606
- const shortCode = (input.shortCode?.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-") || (0, import_node_crypto40.randomBytes)(5).toString("base64url").toLowerCase()).slice(0, 48);
70014
+ const shortCode = (input.shortCode?.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-") || (0, import_node_crypto41.randomBytes)(5).toString("base64url").toLowerCase()).slice(0, 48);
69607
70015
  try {
69608
70016
  const result = await db.query(
69609
70017
  `INSERT INTO analytics_campaign_links(
@@ -69612,7 +70020,7 @@ async function createAnalyticsCampaignLink(input) {
69612
70020
  ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)
69613
70021
  RETURNING *, 0::int AS click_count`,
69614
70022
  [
69615
- (0, import_node_crypto40.randomUUID)(),
70023
+ (0, import_node_crypto41.randomUUID)(),
69616
70024
  input.siteId,
69617
70025
  input.pixelId ?? null,
69618
70026
  input.name.trim(),
@@ -69683,7 +70091,7 @@ async function resolveAnalyticsCampaignLink(shortCode, referrer) {
69683
70091
  if (!row) return null;
69684
70092
  await db.query(
69685
70093
  `INSERT INTO analytics_campaign_clicks(id, link_id, referrer) VALUES ($1, $2, $3)`,
69686
- [(0, import_node_crypto40.randomUUID)(), row.id, normalizeAnalyticsUrl(referrer || void 0)]
70094
+ [(0, import_node_crypto41.randomUUID)(), row.id, normalizeAnalyticsUrl(referrer || void 0)]
69687
70095
  );
69688
70096
  return buildTaggedCampaignUrl(row);
69689
70097
  }
@@ -69701,14 +70109,14 @@ async function createAnalyticsForm(input) {
69701
70109
  404
69702
70110
  );
69703
70111
  const baseSlug = normalizeSlug2(input.name);
69704
- const slug4 = `${baseSlug}-${(0, import_node_crypto40.randomBytes)(3).toString("hex")}`;
69705
- const publicId = `form_${(0, import_node_crypto40.randomBytes)(18).toString("base64url")}`;
70112
+ const slug4 = `${baseSlug}-${(0, import_node_crypto41.randomBytes)(3).toString("hex")}`;
70113
+ const publicId = `form_${(0, import_node_crypto41.randomBytes)(18).toString("base64url")}`;
69706
70114
  const result = await db.query(
69707
70115
  `INSERT INTO analytics_forms(id, public_id, site_id, pixel_id, name, slug, fields, brand, submit_label, success_message, redirect_url, consent_text, status, created_by_user_id)
69708
70116
  VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8::jsonb,$9,$10,$11,$12,$13,$14)
69709
70117
  RETURNING *, 0::int AS submission_count`,
69710
70118
  [
69711
- (0, import_node_crypto40.randomUUID)(),
70119
+ (0, import_node_crypto41.randomUUID)(),
69712
70120
  publicId,
69713
70121
  input.siteId,
69714
70122
  input.pixelId,
@@ -69753,7 +70161,7 @@ async function getPublicAnalyticsForm(publicId) {
69753
70161
  return result.rows[0] ?? null;
69754
70162
  }
69755
70163
  async function recordAnalyticsFormSubmission(input) {
69756
- const id = (0, import_node_crypto40.randomUUID)();
70164
+ const id = (0, import_node_crypto41.randomUUID)();
69757
70165
  await getAnalyticsPool().query(
69758
70166
  `INSERT INTO analytics_form_submissions(id, form_id, site_id, pixel_id, visitor_id, session_id, crm_person_ref, source, medium, campaign, crm_delivery_status, click_ids)
69759
70167
  VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12::jsonb)`,
@@ -69784,7 +70192,7 @@ function sanitizeClickIds(value) {
69784
70192
  return output;
69785
70193
  }
69786
70194
  function identityHmac(kind, value) {
69787
- return (0, import_node_crypto40.createHmac)("sha256", getSessionSecret()).update(`${kind}:${value.trim().toLowerCase()}`).digest("hex");
70195
+ return (0, import_node_crypto41.createHmac)("sha256", getSessionSecret()).update(`${kind}:${value.trim().toLowerCase()}`).digest("hex");
69788
70196
  }
69789
70197
  async function linkAnalyticsFormIdentity(input) {
69790
70198
  const client2 = await getAnalyticsPool().connect();
@@ -69793,7 +70201,7 @@ async function linkAnalyticsFormIdentity(input) {
69793
70201
  const person = await client2.query(
69794
70202
  `INSERT INTO analytics_people(id, site_id, crm_person_ref) VALUES ($1,$2,$3)
69795
70203
  ON CONFLICT(site_id, crm_person_ref) DO UPDATE SET last_seen_at = now() RETURNING id`,
69796
- [(0, import_node_crypto40.randomUUID)(), input.siteId, input.crmPersonRef]
70204
+ [(0, import_node_crypto41.randomUUID)(), input.siteId, input.crmPersonRef]
69797
70205
  );
69798
70206
  const personId = person.rows[0].id;
69799
70207
  const signals = [];
@@ -69840,7 +70248,7 @@ async function linkAnalyticsFormIdentity(input) {
69840
70248
  `INSERT INTO analytics_identity_nodes(id, site_id, kind, value_hmac) VALUES ($1,$2,$3,$4)
69841
70249
  ON CONFLICT(site_id, kind, value_hmac) DO UPDATE SET last_seen_at = now() RETURNING id`,
69842
70250
  [
69843
- (0, import_node_crypto40.randomUUID)(),
70251
+ (0, import_node_crypto41.randomUUID)(),
69844
70252
  input.siteId,
69845
70253
  signal.kind,
69846
70254
  identityHmac(signal.kind, signal.value)
@@ -69851,7 +70259,7 @@ async function linkAnalyticsFormIdentity(input) {
69851
70259
  VALUES ($1,$2,$3,$4,$5,$6)
69852
70260
  ON CONFLICT(person_id, identity_node_id, evidence_kind) DO UPDATE SET last_seen_at = now(), confidence = greatest(analytics_identity_edges.confidence, EXCLUDED.confidence)`,
69853
70261
  [
69854
- (0, import_node_crypto40.randomUUID)(),
70262
+ (0, import_node_crypto41.randomUUID)(),
69855
70263
  input.siteId,
69856
70264
  personId,
69857
70265
  node.rows[0].id,
@@ -69949,7 +70357,7 @@ async function createAnalyticsCrmImport(input) {
69949
70357
  const db = getAnalyticsPool();
69950
70358
  await requireEditor(db, input.siteId, input.userId);
69951
70359
  const client2 = await db.connect();
69952
- const id = (0, import_node_crypto40.randomUUID)();
70360
+ const id = (0, import_node_crypto41.randomUUID)();
69953
70361
  try {
69954
70362
  await client2.query("BEGIN");
69955
70363
  await client2.query(
@@ -69969,7 +70377,7 @@ async function createAnalyticsCrmImport(input) {
69969
70377
  await client2.query(
69970
70378
  `INSERT INTO analytics_crm_import_rows(id, import_id, crm_person_ref, payload_ciphertext)
69971
70379
  VALUES ($1,$2,$3,$4) ON CONFLICT(import_id, crm_person_ref) DO NOTHING`,
69972
- [(0, import_node_crypto40.randomUUID)(), id, row.crmPersonRef, row.payloadCiphertext]
70380
+ [(0, import_node_crypto41.randomUUID)(), id, row.crmPersonRef, row.payloadCiphertext]
69973
70381
  );
69974
70382
  }
69975
70383
  await client2.query("COMMIT");
@@ -70008,7 +70416,7 @@ async function createAnalyticsActivationDestination(input) {
70008
70416
  `INSERT INTO analytics_activation_destinations(id, site_id, platform, name, connection_ref, external_dataset_id, event_mapping, created_by_user_id)
70009
70417
  VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8) RETURNING *`,
70010
70418
  [
70011
- (0, import_node_crypto40.randomUUID)(),
70419
+ (0, import_node_crypto41.randomUUID)(),
70012
70420
  input.siteId,
70013
70421
  input.platform,
70014
70422
  input.name.trim(),
@@ -70060,7 +70468,7 @@ async function queueAnalyticsActivation(input) {
70060
70468
  `INSERT INTO analytics_activation_jobs(id, destination_id, conversion_id, person_id, payload_ciphertext)
70061
70469
  VALUES ($1,$2,$3,$4,$5) ON CONFLICT(destination_id, conversion_id) DO NOTHING`,
70062
70470
  [
70063
- (0, import_node_crypto40.randomUUID)(),
70471
+ (0, import_node_crypto41.randomUUID)(),
70064
70472
  destination.id,
70065
70473
  input.conversionId,
70066
70474
  input.personId ?? null,
@@ -70072,7 +70480,7 @@ async function queueAnalyticsActivation(input) {
70072
70480
  return queued;
70073
70481
  }
70074
70482
  async function queueAnalyticsFormDelivery(input) {
70075
- const id = (0, import_node_crypto40.randomUUID)();
70483
+ const id = (0, import_node_crypto41.randomUUID)();
70076
70484
  await getAnalyticsPool().query(
70077
70485
  `INSERT INTO analytics_form_delivery_jobs(id, submission_id, owner_user_id, payload_ciphertext, last_error_code)
70078
70486
  VALUES ($1,$2,$3,$4,$5)`,
@@ -70199,7 +70607,7 @@ async function analyticsHealth(siteId, userId) {
70199
70607
  }
70200
70608
  async function refreshAnalyticsDailyRollups(input) {
70201
70609
  const db = getAnalyticsPool();
70202
- const runId = (0, import_node_crypto40.randomUUID)();
70610
+ const runId = (0, import_node_crypto41.randomUUID)();
70203
70611
  await db.query(
70204
70612
  `INSERT INTO analytics_rollup_runs(id, window_start, window_end, status) VALUES ($1, $2, $3, 'running')`,
70205
70613
  [runId, input.start, input.end]
@@ -70396,7 +70804,7 @@ async function createAnalyticsExport(input) {
70396
70804
  `Generated ${(/* @__PURE__ */ new Date()).toISOString()} from the governed ${input.report} report contract.`
70397
70805
  ].join("\n");
70398
70806
  }
70399
- const id = (0, import_node_crypto40.randomUUID)();
70807
+ const id = (0, import_node_crypto41.randomUUID)();
70400
70808
  const inserted = await getAnalyticsPool().query(
70401
70809
  `INSERT INTO analytics_exports(id, site_id, requested_by_user_id, idempotency_key, report, format, filters, content)
70402
70810
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8)
@@ -70429,11 +70837,11 @@ async function createAnalyticsExport(input) {
70429
70837
  }
70430
70838
  return describeArtifact(replay.rows[0]);
70431
70839
  }
70432
- var import_node_crypto40, import_pg, AnalyticsRepositoryError, pool2, MAX_ENGAGED_MS, ENGAGED_SESSION_MS, blockedPropertyName, inferredFamilySql, ANALYTICS_CONTENT_SORTS, clickIdKeys;
70840
+ var import_node_crypto41, import_pg, AnalyticsRepositoryError, pool2, MAX_ENGAGED_MS, ENGAGED_SESSION_MS, blockedPropertyName, inferredFamilySql, ANALYTICS_CONTENT_SORTS, clickIdKeys;
70433
70841
  var init_analytics_repository = __esm({
70434
70842
  "src/api/analytics-repository.ts"() {
70435
70843
  "use strict";
70436
- import_node_crypto40 = require("crypto");
70844
+ import_node_crypto41 = require("crypto");
70437
70845
  import_pg = require("pg");
70438
70846
  init_session();
70439
70847
  init_analytics_attribution();
@@ -70672,8 +71080,8 @@ function dashboardCallbackUrl() {
70672
71080
  }
70673
71081
  async function createThorbitConnectUrl(userId) {
70674
71082
  if (!bridgeConfigured()) throw new Error("Thorbit X-Ray account bridge is not configured");
70675
- const state = (0, import_node_crypto41.randomBytes)(32).toString("base64url");
70676
- const stateHash = (0, import_node_crypto41.createHash)("sha256").update(state).digest("hex");
71083
+ const state = (0, import_node_crypto42.randomBytes)(32).toString("base64url");
71084
+ const stateHash = (0, import_node_crypto42.createHash)("sha256").update(state).digest("hex");
70677
71085
  await getAnalyticsPool().query(
70678
71086
  `INSERT INTO analytics_thorbit_connect_states(state_hash,user_id,expires_at)
70679
71087
  VALUES ($1,$2,now()+interval '10 minutes')
@@ -70688,9 +71096,9 @@ async function createThorbitConnectUrl(userId) {
70688
71096
  function verifyThorbitAssertion(token6) {
70689
71097
  const [encoded, signature] = token6.split(".");
70690
71098
  if (!encoded || !signature || !bridgeConfigured()) throw new Error("Invalid Thorbit assertion");
70691
- const expected = (0, import_node_crypto41.createHmac)("sha256", bridgeSecret()).update(encoded).digest();
71099
+ const expected = (0, import_node_crypto42.createHmac)("sha256", bridgeSecret()).update(encoded).digest();
70692
71100
  const actual = Buffer.from(signature, "base64url");
70693
- if (actual.length !== expected.length || !(0, import_node_crypto41.timingSafeEqual)(actual, expected)) {
71101
+ if (actual.length !== expected.length || !(0, import_node_crypto42.timingSafeEqual)(actual, expected)) {
70694
71102
  throw new Error("Invalid Thorbit assertion signature");
70695
71103
  }
70696
71104
  const parsed = AssertionSchema.safeParse(
@@ -70702,7 +71110,7 @@ function verifyThorbitAssertion(token6) {
70702
71110
  return parsed.data.entitlement;
70703
71111
  }
70704
71112
  async function consumeThorbitConnectCallback(input) {
70705
- const stateHash = (0, import_node_crypto41.createHash)("sha256").update(input.state).digest("hex");
71113
+ const stateHash = (0, import_node_crypto42.createHash)("sha256").update(input.state).digest("hex");
70706
71114
  const client2 = await getAnalyticsPool().connect();
70707
71115
  try {
70708
71116
  await client2.query("BEGIN");
@@ -70742,11 +71150,11 @@ async function disconnectAnalyticsEntitlement(userId) {
70742
71150
  [userId]
70743
71151
  );
70744
71152
  }
70745
- var import_node_crypto41, import_zod53, THORBIT_PRODUCT_URL, REFRESH_INTERVAL_MS, ELIGIBLE_GRACE_MS, TRIAL_LENGTH_MS, ThorbitEntitlementSchema, AssertionSchema;
71153
+ var import_node_crypto42, import_zod53, THORBIT_PRODUCT_URL, REFRESH_INTERVAL_MS, ELIGIBLE_GRACE_MS, TRIAL_LENGTH_MS, ThorbitEntitlementSchema, AssertionSchema;
70746
71154
  var init_analytics_entitlement = __esm({
70747
71155
  "src/api/analytics-entitlement.ts"() {
70748
71156
  "use strict";
70749
- import_node_crypto41 = require("crypto");
71157
+ import_node_crypto42 = require("crypto");
70750
71158
  import_zod53 = require("zod");
70751
71159
  init_analytics_repository();
70752
71160
  THORBIT_PRODUCT_URL = "https://thorbit.ai";
@@ -70886,14 +71294,14 @@ function renderPublicForm(form, placementUrl) {
70886
71294
  const brand = form.brand;
70887
71295
  return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>:root{font-family:ui-sans-serif,system-ui;color:${escapeHtml7(brand.textColor)};background:${escapeHtml7(brand.backgroundColor)}}*{box-sizing:border-box}body{margin:0;padding:18px}form{display:grid;gap:14px}label{display:grid;gap:6px;font-size:13px;font-weight:650}.check{grid-template-columns:auto 1fr;align-items:center}.check span{grid-column:2}.check input{grid-column:1;grid-row:1}input,textarea,select{width:100%;min-height:44px;padding:10px 12px;border:1px solid #ccd3df;border-radius:${brand.radius}px;background:white;color:inherit;font:inherit}textarea{min-height:110px;resize:vertical}button{min-height:46px;border:0;border-radius:${brand.radius}px;color:white;background:${escapeHtml7(brand.primaryColor)};font:inherit;font-weight:750;cursor:pointer}.consent{margin:0;color:#667085;font-size:11px;line-height:1.45}.notice{display:none;padding:12px;border-radius:${brand.radius}px;background:#edf8f2;color:#17613c}.hp{position:absolute!important;left:-9999px!important}</style></head><body><form id="mcp-form">${fields}<label class="hp">Website<input name="website" autocomplete="off" tabindex="-1"></label>${form.consent_text ? `<p class="consent">${escapeHtml7(form.consent_text)}</p>` : ""}<button>${escapeHtml7(form.submit_label)}</button><div class="notice" role="status"></div></form><script>(()=>{const form=document.querySelector('#mcp-form'),notice=form.querySelector('.notice'),q=new URLSearchParams(location.search);form.addEventListener('submit',async event=>{event.preventDefault();const button=form.querySelector('button');button.disabled=true;const raw=Object.fromEntries(new FormData(form).entries()),website=String(raw.website||''),clickIds={};delete raw.website;for(const k of ['fbclid','gclid','gbraid','wbraid','ttclid','rdt_cid','msclkid']){const v=q.get(k);if(v)clickIds[k]=v}try{const response=await fetch('/analytics/forms/${escapeHtml7(form.public_id)}/submissions',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({data:raw,website,placementUrl:${JSON.stringify(placementUrl)},visitorId:q.get('visitor')||undefined,sessionId:q.get('session')||undefined,source:q.get('source')||undefined,medium:q.get('medium')||undefined,campaign:q.get('campaign')||undefined,clickIds})});if(!response.ok)throw new Error('submit');notice.textContent=${JSON.stringify(form.success_message)};notice.style.display='block';form.reset();${form.redirect_url ? `setTimeout(()=>{top.location.href=${JSON.stringify(form.redirect_url)}},700);` : ""}}catch{notice.textContent='Your response could not be submitted. Please try again.';notice.style.display='block'}finally{button.disabled=false}})})()</script></body></html>`;
70888
71296
  }
70889
- var import_hono32, import_factory6, import_zod54, import_node_crypto42, import_papaparse4, analyticsApp, auth3, entitlementGuard, ThorbitApiKeySchema, ThorbitCallbackSchema, SiteInputSchema, PixelInputSchema, PixelUpdateSchema, EventSchema, IngestionSchema, ConversionSchema, ExportSchema2, CampaignLinkSchema, FormFieldSchema, FormInputSchema, FormSubmissionSchema, CrmImportSchema, ActivationDestinationSchema, BusinessModelSchema, AdSpendSchema;
71297
+ var import_hono32, import_factory6, import_zod54, import_node_crypto43, import_papaparse4, analyticsApp, auth3, entitlementGuard, ThorbitApiKeySchema, ThorbitCallbackSchema, SiteInputSchema, PixelInputSchema, PixelUpdateSchema, EventSchema, IngestionSchema, ConversionSchema, ExportSchema2, CampaignLinkSchema, FormFieldSchema, FormInputSchema, FormSubmissionSchema, CrmImportSchema, ActivationDestinationSchema, BusinessModelSchema, AdSpendSchema;
70890
71298
  var init_analytics_routes = __esm({
70891
71299
  "src/api/analytics-routes.ts"() {
70892
71300
  "use strict";
70893
71301
  import_hono32 = require("hono");
70894
71302
  import_factory6 = require("hono/factory");
70895
71303
  import_zod54 = require("zod");
70896
- import_node_crypto42 = require("crypto");
71304
+ import_node_crypto43 = require("crypto");
70897
71305
  import_papaparse4 = __toESM(require("papaparse"), 1);
70898
71306
  init_api_auth();
70899
71307
  init_db();
@@ -71224,7 +71632,7 @@ var init_analytics_routes = __esm({
71224
71632
  const lastName = typeof data.last_name === "string" ? data.last_name.trim() : "";
71225
71633
  const fullName = [firstName, lastName].filter(Boolean).join(" ") || (typeof data.name === "string" ? data.name.trim() : "") || email || "Website lead";
71226
71634
  const identitySeed = email || `${fullName}:${Date.now()}`;
71227
- const suffix2 = (0, import_node_crypto42.createHash)("sha256").update(identitySeed).digest("hex").slice(0, 12);
71635
+ const suffix2 = (0, import_node_crypto43.createHash)("sha256").update(identitySeed).digest("hex").slice(0, 12);
71228
71636
  const path6 = `Leads/person-${suffix2}`;
71229
71637
  const { key: memoryKey, error: memoryKeyError } = await getOrCreateUserMemoryKey(user);
71230
71638
  const existing = memoryKey ? await memoryCall("getTool", { vault: "People", path: path6 }, memoryKey) : { ok: false, error: memoryKeyError || "memory_credential_unavailable" };
@@ -71323,9 +71731,9 @@ Submitted the published form.
71323
71731
  occurredAt: (/* @__PURE__ */ new Date()).toISOString()
71324
71732
  });
71325
71733
  const match = {
71326
- ...email ? { emailSha256: (0, import_node_crypto42.createHash)("sha256").update(email).digest("hex") } : {},
71734
+ ...email ? { emailSha256: (0, import_node_crypto43.createHash)("sha256").update(email).digest("hex") } : {},
71327
71735
  ...phone ? {
71328
- phoneSha256: (0, import_node_crypto42.createHash)("sha256").update(phone.replace(/\D/g, "")).digest("hex")
71736
+ phoneSha256: (0, import_node_crypto43.createHash)("sha256").update(phone.replace(/\D/g, "")).digest("hex")
71329
71737
  } : {}
71330
71738
  };
71331
71739
  const activationQueued = await queueAnalyticsActivation({
@@ -71753,7 +72161,7 @@ Submitted the published form.
71753
72161
  rejectedCount += 1;
71754
72162
  continue;
71755
72163
  }
71756
- const digest2 = (0, import_node_crypto42.createHash)("sha256").update(
72164
+ const digest2 = (0, import_node_crypto43.createHash)("sha256").update(
71757
72165
  `${parsed.data.sourceSystem}:${externalId || email || phone || `${fullName}:${index}`}`
71758
72166
  ).digest("hex").slice(0, 16);
71759
72167
  const path6 = `Leads/person-${digest2}`;
@@ -72211,13 +72619,13 @@ var init_analytics_delivery = __esm({
72211
72619
 
72212
72620
  // src/api/scheduled-artifact-owner.ts
72213
72621
  function scheduledArtifactOwnerIdForApiKey(apiKey) {
72214
- return (0, import_node_crypto43.createHash)("sha256").update(apiKey).digest("hex").slice(0, 24);
72622
+ return (0, import_node_crypto44.createHash)("sha256").update(apiKey).digest("hex").slice(0, 24);
72215
72623
  }
72216
- var import_node_crypto43;
72624
+ var import_node_crypto44;
72217
72625
  var init_scheduled_artifact_owner = __esm({
72218
72626
  "src/api/scheduled-artifact-owner.ts"() {
72219
72627
  "use strict";
72220
- import_node_crypto43 = require("crypto");
72628
+ import_node_crypto44 = require("crypto");
72221
72629
  }
72222
72630
  });
72223
72631
 
@@ -72274,7 +72682,7 @@ async function ensureScheduledRunViewLinksSchema() {
72274
72682
  schemaReady2 = true;
72275
72683
  }
72276
72684
  function tokenHash2(token6) {
72277
- return (0, import_node_crypto44.createHash)("sha256").update(token6).digest("hex");
72685
+ return (0, import_node_crypto45.createHash)("sha256").update(token6).digest("hex");
72278
72686
  }
72279
72687
  function mapRow(row) {
72280
72688
  return {
@@ -72294,9 +72702,9 @@ function mapRow(row) {
72294
72702
  async function createScheduledRunViewLink(input) {
72295
72703
  await ensureScheduledRunViewLinksSchema();
72296
72704
  const now = input.now ?? /* @__PURE__ */ new Date();
72297
- const token6 = (0, import_node_crypto44.randomBytes)(32).toString("base64url");
72705
+ const token6 = (0, import_node_crypto45.randomBytes)(32).toString("base64url");
72298
72706
  const record = {
72299
- shareId: (0, import_node_crypto44.randomUUID)(),
72707
+ shareId: (0, import_node_crypto45.randomUUID)(),
72300
72708
  ownerId: input.ownerId,
72301
72709
  runId: input.runId,
72302
72710
  artifactId: input.artifactId,
@@ -72359,11 +72767,11 @@ async function revokeScheduledRunViewLink(ownerId, runId, shareId, now = /* @__P
72359
72767
  });
72360
72768
  return result.rowsAffected > 0;
72361
72769
  }
72362
- var import_node_crypto44, schemaReady2;
72770
+ var import_node_crypto45, schemaReady2;
72363
72771
  var init_scheduled_run_view_links = __esm({
72364
72772
  "src/api/scheduled-run-view-links.ts"() {
72365
72773
  "use strict";
72366
- import_node_crypto44 = require("crypto");
72774
+ import_node_crypto45 = require("crypto");
72367
72775
  init_db();
72368
72776
  schemaReady2 = false;
72369
72777
  }
@@ -72429,15 +72837,15 @@ ${section(flags.finalCta && Boolean(finalCta), `<section class="section cta" id=
72429
72837
  html,
72430
72838
  filename: `${input.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "personal-authority"}.html`,
72431
72839
  bytes,
72432
- sha256: (0, import_node_crypto45.createHash)("sha256").update(html).digest("hex"),
72840
+ sha256: (0, import_node_crypto46.createHash)("sha256").update(html).digest("hex"),
72433
72841
  generatedAt: generatedAtIso
72434
72842
  };
72435
72843
  }
72436
- var import_node_crypto45;
72844
+ var import_node_crypto46;
72437
72845
  var init_render2 = __esm({
72438
72846
  "src/personal-authority/render.ts"() {
72439
72847
  "use strict";
72440
- import_node_crypto45 = require("crypto");
72848
+ import_node_crypto46 = require("crypto");
72441
72849
  }
72442
72850
  });
72443
72851
 
@@ -72540,15 +72948,15 @@ ${section2(flags.finalCta && Boolean(finalCta), `<section class="section cta" id
72540
72948
  html,
72541
72949
  filename: `${input.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "personal-authority"}.html`,
72542
72950
  bytes,
72543
- sha256: (0, import_node_crypto46.createHash)("sha256").update(html).digest("hex"),
72951
+ sha256: (0, import_node_crypto47.createHash)("sha256").update(html).digest("hex"),
72544
72952
  generatedAt: generatedAtIso
72545
72953
  };
72546
72954
  }
72547
- var import_node_crypto46, FONT_STACKS;
72955
+ var import_node_crypto47, FONT_STACKS;
72548
72956
  var init_render_v2 = __esm({
72549
72957
  "src/personal-authority/render-v2.ts"() {
72550
72958
  "use strict";
72551
- import_node_crypto46 = require("crypto");
72959
+ import_node_crypto47 = require("crypto");
72552
72960
  init_contracts();
72553
72961
  FONT_STACKS = Object.freeze({
72554
72962
  "editorial-serif": "Iowan Old Style, Baskerville, Times New Roman, serif",
@@ -72664,15 +73072,15 @@ body{overflow-x:hidden}.lead-story__body,.lead-support__item,.story-card__body{m
72664
73072
  @media(max-width:620px){.desk-section__grid--panorama,.desk-section__grid--mosaic,.desk-section__grid--tiles{grid-template-columns:1fr}.story-card--panorama{display:block}.story-card--panorama h3,.story-card--mosaic-lead h3{font-size:34px}.story-card--mosaic-lead{grid-column:auto;grid-row:auto}.desk-section__grid--tiles>.story-card:nth-child(3n+2){margin-top:0}}
72665
73073
  </style></head><body data-theme="${escapeHtml10(config.theme)}" id="top" data-scheduled-renderer="newsroom_publisher_v1">${ticker}${masthead}${navigation}<main>${leadGrid}${latest}${sections}${newsletter}${pressRoom}${trustCenter}</main>${trustFooter}</body></html>`;
72666
73074
  const bytes = Buffer.byteLength(html);
72667
- const sha2563 = (0, import_node_crypto47.createHash)("sha256").update(html).digest("hex");
73075
+ const sha2563 = (0, import_node_crypto48.createHash)("sha256").update(html).digest("hex");
72668
73076
  const filename = `${brand.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "newsroom"}-news-site.html`;
72669
73077
  return { html, filename, bytes, sha256: sha2563, generatedAt: generatedAtIso };
72670
73078
  }
72671
- var import_node_crypto47;
73079
+ var import_node_crypto48;
72672
73080
  var init_render3 = __esm({
72673
73081
  "src/newsroom-publisher/render.ts"() {
72674
73082
  "use strict";
72675
- import_node_crypto47 = require("crypto");
73083
+ import_node_crypto48 = require("crypto");
72676
73084
  }
72677
73085
  });
72678
73086
 
@@ -72764,7 +73172,7 @@ function renderBlogArticleV1(input, config, generatedAt) {
72764
73172
  :root{--ink:#10213d;--ink2:#1c355a;--blue:#2563eb;--blue2:#1749b6;--mint:#45d5aa;--paper:#f7f5f0;--line:#dce4ef;--card:#fff;--body:#475569;--serif:Iowan Old Style,Palatino Linotype,Palatino,Georgia,serif;--sans:Inter,Avenir Next,ui-sans-serif,system-ui,sans-serif}body[data-theme="slate"]{--ink:#1e293b;--ink2:#334155;--blue:#475569;--blue2:#1e293b;--mint:#94a3b8;--paper:#f8fafc}body[data-theme="forest"]{--ink:#12352e;--ink2:#23584c;--blue:#147d64;--blue2:#0c5b48;--mint:#79d8bd;--paper:#f4f8f3}*{box-sizing:border-box}html{scroll-behavior:smooth;background:var(--paper)}body{margin:0;color:#252a33;background:var(--paper);font-family:var(--sans);line-height:1.6}a{color:inherit}button{font:inherit}button:focus-visible,a:focus-visible{outline:3px solid var(--mint);outline-offset:3px}.progress{position:fixed;z-index:50;top:0;left:0;height:4px;width:0;background:var(--mint)}.hero{position:relative;overflow:hidden;color:#fff;background:var(--ink)}.hero:after{position:absolute;right:-110px;top:-150px;width:420px;height:420px;border:1px solid #ffffff1a;border-radius:50%;content:""}.hero-inner{position:relative;z-index:1;max-width:1240px;margin:auto;padding:58px 42px 126px}.breadcrumb{display:flex;gap:8px;margin-bottom:34px;color:#dbeafecc;font-size:14px}.eyebrow{display:block;color:var(--blue);font-size:12px;font-weight:800;letter-spacing:.16em;text-transform:uppercase}.hero .eyebrow{color:var(--mint)}h1,h2,h3,p{margin-top:0}.hero h1{max-width:1000px;margin:14px 0 24px;font:700 clamp(44px,6vw,78px)/.98 var(--serif);letter-spacing:-.035em}.hero .dek{max-width:780px;color:#dbeafe;font-size:20px}.contributors{margin-top:30px;color:#eff6ff;font-size:14px}.contributors a{font-weight:800}.meta{display:flex;flex-wrap:wrap;gap:12px;margin-top:6px;color:#dbeafecc}.layout{position:relative;z-index:2;display:grid;grid-template-columns:minmax(0,760px) 290px;gap:64px;max-width:1240px;margin:-78px auto 0;padding:0 42px 80px}.main{min-width:0}.takeaways{padding:38px 44px;border-radius:24px;background:#fff;box-shadow:0 24px 70px #10213d24}.takeaways h2{margin:5px 0 20px;font:700 32px/1.1 var(--serif);color:var(--ink)}.takeaways ul{display:grid;gap:16px;margin:0;padding:0;list-style:none}.takeaways li{position:relative;padding-left:26px;color:var(--body);font-size:17px}.takeaways li:before{position:absolute;left:2px;top:.7em;width:8px;height:8px;border-radius:50%;background:var(--blue);content:""}.intro{padding:40px 0 4px}.intro p,.article-section>p{margin-bottom:22px;color:var(--body);font:400 18px/1.75 var(--sans)}.article-section{scroll-margin-top:28px;padding-top:45px}.article-section h2,.faq h2{margin:6px 0 20px;color:var(--ink);font:700 38px/1.08 var(--serif);letter-spacing:-.02em}.article-section ul{display:grid;gap:10px;margin:22px 0;padding-left:24px;color:var(--body);font-size:17px}.article-section li::marker{color:var(--blue)}.callout{margin:34px 0;padding:24px 28px;border-left:4px solid var(--blue);border-radius:0 16px 16px 0;background:#fff}.callout span{color:var(--blue);font-size:11px;font-weight:800;letter-spacing:.13em;text-transform:uppercase}.callout h3{margin:5px 0 8px;color:var(--ink);font:700 24px/1.15 var(--serif)}.callout p{margin:0;color:var(--body)}.rail{padding-top:0}.sidebar-media{overflow:hidden;margin:0 0 18px;border:1px solid #dbeafe;border-radius:16px;background:#fff}.sidebar-media img,.sidebar-media svg{display:block;width:100%;aspect-ratio:16/9;object-fit:cover}.sidebar-media figcaption{padding:10px 13px;color:#64748b;font-size:11px}.disclosure{margin-bottom:28px;border-top:1px solid #dbeafe;border-bottom:1px solid #dbeafe}.disclosure button{display:flex;width:100%;align-items:center;justify-content:space-between;gap:14px;padding:15px 0;border:0;color:#64748b;background:transparent;text-align:left;font-size:12px;cursor:pointer}.disclosure button b{color:var(--blue);font-size:18px;transition:transform .2s}.disclosure button[aria-expanded="true"] b{transform:rotate(180deg)}.disclosure p{padding:0 0 16px;margin:0;color:#64748b;font-size:12px}.toc{position:sticky;top:30px;padding:22px;border:1px solid var(--line);border-radius:16px;background:#fff;box-shadow:0 2px 5px #10213d12}.toc header{display:flex;align-items:center;justify-content:space-between}.toc header strong{color:var(--ink);font:700 21px/1 var(--serif)}.toc header button{width:34px;height:34px;border:0;border-radius:50%;color:var(--blue);background:transparent;cursor:pointer}.toc nav{display:grid;margin-top:14px}.toc nav a{display:flex;align-items:flex-start;gap:11px;padding:8px 0;color:#64748b;text-decoration:none;font-size:13px;line-height:1.35}.toc nav i{width:8px;height:8px;margin-top:5px;border:2px solid #bfdbfe;border-radius:50%}.toc nav a[aria-current="location"]{color:var(--ink);font-weight:800}.toc nav a[aria-current="location"] i{border-color:var(--blue);background:var(--blue)}.back-top{width:100%;margin-top:18px;padding:16px 0 0;border:0;border-top:1px solid var(--line);color:var(--blue);background:transparent;text-align:right;font-size:13px;font-weight:800;cursor:pointer}.faq{scroll-margin-top:28px;padding-top:64px}.faq>div{border-top:1px solid var(--line);border-bottom:1px solid var(--line)}.faq article+article{border-top:1px solid var(--line)}.faq h3{margin:0}.faq h3 button{display:flex;width:100%;align-items:center;justify-content:space-between;gap:18px;padding:19px 0;border:0;color:var(--ink);background:transparent;text-align:left;font-weight:800;cursor:pointer}.faq h3 b{color:var(--blue);font-size:22px}.faq article>p{padding:0 0 20px;margin:0;color:var(--body);font-size:16px}.author-card{margin-top:64px;padding:32px 36px;border-radius:18px;background:#fff}.article-actions{display:flex;flex-wrap:wrap;justify-content:space-between;gap:14px}.article-actions button{display:inline-flex;min-height:46px;align-items:center;justify-content:center;gap:8px;padding:8px 18px;border:2px solid var(--blue);border-radius:999px;color:var(--ink);background:#fff;font-weight:800;cursor:pointer}.article-actions button:hover{color:#fff;background:var(--blue)}.action-status{min-height:20px;margin:8px 0 0;color:var(--blue);text-align:right;font-size:13px;font-weight:700}.author-grid{display:grid;grid-template-columns:auto 1fr auto;gap:22px;align-items:center;margin-top:24px}.author-portrait{width:88px;height:88px;border-radius:50%;object-fit:cover}.author-initials{display:grid;place-items:center;color:var(--ink);background:#fecdd3;font-size:22px;font-weight:900}.author-grid span{color:#64748b;font-size:13px}.author-grid h2{margin:2px 0;color:var(--ink);font:700 27px/1 var(--serif)}.author-grid h2 a{text-decoration:none}.author-grid p{margin:8px 0;color:#64748b;font-size:14px}.author-grid nav{display:flex;flex-wrap:wrap;gap:14px}.author-grid nav a{color:#64748b;font-size:12px}.author-more{display:inline-flex;min-height:48px;align-items:center;justify-content:center;padding:10px 24px;border:2px solid var(--blue);color:var(--blue);text-align:center;text-decoration:none;font-size:13px;font-weight:800}.author-bio{margin:24px 0 0!important;padding-top:20px;border-top:1px solid var(--line);color:var(--body);font-size:16px}dialog{width:min(720px,calc(100% - 30px));max-height:calc(100dvh - 30px);padding:0;border:0;border-radius:18px;color:var(--ink);background:#fff;box-shadow:0 24px 70px #10213d38}dialog::backdrop{background:#0f172a7a;backdrop-filter:blur(2px)}dialog>div{position:relative;padding:30px}dialog h2{margin:0 50px 2px 0;font:700 30px/1.1 var(--serif)}dialog>div>p{color:#64748b}.dialog-close{position:absolute;top:13px;right:13px;width:44px;height:44px;border:0;border-radius:50%;color:var(--blue);background:transparent;font-size:29px;cursor:pointer}dialog section{border-top:1px solid var(--line)}dialog section button{display:block;width:100%;padding:17px 5px;border:0;border-bottom:1px solid var(--line);color:var(--body);background:#fff;text-align:left;line-height:1.65;cursor:pointer}.citation-status{min-height:22px;margin:14px 0 0!important;color:var(--blue)!important;font-size:13px;font-weight:800}.generated{max-width:1156px;margin:-52px auto 50px;padding:0 42px;color:#94a3b8;font-size:11px}@media(max-width:900px){.layout{display:block}.rail{display:none}.hero-inner{padding-bottom:116px}}@media(max-width:620px){.hero-inner{padding:36px 20px 104px}.hero h1{font-size:46px}.hero .dek{font-size:17px}.layout{margin-top:-70px;padding:0 16px 54px}.takeaways{padding:28px 24px}.article-section h2,.faq h2{font-size:32px}.author-card{padding:24px}.article-actions{display:grid;justify-content:start}.author-grid{grid-template-columns:1fr;gap:12px}.author-more{width:100%}dialog>div{padding:24px}.generated{padding:0 20px}}
72765
73173
  </style></head><body data-theme="${escapeHtml11(config.theme)}" data-scheduled-renderer="blog_article_v1"><div class="progress" aria-hidden="true"></div><header class="hero"><div class="hero-inner"><nav class="breadcrumb" aria-label="Breadcrumb"><a href="${safeHref4(input.canonicalUrl)}">${escapeHtml11(input.category)}</a><span>/</span><span>Article</span></nav><span class="eyebrow">${escapeHtml11(input.eyebrow)}</span><h1>${escapeHtml11(input.title)}</h1>${input.dek ? `<p class="dek">${escapeHtml11(input.dek)}</p>` : ""}<div class="contributors">${contributorText}<div class="meta"><time datetime="${escapeHtml11(articleDate)}">Updated ${formatDate2(articleDate)}</time><span>\u2022</span><span>${input.readTimeMinutes} min read</span>${input.reviewer ? "<span>\u2022</span><strong>Expert reviewed</strong>" : ""}</div></div></div></header><main class="layout"><article class="main"><section class="takeaways"><span class="eyebrow">The short answer</span><h2>Key takeaways</h2><ul>${input.keyTakeaways.map((item) => `<li>${escapeHtml11(item)}</li>`).join("")}</ul></section><section class="intro">${input.introduction.map((paragraph) => `<p>${escapeHtml11(paragraph)}</p>`).join("")}</section>${sections}${faq}${author}</article><div class="rail">${media}${disclosure}${toc}</div></main>${config.showGeneratedAt ? `<p class="generated">Rendered ${formatDate2(generatedAtIso)} by MCP Scraper.</p>` : ""}${citationDialog}<script>(()=>{const citations=${jsonForScript(citationValues)};const canonical=${jsonForScript(input.canonicalUrl)};const title=${jsonForScript(input.title)};const progress=document.querySelector('.progress');const tocLinks=[...document.querySelectorAll('.toc nav a')];const targets=tocLinks.map(link=>document.querySelector(link.hash)).filter(Boolean);const update=()=>{const max=document.documentElement.scrollHeight-innerHeight;if(progress)progress.style.width=(max>0?scrollY/max*100:0)+'%';let active=targets[0]?.id;for(const target of targets){if(target.getBoundingClientRect().top<=innerHeight*.55)active=target.id}tocLinks.forEach(link=>link.toggleAttribute('aria-current',link.hash==='#'+active));tocLinks.forEach(link=>{if(link.hash==='#'+active)link.setAttribute('aria-current','location');else link.removeAttribute('aria-current')})};addEventListener('scroll',update,{passive:true});addEventListener('resize',update);update();document.querySelectorAll('.faq h3 button').forEach(button=>button.addEventListener('click',()=>{const panel=document.getElementById(button.getAttribute('aria-controls'));const open=button.getAttribute('aria-expanded')==='true';button.setAttribute('aria-expanded',String(!open));panel.hidden=open;button.querySelector('b').textContent=open?'+':'\u2212'}));const disclosure=document.querySelector('.disclosure button');disclosure?.addEventListener('click',()=>{const panel=document.getElementById(disclosure.getAttribute('aria-controls'));const open=disclosure.getAttribute('aria-expanded')==='true';disclosure.setAttribute('aria-expanded',String(!open));panel.hidden=open});const tocToggle=document.querySelector('.toc header button');tocToggle?.addEventListener('click',()=>{const nav=document.getElementById('toc-links');const open=tocToggle.getAttribute('aria-expanded')==='true';tocToggle.setAttribute('aria-expanded',String(!open));nav.hidden=open;tocToggle.textContent=open?'\u2304':'\u2303'});document.querySelector('.back-top')?.addEventListener('click',()=>scrollTo({top:0,behavior:'smooth'}));const copy=async text=>{try{await navigator.clipboard.writeText(text)}catch{const area=document.createElement('textarea');area.value=text;area.style.position='fixed';area.style.opacity='0';document.body.append(area);area.select();document.execCommand('copy');area.remove()}};const dialog=document.getElementById('citation-dialog');document.querySelector('[data-cite]')?.addEventListener('click',()=>dialog.showModal());document.querySelector('.dialog-close')?.addEventListener('click',()=>dialog.close());dialog?.addEventListener('click',event=>{if(event.target===dialog)dialog.close()});document.querySelectorAll('[data-citation]').forEach(button=>button.addEventListener('click',async()=>{const style=button.dataset.citation;await copy(citations[style]);document.querySelector('.citation-status').textContent=style+' citation copied to clipboard.'}));document.querySelector('[data-share]')?.addEventListener('click',async()=>{const status=document.querySelector('.action-status');if(navigator.share){try{await navigator.share({title,url:canonical});status.textContent='Sharing options opened.';return}catch(error){if(error.name==='AbortError')return}}await copy(canonical);status.textContent='Article link copied to clipboard.'})})()</script></body></html>`;
72766
73174
  const bytes = Buffer.byteLength(html);
72767
- const sha2563 = (0, import_node_crypto48.createHash)("sha256").update(html).digest("hex");
73175
+ const sha2563 = (0, import_node_crypto49.createHash)("sha256").update(html).digest("hex");
72768
73176
  return {
72769
73177
  html,
72770
73178
  filename: "blog-article.html",
@@ -72774,11 +73182,11 @@ function renderBlogArticleV1(input, config, generatedAt) {
72774
73182
  generatedAt: generatedAtIso
72775
73183
  };
72776
73184
  }
72777
- var import_node_crypto48;
73185
+ var import_node_crypto49;
72778
73186
  var init_render4 = __esm({
72779
73187
  "src/blog-article/render.ts"() {
72780
73188
  "use strict";
72781
- import_node_crypto48 = require("crypto");
73189
+ import_node_crypto49 = require("crypto");
72782
73190
  }
72783
73191
  });
72784
73192
 
@@ -73109,7 +73517,7 @@ function policy5() {
73109
73517
  };
73110
73518
  }
73111
73519
  function runStorageSegment(runId) {
73112
- return /^[a-zA-Z0-9_-]{1,160}$/.test(runId) ? runId : `run-${(0, import_node_crypto49.createHash)("sha256").update(runId).digest("hex").slice(0, 32)}`;
73520
+ return /^[a-zA-Z0-9_-]{1,160}$/.test(runId) ? runId : `run-${(0, import_node_crypto50.createHash)("sha256").update(runId).digest("hex").slice(0, 32)}`;
73113
73521
  }
73114
73522
  async function createScheduledRunArtifact(args) {
73115
73523
  if (args.rendered.bytes > SCHEDULED_RUN_ARTIFACT_MAX_BYTES) {
@@ -73150,12 +73558,12 @@ async function readScheduledRunArtifact(args) {
73150
73558
  if (!window2 || window2.nextOffset !== null || window2.totalBytes > SCHEDULED_RUN_ARTIFACT_MAX_BYTES) return null;
73151
73559
  return window2.text;
73152
73560
  }
73153
- var import_node_crypto49, SCHEDULED_RUN_ARTIFACT_PREFIX, SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS, SCHEDULED_RUN_ARTIFACT_MAX_BYTES;
73561
+ var import_node_crypto50, SCHEDULED_RUN_ARTIFACT_PREFIX, SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS, SCHEDULED_RUN_ARTIFACT_MAX_BYTES;
73154
73562
  var init_scheduled_run_artifact_store = __esm({
73155
73563
  "src/scheduled-artifacts/scheduled-run-artifact-store.ts"() {
73156
73564
  "use strict";
73157
73565
  init_private_artifacts();
73158
- import_node_crypto49 = require("crypto");
73566
+ import_node_crypto50 = require("crypto");
73159
73567
  SCHEDULED_RUN_ARTIFACT_PREFIX = "scheduled-run-artifacts/";
73160
73568
  SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS = 15 * 60 * 1e3;
73161
73569
  SCHEDULED_RUN_ARTIFACT_MAX_BYTES = 2e6;
@@ -73373,7 +73781,7 @@ async function reconcileDiscoveredNangoConnections(identity, discovered) {
73373
73781
  updated_at = excluded.updated_at
73374
73782
  `,
73375
73783
  args: [
73376
- (0, import_node_crypto50.randomUUID)(),
73784
+ (0, import_node_crypto51.randomUUID)(),
73377
73785
  userId,
73378
73786
  connection.providerConfigKey,
73379
73787
  connection.provider,
@@ -73444,7 +73852,7 @@ async function recordServiceConnectionHealth(args) {
73444
73852
  });
73445
73853
  await getDb().execute({
73446
73854
  sql: `INSERT INTO service_connection_health_events (id, connection_id, operational_status, failure_code, retryable, evidence_source) VALUES (?, ?, ?, ?, ?, ?)`,
73447
- args: [(0, import_node_crypto50.randomUUID)(), args.connectionId, args.operationalStatus, args.failureCode ?? null, args.retryable == null ? null : args.retryable ? 1 : 0, args.evidenceSource]
73855
+ args: [(0, import_node_crypto51.randomUUID)(), args.connectionId, args.operationalStatus, args.failureCode ?? null, args.retryable == null ? null : args.retryable ? 1 : 0, args.evidenceSource]
73448
73856
  });
73449
73857
  }
73450
73858
  async function setServiceConnectionActions(identity, connectionId, enabled) {
@@ -73484,7 +73892,7 @@ async function claimServiceConnectionAction(args) {
73484
73892
  if (!connection) throw new Error("service_connection_not_found");
73485
73893
  const inserted = await getDb().execute({
73486
73894
  sql: `INSERT OR IGNORE INTO service_connection_action_audit (id, connection_id, user_id, tool, request_id, status, request_digest) VALUES (?, ?, ?, ?, ?, 'started', ?)`,
73487
- args: [(0, import_node_crypto50.randomUUID)(), connection.id, connection.userId, args.tool, args.requestId, args.requestDigest]
73895
+ args: [(0, import_node_crypto51.randomUUID)(), connection.id, connection.userId, args.tool, args.requestId, args.requestDigest]
73488
73896
  });
73489
73897
  if (Number(inserted.rowsAffected ?? 0) === 1) return { claimed: true };
73490
73898
  const existing = await getDb().execute({
@@ -73509,11 +73917,11 @@ async function claimServiceConnectionAction(args) {
73509
73917
  ...result !== void 0 ? { result } : {}
73510
73918
  };
73511
73919
  }
73512
- var import_node_crypto50, schemaReady3, schemaDb4;
73920
+ var import_node_crypto51, schemaReady3, schemaDb4;
73513
73921
  var init_service_connections = __esm({
73514
73922
  "src/api/service-connections.ts"() {
73515
73923
  "use strict";
73516
- import_node_crypto50 = require("crypto");
73924
+ import_node_crypto51 = require("crypto");
73517
73925
  init_db();
73518
73926
  schemaReady3 = null;
73519
73927
  schemaDb4 = null;
@@ -73527,8 +73935,8 @@ function signingSecret() {
73527
73935
  return secret2;
73528
73936
  }
73529
73937
  function schedulerIntegrationSignature(args) {
73530
- const bodyHash = (0, import_node_crypto51.createHash)("sha256").update(args.body).digest("hex");
73531
- return (0, import_node_crypto51.createHmac)("sha256", args.secret).update(`${args.method.toUpperCase()}
73938
+ const bodyHash = (0, import_node_crypto52.createHash)("sha256").update(args.body).digest("hex");
73939
+ return (0, import_node_crypto52.createHmac)("sha256", args.secret).update(`${args.method.toUpperCase()}
73532
73940
  ${args.path}
73533
73941
  ${args.timestamp}
73534
73942
  ${args.nonce}
@@ -73575,17 +73983,17 @@ async function verifySchedulerIntegrationRequest(request, rawBody) {
73575
73983
  });
73576
73984
  const suppliedBytes = Buffer.from(signature, "hex");
73577
73985
  const expectedBytes = Buffer.from(expected, "hex");
73578
- if (suppliedBytes.length !== expectedBytes.length || !(0, import_node_crypto51.timingSafeEqual)(suppliedBytes, expectedBytes)) {
73986
+ if (suppliedBytes.length !== expectedBytes.length || !(0, import_node_crypto52.timingSafeEqual)(suppliedBytes, expectedBytes)) {
73579
73987
  throw new SchedulerIntegrationAuthError("invalid_signature");
73580
73988
  }
73581
73989
  await claimNonce(nonce, timestampMs);
73582
73990
  return { requestId };
73583
73991
  }
73584
- var import_node_crypto51, MAX_CLOCK_SKEW_MS, SchedulerIntegrationAuthError;
73992
+ var import_node_crypto52, MAX_CLOCK_SKEW_MS, SchedulerIntegrationAuthError;
73585
73993
  var init_scheduler_integration_auth = __esm({
73586
73994
  "src/api/scheduler-integration-auth.ts"() {
73587
73995
  "use strict";
73588
- import_node_crypto51 = require("crypto");
73996
+ import_node_crypto52 = require("crypto");
73589
73997
  init_db();
73590
73998
  init_service_connections();
73591
73999
  MAX_CLOCK_SKEW_MS = 5 * 60 * 1e3;
@@ -73893,7 +74301,7 @@ async function fetchMedia(rawUrl, expectedType) {
73893
74301
  finalUrl: checked.parsed.href,
73894
74302
  width: dimensions?.width ?? null,
73895
74303
  height: dimensions?.height ?? null,
73896
- sha256: (0, import_node_crypto52.createHash)("sha256").update(bytes).digest("hex")
74304
+ sha256: (0, import_node_crypto53.createHash)("sha256").update(bytes).digest("hex")
73897
74305
  };
73898
74306
  }
73899
74307
  throw new Error("media_redirect_rejected");
@@ -74007,7 +74415,7 @@ async function packagePageMedia(args) {
74007
74415
  files.push({ path: "summary.json", content: Buffer.from(JSON.stringify(summary, null, 2)) });
74008
74416
  files.push({ path: "media.jsonl", content: Buffer.from(cleanAssets.map((asset) => JSON.stringify(asset)).join("\n") + "\n") });
74009
74417
  const archive = await zipBuffer2(files);
74010
- const id = (0, import_node_crypto52.randomBytes)(6).toString("hex");
74418
+ const id = (0, import_node_crypto53.randomBytes)(6).toString("hex");
74011
74419
  const pointer = await createPrivateArtifact({
74012
74420
  policy: policy6(),
74013
74421
  ownerId: args.ownerId,
@@ -74020,11 +74428,11 @@ async function packagePageMedia(args) {
74020
74428
  const localPath = token5() ? null : (0, import_node_path21.join)(process.env.MCP_SCRAPER_OUTPUT_DIR?.trim() || (0, import_node_path21.join)((0, import_node_os14.homedir)(), "Downloads", "mcp-scraper"), "blobs", pointer.artifactId);
74021
74429
  return { media: args.media, artifact: { ...pointer, localPath } };
74022
74430
  }
74023
- var import_node_crypto52, import_node_os14, import_node_path21, import_p_limit7, import_yazl3, PAGE_MEDIA_ARTIFACT_PREFIX, PAGE_MEDIA_ARTIFACT_TTL_MS, PAGE_MEDIA_DOWNLOAD_TTL_MS, MAX_FILE_BYTES, MAX_ARCHIVE_MEDIA_BYTES, MAX_INLINE_IMAGE_BYTES2, MAX_INLINE_TOTAL_BYTES2, DOWNLOAD_CONCURRENCY2, MAX_REDIRECTS3;
74431
+ var import_node_crypto53, import_node_os14, import_node_path21, import_p_limit7, import_yazl3, PAGE_MEDIA_ARTIFACT_PREFIX, PAGE_MEDIA_ARTIFACT_TTL_MS, PAGE_MEDIA_DOWNLOAD_TTL_MS, MAX_FILE_BYTES, MAX_ARCHIVE_MEDIA_BYTES, MAX_INLINE_IMAGE_BYTES2, MAX_INLINE_TOTAL_BYTES2, DOWNLOAD_CONCURRENCY2, MAX_REDIRECTS3;
74024
74432
  var init_page_media_artifacts = __esm({
74025
74433
  "src/api/page-media-artifacts.ts"() {
74026
74434
  "use strict";
74027
- import_node_crypto52 = require("crypto");
74435
+ import_node_crypto53 = require("crypto");
74028
74436
  import_node_os14 = require("os");
74029
74437
  import_node_path21 = require("path");
74030
74438
  import_p_limit7 = __toESM(require("p-limit"), 1);
@@ -74118,7 +74526,7 @@ var init_site_extract_reconciliation = __esm({
74118
74526
 
74119
74527
  // src/api/page-diff.ts
74120
74528
  function sha256Hex(value) {
74121
- return (0, import_node_crypto53.createHash)("sha256").update(value).digest("hex");
74529
+ return (0, import_node_crypto54.createHash)("sha256").update(value).digest("hex");
74122
74530
  }
74123
74531
  function truncateForStorage(value, maxChars = MAX_SNAPSHOT_CONTENT_CHARS) {
74124
74532
  if (value.length <= maxChars) return { value, truncated: false };
@@ -74175,11 +74583,11 @@ function diffPageContent(oldContent, newContent) {
74175
74583
  totalChangedLineCount
74176
74584
  };
74177
74585
  }
74178
- var import_node_crypto53, import_diff, MAX_SNAPSHOT_CONTENT_CHARS, MAX_DIFF_HUNKS, MAX_DIFF_LINES_PER_RESPONSE;
74586
+ var import_node_crypto54, import_diff, MAX_SNAPSHOT_CONTENT_CHARS, MAX_DIFF_HUNKS, MAX_DIFF_LINES_PER_RESPONSE;
74179
74587
  var init_page_diff = __esm({
74180
74588
  "src/api/page-diff.ts"() {
74181
74589
  "use strict";
74182
- import_node_crypto53 = require("crypto");
74590
+ import_node_crypto54 = require("crypto");
74183
74591
  import_diff = require("diff");
74184
74592
  MAX_SNAPSHOT_CONTENT_CHARS = 25e4;
74185
74593
  MAX_DIFF_HUNKS = 200;
@@ -74253,7 +74661,7 @@ var init_scrape_vault_sink = __esm({
74253
74661
 
74254
74662
  // src/api/scrape-image-sink.ts
74255
74663
  function idempotencyKey2(userId, vault, input) {
74256
- return `scrape-image-${(0, import_node_crypto54.createHash)("sha256").update(`${userId}\0${vault}\0${input.sourceKind}\0${input.sourceUrl}\0${input.imageUrl ?? ""}\0${input.imageBase64 ?? ""}`).digest("hex")}`;
74664
+ return `scrape-image-${(0, import_node_crypto55.createHash)("sha256").update(`${userId}\0${vault}\0${input.sourceKind}\0${input.sourceUrl}\0${input.imageUrl ?? ""}\0${input.imageBase64 ?? ""}`).digest("hex")}`;
74257
74665
  }
74258
74666
  async function persistScrapeImagesToMemory(user, inputs, vault) {
74259
74667
  const selected = inputs.slice(0, MAX_IMAGES_PER_SCRAPE);
@@ -74295,11 +74703,11 @@ async function persistScrapeImagesToMemory(user, inputs, vault) {
74295
74703
  assets
74296
74704
  };
74297
74705
  }
74298
- var import_node_crypto54, MAX_IMAGES_PER_SCRAPE;
74706
+ var import_node_crypto55, MAX_IMAGES_PER_SCRAPE;
74299
74707
  var init_scrape_image_sink = __esm({
74300
74708
  "src/api/scrape-image-sink.ts"() {
74301
74709
  "use strict";
74302
- import_node_crypto54 = require("crypto");
74710
+ import_node_crypto55 = require("crypto");
74303
74711
  init_memory();
74304
74712
  MAX_IMAGES_PER_SCRAPE = 25;
74305
74713
  }
@@ -74752,7 +75160,7 @@ function canonicalJson(value) {
74752
75160
  return JSON.stringify(value);
74753
75161
  }
74754
75162
  function sha2562(value) {
74755
- return (0, import_node_crypto55.createHash)("sha256").update(value).digest("hex");
75163
+ return (0, import_node_crypto56.createHash)("sha256").update(value).digest("hex");
74756
75164
  }
74757
75165
  function sensitiveKey(key) {
74758
75166
  const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
@@ -74967,11 +75375,11 @@ async function importServiceConnectionToMemory(identity, input, dependencies) {
74967
75375
  ...!searchReady ? { warning: "The snapshot was stored, but no search chunks were indexed yet." } : {}
74968
75376
  };
74969
75377
  }
74970
- var import_node_crypto55, CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES, CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES, CONNECTION_MEMORY_IMPORT_MAX_STRING_CHARS, CONNECTION_MEMORY_IMPORT_MAX_DEPTH, ConnectionMemoryImportError;
75378
+ var import_node_crypto56, CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES, CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES, CONNECTION_MEMORY_IMPORT_MAX_STRING_CHARS, CONNECTION_MEMORY_IMPORT_MAX_DEPTH, ConnectionMemoryImportError;
74971
75379
  var init_connection_memory_import = __esm({
74972
75380
  "src/api/connection-memory-import.ts"() {
74973
75381
  "use strict";
74974
- import_node_crypto55 = require("crypto");
75382
+ import_node_crypto56 = require("crypto");
74975
75383
  init_slugify();
74976
75384
  CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES = 64 * 1024;
74977
75385
  CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES = 1e6;
@@ -75016,16 +75424,16 @@ async function cleanupLocal(cutoff) {
75016
75424
  let deleted = 0;
75017
75425
  let entries;
75018
75426
  try {
75019
- entries = await (0, import_promises15.readdir)(dir);
75427
+ entries = await (0, import_promises16.readdir)(dir);
75020
75428
  } catch {
75021
75429
  return { deleted: 0, store: "local" };
75022
75430
  }
75023
75431
  for (const name of entries) {
75024
75432
  const path6 = (0, import_node_path23.join)(dir, name);
75025
75433
  try {
75026
- const s = await (0, import_promises15.stat)(path6);
75434
+ const s = await (0, import_promises16.stat)(path6);
75027
75435
  if (s.isFile() && s.mtimeMs < cutoff) {
75028
- await (0, import_promises15.unlink)(path6);
75436
+ await (0, import_promises16.unlink)(path6);
75029
75437
  deleted++;
75030
75438
  }
75031
75439
  } catch {
@@ -75042,11 +75450,11 @@ async function cleanupExpiredScrapeBlobs(maxAgeMs = SCRAPE_BLOB_TTL_MS) {
75042
75450
  return { deleted: 0, store: "none" };
75043
75451
  }
75044
75452
  }
75045
- var import_promises15, import_node_os15, import_node_path23;
75453
+ var import_promises16, import_node_os15, import_node_path23;
75046
75454
  var init_scrape_blob_cleanup = __esm({
75047
75455
  "src/api/scrape-blob-cleanup.ts"() {
75048
75456
  "use strict";
75049
- import_promises15 = require("fs/promises");
75457
+ import_promises16 = require("fs/promises");
75050
75458
  import_node_os15 = require("os");
75051
75459
  import_node_path23 = require("path");
75052
75460
  init_scrape_vault_sink();
@@ -75189,7 +75597,7 @@ function finitePositive(value, fallback) {
75189
75597
  return Number.isFinite(value) && value > 0 ? value : fallback;
75190
75598
  }
75191
75599
  async function collectConnectedDataExport(args) {
75192
- const exportId = (0, import_node_crypto56.randomUUID)();
75600
+ const exportId = (0, import_node_crypto57.randomUUID)();
75193
75601
  const now = args.now ?? Date.now;
75194
75602
  const startedAt = now();
75195
75603
  const budgetMs = finitePositive(CONNECTED_DATA_EXPORT_BUDGET_MS, 24e4);
@@ -75303,11 +75711,11 @@ ${lines.length ? `${lines.join("\n")}
75303
75711
  untrustedContent: true
75304
75712
  };
75305
75713
  }
75306
- var import_node_crypto56, CONNECTED_DATA_INLINE_BUDGET_BYTES, CONNECTED_DATA_MAX_EXPORT_BYTES, CONNECTED_DATA_EXPORT_BUDGET_MS, CONNECTED_DATA_PAGE_START_HEADROOM_MS, CONNECTED_DATA_DATASETS, ConnectedDataExportValidationError;
75714
+ var import_node_crypto57, CONNECTED_DATA_INLINE_BUDGET_BYTES, CONNECTED_DATA_MAX_EXPORT_BYTES, CONNECTED_DATA_EXPORT_BUDGET_MS, CONNECTED_DATA_PAGE_START_HEADROOM_MS, CONNECTED_DATA_DATASETS, ConnectedDataExportValidationError;
75307
75715
  var init_connected_data_export = __esm({
75308
75716
  "src/api/connected-data-export.ts"() {
75309
75717
  "use strict";
75310
- import_node_crypto56 = require("crypto");
75718
+ import_node_crypto57 = require("crypto");
75311
75719
  CONNECTED_DATA_INLINE_BUDGET_BYTES = Number(
75312
75720
  process.env.MCP_SCRAPER_CONNECTED_DATA_INLINE_BUDGET_BYTES ?? 5e4
75313
75721
  );
@@ -75421,7 +75829,7 @@ async function exportSearchConsoleTableData(args) {
75421
75829
  offset += rows.length;
75422
75830
  if (stoppedForBytes || rows.length < limit || offset >= matchedRows) break;
75423
75831
  }
75424
- const exportId = (0, import_node_crypto57.randomUUID)();
75832
+ const exportId = (0, import_node_crypto58.randomUUID)();
75425
75833
  const artifact = await args.writeArtifact({
75426
75834
  ownerId: args.ownerId,
75427
75835
  exportId,
@@ -75443,11 +75851,11 @@ async function exportSearchConsoleTableData(args) {
75443
75851
  warnings
75444
75852
  };
75445
75853
  }
75446
- var import_node_crypto57, SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS, SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE, SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES, SEARCH_CONSOLE_TABLE_COLUMNS, SearchConsoleTableExportValidationError;
75854
+ var import_node_crypto58, SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS, SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE, SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES, SEARCH_CONSOLE_TABLE_COLUMNS, SearchConsoleTableExportValidationError;
75447
75855
  var init_search_console_table_export = __esm({
75448
75856
  "src/api/search-console-table-export.ts"() {
75449
75857
  "use strict";
75450
- import_node_crypto57 = require("crypto");
75858
+ import_node_crypto58 = require("crypto");
75451
75859
  SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS = 5e4;
75452
75860
  SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE = 2e3;
75453
75861
  SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES = 50 * 1024 * 1024;
@@ -76626,7 +77034,7 @@ async function listNangoToolsDirect(identity, connectionId) {
76626
77034
  });
76627
77035
  const readTools = [...policies.values()].filter((policy7) => policy7.classification === "read").map((policy7) => policy7.name);
76628
77036
  const actionTools = [...policies.values()].filter((policy7) => policy7.classification === "action").map((policy7) => policy7.name);
76629
- const revision = (0, import_node_crypto58.createHash)("sha256").update(JSON.stringify(tools.map((tool) => ({ name: tool.name, inputSchema: tool.inputSchema })))).digest("hex");
77037
+ const revision = (0, import_node_crypto59.createHash)("sha256").update(JSON.stringify(tools.map((tool) => ({ name: tool.name, inputSchema: tool.inputSchema })))).digest("hex");
76630
77038
  await updateServiceConnectionTools(connection.id, readTools, actionTools, revision);
76631
77039
  const refreshed = await getOwnedServiceConnection(identity, connection.id);
76632
77040
  return { connection: refreshed ?? { ...connection, readTools, actionTools, toolRevision: revision }, tools };
@@ -76653,8 +77061,8 @@ async function callNangoToolDirect(args) {
76653
77061
  identity: args.identity,
76654
77062
  ratePolicyVersion: CONNECTED_USAGE_RATE_POLICY_VERSION
76655
77063
  });
76656
- const requestId = args.requestId?.trim() || (0, import_node_crypto58.randomUUID)();
76657
- const idempotencyKey3 = `main-nango:${(0, import_node_crypto58.createHash)("sha256").update(args.identity.toLowerCase()).update("\0").update(connection.id).update("\0").update(args.tool).update("\0").update(requestId).digest("hex")}`;
77064
+ const requestId = args.requestId?.trim() || (0, import_node_crypto59.randomUUID)();
77065
+ const idempotencyKey3 = `main-nango:${(0, import_node_crypto59.createHash)("sha256").update(args.identity.toLowerCase()).update("\0").update(connection.id).update("\0").update(args.tool).update("\0").update(requestId).digest("hex")}`;
76658
77066
  const startedAt = /* @__PURE__ */ new Date();
76659
77067
  const started = performance.now();
76660
77068
  let result;
@@ -76679,7 +77087,7 @@ async function callNangoToolDirect(args) {
76679
77087
  toolName: args.tool,
76680
77088
  operationKind: args.operationKind ?? args.classification,
76681
77089
  outcome: providerError ? "error" : "partial",
76682
- requestId: requestId.length <= 200 ? requestId : (0, import_node_crypto58.createHash)("sha256").update(requestId).digest("hex"),
77090
+ requestId: requestId.length <= 200 ? requestId : (0, import_node_crypto59.createHash)("sha256").update(requestId).digest("hex"),
76683
77091
  startedAt: startedAt.toISOString(),
76684
77092
  completedAt: completedAt.toISOString()
76685
77093
  }
@@ -76721,15 +77129,15 @@ async function describeNangoToolDirect(identity, connectionId, toolName) {
76721
77129
  providerContractHash: MAIN_INTEGRATION_CONTRACT_HASH,
76722
77130
  protocolVersion: null,
76723
77131
  schemaSource: "live_tools_list",
76724
- schemaHash: (0, import_node_crypto58.createHash)("sha256").update(JSON.stringify(projected)).digest("hex"),
77132
+ schemaHash: (0, import_node_crypto59.createHash)("sha256").update(JSON.stringify(projected)).digest("hex"),
76725
77133
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
76726
77134
  };
76727
77135
  }
76728
- var import_node_crypto58, import_client13, DEFAULT_NANGO_MCP_URL, NANGO_TIMEOUT_MS, NANGO_CONNECTION_PAGE_SIZE, NANGO_CONNECTION_MAX_PAGES, MAIN_INTEGRATION_CONTRACT_VERSION, MAIN_INTEGRATION_CONTRACT_HASH, MainNangoTransportError;
77136
+ var import_node_crypto59, import_client13, DEFAULT_NANGO_MCP_URL, NANGO_TIMEOUT_MS, NANGO_CONNECTION_PAGE_SIZE, NANGO_CONNECTION_MAX_PAGES, MAIN_INTEGRATION_CONTRACT_VERSION, MAIN_INTEGRATION_CONTRACT_HASH, MainNangoTransportError;
76729
77137
  var init_main_nango_transport = __esm({
76730
77138
  "src/api/main-nango-transport.ts"() {
76731
77139
  "use strict";
76732
- import_node_crypto58 = require("crypto");
77140
+ import_node_crypto59 = require("crypto");
76733
77141
  import_client13 = require("@modelcontextprotocol/client");
76734
77142
  init_service_connections();
76735
77143
  init_connected_usage_billing();
@@ -77353,8 +77761,8 @@ async function setScheduleConnectionActionsEnabled(identity, connectionId, enabl
77353
77761
  return data.connection.actionsEnabled === true;
77354
77762
  }
77355
77763
  async function callScheduleConnectionAction(identity, connectionId, input, tool, idempotencyKey3) {
77356
- const requestId = `main-connected-action:${(0, import_node_crypto59.createHash)("sha256").update(identity).update("\0").update(idempotencyKey3?.trim() || (0, import_node_crypto59.randomUUID)()).digest("hex")}`;
77357
- const requestDigest = (0, import_node_crypto59.createHash)("sha256").update(connectionId).update("\0").update(tool?.trim() ?? "").update("\0").update(canonicalJson2(input)).digest("hex");
77764
+ const requestId = `main-connected-action:${(0, import_node_crypto60.createHash)("sha256").update(identity).update("\0").update(idempotencyKey3?.trim() || (0, import_node_crypto60.randomUUID)()).digest("hex")}`;
77765
+ const requestDigest = (0, import_node_crypto60.createHash)("sha256").update(connectionId).update("\0").update(tool?.trim() ?? "").update("\0").update(canonicalJson2(input)).digest("hex");
77358
77766
  if (mainOwnsIntegrations()) {
77359
77767
  const selectedTool = tool?.trim();
77360
77768
  if (!selectedTool) throw new NangoControlError("An action tool is required.", 400, "invalid_request", false);
@@ -77505,7 +77913,7 @@ function canonicalJson2(value) {
77505
77913
  return JSON.stringify(value);
77506
77914
  }
77507
77915
  function projectedToolSchemaHash(tool) {
77508
- return (0, import_node_crypto59.createHash)("sha256").update(canonicalJson2(tool)).digest("hex");
77916
+ return (0, import_node_crypto60.createHash)("sha256").update(canonicalJson2(tool)).digest("hex");
77509
77917
  }
77510
77918
  async function describeNangoTool(identity, connectionId, tool, fresh) {
77511
77919
  if (mainOwnsIntegrations()) {
@@ -77782,11 +78190,11 @@ async function callMainOwnedExportPage(identity, input) {
77782
78190
  untrustedContent: true
77783
78191
  };
77784
78192
  }
77785
- var import_node_crypto59, DEFAULT_NANGO_CONTROL_URL, DISABLED_NANGO_TOOLS, CONNECTION_SYNC_REQUIRED_TOOLS, CONNECTION_SYNC_OPTIONAL_TOOLS, NangoControlError, ScheduleConnectionValidationError, SAFE_CONTROL_ERROR_CODES, FIXED_CONTROL_ERROR_MESSAGES, CONTROL_ERROR_CODE_ALIASES;
78193
+ var import_node_crypto60, DEFAULT_NANGO_CONTROL_URL, DISABLED_NANGO_TOOLS, CONNECTION_SYNC_REQUIRED_TOOLS, CONNECTION_SYNC_OPTIONAL_TOOLS, NangoControlError, ScheduleConnectionValidationError, SAFE_CONTROL_ERROR_CODES, FIXED_CONTROL_ERROR_MESSAGES, CONTROL_ERROR_CODE_ALIASES;
77786
78194
  var init_nango_control = __esm({
77787
78195
  "src/api/nango-control.ts"() {
77788
78196
  "use strict";
77789
- import_node_crypto59 = require("crypto");
78197
+ import_node_crypto60 = require("crypto");
77790
78198
  init_connected_data_export();
77791
78199
  init_slack_connected_data_export();
77792
78200
  init_main_nango_transport();
@@ -78142,7 +78550,7 @@ async function callResendRead(identity, connectionId, tool, args) {
78142
78550
  return isRecord5(data) ? data.result ?? data : data;
78143
78551
  }
78144
78552
  async function callResendAction(identity, connectionId, tool, input, idempotencyKey3) {
78145
- const requestId = `main-resend-action:${(0, import_node_crypto60.createHash)("sha256").update(identity).update("\0").update(idempotencyKey3.trim()).digest("hex")}`;
78553
+ const requestId = `main-resend-action:${(0, import_node_crypto61.createHash)("sha256").update(identity).update("\0").update(idempotencyKey3.trim()).digest("hex")}`;
78146
78554
  const body = await controlRequest2("/api/internal/resend/actions/call", {
78147
78555
  method: "POST",
78148
78556
  headers: { "x-request-id": requestId },
@@ -78207,12 +78615,12 @@ async function callResendExportPage(identity, input) {
78207
78615
  untrustedContent: true
78208
78616
  };
78209
78617
  }
78210
- var import_node_crypto60, DEFAULT_CONNECTION_CONTROL_URL, RESEND_PROVIDER_CONFIG_KEY, RESEND_LOGO_URL, RESEND_DOCS_URL, RESEND_ADMIN_BLOCKED_TOOLS, RESEND_CONNECTION_SYNC_REQUIRED_TOOLS, ResendControlError;
78618
+ var import_node_crypto61, DEFAULT_CONNECTION_CONTROL_URL, RESEND_PROVIDER_CONFIG_KEY, RESEND_LOGO_URL, RESEND_DOCS_URL, RESEND_ADMIN_BLOCKED_TOOLS, RESEND_CONNECTION_SYNC_REQUIRED_TOOLS, ResendControlError;
78211
78619
  var init_resend_control = __esm({
78212
78620
  "src/api/resend-control.ts"() {
78213
78621
  "use strict";
78214
78622
  init_connected_data_export();
78215
- import_node_crypto60 = require("crypto");
78623
+ import_node_crypto61 = require("crypto");
78216
78624
  DEFAULT_CONNECTION_CONTROL_URL = "https://mcp-scraper-scheduler.vercel.app";
78217
78625
  RESEND_PROVIDER_CONFIG_KEY = "resend";
78218
78626
  RESEND_LOGO_URL = "https://cdn.resend.com/brand/resend-icon-black.svg";
@@ -78797,7 +79205,7 @@ function settleWithinTickBudget(label, unfinished, work, onDeadlineOrError) {
78797
79205
  );
78798
79206
  });
78799
79207
  }
78800
- var import_resend3, import_node_crypto61, import_hono36, import_hono37, import_factory8, import_cookie2, import_stripe2, secureCookies2, isProduction2, sessionCookieOptions2, requireAllowedOrigin, auth4, sessionAuth, requireIntegrationsTier, requirePaidSchedulingTier, app, deploymentProfile, STRIPE_API_VERSION, SYNC_HARVEST_TIMEOUT_OVERRIDE_MS, CRON_TICK_BUDGET_MS, CRON_TICK_DRAIN_BUDGET_MS;
79208
+ var import_resend3, import_node_crypto62, import_hono36, import_hono37, import_factory8, import_cookie2, import_stripe2, secureCookies2, isProduction2, sessionCookieOptions2, requireAllowedOrigin, auth4, sessionAuth, requireIntegrationsTier, requirePaidSchedulingTier, app, deploymentProfile, STRIPE_API_VERSION, SYNC_HARVEST_TIMEOUT_OVERRIDE_MS, CRON_TICK_BUDGET_MS, CRON_TICK_DRAIN_BUDGET_MS;
78801
79209
  var init_server = __esm({
78802
79210
  "src/api/server.ts"() {
78803
79211
  "use strict";
@@ -78810,7 +79218,7 @@ var init_server = __esm({
78810
79218
  init_og();
78811
79219
  import_resend3 = require("resend");
78812
79220
  init_url_utils();
78813
- import_node_crypto61 = require("crypto");
79221
+ import_node_crypto62 = require("crypto");
78814
79222
  init_kpo_extractor();
78815
79223
  init_screenshot();
78816
79224
  init_media_extractor();
@@ -80360,7 +80768,7 @@ var init_server = __esm({
80360
80768
  if (!harvestOk) return c.json(insufficientBalanceResponse(harvestBal, harvestCost), 402);
80361
80769
  jobId2 = await createJob(user.id, options.query, { ...options, billingHoldMc: harvestCost }, body.callback_url);
80362
80770
  } else {
80363
- jobId2 = (0, import_node_crypto61.randomUUID)();
80771
+ jobId2 = (0, import_node_crypto62.randomUUID)();
80364
80772
  const billingDebitKey = `paa-harvest:${jobId2}:hold`;
80365
80773
  const description = `PAA harvest: ${options.query}`.slice(0, 500);
80366
80774
  const hold = await debitMcIdempotent(