mcp-scraper 0.45.0 → 0.47.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/README.md +6 -2
- package/dist/bin/api-server.cjs +1098 -198
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +3 -3
- 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 +3 -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 +251 -34
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +5 -5
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +3 -3
- package/dist/{chunk-5X7CJEK3.js → chunk-5S6LYQQO.js} +2 -2
- package/dist/chunk-5S6LYQQO.js.map +1 -0
- package/dist/{chunk-OVD4E4AP.js → chunk-GV5EGE5M.js} +2 -2
- package/dist/chunk-H4NWAF5B.js +7 -0
- package/dist/chunk-H4NWAF5B.js.map +1 -0
- package/dist/{chunk-T5AFM4G5.js → chunk-IWYYZXBR.js} +2 -2
- package/dist/{chunk-RJMOOII7.js → chunk-JH7A4HA7.js} +2 -2
- package/dist/{chunk-JFOT5JEL.js → chunk-MW3ET532.js} +2 -2
- package/dist/{chunk-GRODXRUY.js → chunk-QSUGPYGQ.js} +3 -2
- package/dist/chunk-QSUGPYGQ.js.map +1 -0
- package/dist/{chunk-WT2UHDSE.js → chunk-TV6KPZ6J.js} +2 -2
- package/dist/{chunk-FQIICRR4.js → chunk-XCGQBGDA.js} +296 -38
- package/dist/chunk-XCGQBGDA.js.map +1 -0
- package/dist/{db-4ABEYNDM.js → db-W3RPRILW.js} +2 -2
- package/dist/{extract-bundle-DYMQRQWG.js → extract-bundle-DXWWQER6.js} +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -3
- package/dist/{location-data-repository-HDNCU4Z4.js → location-data-repository-2FJVMTZ2.js} +3 -3
- package/dist/{server-7B75SDNG.js → server-OHMZJRSB.js} +778 -141
- package/dist/server-OHMZJRSB.js.map +1 -0
- package/dist/{site-extract-repository-G723PZIL.js → site-extract-repository-UULQAGO4.js} +3 -3
- package/dist/{worker-4LZGRHDA.js → worker-3QX7LG62.js} +5 -5
- package/package.json +2 -2
- package/dist/chunk-5X7CJEK3.js.map +0 -1
- package/dist/chunk-CEZ3NFBP.js +0 -7
- package/dist/chunk-CEZ3NFBP.js.map +0 -1
- package/dist/chunk-FQIICRR4.js.map +0 -1
- package/dist/chunk-GRODXRUY.js.map +0 -1
- package/dist/server-7B75SDNG.js.map +0 -1
- /package/dist/{chunk-OVD4E4AP.js.map → chunk-GV5EGE5M.js.map} +0 -0
- /package/dist/{chunk-T5AFM4G5.js.map → chunk-IWYYZXBR.js.map} +0 -0
- /package/dist/{chunk-RJMOOII7.js.map → chunk-JH7A4HA7.js.map} +0 -0
- /package/dist/{chunk-JFOT5JEL.js.map → chunk-MW3ET532.js.map} +0 -0
- /package/dist/{chunk-WT2UHDSE.js.map → chunk-TV6KPZ6J.js.map} +0 -0
- /package/dist/{db-4ABEYNDM.js.map → db-W3RPRILW.js.map} +0 -0
- /package/dist/{extract-bundle-DYMQRQWG.js.map → extract-bundle-DXWWQER6.js.map} +0 -0
- /package/dist/{location-data-repository-HDNCU4Z4.js.map → location-data-repository-2FJVMTZ2.js.map} +0 -0
- /package/dist/{site-extract-repository-G723PZIL.js.map → site-extract-repository-UULQAGO4.js.map} +0 -0
- /package/dist/{worker-4LZGRHDA.js.map → worker-3QX7LG62.js.map} +0 -0
|
@@ -10,14 +10,14 @@ import {
|
|
|
10
10
|
importHostedCensusPlacesCsv,
|
|
11
11
|
importHostedZipGroupsCsv,
|
|
12
12
|
listActiveHostedLocationDatasets
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-TV6KPZ6J.js";
|
|
14
14
|
import {
|
|
15
15
|
classifyHarvestProblem,
|
|
16
16
|
createHarvestAttemptRecorder,
|
|
17
17
|
deliverWebhook,
|
|
18
18
|
harvestProblemResponse,
|
|
19
19
|
serializeHarvestProblem
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-GV5EGE5M.js";
|
|
21
21
|
import {
|
|
22
22
|
AIModeSelectors,
|
|
23
23
|
AIOverviewSelectors,
|
|
@@ -44,22 +44,29 @@ import {
|
|
|
44
44
|
resolveKernelProxyId,
|
|
45
45
|
serpArrivalUrl,
|
|
46
46
|
withRuntimeUserAgent
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-JH7A4HA7.js";
|
|
48
48
|
import {
|
|
49
49
|
ApiErrorResponseSchema,
|
|
50
|
-
|
|
50
|
+
CommonsClaimPublicationInputSchema,
|
|
51
|
+
CommonsPreparePublicationInputSchema,
|
|
52
|
+
CommonsPublishEditorialInputSchema,
|
|
53
|
+
CommonsValidatePublicationInputSchema,
|
|
51
54
|
CreateArtifactTemplateInputSchema,
|
|
52
55
|
CreateEditorialReadingRoomInputSchema,
|
|
53
56
|
CreateScheduledRunViewLinkInputSchema,
|
|
57
|
+
EDITORIAL_READING_ROOM_TEMPLATE_PRESET,
|
|
58
|
+
EditorialArtifactTemplateConfigSchema,
|
|
54
59
|
EditorialReadingRoomV1InputSchema,
|
|
55
60
|
GetScheduledRunResponseSchema,
|
|
56
61
|
HttpMcpToolExecutor,
|
|
57
62
|
InternalScheduledArtifactRenderRequestSchema,
|
|
58
63
|
ListScheduledRunsQuerySchema,
|
|
59
64
|
LocalSourcebookSchemaTypeSchema,
|
|
65
|
+
PERSONAL_AUTHORITY_TEMPLATE_PRESET,
|
|
66
|
+
PersonalAuthorityTemplateConfigSchema,
|
|
67
|
+
PersonalAuthorityV1InputSchema,
|
|
60
68
|
PublicScheduledRunTokenSchema,
|
|
61
69
|
RenewEditorialReadingRoomDownloadInputSchema,
|
|
62
|
-
SCHEDULED_ARTIFACT_TEMPLATE_PRESET,
|
|
63
70
|
ScheduledResultsMcpExecutor,
|
|
64
71
|
UpdateArtifactTemplateInputSchema,
|
|
65
72
|
WIZPER_LANGUAGES,
|
|
@@ -90,7 +97,7 @@ import {
|
|
|
90
97
|
resolveDeploymentProfile,
|
|
91
98
|
resolveLocalSourcebookSchemaType,
|
|
92
99
|
transcribeMediaUrl
|
|
93
|
-
} from "./chunk-
|
|
100
|
+
} from "./chunk-XCGQBGDA.js";
|
|
94
101
|
import {
|
|
95
102
|
auditImageUrls,
|
|
96
103
|
auditImages,
|
|
@@ -118,7 +125,7 @@ import {
|
|
|
118
125
|
recordKernelSession,
|
|
119
126
|
recordVendorUsage,
|
|
120
127
|
runWithCostContext
|
|
121
|
-
} from "./chunk-
|
|
128
|
+
} from "./chunk-IWYYZXBR.js";
|
|
122
129
|
import {
|
|
123
130
|
listWorkflowDefinitions,
|
|
124
131
|
runWorkflow,
|
|
@@ -146,7 +153,7 @@ import {
|
|
|
146
153
|
} from "./chunk-3ZUBQQPQ.js";
|
|
147
154
|
import {
|
|
148
155
|
PACKAGE_VERSION
|
|
149
|
-
} from "./chunk-
|
|
156
|
+
} from "./chunk-H4NWAF5B.js";
|
|
150
157
|
import {
|
|
151
158
|
abandonExtractSettlement,
|
|
152
159
|
countSuccessfulPages,
|
|
@@ -169,7 +176,7 @@ import {
|
|
|
169
176
|
setExtractJobTotal,
|
|
170
177
|
settleExtractJob,
|
|
171
178
|
terminalExtractJobStatus
|
|
172
|
-
} from "./chunk-
|
|
179
|
+
} from "./chunk-MW3ET532.js";
|
|
173
180
|
import {
|
|
174
181
|
BROWSER_OPEN_MIN_BALANCE_MC,
|
|
175
182
|
CONCURRENCY_PRICE_ID,
|
|
@@ -326,7 +333,7 @@ import {
|
|
|
326
333
|
startHarvestAttempt,
|
|
327
334
|
upsertPageSnapshot,
|
|
328
335
|
verifyPassword
|
|
329
|
-
} from "./chunk-
|
|
336
|
+
} from "./chunk-5S6LYQQO.js";
|
|
330
337
|
|
|
331
338
|
// src/blog/registry.ts
|
|
332
339
|
var posts = [
|
|
@@ -2686,38 +2693,38 @@ function renderCard(card) {
|
|
|
2686
2693
|
</div>
|
|
2687
2694
|
</div>`;
|
|
2688
2695
|
}
|
|
2689
|
-
function renderSection(
|
|
2690
|
-
const calloutHtml =
|
|
2696
|
+
function renderSection(section2, tocTitle) {
|
|
2697
|
+
const calloutHtml = section2.callout ? `
|
|
2691
2698
|
<div class="callout">
|
|
2692
|
-
<p class="callout-eyebrow">${
|
|
2693
|
-
<h3 class="callout-heading">${
|
|
2694
|
-
<p class="callout-body">${
|
|
2699
|
+
<p class="callout-eyebrow">${section2.callout.eyebrow}</p>
|
|
2700
|
+
<h3 class="callout-heading">${section2.callout.heading}</h3>
|
|
2701
|
+
<p class="callout-body">${section2.callout.body}</p>
|
|
2695
2702
|
</div>` : "";
|
|
2696
|
-
const cardsHtml =
|
|
2697
|
-
const interactiveHtml =
|
|
2698
|
-
<div class="section-interactive">${
|
|
2699
|
-
return `<section class="post-section" id="${
|
|
2703
|
+
const cardsHtml = section2.cards.map(renderCard).join("\n ");
|
|
2704
|
+
const interactiveHtml = section2.interactiveHtml ? `
|
|
2705
|
+
<div class="section-interactive">${section2.interactiveHtml}</div>` : "";
|
|
2706
|
+
return `<section class="post-section" id="${section2.id}" aria-label="${tocTitle}">
|
|
2700
2707
|
<div class="section-header">
|
|
2701
|
-
<span class="section-num">${
|
|
2702
|
-
<h2 class="section-title">${
|
|
2703
|
-
<p class="section-deck">${
|
|
2708
|
+
<span class="section-num">${section2.num}</span>
|
|
2709
|
+
<h2 class="section-title">${section2.title}${section2.titleItalic ? ` <em>${section2.titleItalic}</em>` : ""}</h2>
|
|
2710
|
+
<p class="section-deck">${section2.deck}</p>
|
|
2704
2711
|
</div>${calloutHtml}
|
|
2705
2712
|
<div class="card-stack">
|
|
2706
2713
|
${cardsHtml}
|
|
2707
2714
|
</div>${interactiveHtml}
|
|
2708
2715
|
</section>`;
|
|
2709
2716
|
}
|
|
2710
|
-
function tocItem(
|
|
2711
|
-
const label = `${
|
|
2712
|
-
const subItems =
|
|
2717
|
+
function tocItem(section2) {
|
|
2718
|
+
const label = `${section2.title}${section2.titleItalic ? " " + section2.titleItalic : ""}`;
|
|
2719
|
+
const subItems = section2.cards.map((card) => `<li>
|
|
2713
2720
|
<a href="#${card.id}" class="toc-sub-link" data-toc="${card.id}">
|
|
2714
2721
|
<span class="toc-sub-num">${card.num}</span>
|
|
2715
2722
|
<span class="toc-sub-label">${card.question}</span>
|
|
2716
2723
|
</a>
|
|
2717
2724
|
</li>`).join("\n ");
|
|
2718
2725
|
return `<li>
|
|
2719
|
-
<a href="#${
|
|
2720
|
-
<span class="toc-num">${
|
|
2726
|
+
<a href="#${section2.id}" class="toc-link" data-toc="${section2.id}">
|
|
2727
|
+
<span class="toc-num">${section2.num}</span>
|
|
2721
2728
|
<span class="toc-label">${label}</span>
|
|
2722
2729
|
</a>
|
|
2723
2730
|
<ul class="toc-sub-list">
|
|
@@ -3643,7 +3650,7 @@ async function generateOgImage(post) {
|
|
|
3643
3650
|
|
|
3644
3651
|
// src/api/server.ts
|
|
3645
3652
|
import { Resend as Resend3 } from "resend";
|
|
3646
|
-
import { randomUUID as
|
|
3653
|
+
import { randomUUID as randomUUID23 } from "crypto";
|
|
3647
3654
|
|
|
3648
3655
|
// src/api/kpo-extractor.ts
|
|
3649
3656
|
import TurndownService from "turndown";
|
|
@@ -8672,7 +8679,7 @@ var siteExtractFn = inngest.createFunction(
|
|
|
8672
8679
|
});
|
|
8673
8680
|
}) : null;
|
|
8674
8681
|
const finalized = await step.run("finalize", async () => {
|
|
8675
|
-
const { assembleExtractArtifacts } = await import("./extract-bundle-
|
|
8682
|
+
const { assembleExtractArtifacts } = await import("./extract-bundle-DXWWQER6.js");
|
|
8676
8683
|
const current = await getExtractJob(jobId2);
|
|
8677
8684
|
if (!current) throw new Error("extract job disappeared before finalization");
|
|
8678
8685
|
const stored = await assembleExtractArtifacts(current, { branding, imageAudit });
|
|
@@ -9614,7 +9621,7 @@ async function resolveUsZipsPath(requestedPath) {
|
|
|
9614
9621
|
async function loadZipGroups(stateAbbr, requestedPath, warnings, sourceMode) {
|
|
9615
9622
|
const mode = resolvedLocationDataMode({ zipSource: sourceMode });
|
|
9616
9623
|
if (mode === "hosted") {
|
|
9617
|
-
const { getHostedZipGroups } = await import("./location-data-repository-
|
|
9624
|
+
const { getHostedZipGroups } = await import("./location-data-repository-2FJVMTZ2.js");
|
|
9618
9625
|
const hosted = await getHostedZipGroups(stateAbbr);
|
|
9619
9626
|
if (!hosted.dataset) {
|
|
9620
9627
|
throw new DirectoryLocationError(
|
|
@@ -9677,7 +9684,7 @@ async function resolveDirectoryMarkets(options, config = {}) {
|
|
|
9677
9684
|
const sourceUrl = censusStateUrl(state.fips);
|
|
9678
9685
|
const warnings = [];
|
|
9679
9686
|
if (resolvedLocationDataMode(config) === "hosted") {
|
|
9680
|
-
const { queryHostedLocationMarkets } = await import("./location-data-repository-
|
|
9687
|
+
const { queryHostedLocationMarkets } = await import("./location-data-repository-2FJVMTZ2.js");
|
|
9681
9688
|
const hosted = await queryHostedLocationMarkets({
|
|
9682
9689
|
state: state.abbr,
|
|
9683
9690
|
minPopulation: options.minPopulation,
|
|
@@ -16067,8 +16074,8 @@ var YouTubeExtractor = class {
|
|
|
16067
16074
|
const sections = slr?.contents ?? [];
|
|
16068
16075
|
const videos = [];
|
|
16069
16076
|
const channels = [];
|
|
16070
|
-
for (const
|
|
16071
|
-
const contents =
|
|
16077
|
+
for (const section2 of sections) {
|
|
16078
|
+
const contents = section2.itemSectionRenderer?.contents ?? [];
|
|
16072
16079
|
for (const item of contents) {
|
|
16073
16080
|
const rec = item;
|
|
16074
16081
|
if (rec.videoRenderer) {
|
|
@@ -27474,10 +27481,10 @@ oauthApp.post("/oauth/authorize", async (c) => {
|
|
|
27474
27481
|
return typeof v === "string" ? v : void 0;
|
|
27475
27482
|
};
|
|
27476
27483
|
const p = parseAuthParams(get);
|
|
27477
|
-
const
|
|
27484
|
+
const action2 = get("action") ?? "";
|
|
27478
27485
|
const valid = await validateAuthRequest(p);
|
|
27479
27486
|
if (!valid.ok) return c.text(valid.message, valid.status);
|
|
27480
|
-
if (
|
|
27487
|
+
if (action2 === "login") {
|
|
27481
27488
|
const email = (get("email") ?? "").trim().toLowerCase();
|
|
27482
27489
|
const password = get("password") ?? "";
|
|
27483
27490
|
const user2 = email ? await withOAuthDatabaseDeadline("login-user", getUserByEmail(email)) : void 0;
|
|
@@ -27491,8 +27498,8 @@ oauthApp.post("/oauth/authorize", async (c) => {
|
|
|
27491
27498
|
}
|
|
27492
27499
|
const user = await sessionUser(getCookie(c, "session"));
|
|
27493
27500
|
if (!user) return c.html(renderLogin(p, "Your session expired. Please sign in again."));
|
|
27494
|
-
if (
|
|
27495
|
-
if (
|
|
27501
|
+
if (action2 === "deny") return redirectWithError(p.redirect_uri, p.state, "access_denied");
|
|
27502
|
+
if (action2 !== "approve") return c.text("unsupported action", 400);
|
|
27496
27503
|
const scope = negotiateScope(p.scope, user, p.resource);
|
|
27497
27504
|
const code = `code_${randomBytes2(32).toString("base64url")}`;
|
|
27498
27505
|
const expiresAt = new Date(Date.now() + CODE_TTL_SECONDS * 1e3).toISOString();
|
|
@@ -28548,12 +28555,12 @@ vaultApp.post("/api/communications/review", async (c) => {
|
|
|
28548
28555
|
const token4 = typeof body.token === "string" ? body.token : void 0;
|
|
28549
28556
|
if (!validToken(token4)) return c.json({ ok: false, error: "missing or malformed Vault App link" }, 401);
|
|
28550
28557
|
const path5 = stringValue(body.path, 320);
|
|
28551
|
-
const
|
|
28558
|
+
const action2 = stringValue(body.action, 24);
|
|
28552
28559
|
const title = typeof body.title === "string" ? stringValue(body.title, 320) : void 0;
|
|
28553
28560
|
const content = typeof body.content === "string" && body.content.length <= 1e5 ? body.content : void 0;
|
|
28554
28561
|
const feedback = typeof body.feedback === "string" ? body.feedback.trim().slice(0, 1e4) : void 0;
|
|
28555
28562
|
const baseRevision = typeof body.baseRevision === "number" && Number.isInteger(body.baseRevision) ? body.baseRevision : void 0;
|
|
28556
|
-
const normalizedAction =
|
|
28563
|
+
const normalizedAction = action2 === "accept" ? "approve" : action2;
|
|
28557
28564
|
if (!path5 || !normalizedAction || !["approve", "deny", "edit"].includes(normalizedAction) || body.title !== void 0 && !title) {
|
|
28558
28565
|
return c.json({ ok: false, error: "valid path, action, and optional draft fields are required" }, 400);
|
|
28559
28566
|
}
|
|
@@ -30686,30 +30693,30 @@ function renderEditorialReadingRoomV1(input, config, now = /* @__PURE__ */ new D
|
|
|
30686
30693
|
startLabel: "Start reading"
|
|
30687
30694
|
},
|
|
30688
30695
|
deck: input.dek ?? `Prepared for ${input.generatedFor}.`,
|
|
30689
|
-
articles: input.sections.map((
|
|
30696
|
+
articles: input.sections.map((section2, index) => {
|
|
30690
30697
|
const provenance = [
|
|
30691
30698
|
"",
|
|
30692
30699
|
"## Sources",
|
|
30693
30700
|
"",
|
|
30694
|
-
...
|
|
30695
|
-
...
|
|
30701
|
+
...section2.sourceLabels.map((label) => `- ${label}`),
|
|
30702
|
+
...section2.sourceRefs.map((ref) => `- ${scheduledSourceRef(ref)}`)
|
|
30696
30703
|
].join("\n");
|
|
30697
|
-
const withFiles = index === input.sections.length - 1 && filesMarkdown ? `${
|
|
30704
|
+
const withFiles = index === input.sections.length - 1 && filesMarkdown ? `${section2.markdown}
|
|
30698
30705
|
|
|
30699
30706
|
${provenance}
|
|
30700
30707
|
|
|
30701
|
-
${filesMarkdown}` : `${
|
|
30708
|
+
${filesMarkdown}` : `${section2.markdown}
|
|
30702
30709
|
|
|
30703
30710
|
${provenance}`;
|
|
30704
30711
|
return {
|
|
30705
|
-
slug:
|
|
30712
|
+
slug: section2.id,
|
|
30706
30713
|
category: "Report",
|
|
30707
|
-
kicker:
|
|
30714
|
+
kicker: section2.summary ?? "Scheduled result",
|
|
30708
30715
|
order: index + 1,
|
|
30709
|
-
title:
|
|
30710
|
-
summary:
|
|
30716
|
+
title: section2.title,
|
|
30717
|
+
summary: section2.summary ?? section2.title,
|
|
30711
30718
|
sourceType: "Scheduled result",
|
|
30712
|
-
sourceLabel:
|
|
30719
|
+
sourceLabel: section2.sourceLabels.join(" \xB7 ").slice(0, 500),
|
|
30713
30720
|
markdown: withFiles
|
|
30714
30721
|
};
|
|
30715
30722
|
}),
|
|
@@ -30845,7 +30852,7 @@ import { z as z32 } from "zod";
|
|
|
30845
30852
|
|
|
30846
30853
|
// src/api/commons-repository.ts
|
|
30847
30854
|
import { createHash as createHash12, randomUUID as randomUUID15 } from "crypto";
|
|
30848
|
-
var COMMONS_SCHEMA_VERSION = "2026-08-04.
|
|
30855
|
+
var COMMONS_SCHEMA_VERSION = "2026-08-04.3";
|
|
30849
30856
|
var DEFAULT_COMMONS_BASE_URL = "https://transparent-commons.cc";
|
|
30850
30857
|
var DEFAULT_ENTITY_TYPE = "PublicArticle";
|
|
30851
30858
|
var COMMONS_ENTITY_PROFILES = {
|
|
@@ -31127,6 +31134,54 @@ async function ensureCommonsSchema() {
|
|
|
31127
31134
|
},
|
|
31128
31135
|
{ sql: `CREATE INDEX IF NOT EXISTS commons_index_documents_status_updated ON commons_index_documents(embedding_status, updated_at DESC)`, args: [] },
|
|
31129
31136
|
{ sql: `CREATE INDEX IF NOT EXISTS commons_index_documents_entity_type ON commons_index_documents(entity_id, document_type)`, args: [] },
|
|
31137
|
+
{
|
|
31138
|
+
sql: `
|
|
31139
|
+
CREATE TABLE IF NOT EXISTS commons_publications (
|
|
31140
|
+
id TEXT PRIMARY KEY,
|
|
31141
|
+
owner_user_id INTEGER NOT NULL REFERENCES users(id),
|
|
31142
|
+
subdomain TEXT NOT NULL UNIQUE,
|
|
31143
|
+
title TEXT NOT NULL,
|
|
31144
|
+
description TEXT NOT NULL DEFAULT '',
|
|
31145
|
+
claim_idempotency_key TEXT NOT NULL,
|
|
31146
|
+
latest_edition_id TEXT,
|
|
31147
|
+
created_at TEXT NOT NULL,
|
|
31148
|
+
updated_at TEXT NOT NULL,
|
|
31149
|
+
UNIQUE(owner_user_id),
|
|
31150
|
+
UNIQUE(owner_user_id, claim_idempotency_key)
|
|
31151
|
+
)
|
|
31152
|
+
`,
|
|
31153
|
+
args: []
|
|
31154
|
+
},
|
|
31155
|
+
{ sql: `CREATE INDEX IF NOT EXISTS commons_publications_updated ON commons_publications(updated_at DESC)`, args: [] },
|
|
31156
|
+
{
|
|
31157
|
+
sql: `
|
|
31158
|
+
CREATE TABLE IF NOT EXISTS commons_publication_editions (
|
|
31159
|
+
id TEXT PRIMARY KEY,
|
|
31160
|
+
publication_id TEXT NOT NULL REFERENCES commons_publications(id),
|
|
31161
|
+
owner_user_id INTEGER NOT NULL REFERENCES users(id),
|
|
31162
|
+
edition_slug TEXT NOT NULL,
|
|
31163
|
+
revision INTEGER NOT NULL,
|
|
31164
|
+
title TEXT NOT NULL,
|
|
31165
|
+
site_json TEXT NOT NULL,
|
|
31166
|
+
deck TEXT NOT NULL,
|
|
31167
|
+
articles_json TEXT NOT NULL,
|
|
31168
|
+
html TEXT NOT NULL,
|
|
31169
|
+
filename TEXT NOT NULL,
|
|
31170
|
+
sha256 TEXT NOT NULL,
|
|
31171
|
+
article_count INTEGER NOT NULL,
|
|
31172
|
+
word_count INTEGER NOT NULL,
|
|
31173
|
+
bytes INTEGER NOT NULL,
|
|
31174
|
+
warnings_json TEXT NOT NULL DEFAULT '[]',
|
|
31175
|
+
idempotency_key TEXT NOT NULL,
|
|
31176
|
+
created_at TEXT NOT NULL,
|
|
31177
|
+
published_at TEXT NOT NULL,
|
|
31178
|
+
UNIQUE(publication_id, edition_slug, revision),
|
|
31179
|
+
UNIQUE(owner_user_id, idempotency_key)
|
|
31180
|
+
)
|
|
31181
|
+
`,
|
|
31182
|
+
args: []
|
|
31183
|
+
},
|
|
31184
|
+
{ sql: `CREATE INDEX IF NOT EXISTS commons_publication_editions_publication_published ON commons_publication_editions(publication_id, published_at DESC)`, args: [] },
|
|
31130
31185
|
{
|
|
31131
31186
|
sql: "INSERT OR IGNORE INTO schema_migrations (version) VALUES (?)",
|
|
31132
31187
|
args: [COMMONS_SCHEMA_VERSION]
|
|
@@ -31243,7 +31298,7 @@ async function prepareCommonsEntity(input, user) {
|
|
|
31243
31298
|
presentHeadings: headings,
|
|
31244
31299
|
recommendedSections: contract.recommendedSections,
|
|
31245
31300
|
optionalSections: contract.optionalSections,
|
|
31246
|
-
missingRecommendedSections: contract.recommendedSections.filter((
|
|
31301
|
+
missingRecommendedSections: contract.recommendedSections.filter((section2) => !presentHeadingLabels.has(section2.toLowerCase())),
|
|
31247
31302
|
rule: "Use the entity profile as a guide, but omit any section or subsection that is not supported by source evidence."
|
|
31248
31303
|
},
|
|
31249
31304
|
instructions: [
|
|
@@ -31316,8 +31371,8 @@ async function validateCommonsEntity(input, user) {
|
|
|
31316
31371
|
headings,
|
|
31317
31372
|
placeholderSections,
|
|
31318
31373
|
requiredOmissionRule: "Omit unsupported profile sections; do not publish sections whose only content is that the source lacks evidence.",
|
|
31319
|
-
supportedRecommendedSections: contract.recommendedSections.filter((
|
|
31320
|
-
unsupportedRecommendedSections: contract.recommendedSections.filter((
|
|
31374
|
+
supportedRecommendedSections: contract.recommendedSections.filter((section2) => headings.some((heading) => heading.text.toLowerCase() === section2.toLowerCase())),
|
|
31375
|
+
unsupportedRecommendedSections: contract.recommendedSections.filter((section2) => !headings.some((heading) => heading.text.toLowerCase() === section2.toLowerCase()))
|
|
31321
31376
|
},
|
|
31322
31377
|
tagResolutions: resolveCommonsTags(
|
|
31323
31378
|
uniqueStrings([...input.tags ?? [], ...input.keywords ?? []]).slice(0, 20),
|
|
@@ -31842,7 +31897,7 @@ function unsupportedPlaceholderSections(markdown) {
|
|
|
31842
31897
|
/insufficient evidence/i,
|
|
31843
31898
|
/source material does not/i
|
|
31844
31899
|
];
|
|
31845
|
-
return sections.filter((
|
|
31900
|
+
return sections.filter((section2) => placeholderPatterns.some((pattern) => pattern.test(section2.body)) && wordCount(section2.body) <= 160).map((section2) => section2.heading);
|
|
31846
31901
|
}
|
|
31847
31902
|
function markdownSectionBodies(markdown) {
|
|
31848
31903
|
const lines = String(markdown || "").split(/\r?\n/);
|
|
@@ -32326,18 +32381,18 @@ function normalizeBody(bodyMarkdown, contentSections) {
|
|
|
32326
32381
|
const body = String(bodyMarkdown ?? "").trim();
|
|
32327
32382
|
if (body) return body.slice(0, 5e5);
|
|
32328
32383
|
const sections = normalizeContentSections(contentSections);
|
|
32329
|
-
return sections.map((
|
|
32384
|
+
return sections.map((section2) => `## ${section2.heading}
|
|
32330
32385
|
|
|
32331
|
-
${
|
|
32386
|
+
${section2.body}`).join("\n\n").slice(0, 5e5);
|
|
32332
32387
|
}
|
|
32333
32388
|
function normalizeContentSections(sections) {
|
|
32334
32389
|
if (!Array.isArray(sections)) return [];
|
|
32335
|
-
return sections.map((
|
|
32336
|
-
id: normalizeSlug(
|
|
32337
|
-
heading: requiredText(
|
|
32338
|
-
body: requiredText(
|
|
32339
|
-
position: Number.isFinite(
|
|
32340
|
-
citations: normalizeCitations(
|
|
32390
|
+
return sections.map((section2, index) => ({
|
|
32391
|
+
id: normalizeSlug(section2.id || section2.heading),
|
|
32392
|
+
heading: requiredText(section2.heading, "section.heading", 1, 180),
|
|
32393
|
+
body: requiredText(section2.body, "section.body", 1, 1e5),
|
|
32394
|
+
position: Number.isFinite(section2.position) ? Number(section2.position) : index + 1,
|
|
32395
|
+
citations: normalizeCitations(section2.citations)
|
|
32341
32396
|
})).slice(0, 80);
|
|
32342
32397
|
}
|
|
32343
32398
|
function normalizeArticleSections(sections) {
|
|
@@ -32704,6 +32759,392 @@ var CommonsRepositoryError = class extends Error {
|
|
|
32704
32759
|
httpStatus;
|
|
32705
32760
|
};
|
|
32706
32761
|
|
|
32762
|
+
// src/api/commons-publication-repository.ts
|
|
32763
|
+
import { createHash as createHash13, randomUUID as randomUUID16 } from "crypto";
|
|
32764
|
+
var PUBLICATION_ROOT_DOMAIN = process.env.COMMONS_PUBLICATION_ROOT_DOMAIN || "transparent-commons.cc";
|
|
32765
|
+
var RESERVED_SUBDOMAINS = /* @__PURE__ */ new Set([
|
|
32766
|
+
"admin",
|
|
32767
|
+
"api",
|
|
32768
|
+
"app",
|
|
32769
|
+
"assets",
|
|
32770
|
+
"auth",
|
|
32771
|
+
"blog",
|
|
32772
|
+
"cdn",
|
|
32773
|
+
"docs",
|
|
32774
|
+
"help",
|
|
32775
|
+
"login",
|
|
32776
|
+
"mail",
|
|
32777
|
+
"mcp",
|
|
32778
|
+
"signup",
|
|
32779
|
+
"sitemap",
|
|
32780
|
+
"static",
|
|
32781
|
+
"support",
|
|
32782
|
+
"transparent-commons",
|
|
32783
|
+
"transparentcommons",
|
|
32784
|
+
"www",
|
|
32785
|
+
"wiki"
|
|
32786
|
+
]);
|
|
32787
|
+
var CommonsPublicationError = class extends Error {
|
|
32788
|
+
constructor(code, message, httpStatus = 400) {
|
|
32789
|
+
super(message);
|
|
32790
|
+
this.code = code;
|
|
32791
|
+
this.httpStatus = httpStatus;
|
|
32792
|
+
this.name = "CommonsPublicationError";
|
|
32793
|
+
}
|
|
32794
|
+
code;
|
|
32795
|
+
httpStatus;
|
|
32796
|
+
};
|
|
32797
|
+
async function prepareCommonsPublication(input, user) {
|
|
32798
|
+
await ensureCommonsSchema();
|
|
32799
|
+
const subdomain = normalizePublicationSubdomain(input.requestedSubdomain);
|
|
32800
|
+
const [owned, claimed] = await Promise.all([
|
|
32801
|
+
getCommonsPublicationForOwner(Number(user.id)),
|
|
32802
|
+
getCommonsPublicationBySubdomain(subdomain)
|
|
32803
|
+
]);
|
|
32804
|
+
const availability = claimed ? claimed.ownerUserId === Number(user.id) ? "owned_by_caller" : "unavailable" : "available";
|
|
32805
|
+
return {
|
|
32806
|
+
requestedSubdomain: input.requestedSubdomain,
|
|
32807
|
+
normalizedSubdomain: subdomain,
|
|
32808
|
+
availability,
|
|
32809
|
+
requestedPublicUrl: publicationPublicUrl(subdomain),
|
|
32810
|
+
currentPublication: owned,
|
|
32811
|
+
contract: {
|
|
32812
|
+
ownership: "One publication per authenticated MCP Scraper account.",
|
|
32813
|
+
permanence: "Claimed names are stable and globally unique.",
|
|
32814
|
+
renderer: "Published editions use the MCP Scraper editorial reading-room renderer.",
|
|
32815
|
+
revisionRule: "Editing an existing edition requires its current baseRevision.",
|
|
32816
|
+
publicRoutes: ["/", "/archive", "/editions/{editionSlug}"],
|
|
32817
|
+
workflow: [
|
|
32818
|
+
"commons_prepare_publication",
|
|
32819
|
+
"commons_validate_publication",
|
|
32820
|
+
"commons_claim_publication",
|
|
32821
|
+
"commons_publish_editorial"
|
|
32822
|
+
]
|
|
32823
|
+
},
|
|
32824
|
+
proposed: {
|
|
32825
|
+
title: cleanText(input.title, 140) || publicationTitleFromSubdomain(subdomain),
|
|
32826
|
+
description: cleanText(input.description, 500)
|
|
32827
|
+
}
|
|
32828
|
+
};
|
|
32829
|
+
}
|
|
32830
|
+
async function validateCommonsPublication(input, user) {
|
|
32831
|
+
await ensureCommonsSchema();
|
|
32832
|
+
const errors = [];
|
|
32833
|
+
const warnings = [];
|
|
32834
|
+
let subdomain = "";
|
|
32835
|
+
try {
|
|
32836
|
+
subdomain = normalizePublicationSubdomain(input.requestedSubdomain || input.publicationSubdomain || "");
|
|
32837
|
+
} catch (error) {
|
|
32838
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
32839
|
+
}
|
|
32840
|
+
const publication = subdomain ? await getCommonsPublicationBySubdomain(subdomain) : null;
|
|
32841
|
+
if (input.operation === "claim") {
|
|
32842
|
+
const owned = await getCommonsPublicationForOwner(Number(user.id));
|
|
32843
|
+
if (publication && publication.ownerUserId !== Number(user.id)) errors.push("That publication name is already claimed by another account.");
|
|
32844
|
+
if (owned && owned.subdomain !== subdomain) errors.push(`This account already owns ${owned.publicUrl}.`);
|
|
32845
|
+
if (!cleanText(input.title, 140)) warnings.push("No display title was supplied; the publication name will be title-cased.");
|
|
32846
|
+
} else {
|
|
32847
|
+
if (!publication) errors.push("Claim this publication name before publishing an edition.");
|
|
32848
|
+
else if (publication.ownerUserId !== Number(user.id)) errors.push("Only the account that claimed this publication can publish to it.");
|
|
32849
|
+
if (!input.edition) errors.push("A complete editorial reading-room edition is required for publish validation.");
|
|
32850
|
+
if (input.edition) {
|
|
32851
|
+
try {
|
|
32852
|
+
renderEditorialReadingRoom(input.edition);
|
|
32853
|
+
} catch (error) {
|
|
32854
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
32855
|
+
}
|
|
32856
|
+
const slug2 = normalizeEditionSlug(input.editionSlug || input.edition.site.slug);
|
|
32857
|
+
const latest = publication ? await getLatestEdition(publication.id, slug2) : null;
|
|
32858
|
+
if (latest && input.baseRevision === void 0) errors.push(`Edition ${slug2} already exists at revision ${latest.revision}; supply baseRevision to edit it.`);
|
|
32859
|
+
if (latest && input.baseRevision !== void 0 && latest.revision !== input.baseRevision) {
|
|
32860
|
+
errors.push(`Edition ${slug2} is revision ${latest.revision}; the proposed edit targeted ${input.baseRevision}.`);
|
|
32861
|
+
}
|
|
32862
|
+
}
|
|
32863
|
+
}
|
|
32864
|
+
return {
|
|
32865
|
+
valid: errors.length === 0,
|
|
32866
|
+
operation: input.operation,
|
|
32867
|
+
normalizedSubdomain: subdomain || null,
|
|
32868
|
+
publicUrl: subdomain ? publicationPublicUrl(subdomain) : null,
|
|
32869
|
+
errors,
|
|
32870
|
+
warnings,
|
|
32871
|
+
publication
|
|
32872
|
+
};
|
|
32873
|
+
}
|
|
32874
|
+
async function claimCommonsPublication(input, user) {
|
|
32875
|
+
await ensureCommonsSchema();
|
|
32876
|
+
const subdomain = normalizePublicationSubdomain(input.requestedSubdomain);
|
|
32877
|
+
const userId = Number(user.id);
|
|
32878
|
+
const existingOwned = await getCommonsPublicationForOwner(userId);
|
|
32879
|
+
if (existingOwned) {
|
|
32880
|
+
if (existingOwned.subdomain === subdomain) return { publication: existingOwned, idempotentReplay: true };
|
|
32881
|
+
throw new CommonsPublicationError("publication_already_claimed", `This account already owns ${existingOwned.publicUrl}.`, 409);
|
|
32882
|
+
}
|
|
32883
|
+
const existingName = await getCommonsPublicationBySubdomain(subdomain);
|
|
32884
|
+
if (existingName) throw new CommonsPublicationError("publication_name_unavailable", "That publication name is already claimed.", 409);
|
|
32885
|
+
const id = `tcpub_${randomUUID16()}`;
|
|
32886
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
32887
|
+
try {
|
|
32888
|
+
await getDb().execute({
|
|
32889
|
+
sql: `
|
|
32890
|
+
INSERT INTO commons_publications (
|
|
32891
|
+
id, owner_user_id, subdomain, title, description, claim_idempotency_key, created_at, updated_at
|
|
32892
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
32893
|
+
`,
|
|
32894
|
+
args: [
|
|
32895
|
+
id,
|
|
32896
|
+
userId,
|
|
32897
|
+
subdomain,
|
|
32898
|
+
cleanText(input.title, 140) || publicationTitleFromSubdomain(subdomain),
|
|
32899
|
+
cleanText(input.description, 500),
|
|
32900
|
+
requiredIdempotencyKey(input.idempotencyKey),
|
|
32901
|
+
now,
|
|
32902
|
+
now
|
|
32903
|
+
]
|
|
32904
|
+
});
|
|
32905
|
+
} catch (error) {
|
|
32906
|
+
const replay = await getCommonsPublicationForOwner(userId);
|
|
32907
|
+
if (replay?.subdomain === subdomain) return { publication: replay, idempotentReplay: true };
|
|
32908
|
+
throw error;
|
|
32909
|
+
}
|
|
32910
|
+
const publication = await getCommonsPublicationBySubdomain(subdomain);
|
|
32911
|
+
if (!publication) throw new CommonsPublicationError("publication_claim_failed", "The publication claim was not persisted.");
|
|
32912
|
+
return { publication, idempotentReplay: false };
|
|
32913
|
+
}
|
|
32914
|
+
async function publishCommonsEditorial(input, user) {
|
|
32915
|
+
await ensureCommonsSchema();
|
|
32916
|
+
const subdomain = normalizePublicationSubdomain(input.publicationSubdomain);
|
|
32917
|
+
const publication = await getCommonsPublicationBySubdomain(subdomain);
|
|
32918
|
+
if (!publication) throw new CommonsPublicationError("publication_not_found", "Claim this publication name before publishing an edition.", 404);
|
|
32919
|
+
if (publication.ownerUserId !== Number(user.id)) {
|
|
32920
|
+
throw new CommonsPublicationError("publication_not_owned", "Only the account that claimed this publication can publish to it.", 404);
|
|
32921
|
+
}
|
|
32922
|
+
const idempotencyKey3 = requiredIdempotencyKey(input.idempotencyKey);
|
|
32923
|
+
const replay = await getEditionByIdempotency(Number(user.id), idempotencyKey3);
|
|
32924
|
+
if (replay) return publicationResult(publication, replay, true);
|
|
32925
|
+
const editionSlug = normalizeEditionSlug(input.editionSlug || input.site.slug);
|
|
32926
|
+
const latest = await getLatestEdition(publication.id, editionSlug);
|
|
32927
|
+
if (latest && input.baseRevision === void 0) {
|
|
32928
|
+
throw new CommonsPublicationError("publication_base_revision_required", `Edition ${editionSlug} already exists at revision ${latest.revision}; supply baseRevision to edit it.`, 409);
|
|
32929
|
+
}
|
|
32930
|
+
if (latest && input.baseRevision !== latest.revision) {
|
|
32931
|
+
throw new CommonsPublicationError("publication_revision_conflict", `Edition ${editionSlug} is revision ${latest.revision}; refresh it before publishing an edit.`, 409);
|
|
32932
|
+
}
|
|
32933
|
+
const { publicationSubdomain: _publicationSubdomain, editionSlug: _editionSlug, idempotencyKey: _idempotencyKey, baseRevision: _baseRevision, ...editionInput } = input;
|
|
32934
|
+
const canonicalUrl = editionPublicUrl(subdomain, editionSlug);
|
|
32935
|
+
const rendered = renderEditorialReadingRoom(editionInput);
|
|
32936
|
+
const html = addPublicMetadata(rendered.html, canonicalUrl, publication.title);
|
|
32937
|
+
const editionId = `tced_${randomUUID16()}`;
|
|
32938
|
+
const revision = (latest?.revision ?? 0) + 1;
|
|
32939
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
32940
|
+
await getDb().batch([
|
|
32941
|
+
{
|
|
32942
|
+
sql: `
|
|
32943
|
+
INSERT INTO commons_publication_editions (
|
|
32944
|
+
id, publication_id, owner_user_id, edition_slug, revision, title, site_json, deck,
|
|
32945
|
+
articles_json, html, filename, sha256, article_count, word_count, bytes, warnings_json,
|
|
32946
|
+
idempotency_key, created_at, published_at
|
|
32947
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
32948
|
+
`,
|
|
32949
|
+
args: [
|
|
32950
|
+
editionId,
|
|
32951
|
+
publication.id,
|
|
32952
|
+
Number(user.id),
|
|
32953
|
+
editionSlug,
|
|
32954
|
+
revision,
|
|
32955
|
+
editionInput.site.title,
|
|
32956
|
+
JSON.stringify(editionInput.site),
|
|
32957
|
+
editionInput.deck,
|
|
32958
|
+
JSON.stringify(editionInput.articles),
|
|
32959
|
+
html,
|
|
32960
|
+
rendered.filename,
|
|
32961
|
+
createHash13("sha256").update(html).digest("hex"),
|
|
32962
|
+
rendered.articleCount,
|
|
32963
|
+
rendered.wordCount,
|
|
32964
|
+
Buffer.byteLength(html),
|
|
32965
|
+
JSON.stringify(rendered.warnings),
|
|
32966
|
+
idempotencyKey3,
|
|
32967
|
+
now,
|
|
32968
|
+
now
|
|
32969
|
+
]
|
|
32970
|
+
},
|
|
32971
|
+
{
|
|
32972
|
+
sql: "UPDATE commons_publications SET latest_edition_id = ?, updated_at = ? WHERE id = ? AND owner_user_id = ?",
|
|
32973
|
+
args: [editionId, now, publication.id, Number(user.id)]
|
|
32974
|
+
}
|
|
32975
|
+
], "write");
|
|
32976
|
+
const edition = await getPublicationEditionById(editionId);
|
|
32977
|
+
if (!edition) throw new CommonsPublicationError("publication_publish_failed", "The published edition was not persisted.");
|
|
32978
|
+
return publicationResult({ ...publication, latestEditionId: editionId, updatedAt: now }, edition, false);
|
|
32979
|
+
}
|
|
32980
|
+
async function getCommonsPublicationBySubdomain(subdomainInput) {
|
|
32981
|
+
await ensureCommonsSchema();
|
|
32982
|
+
const subdomain = normalizePublicationSubdomain(subdomainInput);
|
|
32983
|
+
const result = await getDb().execute({ sql: "SELECT * FROM commons_publications WHERE subdomain = ? LIMIT 1", args: [subdomain] });
|
|
32984
|
+
return result.rows[0] ? rowToPublication(result.rows[0]) : null;
|
|
32985
|
+
}
|
|
32986
|
+
async function getCommonsPublicationForOwner(userId) {
|
|
32987
|
+
await ensureCommonsSchema();
|
|
32988
|
+
const result = await getDb().execute({ sql: "SELECT * FROM commons_publications WHERE owner_user_id = ? LIMIT 1", args: [userId] });
|
|
32989
|
+
return result.rows[0] ? rowToPublication(result.rows[0]) : null;
|
|
32990
|
+
}
|
|
32991
|
+
async function listCommonsPublicationEditions(publicationId) {
|
|
32992
|
+
await ensureCommonsSchema();
|
|
32993
|
+
const result = await getDb().execute({
|
|
32994
|
+
sql: `
|
|
32995
|
+
SELECT edition.* FROM commons_publication_editions edition
|
|
32996
|
+
JOIN (
|
|
32997
|
+
SELECT edition_slug, MAX(revision) AS revision
|
|
32998
|
+
FROM commons_publication_editions WHERE publication_id = ? GROUP BY edition_slug
|
|
32999
|
+
) latest ON latest.edition_slug = edition.edition_slug AND latest.revision = edition.revision
|
|
33000
|
+
WHERE edition.publication_id = ? ORDER BY edition.published_at DESC
|
|
33001
|
+
`,
|
|
33002
|
+
args: [publicationId, publicationId]
|
|
33003
|
+
});
|
|
33004
|
+
const publication = await getPublicationById(publicationId);
|
|
33005
|
+
if (!publication) return [];
|
|
33006
|
+
return result.rows.map((row) => rowToEdition(row, publication.subdomain));
|
|
33007
|
+
}
|
|
33008
|
+
async function getCommonsPublicationEditionHtml(subdomainInput, editionSlugInput) {
|
|
33009
|
+
const publication = await getCommonsPublicationBySubdomain(subdomainInput);
|
|
33010
|
+
if (!publication) return null;
|
|
33011
|
+
const result = editionSlugInput ? await getDb().execute({
|
|
33012
|
+
sql: "SELECT * FROM commons_publication_editions WHERE publication_id = ? AND edition_slug = ? ORDER BY revision DESC LIMIT 1",
|
|
33013
|
+
args: [publication.id, normalizeEditionSlug(editionSlugInput)]
|
|
33014
|
+
}) : await getDb().execute({
|
|
33015
|
+
sql: "SELECT * FROM commons_publication_editions WHERE id = ? AND publication_id = ? LIMIT 1",
|
|
33016
|
+
args: [publication.latestEditionId || "", publication.id]
|
|
33017
|
+
});
|
|
33018
|
+
if (!result.rows[0]) return null;
|
|
33019
|
+
const record = result.rows[0];
|
|
33020
|
+
return { publication, edition: rowToEdition(record, publication.subdomain), html: String(record.html || "") };
|
|
33021
|
+
}
|
|
33022
|
+
async function getPublicationById(id) {
|
|
33023
|
+
const result = await getDb().execute({ sql: "SELECT * FROM commons_publications WHERE id = ? LIMIT 1", args: [id] });
|
|
33024
|
+
return result.rows[0] ? rowToPublication(result.rows[0]) : null;
|
|
33025
|
+
}
|
|
33026
|
+
async function getLatestEdition(publicationId, editionSlug) {
|
|
33027
|
+
const publication = await getPublicationById(publicationId);
|
|
33028
|
+
if (!publication) return null;
|
|
33029
|
+
const result = await getDb().execute({
|
|
33030
|
+
sql: "SELECT * FROM commons_publication_editions WHERE publication_id = ? AND edition_slug = ? ORDER BY revision DESC LIMIT 1",
|
|
33031
|
+
args: [publicationId, editionSlug]
|
|
33032
|
+
});
|
|
33033
|
+
return result.rows[0] ? rowToEdition(result.rows[0], publication.subdomain) : null;
|
|
33034
|
+
}
|
|
33035
|
+
async function getEditionByIdempotency(userId, idempotencyKey3) {
|
|
33036
|
+
const result = await getDb().execute({
|
|
33037
|
+
sql: `SELECT edition.*, publication.subdomain FROM commons_publication_editions edition JOIN commons_publications publication ON publication.id = edition.publication_id WHERE edition.owner_user_id = ? AND edition.idempotency_key = ? LIMIT 1`,
|
|
33038
|
+
args: [userId, idempotencyKey3]
|
|
33039
|
+
});
|
|
33040
|
+
if (!result.rows[0]) return null;
|
|
33041
|
+
const record = result.rows[0];
|
|
33042
|
+
return rowToEdition(record, String(record.subdomain));
|
|
33043
|
+
}
|
|
33044
|
+
async function getPublicationEditionById(id) {
|
|
33045
|
+
const result = await getDb().execute({
|
|
33046
|
+
sql: `SELECT edition.*, publication.subdomain FROM commons_publication_editions edition JOIN commons_publications publication ON publication.id = edition.publication_id WHERE edition.id = ? LIMIT 1`,
|
|
33047
|
+
args: [id]
|
|
33048
|
+
});
|
|
33049
|
+
if (!result.rows[0]) return null;
|
|
33050
|
+
const record = result.rows[0];
|
|
33051
|
+
return rowToEdition(record, String(record.subdomain));
|
|
33052
|
+
}
|
|
33053
|
+
function rowToPublication(row) {
|
|
33054
|
+
const subdomain = String(row.subdomain);
|
|
33055
|
+
return {
|
|
33056
|
+
id: String(row.id),
|
|
33057
|
+
ownerUserId: Number(row.owner_user_id),
|
|
33058
|
+
subdomain,
|
|
33059
|
+
title: String(row.title),
|
|
33060
|
+
description: String(row.description || ""),
|
|
33061
|
+
latestEditionId: row.latest_edition_id ? String(row.latest_edition_id) : null,
|
|
33062
|
+
createdAt: String(row.created_at),
|
|
33063
|
+
updatedAt: String(row.updated_at),
|
|
33064
|
+
publicUrl: publicationPublicUrl(subdomain),
|
|
33065
|
+
archiveUrl: `${publicationPublicUrl(subdomain)}/archive`
|
|
33066
|
+
};
|
|
33067
|
+
}
|
|
33068
|
+
function rowToEdition(row, subdomain) {
|
|
33069
|
+
const editionSlug = String(row.edition_slug);
|
|
33070
|
+
return {
|
|
33071
|
+
id: String(row.id),
|
|
33072
|
+
publicationId: String(row.publication_id),
|
|
33073
|
+
editionSlug,
|
|
33074
|
+
revision: Number(row.revision),
|
|
33075
|
+
title: String(row.title),
|
|
33076
|
+
site: parseJson4(row.site_json, {}),
|
|
33077
|
+
deck: String(row.deck),
|
|
33078
|
+
filename: String(row.filename),
|
|
33079
|
+
sha256: String(row.sha256),
|
|
33080
|
+
articleCount: Number(row.article_count),
|
|
33081
|
+
wordCount: Number(row.word_count),
|
|
33082
|
+
bytes: Number(row.bytes),
|
|
33083
|
+
warnings: parseJson4(row.warnings_json, []),
|
|
33084
|
+
createdAt: String(row.created_at),
|
|
33085
|
+
publishedAt: String(row.published_at),
|
|
33086
|
+
publicUrl: editionPublicUrl(subdomain, editionSlug)
|
|
33087
|
+
};
|
|
33088
|
+
}
|
|
33089
|
+
function publicationResult(publication, edition, idempotentReplay) {
|
|
33090
|
+
return {
|
|
33091
|
+
publication,
|
|
33092
|
+
edition,
|
|
33093
|
+
publicUrl: publication.publicUrl,
|
|
33094
|
+
archiveUrl: publication.archiveUrl,
|
|
33095
|
+
editionUrl: edition.publicUrl,
|
|
33096
|
+
idempotentReplay
|
|
33097
|
+
};
|
|
33098
|
+
}
|
|
33099
|
+
function normalizePublicationSubdomain(value) {
|
|
33100
|
+
const normalized = String(value || "").trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
33101
|
+
if (!/^[a-z0-9](?:[a-z0-9-]{1,48}[a-z0-9])$/.test(normalized)) {
|
|
33102
|
+
throw new CommonsPublicationError("publication_name_invalid", "Choose a publication name between 3 and 50 characters using letters, numbers, and interior hyphens.");
|
|
33103
|
+
}
|
|
33104
|
+
if (RESERVED_SUBDOMAINS.has(normalized)) throw new CommonsPublicationError("publication_name_reserved", "That publication name is reserved; choose a more specific name.", 409);
|
|
33105
|
+
return normalized;
|
|
33106
|
+
}
|
|
33107
|
+
function normalizeEditionSlug(value) {
|
|
33108
|
+
const normalized = String(value || "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 80);
|
|
33109
|
+
if (!normalized) throw new CommonsPublicationError("edition_slug_invalid", "A published edition needs a stable slug.");
|
|
33110
|
+
return normalized;
|
|
33111
|
+
}
|
|
33112
|
+
function requiredIdempotencyKey(value) {
|
|
33113
|
+
const normalized = cleanText(value, 200);
|
|
33114
|
+
if (normalized.length < 8) throw new CommonsPublicationError("idempotency_key_invalid", "Use an idempotency key of at least 8 characters and reuse it only when retrying the same write.");
|
|
33115
|
+
return normalized;
|
|
33116
|
+
}
|
|
33117
|
+
function publicationPublicUrl(subdomain) {
|
|
33118
|
+
return `https://${subdomain}.${PUBLICATION_ROOT_DOMAIN}`;
|
|
33119
|
+
}
|
|
33120
|
+
function editionPublicUrl(subdomain, editionSlug) {
|
|
33121
|
+
return `${publicationPublicUrl(subdomain)}/editions/${encodeURIComponent(editionSlug)}`;
|
|
33122
|
+
}
|
|
33123
|
+
function publicationTitleFromSubdomain(subdomain) {
|
|
33124
|
+
return subdomain.split("-").map((part) => part ? `${part[0]?.toUpperCase()}${part.slice(1)}` : "").join(" ");
|
|
33125
|
+
}
|
|
33126
|
+
function cleanText(value, max) {
|
|
33127
|
+
return String(value || "").replace(/\s+/g, " ").trim().slice(0, max);
|
|
33128
|
+
}
|
|
33129
|
+
function parseJson4(value, fallback) {
|
|
33130
|
+
try {
|
|
33131
|
+
return JSON.parse(String(value || ""));
|
|
33132
|
+
} catch {
|
|
33133
|
+
return fallback;
|
|
33134
|
+
}
|
|
33135
|
+
}
|
|
33136
|
+
function addPublicMetadata(html, canonicalUrl, publicationTitle) {
|
|
33137
|
+
const escapedUrl = canonicalUrl.replace(/&/g, "&").replace(/"/g, """);
|
|
33138
|
+
const escapedTitle = publicationTitle.replace(/&/g, "&").replace(/"/g, """);
|
|
33139
|
+
return html.replace("</head>", [
|
|
33140
|
+
` <link rel="canonical" href="${escapedUrl}">`,
|
|
33141
|
+
` <meta property="og:url" content="${escapedUrl}">`,
|
|
33142
|
+
` <meta property="og:site_name" content="${escapedTitle}">`,
|
|
33143
|
+
' <meta name="robots" content="index,follow">',
|
|
33144
|
+
"</head>"
|
|
33145
|
+
].join("\n"));
|
|
33146
|
+
}
|
|
33147
|
+
|
|
32707
33148
|
// src/api/commons-routes.ts
|
|
32708
33149
|
var commonsApp = new Hono31();
|
|
32709
33150
|
var auth2 = createApiKeyAuth();
|
|
@@ -32865,10 +33306,83 @@ var NeedsLinkBodySchema = z32.object({
|
|
|
32865
33306
|
limit: z32.number().int().min(1).max(100).optional(),
|
|
32866
33307
|
offset: z32.number().int().min(0).max(1e4).optional()
|
|
32867
33308
|
}).strict();
|
|
33309
|
+
var PreparePublicationSchema = z32.object(CommonsPreparePublicationInputSchema).strict();
|
|
33310
|
+
var ValidatePublicationSchema = z32.object(CommonsValidatePublicationInputSchema).strict();
|
|
33311
|
+
var ClaimPublicationSchema = z32.object(CommonsClaimPublicationInputSchema).strict();
|
|
33312
|
+
var PublishEditorialSchema = z32.object(CommonsPublishEditorialInputSchema).strict();
|
|
32868
33313
|
commonsApp.get("/health", async (c) => {
|
|
32869
33314
|
await ensureCommonsSchema();
|
|
32870
33315
|
return c.json({ ok: true, data: commonsDatabaseReport() });
|
|
32871
33316
|
});
|
|
33317
|
+
commonsApp.post("/publications/prepare", auth2, async (c) => {
|
|
33318
|
+
const parsed = PreparePublicationSchema.safeParse(await c.req.json().catch(() => ({})));
|
|
33319
|
+
if (!parsed.success) return validationError(c, parsed.error);
|
|
33320
|
+
return publicationOperation(c, () => prepareCommonsPublication(parsed.data, c.get("user")));
|
|
33321
|
+
});
|
|
33322
|
+
commonsApp.post("/publications/validate", auth2, async (c) => {
|
|
33323
|
+
const parsed = ValidatePublicationSchema.safeParse(await c.req.json().catch(() => ({})));
|
|
33324
|
+
if (!parsed.success) return validationError(c, parsed.error);
|
|
33325
|
+
return publicationOperation(c, () => validateCommonsPublication(parsed.data, c.get("user")));
|
|
33326
|
+
});
|
|
33327
|
+
commonsApp.post("/publications/claim", auth2, async (c) => {
|
|
33328
|
+
const parsed = ClaimPublicationSchema.safeParse(await c.req.json().catch(() => ({})));
|
|
33329
|
+
if (!parsed.success) return validationError(c, parsed.error);
|
|
33330
|
+
return publicationOperation(c, () => claimCommonsPublication(parsed.data, c.get("user")), 201);
|
|
33331
|
+
});
|
|
33332
|
+
commonsApp.post("/publications/publish", auth2, async (c) => {
|
|
33333
|
+
const parsed = PublishEditorialSchema.safeParse(await c.req.json().catch(() => ({})));
|
|
33334
|
+
if (!parsed.success) return validationError(c, parsed.error);
|
|
33335
|
+
return publicationOperation(c, () => publishCommonsEditorial(parsed.data, c.get("user")), 201);
|
|
33336
|
+
});
|
|
33337
|
+
commonsApp.get("/publications/me", auth2, async (c) => {
|
|
33338
|
+
const publication = await getCommonsPublicationForOwner(Number(c.get("user").id));
|
|
33339
|
+
if (!publication) return c.json({ ok: false, error: "publication_not_found", message: "This account has not claimed a Commons publication yet." }, 404);
|
|
33340
|
+
const editions = c.req.query("includeEditions") === "false" ? [] : await listCommonsPublicationEditions(publication.id);
|
|
33341
|
+
return c.json({ ok: true, data: { publication, editions } });
|
|
33342
|
+
});
|
|
33343
|
+
commonsApp.get("/publications/:subdomain/site", async (c) => {
|
|
33344
|
+
try {
|
|
33345
|
+
const result = await getCommonsPublicationEditionHtml(c.req.param("subdomain"));
|
|
33346
|
+
if (!result) return c.json({ ok: false, error: "publication_not_published", message: "No published edition exists for this publication." }, 404);
|
|
33347
|
+
return c.html(result.html, 200, {
|
|
33348
|
+
"cache-control": "public, max-age=60, s-maxage=300",
|
|
33349
|
+
"x-robots-tag": "index, follow"
|
|
33350
|
+
});
|
|
33351
|
+
} catch (error) {
|
|
33352
|
+
return publicationError(c, error);
|
|
33353
|
+
}
|
|
33354
|
+
});
|
|
33355
|
+
commonsApp.get("/publications/:subdomain/editions/:editionSlug/site", async (c) => {
|
|
33356
|
+
try {
|
|
33357
|
+
const result = await getCommonsPublicationEditionHtml(c.req.param("subdomain"), c.req.param("editionSlug"));
|
|
33358
|
+
if (!result) return c.json({ ok: false, error: "edition_not_found", message: "No published edition matched that publication and slug." }, 404);
|
|
33359
|
+
return c.html(result.html, 200, {
|
|
33360
|
+
"cache-control": "public, max-age=60, s-maxage=300",
|
|
33361
|
+
"x-robots-tag": "index, follow"
|
|
33362
|
+
});
|
|
33363
|
+
} catch (error) {
|
|
33364
|
+
return publicationError(c, error);
|
|
33365
|
+
}
|
|
33366
|
+
});
|
|
33367
|
+
commonsApp.get("/publications/:subdomain/editions", async (c) => {
|
|
33368
|
+
try {
|
|
33369
|
+
const publication = await getCommonsPublicationBySubdomain(c.req.param("subdomain"));
|
|
33370
|
+
if (!publication) return c.json({ ok: false, error: "publication_not_found", message: "No Commons publication matched that name." }, 404);
|
|
33371
|
+
return c.json({ ok: true, data: { publication, editions: await listCommonsPublicationEditions(publication.id) } });
|
|
33372
|
+
} catch (error) {
|
|
33373
|
+
return publicationError(c, error);
|
|
33374
|
+
}
|
|
33375
|
+
});
|
|
33376
|
+
commonsApp.get("/publications/:subdomain", async (c) => {
|
|
33377
|
+
try {
|
|
33378
|
+
const publication = await getCommonsPublicationBySubdomain(c.req.param("subdomain"));
|
|
33379
|
+
if (!publication) return c.json({ ok: false, error: "publication_not_found", message: "No Commons publication matched that name." }, 404);
|
|
33380
|
+
const editions = c.req.query("includeEditions") === "false" ? [] : await listCommonsPublicationEditions(publication.id);
|
|
33381
|
+
return c.json({ ok: true, data: { publication, editions } });
|
|
33382
|
+
} catch (error) {
|
|
33383
|
+
return publicationError(c, error);
|
|
33384
|
+
}
|
|
33385
|
+
});
|
|
32872
33386
|
commonsApp.get("/entities", async (c) => {
|
|
32873
33387
|
const result = await searchCommonsEntities(filtersFromQuery(c.req.query()));
|
|
32874
33388
|
return c.json({ ok: true, data: result });
|
|
@@ -33030,6 +33544,19 @@ function repositoryStatus(error) {
|
|
|
33030
33544
|
if (error.httpStatus === 409) return 409;
|
|
33031
33545
|
return 400;
|
|
33032
33546
|
}
|
|
33547
|
+
async function publicationOperation(c, operation, successStatus = 200) {
|
|
33548
|
+
try {
|
|
33549
|
+
return c.json({ ok: true, data: await operation() }, successStatus);
|
|
33550
|
+
} catch (error) {
|
|
33551
|
+
return publicationError(c, error);
|
|
33552
|
+
}
|
|
33553
|
+
}
|
|
33554
|
+
function publicationError(c, error) {
|
|
33555
|
+
if (error instanceof CommonsPublicationError) {
|
|
33556
|
+
return c.json({ ok: false, error: error.code, message: error.message }, error.httpStatus);
|
|
33557
|
+
}
|
|
33558
|
+
throw error;
|
|
33559
|
+
}
|
|
33033
33560
|
function xmlEscape(value) {
|
|
33034
33561
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
33035
33562
|
}
|
|
@@ -33039,13 +33566,13 @@ import { Hono as Hono32 } from "hono";
|
|
|
33039
33566
|
import { z as z33 } from "zod";
|
|
33040
33567
|
|
|
33041
33568
|
// src/api/scheduled-artifact-owner.ts
|
|
33042
|
-
import { createHash as
|
|
33569
|
+
import { createHash as createHash14 } from "crypto";
|
|
33043
33570
|
function scheduledArtifactOwnerIdForApiKey(apiKey) {
|
|
33044
|
-
return
|
|
33571
|
+
return createHash14("sha256").update(apiKey).digest("hex").slice(0, 24);
|
|
33045
33572
|
}
|
|
33046
33573
|
|
|
33047
33574
|
// src/api/scheduled-run-view-links.ts
|
|
33048
|
-
import { createHash as
|
|
33575
|
+
import { createHash as createHash15, randomBytes as randomBytes3, randomUUID as randomUUID17 } from "crypto";
|
|
33049
33576
|
var schemaReady3 = false;
|
|
33050
33577
|
async function ensureScheduledRunViewLinksSchema() {
|
|
33051
33578
|
if (schemaReady3) return;
|
|
@@ -33058,13 +33585,36 @@ async function ensureScheduledRunViewLinksSchema() {
|
|
|
33058
33585
|
artifact_id TEXT NOT NULL,
|
|
33059
33586
|
title TEXT NOT NULL,
|
|
33060
33587
|
summary TEXT NOT NULL,
|
|
33061
|
-
renderer_key TEXT NOT NULL CHECK (renderer_key
|
|
33588
|
+
renderer_key TEXT NOT NULL CHECK (renderer_key IN ('editorial_reading_room_v1','personal_authority_v1')),
|
|
33062
33589
|
token_hash TEXT NOT NULL UNIQUE,
|
|
33063
33590
|
expires_at TEXT NOT NULL,
|
|
33064
33591
|
revoked_at TEXT,
|
|
33065
33592
|
created_at TEXT NOT NULL
|
|
33066
33593
|
)
|
|
33067
33594
|
`);
|
|
33595
|
+
const schema = await db.execute({
|
|
33596
|
+
sql: `SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'scheduled_run_view_links' LIMIT 1`,
|
|
33597
|
+
args: []
|
|
33598
|
+
});
|
|
33599
|
+
const createSql = String(schema.rows[0]?.sql ?? "");
|
|
33600
|
+
if (createSql.includes("renderer_key = 'editorial_reading_room_v1'")) {
|
|
33601
|
+
await db.batch([
|
|
33602
|
+
`DROP TABLE IF EXISTS scheduled_run_view_links_v2`,
|
|
33603
|
+
`CREATE TABLE scheduled_run_view_links_v2 (
|
|
33604
|
+
share_id TEXT PRIMARY KEY, owner_id TEXT NOT NULL, run_id TEXT NOT NULL,
|
|
33605
|
+
artifact_id TEXT NOT NULL, title TEXT NOT NULL, summary TEXT NOT NULL,
|
|
33606
|
+
renderer_key TEXT NOT NULL CHECK (renderer_key IN ('editorial_reading_room_v1','personal_authority_v1')),
|
|
33607
|
+
token_hash TEXT NOT NULL UNIQUE, expires_at TEXT NOT NULL, revoked_at TEXT,
|
|
33608
|
+
created_at TEXT NOT NULL
|
|
33609
|
+
)`,
|
|
33610
|
+
`INSERT INTO scheduled_run_view_links_v2
|
|
33611
|
+
SELECT share_id, owner_id, run_id, artifact_id, title, summary, renderer_key,
|
|
33612
|
+
token_hash, expires_at, revoked_at, created_at
|
|
33613
|
+
FROM scheduled_run_view_links`,
|
|
33614
|
+
`DROP TABLE scheduled_run_view_links`,
|
|
33615
|
+
`ALTER TABLE scheduled_run_view_links_v2 RENAME TO scheduled_run_view_links`
|
|
33616
|
+
], "write");
|
|
33617
|
+
}
|
|
33068
33618
|
await db.execute(`
|
|
33069
33619
|
CREATE INDEX IF NOT EXISTS scheduled_run_view_links_owner_run
|
|
33070
33620
|
ON scheduled_run_view_links(owner_id, run_id, created_at DESC)
|
|
@@ -33076,7 +33626,7 @@ async function ensureScheduledRunViewLinksSchema() {
|
|
|
33076
33626
|
schemaReady3 = true;
|
|
33077
33627
|
}
|
|
33078
33628
|
function tokenHash2(token4) {
|
|
33079
|
-
return
|
|
33629
|
+
return createHash15("sha256").update(token4).digest("hex");
|
|
33080
33630
|
}
|
|
33081
33631
|
function mapRow(row) {
|
|
33082
33632
|
return {
|
|
@@ -33086,7 +33636,7 @@ function mapRow(row) {
|
|
|
33086
33636
|
artifactId: String(row.artifact_id),
|
|
33087
33637
|
title: String(row.title),
|
|
33088
33638
|
summary: String(row.summary),
|
|
33089
|
-
rendererKey: "editorial_reading_room_v1",
|
|
33639
|
+
rendererKey: row.renderer_key === "personal_authority_v1" ? "personal_authority_v1" : "editorial_reading_room_v1",
|
|
33090
33640
|
tokenHash: String(row.token_hash),
|
|
33091
33641
|
expiresAt: String(row.expires_at),
|
|
33092
33642
|
revokedAt: row.revoked_at == null ? null : String(row.revoked_at),
|
|
@@ -33098,7 +33648,7 @@ async function createScheduledRunViewLink(input) {
|
|
|
33098
33648
|
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
33099
33649
|
const token4 = randomBytes3(32).toString("base64url");
|
|
33100
33650
|
const record = {
|
|
33101
|
-
shareId:
|
|
33651
|
+
shareId: randomUUID17(),
|
|
33102
33652
|
ownerId: input.ownerId,
|
|
33103
33653
|
runId: input.runId,
|
|
33104
33654
|
artifactId: input.artifactId,
|
|
@@ -33162,6 +33712,121 @@ async function revokeScheduledRunViewLink(ownerId, runId, shareId, now = /* @__P
|
|
|
33162
33712
|
return result.rowsAffected > 0;
|
|
33163
33713
|
}
|
|
33164
33714
|
|
|
33715
|
+
// src/personal-authority/render.ts
|
|
33716
|
+
import { createHash as createHash16 } from "crypto";
|
|
33717
|
+
function escapeHtml6(value) {
|
|
33718
|
+
return value.replace(/[&<>"']/g, (character) => ({
|
|
33719
|
+
"&": "&",
|
|
33720
|
+
"<": "<",
|
|
33721
|
+
">": ">",
|
|
33722
|
+
'"': """,
|
|
33723
|
+
"'": "'"
|
|
33724
|
+
})[character] ?? character);
|
|
33725
|
+
}
|
|
33726
|
+
function safeHref(value) {
|
|
33727
|
+
const trimmed = value.trim();
|
|
33728
|
+
return /^(?:https?:|mailto:|tel:|#|\/)/i.test(trimmed) ? escapeHtml6(trimmed) : "#";
|
|
33729
|
+
}
|
|
33730
|
+
function initials(name) {
|
|
33731
|
+
return name.split(/\s+/).filter(Boolean).slice(0, 2).map((part) => part[0]?.toUpperCase()).join("") || "PA";
|
|
33732
|
+
}
|
|
33733
|
+
function action(action2, secondary = false) {
|
|
33734
|
+
return `<a class="button${secondary ? " button--secondary" : ""}" href="${safeHref(action2.href)}">${escapeHtml6(action2.label)} <span aria-hidden="true">\u2197</span></a>`;
|
|
33735
|
+
}
|
|
33736
|
+
function section(enabled, html) {
|
|
33737
|
+
return enabled ? html : "";
|
|
33738
|
+
}
|
|
33739
|
+
function renderPersonalAuthorityV1(input, config, generatedAt) {
|
|
33740
|
+
const flags = config.featureFlags;
|
|
33741
|
+
const brand = config.brandName || input.name;
|
|
33742
|
+
const generatedAtIso = generatedAt.toISOString();
|
|
33743
|
+
const social = flags.socialLinks && input.socialLinks.length > 0 ? `<nav class="social" aria-label="Professional profiles">${input.socialLinks.map((link) => `<a href="${safeHref(link.href)}">${escapeHtml6(link.label)}</a>`).join("")}</nav>` : "";
|
|
33744
|
+
const navigation = flags.navigation ? `<button class="menu" type="button" aria-expanded="false" aria-controls="nav">Menu</button><nav id="nav" class="nav" aria-label="Primary navigation"><a href="#about">About</a><a href="#services">Work</a><a href="#proof">Proof</a><a href="#contact">Contact</a></nav>` : "";
|
|
33745
|
+
const authority = input.authority;
|
|
33746
|
+
const consultation = input.consultation;
|
|
33747
|
+
const learning = input.learning;
|
|
33748
|
+
const services = input.services;
|
|
33749
|
+
const proof = input.proof;
|
|
33750
|
+
const finalCta = input.finalCta;
|
|
33751
|
+
const html = `<!doctype html>
|
|
33752
|
+
<html lang="en" data-theme="${config.theme}">
|
|
33753
|
+
<head>
|
|
33754
|
+
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
33755
|
+
<meta name="description" content="${escapeHtml6(input.summary)}"><title>${escapeHtml6(input.name)} \u2014 ${escapeHtml6(input.professionalTitle)}</title>
|
|
33756
|
+
<style>
|
|
33757
|
+
:root{--blue:#2f66eb;--blue-soft:#dbe6ff;--ink:#050708;--paper:#f7f8fb;--white:#fff;--gray:#9ca5b5;--line:#27303b;--serif:Georgia,'Times New Roman',serif;--sans:Arial,Helvetica,sans-serif}*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:var(--ink);color:var(--white);font-family:var(--sans);line-height:1.55}a{color:inherit}.skip{position:absolute;left:-9999px}.skip:focus{left:16px;top:16px;z-index:9;background:#fff;color:#000;padding:10px}.header{height:88px;display:flex;align-items:center;justify-content:space-between;padding:0 clamp(20px,4vw,72px);border-bottom:1px solid var(--line);position:relative;z-index:3;background:rgba(5,7,8,.96)}.brand{display:flex;align-items:center;gap:12px;text-decoration:none;font-family:var(--serif);font-size:24px}.mark{width:40px;height:40px;border-radius:50%;display:grid;place-items:center;background:var(--blue);font-family:var(--sans);font-weight:700}.nav{display:flex;align-items:center;gap:28px}.nav a{text-decoration:none;color:#c8ced8;font-size:14px}.nav a:hover{color:#fff}.menu{display:none;background:transparent;color:#fff;border:1px solid #5b6471;padding:10px 14px}.hero{display:grid;grid-template-columns:minmax(0,.9fr) minmax(0,1.25fr);min-height:760px;border-bottom:3px solid var(--blue)}.portrait{display:grid;place-items:end center;background:radial-gradient(circle at 50% 38%,#273243 0,#11161d 38%,#060708 70%);overflow:hidden}.portrait-card{width:min(82%,560px);aspect-ratio:4/5;display:grid;place-items:center;align-self:end;border:1px solid #263142;border-bottom:0;background:linear-gradient(145deg,#252d38,#0b0d10 58%);font-family:var(--serif);font-size:clamp(80px,13vw,180px);color:#d9e4ff;text-shadow:0 0 42px rgba(47,102,235,.5)}.hero-copy{padding:clamp(44px,7vw,110px) clamp(24px,6vw,100px);display:flex;flex-direction:column;justify-content:center;background:linear-gradient(135deg,#080a0d,#101318)}h1,h2,h3,p{margin-top:0}.hero h1{font-family:var(--serif);font-size:clamp(58px,8vw,116px);line-height:.92;font-weight:400;color:var(--blue);margin-bottom:24px}.role{font-family:var(--serif);font-size:clamp(28px,3.8vw,52px);line-height:1.05;border-left:5px solid var(--blue);padding-left:22px;max-width:820px}.summary{color:#c6cbd3;font-size:clamp(17px,1.5vw,22px);max-width:760px}.actions{display:flex;gap:12px;flex-wrap:wrap;margin:24px 0}.button{display:inline-flex;align-items:center;justify-content:space-between;gap:28px;min-height:58px;padding:0 26px;background:var(--blue);border:1px solid var(--blue);text-decoration:none;font-weight:700}.button--secondary{background:transparent;border-color:#5c6572}.social{display:flex;gap:10px;flex-wrap:wrap}.social a{border:1px solid #39414d;border-radius:999px;padding:8px 13px;text-decoration:none;color:#d8dce4;font-size:13px}.light{background:var(--paper);color:#111722;padding:clamp(70px,10vw,150px) clamp(20px,6vw,90px)}.light h2,.section h2{font-family:var(--serif);font-size:clamp(42px,6vw,82px);line-height:1.02;font-weight:400;max-width:1080px}.light p{max-width:920px;color:#4e596a;font-size:18px}.section{padding:clamp(70px,9vw,130px) clamp(20px,6vw,90px);border-bottom:1px solid var(--line)}.section-head{max-width:920px;margin-bottom:46px}.eyebrow{text-transform:uppercase;letter-spacing:.18em;font-size:12px;color:#aab2bf}.consult{display:grid;grid-template-columns:1.3fr .7fr;gap:50px;background:linear-gradient(130deg,#060708,#111721)}.accent-card{min-height:360px;border:1px solid #2a3545;background:linear-gradient(145deg,rgba(47,102,235,.22),transparent 55%);display:grid;place-items:center;font-family:var(--serif);font-size:96px;color:var(--blue)}.grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}.card{border:1px solid #323a46;padding:26px;min-height:220px;background:#0b0e12}.card h3{font-family:var(--serif);font-size:30px}.card p{color:#b9c0ca}.learning{background:#020303}.topic-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1px;background:#29313c;border:1px solid #29313c}.topic{background:#050708;padding:30px}.topic strong{display:block;color:var(--blue);text-transform:uppercase;font-size:11px;letter-spacing:.15em}.topic h3{font-family:var(--serif);font-size:30px;margin:8px 0}.topic p{color:#aeb6c1}.proof{background:linear-gradient(180deg,#0a0d11,#050607)}.quotes{display:flex;gap:14px;overflow:auto;scroll-snap-type:x mandatory;padding-bottom:12px}.quote{min-width:min(420px,86vw);scroll-snap-align:start;border:1px solid #343d49;padding:30px;background:#0c1015}.quote q{font-family:var(--serif);font-size:30px;line-height:1.25}.quote span{display:block;margin-top:24px;color:#aeb5c0;font-size:12px;text-transform:uppercase;letter-spacing:.12em}.cta{text-align:center;border-top:3px solid var(--blue);background:radial-gradient(circle at 50% 0,rgba(47,102,235,.25),transparent 50%)}.cta h2{margin-left:auto;margin-right:auto}.footer{display:grid;grid-template-columns:1fr auto;gap:40px;padding:46px clamp(20px,6vw,90px);color:#abb3bf}.footer strong{color:#fff;font-family:var(--serif);font-size:26px}.generated{font-size:11px;color:#737d8b;margin-top:10px}html[data-theme='slate']{--blue:#7f95b8;--blue-soft:#dfe6ef}html[data-theme='monochrome']{--blue:#fff;--blue-soft:#e7e7e7}.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}
|
|
33758
|
+
@media(max-width:760px){.header{height:78px}.menu{display:block}.nav{display:none;position:absolute;left:0;right:0;top:77px;padding:18px 20px;background:#090b0e;border-bottom:1px solid #303844;flex-direction:column;align-items:stretch}.nav.open{display:flex}.hero{grid-template-columns:1fr;min-height:0}.portrait{min-height:390px}.portrait-card{width:72%;max-height:360px}.hero-copy{padding:48px 20px 66px}.hero h1{font-size:64px}.role{font-size:34px}.actions{display:grid}.button{width:100%}.consult{grid-template-columns:1fr}.accent-card{min-height:220px}.grid,.topic-grid{grid-template-columns:1fr}.light,.section{padding:64px 20px}.light h2,.section h2{font-size:46px}.footer{grid-template-columns:1fr}}
|
|
33759
|
+
</style></head>
|
|
33760
|
+
<body><a class="skip" href="#main">Skip to main content</a>
|
|
33761
|
+
<header class="header"><a class="brand" href="#top"><span class="mark">${escapeHtml6(initials(input.name).slice(0, 1))}</span><strong>${escapeHtml6(brand)}</strong></a>${navigation}</header>
|
|
33762
|
+
<main id="main"><section class="hero" id="top"><div class="portrait" aria-label="Portrait placeholder"><div class="portrait-card" aria-hidden="true">${escapeHtml6(initials(input.name))}</div></div><div class="hero-copy"><h1>${escapeHtml6(input.name)}</h1><p class="role">${escapeHtml6(input.professionalTitle)}</p><p class="summary">${escapeHtml6(input.summary)}</p><div class="actions">${action(input.primaryAction)}${input.secondaryAction ? action(input.secondaryAction, true) : ""}</div>${social}</div></section>
|
|
33763
|
+
${section(flags.authority && Boolean(authority), `<section class="light" id="about"><h2>${escapeHtml6(authority?.heading ?? "")}</h2>${authority?.paragraphs.map((paragraph) => `<p>${escapeHtml6(paragraph)}</p>`).join("") ?? ""}</section>`)}
|
|
33764
|
+
${section(flags.consultation && Boolean(consultation), `<section class="section consult"><div><p class="eyebrow">${escapeHtml6(consultation?.eyebrow ?? "Start a conversation")}</p><h2>${escapeHtml6(consultation?.heading ?? "")}</h2><p class="summary">${escapeHtml6(consultation?.summary ?? "")}</p>${consultation ? action(consultation.action) : ""}</div><div class="accent-card" aria-label="Image placeholder">${escapeHtml6(initials(input.name))}</div></section>`)}
|
|
33765
|
+
${section(flags.learning && Boolean(learning), `<section class="section learning"><div class="section-head"><h2>${escapeHtml6(learning?.heading ?? "")}</h2><p class="summary">${escapeHtml6(learning?.summary ?? "")}</p></div><div class="topic-grid">${learning?.topics.map((topic) => `<article class="topic"><strong>${escapeHtml6(topic.title)}</strong><h3>${escapeHtml6(topic.headline)}</h3><p>${escapeHtml6(topic.summary)}</p>${topic.href ? `<a href="${safeHref(topic.href)}">Explore \u2192</a>` : ""}</article>`).join("") ?? ""}</div></section>`)}
|
|
33766
|
+
${section(flags.services && Boolean(services), `<section class="section" id="services"><div class="section-head"><h2>${escapeHtml6(services?.heading ?? "")}</h2><p class="summary">${escapeHtml6(services?.summary ?? "")}</p></div><div class="grid">${services?.offers.map((offer) => `<article class="card"><h3>${escapeHtml6(offer.title)}</h3><p>${escapeHtml6(offer.description)}</p>${offer.action ? action(offer.action, true) : ""}</article>`).join("") ?? ""}</div></section>`)}
|
|
33767
|
+
${section(flags.proof && Boolean(proof), `<section class="section proof" id="proof"><div class="section-head"><h2>${escapeHtml6(proof?.heading ?? "")}</h2><p class="summary">${escapeHtml6(proof?.summary ?? "")}</p></div><div class="quotes">${proof?.testimonials.map((item) => `<article class="quote"><q>${escapeHtml6(item.quote)}</q><span>${escapeHtml6(item.person)}${item.role ? ` / ${escapeHtml6(item.role)}` : ""}</span></article>`).join("") ?? ""}</div></section>`)}
|
|
33768
|
+
${section(flags.finalCta && Boolean(finalCta), `<section class="section cta" id="contact"><p class="eyebrow">${escapeHtml6(finalCta?.eyebrow ?? "Ready when you are")}</p><h2>${escapeHtml6(finalCta?.heading ?? "")}</h2><p class="summary">${escapeHtml6(finalCta?.summary ?? "")}</p>${finalCta ? action(finalCta.action) : ""}</section>`)}
|
|
33769
|
+
</main><footer class="footer"><div><strong>${escapeHtml6(input.name)}</strong><div>${escapeHtml6(input.professionalTitle)}</div>${config.showGeneratedAt ? `<div class="generated">Generated ${escapeHtml6(generatedAtIso)}</div>` : ""}</div><div>${input.location ? `<div>${escapeHtml6(input.location)}</div>` : ""}${input.email ? `<a href="mailto:${escapeHtml6(input.email)}">${escapeHtml6(input.email)}</a>` : ""}${input.phone ? `<div>${escapeHtml6(input.phone)}</div>` : ""}</div></footer>
|
|
33770
|
+
<script>document.querySelector('.menu')?.addEventListener('click',function(){var nav=document.querySelector('.nav');var open=this.getAttribute('aria-expanded')!=='true';this.setAttribute('aria-expanded',String(open));nav?.classList.toggle('open',open)});document.querySelectorAll('.nav a').forEach(function(link){link.addEventListener('click',function(){document.querySelector('.nav')?.classList.remove('open');document.querySelector('.menu')?.setAttribute('aria-expanded','false')})});</script></body></html>`;
|
|
33771
|
+
const bytes = Buffer.byteLength(html, "utf8");
|
|
33772
|
+
return {
|
|
33773
|
+
html,
|
|
33774
|
+
filename: `${input.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "personal-authority"}.html`,
|
|
33775
|
+
bytes,
|
|
33776
|
+
sha256: createHash16("sha256").update(html).digest("hex"),
|
|
33777
|
+
generatedAt: generatedAtIso
|
|
33778
|
+
};
|
|
33779
|
+
}
|
|
33780
|
+
|
|
33781
|
+
// src/scheduled-artifacts/scheduled-artifact-renderer-registry.ts
|
|
33782
|
+
var editorialReadingRoomV1 = {
|
|
33783
|
+
rendererKey: "editorial_reading_room_v1",
|
|
33784
|
+
rendererVersion: 1,
|
|
33785
|
+
async render(rawInput, rawConfig, generatedAt) {
|
|
33786
|
+
const input = EditorialReadingRoomV1InputSchema.parse(rawInput);
|
|
33787
|
+
const config = EditorialArtifactTemplateConfigSchema.parse(rawConfig);
|
|
33788
|
+
const rendered = renderEditorialReadingRoomV1(input, config, generatedAt);
|
|
33789
|
+
if (rendered.bytes > 2e6) {
|
|
33790
|
+
throw new Error("Scheduled artifact HTML must be 2,000,000 bytes or fewer.");
|
|
33791
|
+
}
|
|
33792
|
+
return {
|
|
33793
|
+
html: rendered.html,
|
|
33794
|
+
filename: rendered.filename,
|
|
33795
|
+
title: input.title,
|
|
33796
|
+
bytes: rendered.bytes,
|
|
33797
|
+
sha256: rendered.sha256,
|
|
33798
|
+
generatedAt: rendered.generatedAt
|
|
33799
|
+
};
|
|
33800
|
+
}
|
|
33801
|
+
};
|
|
33802
|
+
var personalAuthorityV1 = {
|
|
33803
|
+
rendererKey: "personal_authority_v1",
|
|
33804
|
+
rendererVersion: 1,
|
|
33805
|
+
async render(rawInput, rawConfig, generatedAt) {
|
|
33806
|
+
const input = PersonalAuthorityV1InputSchema.parse(rawInput);
|
|
33807
|
+
const config = PersonalAuthorityTemplateConfigSchema.parse(rawConfig);
|
|
33808
|
+
const rendered = renderPersonalAuthorityV1(input, config, generatedAt);
|
|
33809
|
+
if (rendered.bytes > 2e6) {
|
|
33810
|
+
throw new Error("Scheduled artifact HTML must be 2,000,000 bytes or fewer.");
|
|
33811
|
+
}
|
|
33812
|
+
return {
|
|
33813
|
+
...rendered,
|
|
33814
|
+
title: input.name
|
|
33815
|
+
};
|
|
33816
|
+
}
|
|
33817
|
+
};
|
|
33818
|
+
var scheduledArtifactRendererRegistry = Object.freeze({
|
|
33819
|
+
"editorial_reading_room_v1:1": editorialReadingRoomV1,
|
|
33820
|
+
"personal_authority_v1:1": personalAuthorityV1
|
|
33821
|
+
});
|
|
33822
|
+
function getScheduledArtifactRenderer(rendererKey, rendererVersion) {
|
|
33823
|
+
return scheduledArtifactRendererRegistry[`${rendererKey}:${rendererVersion}`] ?? null;
|
|
33824
|
+
}
|
|
33825
|
+
var scheduledArtifactTemplatePresets = [
|
|
33826
|
+
EDITORIAL_READING_ROOM_TEMPLATE_PRESET,
|
|
33827
|
+
PERSONAL_AUTHORITY_TEMPLATE_PRESET
|
|
33828
|
+
];
|
|
33829
|
+
|
|
33165
33830
|
// src/api/scheduled-result-routes.ts
|
|
33166
33831
|
var TemplateParamsSchema = z33.object({ templateId: z33.string().uuid() }).strict();
|
|
33167
33832
|
var RunParamsSchema = z33.object({ runId: z33.string().min(1).max(200) }).strict();
|
|
@@ -33213,7 +33878,7 @@ scheduledResultApp.get("/artifact-templates", async (c) => {
|
|
|
33213
33878
|
c.header("Cache-Control", "private, no-store");
|
|
33214
33879
|
return c.json({
|
|
33215
33880
|
...result,
|
|
33216
|
-
presets: Array.isArray(result.presets) ? result.presets :
|
|
33881
|
+
presets: Array.isArray(result.presets) ? result.presets : scheduledArtifactTemplatePresets
|
|
33217
33882
|
});
|
|
33218
33883
|
});
|
|
33219
33884
|
scheduledResultApp.post("/artifact-templates", async (c) => {
|
|
@@ -33335,7 +34000,7 @@ scheduledResultApp.delete("/schedule-runs/:runId/view-links/:shareId", async (c)
|
|
|
33335
34000
|
import { Hono as Hono33 } from "hono";
|
|
33336
34001
|
|
|
33337
34002
|
// src/scheduled-artifacts/scheduled-run-artifact-store.ts
|
|
33338
|
-
import { createHash as
|
|
34003
|
+
import { createHash as createHash17 } from "crypto";
|
|
33339
34004
|
var SCHEDULED_RUN_ARTIFACT_PREFIX = "scheduled-run-artifacts/";
|
|
33340
34005
|
var SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS = 15 * 60 * 1e3;
|
|
33341
34006
|
var SCHEDULED_RUN_ARTIFACT_MAX_BYTES = 2e6;
|
|
@@ -33351,7 +34016,7 @@ function policy3() {
|
|
|
33351
34016
|
};
|
|
33352
34017
|
}
|
|
33353
34018
|
function runStorageSegment(runId) {
|
|
33354
|
-
return /^[a-zA-Z0-9_-]{1,160}$/.test(runId) ? runId : `run-${
|
|
34019
|
+
return /^[a-zA-Z0-9_-]{1,160}$/.test(runId) ? runId : `run-${createHash17("sha256").update(runId).digest("hex").slice(0, 32)}`;
|
|
33355
34020
|
}
|
|
33356
34021
|
async function createScheduledRunArtifact(args) {
|
|
33357
34022
|
if (args.rendered.bytes > SCHEDULED_RUN_ARTIFACT_MAX_BYTES) {
|
|
@@ -33458,10 +34123,10 @@ publicScheduledResultApp.get("/scheduled-run-view-links/:token/content", async (
|
|
|
33458
34123
|
import { Hono as Hono34 } from "hono";
|
|
33459
34124
|
|
|
33460
34125
|
// src/api/scheduler-integration-auth.ts
|
|
33461
|
-
import { createHash as
|
|
34126
|
+
import { createHash as createHash18, createHmac as createHmac4, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
33462
34127
|
|
|
33463
34128
|
// src/api/service-connections.ts
|
|
33464
|
-
import { randomUUID as
|
|
34129
|
+
import { randomUUID as randomUUID18 } from "crypto";
|
|
33465
34130
|
var schemaReady4 = null;
|
|
33466
34131
|
var schemaDb3 = null;
|
|
33467
34132
|
function ensureServiceConnectionsSchema() {
|
|
@@ -33603,7 +34268,7 @@ async function reconcileDiscoveredNangoConnections(identity, discovered) {
|
|
|
33603
34268
|
updated_at = excluded.updated_at
|
|
33604
34269
|
`,
|
|
33605
34270
|
args: [
|
|
33606
|
-
|
|
34271
|
+
randomUUID18(),
|
|
33607
34272
|
userId,
|
|
33608
34273
|
connection.providerConfigKey,
|
|
33609
34274
|
connection.provider,
|
|
@@ -33674,7 +34339,7 @@ async function recordServiceConnectionHealth(args) {
|
|
|
33674
34339
|
});
|
|
33675
34340
|
await getDb().execute({
|
|
33676
34341
|
sql: `INSERT INTO service_connection_health_events (id, connection_id, operational_status, failure_code, retryable, evidence_source) VALUES (?, ?, ?, ?, ?, ?)`,
|
|
33677
|
-
args: [
|
|
34342
|
+
args: [randomUUID18(), args.connectionId, args.operationalStatus, args.failureCode ?? null, args.retryable == null ? null : args.retryable ? 1 : 0, args.evidenceSource]
|
|
33678
34343
|
});
|
|
33679
34344
|
}
|
|
33680
34345
|
async function setServiceConnectionActions(identity, connectionId, enabled) {
|
|
@@ -33714,7 +34379,7 @@ async function claimServiceConnectionAction(args) {
|
|
|
33714
34379
|
if (!connection) throw new Error("service_connection_not_found");
|
|
33715
34380
|
const inserted = await getDb().execute({
|
|
33716
34381
|
sql: `INSERT OR IGNORE INTO service_connection_action_audit (id, connection_id, user_id, tool, request_id, status, request_digest) VALUES (?, ?, ?, ?, ?, 'started', ?)`,
|
|
33717
|
-
args: [
|
|
34382
|
+
args: [randomUUID18(), connection.id, connection.userId, args.tool, args.requestId, args.requestDigest]
|
|
33718
34383
|
});
|
|
33719
34384
|
if (Number(inserted.rowsAffected ?? 0) === 1) return { claimed: true };
|
|
33720
34385
|
const existing = await getDb().execute({
|
|
@@ -33756,7 +34421,7 @@ function signingSecret() {
|
|
|
33756
34421
|
return secret2;
|
|
33757
34422
|
}
|
|
33758
34423
|
function schedulerIntegrationSignature(args) {
|
|
33759
|
-
const bodyHash =
|
|
34424
|
+
const bodyHash = createHash18("sha256").update(args.body).digest("hex");
|
|
33760
34425
|
return createHmac4("sha256", args.secret).update(`${args.method.toUpperCase()}
|
|
33761
34426
|
${args.path}
|
|
33762
34427
|
${args.timestamp}
|
|
@@ -33811,34 +34476,6 @@ async function verifySchedulerIntegrationRequest(request, rawBody) {
|
|
|
33811
34476
|
return { requestId };
|
|
33812
34477
|
}
|
|
33813
34478
|
|
|
33814
|
-
// src/scheduled-artifacts/scheduled-artifact-renderer-registry.ts
|
|
33815
|
-
var editorialReadingRoomV1 = {
|
|
33816
|
-
rendererKey: "editorial_reading_room_v1",
|
|
33817
|
-
rendererVersion: 1,
|
|
33818
|
-
async render(rawInput, rawConfig, generatedAt) {
|
|
33819
|
-
const input = EditorialReadingRoomV1InputSchema.parse(rawInput);
|
|
33820
|
-
const config = ArtifactTemplateConfigSchema.parse(rawConfig);
|
|
33821
|
-
const rendered = renderEditorialReadingRoomV1(input, config, generatedAt);
|
|
33822
|
-
if (rendered.bytes > 2e6) {
|
|
33823
|
-
throw new Error("Scheduled artifact HTML must be 2,000,000 bytes or fewer.");
|
|
33824
|
-
}
|
|
33825
|
-
return {
|
|
33826
|
-
html: rendered.html,
|
|
33827
|
-
filename: rendered.filename,
|
|
33828
|
-
title: input.title,
|
|
33829
|
-
bytes: rendered.bytes,
|
|
33830
|
-
sha256: rendered.sha256,
|
|
33831
|
-
generatedAt: rendered.generatedAt
|
|
33832
|
-
};
|
|
33833
|
-
}
|
|
33834
|
-
};
|
|
33835
|
-
var scheduledArtifactRendererRegistry = Object.freeze({
|
|
33836
|
-
"editorial_reading_room_v1:1": editorialReadingRoomV1
|
|
33837
|
-
});
|
|
33838
|
-
function getScheduledArtifactRenderer(rendererKey, rendererVersion) {
|
|
33839
|
-
return scheduledArtifactRendererRegistry[`${rendererKey}:${rendererVersion}`] ?? null;
|
|
33840
|
-
}
|
|
33841
|
-
|
|
33842
34479
|
// src/api/scheduled-artifact-internal-routes.ts
|
|
33843
34480
|
var scheduledArtifactInternalApp = new Hono34();
|
|
33844
34481
|
function errorPayload(errorCode, error, requestId) {
|
|
@@ -34040,13 +34677,13 @@ async function reconcileSiteExtractSettlements(limit = 25) {
|
|
|
34040
34677
|
}
|
|
34041
34678
|
|
|
34042
34679
|
// src/api/page-diff.ts
|
|
34043
|
-
import { createHash as
|
|
34680
|
+
import { createHash as createHash19 } from "crypto";
|
|
34044
34681
|
import { diffLines } from "diff";
|
|
34045
34682
|
var MAX_SNAPSHOT_CONTENT_CHARS = 25e4;
|
|
34046
34683
|
var MAX_DIFF_HUNKS = 200;
|
|
34047
34684
|
var MAX_DIFF_LINES_PER_RESPONSE = 2e3;
|
|
34048
34685
|
function sha256Hex(value) {
|
|
34049
|
-
return
|
|
34686
|
+
return createHash19("sha256").update(value).digest("hex");
|
|
34050
34687
|
}
|
|
34051
34688
|
function truncateForStorage(value, maxChars = MAX_SNAPSHOT_CONTENT_CHARS) {
|
|
34052
34689
|
if (value.length <= maxChars) return { value, truncated: false };
|
|
@@ -34160,10 +34797,10 @@ async function persistScrapeBody(user, opts) {
|
|
|
34160
34797
|
}
|
|
34161
34798
|
|
|
34162
34799
|
// src/api/scrape-image-sink.ts
|
|
34163
|
-
import { createHash as
|
|
34800
|
+
import { createHash as createHash20 } from "crypto";
|
|
34164
34801
|
var MAX_IMAGES_PER_SCRAPE = 25;
|
|
34165
34802
|
function idempotencyKey2(userId, vault, input) {
|
|
34166
|
-
return `scrape-image-${
|
|
34803
|
+
return `scrape-image-${createHash20("sha256").update(`${userId}\0${vault}\0${input.sourceKind}\0${input.sourceUrl}\0${input.imageUrl ?? ""}\0${input.imageBase64 ?? ""}`).digest("hex")}`;
|
|
34167
34804
|
}
|
|
34168
34805
|
async function persistScrapeImagesToMemory(user, inputs, vault) {
|
|
34169
34806
|
const selected = inputs.slice(0, MAX_IMAGES_PER_SCRAPE);
|
|
@@ -34635,7 +35272,7 @@ function inferredWaybackCapturedAt(content) {
|
|
|
34635
35272
|
}
|
|
34636
35273
|
|
|
34637
35274
|
// src/api/connection-memory-import.ts
|
|
34638
|
-
import { createHash as
|
|
35275
|
+
import { createHash as createHash21 } from "crypto";
|
|
34639
35276
|
var CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES = 64 * 1024;
|
|
34640
35277
|
var CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES = 1e6;
|
|
34641
35278
|
var CONNECTION_MEMORY_IMPORT_MAX_STRING_CHARS = 256e3;
|
|
@@ -34665,7 +35302,7 @@ function canonicalJson(value) {
|
|
|
34665
35302
|
return JSON.stringify(value);
|
|
34666
35303
|
}
|
|
34667
35304
|
function sha2562(value) {
|
|
34668
|
-
return
|
|
35305
|
+
return createHash21("sha256").update(value).digest("hex");
|
|
34669
35306
|
}
|
|
34670
35307
|
function sensitiveKey(key) {
|
|
34671
35308
|
const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
@@ -34934,7 +35571,7 @@ async function cleanupExpiredScrapeBlobs(maxAgeMs = SCRAPE_BLOB_TTL_MS) {
|
|
|
34934
35571
|
}
|
|
34935
35572
|
|
|
34936
35573
|
// src/api/connected-data-export.ts
|
|
34937
|
-
import { randomUUID as
|
|
35574
|
+
import { randomUUID as randomUUID19 } from "crypto";
|
|
34938
35575
|
var CONNECTED_DATA_INLINE_BUDGET_BYTES = Number(
|
|
34939
35576
|
process.env.MCP_SCRAPER_CONNECTED_DATA_INLINE_BUDGET_BYTES ?? 5e4
|
|
34940
35577
|
);
|
|
@@ -35105,7 +35742,7 @@ function finitePositive(value, fallback) {
|
|
|
35105
35742
|
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
35106
35743
|
}
|
|
35107
35744
|
async function collectConnectedDataExport(args) {
|
|
35108
|
-
const exportId =
|
|
35745
|
+
const exportId = randomUUID19();
|
|
35109
35746
|
const now = args.now ?? Date.now;
|
|
35110
35747
|
const startedAt = now();
|
|
35111
35748
|
const budgetMs = finitePositive(CONNECTED_DATA_EXPORT_BUDGET_MS, 24e4);
|
|
@@ -35221,7 +35858,7 @@ ${lines.length ? `${lines.join("\n")}
|
|
|
35221
35858
|
}
|
|
35222
35859
|
|
|
35223
35860
|
// src/api/search-console-table-export.ts
|
|
35224
|
-
import { randomUUID as
|
|
35861
|
+
import { randomUUID as randomUUID20 } from "crypto";
|
|
35225
35862
|
var SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS = 5e4;
|
|
35226
35863
|
var SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE = 2e3;
|
|
35227
35864
|
var SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES = 50 * 1024 * 1024;
|
|
@@ -35321,7 +35958,7 @@ async function exportSearchConsoleTableData(args) {
|
|
|
35321
35958
|
offset += rows.length;
|
|
35322
35959
|
if (stoppedForBytes || rows.length < limit || offset >= matchedRows) break;
|
|
35323
35960
|
}
|
|
35324
|
-
const exportId =
|
|
35961
|
+
const exportId = randomUUID20();
|
|
35325
35962
|
const artifact = await args.writeArtifact({
|
|
35326
35963
|
ownerId: args.ownerId,
|
|
35327
35964
|
exportId,
|
|
@@ -35676,7 +36313,7 @@ async function dbUsage(identity, plan) {
|
|
|
35676
36313
|
}
|
|
35677
36314
|
|
|
35678
36315
|
// src/api/nango-control.ts
|
|
35679
|
-
import { createHash as
|
|
36316
|
+
import { createHash as createHash23, randomUUID as randomUUID22 } from "crypto";
|
|
35680
36317
|
|
|
35681
36318
|
// src/api/slack-archive-analysis.ts
|
|
35682
36319
|
function isRecord2(value) {
|
|
@@ -36050,7 +36687,7 @@ async function exportSlackChannelPage(input, dependencies) {
|
|
|
36050
36687
|
}
|
|
36051
36688
|
|
|
36052
36689
|
// src/api/main-nango-transport.ts
|
|
36053
|
-
import { createHash as
|
|
36690
|
+
import { createHash as createHash22, randomUUID as randomUUID21 } from "crypto";
|
|
36054
36691
|
import { Client, StreamableHTTPClientTransport } from "@modelcontextprotocol/client";
|
|
36055
36692
|
|
|
36056
36693
|
// src/api/connected-tool-policy.ts
|
|
@@ -36443,7 +37080,7 @@ async function listNangoToolsDirect(identity, connectionId) {
|
|
|
36443
37080
|
});
|
|
36444
37081
|
const readTools = [...policies.values()].filter((policy4) => policy4.classification === "read").map((policy4) => policy4.name);
|
|
36445
37082
|
const actionTools = [...policies.values()].filter((policy4) => policy4.classification === "action").map((policy4) => policy4.name);
|
|
36446
|
-
const revision =
|
|
37083
|
+
const revision = createHash22("sha256").update(JSON.stringify(tools.map((tool) => ({ name: tool.name, inputSchema: tool.inputSchema })))).digest("hex");
|
|
36447
37084
|
await updateServiceConnectionTools(connection.id, readTools, actionTools, revision);
|
|
36448
37085
|
const refreshed = await getOwnedServiceConnection(identity, connection.id);
|
|
36449
37086
|
return { connection: refreshed ?? { ...connection, readTools, actionTools, toolRevision: revision }, tools };
|
|
@@ -36470,8 +37107,8 @@ async function callNangoToolDirect(args) {
|
|
|
36470
37107
|
identity: args.identity,
|
|
36471
37108
|
ratePolicyVersion: CONNECTED_USAGE_RATE_POLICY_VERSION
|
|
36472
37109
|
});
|
|
36473
|
-
const requestId = args.requestId?.trim() ||
|
|
36474
|
-
const idempotencyKey3 = `main-nango:${
|
|
37110
|
+
const requestId = args.requestId?.trim() || randomUUID21();
|
|
37111
|
+
const idempotencyKey3 = `main-nango:${createHash22("sha256").update(args.identity.toLowerCase()).update("\0").update(connection.id).update("\0").update(args.tool).update("\0").update(requestId).digest("hex")}`;
|
|
36475
37112
|
const startedAt = /* @__PURE__ */ new Date();
|
|
36476
37113
|
const started = performance.now();
|
|
36477
37114
|
let result;
|
|
@@ -36496,7 +37133,7 @@ async function callNangoToolDirect(args) {
|
|
|
36496
37133
|
toolName: args.tool,
|
|
36497
37134
|
operationKind: args.operationKind ?? args.classification,
|
|
36498
37135
|
outcome: providerError ? "error" : "partial",
|
|
36499
|
-
requestId: requestId.length <= 200 ? requestId :
|
|
37136
|
+
requestId: requestId.length <= 200 ? requestId : createHash22("sha256").update(requestId).digest("hex"),
|
|
36500
37137
|
startedAt: startedAt.toISOString(),
|
|
36501
37138
|
completedAt: completedAt.toISOString()
|
|
36502
37139
|
}
|
|
@@ -36538,7 +37175,7 @@ async function describeNangoToolDirect(identity, connectionId, toolName) {
|
|
|
36538
37175
|
providerContractHash: MAIN_INTEGRATION_CONTRACT_HASH,
|
|
36539
37176
|
protocolVersion: null,
|
|
36540
37177
|
schemaSource: "live_tools_list",
|
|
36541
|
-
schemaHash:
|
|
37178
|
+
schemaHash: createHash22("sha256").update(JSON.stringify(projected)).digest("hex"),
|
|
36542
37179
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
36543
37180
|
};
|
|
36544
37181
|
}
|
|
@@ -37262,8 +37899,8 @@ async function setScheduleConnectionActionsEnabled(identity, connectionId, enabl
|
|
|
37262
37899
|
return data.connection.actionsEnabled === true;
|
|
37263
37900
|
}
|
|
37264
37901
|
async function callScheduleConnectionAction(identity, connectionId, input, tool, idempotencyKey3) {
|
|
37265
|
-
const requestId = `main-connected-action:${
|
|
37266
|
-
const requestDigest =
|
|
37902
|
+
const requestId = `main-connected-action:${createHash23("sha256").update(identity).update("\0").update(idempotencyKey3?.trim() || randomUUID22()).digest("hex")}`;
|
|
37903
|
+
const requestDigest = createHash23("sha256").update(connectionId).update("\0").update(tool?.trim() ?? "").update("\0").update(canonicalJson2(input)).digest("hex");
|
|
37267
37904
|
if (mainOwnsIntegrations()) {
|
|
37268
37905
|
const selectedTool = tool?.trim();
|
|
37269
37906
|
if (!selectedTool) throw new NangoControlError("An action tool is required.", 400, "invalid_request", false);
|
|
@@ -37414,7 +38051,7 @@ function canonicalJson2(value) {
|
|
|
37414
38051
|
return JSON.stringify(value);
|
|
37415
38052
|
}
|
|
37416
38053
|
function projectedToolSchemaHash(tool) {
|
|
37417
|
-
return
|
|
38054
|
+
return createHash23("sha256").update(canonicalJson2(tool)).digest("hex");
|
|
37418
38055
|
}
|
|
37419
38056
|
async function describeNangoTool(identity, connectionId, tool, fresh) {
|
|
37420
38057
|
if (mainOwnsIntegrations()) {
|
|
@@ -37693,7 +38330,7 @@ async function callMainOwnedExportPage(identity, input) {
|
|
|
37693
38330
|
}
|
|
37694
38331
|
|
|
37695
38332
|
// src/api/resend-control.ts
|
|
37696
|
-
import { createHash as
|
|
38333
|
+
import { createHash as createHash24 } from "crypto";
|
|
37697
38334
|
var DEFAULT_CONNECTION_CONTROL_URL = "https://mcp-scraper-scheduler.vercel.app";
|
|
37698
38335
|
var RESEND_PROVIDER_CONFIG_KEY = "resend";
|
|
37699
38336
|
var RESEND_LOGO_URL = "https://cdn.resend.com/brand/resend-icon-black.svg";
|
|
@@ -37952,7 +38589,7 @@ async function callResendRead(identity, connectionId, tool, args) {
|
|
|
37952
38589
|
return isRecord5(data) ? data.result ?? data : data;
|
|
37953
38590
|
}
|
|
37954
38591
|
async function callResendAction(identity, connectionId, tool, input, idempotencyKey3) {
|
|
37955
|
-
const requestId = `main-resend-action:${
|
|
38592
|
+
const requestId = `main-resend-action:${createHash24("sha256").update(identity).update("\0").update(idempotencyKey3.trim()).digest("hex")}`;
|
|
37956
38593
|
const body = await controlRequest2("/api/internal/resend/actions/call", {
|
|
37957
38594
|
method: "POST",
|
|
37958
38595
|
headers: { "x-request-id": requestId },
|
|
@@ -39489,12 +40126,12 @@ app.get("/schedule-actions", auth3, async (c) => {
|
|
|
39489
40126
|
const r = await memoryCall("listScheduledActionsTool", {}, key);
|
|
39490
40127
|
if (!r.ok || !Array.isArray(r.actions)) return c.json(r);
|
|
39491
40128
|
try {
|
|
39492
|
-
const actions = await Promise.all(r.actions.map(async (
|
|
39493
|
-
const id = typeof
|
|
39494
|
-
const executionMode =
|
|
39495
|
-
if (!id) return { ...
|
|
40129
|
+
const actions = await Promise.all(r.actions.map(async (action2) => {
|
|
40130
|
+
const id = typeof action2.id === "string" ? action2.id : "";
|
|
40131
|
+
const executionMode = action2.executionMode === "connection_sync" ? "connection_sync" : "agent";
|
|
40132
|
+
if (!id) return { ...action2, executionMode, connections: [] };
|
|
39496
40133
|
const bindings = await getScheduleConnectionBindings(user.email, id);
|
|
39497
|
-
return { ...
|
|
40134
|
+
return { ...action2, executionMode, connections: bindingsForSchedule(bindings, id) };
|
|
39498
40135
|
}));
|
|
39499
40136
|
return c.json({
|
|
39500
40137
|
...r,
|
|
@@ -39504,7 +40141,7 @@ app.get("/schedule-actions", auth3, async (c) => {
|
|
|
39504
40141
|
console.warn("[schedule-actions] connection bindings unavailable:", err instanceof Error ? err.message : String(err));
|
|
39505
40142
|
return c.json({
|
|
39506
40143
|
...r,
|
|
39507
|
-
actions: r.actions.map((
|
|
40144
|
+
actions: r.actions.map((action2) => ({ ...action2, executionMode: action2.executionMode === "connection_sync" ? "connection_sync" : "agent", connections: [] })),
|
|
39508
40145
|
connectionBindingsUnavailable: true
|
|
39509
40146
|
});
|
|
39510
40147
|
}
|
|
@@ -39587,7 +40224,7 @@ app.put("/schedule-actions/:id/connections", auth3, requireIntegrationsTier, asy
|
|
|
39587
40224
|
if (!key) return c.json({ ok: false, error: error ?? "memory unavailable" }, 503);
|
|
39588
40225
|
const schedules = await memoryCall("listScheduledActionsTool", {}, key);
|
|
39589
40226
|
if (!schedules.ok || !Array.isArray(schedules.actions)) return c.json(schedules, 502);
|
|
39590
|
-
const schedule = schedules.actions.find((
|
|
40227
|
+
const schedule = schedules.actions.find((action2) => action2.id === scheduleActionId);
|
|
39591
40228
|
if (!schedule) return c.json({ ok: false, error: "scheduled action not found" }, 404);
|
|
39592
40229
|
if (schedule.executionMode === "connection_sync") {
|
|
39593
40230
|
const policyError = connectionSyncSelectionError(connections);
|
|
@@ -39786,7 +40423,7 @@ app.post("/harvest", auth3, async (c) => {
|
|
|
39786
40423
|
if (!harvestOk) return c.json(insufficientBalanceResponse(harvestBal, harvestCost), 402);
|
|
39787
40424
|
jobId2 = await createJob(user.id, options.query, { ...options, billingHoldMc: harvestCost }, body.callback_url);
|
|
39788
40425
|
} else {
|
|
39789
|
-
jobId2 =
|
|
40426
|
+
jobId2 = randomUUID23();
|
|
39790
40427
|
const billingDebitKey = `paa-harvest:${jobId2}:hold`;
|
|
39791
40428
|
const description = `PAA harvest: ${options.query}`.slice(0, 500);
|
|
39792
40429
|
const hold = await debitMcIdempotent(
|
|
@@ -41004,7 +41641,7 @@ app.get("/cron/tick", async (c) => {
|
|
|
41004
41641
|
if (!process.env.CRON_SECRET || secret2 !== `Bearer ${process.env.CRON_SECRET}`) {
|
|
41005
41642
|
return c.json({ error: "Unauthorized" }, 401);
|
|
41006
41643
|
}
|
|
41007
|
-
const { drainQueue } = await import("./worker-
|
|
41644
|
+
const { drainQueue } = await import("./worker-3QX7LG62.js");
|
|
41008
41645
|
const budget = { maxJobs: 10, deadlineMs: Date.now() + 28e4 };
|
|
41009
41646
|
const workflowDispatchResult = await dispatchDueWorkflowSchedules(`${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`);
|
|
41010
41647
|
const [results, sweepResult, reapResult, expiredResult, blobCleanup, connectedDataArtifactCleanup, connectedAccountBilling, directoryWorkflowDispatch, directoryWorkflowReconciliation, directoryArtifactCleanup, siteExtractArtifactCleanup, siteExtractRedispatch, siteExtractReconciliation, paaHarvestRedispatch, paaHarvestReconciliation, localSourcebookRedispatch] = await Promise.all([
|
|
@@ -41059,8 +41696,8 @@ app.post("/api/internal/extract-refinalize/:id", async (c) => {
|
|
|
41059
41696
|
finishExtractJob: finishExtractJob2,
|
|
41060
41697
|
terminalExtractJobStatus: terminalExtractJobStatus2,
|
|
41061
41698
|
extractJobLimitInfo: extractJobLimitInfo2
|
|
41062
|
-
} = await import("./site-extract-repository-
|
|
41063
|
-
const { assembleExtractArtifacts } = await import("./extract-bundle-
|
|
41699
|
+
} = await import("./site-extract-repository-UULQAGO4.js");
|
|
41700
|
+
const { assembleExtractArtifacts } = await import("./extract-bundle-DXWWQER6.js");
|
|
41064
41701
|
const existing = await getExtractJob2(jobId2);
|
|
41065
41702
|
if (!existing) return c.json({ error: "job not found" }, 404);
|
|
41066
41703
|
if (existing.status !== "failed") {
|
|
@@ -41087,7 +41724,7 @@ app.post("/api/internal/extract-refinalize/:id", async (c) => {
|
|
|
41087
41724
|
if (!updated) return c.json({ error: "job state changed during re-finalization" }, 409);
|
|
41088
41725
|
let settlement = "already_settled_or_refunded";
|
|
41089
41726
|
if (job.billedMc == null && job.userId != null) {
|
|
41090
|
-
const { settleExtractJob: settleExtractJob3, countSuccessfulPages: countSuccessfulPages2 } = await import("./site-extract-repository-
|
|
41727
|
+
const { settleExtractJob: settleExtractJob3, countSuccessfulPages: countSuccessfulPages2 } = await import("./site-extract-repository-UULQAGO4.js");
|
|
41091
41728
|
const heldMc = Number(job.options.heldMc ?? 0);
|
|
41092
41729
|
const successful = await countSuccessfulPages2(jobId2);
|
|
41093
41730
|
const usedMc = Math.min(successful * MC_COSTS.page_scrape, heldMc);
|
|
@@ -41271,4 +41908,4 @@ app.get("/blog/:slug/", (c) => {
|
|
|
41271
41908
|
export {
|
|
41272
41909
|
app
|
|
41273
41910
|
};
|
|
41274
|
-
//# sourceMappingURL=server-
|
|
41911
|
+
//# sourceMappingURL=server-OHMZJRSB.js.map
|