@anthusai/papyrus 2.0.0-next.1 → 2.0.0-next.100
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/amplify/auth/resource.js +16 -1
- package/amplify/auth/resource.ts +19 -1
- package/amplify/data/function-handler.js +20 -0
- package/amplify/data/function-handler.ts +40 -0
- package/amplify/data/resource.js +158 -61
- package/amplify/data/resource.ts +165 -61
- package/amplify/data/schema.js +2103 -1979
- package/amplify/data/schema.ts +206 -68
- package/amplify/functions/assignment-action/data-access.js +90 -0
- package/amplify/functions/assignment-action/data-access.ts +89 -0
- package/amplify/functions/assignment-action/handler.ts +1 -1
- package/amplify/functions/automation-view/handler.py +17 -0
- package/amplify/functions/automation-view/resource.js +24 -0
- package/amplify/functions/automation-view/resource.ts +34 -0
- package/amplify/functions/automation-view/schema.js +26 -0
- package/amplify/functions/automation-view/schema.ts +28 -0
- package/amplify/functions/automation-view/wire.js +56 -0
- package/amplify/functions/automation-view/wire.ts +76 -0
- package/amplify/functions/content-actions/handler.py +70 -2
- package/amplify/functions/content-actions/requirements.txt +1 -0
- package/amplify/functions/content-actions/resource.js +1 -1
- package/amplify/functions/content-actions/resource.ts +1 -1
- package/amplify/functions/editorial-checks/handler.py +14 -0
- package/amplify/functions/editorial-checks/requirements.txt +1 -0
- package/amplify/functions/editorial-checks/resource.js +43 -0
- package/amplify/functions/editorial-checks/resource.ts +60 -0
- package/amplify/functions/editorial-checks/schema.js +122 -0
- package/amplify/functions/editorial-checks/schema.ts +124 -0
- package/amplify/functions/editorial-checks/wire.js +53 -0
- package/amplify/functions/editorial-checks/wire.ts +61 -0
- package/amplify/functions/model-attachment-upload/handler.ts +1 -0
- package/amplify/functions/monitoring-view/handler.py +11 -0
- package/amplify/functions/monitoring-view/resource.js +24 -0
- package/amplify/functions/monitoring-view/resource.ts +34 -0
- package/amplify/functions/monitoring-view/schema.js +29 -0
- package/amplify/functions/monitoring-view/schema.ts +31 -0
- package/amplify/functions/monitoring-view/wire.js +64 -0
- package/amplify/functions/monitoring-view/wire.ts +81 -0
- package/amplify/functions/public-forms/core.js +247 -0
- package/amplify/functions/public-forms/core.ts +310 -0
- package/amplify/functions/public-forms/handler.ts +41 -0
- package/amplify/functions/public-forms/origins.js +35 -0
- package/amplify/functions/public-forms/origins.ts +43 -0
- package/amplify/functions/public-forms/resource.js +9 -0
- package/amplify/functions/public-forms/resource.ts +10 -0
- package/amplify/functions/public-forms/wire.js +25 -0
- package/amplify/functions/public-forms/wire.ts +32 -0
- package/amplify/functions/shared/editorial-context.ts +63 -4
- package/amplify/functions/shared/python-bundle.js +20 -14
- package/amplify/functions/shared/python-bundle.ts +25 -18
- package/amplify/plugins/newsletter/config.js +245 -0
- package/amplify/plugins/newsletter/config.ts +305 -0
- package/amplify/plugins/newsletter/function/handler.py +6 -0
- package/amplify/plugins/newsletter/function/requirements.txt +5 -0
- package/amplify/plugins/newsletter/ops/handler.py +12 -0
- package/amplify/plugins/newsletter/ops/resource.js +27 -0
- package/amplify/plugins/newsletter/ops/resource.ts +37 -0
- package/amplify/plugins/newsletter/papyrus.plugin.js +263 -0
- package/amplify/plugins/newsletter/papyrus.plugin.ts +311 -0
- package/amplify/plugins/newsletter/schema.js +57 -0
- package/amplify/plugins/newsletter/schema.ts +62 -0
- package/amplify/plugins/newsletter/secure-string-parameter.js +107 -0
- package/amplify/plugins/newsletter/secure-string-parameter.ts +120 -0
- package/amplify/plugins/newsletter/sending.js +140 -0
- package/amplify/plugins/newsletter/sending.ts +186 -0
- package/amplify/plugins/reader-steering/config.js +182 -0
- package/amplify/plugins/reader-steering/config.ts +290 -0
- package/amplify/plugins/reader-steering/function/ai_moderation.py +246 -0
- package/amplify/plugins/reader-steering/function/handler.py +1567 -0
- package/amplify/plugins/reader-steering/function/moderation-prompt.md +27 -0
- package/amplify/plugins/reader-steering/function/moderation.py +316 -0
- package/amplify/plugins/reader-steering/function/proposals.py +458 -0
- package/amplify/plugins/reader-steering/function/resource.js +27 -0
- package/amplify/plugins/reader-steering/function/resource.ts +35 -0
- package/amplify/plugins/reader-steering/papyrus.plugin.js +186 -0
- package/amplify/plugins/reader-steering/papyrus.plugin.ts +212 -0
- package/amplify/plugins/reader-steering/schema.js +229 -0
- package/amplify/plugins/reader-steering/schema.ts +231 -0
- package/amplify/plugins/registry.js +4 -0
- package/amplify/plugins/registry.ts +4 -0
- package/amplify/plugins/signup/schema.js +2 -1
- package/amplify/plugins/signup/schema.ts +2 -1
- package/amplify/plugins/types.ts +5 -0
- package/amplify/plugins/wire.js +1 -0
- package/amplify/plugins/wire.ts +1 -0
- package/amplify/publication/projection-lease.js +30 -0
- package/amplify/publication/projection-lease.ts +39 -0
- package/amplify/publication/redirect-and-index.js +105 -0
- package/amplify/publication/redirect-rules.js +73 -0
- package/amplify/publication/redirect-rules.ts +93 -0
- package/amplify/publication/s3-projection.js +7 -1
- package/amplify/publication/s3-projection.ts +9 -1
- package/amplify/site-backend-config.js +19 -0
- package/amplify/site-backend-config.ts +45 -1
- package/amplify/site-backend-names.js +4 -2
- package/amplify/site-backend-names.ts +4 -2
- package/amplify/site-backend.js +51 -4
- package/amplify/site-backend.ts +60 -4
- package/app/api/auth/prospectus/sign-in/route.ts +4 -0
- package/app/api/newsletter/confirm/route.ts +13 -0
- package/app/api/newsletter/preferences/route.ts +13 -0
- package/app/api/newsletter/state/route.ts +9 -0
- package/app/api/newsletter/subscribe/route.ts +9 -0
- package/app/api/newsletter/unsubscribe/route.ts +13 -0
- package/app/api/newsroom/chat/[...path]/route.ts +17 -0
- package/app/api/newsroom/curation/context/route.ts +9 -0
- package/app/api/newsroom/curation/priming/route.ts +9 -0
- package/app/api/newsroom/curation/publication/route.ts +9 -0
- package/app/api/reader/comment/delete/route.ts +9 -0
- package/app/api/reader/comment/edit/route.ts +9 -0
- package/app/api/reader/comment/route.ts +9 -0
- package/app/api/reader/flag/route.ts +9 -0
- package/app/api/reader/handle/route.ts +9 -0
- package/app/api/reader/me/route.ts +9 -0
- package/app/api/reader/rating/route.ts +9 -0
- package/app/api/reader/report/route.ts +9 -0
- package/app/api/reader/retired/route.ts +9 -0
- package/app/api/reader/submit/route.ts +9 -0
- package/app/api/reader/vote/route.ts +9 -0
- package/app/layout.tsx +5 -1
- package/bin/papyrus-infra.mjs +20 -1
- package/components/automation-panel.tsx +49 -0
- package/components/automation-run-now.tsx +97 -0
- package/components/automation-view.tsx +195 -0
- package/components/editorial-checks-admin-panel.tsx +362 -0
- package/components/editorial-finding-card.tsx +162 -0
- package/components/editorial-findings-panel.tsx +91 -0
- package/components/editorial-preview-popover.tsx +103 -0
- package/components/markus-body-editor.tsx +213 -0
- package/components/markus-ir-preview.tsx +147 -6
- package/components/moderation-panel.tsx +248 -0
- package/components/monitoring-overview-view.tsx +414 -0
- package/components/monitoring-panel.tsx +60 -0
- package/components/news-desk-client-provider.tsx +18 -18
- package/components/news-desk-taxonomy-client.ts +28 -8
- package/components/newsletter-panel.tsx +474 -0
- package/components/newsletter-sections.tsx +163 -0
- package/components/newsroom-access-gate.tsx +16 -2
- package/components/newsroom-article-editor.tsx +145 -18
- package/components/newsroom-articles-view.tsx +6 -6
- package/components/newsroom-assignment-detail.tsx +6 -6
- package/components/newsroom-board-view.tsx +14 -10
- package/components/newsroom-curation-bootstrap.tsx +45 -0
- package/components/newsroom-ops-overview.tsx +26 -96
- package/components/newsroom-ops-shell.tsx +9 -5
- package/components/newsroom-organizer.css +90 -61
- package/components/newsroom-overview-skeleton.tsx +25 -0
- package/components/newsroom-references-view.tsx +192 -49
- package/components/newsroom-theme-fonts/broadsheet-green.css +10 -0
- package/components/newsroom-theme-fonts/broadsheet-green.tsx +6 -0
- package/components/newsroom-theme-fonts/newspaper-gothic.css +9 -0
- package/components/newsroom-theme-fonts/newspaper-gothic.tsx +6 -0
- package/components/newsroom-theme-fonts/night-desk.css +10 -0
- package/components/newsroom-theme-fonts/night-desk.tsx +6 -0
- package/components/newsroom-theme-fonts/refined-newsprint.css +11 -0
- package/components/newsroom-theme-fonts/refined-newsprint.tsx +6 -0
- package/components/newsroom-theme.css +39 -0
- package/components/newsroom-theme.tsx +33 -0
- package/components/notification-center.tsx +281 -0
- package/components/papyrus-console-conversation.css +14 -0
- package/components/papyrus-console-conversation.tsx +55 -0
- package/components/papyrus-console-shell.tsx +62 -420
- package/components/papyrus-private-chat-panel.tsx +134 -0
- package/components/publication-cyclotron-panel.tsx +15 -10
- package/components/reader-auth-state.ts +1 -1
- package/components/reference-add-entry.tsx +75 -0
- package/components/reference-curation-controls.tsx +31 -22
- package/components/reference-cyclotron-priming.tsx +63 -25
- package/components/reference-cyclotron-status.tsx +4 -4
- package/components/reference-cyclotron-suggestion.tsx +15 -11
- package/components/reference-live-agreement.tsx +10 -10
- package/components/reference-priming.tsx +14 -10
- package/components/reference-relevance-review.tsx +4 -4
- package/components/reference-section-suggestion.tsx +2 -2
- package/components/topic-steering-workspace.tsx +241 -144
- package/components/use-editorial-checks.ts +133 -0
- package/infra/amplify-app-shell.js +96 -16
- package/infra/amplify-app-shell.ts +108 -18
- package/infra/cloud-email-config.js +32 -0
- package/infra/cloud-email-config.ts +39 -0
- package/infra/cloud-email.js +57 -0
- package/infra/cloud-email.test.js +69 -0
- package/infra/cloud-email.test.ts +66 -0
- package/infra/cloud-email.ts +60 -0
- package/infra/index.js +1 -0
- package/infra/secure-string-parameter-handler.js +41 -0
- package/infra/secure-string-parameter-handler.ts +41 -0
- package/infra/secure-string-parameter.js +79 -0
- package/infra/secure-string-parameter.ts +91 -0
- package/infra/site-config.js +92 -6
- package/infra/site-config.ts +138 -6
- package/lib/automation-client.ts +54 -0
- package/lib/automation-fixture.ts +93 -0
- package/lib/automation-overview.ts +252 -0
- package/lib/category-repository.ts +14 -12
- package/lib/console-web-context.ts +1 -1
- package/lib/curation-routes.ts +345 -0
- package/lib/cyclotron-suggestions.ts +11 -11
- package/lib/editorial-checks-backend.ts +16 -0
- package/lib/editorial-checks-client.ts +212 -0
- package/lib/editorial-checks-demo.ts +147 -0
- package/lib/editorial-checks-settings.ts +27 -0
- package/lib/editorial-checks.ts +175 -0
- package/lib/editorial-preview.ts +228 -0
- package/lib/monitoring-client.ts +83 -0
- package/lib/monitoring-overview-fixture.ts +51 -0
- package/lib/monitoring-overview.ts +188 -0
- package/lib/news-desk-session.ts +12 -21
- package/lib/newsletter-api.ts +418 -0
- package/lib/newsletter-client.ts +46 -0
- package/lib/newsletter-fixture.ts +184 -0
- package/lib/newsletter-model.ts +202 -0
- package/lib/newsroom-board.ts +27 -2
- package/lib/newsroom-demo-dashboard.ts +1 -1
- package/lib/newsroom-nav.ts +1 -1
- package/lib/newsroom-organizer-nav.ts +2 -1
- package/lib/papyrus-chat-admission.ts +65 -0
- package/lib/papyrus-hosted-chat.ts +217 -0
- package/lib/papyrus-private-chat-client.ts +81 -0
- package/lib/prospectus-session.ts +30 -14
- package/lib/prospectus-sign-in-routes.ts +106 -31
- package/lib/prospectus-site-identity.ts +46 -6
- package/lib/reader-api.ts +145 -0
- package/lib/reader-moderation.ts +220 -0
- package/lib/reader-retirement-route.ts +89 -0
- package/lib/reader-subject.md +29 -0
- package/lib/reader-subject.ts +53 -0
- package/lib/reference-review-details.ts +71 -0
- package/lib/reference-review-request.ts +14 -0
- package/lib/relevance-decisions.ts +42 -10
- package/lib/relevance-status.ts +6 -6
- package/lib/review-client.ts +120 -0
- package/lib/review-fixture.ts +45 -0
- package/lib/review-requests.ts +246 -0
- package/lib/semantic-graph.ts +2 -0
- package/lib/shared-chat-admission.ts +182 -0
- package/lib/site-brand-tenant.ts +11 -6
- package/lib/site-brand.ts +23 -3
- package/lib/site-origin-value.ts +50 -0
- package/lib/site-origin.ts +8 -0
- package/lib/use-reference-review-data.ts +44 -0
- package/middleware.ts +14 -3
- package/package.json +98 -4
- package/routes.manifest.json +227 -2
- package/src/with-papyrus.mjs +1 -1
- package/components/newsroom-fonts/playfair-display-700.ttf +0 -0
- package/components/newsroom-fonts/playfair-display-900.ttf +0 -0
- package/components/newsroom-fonts/playfair-display-OFL.txt +0 -93
- package/components/newsroom-fonts/plus-jakarta-sans-400.ttf +0 -0
- package/components/newsroom-fonts/plus-jakarta-sans-600.ttf +0 -0
- package/components/newsroom-fonts/plus-jakarta-sans-OFL.txt +0 -93
- package/components/newsroom-newsprint-board.css +0 -7
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { MonitoringOverview } from "./monitoring-overview";
|
|
2
|
+
|
|
3
|
+
/** A realistic overview for tests and the `?demo=1` newsroom. Public Hacker News titles only. */
|
|
4
|
+
export const FIXTURE_MONITORING_OVERVIEW: MonitoringOverview = {
|
|
5
|
+
state: "ready",
|
|
6
|
+
asOf: "2026-10-21T02:40:00Z",
|
|
7
|
+
scope: { mode: "publication", publicationId: "desk-a", site: "a.example.test", canViewAll: true },
|
|
8
|
+
feeds: [{ id: "hn", sourceUrl: "https://news.ycombinator.com/rss", intervalSeconds: 900, limit: 30, enabled: true,
|
|
9
|
+
lastStatus: "complete", lastFinishedAt: "2026-10-21T02:33:00Z", nextDueAt: "2026-10-21T02:50:00Z" }],
|
|
10
|
+
runs: [
|
|
11
|
+
{ runId: "r3", startedAt: "2026-10-21T02:33:00.000000Z", status: "complete", new: 2, revised: 1, unchanged: 27, itemCount: 30, errorCount: 0, errors: [] },
|
|
12
|
+
{ runId: "r1", startedAt: "2026-10-21T02:18:00.000000Z", status: "complete", new: 0, revised: 0, unchanged: 30, itemCount: 30, errorCount: 0, errors: [] },
|
|
13
|
+
{ runId: "r0", startedAt: "2026-10-21T02:03:00.000000Z", status: "partial", new: 1, revised: 0, unchanged: 28, itemCount: 30, errorCount: 1, errors: ["ConnectionError: timeout"] },
|
|
14
|
+
],
|
|
15
|
+
routerRuns: [{ runId: "q2", startedAt: "2026-10-21T02:37:00.000000Z", mode: "live", status: "stopped", stoppedBy: "request_ceiling_reached",
|
|
16
|
+
counts: { examined: 3, decided: 1, delivered: 1, deliveryFailed: 1, routed: 1 }, errorCount: 0 }],
|
|
17
|
+
router: { mode: "live", feedback: true, enabled: true, maxItemsPerRun: 5 },
|
|
18
|
+
usage: { day: "2026-10-21", decisionsToday: 2, decisionsTotal: 3, historyComplete: true },
|
|
19
|
+
hiddenNotRelevant: 1,
|
|
20
|
+
items: [
|
|
21
|
+
{ sourceId: "104", revision: 1, title: "Fresh Show HN", url: "https://example.org/104", discussionUrl: "https://news.ycombinator.com/item?id=104",
|
|
22
|
+
observedAt: "2026-10-21T02:30:00.000000Z", routing: "collected", routingNote: null, decidedAt: null,
|
|
23
|
+
relevance: [{ publicationId: "desk-a", site: "a.example.test", relevant: null, label: null, confidence: null, delivery: { state: null } }] },
|
|
24
|
+
{ sourceId: "105", revision: 1, title: "Broken request", url: "https://example.org/105", discussionUrl: "https://news.ycombinator.com/item?id=105",
|
|
25
|
+
observedAt: "2026-10-21T01:30:00.000000Z", routing: "error", routingNote: "invalid request: bad title", decidedAt: null,
|
|
26
|
+
relevance: [{ publicationId: "desk-a", site: "a.example.test", relevant: null, label: null, confidence: null, delivery: { state: null } }] },
|
|
27
|
+
{ sourceId: "101", revision: 1, title: "Quantum paper on arXiv", url: "https://example.org/101", discussionUrl: "https://news.ycombinator.com/item?id=101",
|
|
28
|
+
observedAt: "2026-10-21T00:10:00.000000Z", routing: "routed", routingNote: null, decidedAt: "2026-10-21T01:00:00Z",
|
|
29
|
+
relevance: [{ publicationId: "desk-a", site: "a.example.test", relevant: true, label: "include", confidence: 0.91,
|
|
30
|
+
delivery: { state: "delivered", referenceId: "ref-1", referenceLineageId: "lin-1", deliveredAt: "2026-10-21T01:00:05Z" } }] },
|
|
31
|
+
{ sourceId: "103", revision: 1, title: "Phishing kit leaks", url: "https://example.org/103", discussionUrl: "https://news.ycombinator.com/item?id=103",
|
|
32
|
+
observedAt: "2026-10-20T23:00:00.000000Z", routing: "decided", routingNote: null, decidedAt: "2026-10-20T23:10:00Z",
|
|
33
|
+
relevance: [{ publicationId: "desk-a", site: "a.example.test", relevant: true, label: "include", confidence: 0.7,
|
|
34
|
+
delivery: { state: "failed", error: "ClientError: throttled", attempts: 2 } }] },
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const FIXTURE_ALL_PIPELINE_OVERVIEW: MonitoringOverview = {
|
|
39
|
+
...FIXTURE_MONITORING_OVERVIEW,
|
|
40
|
+
scope: { mode: "all", publicationId: "desk-a", site: "a.example.test", canViewAll: true },
|
|
41
|
+
hiddenNotRelevant: 0,
|
|
42
|
+
items: FIXTURE_MONITORING_OVERVIEW.items.map((item) => item.sourceId !== "101" ? item : {
|
|
43
|
+
...item,
|
|
44
|
+
relevance: [
|
|
45
|
+
...item.relevance,
|
|
46
|
+
{ publicationId: "desk-b", site: "b.example.test", relevant: false, label: "exclude", confidence: 0.8, delivery: { state: "not_applicable" } },
|
|
47
|
+
{ publicationId: "desk-c", site: "desk-c", relevant: true, label: "include", confidence: null,
|
|
48
|
+
delivery: { state: "delivered", referenceId: "ref-2", referenceLineageId: "lin-2" } },
|
|
49
|
+
],
|
|
50
|
+
}),
|
|
51
|
+
};
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/** Shapes and wording for the newsroom Monitoring page. The function in src/papyrus_content/monitoring_view.py produces them. */
|
|
2
|
+
import { getNewsroomNavHref } from "./newsroom-nav";
|
|
3
|
+
|
|
4
|
+
export type MonitoringDeliveryState = "delivered" | "failed" | "pending" | "not_applicable" | null;
|
|
5
|
+
|
|
6
|
+
export type MonitoringRelevance = {
|
|
7
|
+
publicationId: string;
|
|
8
|
+
site: string | null;
|
|
9
|
+
relevant: boolean | null;
|
|
10
|
+
label: string | null;
|
|
11
|
+
confidence: number | null;
|
|
12
|
+
delivery: {
|
|
13
|
+
state: MonitoringDeliveryState;
|
|
14
|
+
referenceId?: string | null;
|
|
15
|
+
referenceLineageId?: string | null;
|
|
16
|
+
deliveredAt?: string | null;
|
|
17
|
+
error?: string | null;
|
|
18
|
+
attempts?: number | null;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type MonitoringItem = {
|
|
23
|
+
sourceId: string;
|
|
24
|
+
revision: number;
|
|
25
|
+
title: string;
|
|
26
|
+
url: string | null;
|
|
27
|
+
discussionUrl: string | null;
|
|
28
|
+
observedAt: string | null;
|
|
29
|
+
routing: string;
|
|
30
|
+
routingNote: string | null;
|
|
31
|
+
decidedAt: string | null;
|
|
32
|
+
relevance: MonitoringRelevance[];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type MonitoringEmailSummary = {
|
|
36
|
+
receiptId: string; observedAt: string | null; status: string; subject: string | null;
|
|
37
|
+
envelopeSender: string | null; headerFrom: string | null; warnings: string[];
|
|
38
|
+
verdicts: Record<string, string>; linkCount: number; processedCount: number; unresolvedLinkCount: number;
|
|
39
|
+
reviewRequired: boolean; errorCode: string | null;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type MonitoringEmailDetails = {
|
|
43
|
+
submission: MonitoringEmailSummary & { corpusId: string | null }; safeBodyText: string;
|
|
44
|
+
candidateLinks: { url: string; title: string; resolutionStatus?: "unresolved" }[]; sourceMaterialIsUntrusted: true;
|
|
45
|
+
} | { error: "forbidden" | "bad_request" } | { state: "unavailable" | "not_configured" };
|
|
46
|
+
|
|
47
|
+
export type MonitoringEmailArticleResolution = {
|
|
48
|
+
url: string; title: string; resolved: true;
|
|
49
|
+
} | { error: "forbidden" | "bad_request" | "not_resolvable" | "resolution_failed"; reason?: string } | { state: "unavailable" };
|
|
50
|
+
|
|
51
|
+
export type EmailReferenceImportItem = { candidateIndex: number; source_uri: string };
|
|
52
|
+
export type EmailReferenceImportOutcome = {
|
|
53
|
+
imported: number; references: { id: string; lineageId: string; curationStatus: string }[]; error?: string;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** Split a complete editor selection into API work batches; the batch size is not a total link limit. */
|
|
57
|
+
export function emailReferenceBatches<T>(items: T[], batchSize = 20): T[][] {
|
|
58
|
+
if (!Number.isSafeInteger(batchSize) || batchSize < 1) throw new Error("batchSize must be a positive integer");
|
|
59
|
+
const batches: T[][] = [];
|
|
60
|
+
for (let offset = 0; offset < items.length; offset += batchSize) {
|
|
61
|
+
batches.push(items.slice(offset, offset + batchSize));
|
|
62
|
+
}
|
|
63
|
+
return batches;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Async email actions may only update the receipt that is still open. */
|
|
67
|
+
export function isCurrentEmailSubmissionAction(activeReceiptId: string | null, actionReceiptId: string): boolean {
|
|
68
|
+
return activeReceiptId === actionReceiptId;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function beginEmailSubmissionAction(inFlight: Set<string>, actionKey: string): boolean {
|
|
72
|
+
if (inFlight.has(actionKey)) return false;
|
|
73
|
+
inFlight.add(actionKey);
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function endEmailSubmissionAction(inFlight: Set<string>, actionKey: string): void {
|
|
78
|
+
inFlight.delete(actionKey);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type MonitoringRun = {
|
|
82
|
+
runId: string;
|
|
83
|
+
startedAt: string;
|
|
84
|
+
status: string;
|
|
85
|
+
new: number;
|
|
86
|
+
revised: number;
|
|
87
|
+
unchanged: number;
|
|
88
|
+
itemCount: number | null;
|
|
89
|
+
errorCount: number;
|
|
90
|
+
errors: (string | null)[];
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export type MonitoringRouterRun = {
|
|
94
|
+
runId: string;
|
|
95
|
+
startedAt: string;
|
|
96
|
+
mode: string;
|
|
97
|
+
status: string;
|
|
98
|
+
stoppedBy: string | null;
|
|
99
|
+
waitingForClassifier?: string[];
|
|
100
|
+
counts: Record<string, number>;
|
|
101
|
+
errorCount: number;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export type MonitoringFeed = {
|
|
105
|
+
id: string;
|
|
106
|
+
sourceUrl: string | null;
|
|
107
|
+
intervalSeconds: number | null;
|
|
108
|
+
limit: number | null;
|
|
109
|
+
enabled: boolean;
|
|
110
|
+
lastStatus: string | null;
|
|
111
|
+
lastFinishedAt: string | null;
|
|
112
|
+
nextDueAt: string | null;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export type MonitoringOverview = {
|
|
116
|
+
state: "ready" | "empty";
|
|
117
|
+
asOf: string;
|
|
118
|
+
notice?: "publication_not_routed";
|
|
119
|
+
scope: { mode: "publication" | "all"; publicationId: string | null; site: string | null; canViewAll: boolean };
|
|
120
|
+
feeds: MonitoringFeed[];
|
|
121
|
+
runs: MonitoringRun[];
|
|
122
|
+
routerRuns: MonitoringRouterRun[];
|
|
123
|
+
router: { mode: string; feedback: boolean; enabled: boolean; maxItemsPerRun: number | null } | null;
|
|
124
|
+
usage: {
|
|
125
|
+
day: string;
|
|
126
|
+
decisionsToday: number;
|
|
127
|
+
decisionsTotal: number;
|
|
128
|
+
historyComplete: boolean;
|
|
129
|
+
} | null;
|
|
130
|
+
items: MonitoringItem[];
|
|
131
|
+
emailSubmissions?: MonitoringEmailSummary[];
|
|
132
|
+
hiddenNotRelevant: number;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export type MonitoringResult =
|
|
136
|
+
| MonitoringOverview
|
|
137
|
+
| { state: "not_configured" }
|
|
138
|
+
| { state: "unavailable" }
|
|
139
|
+
| { error: "forbidden" | "bad_request" };
|
|
140
|
+
|
|
141
|
+
const ROUTING_WORDS: Record<string, string> = {
|
|
142
|
+
collected: "Collected, waiting for the router",
|
|
143
|
+
"dry-run-validated": "Checked in a dry run, not sent",
|
|
144
|
+
decided: "Judged, delivery not finished",
|
|
145
|
+
routed: "Routed",
|
|
146
|
+
refused: "Refused by the shared Cyclotron",
|
|
147
|
+
uncertain: "Uncertain, needs a person",
|
|
148
|
+
error: "Error",
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export function routingLabel(routing: string): string {
|
|
152
|
+
return ROUTING_WORDS[routing] ?? routing;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Fixed-format UTC time so the page reads the same everywhere. */
|
|
156
|
+
export function formatUtc(iso: string | null | undefined): string {
|
|
157
|
+
if (!iso) return "never";
|
|
158
|
+
const match = /^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2})/.exec(iso);
|
|
159
|
+
return match ? `${match[1]} ${match[2]} UTC` : iso;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function confidenceText(confidence: number | null): string {
|
|
163
|
+
return confidence === null ? "confidence not reported" : `${Math.round(confidence * 100)}% confidence`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function everyText(seconds: number | null): string {
|
|
167
|
+
if (!seconds) return "on a schedule";
|
|
168
|
+
return seconds % 3600 === 0 ? `every ${seconds / 3600} h` : seconds % 60 === 0 ? `every ${seconds / 60} min` : `every ${seconds} s`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function referenceHref(lineageId: string, demo?: boolean): string {
|
|
172
|
+
return getNewsroomNavHref(`/newsroom/references/${encodeURIComponent(lineageId)}`, demo);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function describeRelevance(entry: MonitoringRelevance): string {
|
|
176
|
+
if (entry.relevant === null) return "Not judged yet";
|
|
177
|
+
return entry.relevant ? "Relevant" : "Not relevant";
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function describeDelivery(entry: MonitoringRelevance): string {
|
|
181
|
+
switch (entry.delivery.state) {
|
|
182
|
+
case "delivered": return "In the References queue";
|
|
183
|
+
case "failed": return "Not in the References queue: delivery failed";
|
|
184
|
+
case "pending": return "Not in the References queue yet";
|
|
185
|
+
case "not_applicable": return "Nothing to deliver";
|
|
186
|
+
default: return "";
|
|
187
|
+
}
|
|
188
|
+
}
|
package/lib/news-desk-session.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { CategorySteeringDashboard } from "./category-repository";
|
|
2
|
-
import { createEmptyCategorySteeringDashboard } from "./category-dashboard";
|
|
3
2
|
import type { ReaderAuthSnapshot } from "../components/reader-auth-state";
|
|
4
3
|
|
|
5
4
|
export type NewsDeskShellPhase =
|
|
@@ -59,26 +58,6 @@ export function beginDeskLoad(state: NewsDeskShellState, auth: ReaderAuthSnapsho
|
|
|
59
58
|
};
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
export function beginAuthenticatedDeskHydration(
|
|
63
|
-
state: NewsDeskShellState,
|
|
64
|
-
auth: ReaderAuthSnapshot,
|
|
65
|
-
options: { canManageUsers: boolean },
|
|
66
|
-
): NewsDeskShellState {
|
|
67
|
-
const dashboard = state.dashboard ?? {
|
|
68
|
-
...createEmptyCategorySteeringDashboard(),
|
|
69
|
-
isPublicSkeleton: false,
|
|
70
|
-
canManageUsers: options.canManageUsers,
|
|
71
|
-
};
|
|
72
|
-
return {
|
|
73
|
-
...state,
|
|
74
|
-
phase: state.dashboard ? "refreshing" : "loadingDesk",
|
|
75
|
-
auth,
|
|
76
|
-
dashboard,
|
|
77
|
-
stale: false,
|
|
78
|
-
error: null,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
61
|
export function resolveSignedOut(state: NewsDeskShellState, auth: ReaderAuthSnapshot): NewsDeskShellState {
|
|
83
62
|
return {
|
|
84
63
|
...state,
|
|
@@ -143,6 +122,18 @@ export function resolveDashboardFailure(
|
|
|
143
122
|
};
|
|
144
123
|
}
|
|
145
124
|
|
|
125
|
+
/** An unverified session must never leave private records on screen. */
|
|
126
|
+
export function resolveAccessFailure(state: NewsDeskShellState, error: string): NewsDeskShellState {
|
|
127
|
+
return {
|
|
128
|
+
...state,
|
|
129
|
+
phase: "error",
|
|
130
|
+
auth: { status: "signedOut", label: "Signed out" },
|
|
131
|
+
dashboard: null,
|
|
132
|
+
stale: false,
|
|
133
|
+
error,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
146
137
|
export function patchDashboard(
|
|
147
138
|
state: NewsDeskShellState,
|
|
148
139
|
patch: (dashboard: CategorySteeringDashboard) => CategorySteeringDashboard,
|