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.
- package/CHANGELOG.md +30 -0
- package/README.md +2 -2
- package/dist/bin/api-server.cjs +615 -172
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +2 -2
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-install.cjs +2 -2
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +2 -2
- package/dist/bin/mcp-stdio-server.cjs +49 -4
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +4 -4
- package/dist/{chunk-2FL5FXMV.js → chunk-5XYBKE3X.js} +2 -2
- package/dist/{chunk-PGI2QPFE.js → chunk-HU4R6BP2.js} +2 -2
- package/dist/{chunk-PGI2QPFE.js.map → chunk-HU4R6BP2.js.map} +1 -1
- package/dist/chunk-QQ6ZBYZZ.js +7 -0
- package/dist/chunk-QQ6ZBYZZ.js.map +1 -0
- package/dist/{chunk-N4LT53JX.js → chunk-R6CO77BN.js} +3 -3
- package/dist/{chunk-N4LT53JX.js.map → chunk-R6CO77BN.js.map} +1 -1
- package/dist/{chunk-WYLIDW4S.js → chunk-ULAJAZQF.js} +50 -5
- package/dist/chunk-ULAJAZQF.js.map +1 -0
- package/dist/{extract-bundle-3CRG7FAN.js → extract-bundle-H7JEELY7.js} +3 -3
- package/dist/{server-WKBCZ2TH.js → server-NBGZJX4P.js} +474 -86
- package/dist/server-NBGZJX4P.js.map +1 -0
- package/dist/{site-extract-repository-QSRPXNMI.js → site-extract-repository-CFMSWAVL.js} +3 -3
- package/dist/{worker-W5C6SKZT.js → worker-S2WXELUC.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-V2QWNGEK.js +0 -7
- package/dist/chunk-V2QWNGEK.js.map +0 -1
- package/dist/chunk-WYLIDW4S.js.map +0 -1
- package/dist/server-WKBCZ2TH.js.map +0 -1
- /package/dist/{chunk-2FL5FXMV.js.map → chunk-5XYBKE3X.js.map} +0 -0
- /package/dist/{extract-bundle-3CRG7FAN.js.map → extract-bundle-H7JEELY7.js.map} +0 -0
- /package/dist/{site-extract-repository-QSRPXNMI.js.map → site-extract-repository-CFMSWAVL.js.map} +0 -0
- /package/dist/{worker-W5C6SKZT.js.map → worker-S2WXELUC.js.map} +0 -0
package/dist/bin/api-server.cjs
CHANGED
|
@@ -12455,8 +12455,8 @@ var init_product_contract_generated = __esm({
|
|
|
12455
12455
|
"freeSignupCredits": 0
|
|
12456
12456
|
},
|
|
12457
12457
|
"inventory": {
|
|
12458
|
-
"totalTools":
|
|
12459
|
-
"scraperTools":
|
|
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.
|
|
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
|
|
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.
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
64763
|
-
|
|
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
|
|
65063
|
+
LIMIT ${SEARCH_CANDIDATE_LIMIT}
|
|
64770
65064
|
`,
|
|
64771
65065
|
args
|
|
64772
65066
|
});
|
|
64773
|
-
|
|
64774
|
-
|
|
64775
|
-
|
|
64776
|
-
|
|
64777
|
-
|
|
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(
|
|
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
|
|
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
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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
|
-
|
|
65183
|
-
|
|
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: "
|
|
65238
|
-
conflictBehavior: "
|
|
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:
|
|
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:
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
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("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
69670
|
-
const publicId = `form_${(0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
70641
|
-
const stateHash = (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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,
|
|
71734
|
+
...email ? { emailSha256: (0, import_node_crypto43.createHash)("sha256").update(email).digest("hex") } : {},
|
|
71292
71735
|
...phone ? {
|
|
71293
|
-
phoneSha256: (0,
|
|
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,
|
|
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,
|
|
72622
|
+
return (0, import_node_crypto44.createHash)("sha256").update(apiKey).digest("hex").slice(0, 24);
|
|
72180
72623
|
}
|
|
72181
|
-
var
|
|
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
|
-
|
|
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,
|
|
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,
|
|
72705
|
+
const token6 = (0, import_node_crypto45.randomBytes)(32).toString("base64url");
|
|
72263
72706
|
const record = {
|
|
72264
|
-
shareId: (0,
|
|
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
|
|
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
|
-
|
|
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,
|
|
72840
|
+
sha256: (0, import_node_crypto46.createHash)("sha256").update(html).digest("hex"),
|
|
72398
72841
|
generatedAt: generatedAtIso
|
|
72399
72842
|
};
|
|
72400
72843
|
}
|
|
72401
|
-
var
|
|
72844
|
+
var import_node_crypto46;
|
|
72402
72845
|
var init_render2 = __esm({
|
|
72403
72846
|
"src/personal-authority/render.ts"() {
|
|
72404
72847
|
"use strict";
|
|
72405
|
-
|
|
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,
|
|
72951
|
+
sha256: (0, import_node_crypto47.createHash)("sha256").update(html).digest("hex"),
|
|
72509
72952
|
generatedAt: generatedAtIso
|
|
72510
72953
|
};
|
|
72511
72954
|
}
|
|
72512
|
-
var
|
|
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
|
-
|
|
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,
|
|
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
|
|
73079
|
+
var import_node_crypto48;
|
|
72637
73080
|
var init_render3 = __esm({
|
|
72638
73081
|
"src/newsroom-publisher/render.ts"() {
|
|
72639
73082
|
"use strict";
|
|
72640
|
-
|
|
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,
|
|
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
|
|
73185
|
+
var import_node_crypto49;
|
|
72743
73186
|
var init_render4 = __esm({
|
|
72744
73187
|
"src/blog-article/render.ts"() {
|
|
72745
73188
|
"use strict";
|
|
72746
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
73496
|
-
return (0,
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
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,
|
|
75434
|
+
const s = await (0, import_promises16.stat)(path6);
|
|
74992
75435
|
if (s.isFile() && s.mtimeMs < cutoff) {
|
|
74993
|
-
await (0,
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
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,
|
|
76622
|
-
const idempotencyKey3 = `main-nango:${(0,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
77322
|
-
const requestDigest = (0,
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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(
|