@anthusai/papyrus 2.0.0-next.10 → 2.0.0-next.101

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/amplify/auth/resource.js +16 -1
  2. package/amplify/auth/resource.ts +19 -1
  3. package/amplify/data/function-handler.js +20 -0
  4. package/amplify/data/function-handler.ts +40 -0
  5. package/amplify/data/resource.js +152 -62
  6. package/amplify/data/resource.ts +158 -62
  7. package/amplify/data/schema.js +2102 -1986
  8. package/amplify/data/schema.ts +199 -70
  9. package/amplify/functions/assignment-action/data-access.js +2 -0
  10. package/amplify/functions/assignment-action/data-access.ts +2 -0
  11. package/amplify/functions/assignment-action/handler.ts +1 -1
  12. package/amplify/functions/automation-view/handler.py +17 -0
  13. package/amplify/functions/automation-view/resource.js +24 -0
  14. package/amplify/functions/automation-view/resource.ts +34 -0
  15. package/amplify/functions/automation-view/schema.js +26 -0
  16. package/amplify/functions/automation-view/schema.ts +28 -0
  17. package/amplify/functions/automation-view/wire.js +56 -0
  18. package/amplify/functions/automation-view/wire.ts +76 -0
  19. package/amplify/functions/content-actions/handler.py +64 -3
  20. package/amplify/functions/content-actions/requirements.txt +1 -0
  21. package/amplify/functions/content-actions/resource.js +1 -1
  22. package/amplify/functions/content-actions/resource.ts +1 -1
  23. package/amplify/{plugins/editorial-checks/function → functions/editorial-checks}/handler.py +3 -3
  24. package/amplify/functions/editorial-checks/requirements.txt +1 -0
  25. package/amplify/{plugins/editorial-checks/function → functions/editorial-checks}/resource.js +7 -7
  26. package/amplify/{plugins/editorial-checks/function → functions/editorial-checks}/resource.ts +7 -7
  27. package/amplify/functions/editorial-checks/schema.js +122 -0
  28. package/amplify/{plugins → functions}/editorial-checks/schema.ts +40 -12
  29. package/amplify/functions/editorial-checks/wire.js +53 -0
  30. package/amplify/functions/editorial-checks/wire.ts +61 -0
  31. package/amplify/functions/monitoring-view/handler.py +11 -0
  32. package/amplify/functions/monitoring-view/resource.js +24 -0
  33. package/amplify/functions/monitoring-view/resource.ts +34 -0
  34. package/amplify/functions/monitoring-view/schema.js +29 -0
  35. package/amplify/functions/monitoring-view/schema.ts +31 -0
  36. package/amplify/functions/monitoring-view/wire.js +64 -0
  37. package/amplify/functions/monitoring-view/wire.ts +81 -0
  38. package/amplify/functions/public-forms/core.js +247 -0
  39. package/amplify/functions/public-forms/core.ts +310 -0
  40. package/amplify/functions/public-forms/handler.ts +41 -0
  41. package/amplify/functions/public-forms/origins.js +35 -0
  42. package/amplify/functions/public-forms/origins.ts +43 -0
  43. package/amplify/functions/public-forms/resource.js +9 -0
  44. package/amplify/functions/public-forms/resource.ts +10 -0
  45. package/amplify/functions/public-forms/wire.js +25 -0
  46. package/amplify/functions/public-forms/wire.ts +32 -0
  47. package/amplify/functions/shared/editorial-context.ts +63 -4
  48. package/amplify/functions/shared/python-bundle.js +20 -14
  49. package/amplify/functions/shared/python-bundle.ts +25 -18
  50. package/amplify/plugins/newsletter/config.js +245 -0
  51. package/amplify/plugins/newsletter/config.ts +305 -0
  52. package/amplify/plugins/newsletter/function/handler.py +6 -0
  53. package/amplify/plugins/newsletter/function/requirements.txt +5 -0
  54. package/amplify/plugins/newsletter/ops/handler.py +12 -0
  55. package/amplify/plugins/newsletter/ops/resource.js +27 -0
  56. package/amplify/plugins/newsletter/ops/resource.ts +37 -0
  57. package/amplify/plugins/newsletter/papyrus.plugin.js +263 -0
  58. package/amplify/plugins/newsletter/papyrus.plugin.ts +311 -0
  59. package/amplify/plugins/newsletter/schema.js +57 -0
  60. package/amplify/plugins/newsletter/schema.ts +62 -0
  61. package/amplify/plugins/newsletter/secure-string-parameter.js +107 -0
  62. package/amplify/plugins/newsletter/secure-string-parameter.ts +120 -0
  63. package/amplify/plugins/newsletter/sending.js +140 -0
  64. package/amplify/plugins/newsletter/sending.ts +186 -0
  65. package/amplify/plugins/reader-steering/config.js +182 -0
  66. package/amplify/plugins/reader-steering/config.ts +290 -0
  67. package/amplify/plugins/reader-steering/function/ai_moderation.py +246 -0
  68. package/amplify/plugins/reader-steering/function/handler.py +1567 -0
  69. package/amplify/plugins/reader-steering/function/moderation-prompt.md +27 -0
  70. package/amplify/plugins/reader-steering/function/moderation.py +316 -0
  71. package/amplify/plugins/reader-steering/function/proposals.py +458 -0
  72. package/amplify/plugins/reader-steering/function/resource.js +27 -0
  73. package/amplify/plugins/reader-steering/function/resource.ts +35 -0
  74. package/amplify/plugins/reader-steering/papyrus.plugin.js +186 -0
  75. package/amplify/plugins/reader-steering/papyrus.plugin.ts +212 -0
  76. package/amplify/plugins/reader-steering/schema.js +229 -0
  77. package/amplify/plugins/reader-steering/schema.ts +231 -0
  78. package/amplify/plugins/registry.js +4 -2
  79. package/amplify/plugins/registry.ts +4 -2
  80. package/amplify/plugins/signup/schema.js +2 -1
  81. package/amplify/plugins/signup/schema.ts +2 -1
  82. package/amplify/plugins/types.ts +5 -0
  83. package/amplify/plugins/wire.js +1 -0
  84. package/amplify/plugins/wire.ts +1 -0
  85. package/amplify/publication/projection-lease.js +30 -0
  86. package/amplify/publication/projection-lease.ts +39 -0
  87. package/amplify/publication/redirect-and-index.js +105 -0
  88. package/amplify/publication/redirect-rules.js +73 -0
  89. package/amplify/publication/redirect-rules.ts +93 -0
  90. package/amplify/publication/s3-projection.js +7 -1
  91. package/amplify/publication/s3-projection.ts +9 -1
  92. package/amplify/site-backend-config.js +19 -0
  93. package/amplify/site-backend-config.ts +45 -1
  94. package/amplify/site-backend-names.js +4 -2
  95. package/amplify/site-backend-names.ts +4 -2
  96. package/amplify/site-backend.js +46 -4
  97. package/amplify/site-backend.ts +49 -4
  98. package/app/api/auth/prospectus/sign-in/route.ts +4 -0
  99. package/app/api/newsletter/confirm/route.ts +13 -0
  100. package/app/api/newsletter/preferences/route.ts +13 -0
  101. package/app/api/newsletter/state/route.ts +9 -0
  102. package/app/api/newsletter/subscribe/route.ts +9 -0
  103. package/app/api/newsletter/unsubscribe/route.ts +13 -0
  104. package/app/api/newsroom/chat/[...path]/route.ts +17 -0
  105. package/app/api/newsroom/curation/context/route.ts +9 -0
  106. package/app/api/newsroom/curation/priming/route.ts +9 -0
  107. package/app/api/newsroom/curation/publication/route.ts +9 -0
  108. package/app/api/reader/comment/delete/route.ts +9 -0
  109. package/app/api/reader/comment/edit/route.ts +9 -0
  110. package/app/api/reader/comment/route.ts +9 -0
  111. package/app/api/reader/flag/route.ts +9 -0
  112. package/app/api/reader/handle/route.ts +9 -0
  113. package/app/api/reader/me/route.ts +9 -0
  114. package/app/api/reader/rating/route.ts +9 -0
  115. package/app/api/reader/report/route.ts +9 -0
  116. package/app/api/reader/retired/route.ts +9 -0
  117. package/app/api/reader/submit/route.ts +9 -0
  118. package/app/api/reader/vote/route.ts +9 -0
  119. package/app/layout.tsx +5 -1
  120. package/bin/papyrus-infra.mjs +20 -1
  121. package/components/automation-panel.tsx +49 -0
  122. package/components/automation-run-now.tsx +97 -0
  123. package/components/automation-view.tsx +195 -0
  124. package/components/editorial-checks-admin-panel.tsx +38 -13
  125. package/components/moderation-panel.tsx +248 -0
  126. package/components/monitoring-overview-view.tsx +414 -0
  127. package/components/monitoring-panel.tsx +60 -0
  128. package/components/news-desk-client-provider.tsx +18 -18
  129. package/components/news-desk-taxonomy-client.ts +28 -8
  130. package/components/newsletter-panel.tsx +474 -0
  131. package/components/newsletter-sections.tsx +163 -0
  132. package/components/newsroom-access-gate.tsx +16 -2
  133. package/components/newsroom-article-editor.tsx +11 -11
  134. package/components/newsroom-articles-view.tsx +6 -6
  135. package/components/newsroom-assignment-detail.tsx +6 -6
  136. package/components/newsroom-board-view.tsx +8 -3
  137. package/components/newsroom-curation-bootstrap.tsx +45 -0
  138. package/components/newsroom-ops-overview.tsx +26 -96
  139. package/components/newsroom-ops-shell.tsx +2 -2
  140. package/components/newsroom-organizer.css +33 -0
  141. package/components/newsroom-overview-skeleton.tsx +25 -0
  142. package/components/newsroom-references-view.tsx +192 -49
  143. package/components/notification-center.tsx +281 -0
  144. package/components/papyrus-console-conversation.css +14 -0
  145. package/components/papyrus-console-conversation.tsx +55 -0
  146. package/components/papyrus-console-shell.tsx +62 -420
  147. package/components/papyrus-private-chat-panel.tsx +134 -0
  148. package/components/publication-cyclotron-panel.tsx +15 -10
  149. package/components/reader-auth-state.ts +1 -1
  150. package/components/reference-add-entry.tsx +75 -0
  151. package/components/reference-curation-controls.tsx +31 -22
  152. package/components/reference-cyclotron-priming.tsx +58 -19
  153. package/components/reference-cyclotron-suggestion.tsx +10 -6
  154. package/components/reference-priming.tsx +14 -10
  155. package/components/reference-relevance-review.tsx +3 -3
  156. package/components/reference-section-suggestion.tsx +1 -1
  157. package/components/topic-steering-workspace.tsx +237 -158
  158. package/components/use-editorial-checks.ts +12 -10
  159. package/infra/amplify-app-shell.js +96 -16
  160. package/infra/amplify-app-shell.ts +108 -18
  161. package/infra/cloud-email-config.js +32 -0
  162. package/infra/cloud-email-config.ts +39 -0
  163. package/infra/cloud-email.js +57 -0
  164. package/infra/cloud-email.test.js +69 -0
  165. package/infra/cloud-email.test.ts +66 -0
  166. package/infra/cloud-email.ts +60 -0
  167. package/infra/index.js +1 -0
  168. package/infra/secure-string-parameter-handler.js +41 -0
  169. package/infra/secure-string-parameter-handler.ts +41 -0
  170. package/infra/secure-string-parameter.js +79 -0
  171. package/infra/secure-string-parameter.ts +91 -0
  172. package/infra/site-config.js +92 -6
  173. package/infra/site-config.ts +138 -6
  174. package/lib/automation-client.ts +54 -0
  175. package/lib/automation-fixture.ts +93 -0
  176. package/lib/automation-overview.ts +252 -0
  177. package/lib/category-repository.ts +14 -12
  178. package/lib/console-web-context.ts +1 -1
  179. package/lib/curation-routes.ts +345 -0
  180. package/lib/cyclotron-suggestions.ts +4 -4
  181. package/lib/editorial-checks-backend.ts +1 -1
  182. package/lib/editorial-checks-client.ts +19 -1
  183. package/lib/editorial-checks-demo.ts +2 -2
  184. package/lib/editorial-checks-settings.ts +3 -0
  185. package/lib/editorial-checks.ts +8 -11
  186. package/lib/editorial-preview.ts +5 -5
  187. package/lib/monitoring-client.ts +83 -0
  188. package/lib/monitoring-overview-fixture.ts +51 -0
  189. package/lib/monitoring-overview.ts +188 -0
  190. package/lib/news-desk-session.ts +12 -21
  191. package/lib/newsletter-api.ts +418 -0
  192. package/lib/newsletter-client.ts +46 -0
  193. package/lib/newsletter-fixture.ts +184 -0
  194. package/lib/newsletter-model.ts +202 -0
  195. package/lib/newsroom-demo-dashboard.ts +1 -1
  196. package/lib/newsroom-nav.ts +1 -1
  197. package/lib/newsroom-organizer-nav.ts +2 -1
  198. package/lib/papyrus-chat-admission.ts +65 -0
  199. package/lib/papyrus-hosted-chat.ts +217 -0
  200. package/lib/papyrus-private-chat-client.ts +81 -0
  201. package/lib/prospectus-session.ts +30 -14
  202. package/lib/prospectus-sign-in-routes.ts +106 -31
  203. package/lib/prospectus-site-identity.ts +46 -6
  204. package/lib/reader-api.ts +145 -0
  205. package/lib/reader-moderation.ts +220 -0
  206. package/lib/reader-retirement-route.ts +89 -0
  207. package/lib/reader-subject.md +29 -0
  208. package/lib/reader-subject.ts +53 -0
  209. package/lib/reference-review-details.ts +71 -0
  210. package/lib/reference-review-request.ts +14 -0
  211. package/lib/relevance-decisions.ts +42 -10
  212. package/lib/relevance-status.ts +2 -2
  213. package/lib/review-client.ts +120 -0
  214. package/lib/review-fixture.ts +45 -0
  215. package/lib/review-requests.ts +246 -0
  216. package/lib/semantic-graph.ts +2 -0
  217. package/lib/shared-chat-admission.ts +182 -0
  218. package/lib/site-brand-tenant.ts +11 -6
  219. package/lib/site-brand.ts +6 -3
  220. package/lib/site-origin-value.ts +50 -0
  221. package/lib/site-origin.ts +8 -0
  222. package/lib/use-reference-review-data.ts +44 -0
  223. package/middleware.ts +14 -3
  224. package/package.json +65 -3
  225. package/routes.manifest.json +227 -2
  226. package/src/with-papyrus.mjs +1 -1
  227. package/amplify/plugins/editorial-checks/function/requirements.txt +0 -1
  228. package/amplify/plugins/editorial-checks/papyrus.plugin.js +0 -71
  229. package/amplify/plugins/editorial-checks/papyrus.plugin.ts +0 -87
  230. package/amplify/plugins/editorial-checks/schema.js +0 -94
@@ -1,13 +1,18 @@
1
1
  import { type ClientSchema, a } from "@aws-amplify/backend";
2
2
  import { assignmentAction } from "../functions/assignment-action/resource";
3
3
  import { categoryAction } from "../functions/category-action/resource";
4
+ import { editorialChecksSchemaDefinitions } from "../functions/editorial-checks/schema";
5
+ import { automationViewSchemaDefinitions } from "../functions/automation-view/schema";
6
+ import { monitoringViewSchemaDefinitions } from "../functions/monitoring-view/schema";
4
7
  import { contentActions } from "../functions/content-actions/resource";
5
8
  import { knowledgeQuery } from "../functions/knowledge-query/resource";
6
9
  import { manageUserRole } from "../functions/manage-user-role/resource";
7
10
  import { modelAttachmentUpload } from "../functions/model-attachment-upload/resource";
8
11
  import { newsroomSummary } from "../functions/newsroom-summary/resource";
12
+ import { publicForms } from "../functions/public-forms/resource";
9
13
  import { procedureAction } from "../functions/procedure-action/resource";
10
14
  import { readerSettings } from "../functions/reader-settings/resource";
15
+ import { functionHandler } from "./function-handler";
11
16
 
12
17
  const authoringOperations: ("read" | "create" | "update" | "delete")[] = [
13
18
  "read",
@@ -23,7 +28,27 @@ const modelWithoutSubscriptions = <Fields extends Parameters<typeof a.model>[0]>
23
28
  a.model(fields).disableOperations(["subscriptions"]);
24
29
  const realtimeModel = <Fields extends Parameters<typeof a.model>[0]>(fields: Fields) => a.model(fields);
25
30
 
26
- const baseDefinitions = {
31
+ /**
32
+ * Model groups a site may leave out of its data API. CloudFormation creates at most 2,500 resources in one stack
33
+ * operation, nested stacks included, and the full model set exceeds that on a fresh create. A first deploy that
34
+ * excludes a group fits; a second deploy without the exclusion adds the models as an ordinary update.
35
+ */
36
+ export const OPTIONAL_MODEL_GROUPS = {
37
+ editions: ["Edition", "EditionItem", "PublishedEdition", "PublishedEditionItem"],
38
+ } as const;
39
+
40
+ export type OptionalModelGroup = keyof typeof OPTIONAL_MODEL_GROUPS;
41
+
42
+ export function assertKnownModelGroups(groups: readonly string[]): asserts groups is readonly OptionalModelGroup[] {
43
+ for (const group of groups) {
44
+ if (!(group in OPTIONAL_MODEL_GROUPS)) {
45
+ throw new Error(`Unknown model group "${group}"; known groups: ${Object.keys(OPTIONAL_MODEL_GROUPS).join(", ")}`);
46
+ }
47
+ }
48
+ }
49
+
50
+ function buildBaseDefinitions(includeEditions: boolean) {
51
+ const definitions = {
27
52
  UserProfile: modelWithoutSubscriptions({
28
53
  id: a.id().required(),
29
54
  email: a.email(),
@@ -138,7 +163,7 @@ const baseDefinitions = {
138
163
  .query()
139
164
  .returns(a.ref("UserDirectoryResult"))
140
165
  .authorization((allow) => [allow.group(adminGroup)])
141
- .handler(a.handler.function(manageUserRole)),
166
+ .handler(functionHandler("manageUserRole", manageUserRole)),
142
167
 
143
168
  grantUserRole: a
144
169
  .mutation()
@@ -150,7 +175,7 @@ const baseDefinitions = {
150
175
  })
151
176
  .returns(a.ref("UserRoleMutationResult"))
152
177
  .authorization((allow) => [allow.group(adminGroup)])
153
- .handler(a.handler.function(manageUserRole)),
178
+ .handler(functionHandler("manageUserRole", manageUserRole)),
154
179
 
155
180
  revokeUserRole: a
156
181
  .mutation()
@@ -162,7 +187,7 @@ const baseDefinitions = {
162
187
  })
163
188
  .returns(a.ref("UserRoleMutationResult"))
164
189
  .authorization((allow) => [allow.group(adminGroup)])
165
- .handler(a.handler.function(manageUserRole)),
190
+ .handler(functionHandler("manageUserRole", manageUserRole)),
166
191
 
167
192
  mergeUserProfiles: a
168
193
  .mutation()
@@ -175,13 +200,13 @@ const baseDefinitions = {
175
200
  })
176
201
  .returns(a.ref("UserDirectoryEntry"))
177
202
  .authorization((allow) => [allow.group(adminGroup)])
178
- .handler(a.handler.function(manageUserRole)),
203
+ .handler(functionHandler("manageUserRole", manageUserRole)),
179
204
 
180
205
  getReaderSettings: a
181
206
  .query()
182
207
  .returns(a.ref("ReaderSettingsResult"))
183
208
  .authorization((allow) => [allow.authenticated()])
184
- .handler(a.handler.function(readerSettings)),
209
+ .handler(functionHandler("readerSettings", readerSettings)),
185
210
 
186
211
  updateReaderSettings: a
187
212
  .mutation()
@@ -190,7 +215,7 @@ const baseDefinitions = {
190
215
  })
191
216
  .returns(a.ref("ReaderSettingsResult"))
192
217
  .authorization((allow) => [allow.authenticated()])
193
- .handler(a.handler.function(readerSettings)),
218
+ .handler(functionHandler("readerSettings", readerSettings)),
194
219
 
195
220
  SteeringActionResult: a.customType({
196
221
  ok: a.boolean().required(),
@@ -220,7 +245,7 @@ const baseDefinitions = {
220
245
  })
221
246
  .returns(a.ref("SteeringActionResult"))
222
247
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
223
- .handler(a.handler.function(categoryAction)),
248
+ .handler(functionHandler("categoryAction", categoryAction)),
224
249
 
225
250
  CurationDelegationResult: a.customType({
226
251
  ok: a.boolean().required(),
@@ -307,7 +332,7 @@ const baseDefinitions = {
307
332
  .authorization((allow) => [
308
333
  allow.groups(categoryWriteGroups),
309
334
  ])
310
- .handler(a.handler.function(categoryAction)),
335
+ .handler(functionHandler("categoryAction", categoryAction)),
311
336
 
312
337
  createCurationDelegation: a
313
338
  .mutation()
@@ -319,7 +344,7 @@ const baseDefinitions = {
319
344
  .authorization((allow) => [
320
345
  allow.groups(categoryWriteGroups),
321
346
  ])
322
- .handler(a.handler.function(categoryAction)),
347
+ .handler(functionHandler("categoryAction", categoryAction)),
323
348
 
324
349
  revokeCurationDelegation: a
325
350
  .mutation()
@@ -330,7 +355,7 @@ const baseDefinitions = {
330
355
  .authorization((allow) => [
331
356
  allow.groups(categoryWriteGroups),
332
357
  ])
333
- .handler(a.handler.function(categoryAction)),
358
+ .handler(functionHandler("categoryAction", categoryAction)),
334
359
 
335
360
  listCurationDelegations: a
336
361
  .query()
@@ -338,7 +363,7 @@ const baseDefinitions = {
338
363
  .authorization((allow) => [
339
364
  allow.groups(categoryWriteGroups),
340
365
  ])
341
- .handler(a.handler.function(categoryAction)),
366
+ .handler(functionHandler("categoryAction", categoryAction)),
342
367
 
343
368
  requestCurationDelegation: a
344
369
  .mutation()
@@ -352,7 +377,7 @@ const baseDefinitions = {
352
377
  .authorization((allow) => [
353
378
  allow.authenticated("identityPool"),
354
379
  ])
355
- .handler(a.handler.function(categoryAction)),
380
+ .handler(functionHandler("categoryAction", categoryAction)),
356
381
 
357
382
  approveCurationDelegation: a
358
383
  .mutation()
@@ -363,7 +388,7 @@ const baseDefinitions = {
363
388
  .authorization((allow) => [
364
389
  allow.groups(categoryWriteGroups),
365
390
  ])
366
- .handler(a.handler.function(categoryAction)),
391
+ .handler(functionHandler("categoryAction", categoryAction)),
367
392
 
368
393
  denyCurationDelegation: a
369
394
  .mutation()
@@ -374,7 +399,7 @@ const baseDefinitions = {
374
399
  .authorization((allow) => [
375
400
  allow.groups(categoryWriteGroups),
376
401
  ])
377
- .handler(a.handler.function(categoryAction)),
402
+ .handler(functionHandler("categoryAction", categoryAction)),
378
403
 
379
404
  getCurationDelegationStatus: a
380
405
  .query()
@@ -385,7 +410,7 @@ const baseDefinitions = {
385
410
  .authorization((allow) => [
386
411
  allow.authenticated("identityPool"),
387
412
  ])
388
- .handler(a.handler.function(categoryAction)),
413
+ .handler(functionHandler("categoryAction", categoryAction)),
389
414
 
390
415
  recordDelegatedCuration: a
391
416
  .mutation()
@@ -407,7 +432,7 @@ const baseDefinitions = {
407
432
  .authorization((allow) => [
408
433
  allow.authenticated("identityPool"),
409
434
  ])
410
- .handler(a.handler.function(categoryAction)),
435
+ .handler(functionHandler("categoryAction", categoryAction)),
411
436
 
412
437
  setReferenceQualityRating: a
413
438
  .mutation()
@@ -422,7 +447,7 @@ const baseDefinitions = {
422
447
  .authorization((allow) => [
423
448
  allow.groups(categoryWriteGroups),
424
449
  ])
425
- .handler(a.handler.function(categoryAction)),
450
+ .handler(functionHandler("categoryAction", categoryAction)),
426
451
 
427
452
  createCategorySetDraft: a
428
453
  .mutation()
@@ -434,7 +459,7 @@ const baseDefinitions = {
434
459
  })
435
460
  .returns(a.ref("CategorySetDraftActionResult"))
436
461
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
437
- .handler(a.handler.function(categoryAction)),
462
+ .handler(functionHandler("categoryAction", categoryAction)),
438
463
 
439
464
  promoteCategorySetDraft: a
440
465
  .mutation()
@@ -445,7 +470,7 @@ const baseDefinitions = {
445
470
  })
446
471
  .returns(a.ref("CategorySetDraftActionResult"))
447
472
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
448
- .handler(a.handler.function(categoryAction)),
473
+ .handler(functionHandler("categoryAction", categoryAction)),
449
474
 
450
475
  discardCategorySetDraft: a
451
476
  .mutation()
@@ -456,7 +481,7 @@ const baseDefinitions = {
456
481
  })
457
482
  .returns(a.ref("CategorySetDraftActionResult"))
458
483
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
459
- .handler(a.handler.function(categoryAction)),
484
+ .handler(functionHandler("categoryAction", categoryAction)),
460
485
 
461
486
  createDraftCategory: a
462
487
  .mutation()
@@ -472,7 +497,7 @@ const baseDefinitions = {
472
497
  })
473
498
  .returns(a.ref("SteeringActionResult"))
474
499
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
475
- .handler(a.handler.function(categoryAction)),
500
+ .handler(functionHandler("categoryAction", categoryAction)),
476
501
 
477
502
  updateDraftCategory: a
478
503
  .mutation()
@@ -488,7 +513,7 @@ const baseDefinitions = {
488
513
  })
489
514
  .returns(a.ref("SteeringActionResult"))
490
515
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
491
- .handler(a.handler.function(categoryAction)),
516
+ .handler(functionHandler("categoryAction", categoryAction)),
492
517
 
493
518
  archiveDraftCategory: a
494
519
  .mutation()
@@ -499,7 +524,7 @@ const baseDefinitions = {
499
524
  })
500
525
  .returns(a.ref("SteeringActionResult"))
501
526
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
502
- .handler(a.handler.function(categoryAction)),
527
+ .handler(functionHandler("categoryAction", categoryAction)),
503
528
 
504
529
  reviewReferenceTopicLabel: a
505
530
  .mutation()
@@ -513,7 +538,7 @@ const baseDefinitions = {
513
538
  })
514
539
  .returns(a.ref("TopicLabelActionResult"))
515
540
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
516
- .handler(a.handler.function(categoryAction)),
541
+ .handler(functionHandler("categoryAction", categoryAction)),
517
542
 
518
543
  NewsroomSummary: a.customType({
519
544
  generatedAt: a.datetime().required(),
@@ -533,49 +558,63 @@ const baseDefinitions = {
533
558
  .query()
534
559
  .returns(a.ref("NewsroomSummary"))
535
560
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
536
- .handler(a.handler.function(newsroomSummary)),
561
+ .handler(functionHandler("newsroomSummary", newsroomSummary)),
537
562
 
538
563
  deriveMarkus: a
539
564
  .query()
540
565
  .arguments({ input: a.json().required() })
541
566
  .returns(a.json())
542
567
  .authorization((allow) => [allow.groups(contentWriteGroups)])
543
- .handler(a.handler.function(contentActions)),
568
+ .handler(functionHandler("contentActions", contentActions)),
544
569
 
545
570
  intakePendingReferences: a
546
571
  .mutation()
547
572
  .arguments({ input: a.json().required() })
548
573
  .returns(a.json())
549
574
  .authorization((allow) => [allow.groups(contentWriteGroups)])
550
- .handler(a.handler.function(contentActions)),
575
+ .handler(functionHandler("contentActions", contentActions)),
576
+
577
+ intakeEmailSubmissionReferences: a
578
+ .mutation()
579
+ .arguments({ input: a.json().required() })
580
+ .returns(a.json())
581
+ .authorization((allow) => [allow.groups(contentWriteGroups)])
582
+ .handler(functionHandler("contentActions", contentActions)),
551
583
 
552
584
  saveItemDraft: a
553
585
  .mutation()
554
586
  .arguments({ input: a.json().required() })
555
587
  .returns(a.json())
556
588
  .authorization((allow) => [allow.groups(contentWriteGroups)])
557
- .handler(a.handler.function(contentActions)),
589
+ .handler(functionHandler("contentActions", contentActions)),
558
590
 
559
591
  publishItem: a
560
592
  .mutation()
561
593
  .arguments({ input: a.json().required() })
562
594
  .returns(a.json())
563
595
  .authorization((allow) => [allow.groups(contentWriteGroups)])
564
- .handler(a.handler.function(contentActions)),
596
+ .handler(functionHandler("contentActions", contentActions)),
597
+
598
+ moveStoryStage: a
599
+ .mutation()
600
+ .arguments({ input: a.json().required() })
601
+ .returns(a.json())
602
+ .authorization((allow) => [allow.groups(contentWriteGroups)])
603
+ .handler(functionHandler("contentActions", contentActions)),
565
604
 
566
605
  unpublishItem: a
567
606
  .mutation()
568
607
  .arguments({ input: a.json().required() })
569
608
  .returns(a.json())
570
609
  .authorization((allow) => [allow.groups(contentWriteGroups)])
571
- .handler(a.handler.function(contentActions)),
610
+ .handler(functionHandler("contentActions", contentActions)),
572
611
 
573
612
  requestStagingBuild: a
574
613
  .mutation()
575
614
  .arguments({ input: a.json().required() })
576
615
  .returns(a.json())
577
616
  .authorization((allow) => [allow.groups(contentWriteGroups)])
578
- .handler(a.handler.function(contentActions)),
617
+ .handler(functionHandler("contentActions", contentActions)),
579
618
 
580
619
  knowledgeQuery: a
581
620
  .query()
@@ -584,7 +623,7 @@ const baseDefinitions = {
584
623
  })
585
624
  .returns(a.json())
586
625
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
587
- .handler(a.handler.function(knowledgeQuery)),
626
+ .handler(functionHandler("knowledgeQuery", knowledgeQuery)),
588
627
 
589
628
  updateNewsroomSummary: a
590
629
  .mutation()
@@ -595,7 +634,7 @@ const baseDefinitions = {
595
634
  })
596
635
  .returns(a.ref("NewsroomSummary"))
597
636
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
598
- .handler(a.handler.function(newsroomSummary)),
637
+ .handler(functionHandler("newsroomSummary", newsroomSummary)),
599
638
 
600
639
  ModelAttachmentUploadSlot: a.customType({
601
640
  ok: a.boolean().required(),
@@ -655,7 +694,7 @@ const baseDefinitions = {
655
694
  })
656
695
  .returns(a.ref("ModelAttachmentUploadSlot"))
657
696
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
658
- .handler(a.handler.function(modelAttachmentUpload)),
697
+ .handler(functionHandler("modelAttachmentUpload", modelAttachmentUpload)),
659
698
 
660
699
  completeModelAttachmentUpload: a
661
700
  .mutation()
@@ -677,7 +716,7 @@ const baseDefinitions = {
677
716
  })
678
717
  .returns(a.ref("ModelAttachment"))
679
718
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
680
- .handler(a.handler.function(modelAttachmentUpload)),
719
+ .handler(functionHandler("modelAttachmentUpload", modelAttachmentUpload)),
681
720
 
682
721
  abortModelAttachmentUpload: a
683
722
  .mutation()
@@ -699,7 +738,7 @@ const baseDefinitions = {
699
738
  })
700
739
  .returns(a.ref("ModelAttachmentUploadAbortResult"))
701
740
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
702
- .handler(a.handler.function(modelAttachmentUpload)),
741
+ .handler(functionHandler("modelAttachmentUpload", modelAttachmentUpload)),
703
742
 
704
743
  createModelAttachmentDownload: a
705
744
  .mutation()
@@ -708,7 +747,7 @@ const baseDefinitions = {
708
747
  })
709
748
  .returns(a.ref("ModelAttachmentDownloadSlot"))
710
749
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
711
- .handler(a.handler.function(modelAttachmentUpload)),
750
+ .handler(functionHandler("modelAttachmentUpload", modelAttachmentUpload)),
712
751
 
713
752
  AssignmentActionResult: a.customType({
714
753
  ok: a.boolean().required(),
@@ -803,7 +842,7 @@ const baseDefinitions = {
803
842
  })
804
843
  .returns(a.ref("AssignmentContext").array().required())
805
844
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
806
- .handler(a.handler.function(assignmentAction)),
845
+ .handler(functionHandler("assignmentAction", assignmentAction)),
807
846
 
808
847
  listAssignmentQueue: a
809
848
  .query()
@@ -814,7 +853,7 @@ const baseDefinitions = {
814
853
  })
815
854
  .returns(a.ref("AssignmentContext").array().required())
816
855
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
817
- .handler(a.handler.function(assignmentAction)),
856
+ .handler(functionHandler("assignmentAction", assignmentAction)),
818
857
 
819
858
  getEditorialContext: a
820
859
  .query()
@@ -824,10 +863,11 @@ const baseDefinitions = {
824
863
  storyLineageId: a.id(),
825
864
  assignmentId: a.id(),
826
865
  maxCharacters: a.integer(),
866
+ optionalPublicationDoctrine: a.boolean(),
827
867
  })
828
868
  .returns(a.json().required())
829
869
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
830
- .handler(a.handler.function(assignmentAction)),
870
+ .handler(functionHandler("assignmentAction", assignmentAction)),
831
871
 
832
872
  getAssignmentContext: a
833
873
  .query()
@@ -836,7 +876,7 @@ const baseDefinitions = {
836
876
  })
837
877
  .returns(a.ref("AssignmentContext"))
838
878
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
839
- .handler(a.handler.function(assignmentAction)),
879
+ .handler(functionHandler("assignmentAction", assignmentAction)),
840
880
 
841
881
  claimAssignment: a
842
882
  .mutation()
@@ -853,7 +893,7 @@ const baseDefinitions = {
853
893
  })
854
894
  .returns(a.ref("AssignmentActionResult"))
855
895
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
856
- .handler(a.handler.function(assignmentAction)),
896
+ .handler(functionHandler("assignmentAction", assignmentAction)),
857
897
 
858
898
  releaseAssignment: a
859
899
  .mutation()
@@ -865,7 +905,7 @@ const baseDefinitions = {
865
905
  })
866
906
  .returns(a.ref("AssignmentActionResult"))
867
907
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
868
- .handler(a.handler.function(assignmentAction)),
908
+ .handler(functionHandler("assignmentAction", assignmentAction)),
869
909
 
870
910
  completeAssignment: a
871
911
  .mutation()
@@ -877,7 +917,7 @@ const baseDefinitions = {
877
917
  })
878
918
  .returns(a.ref("AssignmentActionResult"))
879
919
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
880
- .handler(a.handler.function(assignmentAction)),
920
+ .handler(functionHandler("assignmentAction", assignmentAction)),
881
921
 
882
922
  cancelAssignment: a
883
923
  .mutation()
@@ -889,7 +929,7 @@ const baseDefinitions = {
889
929
  })
890
930
  .returns(a.ref("AssignmentActionResult"))
891
931
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
892
- .handler(a.handler.function(assignmentAction)),
932
+ .handler(functionHandler("assignmentAction", assignmentAction)),
893
933
 
894
934
  reopenAssignment: a
895
935
  .mutation()
@@ -901,7 +941,7 @@ const baseDefinitions = {
901
941
  })
902
942
  .returns(a.ref("AssignmentActionResult"))
903
943
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
904
- .handler(a.handler.function(assignmentAction)),
944
+ .handler(functionHandler("assignmentAction", assignmentAction)),
905
945
 
906
946
  retryImmediateAssignment: a
907
947
  .mutation()
@@ -913,13 +953,13 @@ const baseDefinitions = {
913
953
  })
914
954
  .returns(a.ref("AssignmentActionResult"))
915
955
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
916
- .handler(a.handler.function(assignmentAction)),
956
+ .handler(functionHandler("assignmentAction", assignmentAction)),
917
957
 
918
958
  listNewsroomProcedureDefinitions: a
919
959
  .query()
920
960
  .returns(a.json())
921
961
  .authorization((allow) => [allow.group(adminGroup)])
922
- .handler(a.handler.function(procedureAction)),
962
+ .handler(functionHandler("procedureAction", procedureAction)),
923
963
 
924
964
  getNewsroomProcedureDefinition: a
925
965
  .query()
@@ -929,7 +969,7 @@ const baseDefinitions = {
929
969
  })
930
970
  .returns(a.json())
931
971
  .authorization((allow) => [allow.group(adminGroup)])
932
- .handler(a.handler.function(procedureAction)),
972
+ .handler(functionHandler("procedureAction", procedureAction)),
933
973
 
934
974
  saveNewsroomProcedureDefinition: a
935
975
  .mutation()
@@ -938,7 +978,7 @@ const baseDefinitions = {
938
978
  })
939
979
  .returns(a.json())
940
980
  .authorization((allow) => [allow.group(adminGroup)])
941
- .handler(a.handler.function(procedureAction)),
981
+ .handler(functionHandler("procedureAction", procedureAction)),
942
982
 
943
983
  saveNewsroomProcedureVersionDraft: a
944
984
  .mutation()
@@ -947,7 +987,7 @@ const baseDefinitions = {
947
987
  })
948
988
  .returns(a.json())
949
989
  .authorization((allow) => [allow.group(adminGroup)])
950
- .handler(a.handler.function(procedureAction)),
990
+ .handler(functionHandler("procedureAction", procedureAction)),
951
991
 
952
992
  publishNewsroomProcedureVersion: a
953
993
  .mutation()
@@ -956,7 +996,7 @@ const baseDefinitions = {
956
996
  })
957
997
  .returns(a.json())
958
998
  .authorization((allow) => [allow.group(adminGroup)])
959
- .handler(a.handler.function(procedureAction)),
999
+ .handler(functionHandler("procedureAction", procedureAction)),
960
1000
 
961
1001
  startNewsroomProcedureRun: a
962
1002
  .mutation()
@@ -971,7 +1011,7 @@ const baseDefinitions = {
971
1011
  })
972
1012
  .returns(a.json())
973
1013
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
974
- .handler(a.handler.function(procedureAction)),
1014
+ .handler(functionHandler("procedureAction", procedureAction)),
975
1015
 
976
1016
  getNewsroomProcedureRun: a
977
1017
  .query()
@@ -980,7 +1020,7 @@ const baseDefinitions = {
980
1020
  })
981
1021
  .returns(a.json())
982
1022
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
983
- .handler(a.handler.function(procedureAction)),
1023
+ .handler(functionHandler("procedureAction", procedureAction)),
984
1024
 
985
1025
  listNewsroomProcedureRunsByProcedure: a
986
1026
  .query()
@@ -989,7 +1029,7 @@ const baseDefinitions = {
989
1029
  })
990
1030
  .returns(a.json())
991
1031
  .authorization((allow) => [allow.groups(categoryWriteGroups)])
992
- .handler(a.handler.function(procedureAction)),
1032
+ .handler(functionHandler("procedureAction", procedureAction)),
993
1033
 
994
1034
  Assignment: modelWithoutSubscriptions({
995
1035
  id: a.id().required(),
@@ -1133,6 +1173,8 @@ const baseDefinitions = {
1133
1173
  error: a.json(),
1134
1174
  attempt: a.integer(),
1135
1175
  newsroomFeedKey: a.string(),
1176
+ usage: a.json(),
1177
+ costUsd: a.float(),
1136
1178
  })
1137
1179
  .secondaryIndexes((index) => [
1138
1180
  index("procedureId").sortKeys(["requestedAt"]).queryField("listProcedureRunsByProcedureAndRequestedAt"),
@@ -1145,6 +1187,74 @@ const baseDefinitions = {
1145
1187
  allow.group(adminGroup).to(authoringOperations),
1146
1188
  ]),
1147
1189
 
1190
+ /** A named "when X happens, run procedure Y" (PPY-9ff95b). Step 1 mirrors the monitoring router's procedureTriggers and starts nothing itself. */
1191
+ Automation: modelWithoutSubscriptions({
1192
+ id: a.id().required(),
1193
+ title: a.string().required(),
1194
+ description: a.string(),
1195
+ enabled: a.boolean().required(),
1196
+ trigger: a.json().required(),
1197
+ procedureKey: a.string().required(),
1198
+ inputTemplate: a.json(),
1199
+ limits: a.json(),
1200
+ createdBy: a.string(),
1201
+ createdAt: a.datetime().required(),
1202
+ updatedBy: a.string(),
1203
+ updatedAt: a.datetime().required(),
1204
+ })
1205
+ .authorization((allow) => [
1206
+ allow.group("editor").to(["read", "create", "update"]),
1207
+ allow.group(adminGroup).to(authoringOperations),
1208
+ ]),
1209
+
1210
+ /** A human review point raised by a step on any item (PPY-ab6a50). Review is not a story state. */
1211
+ ReviewRequest: modelWithoutSubscriptions({
1212
+ id: a.id().required(),
1213
+ subject: a.json().required(),
1214
+ mode: a.string().required(),
1215
+ reason: a.string().required(),
1216
+ ask: a.string(),
1217
+ raisedBy: a.string().required(),
1218
+ raisedAt: a.datetime().required(),
1219
+ deadline: a.datetime(),
1220
+ state: a.string().required(),
1221
+ answer: a.json(),
1222
+ links: a.json(),
1223
+ history: a.json(),
1224
+ appliedIn: a.json(),
1225
+ updatedAt: a.datetime().required(),
1226
+ })
1227
+ .authorization((allow) => [
1228
+ allow.group("editor").to(["read", "create", "update"]),
1229
+ allow.group(adminGroup).to(authoringOperations),
1230
+ ]),
1231
+
1232
+ /** One user's read mark on one review request; id is "<userKey>#<requestId>". */
1233
+ ReviewReadMark: modelWithoutSubscriptions({
1234
+ id: a.id().required(),
1235
+ requestId: a.string().required(),
1236
+ userKey: a.string().required(),
1237
+ readAt: a.datetime().required(),
1238
+ })
1239
+ .authorization((allow) => [
1240
+ allow.groups(["editor", adminGroup]).to(["read", "create", "update"]),
1241
+ ]),
1242
+
1243
+ /** Who is told about review requests: scope default, mode or automation. Changed by data, not code. */
1244
+ ReviewRecipientRule: modelWithoutSubscriptions({
1245
+ id: a.id().required(),
1246
+ scope: a.string().required(),
1247
+ key: a.string(),
1248
+ roles: a.string().array(),
1249
+ emails: a.string().array(),
1250
+ updatedBy: a.string(),
1251
+ updatedAt: a.datetime().required(),
1252
+ })
1253
+ .authorization((allow) => [
1254
+ allow.group("editor").to(["read"]),
1255
+ allow.group(adminGroup).to(authoringOperations),
1256
+ ]),
1257
+
1148
1258
  NewsroomSection: modelWithoutSubscriptions({
1149
1259
  id: a.id().required(),
1150
1260
  tenantId: a.string(),
@@ -1789,7 +1899,7 @@ const baseDefinitions = {
1789
1899
  updatedAt: a.datetime(),
1790
1900
  tags: a.hasMany("ItemTag", "itemId"),
1791
1901
  mediaAssets: a.hasMany("MediaAsset", "itemId"),
1792
- editionItems: a.hasMany("EditionItem", "itemId"),
1902
+ ...(includeEditions ? { editionItems: a.hasMany("EditionItem", "itemId") } : {}),
1793
1903
  })
1794
1904
  .secondaryIndexes((index) => [
1795
1905
  index("lineageId").sortKeys(["versionNumber"]).queryField("listItemsByLineageAndVersion"),
@@ -2112,21 +2222,26 @@ const baseDefinitions = {
2112
2222
  allow.guest().to(["read"]),
2113
2223
  allow.groups(contentWriteGroups),
2114
2224
  ]),
2225
+ ...editorialChecksSchemaDefinitions,
2226
+ ...monitoringViewSchemaDefinitions,
2227
+ ...automationViewSchemaDefinitions,
2115
2228
  };
2229
+ if (!includeEditions) {
2230
+ const mutableDefinitions = definitions as Record<string, unknown>;
2231
+ for (const modelName of OPTIONAL_MODEL_GROUPS.editions) delete mutableDefinitions[modelName];
2232
+ }
2233
+ return definitions;
2234
+ }
2116
2235
 
2117
- export const schema = a.schema(baseDefinitions).authorization((allow) => [
2118
- allow.resource(assignmentAction).to(["query", "mutate"]),
2119
- allow.resource(categoryAction),
2120
- allow.resource(contentActions),
2121
- allow.resource(manageUserRole),
2122
- allow.resource(modelAttachmentUpload),
2123
- allow.resource(newsroomSummary),
2124
- allow.resource(procedureAction),
2125
- ]);
2236
+ export function baseModelNames(excludedGroups: readonly OptionalModelGroup[] = []): string[] {
2237
+ return Object.keys(buildBaseDefinitions(!excludedGroups.includes("editions")));
2238
+ }
2126
2239
 
2127
- /** The deployed schema of a site that enables plugins: the base definitions plus what each plugin contributes (docs/plugins.md). */
2128
- export function schemaWithPlugins(pluginDefinitions: Record<string, any>) {
2129
- return a.schema({ ...baseDefinitions, ...pluginDefinitions } as typeof baseDefinitions).authorization((allow) => [
2240
+ const baseDefinitions = buildBaseDefinitions(true);
2241
+
2242
+ /** Schema-level function grants shared by every deployed schema, with or without plugins, so none can drop one. */
2243
+ function functionGrants(allow: any) {
2244
+ return [
2130
2245
  allow.resource(assignmentAction).to(["query", "mutate"]),
2131
2246
  allow.resource(categoryAction),
2132
2247
  allow.resource(contentActions),
@@ -2134,7 +2249,21 @@ export function schemaWithPlugins(pluginDefinitions: Record<string, any>) {
2134
2249
  allow.resource(modelAttachmentUpload),
2135
2250
  allow.resource(newsroomSummary),
2136
2251
  allow.resource(procedureAction),
2137
- ]);
2252
+ allow.resource(publicForms).to(["mutate"]),
2253
+ ];
2254
+ }
2255
+
2256
+ export const schema = a.schema(baseDefinitions).authorization(functionGrants);
2257
+
2258
+ /** The deployed schema of a site that enables plugins: the base definitions plus what each plugin contributes (docs/plugins.md). */
2259
+ export function schemaWithPlugins(pluginDefinitions: Record<string, any>) {
2260
+ return a.schema({ ...baseDefinitions, ...pluginDefinitions } as typeof baseDefinitions).authorization(functionGrants);
2261
+ }
2262
+
2263
+ /** The schema of a site that leaves optional model groups out of its first deploy. */
2264
+ export function schemaExcludingGroups(excludedGroups: readonly OptionalModelGroup[], pluginDefinitions: Record<string, any> = {}) {
2265
+ const definitions = buildBaseDefinitions(!excludedGroups.includes("editions")) as unknown as typeof baseDefinitions;
2266
+ return a.schema({ ...definitions, ...pluginDefinitions } as typeof baseDefinitions).authorization(functionGrants);
2138
2267
  }
2139
2268
 
2140
2269
  export type Schema = ClientSchema<typeof schema>;