@anthusai/papyrus 5.43.0-next.8 → 5.43.0-next.9

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.
@@ -26,6 +26,14 @@ export const monitoringViewSchemaDefinitions = {
26
26
  .returns(a.json())
27
27
  .authorization((allow) => [allow.groups(editors)])
28
28
  .handler(functionHandler("monitoringView", monitoringView)),
29
+ // Accept ("release") or Reject ("discard") one held email submission. The function validates the
30
+ // signed-in editor and that the message is routed to this publication; the receiver applies it.
31
+ monitoringDecideEmailSubmission: a
32
+ .mutation()
33
+ .arguments({ receiptId: a.string().required(), decision: a.string().required(), reason: a.string() })
34
+ .returns(a.json())
35
+ .authorization((allow) => [allow.groups(editors)])
36
+ .handler(functionHandler("monitoringView", monitoringView)),
29
37
  monitoringResolveEmailArticleLink: a
30
38
  .mutation()
31
39
  .arguments({ receiptId: a.string().required(), candidateIndex: a.integer().required() })
@@ -28,6 +28,14 @@ export const monitoringViewSchemaDefinitions = {
28
28
  .returns(a.json())
29
29
  .authorization((allow) => [allow.groups(editors)])
30
30
  .handler(functionHandler("monitoringView", monitoringView)),
31
+ // Accept ("release") or Reject ("discard") one held email submission. The function validates the
32
+ // signed-in editor and that the message is routed to this publication; the receiver applies it.
33
+ monitoringDecideEmailSubmission: a
34
+ .mutation()
35
+ .arguments({ receiptId: a.string().required(), decision: a.string().required(), reason: a.string() })
36
+ .returns(a.json())
37
+ .authorization((allow) => [allow.groups(editors)])
38
+ .handler(functionHandler("monitoringView", monitoringView)),
31
39
  monitoringResolveEmailArticleLink: a
32
40
  .mutation()
33
41
  .arguments({ receiptId: a.string().required(), candidateIndex: a.integer().required() })
@@ -1,5 +1,6 @@
1
1
  import { useEffect, useRef, useState } from "react";
2
2
  import Link from "next/link";
3
+ import { CheckIcon, XIcon } from "lucide-react";
3
4
  import { getNewsroomNavHref } from "../lib/newsroom-nav";
4
5
  import {
5
6
  confidenceText,
@@ -12,6 +13,10 @@ import {
12
13
  beginEmailSubmissionAction,
13
14
  endEmailSubmissionAction,
14
15
  isCurrentEmailSubmissionAction,
16
+ emailSubmissionAccepted,
17
+ QUARANTINE_LABELS,
18
+ type EmailDecisionResult,
19
+ type EmailQuarantine,
15
20
  type MonitoringItem,
16
21
  type MonitoringEmailDetails,
17
22
  type MonitoringEmailArticleResolution,
@@ -97,8 +102,9 @@ function QueueItem({ item, ownId, demo }: { item: MonitoringItem; ownId: string
97
102
  );
98
103
  }
99
104
 
100
- export function EmailSubmissions({ items, detail, onOpen, onResolveArticleLink, onImportCandidates }: {
105
+ export function EmailSubmissions({ items, detail, onOpen, onResolveArticleLink, onImportCandidates, onDecide }: {
101
106
  items: MonitoringEmailSummary[]; detail: MonitoringEmailDetails | null; onOpen?: (receiptId: string) => void;
107
+ onDecide?: (receiptId: string, decision: "accept" | "reject", reason: string) => Promise<EmailDecisionResult>;
102
108
  onResolveArticleLink?: (receiptId: string, candidateIndex: number) => Promise<MonitoringEmailArticleResolution>;
103
109
  onImportCandidates?: (receiptId: string, items: EmailReferenceImportItem[]) => Promise<EmailReferenceImportOutcome>;
104
110
  }) {
@@ -108,8 +114,15 @@ export function EmailSubmissions({ items, detail, onOpen, onResolveArticleLink,
108
114
  const [busyImportByReceipt, setBusyImportByReceipt] = useState<Record<string, boolean>>({});
109
115
  const [noticeByReceipt, setNoticeByReceipt] = useState<Record<string, string>>({});
110
116
  const [importedByReceipt, setImportedByReceipt] = useState<Record<string, EmailReferenceImportOutcome["references"]>>({});
117
+ const [decidedByReceipt, setDecidedByReceipt] = useState<Record<string, EmailQuarantine>>({});
118
+ const [reasonByReceipt, setReasonByReceipt] = useState<Record<string, string>>({});
119
+ const [busyDecisionByReceipt, setBusyDecisionByReceipt] = useState<Record<string, boolean>>({});
111
120
  const receiptId = detail && "submission" in detail ? detail.submission.receiptId : null;
112
121
  const receiptKey = receiptId ?? "";
122
+ const quarantine = (detail && "submission" in detail ? decidedByReceipt[receiptKey] ?? detail.submission.quarantine : null);
123
+ const accepted = emailSubmissionAccepted(quarantine);
124
+ const reason = reasonByReceipt[receiptKey] ?? "";
125
+ const busyDecision = busyDecisionByReceipt[receiptKey] ?? false;
113
126
  const selected = selectedByReceipt[receiptKey] ?? {};
114
127
  const resolved = resolvedByReceipt[receiptKey] ?? {};
115
128
  const busyIndex = busyIndexByReceipt[receiptKey] ?? null;
@@ -160,6 +173,34 @@ export function EmailSubmissions({ items, detail, onOpen, onResolveArticleLink,
160
173
  }
161
174
  };
162
175
 
176
+ const decide = async (decision: "accept" | "reject") => {
177
+ if (!receiptId || !onDecide) return;
178
+ const requestReceiptId = receiptId;
179
+ const actionKey = `decide:${requestReceiptId}`;
180
+ if (!beginEmailSubmissionAction(inFlightActions.current, actionKey)) return;
181
+ setBusyDecisionByReceipt((current) => ({ ...current, [requestReceiptId]: true }));
182
+ setNoticeByReceipt((current) => ({ ...current, [requestReceiptId]: "" }));
183
+ try {
184
+ const result = await onDecide(requestReceiptId, decision, reason.trim());
185
+ if ("ok" in result) {
186
+ setDecidedByReceipt((current) => ({ ...current, [requestReceiptId]: result.quarantine }));
187
+ setNoticeByReceipt((current) => ({ ...current, [requestReceiptId]: decision === "accept"
188
+ ? "Accepted. The receiver queues it within a minute; the links can be added to References once it is queued."
189
+ : "Rejected. The message is closed and kept privately until it expires." }));
190
+ } else if ("error" in result && result.quarantine) {
191
+ setDecidedByReceipt((current) => ({ ...current, [requestReceiptId]: result.quarantine as EmailQuarantine }));
192
+ setNoticeByReceipt((current) => ({ ...current, [requestReceiptId]: "Another decision was already recorded for this message; it cannot be changed." }));
193
+ } else {
194
+ setNoticeByReceipt((current) => ({ ...current, [requestReceiptId]: "The decision was not recorded. Check your editor access and try again." }));
195
+ }
196
+ } catch {
197
+ setNoticeByReceipt((current) => ({ ...current, [requestReceiptId]: "The decision was not recorded. It is safe to try again." }));
198
+ } finally {
199
+ endEmailSubmissionAction(inFlightActions.current, actionKey);
200
+ setBusyDecisionByReceipt((current) => ({ ...current, [requestReceiptId]: false }));
201
+ }
202
+ };
203
+
163
204
  const importSelected = async () => {
164
205
  if (!receiptId || !detail || !("submission" in detail) || !onImportCandidates) return;
165
206
  const requestReceiptId = receiptId;
@@ -212,6 +253,7 @@ export function EmailSubmissions({ items, detail, onOpen, onResolveArticleLink,
212
253
  <span>SES signals — SPF {item.verdicts.spf}, DKIM {item.verdicts.dkim}, DMARC {item.verdicts.dmarc}; spam {item.verdicts.spam}, virus {item.verdicts.virus}</span>
213
254
  {item.warnings.length ? <span>Warnings: {item.warnings.join(", ")}</span> : null}
214
255
  {item.errorCode ? <span>State: {item.status} ({item.errorCode})</span> : <span>State: {item.status}</span>}
256
+ {item.quarantine ? <span data-email-quarantine-state={item.quarantine.state}>{QUARANTINE_LABELS[item.quarantine.state]}</span> : null}
215
257
  {onOpen ? <button type="button" onClick={() => onOpen(item.receiptId)}>Read email and review candidate links</button> : null}
216
258
  </li>)}
217
259
  </ul> : <p>No email submissions are available for this publication.</p>}
@@ -220,32 +262,60 @@ export function EmailSubmissions({ items, detail, onOpen, onResolveArticleLink,
220
262
  <h4>{detail.submission.subject || "Submitted email"}</h4>
221
263
  <p>Original content is shown as inert text. Sender and message content are untrusted; no instructions are executed.</p>
222
264
  <pre style={{ whiteSpace: "pre-wrap", overflowWrap: "anywhere" }}>{detail.safeBodyText}</pre>
265
+ {quarantine ? <section aria-label="Held for review" data-email-quarantine={quarantine.state}>
266
+ <h5>{QUARANTINE_LABELS[quarantine.state]}</h5>
267
+ {quarantine.state === "quarantined" ? <>
268
+ <p>This message was held privately and nothing was routed. Accept it to queue its links as if it had been accepted, or Reject it to close it. Sender identity and authentication signals never grant access; the decision is yours and is recorded.</p>
269
+ <label>Why? (optional)
270
+ <textarea value={reason} rows={3} maxLength={500} style={{ display: "block", width: "100%" }}
271
+ onChange={(event) => setReasonByReceipt((current) => ({ ...current, [receiptKey]: event.target.value }))} />
272
+ </label>
273
+ <button type="button" data-email-decision="accept" disabled={busyDecision || !onDecide} onClick={() => void decide("accept")}>
274
+ <CheckIcon aria-hidden="true" size={16} /> Accept
275
+ </button>
276
+ <button type="button" data-email-decision="reject" disabled={busyDecision || !onDecide} onClick={() => void decide("reject")}>
277
+ <XIcon aria-hidden="true" size={16} /> Reject
278
+ </button>
279
+ </> : null}
280
+ {quarantine.decision ? <p data-email-decision-record>
281
+ {quarantine.decision.action === "accept" ? "Accepted" : "Rejected"}
282
+ {quarantine.decision.by ? ` by ${quarantine.decision.by}` : ""}{quarantine.decision.at ? ` on ${formatUtc(quarantine.decision.at)}` : ""}
283
+ {quarantine.decision.reason ? `. Why: ${quarantine.decision.reason}` : "."}
284
+ </p> : null}
285
+ {quarantine.failure ? <p role="alert">The decision could not be applied ({quarantine.failure}).</p> : null}
286
+ {quarantine.state === "expired" ? <p>This message expired and can no longer be accepted.</p> : null}
287
+ {notice && !accepted ? <p role="status" data-email-decision-result>{notice}</p> : null}
288
+ </section> : null}
223
289
  <h5>Candidate references ({detail.candidateLinks.length})</h5>
224
290
  <ul>{detail.candidateLinks.map((link, index) => <li key={`${link.url}-${index}`}>
225
- {link.resolutionStatus === "unresolved" && !resolved[index]
291
+ {!accepted
292
+ ? <span>{link.title || "Source link"} <code>{link.url}</code></span>
293
+ : link.resolutionStatus === "unresolved" && !resolved[index]
226
294
  ? <span>{link.title || "Newsletter article link"} (tracking redirect not opened)</span>
227
295
  : <a href={(resolved[index]?.url ?? link.url)} target="_blank" rel="noreferrer noopener">{resolved[index]?.title ?? (link.title || "Source link")}</a>}
228
- <label>
296
+ {accepted ? <label>
229
297
  <input type="checkbox" checked={selected[index] ?? false}
230
298
  disabled={link.resolutionStatus === "unresolved" && !resolved[index]}
231
299
  onChange={(event) => setSelectedByReceipt((current) => ({ ...current, [receiptKey]: { ...current[receiptKey], [index]: event.target.checked } }))} />
232
300
  Add as pending Reference
233
- </label>
234
- {link.resolutionStatus === "unresolved" && !resolved[index] ? (
301
+ </label> : null}
302
+ {accepted && link.resolutionStatus === "unresolved" && !resolved[index] ? (
235
303
  <button type="button" disabled={busyIndex !== null || !onResolveArticleLink} onClick={() => void resolveLink(index)}>
236
304
  {busyIndex === index ? "Resolving…" : "Resolve article redirect (may register a click)"}
237
305
  </button>
238
306
  ) : null}
239
307
  </li>)}</ul>
240
- <p>Adding links creates or reuses pending References. It does not approve, label, classify, or publish them.</p>
241
- <button type="button" disabled={busyImport || !onImportCandidates || !detail.submission.corpusId}
242
- onClick={() => void importSelected()}>
243
- {busyImport ? "Adding References…" : "Add selected links to References"}
244
- </button>
245
- {notice ? <p role="status" data-email-import-result>{notice}</p> : null}
246
- {importedReferences.length ? <ul>{importedReferences.map((reference, index) => (
247
- <li key={`${reference.id}-${index}`}><Link href={getNewsroomNavHref(`/newsroom/references/${encodeURIComponent(reference.lineageId)}`, false)}>Review imported Reference {index + 1}</Link></li>
248
- ))}</ul> : null}
308
+ {accepted ? <>
309
+ <p>Adding links creates or reuses pending References. It does not approve, label, classify, or publish them.</p>
310
+ <button type="button" disabled={busyImport || !onImportCandidates || !detail.submission.corpusId}
311
+ onClick={() => void importSelected()}>
312
+ {busyImport ? "Adding References…" : "Add selected links to References"}
313
+ </button>
314
+ {notice ? <p role="status" data-email-import-result>{notice}</p> : null}
315
+ {importedReferences.length ? <ul>{importedReferences.map((reference, index) => (
316
+ <li key={`${reference.id}-${index}`}><Link href={getNewsroomNavHref(`/newsroom/references/${encodeURIComponent(reference.lineageId)}`, false)}>Review imported Reference {index + 1}</Link></li>
317
+ ))}</ul> : null}
318
+ </> : <p>Links stay inert text, and nothing can be added to References, until this message is accepted.</p>}
249
319
  </section>
250
320
  ) : detail && "error" in detail ? <p role="alert">You cannot read this submission.</p>
251
321
  : detail && "state" in detail ? <p role="alert">Submission details are unavailable.</p> : null}
@@ -253,8 +323,9 @@ export function EmailSubmissions({ items, detail, onOpen, onResolveArticleLink,
253
323
  );
254
324
  }
255
325
 
256
- function Ready({ data, demo, emailDetail, onEmailOpen, onResolveArticleLink, onImportCandidates }: {
326
+ function Ready({ data, demo, emailDetail, onEmailOpen, onResolveArticleLink, onImportCandidates, onDecide }: {
257
327
  data: MonitoringOverview; demo: boolean; emailDetail: MonitoringEmailDetails | null; onEmailOpen: (id: string) => void;
328
+ onDecide?: (receiptId: string, decision: "accept" | "reject", reason: string) => Promise<EmailDecisionResult>;
258
329
  onResolveArticleLink?: (receiptId: string, candidateIndex: number) => Promise<MonitoringEmailArticleResolution>;
259
330
  onImportCandidates?: (receiptId: string, items: EmailReferenceImportItem[]) => Promise<EmailReferenceImportOutcome>;
260
331
  }) {
@@ -267,7 +338,8 @@ function Ready({ data, demo, emailDetail, onEmailOpen, onResolveArticleLink, onI
267
338
 
268
339
  <EmailSubmissions items={data.emailSubmissions ?? []} detail={emailDetail} onOpen={onEmailOpen}
269
340
  onResolveArticleLink={demo ? undefined : onResolveArticleLink}
270
- onImportCandidates={demo ? undefined : onImportCandidates} />
341
+ onImportCandidates={demo ? undefined : onImportCandidates}
342
+ onDecide={demo ? undefined : onDecide} />
271
343
 
272
344
  <article className="news-desk-doctrine-card" data-monitoring-section="router">
273
345
  <div className="news-desk-doctrine-card__header"><div><h3>Router</h3><span>Sends collected items to the shared Cyclotron and delivers relevant ones to References</span></div></div>
@@ -375,6 +447,7 @@ export function MonitoringOverviewView({
375
447
  onEmailOpen = () => {},
376
448
  onResolveArticleLink,
377
449
  onImportCandidates,
450
+ onDecide,
378
451
  }: {
379
452
  result: MonitoringResult | null;
380
453
  canViewAll: boolean;
@@ -385,6 +458,7 @@ export function MonitoringOverviewView({
385
458
  onEmailOpen?: (receiptId: string) => void;
386
459
  onResolveArticleLink?: (receiptId: string, candidateIndex: number) => Promise<MonitoringEmailArticleResolution>;
387
460
  onImportCandidates?: (receiptId: string, items: EmailReferenceImportItem[]) => Promise<EmailReferenceImportOutcome>;
461
+ onDecide?: (receiptId: string, decision: "accept" | "reject", reason: string) => Promise<EmailDecisionResult>;
388
462
  }) {
389
463
  let body: React.ReactNode;
390
464
  if (result === null) {
@@ -401,7 +475,7 @@ export function MonitoringOverviewView({
401
475
  body = <Message kind="empty" title="Nothing has been collected yet">No feed is configured and no run has been recorded. Items appear here after the collector&apos;s first run.</Message>;
402
476
  } else {
403
477
  body = <Ready data={result} demo={demo} emailDetail={emailDetail} onEmailOpen={onEmailOpen}
404
- onResolveArticleLink={onResolveArticleLink} onImportCandidates={onImportCandidates} />;
478
+ onResolveArticleLink={onResolveArticleLink} onImportCandidates={onImportCandidates} onDecide={onDecide} />;
405
479
  }
406
480
  const scopeLine = result && "scope" in result
407
481
  ? (result.scope.mode === "all" ? "Showing the whole shared pipeline, every publication." : `Showing what is relevant to ${result.scope.publicationId ?? "this publication"}.`)
@@ -3,6 +3,7 @@
3
3
  import { useEffect, useState } from "react";
4
4
  import { useSearchParams } from "next/navigation";
5
5
  import {
6
+ decideMonitoringEmailSubmission,
6
7
  importMonitoringEmailCandidates,
7
8
  loadMonitoringEmailSubmission,
8
9
  loadMonitoringOverview,
@@ -39,7 +40,8 @@ function MonitoringEmailReceipt({ receiptId, isDemo }: { receiptId: string; isDe
39
40
  return <div data-news-desk-admin-panel="monitoring">
40
41
  {detail === null ? <p role="status">Loading email submission…</p> :
41
42
  <EmailSubmissions items={"submission" in detail ? [detail.submission] : []} detail={detail}
42
- onResolveArticleLink={resolveMonitoringEmailArticleLink} onImportCandidates={importMonitoringEmailCandidates} />}
43
+ onResolveArticleLink={resolveMonitoringEmailArticleLink} onImportCandidates={importMonitoringEmailCandidates}
44
+ onDecide={decideMonitoringEmailSubmission} />}
43
45
  </div>;
44
46
  }
45
47
 
@@ -86,5 +88,5 @@ function MonitoringOverviewPanel({ isAdmin, isDemo }: { isAdmin: boolean; isDemo
86
88
 
87
89
  return <MonitoringOverviewView result={result} canViewAll={isAdmin} mode={mode} onModeChange={setMode} demo={isDemo}
88
90
  emailDetail={emailDetail} onEmailOpen={isDemo ? () => {} : openEmail} onResolveArticleLink={isDemo ? undefined : resolveEmailArticleLink}
89
- onImportCandidates={importEmailCandidates} />;
91
+ onImportCandidates={importEmailCandidates} onDecide={isDemo ? undefined : decideMonitoringEmailSubmission} />;
90
92
  }
@@ -3,6 +3,7 @@
3
3
  import { generateClient } from "aws-amplify/data";
4
4
  import { emailReferenceBatches } from "./monitoring-overview";
5
5
  import type {
6
+ EmailDecisionResult,
6
7
  EmailReferenceImportItem,
7
8
  EmailReferenceImportOutcome,
8
9
  MonitoringEmailArticleResolution,
@@ -10,7 +11,7 @@ import type {
10
11
  MonitoringResult,
11
12
  } from "./monitoring-overview";
12
13
 
13
- type QueryField = "monitoringOverview" | "monitoringEmailSubmission" | "monitoringResolveEmailArticleLink" | "intakeEmailSubmissionReferences" | "monitoringQueueEmailReference";
14
+ type QueryField = "monitoringOverview" | "monitoringEmailSubmission" | "monitoringDecideEmailSubmission" | "monitoringResolveEmailArticleLink" | "intakeEmailSubmissionReferences" | "monitoringQueueEmailReference";
14
15
  type GraphQLResult = { data?: Partial<Record<QueryField, unknown>>; errors?: { message?: string; errorType?: string }[] };
15
16
 
16
17
  async function query<T>(field: QueryField, queryText: string, variables: Record<string, string | number>): Promise<T | { error: "forbidden" } | { state: "unavailable" }> {
@@ -40,6 +41,18 @@ export async function loadMonitoringEmailSubmission(receiptId: string): Promise<
40
41
  `query MonitoringEmailSubmission($receiptId: String!) { monitoringEmailSubmission(receiptId: $receiptId) }`, { receiptId });
41
42
  }
42
43
 
44
+ /**
45
+ * Accept or Reject one held email submission. "Accept" releases it into the normal intake and
46
+ * "Reject" discards it; the editor and the optional reason are recorded server-side.
47
+ */
48
+ export async function decideMonitoringEmailSubmission(
49
+ receiptId: string, decision: "accept" | "reject", reason: string,
50
+ ): Promise<EmailDecisionResult> {
51
+ return query<EmailDecisionResult>("monitoringDecideEmailSubmission",
52
+ `mutation MonitoringDecideEmailSubmission($receiptId: String!, $decision: String!, $reason: String) { monitoringDecideEmailSubmission(receiptId: $receiptId, decision: $decision, reason: $reason) }`,
53
+ { receiptId, decision: decision === "accept" ? "release" : "discard", reason });
54
+ }
55
+
43
56
  /** Resolve one explicitly selected Beehiiv article redirect; this may register a click with the sender. */
44
57
  export async function resolveMonitoringEmailArticleLink(receiptId: string, candidateIndex: number): Promise<MonitoringEmailArticleResolution> {
45
58
  return query<MonitoringEmailArticleResolution>("monitoringResolveEmailArticleLink",
@@ -37,9 +37,39 @@ export type MonitoringEmailSummary = {
37
37
  receiptId: string; observedAt: string | null; status: string; subject: string | null;
38
38
  envelopeSender: string | null; headerFrom: string | null; warnings: string[];
39
39
  verdicts: Record<string, string>; linkCount: number; processedCount: number; unresolvedLinkCount: number;
40
- reviewRequired: boolean; errorCode: string | null;
40
+ reviewRequired: boolean; errorCode: string | null; quarantine: EmailQuarantine | null;
41
41
  };
42
42
 
43
+ /** Where a held email submission stands. `null` means it never needed review. */
44
+ export type EmailQuarantineState =
45
+ | "quarantined" | "release_requested" | "released" | "discard_requested" | "discarded"
46
+ | "expired" | "decision_failed" | "release_failed";
47
+ export type EmailQuarantine = {
48
+ state: EmailQuarantineState;
49
+ decision: { action: "accept" | "reject"; by: string; at: string | null; reason: string } | null;
50
+ failure: string | null;
51
+ };
52
+ export type EmailDecisionResult =
53
+ | { ok: true; already: boolean; quarantine: EmailQuarantine }
54
+ | { error: "forbidden" | "bad_request" | "already_decided" | "not_quarantined" | "expired"; quarantine?: EmailQuarantine }
55
+ | { state: "unavailable" | "not_configured" };
56
+
57
+ export const QUARANTINE_LABELS: Record<EmailQuarantineState, string> = {
58
+ quarantined: "Held for review",
59
+ release_requested: "Accepted, being queued",
60
+ released: "Accepted",
61
+ discard_requested: "Rejected, being closed",
62
+ discarded: "Rejected",
63
+ expired: "Expired",
64
+ decision_failed: "Decision could not be applied",
65
+ release_failed: "Accepted, retrying",
66
+ };
67
+
68
+ /** Links and import are available only once a message is accepted (or never needed review). */
69
+ export function emailSubmissionAccepted(quarantine: EmailQuarantine | null): boolean {
70
+ return quarantine === null || quarantine.state === "released";
71
+ }
72
+
43
73
  export type MonitoringEmailDetails = {
44
74
  submission: MonitoringEmailSummary & { corpusId: string | null }; safeBodyText: string;
45
75
  candidateLinks: { url: string; title: string; resolutionStatus?: "unresolved" }[]; sourceMaterialIsUntrusted: true;
package/package.json CHANGED
@@ -470,7 +470,7 @@
470
470
  "optional": true
471
471
  }
472
472
  },
473
- "version": "5.43.0-next.8",
473
+ "version": "5.43.0-next.9",
474
474
  "dependencies": {
475
475
  "@aws-amplify/adapter-nextjs": "^1.7.3",
476
476
  "@aws-crypto/sha256-js": "5.2.0",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "5.43.0-next.8",
2
+ "version": "5.43.0-next.9",
3
3
  "routes": [
4
4
  {
5
5
  "file": "app/%5F_preview/[[...path]]/route.ts",