mcp-scraper 0.56.0 → 0.57.1

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 +28 -0
  2. package/README.md +2 -2
  3. package/dist/bin/api-server.cjs +576 -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-JEZW6N2X.js → chunk-2KL4MSZP.js} +26 -3
  16. package/dist/chunk-2KL4MSZP.js.map +1 -0
  17. package/dist/{chunk-NP66MZ5A.js → chunk-5XYBKE3X.js} +2 -2
  18. package/dist/{chunk-LUJYVNO7.js → chunk-HU4R6BP2.js} +2 -2
  19. package/dist/{chunk-LUJYVNO7.js.map → chunk-HU4R6BP2.js.map} +1 -1
  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-VTBJVKBE.js +7 -0
  23. package/dist/chunk-VTBJVKBE.js.map +1 -0
  24. package/dist/{extract-bundle-6KYTD55T.js → extract-bundle-H7JEELY7.js} +3 -3
  25. package/dist/{server-SETYJPYO.js → server-DSCKFM7K.js} +459 -72
  26. package/dist/server-DSCKFM7K.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.1";
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,243 @@ 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 commonsImageBlobStore() {
64403
+ const token6 = process.env.COMMONS_IMAGE_READ_WRITE_TOKEN?.trim() || process.env.COMMONS_IMAGE_BLOB_READ_WRITE_TOKEN?.trim() || process.env.BLOB_READ_WRITE_TOKEN?.trim();
64404
+ return token6 ? new VercelBlobStore(token6) : getBlobStore();
64405
+ }
64406
+ function isCommonsHostedImageUrl(url) {
64407
+ return /^https:\/\/[^/]*(\.public\.blob\.vercel-storage\.com|blob\.vercel-storage\.com)\//i.test(url);
64408
+ }
64409
+ async function hostCommonsImage(input) {
64410
+ if (hostOverride) return hostOverride(input);
64411
+ await ensureCommonsImageSchema();
64412
+ if (!input.sourceUrl && !input.imageBase64) {
64413
+ throw new CommonsImageError("image_source_missing", "Provide either sourceUrl or imageBase64.");
64414
+ }
64415
+ const bytes = input.imageBase64 ? decodeBase64Image(input.imageBase64) : await downloadImage(String(input.sourceUrl));
64416
+ const contentType = sniffImageFormat(bytes);
64417
+ if (!contentType) {
64418
+ throw new CommonsImageError(
64419
+ "image_format_unsupported",
64420
+ "Commons hosts JPEG, PNG, GIF, and WebP. SVG and other formats are rejected because the wiki serves them to the public web.",
64421
+ 415
64422
+ );
64423
+ }
64424
+ const digest2 = (0, import_node_crypto37.createHash)("sha256").update(bytes).digest("hex");
64425
+ const existing = await getDb().execute({
64426
+ sql: "SELECT id, url, content_type, bytes, source_url FROM commons_images WHERE digest = ? LIMIT 1",
64427
+ args: [digest2]
64428
+ });
64429
+ const reusable = existing.rows[0];
64430
+ if (reusable) {
64431
+ return {
64432
+ id: String(reusable.id),
64433
+ url: String(reusable.url),
64434
+ contentType: String(reusable.content_type),
64435
+ bytes: Number(reusable.bytes),
64436
+ digest: digest2,
64437
+ sourceUrl: reusable.source_url ? String(reusable.source_url) : null,
64438
+ reused: true
64439
+ };
64440
+ }
64441
+ const extension = contentType.split("/")[1] === "jpeg" ? "jpg" : contentType.split("/")[1];
64442
+ const storageKey = `commons/images/${digest2.slice(0, 2)}/${digest2}.${extension}`;
64443
+ const store = commonsImageBlobStore();
64444
+ if (store.kind !== "vercel-blob") {
64445
+ throw new CommonsImageError(
64446
+ "image_storage_unconfigured",
64447
+ "Commons image hosting needs a Vercel Blob token. Refusing to write a local file path into a public wiki entity.",
64448
+ 422
64449
+ );
64450
+ }
64451
+ const stored = await store.put(storageKey, bytes, contentType);
64452
+ const id = `TPW-IMG-${digest2.slice(0, 16).toUpperCase()}`;
64453
+ await getDb().execute({
64454
+ sql: `
64455
+ INSERT INTO commons_images (id, user_id, digest, storage_key, url, content_type, bytes, source_url, alt, license, attribution, created_at)
64456
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
64457
+ `,
64458
+ args: [
64459
+ id,
64460
+ input.userId,
64461
+ digest2,
64462
+ storageKey,
64463
+ stored.url,
64464
+ contentType,
64465
+ bytes.length,
64466
+ input.sourceUrl ?? null,
64467
+ input.alt ?? null,
64468
+ input.license ?? null,
64469
+ input.attribution ?? null,
64470
+ (/* @__PURE__ */ new Date()).toISOString()
64471
+ ]
64472
+ });
64473
+ return { id, url: stored.url, contentType, bytes: bytes.length, digest: digest2, sourceUrl: input.sourceUrl ?? null, reused: false };
64474
+ }
64475
+ async function hostEntityImages(input) {
64476
+ const rewrite = { mediaUrls: /* @__PURE__ */ new Map(), dropped: [] };
64477
+ const featuredUrl = input.featuredImage?.url;
64478
+ if (featuredUrl && !isCommonsHostedImageUrl(featuredUrl)) {
64479
+ const hosted = await hostCommonsImage({
64480
+ userId: input.userId,
64481
+ sourceUrl: featuredUrl,
64482
+ alt: input.featuredImage?.alt,
64483
+ license: input.featuredImage?.license,
64484
+ attribution: input.featuredImage?.sourceUrl
64485
+ }).catch((error) => {
64486
+ const message = error instanceof Error ? error.message : String(error);
64487
+ throw new CommonsImageError(
64488
+ error instanceof CommonsImageError ? error.code : "featured_image_unhostable",
64489
+ `The featured image could not be hosted, so this entity would publish with a link that can break: ${message}`,
64490
+ 422
64491
+ );
64492
+ });
64493
+ rewrite.featuredImageUrl = hosted.url;
64494
+ }
64495
+ for (const item of input.media ?? []) {
64496
+ if (!item.url || isCommonsHostedImageUrl(item.url)) continue;
64497
+ try {
64498
+ const hosted = await hostCommonsImage({
64499
+ userId: input.userId,
64500
+ sourceUrl: item.url,
64501
+ alt: item.alt,
64502
+ license: item.license,
64503
+ attribution: item.sourceUrl
64504
+ });
64505
+ rewrite.mediaUrls.set(item.url, hosted.url);
64506
+ } catch (error) {
64507
+ rewrite.dropped.push({ url: item.url, reason: error instanceof Error ? error.message : String(error) });
64508
+ }
64509
+ }
64510
+ return rewrite;
64511
+ }
64512
+ 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;
64513
+ var init_commons_image_store = __esm({
64514
+ "src/api/commons-image-store.ts"() {
64515
+ "use strict";
64516
+ import_node_crypto37 = require("crypto");
64517
+ import_promises15 = require("dns/promises");
64518
+ import_node_net2 = require("net");
64519
+ init_blob_store();
64520
+ init_db();
64521
+ COMMONS_IMAGE_MAX_BYTES = 10 * 1024 * 1024;
64522
+ COMMONS_IMAGE_MAX_REDIRECTS = 3;
64523
+ COMMONS_IMAGE_FETCH_TIMEOUT_MS = 2e4;
64524
+ CommonsImageError = class extends Error {
64525
+ constructor(code, message, httpStatus = 400) {
64526
+ super(message);
64527
+ this.code = code;
64528
+ this.httpStatus = httpStatus;
64529
+ this.name = "CommonsImageError";
64530
+ }
64531
+ code;
64532
+ httpStatus;
64533
+ };
64534
+ imageSchemaReady = false;
64535
+ hostOverride = null;
64536
+ }
64537
+ });
64538
+
64279
64539
  // src/api/schema-presence.ts
64280
64540
  async function loadSchemaObjects() {
64281
64541
  const res = await getDb().execute(
@@ -64378,7 +64638,7 @@ function buildCommonsLinkset(entity, claims) {
64378
64638
  context[claim.predicate] = targets;
64379
64639
  }
64380
64640
  const document2 = { linkset: [context] };
64381
- const etag = `"${(0, import_node_crypto37.createHash)("sha256").update(JSON.stringify(document2)).digest("hex")}"`;
64641
+ const etag = `"${(0, import_node_crypto38.createHash)("sha256").update(JSON.stringify(document2)).digest("hex")}"`;
64382
64642
  return { document: document2, etag, publicUrl, linksetUrl, profile: COMMONS_RELATIONSHIP_PROFILE };
64383
64643
  }
64384
64644
  function commonsLinksetDiscoveryHeader(idOrSlug) {
@@ -64446,11 +64706,11 @@ function normalizeHreflang(value) {
64446
64706
  const languages = [...new Set(value.map((item) => optionalText(item, 80)).filter((item) => Boolean(item)))];
64447
64707
  return languages.length ? languages : void 0;
64448
64708
  }
64449
- var import_node_crypto37, COMMONS_LINKSET_MEDIA_TYPE, COMMONS_RELATIONSHIP_PROFILE, REGISTERED_RELATIONS;
64709
+ var import_node_crypto38, COMMONS_LINKSET_MEDIA_TYPE, COMMONS_RELATIONSHIP_PROFILE, REGISTERED_RELATIONS;
64450
64710
  var init_commons_linksets = __esm({
64451
64711
  "src/api/commons-linksets.ts"() {
64452
64712
  "use strict";
64453
- import_node_crypto37 = require("crypto");
64713
+ import_node_crypto38 = require("crypto");
64454
64714
  COMMONS_LINKSET_MEDIA_TYPE = "application/linkset+json";
64455
64715
  COMMONS_RELATIONSHIP_PROFILE = "https://mcpscraper.dev/commons/profiles/relationships/v1";
64456
64716
  REGISTERED_RELATIONS = /* @__PURE__ */ new Set([
@@ -64781,23 +65041,99 @@ async function searchCommonsEntities(filters = {}, userId) {
64781
65041
  const pattern = `%${escapeLike(term)}%`;
64782
65042
  args.push(pattern, pattern, pattern, pattern, pattern);
64783
65043
  }
64784
- args.push(limit, offset);
64785
- const result = await getDb().execute({
65044
+ if (!terms.length) {
65045
+ args.push(limit, offset);
65046
+ const result = await getDb().execute({
65047
+ sql: `
65048
+ SELECT *
65049
+ FROM commons_entities
65050
+ WHERE ${clauses.join(" AND ")}
65051
+ ORDER BY updated_at DESC, title ASC
65052
+ LIMIT ? OFFSET ?
65053
+ `,
65054
+ args
65055
+ });
65056
+ const results2 = result.rows.map((row) => rowToEntity(row));
65057
+ await resolveRelatedLinksAgainstPublishedEntities(results2);
65058
+ return { results: results2, filters: mergedFilters, limit, offset };
65059
+ }
65060
+ const candidates = await getDb().execute({
64786
65061
  sql: `
64787
- SELECT *
65062
+ SELECT id, title, tags_json, keywords_json, description, search_text, updated_at,
65063
+ (
65064
+ SELECT group_concat(
65065
+ coalesce(commons_claims.title, '') || ' ' ||
65066
+ coalesce(commons_claims.predicate, '') || ' ' ||
65067
+ coalesce(commons_claims.predicate_label, '') || ' ' ||
65068
+ coalesce(commons_claims.object_url, ''), ' ')
65069
+ FROM commons_claims
65070
+ WHERE commons_claims.subject_entity_id = commons_entities.id
65071
+ ) AS claim_text
64788
65072
  FROM commons_entities
64789
65073
  WHERE ${clauses.join(" AND ")}
64790
65074
  ORDER BY updated_at DESC, title ASC
64791
- LIMIT ? OFFSET ?
65075
+ LIMIT ${SEARCH_CANDIDATE_LIMIT}
64792
65076
  `,
64793
65077
  args
64794
65078
  });
64795
- return {
64796
- results: result.rows.map((row) => rowToEntity(row)),
64797
- filters: mergedFilters,
64798
- limit,
64799
- offset
64800
- };
65079
+ const ranked = candidates.rows.map((row) => {
65080
+ const record = row;
65081
+ const score = relevanceScore(terms, {
65082
+ title: String(record.title ?? ""),
65083
+ description: String(record.description ?? ""),
65084
+ tags: parseJsonArray(record.tags_json),
65085
+ keywords: parseJsonArray(record.keywords_json),
65086
+ searchText: String(record.search_text ?? ""),
65087
+ claimText: String(record.claim_text ?? "")
65088
+ });
65089
+ return { id: String(record.id), updatedAt: String(record.updated_at ?? ""), title: String(record.title ?? ""), score };
65090
+ }).filter((candidate) => candidate.score > 0).sort((left, right) => right.score - left.score || right.updatedAt.localeCompare(left.updatedAt) || left.title.localeCompare(right.title));
65091
+ const pageIds = ranked.slice(offset, offset + limit).map((candidate) => candidate.id);
65092
+ if (!pageIds.length) return { results: [], filters: mergedFilters, limit, offset };
65093
+ const page = await getDb().execute({
65094
+ sql: `SELECT * FROM commons_entities WHERE id IN (${pageIds.map(() => "?").join(", ")})`,
65095
+ args: pageIds
65096
+ });
65097
+ const byId = new Map(page.rows.map((row) => {
65098
+ const entity = rowToEntity(row);
65099
+ return [entity.id, entity];
65100
+ }));
65101
+ const results = pageIds.map((id) => byId.get(id)).filter((entity) => Boolean(entity));
65102
+ await resolveRelatedLinksAgainstPublishedEntities(results);
65103
+ return { results, filters: mergedFilters, limit, offset };
65104
+ }
65105
+ function parseJsonArray(value) {
65106
+ try {
65107
+ const parsed = JSON.parse(String(value ?? "[]"));
65108
+ return Array.isArray(parsed) ? parsed.map((item) => String(item)) : [];
65109
+ } catch {
65110
+ return [];
65111
+ }
65112
+ }
65113
+ function containsWord(haystack, term) {
65114
+ if (!haystack || !term) return false;
65115
+ const pattern = new RegExp(`(^|[^a-z0-9])${term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}([^a-z0-9]|$)`, "i");
65116
+ return pattern.test(haystack);
65117
+ }
65118
+ function relevanceScore(terms, fields) {
65119
+ let score = 0;
65120
+ const tagText = fields.tags.join(" ");
65121
+ const keywordText = fields.keywords.join(" ");
65122
+ for (const term of terms) {
65123
+ const inTitle = containsWord(fields.title, term);
65124
+ const inTags = containsWord(tagText, term) || containsWord(keywordText, term);
65125
+ const inDescription = containsWord(fields.description, term);
65126
+ const inBody = containsWord(fields.searchText, term);
65127
+ const inClaims = containsWord(fields.claimText, term);
65128
+ if (!inTitle && !inTags && !inDescription && !inBody && !inClaims) return 0;
65129
+ if (inTitle) score += 100;
65130
+ if (inTags) score += 40;
65131
+ if (inDescription) score += 20;
65132
+ if (inClaims) score += 10;
65133
+ if (inBody) score += 5;
65134
+ }
65135
+ if (terms.every((term) => containsWord(fields.title, term))) score += 50;
65136
+ return score;
64801
65137
  }
64802
65138
  async function prepareCommonsEntity(input, user) {
64803
65139
  await ensureCommonsSchema();
@@ -64944,7 +65280,10 @@ async function getCommonsEntity(idOrSlug) {
64944
65280
  `,
64945
65281
  args: [lookup2, normalizeSlug(lookup2)]
64946
65282
  });
64947
- return result.rows[0] ? rowToEntity(result.rows[0]) : null;
65283
+ if (!result.rows[0]) return null;
65284
+ const entity = rowToEntity(result.rows[0]);
65285
+ await resolveRelatedLinksAgainstPublishedEntities([entity]);
65286
+ return entity;
64948
65287
  }
64949
65288
  async function classifyCommonsWrite(input, user) {
64950
65289
  await ensureCommonsSchema();
@@ -64967,14 +65306,30 @@ async function submitCommonsEntity(input, user) {
64967
65306
  applied: existingProposal.status === "accepted",
64968
65307
  reviewReason: existingProposal.reviewReason,
64969
65308
  conflictNote: null,
65309
+ droppedImages: [],
64970
65310
  publicUrl: entity2?.status === "published" ? publicUrlForSlug(entity2.slug) : null
64971
65311
  };
64972
65312
  }
65313
+ const hostedImages = await hostEntityImages({
65314
+ userId: Number(user.id),
65315
+ featuredImage: normalized.featuredImage,
65316
+ media: normalized.media
65317
+ });
65318
+ if (hostedImages.featuredImageUrl && normalized.featuredImage) {
65319
+ normalized.featuredImage = { ...normalized.featuredImage, url: hostedImages.featuredImageUrl };
65320
+ }
65321
+ if (hostedImages.mediaUrls.size || hostedImages.dropped.length) {
65322
+ const droppedUrls = new Set(hostedImages.dropped.map((item) => item.url));
65323
+ normalized.media = normalized.media.filter((item) => !droppedUrls.has(item.url)).map((item) => {
65324
+ const hostedUrl = hostedImages.mediaUrls.get(item.url);
65325
+ return hostedUrl ? { ...item, url: hostedUrl } : item;
65326
+ });
65327
+ }
64973
65328
  const existing = await findEntityBySlugOrIdAnyStatus(normalized.slug, normalized.entityId);
64974
65329
  const existingClaims = existing && normalized.claims !== void 0 ? await getCommonsClaimsByEntityId(existing.id) : [];
64975
65330
  const safety = evaluatePublishSafety(normalized, existing);
64976
65331
  const shouldApply = safety.safe && normalized.reviewPolicy !== "always_review";
64977
- const proposalId = `commons-proposal-${(0, import_node_crypto38.randomUUID)()}`;
65332
+ const proposalId = `commons-proposal-${(0, import_node_crypto39.randomUUID)()}`;
64978
65333
  const now = (/* @__PURE__ */ new Date()).toISOString();
64979
65334
  const entityId = existing?.id ?? normalized.entityId ?? allocateEntityId();
64980
65335
  const proposalStatus = shouldApply ? "accepted" : "pending_review";
@@ -65029,7 +65384,7 @@ async function submitCommonsEntity(input, user) {
65029
65384
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
65030
65385
  `,
65031
65386
  args: [
65032
- `commons-ledger-${(0, import_node_crypto38.randomUUID)()}`,
65387
+ `commons-ledger-${(0, import_node_crypto39.randomUUID)()}`,
65033
65388
  nextEntity.id,
65034
65389
  proposalId,
65035
65390
  user.id,
@@ -65064,6 +65419,7 @@ async function submitCommonsEntity(input, user) {
65064
65419
  applied: shouldApply,
65065
65420
  reviewReason: safety.reason,
65066
65421
  conflictNote: safety.conflictNote,
65422
+ droppedImages: hostedImages.dropped,
65067
65423
  publicUrl: entity?.status === "published" && shouldApply ? publicUrlForSlug(entity.slug) : null
65068
65424
  };
65069
65425
  }
@@ -65134,7 +65490,7 @@ async function saveCommonsFilter(input, user) {
65134
65490
  sql: "SELECT id FROM commons_saved_filters WHERE user_id = ? AND name = ? LIMIT 1",
65135
65491
  args: [user.id, name]
65136
65492
  });
65137
- const id = existing.rows[0]?.id != null ? String(existing.rows[0].id) : `commons-filter-${(0, import_node_crypto38.randomUUID)()}`;
65493
+ const id = existing.rows[0]?.id != null ? String(existing.rows[0].id) : `commons-filter-${(0, import_node_crypto39.randomUUID)()}`;
65138
65494
  await getDb().execute({
65139
65495
  sql: `
65140
65496
  INSERT INTO commons_saved_filters (id, user_id, name, description, filter_json, created_at, updated_at)
@@ -65202,8 +65558,9 @@ async function listCommonsNeedsLinks(filters = {}) {
65202
65558
  });
65203
65559
  const concepts = /* @__PURE__ */ new Map();
65204
65560
  const queryTerms = tokenize(cleanFilters.query);
65205
- for (const row of result.rows) {
65206
- const entity = rowToEntity(row);
65561
+ const scanned = result.rows.map((row) => rowToEntity(row));
65562
+ await resolveRelatedLinksAgainstPublishedEntities(scanned);
65563
+ for (const entity of scanned) {
65207
65564
  for (const link2 of entity.articleSections.relatedLinks) {
65208
65565
  if (!relatedLinkNeedsPage(link2)) continue;
65209
65566
  const haystack = [link2.title, link2.summary, link2.relationship, entity.title, entity.description, entity.tags.join(" ")].filter(Boolean).join(" ").toLowerCase();
@@ -65911,7 +66268,7 @@ function commonsIndexDocuments(entity, now) {
65911
66268
  return documents;
65912
66269
  }
65913
66270
  function commonsIndexDocumentId(entityId, documentType, documentKey) {
65914
- return `commons-index-${(0, import_node_crypto38.createHash)("sha256").update(`${entityId}
66271
+ return `commons-index-${(0, import_node_crypto39.createHash)("sha256").update(`${entityId}
65915
66272
  ${documentType}
65916
66273
  ${documentKey}`).digest("hex").slice(0, 32)}`;
65917
66274
  }
@@ -66246,6 +66603,44 @@ function sanitizeNeedsLinkFilters(filters) {
66246
66603
  offset: clampNumber(filters.offset, 0, 1e4, 0)
66247
66604
  };
66248
66605
  }
66606
+ async function publishedEntityIndexFor(titles) {
66607
+ const index = /* @__PURE__ */ new Map();
66608
+ const slugKeys = uniqueStrings(titles.map((title) => normalizeNeedsLinkTitle(title))).filter(Boolean);
66609
+ const titleKeys = uniqueStrings(titles.map((title) => String(title || "").trim().toLowerCase())).filter(Boolean);
66610
+ if (!slugKeys.length && !titleKeys.length) return index;
66611
+ const result = await getDb().execute({
66612
+ sql: `
66613
+ SELECT id, slug, title
66614
+ FROM commons_entities
66615
+ WHERE status = 'published'
66616
+ AND (slug IN (${slugKeys.map(() => "?").join(", ") || "NULL"})
66617
+ OR lower(title) IN (${titleKeys.map(() => "?").join(", ") || "NULL"}))
66618
+ `,
66619
+ args: [...slugKeys, ...titleKeys]
66620
+ });
66621
+ for (const row of result.rows) {
66622
+ const record = row;
66623
+ const entry = { id: String(record.id), slug: String(record.slug), title: String(record.title) };
66624
+ index.set(entry.slug, entry);
66625
+ index.set(normalizeNeedsLinkTitle(entry.title), entry);
66626
+ }
66627
+ return index;
66628
+ }
66629
+ function unresolvedRelatedLinkTitles(entities) {
66630
+ return entities.flatMap((entity) => entity.articleSections.relatedLinks.filter((link2) => relatedLinkNeedsPage(link2)).map((link2) => link2.title));
66631
+ }
66632
+ async function resolveRelatedLinksAgainstPublishedEntities(entities) {
66633
+ const index = await publishedEntityIndexFor(unresolvedRelatedLinkTitles(entities));
66634
+ if (!index.size) return;
66635
+ for (const entity of entities) {
66636
+ entity.articleSections.relatedLinks = entity.articleSections.relatedLinks.map((link2) => {
66637
+ if (!relatedLinkNeedsPage(link2)) return link2;
66638
+ const match = index.get(normalizeNeedsLinkTitle(link2.title));
66639
+ if (!match || match.id === entity.id) return link2;
66640
+ return { ...link2, slug: match.slug, entityId: match.id, needsLink: false };
66641
+ });
66642
+ }
66643
+ }
66249
66644
  function relatedLinkNeedsPage(link2) {
66250
66645
  if (link2.needsLink === true) return true;
66251
66646
  if (link2.entityId || link2.slug) return false;
@@ -66438,12 +66833,13 @@ function jsonLikeValue(value) {
66438
66833
  function escapeLike(value) {
66439
66834
  return value.replace(/[%_]/g, "");
66440
66835
  }
66441
- var import_node_crypto38, COMMONS_SCHEMA_VERSION, DEFAULT_COMMONS_BASE_URL, DEFAULT_ENTITY_TYPE, COMMONS_ENTITY_PROFILES, schemaReady, COMMONS_SCHEMA_OBJECTS, CommonsRepositoryError;
66836
+ 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
66837
  var init_commons_repository = __esm({
66443
66838
  "src/api/commons-repository.ts"() {
66444
66839
  "use strict";
66445
- import_node_crypto38 = require("crypto");
66840
+ import_node_crypto39 = require("crypto");
66446
66841
  init_db();
66842
+ init_commons_image_store();
66447
66843
  init_schema_presence();
66448
66844
  init_rates();
66449
66845
  init_commons_linksets();
@@ -66621,6 +67017,7 @@ var init_commons_repository = __esm({
66621
67017
  "commons_publications_updated",
66622
67018
  "commons_publication_editions_publication_published"
66623
67019
  ];
67020
+ SEARCH_CANDIDATE_LIMIT = 500;
66624
67021
  CommonsRepositoryError = class extends Error {
66625
67022
  constructor(code, message, httpStatus = 400) {
66626
67023
  super(message);
@@ -66723,7 +67120,7 @@ async function claimCommonsPublication(input, user) {
66723
67120
  }
66724
67121
  const existingName = await getCommonsPublicationBySubdomain(subdomain);
66725
67122
  if (existingName) throw new CommonsPublicationError("publication_name_unavailable", "That publication name is already claimed.", 409);
66726
- const id = `tcpub_${(0, import_node_crypto39.randomUUID)()}`;
67123
+ const id = `tcpub_${(0, import_node_crypto40.randomUUID)()}`;
66727
67124
  const now = (/* @__PURE__ */ new Date()).toISOString();
66728
67125
  try {
66729
67126
  await getDb().execute({
@@ -66775,7 +67172,7 @@ async function publishCommonsEditorial(input, user) {
66775
67172
  const canonicalUrl = editionPublicUrl(subdomain, editionSlug);
66776
67173
  const rendered = renderEditorialReadingRoom(editionInput);
66777
67174
  const html = addPublicMetadata(rendered.html, canonicalUrl, publication.title);
66778
- const editionId = `tced_${(0, import_node_crypto39.randomUUID)()}`;
67175
+ const editionId = `tced_${(0, import_node_crypto40.randomUUID)()}`;
66779
67176
  const revision = (latest?.revision ?? 0) + 1;
66780
67177
  const now = (/* @__PURE__ */ new Date()).toISOString();
66781
67178
  await getDb().batch([
@@ -66799,7 +67196,7 @@ async function publishCommonsEditorial(input, user) {
66799
67196
  JSON.stringify(editionInput.articles),
66800
67197
  html,
66801
67198
  rendered.filename,
66802
- (0, import_node_crypto39.createHash)("sha256").update(html).digest("hex"),
67199
+ (0, import_node_crypto40.createHash)("sha256").update(html).digest("hex"),
66803
67200
  rendered.articleCount,
66804
67201
  rendered.wordCount,
66805
67202
  Buffer.byteLength(html),
@@ -66985,11 +67382,11 @@ function addPublicMetadata(html, canonicalUrl, publicationTitle) {
66985
67382
  "</head>"
66986
67383
  ].join("\n"));
66987
67384
  }
66988
- var import_node_crypto39, PUBLICATION_ROOT_DOMAIN, RESERVED_SUBDOMAINS, CommonsPublicationError;
67385
+ var import_node_crypto40, PUBLICATION_ROOT_DOMAIN, RESERVED_SUBDOMAINS, CommonsPublicationError;
66989
67386
  var init_commons_publication_repository = __esm({
66990
67387
  "src/api/commons-publication-repository.ts"() {
66991
67388
  "use strict";
66992
- import_node_crypto39 = require("crypto");
67389
+ import_node_crypto40 = require("crypto");
66993
67390
  init_db();
66994
67391
  init_commons_repository();
66995
67392
  init_render();
@@ -67090,7 +67487,7 @@ function publicationError(c, error) {
67090
67487
  function xmlEscape(value) {
67091
67488
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
67092
67489
  }
67093
- var import_hono31, import_zod52, commonsApp, auth2, SearchBodySchema, FeaturedImageSchema, MediaSchema, CitationSchema, RelatedLinkSchema, SubmitEntitySchema, PrepareEntitySchema, ValidateEntitySchema, SaveFilterSchema, NeedsLinkBodySchema, PreparePublicationSchema, ValidatePublicationSchema, ClaimPublicationSchema, PublishEditorialSchema;
67490
+ var import_hono31, import_zod52, commonsApp, auth2, SearchBodySchema, FeaturedImageSchema, MediaSchema, CitationSchema, RelatedLinkSchema, SubmitEntitySchema, PrepareEntitySchema, ValidateEntitySchema, SaveFilterSchema, NeedsLinkBodySchema, PreparePublicationSchema, ValidatePublicationSchema, ClaimPublicationSchema, PublishEditorialSchema, HostImageSchema;
67094
67491
  var init_commons_routes = __esm({
67095
67492
  "src/api/commons-routes.ts"() {
67096
67493
  "use strict";
@@ -67098,6 +67495,7 @@ var init_commons_routes = __esm({
67098
67495
  import_zod52 = require("zod");
67099
67496
  init_api_auth();
67100
67497
  init_commons_repository();
67498
+ init_commons_image_store();
67101
67499
  init_commons_linksets();
67102
67500
  init_listing_write_billing();
67103
67501
  init_commons_publication_repository();
@@ -67351,6 +67749,28 @@ var init_commons_routes = __esm({
67351
67749
  return publicationError(c, error);
67352
67750
  }
67353
67751
  });
67752
+ HostImageSchema = import_zod52.z.object({
67753
+ sourceUrl: import_zod52.z.string().url().optional(),
67754
+ imageBase64: import_zod52.z.string().min(16).optional(),
67755
+ alt: import_zod52.z.string().max(500).optional(),
67756
+ license: import_zod52.z.string().max(240).optional(),
67757
+ attribution: import_zod52.z.string().max(500).optional()
67758
+ }).strict().refine((body) => Boolean(body.sourceUrl || body.imageBase64), {
67759
+ message: "Provide either sourceUrl or imageBase64."
67760
+ });
67761
+ commonsApp.post("/images", auth2, async (c) => {
67762
+ const parsed = HostImageSchema.safeParse(await c.req.json().catch(() => ({})));
67763
+ if (!parsed.success) return validationError(c, parsed.error);
67764
+ try {
67765
+ const hosted = await hostCommonsImage({ ...parsed.data, userId: Number(c.get("user").id) });
67766
+ return c.json({ ok: true, data: hosted }, hosted.reused ? 200 : 201);
67767
+ } catch (error) {
67768
+ if (error instanceof CommonsImageError) {
67769
+ return c.json({ ok: false, error: error.code, message: error.message }, error.httpStatus);
67770
+ }
67771
+ throw error;
67772
+ }
67773
+ });
67354
67774
  commonsApp.get("/entities", async (c) => {
67355
67775
  const result = await searchCommonsEntities(filtersFromQuery(c.req.query()));
67356
67776
  return c.json({ ok: true, data: result });
@@ -68152,7 +68572,7 @@ async function migrateAnalytics() {
68152
68572
  }
68153
68573
  function normalizeSlug2(value) {
68154
68574
  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")}`;
68575
+ return slug4 || `site-${(0, import_node_crypto41.randomBytes)(4).toString("hex")}`;
68156
68576
  }
68157
68577
  function normalizeObservedHostname(origin) {
68158
68578
  if (!origin || origin === "null") return null;
@@ -68165,7 +68585,7 @@ function normalizeObservedHostname(origin) {
68165
68585
  }
68166
68586
  }
68167
68587
  function publicPixelId() {
68168
- return `px_${(0, import_node_crypto40.randomBytes)(18).toString("base64url")}`;
68588
+ return `px_${(0, import_node_crypto41.randomBytes)(18).toString("base64url")}`;
68169
68589
  }
68170
68590
  function mapRows(rows) {
68171
68591
  return rows;
@@ -68197,7 +68617,7 @@ async function requireEditor(client2, siteId, userId) {
68197
68617
  async function createAnalyticsSite(input) {
68198
68618
  const db = getAnalyticsPool();
68199
68619
  const client2 = await db.connect();
68200
- const id = (0, import_node_crypto40.randomUUID)();
68620
+ const id = (0, import_node_crypto41.randomUUID)();
68201
68621
  const baseSlug = normalizeSlug2(input.slug || input.name);
68202
68622
  try {
68203
68623
  await client2.query("BEGIN");
@@ -68213,7 +68633,7 @@ async function createAnalyticsSite(input) {
68213
68633
  } catch (error) {
68214
68634
  const code = error.code;
68215
68635
  if (code !== "23505" || attempt === 2) throw error;
68216
- slug4 = `${baseSlug}-${(0, import_node_crypto40.randomBytes)(2).toString("hex")}`;
68636
+ slug4 = `${baseSlug}-${(0, import_node_crypto41.randomBytes)(2).toString("hex")}`;
68217
68637
  }
68218
68638
  }
68219
68639
  await client2.query(
@@ -68428,7 +68848,7 @@ async function createAnalyticsPixel(input) {
68428
68848
  VALUES ($1, $2, $3, $4, $5)
68429
68849
  RETURNING id, site_id, public_id, name, environment, status, created_at::text, NULL::text AS last_event_at`,
68430
68850
  [
68431
- (0, import_node_crypto40.randomUUID)(),
68851
+ (0, import_node_crypto41.randomUUID)(),
68432
68852
  input.siteId,
68433
68853
  publicPixelId(),
68434
68854
  input.name.trim(),
@@ -68505,7 +68925,7 @@ async function setAnalyticsPixelDomainState(input) {
68505
68925
  SELECT $1, p.id, $4, $5 FROM analytics_pixels p WHERE p.id = $2 AND p.site_id = $3
68506
68926
  ON CONFLICT(pixel_id, hostname) DO UPDATE SET state = EXCLUDED.state, updated_at = now()
68507
68927
  RETURNING id`,
68508
- [(0, import_node_crypto40.randomUUID)(), input.pixelId, input.siteId, hostname, input.state]
68928
+ [(0, import_node_crypto41.randomUUID)(), input.pixelId, input.siteId, hostname, input.state]
68509
68929
  );
68510
68930
  if (!result.rowCount)
68511
68931
  throw new AnalyticsRepositoryError(
@@ -68555,7 +68975,7 @@ function sanitizeAnalyticsProperties(value) {
68555
68975
  }
68556
68976
  async function ingestAnalyticsEvents(input) {
68557
68977
  const db = getAnalyticsPool();
68558
- const requestId = `air_${(0, import_node_crypto40.randomUUID)()}`;
68978
+ const requestId = `air_${(0, import_node_crypto41.randomUUID)()}`;
68559
68979
  const hostname = normalizeObservedHostname(input.origin);
68560
68980
  const pixelResult = await db.query(
68561
68981
  `SELECT p.id, p.site_id, p.status,
@@ -68576,7 +68996,7 @@ async function ingestAnalyticsEvents(input) {
68576
68996
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
68577
68997
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
68578
68998
  [
68579
- (0, import_node_crypto40.randomUUID)(),
68999
+ (0, import_node_crypto41.randomUUID)(),
68580
69000
  requestId,
68581
69001
  pixel?.site_id ?? null,
68582
69002
  pixel?.id ?? null,
@@ -68601,7 +69021,7 @@ async function ingestAnalyticsEvents(input) {
68601
69021
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
68602
69022
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
68603
69023
  [
68604
- (0, import_node_crypto40.randomUUID)(),
69024
+ (0, import_node_crypto41.randomUUID)(),
68605
69025
  requestId,
68606
69026
  pixel.site_id,
68607
69027
  pixel.id,
@@ -68626,7 +69046,7 @@ async function ingestAnalyticsEvents(input) {
68626
69046
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, rejected_count, reason_codes)
68627
69047
  VALUES ($1, $2, $3, $4, $5, $6::jsonb)`,
68628
69048
  [
68629
- (0, import_node_crypto40.randomUUID)(),
69049
+ (0, import_node_crypto41.randomUUID)(),
68630
69050
  requestId,
68631
69051
  pixel.site_id,
68632
69052
  pixel.id,
@@ -68650,7 +69070,7 @@ async function ingestAnalyticsEvents(input) {
68650
69070
  ON CONFLICT(pixel_id, hostname) DO UPDATE
68651
69071
  SET last_seen_at = now(), updated_at = now()
68652
69072
  RETURNING state`,
68653
- [(0, import_node_crypto40.randomUUID)(), pixel.id, hostname]
69073
+ [(0, import_node_crypto41.randomUUID)(), pixel.id, hostname]
68654
69074
  );
68655
69075
  if (domainResult.rows[0]?.state !== "approved") {
68656
69076
  await db.query(
@@ -68664,7 +69084,7 @@ async function ingestAnalyticsEvents(input) {
68664
69084
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
68665
69085
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
68666
69086
  [
68667
- (0, import_node_crypto40.randomUUID)(),
69087
+ (0, import_node_crypto41.randomUUID)(),
68668
69088
  requestId,
68669
69089
  pixel.site_id,
68670
69090
  pixel.id,
@@ -68714,7 +69134,7 @@ async function ingestAnalyticsEvents(input) {
68714
69134
  $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33
68715
69135
  ) ON CONFLICT(site_id, event_id) DO NOTHING`,
68716
69136
  [
68717
- (0, import_node_crypto40.randomUUID)(),
69137
+ (0, import_node_crypto41.randomUUID)(),
68718
69138
  event2.eventId,
68719
69139
  pixel.site_id,
68720
69140
  pixel.id,
@@ -68772,7 +69192,7 @@ async function ingestAnalyticsEvents(input) {
68772
69192
  id, request_id, site_id, pixel_id, hostname, accepted_count, rejected_count, reason_codes
68773
69193
  ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb)`,
68774
69194
  [
68775
- (0, import_node_crypto40.randomUUID)(),
69195
+ (0, import_node_crypto41.randomUUID)(),
68776
69196
  requestId,
68777
69197
  pixel.site_id,
68778
69198
  pixel.id,
@@ -68804,7 +69224,7 @@ function normalizeGeographyCode(value, max) {
68804
69224
  return normalized && /^[A-Z0-9-]+$/.test(normalized) ? normalized.slice(0, max) : null;
68805
69225
  }
68806
69226
  function pageFingerprint(scope, siteId, filters) {
68807
- return (0, import_node_crypto40.createHash)("sha256").update(JSON.stringify({ scope, siteId, filters })).digest("base64url").slice(0, 18);
69227
+ return (0, import_node_crypto41.createHash)("sha256").update(JSON.stringify({ scope, siteId, filters })).digest("base64url").slice(0, 18);
68808
69228
  }
68809
69229
  function decodePageOffset(cursor, fingerprint) {
68810
69230
  if (!cursor) return 0;
@@ -68854,7 +69274,7 @@ async function createAnalyticsConversion(input) {
68854
69274
  404
68855
69275
  );
68856
69276
  }
68857
- const id = (0, import_node_crypto40.randomUUID)();
69277
+ const id = (0, import_node_crypto41.randomUUID)();
68858
69278
  const resolvedPerson = input.sessionId ? await db.query(
68859
69279
  `SELECT e.person_id FROM analytics_identity_nodes n JOIN analytics_identity_edges e ON e.identity_node_id = n.id
68860
69280
  WHERE n.site_id = $1 AND n.kind = 'session_id' AND n.value_hmac = $2 ORDER BY e.confidence DESC LIMIT 1`,
@@ -69603,7 +70023,7 @@ async function createAnalyticsCampaignLink(input) {
69603
70023
  404
69604
70024
  );
69605
70025
  }
69606
- const shortCode = (input.shortCode?.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-") || (0, import_node_crypto40.randomBytes)(5).toString("base64url").toLowerCase()).slice(0, 48);
70026
+ const shortCode = (input.shortCode?.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-") || (0, import_node_crypto41.randomBytes)(5).toString("base64url").toLowerCase()).slice(0, 48);
69607
70027
  try {
69608
70028
  const result = await db.query(
69609
70029
  `INSERT INTO analytics_campaign_links(
@@ -69612,7 +70032,7 @@ async function createAnalyticsCampaignLink(input) {
69612
70032
  ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)
69613
70033
  RETURNING *, 0::int AS click_count`,
69614
70034
  [
69615
- (0, import_node_crypto40.randomUUID)(),
70035
+ (0, import_node_crypto41.randomUUID)(),
69616
70036
  input.siteId,
69617
70037
  input.pixelId ?? null,
69618
70038
  input.name.trim(),
@@ -69683,7 +70103,7 @@ async function resolveAnalyticsCampaignLink(shortCode, referrer) {
69683
70103
  if (!row) return null;
69684
70104
  await db.query(
69685
70105
  `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)]
70106
+ [(0, import_node_crypto41.randomUUID)(), row.id, normalizeAnalyticsUrl(referrer || void 0)]
69687
70107
  );
69688
70108
  return buildTaggedCampaignUrl(row);
69689
70109
  }
@@ -69701,14 +70121,14 @@ async function createAnalyticsForm(input) {
69701
70121
  404
69702
70122
  );
69703
70123
  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")}`;
70124
+ const slug4 = `${baseSlug}-${(0, import_node_crypto41.randomBytes)(3).toString("hex")}`;
70125
+ const publicId = `form_${(0, import_node_crypto41.randomBytes)(18).toString("base64url")}`;
69706
70126
  const result = await db.query(
69707
70127
  `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
70128
  VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8::jsonb,$9,$10,$11,$12,$13,$14)
69709
70129
  RETURNING *, 0::int AS submission_count`,
69710
70130
  [
69711
- (0, import_node_crypto40.randomUUID)(),
70131
+ (0, import_node_crypto41.randomUUID)(),
69712
70132
  publicId,
69713
70133
  input.siteId,
69714
70134
  input.pixelId,
@@ -69753,7 +70173,7 @@ async function getPublicAnalyticsForm(publicId) {
69753
70173
  return result.rows[0] ?? null;
69754
70174
  }
69755
70175
  async function recordAnalyticsFormSubmission(input) {
69756
- const id = (0, import_node_crypto40.randomUUID)();
70176
+ const id = (0, import_node_crypto41.randomUUID)();
69757
70177
  await getAnalyticsPool().query(
69758
70178
  `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
70179
  VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12::jsonb)`,
@@ -69784,7 +70204,7 @@ function sanitizeClickIds(value) {
69784
70204
  return output;
69785
70205
  }
69786
70206
  function identityHmac(kind, value) {
69787
- return (0, import_node_crypto40.createHmac)("sha256", getSessionSecret()).update(`${kind}:${value.trim().toLowerCase()}`).digest("hex");
70207
+ return (0, import_node_crypto41.createHmac)("sha256", getSessionSecret()).update(`${kind}:${value.trim().toLowerCase()}`).digest("hex");
69788
70208
  }
69789
70209
  async function linkAnalyticsFormIdentity(input) {
69790
70210
  const client2 = await getAnalyticsPool().connect();
@@ -69793,7 +70213,7 @@ async function linkAnalyticsFormIdentity(input) {
69793
70213
  const person = await client2.query(
69794
70214
  `INSERT INTO analytics_people(id, site_id, crm_person_ref) VALUES ($1,$2,$3)
69795
70215
  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]
70216
+ [(0, import_node_crypto41.randomUUID)(), input.siteId, input.crmPersonRef]
69797
70217
  );
69798
70218
  const personId = person.rows[0].id;
69799
70219
  const signals = [];
@@ -69840,7 +70260,7 @@ async function linkAnalyticsFormIdentity(input) {
69840
70260
  `INSERT INTO analytics_identity_nodes(id, site_id, kind, value_hmac) VALUES ($1,$2,$3,$4)
69841
70261
  ON CONFLICT(site_id, kind, value_hmac) DO UPDATE SET last_seen_at = now() RETURNING id`,
69842
70262
  [
69843
- (0, import_node_crypto40.randomUUID)(),
70263
+ (0, import_node_crypto41.randomUUID)(),
69844
70264
  input.siteId,
69845
70265
  signal.kind,
69846
70266
  identityHmac(signal.kind, signal.value)
@@ -69851,7 +70271,7 @@ async function linkAnalyticsFormIdentity(input) {
69851
70271
  VALUES ($1,$2,$3,$4,$5,$6)
69852
70272
  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
70273
  [
69854
- (0, import_node_crypto40.randomUUID)(),
70274
+ (0, import_node_crypto41.randomUUID)(),
69855
70275
  input.siteId,
69856
70276
  personId,
69857
70277
  node.rows[0].id,
@@ -69949,7 +70369,7 @@ async function createAnalyticsCrmImport(input) {
69949
70369
  const db = getAnalyticsPool();
69950
70370
  await requireEditor(db, input.siteId, input.userId);
69951
70371
  const client2 = await db.connect();
69952
- const id = (0, import_node_crypto40.randomUUID)();
70372
+ const id = (0, import_node_crypto41.randomUUID)();
69953
70373
  try {
69954
70374
  await client2.query("BEGIN");
69955
70375
  await client2.query(
@@ -69969,7 +70389,7 @@ async function createAnalyticsCrmImport(input) {
69969
70389
  await client2.query(
69970
70390
  `INSERT INTO analytics_crm_import_rows(id, import_id, crm_person_ref, payload_ciphertext)
69971
70391
  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]
70392
+ [(0, import_node_crypto41.randomUUID)(), id, row.crmPersonRef, row.payloadCiphertext]
69973
70393
  );
69974
70394
  }
69975
70395
  await client2.query("COMMIT");
@@ -70008,7 +70428,7 @@ async function createAnalyticsActivationDestination(input) {
70008
70428
  `INSERT INTO analytics_activation_destinations(id, site_id, platform, name, connection_ref, external_dataset_id, event_mapping, created_by_user_id)
70009
70429
  VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8) RETURNING *`,
70010
70430
  [
70011
- (0, import_node_crypto40.randomUUID)(),
70431
+ (0, import_node_crypto41.randomUUID)(),
70012
70432
  input.siteId,
70013
70433
  input.platform,
70014
70434
  input.name.trim(),
@@ -70060,7 +70480,7 @@ async function queueAnalyticsActivation(input) {
70060
70480
  `INSERT INTO analytics_activation_jobs(id, destination_id, conversion_id, person_id, payload_ciphertext)
70061
70481
  VALUES ($1,$2,$3,$4,$5) ON CONFLICT(destination_id, conversion_id) DO NOTHING`,
70062
70482
  [
70063
- (0, import_node_crypto40.randomUUID)(),
70483
+ (0, import_node_crypto41.randomUUID)(),
70064
70484
  destination.id,
70065
70485
  input.conversionId,
70066
70486
  input.personId ?? null,
@@ -70072,7 +70492,7 @@ async function queueAnalyticsActivation(input) {
70072
70492
  return queued;
70073
70493
  }
70074
70494
  async function queueAnalyticsFormDelivery(input) {
70075
- const id = (0, import_node_crypto40.randomUUID)();
70495
+ const id = (0, import_node_crypto41.randomUUID)();
70076
70496
  await getAnalyticsPool().query(
70077
70497
  `INSERT INTO analytics_form_delivery_jobs(id, submission_id, owner_user_id, payload_ciphertext, last_error_code)
70078
70498
  VALUES ($1,$2,$3,$4,$5)`,
@@ -70199,7 +70619,7 @@ async function analyticsHealth(siteId, userId) {
70199
70619
  }
70200
70620
  async function refreshAnalyticsDailyRollups(input) {
70201
70621
  const db = getAnalyticsPool();
70202
- const runId = (0, import_node_crypto40.randomUUID)();
70622
+ const runId = (0, import_node_crypto41.randomUUID)();
70203
70623
  await db.query(
70204
70624
  `INSERT INTO analytics_rollup_runs(id, window_start, window_end, status) VALUES ($1, $2, $3, 'running')`,
70205
70625
  [runId, input.start, input.end]
@@ -70396,7 +70816,7 @@ async function createAnalyticsExport(input) {
70396
70816
  `Generated ${(/* @__PURE__ */ new Date()).toISOString()} from the governed ${input.report} report contract.`
70397
70817
  ].join("\n");
70398
70818
  }
70399
- const id = (0, import_node_crypto40.randomUUID)();
70819
+ const id = (0, import_node_crypto41.randomUUID)();
70400
70820
  const inserted = await getAnalyticsPool().query(
70401
70821
  `INSERT INTO analytics_exports(id, site_id, requested_by_user_id, idempotency_key, report, format, filters, content)
70402
70822
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8)
@@ -70429,11 +70849,11 @@ async function createAnalyticsExport(input) {
70429
70849
  }
70430
70850
  return describeArtifact(replay.rows[0]);
70431
70851
  }
70432
- var import_node_crypto40, import_pg, AnalyticsRepositoryError, pool2, MAX_ENGAGED_MS, ENGAGED_SESSION_MS, blockedPropertyName, inferredFamilySql, ANALYTICS_CONTENT_SORTS, clickIdKeys;
70852
+ var import_node_crypto41, import_pg, AnalyticsRepositoryError, pool2, MAX_ENGAGED_MS, ENGAGED_SESSION_MS, blockedPropertyName, inferredFamilySql, ANALYTICS_CONTENT_SORTS, clickIdKeys;
70433
70853
  var init_analytics_repository = __esm({
70434
70854
  "src/api/analytics-repository.ts"() {
70435
70855
  "use strict";
70436
- import_node_crypto40 = require("crypto");
70856
+ import_node_crypto41 = require("crypto");
70437
70857
  import_pg = require("pg");
70438
70858
  init_session();
70439
70859
  init_analytics_attribution();
@@ -70672,8 +71092,8 @@ function dashboardCallbackUrl() {
70672
71092
  }
70673
71093
  async function createThorbitConnectUrl(userId) {
70674
71094
  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");
71095
+ const state = (0, import_node_crypto42.randomBytes)(32).toString("base64url");
71096
+ const stateHash = (0, import_node_crypto42.createHash)("sha256").update(state).digest("hex");
70677
71097
  await getAnalyticsPool().query(
70678
71098
  `INSERT INTO analytics_thorbit_connect_states(state_hash,user_id,expires_at)
70679
71099
  VALUES ($1,$2,now()+interval '10 minutes')
@@ -70688,9 +71108,9 @@ async function createThorbitConnectUrl(userId) {
70688
71108
  function verifyThorbitAssertion(token6) {
70689
71109
  const [encoded, signature] = token6.split(".");
70690
71110
  if (!encoded || !signature || !bridgeConfigured()) throw new Error("Invalid Thorbit assertion");
70691
- const expected = (0, import_node_crypto41.createHmac)("sha256", bridgeSecret()).update(encoded).digest();
71111
+ const expected = (0, import_node_crypto42.createHmac)("sha256", bridgeSecret()).update(encoded).digest();
70692
71112
  const actual = Buffer.from(signature, "base64url");
70693
- if (actual.length !== expected.length || !(0, import_node_crypto41.timingSafeEqual)(actual, expected)) {
71113
+ if (actual.length !== expected.length || !(0, import_node_crypto42.timingSafeEqual)(actual, expected)) {
70694
71114
  throw new Error("Invalid Thorbit assertion signature");
70695
71115
  }
70696
71116
  const parsed = AssertionSchema.safeParse(
@@ -70702,7 +71122,7 @@ function verifyThorbitAssertion(token6) {
70702
71122
  return parsed.data.entitlement;
70703
71123
  }
70704
71124
  async function consumeThorbitConnectCallback(input) {
70705
- const stateHash = (0, import_node_crypto41.createHash)("sha256").update(input.state).digest("hex");
71125
+ const stateHash = (0, import_node_crypto42.createHash)("sha256").update(input.state).digest("hex");
70706
71126
  const client2 = await getAnalyticsPool().connect();
70707
71127
  try {
70708
71128
  await client2.query("BEGIN");
@@ -70742,11 +71162,11 @@ async function disconnectAnalyticsEntitlement(userId) {
70742
71162
  [userId]
70743
71163
  );
70744
71164
  }
70745
- var import_node_crypto41, import_zod53, THORBIT_PRODUCT_URL, REFRESH_INTERVAL_MS, ELIGIBLE_GRACE_MS, TRIAL_LENGTH_MS, ThorbitEntitlementSchema, AssertionSchema;
71165
+ var import_node_crypto42, import_zod53, THORBIT_PRODUCT_URL, REFRESH_INTERVAL_MS, ELIGIBLE_GRACE_MS, TRIAL_LENGTH_MS, ThorbitEntitlementSchema, AssertionSchema;
70746
71166
  var init_analytics_entitlement = __esm({
70747
71167
  "src/api/analytics-entitlement.ts"() {
70748
71168
  "use strict";
70749
- import_node_crypto41 = require("crypto");
71169
+ import_node_crypto42 = require("crypto");
70750
71170
  import_zod53 = require("zod");
70751
71171
  init_analytics_repository();
70752
71172
  THORBIT_PRODUCT_URL = "https://thorbit.ai";
@@ -70886,14 +71306,14 @@ function renderPublicForm(form, placementUrl) {
70886
71306
  const brand = form.brand;
70887
71307
  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
71308
  }
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;
71309
+ 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
71310
  var init_analytics_routes = __esm({
70891
71311
  "src/api/analytics-routes.ts"() {
70892
71312
  "use strict";
70893
71313
  import_hono32 = require("hono");
70894
71314
  import_factory6 = require("hono/factory");
70895
71315
  import_zod54 = require("zod");
70896
- import_node_crypto42 = require("crypto");
71316
+ import_node_crypto43 = require("crypto");
70897
71317
  import_papaparse4 = __toESM(require("papaparse"), 1);
70898
71318
  init_api_auth();
70899
71319
  init_db();
@@ -71224,7 +71644,7 @@ var init_analytics_routes = __esm({
71224
71644
  const lastName = typeof data.last_name === "string" ? data.last_name.trim() : "";
71225
71645
  const fullName = [firstName, lastName].filter(Boolean).join(" ") || (typeof data.name === "string" ? data.name.trim() : "") || email || "Website lead";
71226
71646
  const identitySeed = email || `${fullName}:${Date.now()}`;
71227
- const suffix2 = (0, import_node_crypto42.createHash)("sha256").update(identitySeed).digest("hex").slice(0, 12);
71647
+ const suffix2 = (0, import_node_crypto43.createHash)("sha256").update(identitySeed).digest("hex").slice(0, 12);
71228
71648
  const path6 = `Leads/person-${suffix2}`;
71229
71649
  const { key: memoryKey, error: memoryKeyError } = await getOrCreateUserMemoryKey(user);
71230
71650
  const existing = memoryKey ? await memoryCall("getTool", { vault: "People", path: path6 }, memoryKey) : { ok: false, error: memoryKeyError || "memory_credential_unavailable" };
@@ -71323,9 +71743,9 @@ Submitted the published form.
71323
71743
  occurredAt: (/* @__PURE__ */ new Date()).toISOString()
71324
71744
  });
71325
71745
  const match = {
71326
- ...email ? { emailSha256: (0, import_node_crypto42.createHash)("sha256").update(email).digest("hex") } : {},
71746
+ ...email ? { emailSha256: (0, import_node_crypto43.createHash)("sha256").update(email).digest("hex") } : {},
71327
71747
  ...phone ? {
71328
- phoneSha256: (0, import_node_crypto42.createHash)("sha256").update(phone.replace(/\D/g, "")).digest("hex")
71748
+ phoneSha256: (0, import_node_crypto43.createHash)("sha256").update(phone.replace(/\D/g, "")).digest("hex")
71329
71749
  } : {}
71330
71750
  };
71331
71751
  const activationQueued = await queueAnalyticsActivation({
@@ -71753,7 +72173,7 @@ Submitted the published form.
71753
72173
  rejectedCount += 1;
71754
72174
  continue;
71755
72175
  }
71756
- const digest2 = (0, import_node_crypto42.createHash)("sha256").update(
72176
+ const digest2 = (0, import_node_crypto43.createHash)("sha256").update(
71757
72177
  `${parsed.data.sourceSystem}:${externalId || email || phone || `${fullName}:${index}`}`
71758
72178
  ).digest("hex").slice(0, 16);
71759
72179
  const path6 = `Leads/person-${digest2}`;
@@ -72211,13 +72631,13 @@ var init_analytics_delivery = __esm({
72211
72631
 
72212
72632
  // src/api/scheduled-artifact-owner.ts
72213
72633
  function scheduledArtifactOwnerIdForApiKey(apiKey) {
72214
- return (0, import_node_crypto43.createHash)("sha256").update(apiKey).digest("hex").slice(0, 24);
72634
+ return (0, import_node_crypto44.createHash)("sha256").update(apiKey).digest("hex").slice(0, 24);
72215
72635
  }
72216
- var import_node_crypto43;
72636
+ var import_node_crypto44;
72217
72637
  var init_scheduled_artifact_owner = __esm({
72218
72638
  "src/api/scheduled-artifact-owner.ts"() {
72219
72639
  "use strict";
72220
- import_node_crypto43 = require("crypto");
72640
+ import_node_crypto44 = require("crypto");
72221
72641
  }
72222
72642
  });
72223
72643
 
@@ -72274,7 +72694,7 @@ async function ensureScheduledRunViewLinksSchema() {
72274
72694
  schemaReady2 = true;
72275
72695
  }
72276
72696
  function tokenHash2(token6) {
72277
- return (0, import_node_crypto44.createHash)("sha256").update(token6).digest("hex");
72697
+ return (0, import_node_crypto45.createHash)("sha256").update(token6).digest("hex");
72278
72698
  }
72279
72699
  function mapRow(row) {
72280
72700
  return {
@@ -72294,9 +72714,9 @@ function mapRow(row) {
72294
72714
  async function createScheduledRunViewLink(input) {
72295
72715
  await ensureScheduledRunViewLinksSchema();
72296
72716
  const now = input.now ?? /* @__PURE__ */ new Date();
72297
- const token6 = (0, import_node_crypto44.randomBytes)(32).toString("base64url");
72717
+ const token6 = (0, import_node_crypto45.randomBytes)(32).toString("base64url");
72298
72718
  const record = {
72299
- shareId: (0, import_node_crypto44.randomUUID)(),
72719
+ shareId: (0, import_node_crypto45.randomUUID)(),
72300
72720
  ownerId: input.ownerId,
72301
72721
  runId: input.runId,
72302
72722
  artifactId: input.artifactId,
@@ -72359,11 +72779,11 @@ async function revokeScheduledRunViewLink(ownerId, runId, shareId, now = /* @__P
72359
72779
  });
72360
72780
  return result.rowsAffected > 0;
72361
72781
  }
72362
- var import_node_crypto44, schemaReady2;
72782
+ var import_node_crypto45, schemaReady2;
72363
72783
  var init_scheduled_run_view_links = __esm({
72364
72784
  "src/api/scheduled-run-view-links.ts"() {
72365
72785
  "use strict";
72366
- import_node_crypto44 = require("crypto");
72786
+ import_node_crypto45 = require("crypto");
72367
72787
  init_db();
72368
72788
  schemaReady2 = false;
72369
72789
  }
@@ -72429,15 +72849,15 @@ ${section(flags.finalCta && Boolean(finalCta), `<section class="section cta" id=
72429
72849
  html,
72430
72850
  filename: `${input.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "personal-authority"}.html`,
72431
72851
  bytes,
72432
- sha256: (0, import_node_crypto45.createHash)("sha256").update(html).digest("hex"),
72852
+ sha256: (0, import_node_crypto46.createHash)("sha256").update(html).digest("hex"),
72433
72853
  generatedAt: generatedAtIso
72434
72854
  };
72435
72855
  }
72436
- var import_node_crypto45;
72856
+ var import_node_crypto46;
72437
72857
  var init_render2 = __esm({
72438
72858
  "src/personal-authority/render.ts"() {
72439
72859
  "use strict";
72440
- import_node_crypto45 = require("crypto");
72860
+ import_node_crypto46 = require("crypto");
72441
72861
  }
72442
72862
  });
72443
72863
 
@@ -72540,15 +72960,15 @@ ${section2(flags.finalCta && Boolean(finalCta), `<section class="section cta" id
72540
72960
  html,
72541
72961
  filename: `${input.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "personal-authority"}.html`,
72542
72962
  bytes,
72543
- sha256: (0, import_node_crypto46.createHash)("sha256").update(html).digest("hex"),
72963
+ sha256: (0, import_node_crypto47.createHash)("sha256").update(html).digest("hex"),
72544
72964
  generatedAt: generatedAtIso
72545
72965
  };
72546
72966
  }
72547
- var import_node_crypto46, FONT_STACKS;
72967
+ var import_node_crypto47, FONT_STACKS;
72548
72968
  var init_render_v2 = __esm({
72549
72969
  "src/personal-authority/render-v2.ts"() {
72550
72970
  "use strict";
72551
- import_node_crypto46 = require("crypto");
72971
+ import_node_crypto47 = require("crypto");
72552
72972
  init_contracts();
72553
72973
  FONT_STACKS = Object.freeze({
72554
72974
  "editorial-serif": "Iowan Old Style, Baskerville, Times New Roman, serif",
@@ -72664,15 +73084,15 @@ body{overflow-x:hidden}.lead-story__body,.lead-support__item,.story-card__body{m
72664
73084
  @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
73085
  </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
73086
  const bytes = Buffer.byteLength(html);
72667
- const sha2563 = (0, import_node_crypto47.createHash)("sha256").update(html).digest("hex");
73087
+ const sha2563 = (0, import_node_crypto48.createHash)("sha256").update(html).digest("hex");
72668
73088
  const filename = `${brand.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "newsroom"}-news-site.html`;
72669
73089
  return { html, filename, bytes, sha256: sha2563, generatedAt: generatedAtIso };
72670
73090
  }
72671
- var import_node_crypto47;
73091
+ var import_node_crypto48;
72672
73092
  var init_render3 = __esm({
72673
73093
  "src/newsroom-publisher/render.ts"() {
72674
73094
  "use strict";
72675
- import_node_crypto47 = require("crypto");
73095
+ import_node_crypto48 = require("crypto");
72676
73096
  }
72677
73097
  });
72678
73098
 
@@ -72764,7 +73184,7 @@ function renderBlogArticleV1(input, config, generatedAt) {
72764
73184
  :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
73185
  </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
73186
  const bytes = Buffer.byteLength(html);
72767
- const sha2563 = (0, import_node_crypto48.createHash)("sha256").update(html).digest("hex");
73187
+ const sha2563 = (0, import_node_crypto49.createHash)("sha256").update(html).digest("hex");
72768
73188
  return {
72769
73189
  html,
72770
73190
  filename: "blog-article.html",
@@ -72774,11 +73194,11 @@ function renderBlogArticleV1(input, config, generatedAt) {
72774
73194
  generatedAt: generatedAtIso
72775
73195
  };
72776
73196
  }
72777
- var import_node_crypto48;
73197
+ var import_node_crypto49;
72778
73198
  var init_render4 = __esm({
72779
73199
  "src/blog-article/render.ts"() {
72780
73200
  "use strict";
72781
- import_node_crypto48 = require("crypto");
73201
+ import_node_crypto49 = require("crypto");
72782
73202
  }
72783
73203
  });
72784
73204
 
@@ -73109,7 +73529,7 @@ function policy5() {
73109
73529
  };
73110
73530
  }
73111
73531
  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)}`;
73532
+ 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
73533
  }
73114
73534
  async function createScheduledRunArtifact(args) {
73115
73535
  if (args.rendered.bytes > SCHEDULED_RUN_ARTIFACT_MAX_BYTES) {
@@ -73150,12 +73570,12 @@ async function readScheduledRunArtifact(args) {
73150
73570
  if (!window2 || window2.nextOffset !== null || window2.totalBytes > SCHEDULED_RUN_ARTIFACT_MAX_BYTES) return null;
73151
73571
  return window2.text;
73152
73572
  }
73153
- var import_node_crypto49, SCHEDULED_RUN_ARTIFACT_PREFIX, SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS, SCHEDULED_RUN_ARTIFACT_MAX_BYTES;
73573
+ var import_node_crypto50, SCHEDULED_RUN_ARTIFACT_PREFIX, SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS, SCHEDULED_RUN_ARTIFACT_MAX_BYTES;
73154
73574
  var init_scheduled_run_artifact_store = __esm({
73155
73575
  "src/scheduled-artifacts/scheduled-run-artifact-store.ts"() {
73156
73576
  "use strict";
73157
73577
  init_private_artifacts();
73158
- import_node_crypto49 = require("crypto");
73578
+ import_node_crypto50 = require("crypto");
73159
73579
  SCHEDULED_RUN_ARTIFACT_PREFIX = "scheduled-run-artifacts/";
73160
73580
  SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS = 15 * 60 * 1e3;
73161
73581
  SCHEDULED_RUN_ARTIFACT_MAX_BYTES = 2e6;
@@ -73373,7 +73793,7 @@ async function reconcileDiscoveredNangoConnections(identity, discovered) {
73373
73793
  updated_at = excluded.updated_at
73374
73794
  `,
73375
73795
  args: [
73376
- (0, import_node_crypto50.randomUUID)(),
73796
+ (0, import_node_crypto51.randomUUID)(),
73377
73797
  userId,
73378
73798
  connection.providerConfigKey,
73379
73799
  connection.provider,
@@ -73444,7 +73864,7 @@ async function recordServiceConnectionHealth(args) {
73444
73864
  });
73445
73865
  await getDb().execute({
73446
73866
  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]
73867
+ args: [(0, import_node_crypto51.randomUUID)(), args.connectionId, args.operationalStatus, args.failureCode ?? null, args.retryable == null ? null : args.retryable ? 1 : 0, args.evidenceSource]
73448
73868
  });
73449
73869
  }
73450
73870
  async function setServiceConnectionActions(identity, connectionId, enabled) {
@@ -73484,7 +73904,7 @@ async function claimServiceConnectionAction(args) {
73484
73904
  if (!connection) throw new Error("service_connection_not_found");
73485
73905
  const inserted = await getDb().execute({
73486
73906
  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]
73907
+ args: [(0, import_node_crypto51.randomUUID)(), connection.id, connection.userId, args.tool, args.requestId, args.requestDigest]
73488
73908
  });
73489
73909
  if (Number(inserted.rowsAffected ?? 0) === 1) return { claimed: true };
73490
73910
  const existing = await getDb().execute({
@@ -73509,11 +73929,11 @@ async function claimServiceConnectionAction(args) {
73509
73929
  ...result !== void 0 ? { result } : {}
73510
73930
  };
73511
73931
  }
73512
- var import_node_crypto50, schemaReady3, schemaDb4;
73932
+ var import_node_crypto51, schemaReady3, schemaDb4;
73513
73933
  var init_service_connections = __esm({
73514
73934
  "src/api/service-connections.ts"() {
73515
73935
  "use strict";
73516
- import_node_crypto50 = require("crypto");
73936
+ import_node_crypto51 = require("crypto");
73517
73937
  init_db();
73518
73938
  schemaReady3 = null;
73519
73939
  schemaDb4 = null;
@@ -73527,8 +73947,8 @@ function signingSecret() {
73527
73947
  return secret2;
73528
73948
  }
73529
73949
  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()}
73950
+ const bodyHash = (0, import_node_crypto52.createHash)("sha256").update(args.body).digest("hex");
73951
+ return (0, import_node_crypto52.createHmac)("sha256", args.secret).update(`${args.method.toUpperCase()}
73532
73952
  ${args.path}
73533
73953
  ${args.timestamp}
73534
73954
  ${args.nonce}
@@ -73575,17 +73995,17 @@ async function verifySchedulerIntegrationRequest(request, rawBody) {
73575
73995
  });
73576
73996
  const suppliedBytes = Buffer.from(signature, "hex");
73577
73997
  const expectedBytes = Buffer.from(expected, "hex");
73578
- if (suppliedBytes.length !== expectedBytes.length || !(0, import_node_crypto51.timingSafeEqual)(suppliedBytes, expectedBytes)) {
73998
+ if (suppliedBytes.length !== expectedBytes.length || !(0, import_node_crypto52.timingSafeEqual)(suppliedBytes, expectedBytes)) {
73579
73999
  throw new SchedulerIntegrationAuthError("invalid_signature");
73580
74000
  }
73581
74001
  await claimNonce(nonce, timestampMs);
73582
74002
  return { requestId };
73583
74003
  }
73584
- var import_node_crypto51, MAX_CLOCK_SKEW_MS, SchedulerIntegrationAuthError;
74004
+ var import_node_crypto52, MAX_CLOCK_SKEW_MS, SchedulerIntegrationAuthError;
73585
74005
  var init_scheduler_integration_auth = __esm({
73586
74006
  "src/api/scheduler-integration-auth.ts"() {
73587
74007
  "use strict";
73588
- import_node_crypto51 = require("crypto");
74008
+ import_node_crypto52 = require("crypto");
73589
74009
  init_db();
73590
74010
  init_service_connections();
73591
74011
  MAX_CLOCK_SKEW_MS = 5 * 60 * 1e3;
@@ -73893,7 +74313,7 @@ async function fetchMedia(rawUrl, expectedType) {
73893
74313
  finalUrl: checked.parsed.href,
73894
74314
  width: dimensions?.width ?? null,
73895
74315
  height: dimensions?.height ?? null,
73896
- sha256: (0, import_node_crypto52.createHash)("sha256").update(bytes).digest("hex")
74316
+ sha256: (0, import_node_crypto53.createHash)("sha256").update(bytes).digest("hex")
73897
74317
  };
73898
74318
  }
73899
74319
  throw new Error("media_redirect_rejected");
@@ -74007,7 +74427,7 @@ async function packagePageMedia(args) {
74007
74427
  files.push({ path: "summary.json", content: Buffer.from(JSON.stringify(summary, null, 2)) });
74008
74428
  files.push({ path: "media.jsonl", content: Buffer.from(cleanAssets.map((asset) => JSON.stringify(asset)).join("\n") + "\n") });
74009
74429
  const archive = await zipBuffer2(files);
74010
- const id = (0, import_node_crypto52.randomBytes)(6).toString("hex");
74430
+ const id = (0, import_node_crypto53.randomBytes)(6).toString("hex");
74011
74431
  const pointer = await createPrivateArtifact({
74012
74432
  policy: policy6(),
74013
74433
  ownerId: args.ownerId,
@@ -74020,11 +74440,11 @@ async function packagePageMedia(args) {
74020
74440
  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
74441
  return { media: args.media, artifact: { ...pointer, localPath } };
74022
74442
  }
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;
74443
+ 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
74444
  var init_page_media_artifacts = __esm({
74025
74445
  "src/api/page-media-artifacts.ts"() {
74026
74446
  "use strict";
74027
- import_node_crypto52 = require("crypto");
74447
+ import_node_crypto53 = require("crypto");
74028
74448
  import_node_os14 = require("os");
74029
74449
  import_node_path21 = require("path");
74030
74450
  import_p_limit7 = __toESM(require("p-limit"), 1);
@@ -74118,7 +74538,7 @@ var init_site_extract_reconciliation = __esm({
74118
74538
 
74119
74539
  // src/api/page-diff.ts
74120
74540
  function sha256Hex(value) {
74121
- return (0, import_node_crypto53.createHash)("sha256").update(value).digest("hex");
74541
+ return (0, import_node_crypto54.createHash)("sha256").update(value).digest("hex");
74122
74542
  }
74123
74543
  function truncateForStorage(value, maxChars = MAX_SNAPSHOT_CONTENT_CHARS) {
74124
74544
  if (value.length <= maxChars) return { value, truncated: false };
@@ -74175,11 +74595,11 @@ function diffPageContent(oldContent, newContent) {
74175
74595
  totalChangedLineCount
74176
74596
  };
74177
74597
  }
74178
- var import_node_crypto53, import_diff, MAX_SNAPSHOT_CONTENT_CHARS, MAX_DIFF_HUNKS, MAX_DIFF_LINES_PER_RESPONSE;
74598
+ var import_node_crypto54, import_diff, MAX_SNAPSHOT_CONTENT_CHARS, MAX_DIFF_HUNKS, MAX_DIFF_LINES_PER_RESPONSE;
74179
74599
  var init_page_diff = __esm({
74180
74600
  "src/api/page-diff.ts"() {
74181
74601
  "use strict";
74182
- import_node_crypto53 = require("crypto");
74602
+ import_node_crypto54 = require("crypto");
74183
74603
  import_diff = require("diff");
74184
74604
  MAX_SNAPSHOT_CONTENT_CHARS = 25e4;
74185
74605
  MAX_DIFF_HUNKS = 200;
@@ -74253,7 +74673,7 @@ var init_scrape_vault_sink = __esm({
74253
74673
 
74254
74674
  // src/api/scrape-image-sink.ts
74255
74675
  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")}`;
74676
+ 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
74677
  }
74258
74678
  async function persistScrapeImagesToMemory(user, inputs, vault) {
74259
74679
  const selected = inputs.slice(0, MAX_IMAGES_PER_SCRAPE);
@@ -74295,11 +74715,11 @@ async function persistScrapeImagesToMemory(user, inputs, vault) {
74295
74715
  assets
74296
74716
  };
74297
74717
  }
74298
- var import_node_crypto54, MAX_IMAGES_PER_SCRAPE;
74718
+ var import_node_crypto55, MAX_IMAGES_PER_SCRAPE;
74299
74719
  var init_scrape_image_sink = __esm({
74300
74720
  "src/api/scrape-image-sink.ts"() {
74301
74721
  "use strict";
74302
- import_node_crypto54 = require("crypto");
74722
+ import_node_crypto55 = require("crypto");
74303
74723
  init_memory();
74304
74724
  MAX_IMAGES_PER_SCRAPE = 25;
74305
74725
  }
@@ -74752,7 +75172,7 @@ function canonicalJson(value) {
74752
75172
  return JSON.stringify(value);
74753
75173
  }
74754
75174
  function sha2562(value) {
74755
- return (0, import_node_crypto55.createHash)("sha256").update(value).digest("hex");
75175
+ return (0, import_node_crypto56.createHash)("sha256").update(value).digest("hex");
74756
75176
  }
74757
75177
  function sensitiveKey(key) {
74758
75178
  const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
@@ -74967,11 +75387,11 @@ async function importServiceConnectionToMemory(identity, input, dependencies) {
74967
75387
  ...!searchReady ? { warning: "The snapshot was stored, but no search chunks were indexed yet." } : {}
74968
75388
  };
74969
75389
  }
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;
75390
+ 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
75391
  var init_connection_memory_import = __esm({
74972
75392
  "src/api/connection-memory-import.ts"() {
74973
75393
  "use strict";
74974
- import_node_crypto55 = require("crypto");
75394
+ import_node_crypto56 = require("crypto");
74975
75395
  init_slugify();
74976
75396
  CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES = 64 * 1024;
74977
75397
  CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES = 1e6;
@@ -75016,16 +75436,16 @@ async function cleanupLocal(cutoff) {
75016
75436
  let deleted = 0;
75017
75437
  let entries;
75018
75438
  try {
75019
- entries = await (0, import_promises15.readdir)(dir);
75439
+ entries = await (0, import_promises16.readdir)(dir);
75020
75440
  } catch {
75021
75441
  return { deleted: 0, store: "local" };
75022
75442
  }
75023
75443
  for (const name of entries) {
75024
75444
  const path6 = (0, import_node_path23.join)(dir, name);
75025
75445
  try {
75026
- const s = await (0, import_promises15.stat)(path6);
75446
+ const s = await (0, import_promises16.stat)(path6);
75027
75447
  if (s.isFile() && s.mtimeMs < cutoff) {
75028
- await (0, import_promises15.unlink)(path6);
75448
+ await (0, import_promises16.unlink)(path6);
75029
75449
  deleted++;
75030
75450
  }
75031
75451
  } catch {
@@ -75042,11 +75462,11 @@ async function cleanupExpiredScrapeBlobs(maxAgeMs = SCRAPE_BLOB_TTL_MS) {
75042
75462
  return { deleted: 0, store: "none" };
75043
75463
  }
75044
75464
  }
75045
- var import_promises15, import_node_os15, import_node_path23;
75465
+ var import_promises16, import_node_os15, import_node_path23;
75046
75466
  var init_scrape_blob_cleanup = __esm({
75047
75467
  "src/api/scrape-blob-cleanup.ts"() {
75048
75468
  "use strict";
75049
- import_promises15 = require("fs/promises");
75469
+ import_promises16 = require("fs/promises");
75050
75470
  import_node_os15 = require("os");
75051
75471
  import_node_path23 = require("path");
75052
75472
  init_scrape_vault_sink();
@@ -75189,7 +75609,7 @@ function finitePositive(value, fallback) {
75189
75609
  return Number.isFinite(value) && value > 0 ? value : fallback;
75190
75610
  }
75191
75611
  async function collectConnectedDataExport(args) {
75192
- const exportId = (0, import_node_crypto56.randomUUID)();
75612
+ const exportId = (0, import_node_crypto57.randomUUID)();
75193
75613
  const now = args.now ?? Date.now;
75194
75614
  const startedAt = now();
75195
75615
  const budgetMs = finitePositive(CONNECTED_DATA_EXPORT_BUDGET_MS, 24e4);
@@ -75303,11 +75723,11 @@ ${lines.length ? `${lines.join("\n")}
75303
75723
  untrustedContent: true
75304
75724
  };
75305
75725
  }
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;
75726
+ 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
75727
  var init_connected_data_export = __esm({
75308
75728
  "src/api/connected-data-export.ts"() {
75309
75729
  "use strict";
75310
- import_node_crypto56 = require("crypto");
75730
+ import_node_crypto57 = require("crypto");
75311
75731
  CONNECTED_DATA_INLINE_BUDGET_BYTES = Number(
75312
75732
  process.env.MCP_SCRAPER_CONNECTED_DATA_INLINE_BUDGET_BYTES ?? 5e4
75313
75733
  );
@@ -75421,7 +75841,7 @@ async function exportSearchConsoleTableData(args) {
75421
75841
  offset += rows.length;
75422
75842
  if (stoppedForBytes || rows.length < limit || offset >= matchedRows) break;
75423
75843
  }
75424
- const exportId = (0, import_node_crypto57.randomUUID)();
75844
+ const exportId = (0, import_node_crypto58.randomUUID)();
75425
75845
  const artifact = await args.writeArtifact({
75426
75846
  ownerId: args.ownerId,
75427
75847
  exportId,
@@ -75443,11 +75863,11 @@ async function exportSearchConsoleTableData(args) {
75443
75863
  warnings
75444
75864
  };
75445
75865
  }
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;
75866
+ 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
75867
  var init_search_console_table_export = __esm({
75448
75868
  "src/api/search-console-table-export.ts"() {
75449
75869
  "use strict";
75450
- import_node_crypto57 = require("crypto");
75870
+ import_node_crypto58 = require("crypto");
75451
75871
  SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS = 5e4;
75452
75872
  SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE = 2e3;
75453
75873
  SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES = 50 * 1024 * 1024;
@@ -76626,7 +77046,7 @@ async function listNangoToolsDirect(identity, connectionId) {
76626
77046
  });
76627
77047
  const readTools = [...policies.values()].filter((policy7) => policy7.classification === "read").map((policy7) => policy7.name);
76628
77048
  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");
77049
+ const revision = (0, import_node_crypto59.createHash)("sha256").update(JSON.stringify(tools.map((tool) => ({ name: tool.name, inputSchema: tool.inputSchema })))).digest("hex");
76630
77050
  await updateServiceConnectionTools(connection.id, readTools, actionTools, revision);
76631
77051
  const refreshed = await getOwnedServiceConnection(identity, connection.id);
76632
77052
  return { connection: refreshed ?? { ...connection, readTools, actionTools, toolRevision: revision }, tools };
@@ -76653,8 +77073,8 @@ async function callNangoToolDirect(args) {
76653
77073
  identity: args.identity,
76654
77074
  ratePolicyVersion: CONNECTED_USAGE_RATE_POLICY_VERSION
76655
77075
  });
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")}`;
77076
+ const requestId = args.requestId?.trim() || (0, import_node_crypto59.randomUUID)();
77077
+ 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
77078
  const startedAt = /* @__PURE__ */ new Date();
76659
77079
  const started = performance.now();
76660
77080
  let result;
@@ -76679,7 +77099,7 @@ async function callNangoToolDirect(args) {
76679
77099
  toolName: args.tool,
76680
77100
  operationKind: args.operationKind ?? args.classification,
76681
77101
  outcome: providerError ? "error" : "partial",
76682
- requestId: requestId.length <= 200 ? requestId : (0, import_node_crypto58.createHash)("sha256").update(requestId).digest("hex"),
77102
+ requestId: requestId.length <= 200 ? requestId : (0, import_node_crypto59.createHash)("sha256").update(requestId).digest("hex"),
76683
77103
  startedAt: startedAt.toISOString(),
76684
77104
  completedAt: completedAt.toISOString()
76685
77105
  }
@@ -76721,15 +77141,15 @@ async function describeNangoToolDirect(identity, connectionId, toolName) {
76721
77141
  providerContractHash: MAIN_INTEGRATION_CONTRACT_HASH,
76722
77142
  protocolVersion: null,
76723
77143
  schemaSource: "live_tools_list",
76724
- schemaHash: (0, import_node_crypto58.createHash)("sha256").update(JSON.stringify(projected)).digest("hex"),
77144
+ schemaHash: (0, import_node_crypto59.createHash)("sha256").update(JSON.stringify(projected)).digest("hex"),
76725
77145
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
76726
77146
  };
76727
77147
  }
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;
77148
+ 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
77149
  var init_main_nango_transport = __esm({
76730
77150
  "src/api/main-nango-transport.ts"() {
76731
77151
  "use strict";
76732
- import_node_crypto58 = require("crypto");
77152
+ import_node_crypto59 = require("crypto");
76733
77153
  import_client13 = require("@modelcontextprotocol/client");
76734
77154
  init_service_connections();
76735
77155
  init_connected_usage_billing();
@@ -77353,8 +77773,8 @@ async function setScheduleConnectionActionsEnabled(identity, connectionId, enabl
77353
77773
  return data.connection.actionsEnabled === true;
77354
77774
  }
77355
77775
  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");
77776
+ 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")}`;
77777
+ const requestDigest = (0, import_node_crypto60.createHash)("sha256").update(connectionId).update("\0").update(tool?.trim() ?? "").update("\0").update(canonicalJson2(input)).digest("hex");
77358
77778
  if (mainOwnsIntegrations()) {
77359
77779
  const selectedTool = tool?.trim();
77360
77780
  if (!selectedTool) throw new NangoControlError("An action tool is required.", 400, "invalid_request", false);
@@ -77505,7 +77925,7 @@ function canonicalJson2(value) {
77505
77925
  return JSON.stringify(value);
77506
77926
  }
77507
77927
  function projectedToolSchemaHash(tool) {
77508
- return (0, import_node_crypto59.createHash)("sha256").update(canonicalJson2(tool)).digest("hex");
77928
+ return (0, import_node_crypto60.createHash)("sha256").update(canonicalJson2(tool)).digest("hex");
77509
77929
  }
77510
77930
  async function describeNangoTool(identity, connectionId, tool, fresh) {
77511
77931
  if (mainOwnsIntegrations()) {
@@ -77782,11 +78202,11 @@ async function callMainOwnedExportPage(identity, input) {
77782
78202
  untrustedContent: true
77783
78203
  };
77784
78204
  }
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;
78205
+ 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
78206
  var init_nango_control = __esm({
77787
78207
  "src/api/nango-control.ts"() {
77788
78208
  "use strict";
77789
- import_node_crypto59 = require("crypto");
78209
+ import_node_crypto60 = require("crypto");
77790
78210
  init_connected_data_export();
77791
78211
  init_slack_connected_data_export();
77792
78212
  init_main_nango_transport();
@@ -78142,7 +78562,7 @@ async function callResendRead(identity, connectionId, tool, args) {
78142
78562
  return isRecord5(data) ? data.result ?? data : data;
78143
78563
  }
78144
78564
  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")}`;
78565
+ const requestId = `main-resend-action:${(0, import_node_crypto61.createHash)("sha256").update(identity).update("\0").update(idempotencyKey3.trim()).digest("hex")}`;
78146
78566
  const body = await controlRequest2("/api/internal/resend/actions/call", {
78147
78567
  method: "POST",
78148
78568
  headers: { "x-request-id": requestId },
@@ -78207,12 +78627,12 @@ async function callResendExportPage(identity, input) {
78207
78627
  untrustedContent: true
78208
78628
  };
78209
78629
  }
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;
78630
+ 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
78631
  var init_resend_control = __esm({
78212
78632
  "src/api/resend-control.ts"() {
78213
78633
  "use strict";
78214
78634
  init_connected_data_export();
78215
- import_node_crypto60 = require("crypto");
78635
+ import_node_crypto61 = require("crypto");
78216
78636
  DEFAULT_CONNECTION_CONTROL_URL = "https://mcp-scraper-scheduler.vercel.app";
78217
78637
  RESEND_PROVIDER_CONFIG_KEY = "resend";
78218
78638
  RESEND_LOGO_URL = "https://cdn.resend.com/brand/resend-icon-black.svg";
@@ -78797,7 +79217,7 @@ function settleWithinTickBudget(label, unfinished, work, onDeadlineOrError) {
78797
79217
  );
78798
79218
  });
78799
79219
  }
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;
79220
+ 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
79221
  var init_server = __esm({
78802
79222
  "src/api/server.ts"() {
78803
79223
  "use strict";
@@ -78810,7 +79230,7 @@ var init_server = __esm({
78810
79230
  init_og();
78811
79231
  import_resend3 = require("resend");
78812
79232
  init_url_utils();
78813
- import_node_crypto61 = require("crypto");
79233
+ import_node_crypto62 = require("crypto");
78814
79234
  init_kpo_extractor();
78815
79235
  init_screenshot();
78816
79236
  init_media_extractor();
@@ -80360,7 +80780,7 @@ var init_server = __esm({
80360
80780
  if (!harvestOk) return c.json(insufficientBalanceResponse(harvestBal, harvestCost), 402);
80361
80781
  jobId2 = await createJob(user.id, options.query, { ...options, billingHoldMc: harvestCost }, body.callback_url);
80362
80782
  } else {
80363
- jobId2 = (0, import_node_crypto61.randomUUID)();
80783
+ jobId2 = (0, import_node_crypto62.randomUUID)();
80364
80784
  const billingDebitKey = `paa-harvest:${jobId2}:hold`;
80365
80785
  const description = `PAA harvest: ${options.query}`.slice(0, 500);
80366
80786
  const hold = await debitMcIdempotent(