mcp-scraper 0.55.0 → 0.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +2 -2
  3. package/dist/bin/api-server.cjs +615 -172
  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 +49 -4
  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-2FL5FXMV.js → chunk-5XYBKE3X.js} +2 -2
  16. package/dist/{chunk-PGI2QPFE.js → chunk-HU4R6BP2.js} +2 -2
  17. package/dist/{chunk-PGI2QPFE.js.map → chunk-HU4R6BP2.js.map} +1 -1
  18. package/dist/chunk-QQ6ZBYZZ.js +7 -0
  19. package/dist/chunk-QQ6ZBYZZ.js.map +1 -0
  20. package/dist/{chunk-N4LT53JX.js → chunk-R6CO77BN.js} +3 -3
  21. package/dist/{chunk-N4LT53JX.js.map → chunk-R6CO77BN.js.map} +1 -1
  22. package/dist/{chunk-WYLIDW4S.js → chunk-ULAJAZQF.js} +50 -5
  23. package/dist/chunk-ULAJAZQF.js.map +1 -0
  24. package/dist/{extract-bundle-3CRG7FAN.js → extract-bundle-H7JEELY7.js} +3 -3
  25. package/dist/{server-WKBCZ2TH.js → server-NBGZJX4P.js} +474 -86
  26. package/dist/server-NBGZJX4P.js.map +1 -0
  27. package/dist/{site-extract-repository-QSRPXNMI.js → site-extract-repository-CFMSWAVL.js} +3 -3
  28. package/dist/{worker-W5C6SKZT.js → worker-S2WXELUC.js} +2 -2
  29. package/package.json +1 -1
  30. package/dist/chunk-V2QWNGEK.js +0 -7
  31. package/dist/chunk-V2QWNGEK.js.map +0 -1
  32. package/dist/chunk-WYLIDW4S.js.map +0 -1
  33. package/dist/server-WKBCZ2TH.js.map +0 -1
  34. /package/dist/{chunk-2FL5FXMV.js.map → chunk-5XYBKE3X.js.map} +0 -0
  35. /package/dist/{extract-bundle-3CRG7FAN.js.map → extract-bundle-H7JEELY7.js.map} +0 -0
  36. /package/dist/{site-extract-repository-QSRPXNMI.js.map → site-extract-repository-CFMSWAVL.js.map} +0 -0
  37. /package/dist/{worker-W5C6SKZT.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": 249,
12459
- "scraperTools": 148,
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.55.0";
44627
+ PACKAGE_VERSION = "0.57.0";
44628
44628
  }
44629
44629
  });
44630
44630
 
@@ -44690,7 +44690,7 @@ seam is noted so you can chain them.
44690
44690
  prepare-memory-write: it returns the live entity profile contract, duplicate candidates, tag reuse/review/create
44691
44691
  guidance, recommended sections, optional sections, and heading guidance before any write occurs.
44692
44692
  - Validate a composed entity without writing -> **commons_validate_entity**. Use it after composing and before submit
44693
- to catch missing featured images, missing source/body evidence, duplicate conflicts, revision problems, and
44693
+ to catch missing featured images, missing source/body evidence, duplicate candidates, and
44694
44694
  unsupported placeholder sections.
44695
44695
  - Create or edit a public wiki entity -> **commons_submit_entity**. This is a governed proposal write,
44696
44696
  not direct rendered HTML editing. Normal workflow is prepare -> validate -> submit. Pick the closest entity type
@@ -44698,6 +44698,9 @@ seam is noted so you can chain them.
44698
44698
  Trail, or PublicArticle.
44699
44699
  Creating a new entity costs 10 Credits; editing an existing entity costs 2 Credits. Preparation, validation,
44700
44700
  search, and reads are free. Reusing the same idempotency key for a retry does not charge twice.
44701
+ Commons is an open wiki. Any account may edit any entity, and every edit publishes immediately and is
44702
+ tracked on the public contribution ledger with the actor, the changed fields, and a before/after snapshot.
44703
+ Pass baseRevision on an edit so the ledger records whether it landed on top of a newer revision.
44701
44704
  The body must be a neutral encyclopedia projection, not a blog post or raw scrape dump. Use H2 for the
44702
44705
  entity's main sections and H3/H4/H5 for subtopics; every heading becomes a public page-menu item.
44703
44706
  Entity profiles are adaptive contracts, not empty templates. Use the prepare output as the guide, but omit
@@ -46450,7 +46453,7 @@ var init_contracts = __esm({
46450
46453
  });
46451
46454
 
46452
46455
  // src/mcp/mcp-tool-schemas.ts
46453
- 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, 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;
46454
46457
  var init_mcp_tool_schemas = __esm({
46455
46458
  "src/mcp/mcp-tool-schemas.ts"() {
46456
46459
  "use strict";
@@ -46932,6 +46935,16 @@ var init_mcp_tool_schemas = __esm({
46932
46935
  CommonsGetEntityLedgerInputSchema = {
46933
46936
  idOrSlug: import_zod43.z.string().trim().min(1).max(180).describe("Published entity id or /wiki/ slug whose contribution ledger should be read.")
46934
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
+ };
46945
+ CommonsGetProposalInputSchema = {
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>.")
46947
+ };
46935
46948
  CommonsSaveFilterInputSchema = {
46936
46949
  name: import_zod43.z.string().trim().min(2).max(80).describe("User-facing name for this MCP personalization filter. Reusing a name updates the saved filter."),
46937
46950
  description: import_zod43.z.string().trim().max(500).optional().describe("Optional note explaining when this filter should be used."),
@@ -50447,7 +50460,7 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
50447
50460
  }, async (input) => executor.commonsValidateEntity(input));
50448
50461
  server.registerTool("commons_submit_entity", {
50449
50462
  title: "Transparent Commons Governed Entity Write",
50450
- description: "Create or propose an edit to a Transparent Commons public wiki entity through the governed MCP Scraper write plane. A new entity costs 10 Credits; an edit to an existing entity costs 2 Credits. Preparation and validation are free, and idempotent retries are not charged twice. This never edits rendered HTML directly. Normal workflow is commons_prepare_entity, compose, commons_validate_entity, then submit. Choose a precise entityType and write a neutral encyclopedia projection with the matching entity structure, not a blog article or raw scrape dump. Use H2 sections and H3/H4/H5 subitems; every heading appears in the public page menu. Omit unsupported sections instead of publishing empty/filler headings. It writes a proposal, records a contribution ledger entry when accepted, and auto-publishes only safe non-conflicting changes with a featured image and source/body evidence. Existing entity edits require baseRevision to auto-publish. Requires idempotencyKey.",
50463
+ description: "Create or propose an edit to a Transparent Commons public wiki entity through the governed MCP Scraper write plane. A new entity costs 10 Credits; an edit to an existing entity costs 2 Credits. Preparation and validation are free, and idempotent retries are not charged twice. This never edits rendered HTML directly. Normal workflow is commons_prepare_entity, compose, commons_validate_entity, then submit. Choose a precise entityType and write a neutral encyclopedia projection with the matching entity structure, not a blog article or raw scrape dump. Use H2 sections and H3/H4/H5 subitems; every heading appears in the public page menu. Omit unsupported sections instead of publishing empty/filler headings. Transparent Commons is an open wiki: any account may edit any entity, and every edit publishes and is recorded on the public contribution ledger. Publishing requires a featured image and body/source evidence. Pass baseRevision when editing so the ledger can show whether the edit was written on top of a newer revision. Requires idempotencyKey.",
50451
50464
  inputSchema: CommonsSubmitEntityInputSchema,
50452
50465
  outputSchema: recordOutputSchema("commons_submit_entity", CommonsGenericOutputSchema),
50453
50466
  annotations: {
@@ -50471,6 +50484,32 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
50471
50484
  openWorldHint: false
50472
50485
  }
50473
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));
50500
+ server.registerTool("commons_get_proposal", {
50501
+ title: "Transparent Commons Proposal Status",
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.",
50503
+ inputSchema: CommonsGetProposalInputSchema,
50504
+ outputSchema: recordOutputSchema("commons_get_proposal", CommonsGenericOutputSchema),
50505
+ annotations: {
50506
+ title: "Transparent Commons Proposal Status",
50507
+ readOnlyHint: true,
50508
+ destructiveHint: false,
50509
+ idempotentHint: true,
50510
+ openWorldHint: false
50511
+ }
50512
+ }, async (input) => executor.commonsGetProposal(input));
50474
50513
  server.registerTool("commons_save_filter", {
50475
50514
  title: "Transparent Commons Saved MCP Filter",
50476
50515
  description: "Save or update an account-scoped Commons search filter so one MCP can behave like many personalized reading rooms, trails, source scopes, categories, or tag bundles. Reusing a filter name updates it.",
@@ -51518,6 +51557,12 @@ var init_http_mcp_tool_executor = __esm({
51518
51557
  commonsGetEntityLedger(input) {
51519
51558
  return this.getJson(`/commons/entities/${encodeURIComponent(input.idOrSlug)}/ledger`);
51520
51559
  }
51560
+ commonsGetProposal(input) {
51561
+ return this.getJson(`/commons/proposals/${encodeURIComponent(input.proposalId)}`);
51562
+ }
51563
+ commonsHostImage(input) {
51564
+ return this.call("/commons/images", input);
51565
+ }
51521
51566
  commonsSaveFilter(input) {
51522
51567
  return this.call("/commons/filters", input);
51523
51568
  }
@@ -64254,6 +64299,231 @@ var init_editorial_reading_room_routes = __esm({
64254
64299
  }
64255
64300
  });
64256
64301
 
64302
+ // src/api/commons-image-store.ts
64303
+ async function ensureCommonsImageSchema() {
64304
+ if (imageSchemaReady) return;
64305
+ await getDb().execute(`
64306
+ CREATE TABLE IF NOT EXISTS commons_images (
64307
+ id TEXT PRIMARY KEY,
64308
+ user_id INTEGER NOT NULL,
64309
+ digest TEXT NOT NULL,
64310
+ storage_key TEXT NOT NULL,
64311
+ url TEXT NOT NULL,
64312
+ content_type TEXT NOT NULL,
64313
+ bytes INTEGER NOT NULL,
64314
+ source_url TEXT,
64315
+ alt TEXT,
64316
+ license TEXT,
64317
+ attribution TEXT,
64318
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
64319
+ )
64320
+ `);
64321
+ await getDb().execute(`CREATE UNIQUE INDEX IF NOT EXISTS commons_images_digest ON commons_images(digest)`);
64322
+ imageSchemaReady = true;
64323
+ }
64324
+ function sniffImageFormat(bytes) {
64325
+ if (bytes.length < 12) return null;
64326
+ if (bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) return "image/jpeg";
64327
+ if (bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) return "image/png";
64328
+ if (bytes.subarray(0, 6).toString("ascii") === "GIF87a" || bytes.subarray(0, 6).toString("ascii") === "GIF89a") return "image/gif";
64329
+ if (bytes.subarray(0, 4).toString("ascii") === "RIFF" && bytes.subarray(8, 12).toString("ascii") === "WEBP") return "image/webp";
64330
+ return null;
64331
+ }
64332
+ function isBlockedAddress(address) {
64333
+ if ((0, import_node_net2.isIP)(address) === 6) {
64334
+ const normalized = address.toLowerCase();
64335
+ return normalized === "::1" || normalized.startsWith("fc") || normalized.startsWith("fd") || normalized.startsWith("fe80");
64336
+ }
64337
+ const parts = address.split(".").map(Number);
64338
+ if (parts.length !== 4 || parts.some((part) => Number.isNaN(part))) return true;
64339
+ const [a, b] = parts;
64340
+ if (a === 10 || a === 127 || a === 0) return true;
64341
+ if (a === 172 && b >= 16 && b <= 31) return true;
64342
+ if (a === 192 && b === 168) return true;
64343
+ if (a === 169 && b === 254) return true;
64344
+ if (a === 100 && b >= 64 && b <= 127) return true;
64345
+ return false;
64346
+ }
64347
+ async function assertPublicHttpsUrl(rawUrl) {
64348
+ let url;
64349
+ try {
64350
+ url = new URL(rawUrl);
64351
+ } catch {
64352
+ throw new CommonsImageError("image_url_invalid", `"${rawUrl}" is not a valid URL.`);
64353
+ }
64354
+ if (url.protocol !== "https:") {
64355
+ throw new CommonsImageError("image_url_not_https", "Commons image sources must be https URLs.");
64356
+ }
64357
+ const hostname = url.hostname.replace(/^\[|\]$/g, "");
64358
+ const addresses = (0, import_node_net2.isIP)(hostname) ? [{ address: hostname }] : await (0, import_promises15.lookup)(hostname, { all: true }).catch(() => []);
64359
+ if (!addresses.length) {
64360
+ throw new CommonsImageError("image_host_unresolvable", `Could not resolve ${url.hostname}.`);
64361
+ }
64362
+ if (addresses.some((entry) => isBlockedAddress(entry.address))) {
64363
+ throw new CommonsImageError("image_host_blocked", "Commons image sources cannot resolve to private or loopback addresses.");
64364
+ }
64365
+ return url;
64366
+ }
64367
+ async function downloadImage(sourceUrl) {
64368
+ let current = sourceUrl;
64369
+ for (let redirect = 0; redirect <= COMMONS_IMAGE_MAX_REDIRECTS; redirect += 1) {
64370
+ const url = await assertPublicHttpsUrl(current);
64371
+ const response = await fetch(url, { redirect: "manual", signal: AbortSignal.timeout(COMMONS_IMAGE_FETCH_TIMEOUT_MS) });
64372
+ if (response.status >= 300 && response.status < 400) {
64373
+ const location2 = response.headers.get("location");
64374
+ if (!location2) throw new CommonsImageError("image_redirect_invalid", "Image source redirected without a destination.");
64375
+ current = new URL(location2, url).toString();
64376
+ continue;
64377
+ }
64378
+ if (!response.ok) {
64379
+ throw new CommonsImageError("image_source_unavailable", `Image source returned HTTP ${response.status}.`, 422);
64380
+ }
64381
+ const declaredLength = Number(response.headers.get("content-length") ?? "0");
64382
+ if (declaredLength > COMMONS_IMAGE_MAX_BYTES) {
64383
+ throw new CommonsImageError("image_too_large", `Image exceeds the ${COMMONS_IMAGE_MAX_BYTES} byte limit.`, 413);
64384
+ }
64385
+ const bytes = Buffer.from(await response.arrayBuffer());
64386
+ if (bytes.length > COMMONS_IMAGE_MAX_BYTES) {
64387
+ throw new CommonsImageError("image_too_large", `Image exceeds the ${COMMONS_IMAGE_MAX_BYTES} byte limit.`, 413);
64388
+ }
64389
+ return bytes;
64390
+ }
64391
+ throw new CommonsImageError("image_redirect_limit", "Image source exceeded the redirect limit.");
64392
+ }
64393
+ function decodeBase64Image(value) {
64394
+ const payload = value.includes(",") && value.trim().startsWith("data:") ? value.slice(value.indexOf(",") + 1) : value;
64395
+ const bytes = Buffer.from(payload, "base64");
64396
+ if (!bytes.length) throw new CommonsImageError("image_base64_invalid", "imageBase64 did not decode to any bytes.");
64397
+ if (bytes.length > COMMONS_IMAGE_MAX_BYTES) {
64398
+ throw new CommonsImageError("image_too_large", `Image exceeds the ${COMMONS_IMAGE_MAX_BYTES} byte limit.`, 413);
64399
+ }
64400
+ return bytes;
64401
+ }
64402
+ function isCommonsHostedImageUrl(url) {
64403
+ return /^https:\/\/[^/]*(\.public\.blob\.vercel-storage\.com|blob\.vercel-storage\.com)\//i.test(url) || url.startsWith("file://");
64404
+ }
64405
+ async function hostCommonsImage(input) {
64406
+ if (hostOverride) return hostOverride(input);
64407
+ await ensureCommonsImageSchema();
64408
+ if (!input.sourceUrl && !input.imageBase64) {
64409
+ throw new CommonsImageError("image_source_missing", "Provide either sourceUrl or imageBase64.");
64410
+ }
64411
+ const bytes = input.imageBase64 ? decodeBase64Image(input.imageBase64) : await downloadImage(String(input.sourceUrl));
64412
+ const contentType = sniffImageFormat(bytes);
64413
+ if (!contentType) {
64414
+ throw new CommonsImageError(
64415
+ "image_format_unsupported",
64416
+ "Commons hosts JPEG, PNG, GIF, and WebP. SVG and other formats are rejected because the wiki serves them to the public web.",
64417
+ 415
64418
+ );
64419
+ }
64420
+ const digest2 = (0, import_node_crypto37.createHash)("sha256").update(bytes).digest("hex");
64421
+ const existing = await getDb().execute({
64422
+ sql: "SELECT id, url, content_type, bytes, source_url FROM commons_images WHERE digest = ? LIMIT 1",
64423
+ args: [digest2]
64424
+ });
64425
+ const reusable = existing.rows[0];
64426
+ if (reusable) {
64427
+ return {
64428
+ id: String(reusable.id),
64429
+ url: String(reusable.url),
64430
+ contentType: String(reusable.content_type),
64431
+ bytes: Number(reusable.bytes),
64432
+ digest: digest2,
64433
+ sourceUrl: reusable.source_url ? String(reusable.source_url) : null,
64434
+ reused: true
64435
+ };
64436
+ }
64437
+ const extension = contentType.split("/")[1] === "jpeg" ? "jpg" : contentType.split("/")[1];
64438
+ const storageKey = `commons/images/${digest2.slice(0, 2)}/${digest2}.${extension}`;
64439
+ const stored = await getBlobStore().put(storageKey, bytes, contentType);
64440
+ const id = `TPW-IMG-${digest2.slice(0, 16).toUpperCase()}`;
64441
+ await getDb().execute({
64442
+ sql: `
64443
+ INSERT INTO commons_images (id, user_id, digest, storage_key, url, content_type, bytes, source_url, alt, license, attribution, created_at)
64444
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
64445
+ `,
64446
+ args: [
64447
+ id,
64448
+ input.userId,
64449
+ digest2,
64450
+ storageKey,
64451
+ stored.url,
64452
+ contentType,
64453
+ bytes.length,
64454
+ input.sourceUrl ?? null,
64455
+ input.alt ?? null,
64456
+ input.license ?? null,
64457
+ input.attribution ?? null,
64458
+ (/* @__PURE__ */ new Date()).toISOString()
64459
+ ]
64460
+ });
64461
+ return { id, url: stored.url, contentType, bytes: bytes.length, digest: digest2, sourceUrl: input.sourceUrl ?? null, reused: false };
64462
+ }
64463
+ async function hostEntityImages(input) {
64464
+ const rewrite = { mediaUrls: /* @__PURE__ */ new Map(), dropped: [] };
64465
+ const featuredUrl = input.featuredImage?.url;
64466
+ if (featuredUrl && !isCommonsHostedImageUrl(featuredUrl)) {
64467
+ const hosted = await hostCommonsImage({
64468
+ userId: input.userId,
64469
+ sourceUrl: featuredUrl,
64470
+ alt: input.featuredImage?.alt,
64471
+ license: input.featuredImage?.license,
64472
+ attribution: input.featuredImage?.sourceUrl
64473
+ }).catch((error) => {
64474
+ const message = error instanceof Error ? error.message : String(error);
64475
+ throw new CommonsImageError(
64476
+ error instanceof CommonsImageError ? error.code : "featured_image_unhostable",
64477
+ `The featured image could not be hosted, so this entity would publish with a link that can break: ${message}`,
64478
+ 422
64479
+ );
64480
+ });
64481
+ rewrite.featuredImageUrl = hosted.url;
64482
+ }
64483
+ for (const item of input.media ?? []) {
64484
+ if (!item.url || isCommonsHostedImageUrl(item.url)) continue;
64485
+ try {
64486
+ const hosted = await hostCommonsImage({
64487
+ userId: input.userId,
64488
+ sourceUrl: item.url,
64489
+ alt: item.alt,
64490
+ license: item.license,
64491
+ attribution: item.sourceUrl
64492
+ });
64493
+ rewrite.mediaUrls.set(item.url, hosted.url);
64494
+ } catch (error) {
64495
+ rewrite.dropped.push({ url: item.url, reason: error instanceof Error ? error.message : String(error) });
64496
+ }
64497
+ }
64498
+ return rewrite;
64499
+ }
64500
+ var import_node_crypto37, import_promises15, import_node_net2, COMMONS_IMAGE_MAX_BYTES, COMMONS_IMAGE_MAX_REDIRECTS, COMMONS_IMAGE_FETCH_TIMEOUT_MS, CommonsImageError, imageSchemaReady, hostOverride;
64501
+ var init_commons_image_store = __esm({
64502
+ "src/api/commons-image-store.ts"() {
64503
+ "use strict";
64504
+ import_node_crypto37 = require("crypto");
64505
+ import_promises15 = require("dns/promises");
64506
+ import_node_net2 = require("net");
64507
+ init_blob_store();
64508
+ init_db();
64509
+ COMMONS_IMAGE_MAX_BYTES = 10 * 1024 * 1024;
64510
+ COMMONS_IMAGE_MAX_REDIRECTS = 3;
64511
+ COMMONS_IMAGE_FETCH_TIMEOUT_MS = 2e4;
64512
+ CommonsImageError = class extends Error {
64513
+ constructor(code, message, httpStatus = 400) {
64514
+ super(message);
64515
+ this.code = code;
64516
+ this.httpStatus = httpStatus;
64517
+ this.name = "CommonsImageError";
64518
+ }
64519
+ code;
64520
+ httpStatus;
64521
+ };
64522
+ imageSchemaReady = false;
64523
+ hostOverride = null;
64524
+ }
64525
+ });
64526
+
64257
64527
  // src/api/schema-presence.ts
64258
64528
  async function loadSchemaObjects() {
64259
64529
  const res = await getDb().execute(
@@ -64356,7 +64626,7 @@ function buildCommonsLinkset(entity, claims) {
64356
64626
  context[claim.predicate] = targets;
64357
64627
  }
64358
64628
  const document2 = { linkset: [context] };
64359
- const etag = `"${(0, import_node_crypto37.createHash)("sha256").update(JSON.stringify(document2)).digest("hex")}"`;
64629
+ const etag = `"${(0, import_node_crypto38.createHash)("sha256").update(JSON.stringify(document2)).digest("hex")}"`;
64360
64630
  return { document: document2, etag, publicUrl, linksetUrl, profile: COMMONS_RELATIONSHIP_PROFILE };
64361
64631
  }
64362
64632
  function commonsLinksetDiscoveryHeader(idOrSlug) {
@@ -64424,11 +64694,11 @@ function normalizeHreflang(value) {
64424
64694
  const languages = [...new Set(value.map((item) => optionalText(item, 80)).filter((item) => Boolean(item)))];
64425
64695
  return languages.length ? languages : void 0;
64426
64696
  }
64427
- var import_node_crypto37, COMMONS_LINKSET_MEDIA_TYPE, COMMONS_RELATIONSHIP_PROFILE, REGISTERED_RELATIONS;
64697
+ var import_node_crypto38, COMMONS_LINKSET_MEDIA_TYPE, COMMONS_RELATIONSHIP_PROFILE, REGISTERED_RELATIONS;
64428
64698
  var init_commons_linksets = __esm({
64429
64699
  "src/api/commons-linksets.ts"() {
64430
64700
  "use strict";
64431
- import_node_crypto37 = require("crypto");
64701
+ import_node_crypto38 = require("crypto");
64432
64702
  COMMONS_LINKSET_MEDIA_TYPE = "application/linkset+json";
64433
64703
  COMMONS_RELATIONSHIP_PROFILE = "https://mcpscraper.dev/commons/profiles/relationships/v1";
64434
64704
  REGISTERED_RELATIONS = /* @__PURE__ */ new Set([
@@ -64759,23 +65029,99 @@ async function searchCommonsEntities(filters = {}, userId) {
64759
65029
  const pattern = `%${escapeLike(term)}%`;
64760
65030
  args.push(pattern, pattern, pattern, pattern, pattern);
64761
65031
  }
64762
- args.push(limit, offset);
64763
- const result = await getDb().execute({
65032
+ if (!terms.length) {
65033
+ args.push(limit, offset);
65034
+ const result = await getDb().execute({
65035
+ sql: `
65036
+ SELECT *
65037
+ FROM commons_entities
65038
+ WHERE ${clauses.join(" AND ")}
65039
+ ORDER BY updated_at DESC, title ASC
65040
+ LIMIT ? OFFSET ?
65041
+ `,
65042
+ args
65043
+ });
65044
+ const results2 = result.rows.map((row) => rowToEntity(row));
65045
+ await resolveRelatedLinksAgainstPublishedEntities(results2);
65046
+ return { results: results2, filters: mergedFilters, limit, offset };
65047
+ }
65048
+ const candidates = await getDb().execute({
64764
65049
  sql: `
64765
- SELECT *
65050
+ SELECT id, title, tags_json, keywords_json, description, search_text, updated_at,
65051
+ (
65052
+ SELECT group_concat(
65053
+ coalesce(commons_claims.title, '') || ' ' ||
65054
+ coalesce(commons_claims.predicate, '') || ' ' ||
65055
+ coalesce(commons_claims.predicate_label, '') || ' ' ||
65056
+ coalesce(commons_claims.object_url, ''), ' ')
65057
+ FROM commons_claims
65058
+ WHERE commons_claims.subject_entity_id = commons_entities.id
65059
+ ) AS claim_text
64766
65060
  FROM commons_entities
64767
65061
  WHERE ${clauses.join(" AND ")}
64768
65062
  ORDER BY updated_at DESC, title ASC
64769
- LIMIT ? OFFSET ?
65063
+ LIMIT ${SEARCH_CANDIDATE_LIMIT}
64770
65064
  `,
64771
65065
  args
64772
65066
  });
64773
- return {
64774
- results: result.rows.map((row) => rowToEntity(row)),
64775
- filters: mergedFilters,
64776
- limit,
64777
- offset
64778
- };
65067
+ const ranked = candidates.rows.map((row) => {
65068
+ const record = row;
65069
+ const score = relevanceScore(terms, {
65070
+ title: String(record.title ?? ""),
65071
+ description: String(record.description ?? ""),
65072
+ tags: parseJsonArray(record.tags_json),
65073
+ keywords: parseJsonArray(record.keywords_json),
65074
+ searchText: String(record.search_text ?? ""),
65075
+ claimText: String(record.claim_text ?? "")
65076
+ });
65077
+ return { id: String(record.id), updatedAt: String(record.updated_at ?? ""), title: String(record.title ?? ""), score };
65078
+ }).filter((candidate) => candidate.score > 0).sort((left, right) => right.score - left.score || right.updatedAt.localeCompare(left.updatedAt) || left.title.localeCompare(right.title));
65079
+ const pageIds = ranked.slice(offset, offset + limit).map((candidate) => candidate.id);
65080
+ if (!pageIds.length) return { results: [], filters: mergedFilters, limit, offset };
65081
+ const page = await getDb().execute({
65082
+ sql: `SELECT * FROM commons_entities WHERE id IN (${pageIds.map(() => "?").join(", ")})`,
65083
+ args: pageIds
65084
+ });
65085
+ const byId = new Map(page.rows.map((row) => {
65086
+ const entity = rowToEntity(row);
65087
+ return [entity.id, entity];
65088
+ }));
65089
+ const results = pageIds.map((id) => byId.get(id)).filter((entity) => Boolean(entity));
65090
+ await resolveRelatedLinksAgainstPublishedEntities(results);
65091
+ return { results, filters: mergedFilters, limit, offset };
65092
+ }
65093
+ function parseJsonArray(value) {
65094
+ try {
65095
+ const parsed = JSON.parse(String(value ?? "[]"));
65096
+ return Array.isArray(parsed) ? parsed.map((item) => String(item)) : [];
65097
+ } catch {
65098
+ return [];
65099
+ }
65100
+ }
65101
+ function containsWord(haystack, term) {
65102
+ if (!haystack || !term) return false;
65103
+ const pattern = new RegExp(`(^|[^a-z0-9])${term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}([^a-z0-9]|$)`, "i");
65104
+ return pattern.test(haystack);
65105
+ }
65106
+ function relevanceScore(terms, fields) {
65107
+ let score = 0;
65108
+ const tagText = fields.tags.join(" ");
65109
+ const keywordText = fields.keywords.join(" ");
65110
+ for (const term of terms) {
65111
+ const inTitle = containsWord(fields.title, term);
65112
+ const inTags = containsWord(tagText, term) || containsWord(keywordText, term);
65113
+ const inDescription = containsWord(fields.description, term);
65114
+ const inBody = containsWord(fields.searchText, term);
65115
+ const inClaims = containsWord(fields.claimText, term);
65116
+ if (!inTitle && !inTags && !inDescription && !inBody && !inClaims) return 0;
65117
+ if (inTitle) score += 100;
65118
+ if (inTags) score += 40;
65119
+ if (inDescription) score += 20;
65120
+ if (inClaims) score += 10;
65121
+ if (inBody) score += 5;
65122
+ }
65123
+ if (terms.every((term) => containsWord(fields.title, term))) score += 50;
65124
+ return score;
64779
65125
  }
64780
65126
  async function prepareCommonsEntity(input, user) {
64781
65127
  await ensureCommonsSchema();
@@ -64874,18 +65220,17 @@ async function validateCommonsEntity(input, user) {
64874
65220
  errors.push(`Unsupported placeholder sections must be omitted instead of published: ${placeholderSections.join(", ")}.`);
64875
65221
  }
64876
65222
  if (existing && input.baseRevision === void 0) {
64877
- warnings.push("An existing entity matches this slug or entityId. Submit will create a pending_review proposal unless the current baseRevision is supplied.");
65223
+ warnings.push(`An existing entity matches this slug or entityId. Submit will edit it on top of revision ${existing.revision} and record the edit on the contribution ledger.`);
64878
65224
  }
64879
65225
  if (existing && input.baseRevision !== void 0 && input.baseRevision !== existing.revision) {
64880
- warnings.push(`The matching entity is revision ${existing.revision}; this payload targets revision ${input.baseRevision}. Submit will be held for review.`);
65226
+ warnings.push(`The matching entity is revision ${existing.revision}; this payload targets revision ${input.baseRevision}. Submit will apply on top of revision ${existing.revision} and note the divergence on the ledger.`);
64881
65227
  }
64882
65228
  if (input.reviewPolicy === "always_review") {
64883
65229
  warnings.push("reviewPolicy is always_review, so the proposal will not auto-publish even if the payload is otherwise valid.");
64884
65230
  }
64885
65231
  if (user?.id == null) warnings.push("No authenticated user was supplied to validation; submit still requires API-key authenticated user context.");
64886
65232
  const valid = errors.length === 0;
64887
- const revisionAllowsAutoPublish = !existing || input.baseRevision === existing.revision;
64888
- const autoPublishEligible = valid && revisionAllowsAutoPublish && input.reviewPolicy !== "always_review";
65233
+ const autoPublishEligible = valid && input.reviewPolicy !== "always_review";
64889
65234
  const tagVocabulary = await commonsTagVocabulary();
64890
65235
  return {
64891
65236
  ok: true,
@@ -64923,7 +65268,10 @@ async function getCommonsEntity(idOrSlug) {
64923
65268
  `,
64924
65269
  args: [lookup2, normalizeSlug(lookup2)]
64925
65270
  });
64926
- return result.rows[0] ? rowToEntity(result.rows[0]) : null;
65271
+ if (!result.rows[0]) return null;
65272
+ const entity = rowToEntity(result.rows[0]);
65273
+ await resolveRelatedLinksAgainstPublishedEntities([entity]);
65274
+ return entity;
64927
65275
  }
64928
65276
  async function classifyCommonsWrite(input, user) {
64929
65277
  await ensureCommonsSchema();
@@ -64945,14 +65293,31 @@ async function submitCommonsEntity(input, user) {
64945
65293
  entity: entity2,
64946
65294
  applied: existingProposal.status === "accepted",
64947
65295
  reviewReason: existingProposal.reviewReason,
65296
+ conflictNote: null,
65297
+ droppedImages: [],
64948
65298
  publicUrl: entity2?.status === "published" ? publicUrlForSlug(entity2.slug) : null
64949
65299
  };
64950
65300
  }
65301
+ const hostedImages = await hostEntityImages({
65302
+ userId: Number(user.id),
65303
+ featuredImage: normalized.featuredImage,
65304
+ media: normalized.media
65305
+ });
65306
+ if (hostedImages.featuredImageUrl && normalized.featuredImage) {
65307
+ normalized.featuredImage = { ...normalized.featuredImage, url: hostedImages.featuredImageUrl };
65308
+ }
65309
+ if (hostedImages.mediaUrls.size || hostedImages.dropped.length) {
65310
+ const droppedUrls = new Set(hostedImages.dropped.map((item) => item.url));
65311
+ normalized.media = normalized.media.filter((item) => !droppedUrls.has(item.url)).map((item) => {
65312
+ const hostedUrl = hostedImages.mediaUrls.get(item.url);
65313
+ return hostedUrl ? { ...item, url: hostedUrl } : item;
65314
+ });
65315
+ }
64951
65316
  const existing = await findEntityBySlugOrIdAnyStatus(normalized.slug, normalized.entityId);
64952
65317
  const existingClaims = existing && normalized.claims !== void 0 ? await getCommonsClaimsByEntityId(existing.id) : [];
64953
65318
  const safety = evaluatePublishSafety(normalized, existing);
64954
65319
  const shouldApply = safety.safe && normalized.reviewPolicy !== "always_review";
64955
- const proposalId = `commons-proposal-${(0, import_node_crypto38.randomUUID)()}`;
65320
+ const proposalId = `commons-proposal-${(0, import_node_crypto39.randomUUID)()}`;
64956
65321
  const now = (/* @__PURE__ */ new Date()).toISOString();
64957
65322
  const entityId = existing?.id ?? normalized.entityId ?? allocateEntityId();
64958
65323
  const proposalStatus = shouldApply ? "accepted" : "pending_review";
@@ -65007,7 +65372,7 @@ async function submitCommonsEntity(input, user) {
65007
65372
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
65008
65373
  `,
65009
65374
  args: [
65010
- `commons-ledger-${(0, import_node_crypto38.randomUUID)()}`,
65375
+ `commons-ledger-${(0, import_node_crypto39.randomUUID)()}`,
65011
65376
  nextEntity.id,
65012
65377
  proposalId,
65013
65378
  user.id,
@@ -65018,7 +65383,7 @@ async function submitCommonsEntity(input, user) {
65018
65383
  ...changedFieldPaths(existing, nextEntity),
65019
65384
  ...normalized.claims !== void 0 ? ["claims"] : []
65020
65385
  ]),
65021
- normalized.changeSummary || (existing ? "Updated Commons entity." : "Created Commons entity."),
65386
+ ledgerSummary(normalized.changeSummary, existing, safety.conflictNote),
65022
65387
  existing ? JSON.stringify({
65023
65388
  ...entityLedgerSnapshot(existing),
65024
65389
  ...normalized.claims !== void 0 ? { claims: existingClaims } : {}
@@ -65041,6 +65406,8 @@ async function submitCommonsEntity(input, user) {
65041
65406
  entity,
65042
65407
  applied: shouldApply,
65043
65408
  reviewReason: safety.reason,
65409
+ conflictNote: safety.conflictNote,
65410
+ droppedImages: hostedImages.dropped,
65044
65411
  publicUrl: entity?.status === "published" && shouldApply ? publicUrlForSlug(entity.slug) : null
65045
65412
  };
65046
65413
  }
@@ -65111,7 +65478,7 @@ async function saveCommonsFilter(input, user) {
65111
65478
  sql: "SELECT id FROM commons_saved_filters WHERE user_id = ? AND name = ? LIMIT 1",
65112
65479
  args: [user.id, name]
65113
65480
  });
65114
- const id = existing.rows[0]?.id != null ? String(existing.rows[0].id) : `commons-filter-${(0, import_node_crypto38.randomUUID)()}`;
65481
+ const id = existing.rows[0]?.id != null ? String(existing.rows[0].id) : `commons-filter-${(0, import_node_crypto39.randomUUID)()}`;
65115
65482
  await getDb().execute({
65116
65483
  sql: `
65117
65484
  INSERT INTO commons_saved_filters (id, user_id, name, description, filter_json, created_at, updated_at)
@@ -65179,8 +65546,9 @@ async function listCommonsNeedsLinks(filters = {}) {
65179
65546
  });
65180
65547
  const concepts = /* @__PURE__ */ new Map();
65181
65548
  const queryTerms = tokenize(cleanFilters.query);
65182
- for (const row of result.rows) {
65183
- const entity = rowToEntity(row);
65549
+ const scanned = result.rows.map((row) => rowToEntity(row));
65550
+ await resolveRelatedLinksAgainstPublishedEntities(scanned);
65551
+ for (const entity of scanned) {
65184
65552
  for (const link2 of entity.articleSections.relatedLinks) {
65185
65553
  if (!relatedLinkNeedsPage(link2)) continue;
65186
65554
  const haystack = [link2.title, link2.summary, link2.relationship, entity.title, entity.description, entity.tags.join(" ")].filter(Boolean).join(" ").toLowerCase();
@@ -65234,8 +65602,8 @@ function commonsDatabaseReport() {
65234
65602
  directPublicHtmlWrites: false,
65235
65603
  normalWorkflow: ["commons_prepare_entity", "commons_validate_entity", "commons_submit_entity"],
65236
65604
  writesCreate: ["commons_proposals", "commons_ledger", "commons_entities when accepted"],
65237
- safeAutoPublish: "New non-conflicting entities with title, description, featured image, and body/source evidence.",
65238
- conflictBehavior: "Existing entity edits require baseRevision. Otherwise the proposal is pending_review.",
65605
+ safeAutoPublish: "Any entity with title, description, featured image, and body/source evidence. Creates and edits both publish.",
65606
+ conflictBehavior: "Anyone may edit any entity. A missing or stale baseRevision still applies on top of the current revision; the divergence is recorded on the contribution ledger.",
65239
65607
  billing: { validationCredits: 0, createCredits: LISTING_WRITE_RATE_POLICY.create.credits, editCredits: LISTING_WRITE_RATE_POLICY.edit.credits }
65240
65608
  },
65241
65609
  searchCorpus: ["title", "disambiguationName", "description", "tags", "keywords", "relatedEntities", "relationship claims", "bodyMarkdown", "sections", "media", "source metadata", "jsonLd"],
@@ -65557,19 +65925,23 @@ function normalizeClaimsForRepository(input) {
65557
65925
  throw new CommonsRepositoryError("invalid_claims", error instanceof Error ? error.message : "Claims are invalid.");
65558
65926
  }
65559
65927
  }
65928
+ function ledgerSummary(changeSummary, existing, conflictNote) {
65929
+ const base = changeSummary || (existing ? "Updated Commons entity." : "Created Commons entity.");
65930
+ return conflictNote ? `${base} ${conflictNote}` : base;
65931
+ }
65560
65932
  function evaluatePublishSafety(input, existing) {
65561
- if (!input.featuredImage?.url) return { safe: false, reason: "A featured image is required before a Commons entity can publish." };
65933
+ if (!input.featuredImage?.url) return { safe: false, reason: "A featured image is required before a Commons entity can publish.", conflictNote: null };
65562
65934
  if (!input.bodyMarkdown.trim() && !input.source.originalUrl && !input.source.resolvedUrl) {
65563
- return { safe: false, reason: "A public entity needs article body text or source evidence before publishing." };
65935
+ return { safe: false, reason: "A public entity needs article body text or source evidence before publishing.", conflictNote: null };
65564
65936
  }
65565
- if (!existing) return { safe: true, reason: null };
65937
+ if (!existing) return { safe: true, reason: null, conflictNote: null };
65566
65938
  if (input.baseRevision === void 0) {
65567
- return { safe: false, reason: "Existing entity edits require baseRevision so agents do not overwrite someone else silently." };
65939
+ return { safe: true, reason: null, conflictNote: `Applied without baseRevision on top of revision ${existing.revision}.` };
65568
65940
  }
65569
65941
  if (input.baseRevision !== existing.revision) {
65570
- return { safe: false, reason: `Existing entity revision is ${existing.revision}; proposal targeted ${input.baseRevision}.` };
65942
+ return { safe: true, reason: null, conflictNote: `Applied on top of revision ${existing.revision}; this edit targeted revision ${input.baseRevision}.` };
65571
65943
  }
65572
- return { safe: true, reason: null };
65944
+ return { safe: true, reason: null, conflictNote: null };
65573
65945
  }
65574
65946
  function buildNextEntity(input) {
65575
65947
  const current = input.existing;
@@ -65884,7 +66256,7 @@ function commonsIndexDocuments(entity, now) {
65884
66256
  return documents;
65885
66257
  }
65886
66258
  function commonsIndexDocumentId(entityId, documentType, documentKey) {
65887
- return `commons-index-${(0, import_node_crypto38.createHash)("sha256").update(`${entityId}
66259
+ return `commons-index-${(0, import_node_crypto39.createHash)("sha256").update(`${entityId}
65888
66260
  ${documentType}
65889
66261
  ${documentKey}`).digest("hex").slice(0, 32)}`;
65890
66262
  }
@@ -66219,6 +66591,44 @@ function sanitizeNeedsLinkFilters(filters) {
66219
66591
  offset: clampNumber(filters.offset, 0, 1e4, 0)
66220
66592
  };
66221
66593
  }
66594
+ async function publishedEntityIndexFor(titles) {
66595
+ const index = /* @__PURE__ */ new Map();
66596
+ const slugKeys = uniqueStrings(titles.map((title) => normalizeNeedsLinkTitle(title))).filter(Boolean);
66597
+ const titleKeys = uniqueStrings(titles.map((title) => String(title || "").trim().toLowerCase())).filter(Boolean);
66598
+ if (!slugKeys.length && !titleKeys.length) return index;
66599
+ const result = await getDb().execute({
66600
+ sql: `
66601
+ SELECT id, slug, title
66602
+ FROM commons_entities
66603
+ WHERE status = 'published'
66604
+ AND (slug IN (${slugKeys.map(() => "?").join(", ") || "NULL"})
66605
+ OR lower(title) IN (${titleKeys.map(() => "?").join(", ") || "NULL"}))
66606
+ `,
66607
+ args: [...slugKeys, ...titleKeys]
66608
+ });
66609
+ for (const row of result.rows) {
66610
+ const record = row;
66611
+ const entry = { id: String(record.id), slug: String(record.slug), title: String(record.title) };
66612
+ index.set(entry.slug, entry);
66613
+ index.set(normalizeNeedsLinkTitle(entry.title), entry);
66614
+ }
66615
+ return index;
66616
+ }
66617
+ function unresolvedRelatedLinkTitles(entities) {
66618
+ return entities.flatMap((entity) => entity.articleSections.relatedLinks.filter((link2) => relatedLinkNeedsPage(link2)).map((link2) => link2.title));
66619
+ }
66620
+ async function resolveRelatedLinksAgainstPublishedEntities(entities) {
66621
+ const index = await publishedEntityIndexFor(unresolvedRelatedLinkTitles(entities));
66622
+ if (!index.size) return;
66623
+ for (const entity of entities) {
66624
+ entity.articleSections.relatedLinks = entity.articleSections.relatedLinks.map((link2) => {
66625
+ if (!relatedLinkNeedsPage(link2)) return link2;
66626
+ const match = index.get(normalizeNeedsLinkTitle(link2.title));
66627
+ if (!match || match.id === entity.id) return link2;
66628
+ return { ...link2, slug: match.slug, entityId: match.id, needsLink: false };
66629
+ });
66630
+ }
66631
+ }
66222
66632
  function relatedLinkNeedsPage(link2) {
66223
66633
  if (link2.needsLink === true) return true;
66224
66634
  if (link2.entityId || link2.slug) return false;
@@ -66411,12 +66821,13 @@ function jsonLikeValue(value) {
66411
66821
  function escapeLike(value) {
66412
66822
  return value.replace(/[%_]/g, "");
66413
66823
  }
66414
- var import_node_crypto38, COMMONS_SCHEMA_VERSION, DEFAULT_COMMONS_BASE_URL, DEFAULT_ENTITY_TYPE, COMMONS_ENTITY_PROFILES, schemaReady, COMMONS_SCHEMA_OBJECTS, CommonsRepositoryError;
66824
+ var import_node_crypto39, COMMONS_SCHEMA_VERSION, DEFAULT_COMMONS_BASE_URL, DEFAULT_ENTITY_TYPE, COMMONS_ENTITY_PROFILES, schemaReady, COMMONS_SCHEMA_OBJECTS, SEARCH_CANDIDATE_LIMIT, CommonsRepositoryError;
66415
66825
  var init_commons_repository = __esm({
66416
66826
  "src/api/commons-repository.ts"() {
66417
66827
  "use strict";
66418
- import_node_crypto38 = require("crypto");
66828
+ import_node_crypto39 = require("crypto");
66419
66829
  init_db();
66830
+ init_commons_image_store();
66420
66831
  init_schema_presence();
66421
66832
  init_rates();
66422
66833
  init_commons_linksets();
@@ -66594,6 +67005,7 @@ var init_commons_repository = __esm({
66594
67005
  "commons_publications_updated",
66595
67006
  "commons_publication_editions_publication_published"
66596
67007
  ];
67008
+ SEARCH_CANDIDATE_LIMIT = 500;
66597
67009
  CommonsRepositoryError = class extends Error {
66598
67010
  constructor(code, message, httpStatus = 400) {
66599
67011
  super(message);
@@ -66696,7 +67108,7 @@ async function claimCommonsPublication(input, user) {
66696
67108
  }
66697
67109
  const existingName = await getCommonsPublicationBySubdomain(subdomain);
66698
67110
  if (existingName) throw new CommonsPublicationError("publication_name_unavailable", "That publication name is already claimed.", 409);
66699
- const id = `tcpub_${(0, import_node_crypto39.randomUUID)()}`;
67111
+ const id = `tcpub_${(0, import_node_crypto40.randomUUID)()}`;
66700
67112
  const now = (/* @__PURE__ */ new Date()).toISOString();
66701
67113
  try {
66702
67114
  await getDb().execute({
@@ -66748,7 +67160,7 @@ async function publishCommonsEditorial(input, user) {
66748
67160
  const canonicalUrl = editionPublicUrl(subdomain, editionSlug);
66749
67161
  const rendered = renderEditorialReadingRoom(editionInput);
66750
67162
  const html = addPublicMetadata(rendered.html, canonicalUrl, publication.title);
66751
- const editionId = `tced_${(0, import_node_crypto39.randomUUID)()}`;
67163
+ const editionId = `tced_${(0, import_node_crypto40.randomUUID)()}`;
66752
67164
  const revision = (latest?.revision ?? 0) + 1;
66753
67165
  const now = (/* @__PURE__ */ new Date()).toISOString();
66754
67166
  await getDb().batch([
@@ -66772,7 +67184,7 @@ async function publishCommonsEditorial(input, user) {
66772
67184
  JSON.stringify(editionInput.articles),
66773
67185
  html,
66774
67186
  rendered.filename,
66775
- (0, import_node_crypto39.createHash)("sha256").update(html).digest("hex"),
67187
+ (0, import_node_crypto40.createHash)("sha256").update(html).digest("hex"),
66776
67188
  rendered.articleCount,
66777
67189
  rendered.wordCount,
66778
67190
  Buffer.byteLength(html),
@@ -66958,11 +67370,11 @@ function addPublicMetadata(html, canonicalUrl, publicationTitle) {
66958
67370
  "</head>"
66959
67371
  ].join("\n"));
66960
67372
  }
66961
- var import_node_crypto39, PUBLICATION_ROOT_DOMAIN, RESERVED_SUBDOMAINS, CommonsPublicationError;
67373
+ var import_node_crypto40, PUBLICATION_ROOT_DOMAIN, RESERVED_SUBDOMAINS, CommonsPublicationError;
66962
67374
  var init_commons_publication_repository = __esm({
66963
67375
  "src/api/commons-publication-repository.ts"() {
66964
67376
  "use strict";
66965
- import_node_crypto39 = require("crypto");
67377
+ import_node_crypto40 = require("crypto");
66966
67378
  init_db();
66967
67379
  init_commons_repository();
66968
67380
  init_render();
@@ -67063,7 +67475,7 @@ function publicationError(c, error) {
67063
67475
  function xmlEscape(value) {
67064
67476
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
67065
67477
  }
67066
- var import_hono31, import_zod52, commonsApp, auth2, SearchBodySchema, FeaturedImageSchema, MediaSchema, CitationSchema, RelatedLinkSchema, SubmitEntitySchema, PrepareEntitySchema, ValidateEntitySchema, SaveFilterSchema, NeedsLinkBodySchema, PreparePublicationSchema, ValidatePublicationSchema, ClaimPublicationSchema, PublishEditorialSchema;
67478
+ var import_hono31, import_zod52, commonsApp, auth2, SearchBodySchema, FeaturedImageSchema, MediaSchema, CitationSchema, RelatedLinkSchema, SubmitEntitySchema, PrepareEntitySchema, ValidateEntitySchema, SaveFilterSchema, NeedsLinkBodySchema, PreparePublicationSchema, ValidatePublicationSchema, ClaimPublicationSchema, PublishEditorialSchema, HostImageSchema;
67067
67479
  var init_commons_routes = __esm({
67068
67480
  "src/api/commons-routes.ts"() {
67069
67481
  "use strict";
@@ -67071,6 +67483,7 @@ var init_commons_routes = __esm({
67071
67483
  import_zod52 = require("zod");
67072
67484
  init_api_auth();
67073
67485
  init_commons_repository();
67486
+ init_commons_image_store();
67074
67487
  init_commons_linksets();
67075
67488
  init_listing_write_billing();
67076
67489
  init_commons_publication_repository();
@@ -67324,6 +67737,28 @@ var init_commons_routes = __esm({
67324
67737
  return publicationError(c, error);
67325
67738
  }
67326
67739
  });
67740
+ HostImageSchema = import_zod52.z.object({
67741
+ sourceUrl: import_zod52.z.string().url().optional(),
67742
+ imageBase64: import_zod52.z.string().min(16).optional(),
67743
+ alt: import_zod52.z.string().max(500).optional(),
67744
+ license: import_zod52.z.string().max(240).optional(),
67745
+ attribution: import_zod52.z.string().max(500).optional()
67746
+ }).strict().refine((body) => Boolean(body.sourceUrl || body.imageBase64), {
67747
+ message: "Provide either sourceUrl or imageBase64."
67748
+ });
67749
+ commonsApp.post("/images", auth2, async (c) => {
67750
+ const parsed = HostImageSchema.safeParse(await c.req.json().catch(() => ({})));
67751
+ if (!parsed.success) return validationError(c, parsed.error);
67752
+ try {
67753
+ const hosted = await hostCommonsImage({ ...parsed.data, userId: Number(c.get("user").id) });
67754
+ return c.json({ ok: true, data: hosted }, hosted.reused ? 200 : 201);
67755
+ } catch (error) {
67756
+ if (error instanceof CommonsImageError) {
67757
+ return c.json({ ok: false, error: error.code, message: error.message }, error.httpStatus);
67758
+ }
67759
+ throw error;
67760
+ }
67761
+ });
67327
67762
  commonsApp.get("/entities", async (c) => {
67328
67763
  const result = await searchCommonsEntities(filtersFromQuery(c.req.query()));
67329
67764
  return c.json({ ok: true, data: result });
@@ -67373,7 +67808,15 @@ var init_commons_routes = __esm({
67373
67808
  if ("insufficient" in charged) return c.json(charged.insufficient, 402);
67374
67809
  billing = charged;
67375
67810
  const result = await submitCommonsEntity(parsed.data, c.get("user"));
67376
- return c.json({ ok: true, data: result, billing: publicListingWriteBilling(billing) }, result.applied ? 201 : 202);
67811
+ if (!result.applied) {
67812
+ await refundListingWrite(billing, "Transparent Commons proposal was held for review instead of published.").catch(() => null);
67813
+ return c.json({
67814
+ ok: true,
67815
+ data: result,
67816
+ billing: { ...publicListingWriteBilling(billing), charged: false, refunded: !billing.duplicate }
67817
+ }, 202);
67818
+ }
67819
+ return c.json({ ok: true, data: result, billing: publicListingWriteBilling(billing) }, 201);
67377
67820
  } catch (error) {
67378
67821
  if (billing) await refundListingWrite(billing, "Transparent Commons write failed before persistence.").catch(() => null);
67379
67822
  if (error instanceof CommonsRepositoryError) {
@@ -68117,7 +68560,7 @@ async function migrateAnalytics() {
68117
68560
  }
68118
68561
  function normalizeSlug2(value) {
68119
68562
  const slug4 = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
68120
- return slug4 || `site-${(0, import_node_crypto40.randomBytes)(4).toString("hex")}`;
68563
+ return slug4 || `site-${(0, import_node_crypto41.randomBytes)(4).toString("hex")}`;
68121
68564
  }
68122
68565
  function normalizeObservedHostname(origin) {
68123
68566
  if (!origin || origin === "null") return null;
@@ -68130,7 +68573,7 @@ function normalizeObservedHostname(origin) {
68130
68573
  }
68131
68574
  }
68132
68575
  function publicPixelId() {
68133
- return `px_${(0, import_node_crypto40.randomBytes)(18).toString("base64url")}`;
68576
+ return `px_${(0, import_node_crypto41.randomBytes)(18).toString("base64url")}`;
68134
68577
  }
68135
68578
  function mapRows(rows) {
68136
68579
  return rows;
@@ -68162,7 +68605,7 @@ async function requireEditor(client2, siteId, userId) {
68162
68605
  async function createAnalyticsSite(input) {
68163
68606
  const db = getAnalyticsPool();
68164
68607
  const client2 = await db.connect();
68165
- const id = (0, import_node_crypto40.randomUUID)();
68608
+ const id = (0, import_node_crypto41.randomUUID)();
68166
68609
  const baseSlug = normalizeSlug2(input.slug || input.name);
68167
68610
  try {
68168
68611
  await client2.query("BEGIN");
@@ -68178,7 +68621,7 @@ async function createAnalyticsSite(input) {
68178
68621
  } catch (error) {
68179
68622
  const code = error.code;
68180
68623
  if (code !== "23505" || attempt === 2) throw error;
68181
- slug4 = `${baseSlug}-${(0, import_node_crypto40.randomBytes)(2).toString("hex")}`;
68624
+ slug4 = `${baseSlug}-${(0, import_node_crypto41.randomBytes)(2).toString("hex")}`;
68182
68625
  }
68183
68626
  }
68184
68627
  await client2.query(
@@ -68393,7 +68836,7 @@ async function createAnalyticsPixel(input) {
68393
68836
  VALUES ($1, $2, $3, $4, $5)
68394
68837
  RETURNING id, site_id, public_id, name, environment, status, created_at::text, NULL::text AS last_event_at`,
68395
68838
  [
68396
- (0, import_node_crypto40.randomUUID)(),
68839
+ (0, import_node_crypto41.randomUUID)(),
68397
68840
  input.siteId,
68398
68841
  publicPixelId(),
68399
68842
  input.name.trim(),
@@ -68470,7 +68913,7 @@ async function setAnalyticsPixelDomainState(input) {
68470
68913
  SELECT $1, p.id, $4, $5 FROM analytics_pixels p WHERE p.id = $2 AND p.site_id = $3
68471
68914
  ON CONFLICT(pixel_id, hostname) DO UPDATE SET state = EXCLUDED.state, updated_at = now()
68472
68915
  RETURNING id`,
68473
- [(0, import_node_crypto40.randomUUID)(), input.pixelId, input.siteId, hostname, input.state]
68916
+ [(0, import_node_crypto41.randomUUID)(), input.pixelId, input.siteId, hostname, input.state]
68474
68917
  );
68475
68918
  if (!result.rowCount)
68476
68919
  throw new AnalyticsRepositoryError(
@@ -68520,7 +68963,7 @@ function sanitizeAnalyticsProperties(value) {
68520
68963
  }
68521
68964
  async function ingestAnalyticsEvents(input) {
68522
68965
  const db = getAnalyticsPool();
68523
- const requestId = `air_${(0, import_node_crypto40.randomUUID)()}`;
68966
+ const requestId = `air_${(0, import_node_crypto41.randomUUID)()}`;
68524
68967
  const hostname = normalizeObservedHostname(input.origin);
68525
68968
  const pixelResult = await db.query(
68526
68969
  `SELECT p.id, p.site_id, p.status,
@@ -68541,7 +68984,7 @@ async function ingestAnalyticsEvents(input) {
68541
68984
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
68542
68985
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
68543
68986
  [
68544
- (0, import_node_crypto40.randomUUID)(),
68987
+ (0, import_node_crypto41.randomUUID)(),
68545
68988
  requestId,
68546
68989
  pixel?.site_id ?? null,
68547
68990
  pixel?.id ?? null,
@@ -68566,7 +69009,7 @@ async function ingestAnalyticsEvents(input) {
68566
69009
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
68567
69010
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
68568
69011
  [
68569
- (0, import_node_crypto40.randomUUID)(),
69012
+ (0, import_node_crypto41.randomUUID)(),
68570
69013
  requestId,
68571
69014
  pixel.site_id,
68572
69015
  pixel.id,
@@ -68591,7 +69034,7 @@ async function ingestAnalyticsEvents(input) {
68591
69034
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, rejected_count, reason_codes)
68592
69035
  VALUES ($1, $2, $3, $4, $5, $6::jsonb)`,
68593
69036
  [
68594
- (0, import_node_crypto40.randomUUID)(),
69037
+ (0, import_node_crypto41.randomUUID)(),
68595
69038
  requestId,
68596
69039
  pixel.site_id,
68597
69040
  pixel.id,
@@ -68615,7 +69058,7 @@ async function ingestAnalyticsEvents(input) {
68615
69058
  ON CONFLICT(pixel_id, hostname) DO UPDATE
68616
69059
  SET last_seen_at = now(), updated_at = now()
68617
69060
  RETURNING state`,
68618
- [(0, import_node_crypto40.randomUUID)(), pixel.id, hostname]
69061
+ [(0, import_node_crypto41.randomUUID)(), pixel.id, hostname]
68619
69062
  );
68620
69063
  if (domainResult.rows[0]?.state !== "approved") {
68621
69064
  await db.query(
@@ -68629,7 +69072,7 @@ async function ingestAnalyticsEvents(input) {
68629
69072
  `INSERT INTO analytics_ingestion_receipts(id, request_id, site_id, pixel_id, hostname, rejected_count, reason_codes)
68630
69073
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
68631
69074
  [
68632
- (0, import_node_crypto40.randomUUID)(),
69075
+ (0, import_node_crypto41.randomUUID)(),
68633
69076
  requestId,
68634
69077
  pixel.site_id,
68635
69078
  pixel.id,
@@ -68679,7 +69122,7 @@ async function ingestAnalyticsEvents(input) {
68679
69122
  $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33
68680
69123
  ) ON CONFLICT(site_id, event_id) DO NOTHING`,
68681
69124
  [
68682
- (0, import_node_crypto40.randomUUID)(),
69125
+ (0, import_node_crypto41.randomUUID)(),
68683
69126
  event2.eventId,
68684
69127
  pixel.site_id,
68685
69128
  pixel.id,
@@ -68737,7 +69180,7 @@ async function ingestAnalyticsEvents(input) {
68737
69180
  id, request_id, site_id, pixel_id, hostname, accepted_count, rejected_count, reason_codes
68738
69181
  ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb)`,
68739
69182
  [
68740
- (0, import_node_crypto40.randomUUID)(),
69183
+ (0, import_node_crypto41.randomUUID)(),
68741
69184
  requestId,
68742
69185
  pixel.site_id,
68743
69186
  pixel.id,
@@ -68769,7 +69212,7 @@ function normalizeGeographyCode(value, max) {
68769
69212
  return normalized && /^[A-Z0-9-]+$/.test(normalized) ? normalized.slice(0, max) : null;
68770
69213
  }
68771
69214
  function pageFingerprint(scope, siteId, filters) {
68772
- return (0, import_node_crypto40.createHash)("sha256").update(JSON.stringify({ scope, siteId, filters })).digest("base64url").slice(0, 18);
69215
+ return (0, import_node_crypto41.createHash)("sha256").update(JSON.stringify({ scope, siteId, filters })).digest("base64url").slice(0, 18);
68773
69216
  }
68774
69217
  function decodePageOffset(cursor, fingerprint) {
68775
69218
  if (!cursor) return 0;
@@ -68819,7 +69262,7 @@ async function createAnalyticsConversion(input) {
68819
69262
  404
68820
69263
  );
68821
69264
  }
68822
- const id = (0, import_node_crypto40.randomUUID)();
69265
+ const id = (0, import_node_crypto41.randomUUID)();
68823
69266
  const resolvedPerson = input.sessionId ? await db.query(
68824
69267
  `SELECT e.person_id FROM analytics_identity_nodes n JOIN analytics_identity_edges e ON e.identity_node_id = n.id
68825
69268
  WHERE n.site_id = $1 AND n.kind = 'session_id' AND n.value_hmac = $2 ORDER BY e.confidence DESC LIMIT 1`,
@@ -69568,7 +70011,7 @@ async function createAnalyticsCampaignLink(input) {
69568
70011
  404
69569
70012
  );
69570
70013
  }
69571
- const shortCode = (input.shortCode?.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-") || (0, import_node_crypto40.randomBytes)(5).toString("base64url").toLowerCase()).slice(0, 48);
70014
+ const shortCode = (input.shortCode?.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-") || (0, import_node_crypto41.randomBytes)(5).toString("base64url").toLowerCase()).slice(0, 48);
69572
70015
  try {
69573
70016
  const result = await db.query(
69574
70017
  `INSERT INTO analytics_campaign_links(
@@ -69577,7 +70020,7 @@ async function createAnalyticsCampaignLink(input) {
69577
70020
  ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)
69578
70021
  RETURNING *, 0::int AS click_count`,
69579
70022
  [
69580
- (0, import_node_crypto40.randomUUID)(),
70023
+ (0, import_node_crypto41.randomUUID)(),
69581
70024
  input.siteId,
69582
70025
  input.pixelId ?? null,
69583
70026
  input.name.trim(),
@@ -69648,7 +70091,7 @@ async function resolveAnalyticsCampaignLink(shortCode, referrer) {
69648
70091
  if (!row) return null;
69649
70092
  await db.query(
69650
70093
  `INSERT INTO analytics_campaign_clicks(id, link_id, referrer) VALUES ($1, $2, $3)`,
69651
- [(0, import_node_crypto40.randomUUID)(), row.id, normalizeAnalyticsUrl(referrer || void 0)]
70094
+ [(0, import_node_crypto41.randomUUID)(), row.id, normalizeAnalyticsUrl(referrer || void 0)]
69652
70095
  );
69653
70096
  return buildTaggedCampaignUrl(row);
69654
70097
  }
@@ -69666,14 +70109,14 @@ async function createAnalyticsForm(input) {
69666
70109
  404
69667
70110
  );
69668
70111
  const baseSlug = normalizeSlug2(input.name);
69669
- const slug4 = `${baseSlug}-${(0, import_node_crypto40.randomBytes)(3).toString("hex")}`;
69670
- const publicId = `form_${(0, import_node_crypto40.randomBytes)(18).toString("base64url")}`;
70112
+ const slug4 = `${baseSlug}-${(0, import_node_crypto41.randomBytes)(3).toString("hex")}`;
70113
+ const publicId = `form_${(0, import_node_crypto41.randomBytes)(18).toString("base64url")}`;
69671
70114
  const result = await db.query(
69672
70115
  `INSERT INTO analytics_forms(id, public_id, site_id, pixel_id, name, slug, fields, brand, submit_label, success_message, redirect_url, consent_text, status, created_by_user_id)
69673
70116
  VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8::jsonb,$9,$10,$11,$12,$13,$14)
69674
70117
  RETURNING *, 0::int AS submission_count`,
69675
70118
  [
69676
- (0, import_node_crypto40.randomUUID)(),
70119
+ (0, import_node_crypto41.randomUUID)(),
69677
70120
  publicId,
69678
70121
  input.siteId,
69679
70122
  input.pixelId,
@@ -69718,7 +70161,7 @@ async function getPublicAnalyticsForm(publicId) {
69718
70161
  return result.rows[0] ?? null;
69719
70162
  }
69720
70163
  async function recordAnalyticsFormSubmission(input) {
69721
- const id = (0, import_node_crypto40.randomUUID)();
70164
+ const id = (0, import_node_crypto41.randomUUID)();
69722
70165
  await getAnalyticsPool().query(
69723
70166
  `INSERT INTO analytics_form_submissions(id, form_id, site_id, pixel_id, visitor_id, session_id, crm_person_ref, source, medium, campaign, crm_delivery_status, click_ids)
69724
70167
  VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12::jsonb)`,
@@ -69749,7 +70192,7 @@ function sanitizeClickIds(value) {
69749
70192
  return output;
69750
70193
  }
69751
70194
  function identityHmac(kind, value) {
69752
- return (0, import_node_crypto40.createHmac)("sha256", getSessionSecret()).update(`${kind}:${value.trim().toLowerCase()}`).digest("hex");
70195
+ return (0, import_node_crypto41.createHmac)("sha256", getSessionSecret()).update(`${kind}:${value.trim().toLowerCase()}`).digest("hex");
69753
70196
  }
69754
70197
  async function linkAnalyticsFormIdentity(input) {
69755
70198
  const client2 = await getAnalyticsPool().connect();
@@ -69758,7 +70201,7 @@ async function linkAnalyticsFormIdentity(input) {
69758
70201
  const person = await client2.query(
69759
70202
  `INSERT INTO analytics_people(id, site_id, crm_person_ref) VALUES ($1,$2,$3)
69760
70203
  ON CONFLICT(site_id, crm_person_ref) DO UPDATE SET last_seen_at = now() RETURNING id`,
69761
- [(0, import_node_crypto40.randomUUID)(), input.siteId, input.crmPersonRef]
70204
+ [(0, import_node_crypto41.randomUUID)(), input.siteId, input.crmPersonRef]
69762
70205
  );
69763
70206
  const personId = person.rows[0].id;
69764
70207
  const signals = [];
@@ -69805,7 +70248,7 @@ async function linkAnalyticsFormIdentity(input) {
69805
70248
  `INSERT INTO analytics_identity_nodes(id, site_id, kind, value_hmac) VALUES ($1,$2,$3,$4)
69806
70249
  ON CONFLICT(site_id, kind, value_hmac) DO UPDATE SET last_seen_at = now() RETURNING id`,
69807
70250
  [
69808
- (0, import_node_crypto40.randomUUID)(),
70251
+ (0, import_node_crypto41.randomUUID)(),
69809
70252
  input.siteId,
69810
70253
  signal.kind,
69811
70254
  identityHmac(signal.kind, signal.value)
@@ -69816,7 +70259,7 @@ async function linkAnalyticsFormIdentity(input) {
69816
70259
  VALUES ($1,$2,$3,$4,$5,$6)
69817
70260
  ON CONFLICT(person_id, identity_node_id, evidence_kind) DO UPDATE SET last_seen_at = now(), confidence = greatest(analytics_identity_edges.confidence, EXCLUDED.confidence)`,
69818
70261
  [
69819
- (0, import_node_crypto40.randomUUID)(),
70262
+ (0, import_node_crypto41.randomUUID)(),
69820
70263
  input.siteId,
69821
70264
  personId,
69822
70265
  node.rows[0].id,
@@ -69914,7 +70357,7 @@ async function createAnalyticsCrmImport(input) {
69914
70357
  const db = getAnalyticsPool();
69915
70358
  await requireEditor(db, input.siteId, input.userId);
69916
70359
  const client2 = await db.connect();
69917
- const id = (0, import_node_crypto40.randomUUID)();
70360
+ const id = (0, import_node_crypto41.randomUUID)();
69918
70361
  try {
69919
70362
  await client2.query("BEGIN");
69920
70363
  await client2.query(
@@ -69934,7 +70377,7 @@ async function createAnalyticsCrmImport(input) {
69934
70377
  await client2.query(
69935
70378
  `INSERT INTO analytics_crm_import_rows(id, import_id, crm_person_ref, payload_ciphertext)
69936
70379
  VALUES ($1,$2,$3,$4) ON CONFLICT(import_id, crm_person_ref) DO NOTHING`,
69937
- [(0, import_node_crypto40.randomUUID)(), id, row.crmPersonRef, row.payloadCiphertext]
70380
+ [(0, import_node_crypto41.randomUUID)(), id, row.crmPersonRef, row.payloadCiphertext]
69938
70381
  );
69939
70382
  }
69940
70383
  await client2.query("COMMIT");
@@ -69973,7 +70416,7 @@ async function createAnalyticsActivationDestination(input) {
69973
70416
  `INSERT INTO analytics_activation_destinations(id, site_id, platform, name, connection_ref, external_dataset_id, event_mapping, created_by_user_id)
69974
70417
  VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8) RETURNING *`,
69975
70418
  [
69976
- (0, import_node_crypto40.randomUUID)(),
70419
+ (0, import_node_crypto41.randomUUID)(),
69977
70420
  input.siteId,
69978
70421
  input.platform,
69979
70422
  input.name.trim(),
@@ -70025,7 +70468,7 @@ async function queueAnalyticsActivation(input) {
70025
70468
  `INSERT INTO analytics_activation_jobs(id, destination_id, conversion_id, person_id, payload_ciphertext)
70026
70469
  VALUES ($1,$2,$3,$4,$5) ON CONFLICT(destination_id, conversion_id) DO NOTHING`,
70027
70470
  [
70028
- (0, import_node_crypto40.randomUUID)(),
70471
+ (0, import_node_crypto41.randomUUID)(),
70029
70472
  destination.id,
70030
70473
  input.conversionId,
70031
70474
  input.personId ?? null,
@@ -70037,7 +70480,7 @@ async function queueAnalyticsActivation(input) {
70037
70480
  return queued;
70038
70481
  }
70039
70482
  async function queueAnalyticsFormDelivery(input) {
70040
- const id = (0, import_node_crypto40.randomUUID)();
70483
+ const id = (0, import_node_crypto41.randomUUID)();
70041
70484
  await getAnalyticsPool().query(
70042
70485
  `INSERT INTO analytics_form_delivery_jobs(id, submission_id, owner_user_id, payload_ciphertext, last_error_code)
70043
70486
  VALUES ($1,$2,$3,$4,$5)`,
@@ -70164,7 +70607,7 @@ async function analyticsHealth(siteId, userId) {
70164
70607
  }
70165
70608
  async function refreshAnalyticsDailyRollups(input) {
70166
70609
  const db = getAnalyticsPool();
70167
- const runId = (0, import_node_crypto40.randomUUID)();
70610
+ const runId = (0, import_node_crypto41.randomUUID)();
70168
70611
  await db.query(
70169
70612
  `INSERT INTO analytics_rollup_runs(id, window_start, window_end, status) VALUES ($1, $2, $3, 'running')`,
70170
70613
  [runId, input.start, input.end]
@@ -70361,7 +70804,7 @@ async function createAnalyticsExport(input) {
70361
70804
  `Generated ${(/* @__PURE__ */ new Date()).toISOString()} from the governed ${input.report} report contract.`
70362
70805
  ].join("\n");
70363
70806
  }
70364
- const id = (0, import_node_crypto40.randomUUID)();
70807
+ const id = (0, import_node_crypto41.randomUUID)();
70365
70808
  const inserted = await getAnalyticsPool().query(
70366
70809
  `INSERT INTO analytics_exports(id, site_id, requested_by_user_id, idempotency_key, report, format, filters, content)
70367
70810
  VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8)
@@ -70394,11 +70837,11 @@ async function createAnalyticsExport(input) {
70394
70837
  }
70395
70838
  return describeArtifact(replay.rows[0]);
70396
70839
  }
70397
- var import_node_crypto40, import_pg, AnalyticsRepositoryError, pool2, MAX_ENGAGED_MS, ENGAGED_SESSION_MS, blockedPropertyName, inferredFamilySql, ANALYTICS_CONTENT_SORTS, clickIdKeys;
70840
+ var import_node_crypto41, import_pg, AnalyticsRepositoryError, pool2, MAX_ENGAGED_MS, ENGAGED_SESSION_MS, blockedPropertyName, inferredFamilySql, ANALYTICS_CONTENT_SORTS, clickIdKeys;
70398
70841
  var init_analytics_repository = __esm({
70399
70842
  "src/api/analytics-repository.ts"() {
70400
70843
  "use strict";
70401
- import_node_crypto40 = require("crypto");
70844
+ import_node_crypto41 = require("crypto");
70402
70845
  import_pg = require("pg");
70403
70846
  init_session();
70404
70847
  init_analytics_attribution();
@@ -70637,8 +71080,8 @@ function dashboardCallbackUrl() {
70637
71080
  }
70638
71081
  async function createThorbitConnectUrl(userId) {
70639
71082
  if (!bridgeConfigured()) throw new Error("Thorbit X-Ray account bridge is not configured");
70640
- const state = (0, import_node_crypto41.randomBytes)(32).toString("base64url");
70641
- const stateHash = (0, import_node_crypto41.createHash)("sha256").update(state).digest("hex");
71083
+ const state = (0, import_node_crypto42.randomBytes)(32).toString("base64url");
71084
+ const stateHash = (0, import_node_crypto42.createHash)("sha256").update(state).digest("hex");
70642
71085
  await getAnalyticsPool().query(
70643
71086
  `INSERT INTO analytics_thorbit_connect_states(state_hash,user_id,expires_at)
70644
71087
  VALUES ($1,$2,now()+interval '10 minutes')
@@ -70653,9 +71096,9 @@ async function createThorbitConnectUrl(userId) {
70653
71096
  function verifyThorbitAssertion(token6) {
70654
71097
  const [encoded, signature] = token6.split(".");
70655
71098
  if (!encoded || !signature || !bridgeConfigured()) throw new Error("Invalid Thorbit assertion");
70656
- const expected = (0, import_node_crypto41.createHmac)("sha256", bridgeSecret()).update(encoded).digest();
71099
+ const expected = (0, import_node_crypto42.createHmac)("sha256", bridgeSecret()).update(encoded).digest();
70657
71100
  const actual = Buffer.from(signature, "base64url");
70658
- if (actual.length !== expected.length || !(0, import_node_crypto41.timingSafeEqual)(actual, expected)) {
71101
+ if (actual.length !== expected.length || !(0, import_node_crypto42.timingSafeEqual)(actual, expected)) {
70659
71102
  throw new Error("Invalid Thorbit assertion signature");
70660
71103
  }
70661
71104
  const parsed = AssertionSchema.safeParse(
@@ -70667,7 +71110,7 @@ function verifyThorbitAssertion(token6) {
70667
71110
  return parsed.data.entitlement;
70668
71111
  }
70669
71112
  async function consumeThorbitConnectCallback(input) {
70670
- const stateHash = (0, import_node_crypto41.createHash)("sha256").update(input.state).digest("hex");
71113
+ const stateHash = (0, import_node_crypto42.createHash)("sha256").update(input.state).digest("hex");
70671
71114
  const client2 = await getAnalyticsPool().connect();
70672
71115
  try {
70673
71116
  await client2.query("BEGIN");
@@ -70707,11 +71150,11 @@ async function disconnectAnalyticsEntitlement(userId) {
70707
71150
  [userId]
70708
71151
  );
70709
71152
  }
70710
- var import_node_crypto41, import_zod53, THORBIT_PRODUCT_URL, REFRESH_INTERVAL_MS, ELIGIBLE_GRACE_MS, TRIAL_LENGTH_MS, ThorbitEntitlementSchema, AssertionSchema;
71153
+ var import_node_crypto42, import_zod53, THORBIT_PRODUCT_URL, REFRESH_INTERVAL_MS, ELIGIBLE_GRACE_MS, TRIAL_LENGTH_MS, ThorbitEntitlementSchema, AssertionSchema;
70711
71154
  var init_analytics_entitlement = __esm({
70712
71155
  "src/api/analytics-entitlement.ts"() {
70713
71156
  "use strict";
70714
- import_node_crypto41 = require("crypto");
71157
+ import_node_crypto42 = require("crypto");
70715
71158
  import_zod53 = require("zod");
70716
71159
  init_analytics_repository();
70717
71160
  THORBIT_PRODUCT_URL = "https://thorbit.ai";
@@ -70851,14 +71294,14 @@ function renderPublicForm(form, placementUrl) {
70851
71294
  const brand = form.brand;
70852
71295
  return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>:root{font-family:ui-sans-serif,system-ui;color:${escapeHtml7(brand.textColor)};background:${escapeHtml7(brand.backgroundColor)}}*{box-sizing:border-box}body{margin:0;padding:18px}form{display:grid;gap:14px}label{display:grid;gap:6px;font-size:13px;font-weight:650}.check{grid-template-columns:auto 1fr;align-items:center}.check span{grid-column:2}.check input{grid-column:1;grid-row:1}input,textarea,select{width:100%;min-height:44px;padding:10px 12px;border:1px solid #ccd3df;border-radius:${brand.radius}px;background:white;color:inherit;font:inherit}textarea{min-height:110px;resize:vertical}button{min-height:46px;border:0;border-radius:${brand.radius}px;color:white;background:${escapeHtml7(brand.primaryColor)};font:inherit;font-weight:750;cursor:pointer}.consent{margin:0;color:#667085;font-size:11px;line-height:1.45}.notice{display:none;padding:12px;border-radius:${brand.radius}px;background:#edf8f2;color:#17613c}.hp{position:absolute!important;left:-9999px!important}</style></head><body><form id="mcp-form">${fields}<label class="hp">Website<input name="website" autocomplete="off" tabindex="-1"></label>${form.consent_text ? `<p class="consent">${escapeHtml7(form.consent_text)}</p>` : ""}<button>${escapeHtml7(form.submit_label)}</button><div class="notice" role="status"></div></form><script>(()=>{const form=document.querySelector('#mcp-form'),notice=form.querySelector('.notice'),q=new URLSearchParams(location.search);form.addEventListener('submit',async event=>{event.preventDefault();const button=form.querySelector('button');button.disabled=true;const raw=Object.fromEntries(new FormData(form).entries()),website=String(raw.website||''),clickIds={};delete raw.website;for(const k of ['fbclid','gclid','gbraid','wbraid','ttclid','rdt_cid','msclkid']){const v=q.get(k);if(v)clickIds[k]=v}try{const response=await fetch('/analytics/forms/${escapeHtml7(form.public_id)}/submissions',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({data:raw,website,placementUrl:${JSON.stringify(placementUrl)},visitorId:q.get('visitor')||undefined,sessionId:q.get('session')||undefined,source:q.get('source')||undefined,medium:q.get('medium')||undefined,campaign:q.get('campaign')||undefined,clickIds})});if(!response.ok)throw new Error('submit');notice.textContent=${JSON.stringify(form.success_message)};notice.style.display='block';form.reset();${form.redirect_url ? `setTimeout(()=>{top.location.href=${JSON.stringify(form.redirect_url)}},700);` : ""}}catch{notice.textContent='Your response could not be submitted. Please try again.';notice.style.display='block'}finally{button.disabled=false}})})()</script></body></html>`;
70853
71296
  }
70854
- var import_hono32, import_factory6, import_zod54, import_node_crypto42, import_papaparse4, analyticsApp, auth3, entitlementGuard, ThorbitApiKeySchema, ThorbitCallbackSchema, SiteInputSchema, PixelInputSchema, PixelUpdateSchema, EventSchema, IngestionSchema, ConversionSchema, ExportSchema2, CampaignLinkSchema, FormFieldSchema, FormInputSchema, FormSubmissionSchema, CrmImportSchema, ActivationDestinationSchema, BusinessModelSchema, AdSpendSchema;
71297
+ var import_hono32, import_factory6, import_zod54, import_node_crypto43, import_papaparse4, analyticsApp, auth3, entitlementGuard, ThorbitApiKeySchema, ThorbitCallbackSchema, SiteInputSchema, PixelInputSchema, PixelUpdateSchema, EventSchema, IngestionSchema, ConversionSchema, ExportSchema2, CampaignLinkSchema, FormFieldSchema, FormInputSchema, FormSubmissionSchema, CrmImportSchema, ActivationDestinationSchema, BusinessModelSchema, AdSpendSchema;
70855
71298
  var init_analytics_routes = __esm({
70856
71299
  "src/api/analytics-routes.ts"() {
70857
71300
  "use strict";
70858
71301
  import_hono32 = require("hono");
70859
71302
  import_factory6 = require("hono/factory");
70860
71303
  import_zod54 = require("zod");
70861
- import_node_crypto42 = require("crypto");
71304
+ import_node_crypto43 = require("crypto");
70862
71305
  import_papaparse4 = __toESM(require("papaparse"), 1);
70863
71306
  init_api_auth();
70864
71307
  init_db();
@@ -71189,7 +71632,7 @@ var init_analytics_routes = __esm({
71189
71632
  const lastName = typeof data.last_name === "string" ? data.last_name.trim() : "";
71190
71633
  const fullName = [firstName, lastName].filter(Boolean).join(" ") || (typeof data.name === "string" ? data.name.trim() : "") || email || "Website lead";
71191
71634
  const identitySeed = email || `${fullName}:${Date.now()}`;
71192
- const suffix2 = (0, import_node_crypto42.createHash)("sha256").update(identitySeed).digest("hex").slice(0, 12);
71635
+ const suffix2 = (0, import_node_crypto43.createHash)("sha256").update(identitySeed).digest("hex").slice(0, 12);
71193
71636
  const path6 = `Leads/person-${suffix2}`;
71194
71637
  const { key: memoryKey, error: memoryKeyError } = await getOrCreateUserMemoryKey(user);
71195
71638
  const existing = memoryKey ? await memoryCall("getTool", { vault: "People", path: path6 }, memoryKey) : { ok: false, error: memoryKeyError || "memory_credential_unavailable" };
@@ -71288,9 +71731,9 @@ Submitted the published form.
71288
71731
  occurredAt: (/* @__PURE__ */ new Date()).toISOString()
71289
71732
  });
71290
71733
  const match = {
71291
- ...email ? { emailSha256: (0, import_node_crypto42.createHash)("sha256").update(email).digest("hex") } : {},
71734
+ ...email ? { emailSha256: (0, import_node_crypto43.createHash)("sha256").update(email).digest("hex") } : {},
71292
71735
  ...phone ? {
71293
- phoneSha256: (0, import_node_crypto42.createHash)("sha256").update(phone.replace(/\D/g, "")).digest("hex")
71736
+ phoneSha256: (0, import_node_crypto43.createHash)("sha256").update(phone.replace(/\D/g, "")).digest("hex")
71294
71737
  } : {}
71295
71738
  };
71296
71739
  const activationQueued = await queueAnalyticsActivation({
@@ -71718,7 +72161,7 @@ Submitted the published form.
71718
72161
  rejectedCount += 1;
71719
72162
  continue;
71720
72163
  }
71721
- const digest2 = (0, import_node_crypto42.createHash)("sha256").update(
72164
+ const digest2 = (0, import_node_crypto43.createHash)("sha256").update(
71722
72165
  `${parsed.data.sourceSystem}:${externalId || email || phone || `${fullName}:${index}`}`
71723
72166
  ).digest("hex").slice(0, 16);
71724
72167
  const path6 = `Leads/person-${digest2}`;
@@ -72176,13 +72619,13 @@ var init_analytics_delivery = __esm({
72176
72619
 
72177
72620
  // src/api/scheduled-artifact-owner.ts
72178
72621
  function scheduledArtifactOwnerIdForApiKey(apiKey) {
72179
- return (0, import_node_crypto43.createHash)("sha256").update(apiKey).digest("hex").slice(0, 24);
72622
+ return (0, import_node_crypto44.createHash)("sha256").update(apiKey).digest("hex").slice(0, 24);
72180
72623
  }
72181
- var import_node_crypto43;
72624
+ var import_node_crypto44;
72182
72625
  var init_scheduled_artifact_owner = __esm({
72183
72626
  "src/api/scheduled-artifact-owner.ts"() {
72184
72627
  "use strict";
72185
- import_node_crypto43 = require("crypto");
72628
+ import_node_crypto44 = require("crypto");
72186
72629
  }
72187
72630
  });
72188
72631
 
@@ -72239,7 +72682,7 @@ async function ensureScheduledRunViewLinksSchema() {
72239
72682
  schemaReady2 = true;
72240
72683
  }
72241
72684
  function tokenHash2(token6) {
72242
- return (0, import_node_crypto44.createHash)("sha256").update(token6).digest("hex");
72685
+ return (0, import_node_crypto45.createHash)("sha256").update(token6).digest("hex");
72243
72686
  }
72244
72687
  function mapRow(row) {
72245
72688
  return {
@@ -72259,9 +72702,9 @@ function mapRow(row) {
72259
72702
  async function createScheduledRunViewLink(input) {
72260
72703
  await ensureScheduledRunViewLinksSchema();
72261
72704
  const now = input.now ?? /* @__PURE__ */ new Date();
72262
- const token6 = (0, import_node_crypto44.randomBytes)(32).toString("base64url");
72705
+ const token6 = (0, import_node_crypto45.randomBytes)(32).toString("base64url");
72263
72706
  const record = {
72264
- shareId: (0, import_node_crypto44.randomUUID)(),
72707
+ shareId: (0, import_node_crypto45.randomUUID)(),
72265
72708
  ownerId: input.ownerId,
72266
72709
  runId: input.runId,
72267
72710
  artifactId: input.artifactId,
@@ -72324,11 +72767,11 @@ async function revokeScheduledRunViewLink(ownerId, runId, shareId, now = /* @__P
72324
72767
  });
72325
72768
  return result.rowsAffected > 0;
72326
72769
  }
72327
- var import_node_crypto44, schemaReady2;
72770
+ var import_node_crypto45, schemaReady2;
72328
72771
  var init_scheduled_run_view_links = __esm({
72329
72772
  "src/api/scheduled-run-view-links.ts"() {
72330
72773
  "use strict";
72331
- import_node_crypto44 = require("crypto");
72774
+ import_node_crypto45 = require("crypto");
72332
72775
  init_db();
72333
72776
  schemaReady2 = false;
72334
72777
  }
@@ -72394,15 +72837,15 @@ ${section(flags.finalCta && Boolean(finalCta), `<section class="section cta" id=
72394
72837
  html,
72395
72838
  filename: `${input.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "personal-authority"}.html`,
72396
72839
  bytes,
72397
- sha256: (0, import_node_crypto45.createHash)("sha256").update(html).digest("hex"),
72840
+ sha256: (0, import_node_crypto46.createHash)("sha256").update(html).digest("hex"),
72398
72841
  generatedAt: generatedAtIso
72399
72842
  };
72400
72843
  }
72401
- var import_node_crypto45;
72844
+ var import_node_crypto46;
72402
72845
  var init_render2 = __esm({
72403
72846
  "src/personal-authority/render.ts"() {
72404
72847
  "use strict";
72405
- import_node_crypto45 = require("crypto");
72848
+ import_node_crypto46 = require("crypto");
72406
72849
  }
72407
72850
  });
72408
72851
 
@@ -72505,15 +72948,15 @@ ${section2(flags.finalCta && Boolean(finalCta), `<section class="section cta" id
72505
72948
  html,
72506
72949
  filename: `${input.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "personal-authority"}.html`,
72507
72950
  bytes,
72508
- sha256: (0, import_node_crypto46.createHash)("sha256").update(html).digest("hex"),
72951
+ sha256: (0, import_node_crypto47.createHash)("sha256").update(html).digest("hex"),
72509
72952
  generatedAt: generatedAtIso
72510
72953
  };
72511
72954
  }
72512
- var import_node_crypto46, FONT_STACKS;
72955
+ var import_node_crypto47, FONT_STACKS;
72513
72956
  var init_render_v2 = __esm({
72514
72957
  "src/personal-authority/render-v2.ts"() {
72515
72958
  "use strict";
72516
- import_node_crypto46 = require("crypto");
72959
+ import_node_crypto47 = require("crypto");
72517
72960
  init_contracts();
72518
72961
  FONT_STACKS = Object.freeze({
72519
72962
  "editorial-serif": "Iowan Old Style, Baskerville, Times New Roman, serif",
@@ -72629,15 +73072,15 @@ body{overflow-x:hidden}.lead-story__body,.lead-support__item,.story-card__body{m
72629
73072
  @media(max-width:620px){.desk-section__grid--panorama,.desk-section__grid--mosaic,.desk-section__grid--tiles{grid-template-columns:1fr}.story-card--panorama{display:block}.story-card--panorama h3,.story-card--mosaic-lead h3{font-size:34px}.story-card--mosaic-lead{grid-column:auto;grid-row:auto}.desk-section__grid--tiles>.story-card:nth-child(3n+2){margin-top:0}}
72630
73073
  </style></head><body data-theme="${escapeHtml10(config.theme)}" id="top" data-scheduled-renderer="newsroom_publisher_v1">${ticker}${masthead}${navigation}<main>${leadGrid}${latest}${sections}${newsletter}${pressRoom}${trustCenter}</main>${trustFooter}</body></html>`;
72631
73074
  const bytes = Buffer.byteLength(html);
72632
- const sha2563 = (0, import_node_crypto47.createHash)("sha256").update(html).digest("hex");
73075
+ const sha2563 = (0, import_node_crypto48.createHash)("sha256").update(html).digest("hex");
72633
73076
  const filename = `${brand.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "newsroom"}-news-site.html`;
72634
73077
  return { html, filename, bytes, sha256: sha2563, generatedAt: generatedAtIso };
72635
73078
  }
72636
- var import_node_crypto47;
73079
+ var import_node_crypto48;
72637
73080
  var init_render3 = __esm({
72638
73081
  "src/newsroom-publisher/render.ts"() {
72639
73082
  "use strict";
72640
- import_node_crypto47 = require("crypto");
73083
+ import_node_crypto48 = require("crypto");
72641
73084
  }
72642
73085
  });
72643
73086
 
@@ -72729,7 +73172,7 @@ function renderBlogArticleV1(input, config, generatedAt) {
72729
73172
  :root{--ink:#10213d;--ink2:#1c355a;--blue:#2563eb;--blue2:#1749b6;--mint:#45d5aa;--paper:#f7f5f0;--line:#dce4ef;--card:#fff;--body:#475569;--serif:Iowan Old Style,Palatino Linotype,Palatino,Georgia,serif;--sans:Inter,Avenir Next,ui-sans-serif,system-ui,sans-serif}body[data-theme="slate"]{--ink:#1e293b;--ink2:#334155;--blue:#475569;--blue2:#1e293b;--mint:#94a3b8;--paper:#f8fafc}body[data-theme="forest"]{--ink:#12352e;--ink2:#23584c;--blue:#147d64;--blue2:#0c5b48;--mint:#79d8bd;--paper:#f4f8f3}*{box-sizing:border-box}html{scroll-behavior:smooth;background:var(--paper)}body{margin:0;color:#252a33;background:var(--paper);font-family:var(--sans);line-height:1.6}a{color:inherit}button{font:inherit}button:focus-visible,a:focus-visible{outline:3px solid var(--mint);outline-offset:3px}.progress{position:fixed;z-index:50;top:0;left:0;height:4px;width:0;background:var(--mint)}.hero{position:relative;overflow:hidden;color:#fff;background:var(--ink)}.hero:after{position:absolute;right:-110px;top:-150px;width:420px;height:420px;border:1px solid #ffffff1a;border-radius:50%;content:""}.hero-inner{position:relative;z-index:1;max-width:1240px;margin:auto;padding:58px 42px 126px}.breadcrumb{display:flex;gap:8px;margin-bottom:34px;color:#dbeafecc;font-size:14px}.eyebrow{display:block;color:var(--blue);font-size:12px;font-weight:800;letter-spacing:.16em;text-transform:uppercase}.hero .eyebrow{color:var(--mint)}h1,h2,h3,p{margin-top:0}.hero h1{max-width:1000px;margin:14px 0 24px;font:700 clamp(44px,6vw,78px)/.98 var(--serif);letter-spacing:-.035em}.hero .dek{max-width:780px;color:#dbeafe;font-size:20px}.contributors{margin-top:30px;color:#eff6ff;font-size:14px}.contributors a{font-weight:800}.meta{display:flex;flex-wrap:wrap;gap:12px;margin-top:6px;color:#dbeafecc}.layout{position:relative;z-index:2;display:grid;grid-template-columns:minmax(0,760px) 290px;gap:64px;max-width:1240px;margin:-78px auto 0;padding:0 42px 80px}.main{min-width:0}.takeaways{padding:38px 44px;border-radius:24px;background:#fff;box-shadow:0 24px 70px #10213d24}.takeaways h2{margin:5px 0 20px;font:700 32px/1.1 var(--serif);color:var(--ink)}.takeaways ul{display:grid;gap:16px;margin:0;padding:0;list-style:none}.takeaways li{position:relative;padding-left:26px;color:var(--body);font-size:17px}.takeaways li:before{position:absolute;left:2px;top:.7em;width:8px;height:8px;border-radius:50%;background:var(--blue);content:""}.intro{padding:40px 0 4px}.intro p,.article-section>p{margin-bottom:22px;color:var(--body);font:400 18px/1.75 var(--sans)}.article-section{scroll-margin-top:28px;padding-top:45px}.article-section h2,.faq h2{margin:6px 0 20px;color:var(--ink);font:700 38px/1.08 var(--serif);letter-spacing:-.02em}.article-section ul{display:grid;gap:10px;margin:22px 0;padding-left:24px;color:var(--body);font-size:17px}.article-section li::marker{color:var(--blue)}.callout{margin:34px 0;padding:24px 28px;border-left:4px solid var(--blue);border-radius:0 16px 16px 0;background:#fff}.callout span{color:var(--blue);font-size:11px;font-weight:800;letter-spacing:.13em;text-transform:uppercase}.callout h3{margin:5px 0 8px;color:var(--ink);font:700 24px/1.15 var(--serif)}.callout p{margin:0;color:var(--body)}.rail{padding-top:0}.sidebar-media{overflow:hidden;margin:0 0 18px;border:1px solid #dbeafe;border-radius:16px;background:#fff}.sidebar-media img,.sidebar-media svg{display:block;width:100%;aspect-ratio:16/9;object-fit:cover}.sidebar-media figcaption{padding:10px 13px;color:#64748b;font-size:11px}.disclosure{margin-bottom:28px;border-top:1px solid #dbeafe;border-bottom:1px solid #dbeafe}.disclosure button{display:flex;width:100%;align-items:center;justify-content:space-between;gap:14px;padding:15px 0;border:0;color:#64748b;background:transparent;text-align:left;font-size:12px;cursor:pointer}.disclosure button b{color:var(--blue);font-size:18px;transition:transform .2s}.disclosure button[aria-expanded="true"] b{transform:rotate(180deg)}.disclosure p{padding:0 0 16px;margin:0;color:#64748b;font-size:12px}.toc{position:sticky;top:30px;padding:22px;border:1px solid var(--line);border-radius:16px;background:#fff;box-shadow:0 2px 5px #10213d12}.toc header{display:flex;align-items:center;justify-content:space-between}.toc header strong{color:var(--ink);font:700 21px/1 var(--serif)}.toc header button{width:34px;height:34px;border:0;border-radius:50%;color:var(--blue);background:transparent;cursor:pointer}.toc nav{display:grid;margin-top:14px}.toc nav a{display:flex;align-items:flex-start;gap:11px;padding:8px 0;color:#64748b;text-decoration:none;font-size:13px;line-height:1.35}.toc nav i{width:8px;height:8px;margin-top:5px;border:2px solid #bfdbfe;border-radius:50%}.toc nav a[aria-current="location"]{color:var(--ink);font-weight:800}.toc nav a[aria-current="location"] i{border-color:var(--blue);background:var(--blue)}.back-top{width:100%;margin-top:18px;padding:16px 0 0;border:0;border-top:1px solid var(--line);color:var(--blue);background:transparent;text-align:right;font-size:13px;font-weight:800;cursor:pointer}.faq{scroll-margin-top:28px;padding-top:64px}.faq>div{border-top:1px solid var(--line);border-bottom:1px solid var(--line)}.faq article+article{border-top:1px solid var(--line)}.faq h3{margin:0}.faq h3 button{display:flex;width:100%;align-items:center;justify-content:space-between;gap:18px;padding:19px 0;border:0;color:var(--ink);background:transparent;text-align:left;font-weight:800;cursor:pointer}.faq h3 b{color:var(--blue);font-size:22px}.faq article>p{padding:0 0 20px;margin:0;color:var(--body);font-size:16px}.author-card{margin-top:64px;padding:32px 36px;border-radius:18px;background:#fff}.article-actions{display:flex;flex-wrap:wrap;justify-content:space-between;gap:14px}.article-actions button{display:inline-flex;min-height:46px;align-items:center;justify-content:center;gap:8px;padding:8px 18px;border:2px solid var(--blue);border-radius:999px;color:var(--ink);background:#fff;font-weight:800;cursor:pointer}.article-actions button:hover{color:#fff;background:var(--blue)}.action-status{min-height:20px;margin:8px 0 0;color:var(--blue);text-align:right;font-size:13px;font-weight:700}.author-grid{display:grid;grid-template-columns:auto 1fr auto;gap:22px;align-items:center;margin-top:24px}.author-portrait{width:88px;height:88px;border-radius:50%;object-fit:cover}.author-initials{display:grid;place-items:center;color:var(--ink);background:#fecdd3;font-size:22px;font-weight:900}.author-grid span{color:#64748b;font-size:13px}.author-grid h2{margin:2px 0;color:var(--ink);font:700 27px/1 var(--serif)}.author-grid h2 a{text-decoration:none}.author-grid p{margin:8px 0;color:#64748b;font-size:14px}.author-grid nav{display:flex;flex-wrap:wrap;gap:14px}.author-grid nav a{color:#64748b;font-size:12px}.author-more{display:inline-flex;min-height:48px;align-items:center;justify-content:center;padding:10px 24px;border:2px solid var(--blue);color:var(--blue);text-align:center;text-decoration:none;font-size:13px;font-weight:800}.author-bio{margin:24px 0 0!important;padding-top:20px;border-top:1px solid var(--line);color:var(--body);font-size:16px}dialog{width:min(720px,calc(100% - 30px));max-height:calc(100dvh - 30px);padding:0;border:0;border-radius:18px;color:var(--ink);background:#fff;box-shadow:0 24px 70px #10213d38}dialog::backdrop{background:#0f172a7a;backdrop-filter:blur(2px)}dialog>div{position:relative;padding:30px}dialog h2{margin:0 50px 2px 0;font:700 30px/1.1 var(--serif)}dialog>div>p{color:#64748b}.dialog-close{position:absolute;top:13px;right:13px;width:44px;height:44px;border:0;border-radius:50%;color:var(--blue);background:transparent;font-size:29px;cursor:pointer}dialog section{border-top:1px solid var(--line)}dialog section button{display:block;width:100%;padding:17px 5px;border:0;border-bottom:1px solid var(--line);color:var(--body);background:#fff;text-align:left;line-height:1.65;cursor:pointer}.citation-status{min-height:22px;margin:14px 0 0!important;color:var(--blue)!important;font-size:13px;font-weight:800}.generated{max-width:1156px;margin:-52px auto 50px;padding:0 42px;color:#94a3b8;font-size:11px}@media(max-width:900px){.layout{display:block}.rail{display:none}.hero-inner{padding-bottom:116px}}@media(max-width:620px){.hero-inner{padding:36px 20px 104px}.hero h1{font-size:46px}.hero .dek{font-size:17px}.layout{margin-top:-70px;padding:0 16px 54px}.takeaways{padding:28px 24px}.article-section h2,.faq h2{font-size:32px}.author-card{padding:24px}.article-actions{display:grid;justify-content:start}.author-grid{grid-template-columns:1fr;gap:12px}.author-more{width:100%}dialog>div{padding:24px}.generated{padding:0 20px}}
72730
73173
  </style></head><body data-theme="${escapeHtml11(config.theme)}" data-scheduled-renderer="blog_article_v1"><div class="progress" aria-hidden="true"></div><header class="hero"><div class="hero-inner"><nav class="breadcrumb" aria-label="Breadcrumb"><a href="${safeHref4(input.canonicalUrl)}">${escapeHtml11(input.category)}</a><span>/</span><span>Article</span></nav><span class="eyebrow">${escapeHtml11(input.eyebrow)}</span><h1>${escapeHtml11(input.title)}</h1>${input.dek ? `<p class="dek">${escapeHtml11(input.dek)}</p>` : ""}<div class="contributors">${contributorText}<div class="meta"><time datetime="${escapeHtml11(articleDate)}">Updated ${formatDate2(articleDate)}</time><span>\u2022</span><span>${input.readTimeMinutes} min read</span>${input.reviewer ? "<span>\u2022</span><strong>Expert reviewed</strong>" : ""}</div></div></div></header><main class="layout"><article class="main"><section class="takeaways"><span class="eyebrow">The short answer</span><h2>Key takeaways</h2><ul>${input.keyTakeaways.map((item) => `<li>${escapeHtml11(item)}</li>`).join("")}</ul></section><section class="intro">${input.introduction.map((paragraph) => `<p>${escapeHtml11(paragraph)}</p>`).join("")}</section>${sections}${faq}${author}</article><div class="rail">${media}${disclosure}${toc}</div></main>${config.showGeneratedAt ? `<p class="generated">Rendered ${formatDate2(generatedAtIso)} by MCP Scraper.</p>` : ""}${citationDialog}<script>(()=>{const citations=${jsonForScript(citationValues)};const canonical=${jsonForScript(input.canonicalUrl)};const title=${jsonForScript(input.title)};const progress=document.querySelector('.progress');const tocLinks=[...document.querySelectorAll('.toc nav a')];const targets=tocLinks.map(link=>document.querySelector(link.hash)).filter(Boolean);const update=()=>{const max=document.documentElement.scrollHeight-innerHeight;if(progress)progress.style.width=(max>0?scrollY/max*100:0)+'%';let active=targets[0]?.id;for(const target of targets){if(target.getBoundingClientRect().top<=innerHeight*.55)active=target.id}tocLinks.forEach(link=>link.toggleAttribute('aria-current',link.hash==='#'+active));tocLinks.forEach(link=>{if(link.hash==='#'+active)link.setAttribute('aria-current','location');else link.removeAttribute('aria-current')})};addEventListener('scroll',update,{passive:true});addEventListener('resize',update);update();document.querySelectorAll('.faq h3 button').forEach(button=>button.addEventListener('click',()=>{const panel=document.getElementById(button.getAttribute('aria-controls'));const open=button.getAttribute('aria-expanded')==='true';button.setAttribute('aria-expanded',String(!open));panel.hidden=open;button.querySelector('b').textContent=open?'+':'\u2212'}));const disclosure=document.querySelector('.disclosure button');disclosure?.addEventListener('click',()=>{const panel=document.getElementById(disclosure.getAttribute('aria-controls'));const open=disclosure.getAttribute('aria-expanded')==='true';disclosure.setAttribute('aria-expanded',String(!open));panel.hidden=open});const tocToggle=document.querySelector('.toc header button');tocToggle?.addEventListener('click',()=>{const nav=document.getElementById('toc-links');const open=tocToggle.getAttribute('aria-expanded')==='true';tocToggle.setAttribute('aria-expanded',String(!open));nav.hidden=open;tocToggle.textContent=open?'\u2304':'\u2303'});document.querySelector('.back-top')?.addEventListener('click',()=>scrollTo({top:0,behavior:'smooth'}));const copy=async text=>{try{await navigator.clipboard.writeText(text)}catch{const area=document.createElement('textarea');area.value=text;area.style.position='fixed';area.style.opacity='0';document.body.append(area);area.select();document.execCommand('copy');area.remove()}};const dialog=document.getElementById('citation-dialog');document.querySelector('[data-cite]')?.addEventListener('click',()=>dialog.showModal());document.querySelector('.dialog-close')?.addEventListener('click',()=>dialog.close());dialog?.addEventListener('click',event=>{if(event.target===dialog)dialog.close()});document.querySelectorAll('[data-citation]').forEach(button=>button.addEventListener('click',async()=>{const style=button.dataset.citation;await copy(citations[style]);document.querySelector('.citation-status').textContent=style+' citation copied to clipboard.'}));document.querySelector('[data-share]')?.addEventListener('click',async()=>{const status=document.querySelector('.action-status');if(navigator.share){try{await navigator.share({title,url:canonical});status.textContent='Sharing options opened.';return}catch(error){if(error.name==='AbortError')return}}await copy(canonical);status.textContent='Article link copied to clipboard.'})})()</script></body></html>`;
72731
73174
  const bytes = Buffer.byteLength(html);
72732
- const sha2563 = (0, import_node_crypto48.createHash)("sha256").update(html).digest("hex");
73175
+ const sha2563 = (0, import_node_crypto49.createHash)("sha256").update(html).digest("hex");
72733
73176
  return {
72734
73177
  html,
72735
73178
  filename: "blog-article.html",
@@ -72739,11 +73182,11 @@ function renderBlogArticleV1(input, config, generatedAt) {
72739
73182
  generatedAt: generatedAtIso
72740
73183
  };
72741
73184
  }
72742
- var import_node_crypto48;
73185
+ var import_node_crypto49;
72743
73186
  var init_render4 = __esm({
72744
73187
  "src/blog-article/render.ts"() {
72745
73188
  "use strict";
72746
- import_node_crypto48 = require("crypto");
73189
+ import_node_crypto49 = require("crypto");
72747
73190
  }
72748
73191
  });
72749
73192
 
@@ -73074,7 +73517,7 @@ function policy5() {
73074
73517
  };
73075
73518
  }
73076
73519
  function runStorageSegment(runId) {
73077
- return /^[a-zA-Z0-9_-]{1,160}$/.test(runId) ? runId : `run-${(0, import_node_crypto49.createHash)("sha256").update(runId).digest("hex").slice(0, 32)}`;
73520
+ return /^[a-zA-Z0-9_-]{1,160}$/.test(runId) ? runId : `run-${(0, import_node_crypto50.createHash)("sha256").update(runId).digest("hex").slice(0, 32)}`;
73078
73521
  }
73079
73522
  async function createScheduledRunArtifact(args) {
73080
73523
  if (args.rendered.bytes > SCHEDULED_RUN_ARTIFACT_MAX_BYTES) {
@@ -73115,12 +73558,12 @@ async function readScheduledRunArtifact(args) {
73115
73558
  if (!window2 || window2.nextOffset !== null || window2.totalBytes > SCHEDULED_RUN_ARTIFACT_MAX_BYTES) return null;
73116
73559
  return window2.text;
73117
73560
  }
73118
- var import_node_crypto49, SCHEDULED_RUN_ARTIFACT_PREFIX, SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS, SCHEDULED_RUN_ARTIFACT_MAX_BYTES;
73561
+ var import_node_crypto50, SCHEDULED_RUN_ARTIFACT_PREFIX, SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS, SCHEDULED_RUN_ARTIFACT_MAX_BYTES;
73119
73562
  var init_scheduled_run_artifact_store = __esm({
73120
73563
  "src/scheduled-artifacts/scheduled-run-artifact-store.ts"() {
73121
73564
  "use strict";
73122
73565
  init_private_artifacts();
73123
- import_node_crypto49 = require("crypto");
73566
+ import_node_crypto50 = require("crypto");
73124
73567
  SCHEDULED_RUN_ARTIFACT_PREFIX = "scheduled-run-artifacts/";
73125
73568
  SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS = 15 * 60 * 1e3;
73126
73569
  SCHEDULED_RUN_ARTIFACT_MAX_BYTES = 2e6;
@@ -73338,7 +73781,7 @@ async function reconcileDiscoveredNangoConnections(identity, discovered) {
73338
73781
  updated_at = excluded.updated_at
73339
73782
  `,
73340
73783
  args: [
73341
- (0, import_node_crypto50.randomUUID)(),
73784
+ (0, import_node_crypto51.randomUUID)(),
73342
73785
  userId,
73343
73786
  connection.providerConfigKey,
73344
73787
  connection.provider,
@@ -73409,7 +73852,7 @@ async function recordServiceConnectionHealth(args) {
73409
73852
  });
73410
73853
  await getDb().execute({
73411
73854
  sql: `INSERT INTO service_connection_health_events (id, connection_id, operational_status, failure_code, retryable, evidence_source) VALUES (?, ?, ?, ?, ?, ?)`,
73412
- args: [(0, import_node_crypto50.randomUUID)(), args.connectionId, args.operationalStatus, args.failureCode ?? null, args.retryable == null ? null : args.retryable ? 1 : 0, args.evidenceSource]
73855
+ args: [(0, import_node_crypto51.randomUUID)(), args.connectionId, args.operationalStatus, args.failureCode ?? null, args.retryable == null ? null : args.retryable ? 1 : 0, args.evidenceSource]
73413
73856
  });
73414
73857
  }
73415
73858
  async function setServiceConnectionActions(identity, connectionId, enabled) {
@@ -73449,7 +73892,7 @@ async function claimServiceConnectionAction(args) {
73449
73892
  if (!connection) throw new Error("service_connection_not_found");
73450
73893
  const inserted = await getDb().execute({
73451
73894
  sql: `INSERT OR IGNORE INTO service_connection_action_audit (id, connection_id, user_id, tool, request_id, status, request_digest) VALUES (?, ?, ?, ?, ?, 'started', ?)`,
73452
- args: [(0, import_node_crypto50.randomUUID)(), connection.id, connection.userId, args.tool, args.requestId, args.requestDigest]
73895
+ args: [(0, import_node_crypto51.randomUUID)(), connection.id, connection.userId, args.tool, args.requestId, args.requestDigest]
73453
73896
  });
73454
73897
  if (Number(inserted.rowsAffected ?? 0) === 1) return { claimed: true };
73455
73898
  const existing = await getDb().execute({
@@ -73474,11 +73917,11 @@ async function claimServiceConnectionAction(args) {
73474
73917
  ...result !== void 0 ? { result } : {}
73475
73918
  };
73476
73919
  }
73477
- var import_node_crypto50, schemaReady3, schemaDb4;
73920
+ var import_node_crypto51, schemaReady3, schemaDb4;
73478
73921
  var init_service_connections = __esm({
73479
73922
  "src/api/service-connections.ts"() {
73480
73923
  "use strict";
73481
- import_node_crypto50 = require("crypto");
73924
+ import_node_crypto51 = require("crypto");
73482
73925
  init_db();
73483
73926
  schemaReady3 = null;
73484
73927
  schemaDb4 = null;
@@ -73492,8 +73935,8 @@ function signingSecret() {
73492
73935
  return secret2;
73493
73936
  }
73494
73937
  function schedulerIntegrationSignature(args) {
73495
- const bodyHash = (0, import_node_crypto51.createHash)("sha256").update(args.body).digest("hex");
73496
- return (0, import_node_crypto51.createHmac)("sha256", args.secret).update(`${args.method.toUpperCase()}
73938
+ const bodyHash = (0, import_node_crypto52.createHash)("sha256").update(args.body).digest("hex");
73939
+ return (0, import_node_crypto52.createHmac)("sha256", args.secret).update(`${args.method.toUpperCase()}
73497
73940
  ${args.path}
73498
73941
  ${args.timestamp}
73499
73942
  ${args.nonce}
@@ -73540,17 +73983,17 @@ async function verifySchedulerIntegrationRequest(request, rawBody) {
73540
73983
  });
73541
73984
  const suppliedBytes = Buffer.from(signature, "hex");
73542
73985
  const expectedBytes = Buffer.from(expected, "hex");
73543
- if (suppliedBytes.length !== expectedBytes.length || !(0, import_node_crypto51.timingSafeEqual)(suppliedBytes, expectedBytes)) {
73986
+ if (suppliedBytes.length !== expectedBytes.length || !(0, import_node_crypto52.timingSafeEqual)(suppliedBytes, expectedBytes)) {
73544
73987
  throw new SchedulerIntegrationAuthError("invalid_signature");
73545
73988
  }
73546
73989
  await claimNonce(nonce, timestampMs);
73547
73990
  return { requestId };
73548
73991
  }
73549
- var import_node_crypto51, MAX_CLOCK_SKEW_MS, SchedulerIntegrationAuthError;
73992
+ var import_node_crypto52, MAX_CLOCK_SKEW_MS, SchedulerIntegrationAuthError;
73550
73993
  var init_scheduler_integration_auth = __esm({
73551
73994
  "src/api/scheduler-integration-auth.ts"() {
73552
73995
  "use strict";
73553
- import_node_crypto51 = require("crypto");
73996
+ import_node_crypto52 = require("crypto");
73554
73997
  init_db();
73555
73998
  init_service_connections();
73556
73999
  MAX_CLOCK_SKEW_MS = 5 * 60 * 1e3;
@@ -73858,7 +74301,7 @@ async function fetchMedia(rawUrl, expectedType) {
73858
74301
  finalUrl: checked.parsed.href,
73859
74302
  width: dimensions?.width ?? null,
73860
74303
  height: dimensions?.height ?? null,
73861
- sha256: (0, import_node_crypto52.createHash)("sha256").update(bytes).digest("hex")
74304
+ sha256: (0, import_node_crypto53.createHash)("sha256").update(bytes).digest("hex")
73862
74305
  };
73863
74306
  }
73864
74307
  throw new Error("media_redirect_rejected");
@@ -73972,7 +74415,7 @@ async function packagePageMedia(args) {
73972
74415
  files.push({ path: "summary.json", content: Buffer.from(JSON.stringify(summary, null, 2)) });
73973
74416
  files.push({ path: "media.jsonl", content: Buffer.from(cleanAssets.map((asset) => JSON.stringify(asset)).join("\n") + "\n") });
73974
74417
  const archive = await zipBuffer2(files);
73975
- const id = (0, import_node_crypto52.randomBytes)(6).toString("hex");
74418
+ const id = (0, import_node_crypto53.randomBytes)(6).toString("hex");
73976
74419
  const pointer = await createPrivateArtifact({
73977
74420
  policy: policy6(),
73978
74421
  ownerId: args.ownerId,
@@ -73985,11 +74428,11 @@ async function packagePageMedia(args) {
73985
74428
  const localPath = token5() ? null : (0, import_node_path21.join)(process.env.MCP_SCRAPER_OUTPUT_DIR?.trim() || (0, import_node_path21.join)((0, import_node_os14.homedir)(), "Downloads", "mcp-scraper"), "blobs", pointer.artifactId);
73986
74429
  return { media: args.media, artifact: { ...pointer, localPath } };
73987
74430
  }
73988
- var import_node_crypto52, import_node_os14, import_node_path21, import_p_limit7, import_yazl3, PAGE_MEDIA_ARTIFACT_PREFIX, PAGE_MEDIA_ARTIFACT_TTL_MS, PAGE_MEDIA_DOWNLOAD_TTL_MS, MAX_FILE_BYTES, MAX_ARCHIVE_MEDIA_BYTES, MAX_INLINE_IMAGE_BYTES2, MAX_INLINE_TOTAL_BYTES2, DOWNLOAD_CONCURRENCY2, MAX_REDIRECTS3;
74431
+ var import_node_crypto53, import_node_os14, import_node_path21, import_p_limit7, import_yazl3, PAGE_MEDIA_ARTIFACT_PREFIX, PAGE_MEDIA_ARTIFACT_TTL_MS, PAGE_MEDIA_DOWNLOAD_TTL_MS, MAX_FILE_BYTES, MAX_ARCHIVE_MEDIA_BYTES, MAX_INLINE_IMAGE_BYTES2, MAX_INLINE_TOTAL_BYTES2, DOWNLOAD_CONCURRENCY2, MAX_REDIRECTS3;
73989
74432
  var init_page_media_artifacts = __esm({
73990
74433
  "src/api/page-media-artifacts.ts"() {
73991
74434
  "use strict";
73992
- import_node_crypto52 = require("crypto");
74435
+ import_node_crypto53 = require("crypto");
73993
74436
  import_node_os14 = require("os");
73994
74437
  import_node_path21 = require("path");
73995
74438
  import_p_limit7 = __toESM(require("p-limit"), 1);
@@ -74083,7 +74526,7 @@ var init_site_extract_reconciliation = __esm({
74083
74526
 
74084
74527
  // src/api/page-diff.ts
74085
74528
  function sha256Hex(value) {
74086
- return (0, import_node_crypto53.createHash)("sha256").update(value).digest("hex");
74529
+ return (0, import_node_crypto54.createHash)("sha256").update(value).digest("hex");
74087
74530
  }
74088
74531
  function truncateForStorage(value, maxChars = MAX_SNAPSHOT_CONTENT_CHARS) {
74089
74532
  if (value.length <= maxChars) return { value, truncated: false };
@@ -74140,11 +74583,11 @@ function diffPageContent(oldContent, newContent) {
74140
74583
  totalChangedLineCount
74141
74584
  };
74142
74585
  }
74143
- var import_node_crypto53, import_diff, MAX_SNAPSHOT_CONTENT_CHARS, MAX_DIFF_HUNKS, MAX_DIFF_LINES_PER_RESPONSE;
74586
+ var import_node_crypto54, import_diff, MAX_SNAPSHOT_CONTENT_CHARS, MAX_DIFF_HUNKS, MAX_DIFF_LINES_PER_RESPONSE;
74144
74587
  var init_page_diff = __esm({
74145
74588
  "src/api/page-diff.ts"() {
74146
74589
  "use strict";
74147
- import_node_crypto53 = require("crypto");
74590
+ import_node_crypto54 = require("crypto");
74148
74591
  import_diff = require("diff");
74149
74592
  MAX_SNAPSHOT_CONTENT_CHARS = 25e4;
74150
74593
  MAX_DIFF_HUNKS = 200;
@@ -74218,7 +74661,7 @@ var init_scrape_vault_sink = __esm({
74218
74661
 
74219
74662
  // src/api/scrape-image-sink.ts
74220
74663
  function idempotencyKey2(userId, vault, input) {
74221
- return `scrape-image-${(0, import_node_crypto54.createHash)("sha256").update(`${userId}\0${vault}\0${input.sourceKind}\0${input.sourceUrl}\0${input.imageUrl ?? ""}\0${input.imageBase64 ?? ""}`).digest("hex")}`;
74664
+ return `scrape-image-${(0, import_node_crypto55.createHash)("sha256").update(`${userId}\0${vault}\0${input.sourceKind}\0${input.sourceUrl}\0${input.imageUrl ?? ""}\0${input.imageBase64 ?? ""}`).digest("hex")}`;
74222
74665
  }
74223
74666
  async function persistScrapeImagesToMemory(user, inputs, vault) {
74224
74667
  const selected = inputs.slice(0, MAX_IMAGES_PER_SCRAPE);
@@ -74260,11 +74703,11 @@ async function persistScrapeImagesToMemory(user, inputs, vault) {
74260
74703
  assets
74261
74704
  };
74262
74705
  }
74263
- var import_node_crypto54, MAX_IMAGES_PER_SCRAPE;
74706
+ var import_node_crypto55, MAX_IMAGES_PER_SCRAPE;
74264
74707
  var init_scrape_image_sink = __esm({
74265
74708
  "src/api/scrape-image-sink.ts"() {
74266
74709
  "use strict";
74267
- import_node_crypto54 = require("crypto");
74710
+ import_node_crypto55 = require("crypto");
74268
74711
  init_memory();
74269
74712
  MAX_IMAGES_PER_SCRAPE = 25;
74270
74713
  }
@@ -74717,7 +75160,7 @@ function canonicalJson(value) {
74717
75160
  return JSON.stringify(value);
74718
75161
  }
74719
75162
  function sha2562(value) {
74720
- return (0, import_node_crypto55.createHash)("sha256").update(value).digest("hex");
75163
+ return (0, import_node_crypto56.createHash)("sha256").update(value).digest("hex");
74721
75164
  }
74722
75165
  function sensitiveKey(key) {
74723
75166
  const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
@@ -74932,11 +75375,11 @@ async function importServiceConnectionToMemory(identity, input, dependencies) {
74932
75375
  ...!searchReady ? { warning: "The snapshot was stored, but no search chunks were indexed yet." } : {}
74933
75376
  };
74934
75377
  }
74935
- var import_node_crypto55, CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES, CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES, CONNECTION_MEMORY_IMPORT_MAX_STRING_CHARS, CONNECTION_MEMORY_IMPORT_MAX_DEPTH, ConnectionMemoryImportError;
75378
+ var import_node_crypto56, CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES, CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES, CONNECTION_MEMORY_IMPORT_MAX_STRING_CHARS, CONNECTION_MEMORY_IMPORT_MAX_DEPTH, ConnectionMemoryImportError;
74936
75379
  var init_connection_memory_import = __esm({
74937
75380
  "src/api/connection-memory-import.ts"() {
74938
75381
  "use strict";
74939
- import_node_crypto55 = require("crypto");
75382
+ import_node_crypto56 = require("crypto");
74940
75383
  init_slugify();
74941
75384
  CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES = 64 * 1024;
74942
75385
  CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES = 1e6;
@@ -74981,16 +75424,16 @@ async function cleanupLocal(cutoff) {
74981
75424
  let deleted = 0;
74982
75425
  let entries;
74983
75426
  try {
74984
- entries = await (0, import_promises15.readdir)(dir);
75427
+ entries = await (0, import_promises16.readdir)(dir);
74985
75428
  } catch {
74986
75429
  return { deleted: 0, store: "local" };
74987
75430
  }
74988
75431
  for (const name of entries) {
74989
75432
  const path6 = (0, import_node_path23.join)(dir, name);
74990
75433
  try {
74991
- const s = await (0, import_promises15.stat)(path6);
75434
+ const s = await (0, import_promises16.stat)(path6);
74992
75435
  if (s.isFile() && s.mtimeMs < cutoff) {
74993
- await (0, import_promises15.unlink)(path6);
75436
+ await (0, import_promises16.unlink)(path6);
74994
75437
  deleted++;
74995
75438
  }
74996
75439
  } catch {
@@ -75007,11 +75450,11 @@ async function cleanupExpiredScrapeBlobs(maxAgeMs = SCRAPE_BLOB_TTL_MS) {
75007
75450
  return { deleted: 0, store: "none" };
75008
75451
  }
75009
75452
  }
75010
- var import_promises15, import_node_os15, import_node_path23;
75453
+ var import_promises16, import_node_os15, import_node_path23;
75011
75454
  var init_scrape_blob_cleanup = __esm({
75012
75455
  "src/api/scrape-blob-cleanup.ts"() {
75013
75456
  "use strict";
75014
- import_promises15 = require("fs/promises");
75457
+ import_promises16 = require("fs/promises");
75015
75458
  import_node_os15 = require("os");
75016
75459
  import_node_path23 = require("path");
75017
75460
  init_scrape_vault_sink();
@@ -75154,7 +75597,7 @@ function finitePositive(value, fallback) {
75154
75597
  return Number.isFinite(value) && value > 0 ? value : fallback;
75155
75598
  }
75156
75599
  async function collectConnectedDataExport(args) {
75157
- const exportId = (0, import_node_crypto56.randomUUID)();
75600
+ const exportId = (0, import_node_crypto57.randomUUID)();
75158
75601
  const now = args.now ?? Date.now;
75159
75602
  const startedAt = now();
75160
75603
  const budgetMs = finitePositive(CONNECTED_DATA_EXPORT_BUDGET_MS, 24e4);
@@ -75268,11 +75711,11 @@ ${lines.length ? `${lines.join("\n")}
75268
75711
  untrustedContent: true
75269
75712
  };
75270
75713
  }
75271
- var import_node_crypto56, CONNECTED_DATA_INLINE_BUDGET_BYTES, CONNECTED_DATA_MAX_EXPORT_BYTES, CONNECTED_DATA_EXPORT_BUDGET_MS, CONNECTED_DATA_PAGE_START_HEADROOM_MS, CONNECTED_DATA_DATASETS, ConnectedDataExportValidationError;
75714
+ var import_node_crypto57, CONNECTED_DATA_INLINE_BUDGET_BYTES, CONNECTED_DATA_MAX_EXPORT_BYTES, CONNECTED_DATA_EXPORT_BUDGET_MS, CONNECTED_DATA_PAGE_START_HEADROOM_MS, CONNECTED_DATA_DATASETS, ConnectedDataExportValidationError;
75272
75715
  var init_connected_data_export = __esm({
75273
75716
  "src/api/connected-data-export.ts"() {
75274
75717
  "use strict";
75275
- import_node_crypto56 = require("crypto");
75718
+ import_node_crypto57 = require("crypto");
75276
75719
  CONNECTED_DATA_INLINE_BUDGET_BYTES = Number(
75277
75720
  process.env.MCP_SCRAPER_CONNECTED_DATA_INLINE_BUDGET_BYTES ?? 5e4
75278
75721
  );
@@ -75386,7 +75829,7 @@ async function exportSearchConsoleTableData(args) {
75386
75829
  offset += rows.length;
75387
75830
  if (stoppedForBytes || rows.length < limit || offset >= matchedRows) break;
75388
75831
  }
75389
- const exportId = (0, import_node_crypto57.randomUUID)();
75832
+ const exportId = (0, import_node_crypto58.randomUUID)();
75390
75833
  const artifact = await args.writeArtifact({
75391
75834
  ownerId: args.ownerId,
75392
75835
  exportId,
@@ -75408,11 +75851,11 @@ async function exportSearchConsoleTableData(args) {
75408
75851
  warnings
75409
75852
  };
75410
75853
  }
75411
- var import_node_crypto57, SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS, SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE, SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES, SEARCH_CONSOLE_TABLE_COLUMNS, SearchConsoleTableExportValidationError;
75854
+ var import_node_crypto58, SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS, SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE, SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES, SEARCH_CONSOLE_TABLE_COLUMNS, SearchConsoleTableExportValidationError;
75412
75855
  var init_search_console_table_export = __esm({
75413
75856
  "src/api/search-console-table-export.ts"() {
75414
75857
  "use strict";
75415
- import_node_crypto57 = require("crypto");
75858
+ import_node_crypto58 = require("crypto");
75416
75859
  SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS = 5e4;
75417
75860
  SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE = 2e3;
75418
75861
  SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES = 50 * 1024 * 1024;
@@ -76591,7 +77034,7 @@ async function listNangoToolsDirect(identity, connectionId) {
76591
77034
  });
76592
77035
  const readTools = [...policies.values()].filter((policy7) => policy7.classification === "read").map((policy7) => policy7.name);
76593
77036
  const actionTools = [...policies.values()].filter((policy7) => policy7.classification === "action").map((policy7) => policy7.name);
76594
- const revision = (0, import_node_crypto58.createHash)("sha256").update(JSON.stringify(tools.map((tool) => ({ name: tool.name, inputSchema: tool.inputSchema })))).digest("hex");
77037
+ const revision = (0, import_node_crypto59.createHash)("sha256").update(JSON.stringify(tools.map((tool) => ({ name: tool.name, inputSchema: tool.inputSchema })))).digest("hex");
76595
77038
  await updateServiceConnectionTools(connection.id, readTools, actionTools, revision);
76596
77039
  const refreshed = await getOwnedServiceConnection(identity, connection.id);
76597
77040
  return { connection: refreshed ?? { ...connection, readTools, actionTools, toolRevision: revision }, tools };
@@ -76618,8 +77061,8 @@ async function callNangoToolDirect(args) {
76618
77061
  identity: args.identity,
76619
77062
  ratePolicyVersion: CONNECTED_USAGE_RATE_POLICY_VERSION
76620
77063
  });
76621
- const requestId = args.requestId?.trim() || (0, import_node_crypto58.randomUUID)();
76622
- const idempotencyKey3 = `main-nango:${(0, import_node_crypto58.createHash)("sha256").update(args.identity.toLowerCase()).update("\0").update(connection.id).update("\0").update(args.tool).update("\0").update(requestId).digest("hex")}`;
77064
+ const requestId = args.requestId?.trim() || (0, import_node_crypto59.randomUUID)();
77065
+ const idempotencyKey3 = `main-nango:${(0, import_node_crypto59.createHash)("sha256").update(args.identity.toLowerCase()).update("\0").update(connection.id).update("\0").update(args.tool).update("\0").update(requestId).digest("hex")}`;
76623
77066
  const startedAt = /* @__PURE__ */ new Date();
76624
77067
  const started = performance.now();
76625
77068
  let result;
@@ -76644,7 +77087,7 @@ async function callNangoToolDirect(args) {
76644
77087
  toolName: args.tool,
76645
77088
  operationKind: args.operationKind ?? args.classification,
76646
77089
  outcome: providerError ? "error" : "partial",
76647
- requestId: requestId.length <= 200 ? requestId : (0, import_node_crypto58.createHash)("sha256").update(requestId).digest("hex"),
77090
+ requestId: requestId.length <= 200 ? requestId : (0, import_node_crypto59.createHash)("sha256").update(requestId).digest("hex"),
76648
77091
  startedAt: startedAt.toISOString(),
76649
77092
  completedAt: completedAt.toISOString()
76650
77093
  }
@@ -76686,15 +77129,15 @@ async function describeNangoToolDirect(identity, connectionId, toolName) {
76686
77129
  providerContractHash: MAIN_INTEGRATION_CONTRACT_HASH,
76687
77130
  protocolVersion: null,
76688
77131
  schemaSource: "live_tools_list",
76689
- schemaHash: (0, import_node_crypto58.createHash)("sha256").update(JSON.stringify(projected)).digest("hex"),
77132
+ schemaHash: (0, import_node_crypto59.createHash)("sha256").update(JSON.stringify(projected)).digest("hex"),
76690
77133
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
76691
77134
  };
76692
77135
  }
76693
- var import_node_crypto58, import_client13, DEFAULT_NANGO_MCP_URL, NANGO_TIMEOUT_MS, NANGO_CONNECTION_PAGE_SIZE, NANGO_CONNECTION_MAX_PAGES, MAIN_INTEGRATION_CONTRACT_VERSION, MAIN_INTEGRATION_CONTRACT_HASH, MainNangoTransportError;
77136
+ var import_node_crypto59, import_client13, DEFAULT_NANGO_MCP_URL, NANGO_TIMEOUT_MS, NANGO_CONNECTION_PAGE_SIZE, NANGO_CONNECTION_MAX_PAGES, MAIN_INTEGRATION_CONTRACT_VERSION, MAIN_INTEGRATION_CONTRACT_HASH, MainNangoTransportError;
76694
77137
  var init_main_nango_transport = __esm({
76695
77138
  "src/api/main-nango-transport.ts"() {
76696
77139
  "use strict";
76697
- import_node_crypto58 = require("crypto");
77140
+ import_node_crypto59 = require("crypto");
76698
77141
  import_client13 = require("@modelcontextprotocol/client");
76699
77142
  init_service_connections();
76700
77143
  init_connected_usage_billing();
@@ -77318,8 +77761,8 @@ async function setScheduleConnectionActionsEnabled(identity, connectionId, enabl
77318
77761
  return data.connection.actionsEnabled === true;
77319
77762
  }
77320
77763
  async function callScheduleConnectionAction(identity, connectionId, input, tool, idempotencyKey3) {
77321
- 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")}`;
77322
- const requestDigest = (0, import_node_crypto59.createHash)("sha256").update(connectionId).update("\0").update(tool?.trim() ?? "").update("\0").update(canonicalJson2(input)).digest("hex");
77764
+ const requestId = `main-connected-action:${(0, import_node_crypto60.createHash)("sha256").update(identity).update("\0").update(idempotencyKey3?.trim() || (0, import_node_crypto60.randomUUID)()).digest("hex")}`;
77765
+ const requestDigest = (0, import_node_crypto60.createHash)("sha256").update(connectionId).update("\0").update(tool?.trim() ?? "").update("\0").update(canonicalJson2(input)).digest("hex");
77323
77766
  if (mainOwnsIntegrations()) {
77324
77767
  const selectedTool = tool?.trim();
77325
77768
  if (!selectedTool) throw new NangoControlError("An action tool is required.", 400, "invalid_request", false);
@@ -77470,7 +77913,7 @@ function canonicalJson2(value) {
77470
77913
  return JSON.stringify(value);
77471
77914
  }
77472
77915
  function projectedToolSchemaHash(tool) {
77473
- return (0, import_node_crypto59.createHash)("sha256").update(canonicalJson2(tool)).digest("hex");
77916
+ return (0, import_node_crypto60.createHash)("sha256").update(canonicalJson2(tool)).digest("hex");
77474
77917
  }
77475
77918
  async function describeNangoTool(identity, connectionId, tool, fresh) {
77476
77919
  if (mainOwnsIntegrations()) {
@@ -77747,11 +78190,11 @@ async function callMainOwnedExportPage(identity, input) {
77747
78190
  untrustedContent: true
77748
78191
  };
77749
78192
  }
77750
- var import_node_crypto59, DEFAULT_NANGO_CONTROL_URL, DISABLED_NANGO_TOOLS, CONNECTION_SYNC_REQUIRED_TOOLS, CONNECTION_SYNC_OPTIONAL_TOOLS, NangoControlError, ScheduleConnectionValidationError, SAFE_CONTROL_ERROR_CODES, FIXED_CONTROL_ERROR_MESSAGES, CONTROL_ERROR_CODE_ALIASES;
78193
+ var import_node_crypto60, DEFAULT_NANGO_CONTROL_URL, DISABLED_NANGO_TOOLS, CONNECTION_SYNC_REQUIRED_TOOLS, CONNECTION_SYNC_OPTIONAL_TOOLS, NangoControlError, ScheduleConnectionValidationError, SAFE_CONTROL_ERROR_CODES, FIXED_CONTROL_ERROR_MESSAGES, CONTROL_ERROR_CODE_ALIASES;
77751
78194
  var init_nango_control = __esm({
77752
78195
  "src/api/nango-control.ts"() {
77753
78196
  "use strict";
77754
- import_node_crypto59 = require("crypto");
78197
+ import_node_crypto60 = require("crypto");
77755
78198
  init_connected_data_export();
77756
78199
  init_slack_connected_data_export();
77757
78200
  init_main_nango_transport();
@@ -78107,7 +78550,7 @@ async function callResendRead(identity, connectionId, tool, args) {
78107
78550
  return isRecord5(data) ? data.result ?? data : data;
78108
78551
  }
78109
78552
  async function callResendAction(identity, connectionId, tool, input, idempotencyKey3) {
78110
- const requestId = `main-resend-action:${(0, import_node_crypto60.createHash)("sha256").update(identity).update("\0").update(idempotencyKey3.trim()).digest("hex")}`;
78553
+ const requestId = `main-resend-action:${(0, import_node_crypto61.createHash)("sha256").update(identity).update("\0").update(idempotencyKey3.trim()).digest("hex")}`;
78111
78554
  const body = await controlRequest2("/api/internal/resend/actions/call", {
78112
78555
  method: "POST",
78113
78556
  headers: { "x-request-id": requestId },
@@ -78172,12 +78615,12 @@ async function callResendExportPage(identity, input) {
78172
78615
  untrustedContent: true
78173
78616
  };
78174
78617
  }
78175
- var import_node_crypto60, DEFAULT_CONNECTION_CONTROL_URL, RESEND_PROVIDER_CONFIG_KEY, RESEND_LOGO_URL, RESEND_DOCS_URL, RESEND_ADMIN_BLOCKED_TOOLS, RESEND_CONNECTION_SYNC_REQUIRED_TOOLS, ResendControlError;
78618
+ var import_node_crypto61, DEFAULT_CONNECTION_CONTROL_URL, RESEND_PROVIDER_CONFIG_KEY, RESEND_LOGO_URL, RESEND_DOCS_URL, RESEND_ADMIN_BLOCKED_TOOLS, RESEND_CONNECTION_SYNC_REQUIRED_TOOLS, ResendControlError;
78176
78619
  var init_resend_control = __esm({
78177
78620
  "src/api/resend-control.ts"() {
78178
78621
  "use strict";
78179
78622
  init_connected_data_export();
78180
- import_node_crypto60 = require("crypto");
78623
+ import_node_crypto61 = require("crypto");
78181
78624
  DEFAULT_CONNECTION_CONTROL_URL = "https://mcp-scraper-scheduler.vercel.app";
78182
78625
  RESEND_PROVIDER_CONFIG_KEY = "resend";
78183
78626
  RESEND_LOGO_URL = "https://cdn.resend.com/brand/resend-icon-black.svg";
@@ -78762,7 +79205,7 @@ function settleWithinTickBudget(label, unfinished, work, onDeadlineOrError) {
78762
79205
  );
78763
79206
  });
78764
79207
  }
78765
- var import_resend3, import_node_crypto61, import_hono36, import_hono37, import_factory8, import_cookie2, import_stripe2, secureCookies2, isProduction2, sessionCookieOptions2, requireAllowedOrigin, auth4, sessionAuth, requireIntegrationsTier, requirePaidSchedulingTier, app, deploymentProfile, STRIPE_API_VERSION, SYNC_HARVEST_TIMEOUT_OVERRIDE_MS, CRON_TICK_BUDGET_MS, CRON_TICK_DRAIN_BUDGET_MS;
79208
+ var import_resend3, import_node_crypto62, import_hono36, import_hono37, import_factory8, import_cookie2, import_stripe2, secureCookies2, isProduction2, sessionCookieOptions2, requireAllowedOrigin, auth4, sessionAuth, requireIntegrationsTier, requirePaidSchedulingTier, app, deploymentProfile, STRIPE_API_VERSION, SYNC_HARVEST_TIMEOUT_OVERRIDE_MS, CRON_TICK_BUDGET_MS, CRON_TICK_DRAIN_BUDGET_MS;
78766
79209
  var init_server = __esm({
78767
79210
  "src/api/server.ts"() {
78768
79211
  "use strict";
@@ -78775,7 +79218,7 @@ var init_server = __esm({
78775
79218
  init_og();
78776
79219
  import_resend3 = require("resend");
78777
79220
  init_url_utils();
78778
- import_node_crypto61 = require("crypto");
79221
+ import_node_crypto62 = require("crypto");
78779
79222
  init_kpo_extractor();
78780
79223
  init_screenshot();
78781
79224
  init_media_extractor();
@@ -80325,7 +80768,7 @@ var init_server = __esm({
80325
80768
  if (!harvestOk) return c.json(insufficientBalanceResponse(harvestBal, harvestCost), 402);
80326
80769
  jobId2 = await createJob(user.id, options.query, { ...options, billingHoldMc: harvestCost }, body.callback_url);
80327
80770
  } else {
80328
- jobId2 = (0, import_node_crypto61.randomUUID)();
80771
+ jobId2 = (0, import_node_crypto62.randomUUID)();
80329
80772
  const billingDebitKey = `paa-harvest:${jobId2}:hold`;
80330
80773
  const description = `PAA harvest: ${options.query}`.slice(0, 500);
80331
80774
  const hold = await debitMcIdempotent(