@growth-labs/cms 0.5.29 → 0.5.31

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 (151) hide show
  1. package/dist/engine/foundry-dispatch.d.ts +7 -2
  2. package/dist/engine/foundry-dispatch.d.ts.map +1 -1
  3. package/dist/engine/foundry-dispatch.js +24 -6
  4. package/dist/engine/foundry-dispatch.js.map +1 -1
  5. package/dist/engine/fronts-publish-intent.d.ts +174 -0
  6. package/dist/engine/fronts-publish-intent.d.ts.map +1 -0
  7. package/dist/engine/fronts-publish-intent.js +352 -0
  8. package/dist/engine/fronts-publish-intent.js.map +1 -0
  9. package/dist/engine/fronts-publish.d.ts +80 -0
  10. package/dist/engine/fronts-publish.d.ts.map +1 -0
  11. package/dist/engine/fronts-publish.js +286 -0
  12. package/dist/engine/fronts-publish.js.map +1 -0
  13. package/dist/engine/index.d.ts +7 -0
  14. package/dist/engine/index.d.ts.map +1 -1
  15. package/dist/engine/index.js +10 -0
  16. package/dist/engine/index.js.map +1 -1
  17. package/dist/engine/publication-continuity.d.ts +77 -0
  18. package/dist/engine/publication-continuity.d.ts.map +1 -0
  19. package/dist/engine/publication-continuity.js +94 -0
  20. package/dist/engine/publication-continuity.js.map +1 -0
  21. package/dist/engine/publication-receipt/canonical-json.d.ts +34 -0
  22. package/dist/engine/publication-receipt/canonical-json.d.ts.map +1 -0
  23. package/dist/engine/publication-receipt/canonical-json.js +100 -0
  24. package/dist/engine/publication-receipt/canonical-json.js.map +1 -0
  25. package/dist/engine/publication-receipt/receipt.d.ts +316 -0
  26. package/dist/engine/publication-receipt/receipt.d.ts.map +1 -0
  27. package/dist/engine/publication-receipt/receipt.js +131 -0
  28. package/dist/engine/publication-receipt/receipt.js.map +1 -0
  29. package/dist/engine/publication-receipt/signature.d.ts +36 -0
  30. package/dist/engine/publication-receipt/signature.d.ts.map +1 -0
  31. package/dist/engine/publication-receipt/signature.js +79 -0
  32. package/dist/engine/publication-receipt/signature.js.map +1 -0
  33. package/dist/engine/publication.d.ts.map +1 -1
  34. package/dist/engine/publication.js +2 -0
  35. package/dist/engine/publication.js.map +1 -1
  36. package/dist/engine/publisher.d.ts.map +1 -1
  37. package/dist/engine/publisher.js +23 -1
  38. package/dist/engine/publisher.js.map +1 -1
  39. package/dist/engine/source-sections.d.ts +20 -0
  40. package/dist/engine/source-sections.d.ts.map +1 -0
  41. package/dist/engine/source-sections.js +130 -0
  42. package/dist/engine/source-sections.js.map +1 -0
  43. package/dist/engine/validator/checks/source-section-diagnostics.d.ts +7 -0
  44. package/dist/engine/validator/checks/source-section-diagnostics.d.ts.map +1 -0
  45. package/dist/engine/validator/checks/source-section-diagnostics.js +120 -0
  46. package/dist/engine/validator/checks/source-section-diagnostics.js.map +1 -0
  47. package/dist/engine/validator/index.d.ts +10 -0
  48. package/dist/engine/validator/index.d.ts.map +1 -1
  49. package/dist/engine/validator/index.js +4 -0
  50. package/dist/engine/validator/index.js.map +1 -1
  51. package/dist/engine/validator/types.d.ts +1 -1
  52. package/dist/engine/validator/types.d.ts.map +1 -1
  53. package/dist/engine/validator/types.js +10 -0
  54. package/dist/engine/validator/types.js.map +1 -1
  55. package/dist/integration/index.d.ts.map +1 -1
  56. package/dist/integration/index.js +10 -0
  57. package/dist/integration/index.js.map +1 -1
  58. package/dist/providers/types.d.ts +1 -0
  59. package/dist/providers/types.d.ts.map +1 -1
  60. package/dist/routes/content.d.ts.map +1 -1
  61. package/dist/routes/content.js +22 -2
  62. package/dist/routes/content.js.map +1 -1
  63. package/dist/routes/fronts-publish.d.ts +10 -0
  64. package/dist/routes/fronts-publish.d.ts.map +1 -0
  65. package/dist/routes/fronts-publish.js +250 -0
  66. package/dist/routes/fronts-publish.js.map +1 -0
  67. package/dist/routes/index.d.ts +3 -0
  68. package/dist/routes/index.d.ts.map +1 -1
  69. package/dist/routes/index.js +4 -0
  70. package/dist/routes/index.js.map +1 -1
  71. package/dist/routes/surveys.d.ts.map +1 -1
  72. package/dist/routes/surveys.js +13 -1
  73. package/dist/routes/surveys.js.map +1 -1
  74. package/dist/schema/insights-ingest.d.ts +28 -28
  75. package/dist/schema/layout.d.ts +2 -2
  76. package/dist/schema/migrations.d.ts.map +1 -1
  77. package/dist/schema/migrations.js +36 -0
  78. package/dist/schema/migrations.js.map +1 -1
  79. package/dist/schema/portable-text.d.ts +2 -2
  80. package/dist/schema/tables.d.ts +1 -1
  81. package/dist/schema/tables.d.ts.map +1 -1
  82. package/dist/schema/tables.js +2 -0
  83. package/dist/schema/tables.js.map +1 -1
  84. package/dist/schema/types.d.ts +59 -0
  85. package/dist/schema/types.d.ts.map +1 -1
  86. package/dist/schema/types.js.map +1 -1
  87. package/dist/surveys/access.d.ts +2 -0
  88. package/dist/surveys/access.d.ts.map +1 -1
  89. package/dist/surveys/access.js +7 -0
  90. package/dist/surveys/access.js.map +1 -1
  91. package/dist/surveys/errors.d.ts +8 -0
  92. package/dist/surveys/errors.d.ts.map +1 -1
  93. package/dist/surveys/errors.js +12 -0
  94. package/dist/surveys/errors.js.map +1 -1
  95. package/dist/surveys/schema.d.ts +129 -54
  96. package/dist/surveys/schema.d.ts.map +1 -1
  97. package/dist/surveys/schema.js +45 -0
  98. package/dist/surveys/schema.js.map +1 -1
  99. package/dist/ui/api/fronts/publish-callback.d.ts +3 -0
  100. package/dist/ui/api/fronts/publish-callback.d.ts.map +1 -0
  101. package/dist/ui/api/fronts/publish-callback.js +8 -0
  102. package/dist/ui/api/fronts/publish-callback.js.map +1 -0
  103. package/dist/ui/api/fronts/schedule.d.ts +3 -0
  104. package/dist/ui/api/fronts/schedule.d.ts.map +1 -0
  105. package/dist/ui/api/fronts/schedule.js +8 -0
  106. package/dist/ui/api/fronts/schedule.js.map +1 -0
  107. package/dist/ui/editor/ContentForm.d.ts.map +1 -1
  108. package/dist/ui/editor/ContentForm.js +52 -1
  109. package/dist/ui/editor/ContentForm.js.map +1 -1
  110. package/dist/ui/editor/body-warnings.d.ts +27 -0
  111. package/dist/ui/editor/body-warnings.d.ts.map +1 -0
  112. package/dist/ui/editor/body-warnings.js +58 -0
  113. package/dist/ui/editor/body-warnings.js.map +1 -0
  114. package/dist/ui/screens/SurveyResponsesScreen.d.ts +7 -1
  115. package/dist/ui/screens/SurveyResponsesScreen.d.ts.map +1 -1
  116. package/dist/ui/screens/SurveyResponsesScreen.js +141 -32
  117. package/dist/ui/screens/SurveyResponsesScreen.js.map +1 -1
  118. package/migrations/0026_fronts_publish.sql +31 -0
  119. package/migrations/0027_fronts_publish_intent.sql +44 -0
  120. package/package.json +1 -1
  121. package/src/engine/foundry-dispatch.ts +22 -6
  122. package/src/engine/fronts-publish-intent.ts +676 -0
  123. package/src/engine/fronts-publish.ts +417 -0
  124. package/src/engine/index.ts +60 -0
  125. package/src/engine/publication-continuity.ts +140 -0
  126. package/src/engine/publication-receipt/canonical-json.ts +123 -0
  127. package/src/engine/publication-receipt/receipt.ts +160 -0
  128. package/src/engine/publication-receipt/signature.ts +112 -0
  129. package/src/engine/publication.ts +2 -0
  130. package/src/engine/publisher.ts +23 -1
  131. package/src/engine/source-sections.ts +153 -0
  132. package/src/engine/validator/checks/source-section-diagnostics.ts +187 -0
  133. package/src/engine/validator/index.ts +12 -0
  134. package/src/engine/validator/types.ts +19 -0
  135. package/src/integration/index.ts +10 -0
  136. package/src/providers/types.ts +1 -1
  137. package/src/routes/content.ts +23 -2
  138. package/src/routes/fronts-publish.ts +292 -0
  139. package/src/routes/index.ts +8 -0
  140. package/src/routes/surveys.ts +12 -1
  141. package/src/schema/migrations.ts +36 -0
  142. package/src/schema/tables.ts +2 -0
  143. package/src/schema/types.ts +61 -0
  144. package/src/surveys/access.ts +9 -0
  145. package/src/surveys/errors.ts +12 -0
  146. package/src/surveys/schema.ts +47 -0
  147. package/src/ui/api/fronts/publish-callback.ts +18 -0
  148. package/src/ui/api/fronts/schedule.ts +18 -0
  149. package/src/ui/editor/ContentForm.tsx +93 -0
  150. package/src/ui/editor/body-warnings.ts +78 -0
  151. package/src/ui/screens/SurveyResponsesScreen.tsx +369 -121
@@ -49,8 +49,13 @@ export declare function dispatchToFoundry(db: D1Database, contentId: string, hoo
49
49
  export declare function applyFoundryCallback(db: D1Database, input: CallbackInput): Promise<boolean>;
50
50
  /**
51
51
  * Verify a HMAC-SHA256 signature over `rawBody` using `secret`.
52
- * The `signature` is expected as a lowercase hex string.
53
- * Uses Web Crypto `crypto.subtle` and a constant-time comparison.
52
+ *
53
+ * The `signature` is a hex string, with or without a `sha256=` scheme prefix.
54
+ * Foundry (and the GitHub-webhook convention it mirrors) sends
55
+ * `X-Foundry-Signature: sha256=<hex>`; the prefix MUST be stripped before the
56
+ * hex parse or every prefixed signature fails to decode and the callback 401s.
57
+ * Comparison is case-insensitive on the hex and constant-time on the bytes.
58
+ * Uses Web Crypto `crypto.subtle`.
54
59
  */
55
60
  export declare function verifyHmac(secret: string, rawBody: string, signature: string): Promise<boolean>;
56
61
  //# sourceMappingURL=foundry-dispatch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"foundry-dispatch.d.ts","sourceRoot":"","sources":["../../src/engine/foundry-dispatch.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzC,MAAM,MAAM,eAAe,GACxB,SAAS,GACT,QAAQ,GACR,UAAU,GACV,cAAc,GACd,aAAa,GACb,OAAO,GACP,QAAQ,CAAA;AAIX,MAAM,WAAW,cAAc;IAC9B,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;CACpB;AAED,qBAAa,gCAAiC,SAAQ,KAAK;IAC1D,QAAQ,CAAC,IAAI,EAAG,kBAAkB,CAAS;gBAE/B,SAAS,EAAE,MAAM;CAI7B;AAED,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,eAAe,CAAA;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB;AAwCD;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACtC,EAAE,EAAE,UAAU,EACd,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,IAAI,CAAC,eAAe,EAAE,eAAe,CAAC,GAC1C,OAAO,CAAC,cAAc,CAAC,CAkIzB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,oBAAoB,CAAC,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAoJjG;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAC/B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAmClB"}
1
+ {"version":3,"file":"foundry-dispatch.d.ts","sourceRoot":"","sources":["../../src/engine/foundry-dispatch.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzC,MAAM,MAAM,eAAe,GACxB,SAAS,GACT,QAAQ,GACR,UAAU,GACV,cAAc,GACd,aAAa,GACb,OAAO,GACP,QAAQ,CAAA;AAIX,MAAM,WAAW,cAAc;IAC9B,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;CACpB;AAED,qBAAa,gCAAiC,SAAQ,KAAK;IAC1D,QAAQ,CAAC,IAAI,EAAG,kBAAkB,CAAS;gBAE/B,SAAS,EAAE,MAAM;CAI7B;AAED,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,eAAe,CAAA;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB;AAwCD;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACtC,EAAE,EAAE,UAAU,EACd,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,IAAI,CAAC,eAAe,EAAE,eAAe,CAAC,GAC1C,OAAO,CAAC,cAAc,CAAC,CAkIzB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,oBAAoB,CAAC,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAoJjG;AAED;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAC/B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CA8ClB"}
@@ -269,20 +269,38 @@ export async function applyFoundryCallback(db, input) {
269
269
  }
270
270
  /**
271
271
  * Verify a HMAC-SHA256 signature over `rawBody` using `secret`.
272
- * The `signature` is expected as a lowercase hex string.
273
- * Uses Web Crypto `crypto.subtle` and a constant-time comparison.
272
+ *
273
+ * The `signature` is a hex string, with or without a `sha256=` scheme prefix.
274
+ * Foundry (and the GitHub-webhook convention it mirrors) sends
275
+ * `X-Foundry-Signature: sha256=<hex>`; the prefix MUST be stripped before the
276
+ * hex parse or every prefixed signature fails to decode and the callback 401s.
277
+ * Comparison is case-insensitive on the hex and constant-time on the bytes.
278
+ * Uses Web Crypto `crypto.subtle`.
274
279
  */
275
280
  export async function verifyHmac(secret, rawBody, signature) {
276
281
  if (!signature)
277
282
  return false;
283
+ // Strip the optional `sha256=` scheme prefix (case-insensitive) and any
284
+ // surrounding whitespace before decoding the hex digest.
285
+ let hex = signature.trim();
286
+ if (/^sha256=/i.test(hex))
287
+ hex = hex.slice('sha256='.length).trim();
288
+ if (!hex)
289
+ return false;
290
+ // Reject non-hex up front: `Number.parseInt('1z', 16)` returns 1 (parses the
291
+ // leading nibble and stops), so malformed hex would otherwise decode to a
292
+ // truncated-but-valid byte string. The constant-time compare still guards
293
+ // forgery, but a strict charset check keeps the decode honest.
294
+ if (!/^[0-9a-f]+$/i.test(hex))
295
+ return false;
278
296
  let sigBytes;
279
297
  try {
280
298
  // Parse the hex signature
281
- if (signature.length % 2 !== 0)
299
+ if (hex.length % 2 !== 0)
282
300
  return false;
283
- sigBytes = new Uint8Array(signature.length / 2);
284
- for (let i = 0; i < signature.length; i += 2) {
285
- const byte = Number.parseInt(signature.slice(i, i + 2), 16);
301
+ sigBytes = new Uint8Array(hex.length / 2);
302
+ for (let i = 0; i < hex.length; i += 2) {
303
+ const byte = Number.parseInt(hex.slice(i, i + 2), 16);
286
304
  if (Number.isNaN(byte))
287
305
  return false;
288
306
  sigBytes[i / 2] = byte;
@@ -1 +1 @@
1
- {"version":3,"file":"foundry-dispatch.js","sourceRoot":"","sources":["../../src/engine/foundry-dispatch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"foundry-dispatch.js","sourceRoot":"","sources":["../../src/engine/foundry-dispatch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,174 @@
1
+ import type { ContentType, FrontsPublishStatus } from '../schema/types.js';
2
+ import type { D1Database } from './d1.js';
3
+ /** The CMS's own name for the record this intent describes. */
4
+ export declare const FRONTS_PUBLISH_INTENT_SOURCE = "masthead";
5
+ /**
6
+ * `masthead:<content_id>:<version>`. Stable for as long as the canonical
7
+ * content is, and different the moment `canonical_version` advances, so a
8
+ * meaningful revision can never silently reuse a conflicting identity.
9
+ */
10
+ export declare function frontsPublishIntentId(contentId: string, version: number): string;
11
+ /** Every way a due item can fail to form a complete, honest intent. */
12
+ export type FrontsPublishIntentErrorCode = 'missing_publish_at' | 'invalid_canonical_version' | 'missing_channel_targets' | 'invalid_channel_targets' | 'missing_trigger_token' | 'missing_article_body' | 'video_media_not_ready' | 'missing_podcast_audio';
13
+ /**
14
+ * A due item whose canonical data cannot produce a complete intent. Thrown by
15
+ * the single-item hydration; collected (never swallowed) by the batch one.
16
+ */
17
+ export declare class FrontsPublishIntentError extends Error {
18
+ readonly code: FrontsPublishIntentErrorCode;
19
+ readonly contentId: string;
20
+ constructor(code: FrontsPublishIntentErrorCode, contentId: string, detail?: string);
21
+ }
22
+ export interface FrontsPublishArticleRef {
23
+ /** Absolute when a site base/canonical URL is known, else a site-relative path. */
24
+ url: string;
25
+ slug: string;
26
+ word_count: number | null;
27
+ }
28
+ export interface FrontsPublishImageRef {
29
+ id: string;
30
+ role: 'hero' | 'social' | 'thumbnail';
31
+ url: string;
32
+ width: number | null;
33
+ height: number | null;
34
+ }
35
+ export interface FrontsPublishVideoHlsRef {
36
+ kind: 'hls-manifest';
37
+ url: string;
38
+ poster_url: string | null;
39
+ duration_seconds: number | null;
40
+ }
41
+ export interface FrontsPublishPodcastAudioRef {
42
+ /**
43
+ * `'r2-object'` — a progressive-download R2 audio object
44
+ * (podcast_content.audio_r2_key), which is what the CMS actually stores.
45
+ * `'hls-manifest'` is reserved for when an audio HLS pipeline exists. Always
46
+ * check this before treating `url` as a manifest.
47
+ */
48
+ kind: 'r2-object' | 'hls-manifest';
49
+ url: string;
50
+ duration_seconds: number | null;
51
+ }
52
+ export interface FrontsPublishLinkRef {
53
+ rel: 'canonical' | 'public';
54
+ url: string;
55
+ }
56
+ export interface FrontsPublishContentRefs {
57
+ article: FrontsPublishArticleRef | null;
58
+ /** `[]` is valid — the item simply has no hero/social/thumbnail asset. */
59
+ images: FrontsPublishImageRef[];
60
+ video_hls: FrontsPublishVideoHlsRef | null;
61
+ podcast_audio_hls: FrontsPublishPodcastAudioRef | null;
62
+ /** `[]` is valid. */
63
+ links: FrontsPublishLinkRef[];
64
+ }
65
+ /**
66
+ * One due/overdue item's complete canonical publish intent. Contains NO secret:
67
+ * `trigger_token_hash` is a digest and the raw token never leaves D1.
68
+ */
69
+ export interface FrontsPublishIntent {
70
+ intent_id: string;
71
+ content_id: string;
72
+ version: number;
73
+ type: ContentType;
74
+ slug: string;
75
+ title: string;
76
+ /** The single editorial Fronts channel slug — NOT a publish target. */
77
+ channel: string | null;
78
+ channel_targets: string[];
79
+ /** Unix epoch SECONDS. Equals content_items.publish_at. */
80
+ deadline: number;
81
+ /**
82
+ * Unix epoch SECONDS. Equals `deadline` in this increment: the CMS declares no
83
+ * pre-roll lead of its own rather than inventing one. A host-configurable lead
84
+ * is deliberately deferred — see packages-docs/cms.md.
85
+ */
86
+ trigger_at: number;
87
+ content_refs: FrontsPublishContentRefs;
88
+ /** `sha256:<64 lowercase hex>`. Never the raw token. */
89
+ trigger_token_hash: string;
90
+ /** `deadline <= now` — the consumer is late on this one. */
91
+ overdue: boolean;
92
+ fronts_publish_status: FrontsPublishStatus | null;
93
+ }
94
+ /** The `sha256:` prefix every exposed CMS trigger-token digest carries. */
95
+ export declare const FRONTS_PUBLISH_TRIGGER_TOKEN_HASH_PREFIX = "sha256:";
96
+ /**
97
+ * The exposed form of a trigger token: `sha256:<hex>`. Same convention
98
+ * `engine/foundry-dispatch.ts` already uses for `processingTriggerTokenHash`.
99
+ */
100
+ export declare function frontsPublishTriggerTokenHash(rawToken: string): Promise<string>;
101
+ /**
102
+ * Mint this item's raw publish trigger token if — and only if — it has none.
103
+ *
104
+ * Idempotent by compare-and-set, so concurrent callers cannot produce two tokens
105
+ * and a re-arm never rotates a token out from under an in-flight intent: an
106
+ * armed item keeps one stable `trigger_token_hash` across every later read.
107
+ * Called from `scheduleContent`, the single seam an item passes through to
108
+ * become `scheduled` with a `publish_at`. The raw value is never returned.
109
+ */
110
+ export declare function armFrontsPublishTriggerToken(db: D1Database, contentId: string): Promise<{
111
+ minted: boolean;
112
+ }>;
113
+ /**
114
+ * Parse `content_items.fronts_channel_targets`. Returns the target list, or a
115
+ * loud error code — NEVER a silent `[]`, which would read as a complete intent
116
+ * with nothing to publish to.
117
+ */
118
+ type ChannelTargetsFailure = {
119
+ code: 'missing_channel_targets' | 'invalid_channel_targets';
120
+ };
121
+ export declare function parseFrontsChannelTargets(raw: string | null | undefined): {
122
+ targets: string[];
123
+ } | ChannelTargetsFailure;
124
+ /**
125
+ * Arm an item's publish targets. Rejects an empty or non-string list loudly
126
+ * instead of storing something that would later hydrate as "complete".
127
+ */
128
+ export declare function setFrontsChannelTargets(db: D1Database, contentId: string, targets: readonly string[]): Promise<void>;
129
+ export interface HydrateFrontsPublishIntentsOptions {
130
+ /** Window upper bound, unix epoch seconds. Items due at/before this are considered. */
131
+ toSeconds: number;
132
+ /** Reference time for `overdue`, unix epoch seconds. Defaults to Date.now(). */
133
+ nowSeconds?: number;
134
+ /** Hard cap on candidates. Default 200, max 1000 — mirrors the schedule poll. */
135
+ limit?: number;
136
+ /**
137
+ * Resolve an item's public URL (absolute when the host knows its origin, else
138
+ * a site-relative path). Injected rather than imported so this engine module
139
+ * does not depend on the UI layer.
140
+ */
141
+ resolveContentUrl: (row: {
142
+ type: ContentType;
143
+ slug: string;
144
+ }) => string;
145
+ /** Public media host used for media rows with no stored URL. Default 'media.fronts.co'. */
146
+ mediaPublicDomain?: string;
147
+ }
148
+ /** One due item that could NOT form a complete intent, with the exact reason. */
149
+ export interface FrontsPublishIntentFailure {
150
+ content_id: string;
151
+ code: FrontsPublishIntentErrorCode;
152
+ message: string;
153
+ }
154
+ export interface HydrateFrontsPublishIntentsResult {
155
+ intents: FrontsPublishIntent[];
156
+ /**
157
+ * Due items that are NOT publishable — never empty-defaulted into `intents`
158
+ * and never silently dropped. One bad item does not suppress the others, so a
159
+ * single broken record cannot block the whole publish lane.
160
+ */
161
+ incomplete: FrontsPublishIntentFailure[];
162
+ }
163
+ /**
164
+ * Hydrate every due/overdue `scheduled` item into a complete canonical intent.
165
+ *
166
+ * Uses the SAME due predicate as `listFrontsPublishSchedule` — overdue items
167
+ * included (the window's lower bound is deliberately not applied), future items
168
+ * excluded — so existing window behaviour is preserved exactly. READ ONLY: it
169
+ * never mutates content, advances `canonical_version`, or mints a token, so
170
+ * polling cannot churn an intent identity.
171
+ */
172
+ export declare function hydrateFrontsPublishIntents(db: D1Database, options: HydrateFrontsPublishIntentsOptions): Promise<HydrateFrontsPublishIntentsResult>;
173
+ export {};
174
+ //# sourceMappingURL=fronts-publish-intent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fronts-publish-intent.d.ts","sourceRoot":"","sources":["../../src/engine/fronts-publish-intent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,352 @@
1
+ /** The CMS's own name for the record this intent describes. */
2
+ export const FRONTS_PUBLISH_INTENT_SOURCE = 'masthead';
3
+ /**
4
+ * `masthead:<content_id>:<version>`. Stable for as long as the canonical
5
+ * content is, and different the moment `canonical_version` advances, so a
6
+ * meaningful revision can never silently reuse a conflicting identity.
7
+ */
8
+ export function frontsPublishIntentId(contentId, version) {
9
+ return `${FRONTS_PUBLISH_INTENT_SOURCE}:${contentId}:${version}`;
10
+ }
11
+ /**
12
+ * A due item whose canonical data cannot produce a complete intent. Thrown by
13
+ * the single-item hydration; collected (never swallowed) by the batch one.
14
+ */
15
+ export class FrontsPublishIntentError extends Error {
16
+ code;
17
+ contentId;
18
+ constructor(code, contentId, detail) {
19
+ super(`fronts publish intent ${code} for content ${contentId}${detail ? `: ${detail}` : ''}`);
20
+ this.name = 'FrontsPublishIntentError';
21
+ this.code = code;
22
+ this.contentId = contentId;
23
+ }
24
+ }
25
+ // --- trigger token ---------------------------------------------------------
26
+ /** SHA-256 hex digest via Web Crypto (never node:crypto). */
27
+ async function sha256Hex(input) {
28
+ const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(input));
29
+ return Array.from(new Uint8Array(digest))
30
+ .map((b) => b.toString(16).padStart(2, '0'))
31
+ .join('');
32
+ }
33
+ /** The `sha256:` prefix every exposed CMS trigger-token digest carries. */
34
+ export const FRONTS_PUBLISH_TRIGGER_TOKEN_HASH_PREFIX = 'sha256:';
35
+ /**
36
+ * The exposed form of a trigger token: `sha256:<hex>`. Same convention
37
+ * `engine/foundry-dispatch.ts` already uses for `processingTriggerTokenHash`.
38
+ */
39
+ export async function frontsPublishTriggerTokenHash(rawToken) {
40
+ return `${FRONTS_PUBLISH_TRIGGER_TOKEN_HASH_PREFIX}${await sha256Hex(rawToken)}`;
41
+ }
42
+ /**
43
+ * Mint this item's raw publish trigger token if — and only if — it has none.
44
+ *
45
+ * Idempotent by compare-and-set, so concurrent callers cannot produce two tokens
46
+ * and a re-arm never rotates a token out from under an in-flight intent: an
47
+ * armed item keeps one stable `trigger_token_hash` across every later read.
48
+ * Called from `scheduleContent`, the single seam an item passes through to
49
+ * become `scheduled` with a `publish_at`. The raw value is never returned.
50
+ */
51
+ export async function armFrontsPublishTriggerToken(db, contentId) {
52
+ const result = await db
53
+ .prepare(`UPDATE content_items
54
+ SET fronts_publish_trigger_token = ?
55
+ WHERE id = ? AND fronts_publish_trigger_token IS NULL`)
56
+ .bind(crypto.randomUUID(), contentId)
57
+ .run();
58
+ // `meta` is a structural Record<string, unknown> on the package's minimal D1
59
+ // surface, so narrow before comparing (same idiom as fronts-publish.ts).
60
+ const changes = result.meta?.changes;
61
+ return { minted: typeof changes === 'number' && changes > 0 };
62
+ }
63
+ /** Single source of truth for what a publishable target list IS — shared by the
64
+ * stored-value parser and the setter so the two can never diverge. */
65
+ function normalizeChannelTargets(entries) {
66
+ const targets = [];
67
+ for (const entry of entries) {
68
+ if (typeof entry !== 'string' || entry.trim() === '') {
69
+ return { code: 'invalid_channel_targets' };
70
+ }
71
+ const target = entry.trim();
72
+ if (!targets.includes(target))
73
+ targets.push(target);
74
+ }
75
+ if (targets.length === 0)
76
+ return { code: 'missing_channel_targets' };
77
+ return { targets };
78
+ }
79
+ export function parseFrontsChannelTargets(raw) {
80
+ if (raw === null || raw === undefined || raw.trim() === '') {
81
+ return { code: 'missing_channel_targets' };
82
+ }
83
+ let parsed;
84
+ try {
85
+ parsed = JSON.parse(raw);
86
+ }
87
+ catch {
88
+ return { code: 'invalid_channel_targets' };
89
+ }
90
+ if (!Array.isArray(parsed))
91
+ return { code: 'invalid_channel_targets' };
92
+ return normalizeChannelTargets(parsed);
93
+ }
94
+ /**
95
+ * Arm an item's publish targets. Rejects an empty or non-string list loudly
96
+ * instead of storing something that would later hydrate as "complete".
97
+ */
98
+ export async function setFrontsChannelTargets(db, contentId, targets) {
99
+ const normalized = normalizeChannelTargets(targets);
100
+ if ('code' in normalized) {
101
+ throw new FrontsPublishIntentError(normalized.code, contentId, 'targets must be a non-empty list of non-empty strings');
102
+ }
103
+ await db
104
+ .prepare('UPDATE content_items SET fronts_channel_targets = ? WHERE id = ?')
105
+ .bind(JSON.stringify(normalized.targets), contentId)
106
+ .run();
107
+ }
108
+ const CANDIDATE_COLUMNS = `id, type, slug, title, channel, publish_at, fronts_publish_status,
109
+ canonical_version, fronts_channel_targets, fronts_publish_trigger_token,
110
+ canonical_url, hero_image_id, social_image_id`;
111
+ /** `?, ?, ?` for an IN clause of `count` bound values. */
112
+ function placeholders(count) {
113
+ return new Array(count).fill('?').join(', ');
114
+ }
115
+ /** An R2 object key (or already-absolute URL) against the public media host. */
116
+ function objectKeyUrl(key, mediaPublicDomain) {
117
+ if (/^https?:\/\//i.test(key))
118
+ return key;
119
+ return `https://${mediaPublicDomain}/${key.replace(/^\/+/, '')}`;
120
+ }
121
+ /** Resolve a media row to its public URL, or null when it has no usable form. */
122
+ function mediaUrl(row, mediaPublicDomain) {
123
+ const stored = row.public_url?.trim() || row.url?.trim();
124
+ if (stored)
125
+ return stored;
126
+ const key = row.object_key?.trim();
127
+ return key ? objectKeyUrl(key, mediaPublicDomain) : null;
128
+ }
129
+ /**
130
+ * Hydrate every due/overdue `scheduled` item into a complete canonical intent.
131
+ *
132
+ * Uses the SAME due predicate as `listFrontsPublishSchedule` — overdue items
133
+ * included (the window's lower bound is deliberately not applied), future items
134
+ * excluded — so existing window behaviour is preserved exactly. READ ONLY: it
135
+ * never mutates content, advances `canonical_version`, or mints a token, so
136
+ * polling cannot churn an intent identity.
137
+ */
138
+ export async function hydrateFrontsPublishIntents(db, options) {
139
+ const now = options.nowSeconds ?? Math.floor(Date.now() / 1_000);
140
+ const limit = Math.min(Math.max(1, Math.floor(options.limit ?? 200)), 1000);
141
+ const mediaPublicDomain = options.mediaPublicDomain ?? 'media.fronts.co';
142
+ const candidates = await db
143
+ .prepare(`SELECT ${CANDIDATE_COLUMNS}
144
+ FROM content_items
145
+ WHERE status = 'scheduled'
146
+ AND publish_at IS NOT NULL
147
+ AND publish_at <= ?
148
+ ORDER BY publish_at ASC
149
+ LIMIT ?`)
150
+ .bind(options.toSeconds, limit)
151
+ .all();
152
+ const rows = candidates.results ?? [];
153
+ if (rows.length === 0)
154
+ return { intents: [], incomplete: [] };
155
+ const refs = await loadContentRefSources(db, rows);
156
+ const intents = [];
157
+ const incomplete = [];
158
+ for (const row of rows) {
159
+ try {
160
+ intents.push(await buildIntent(row, refs, {
161
+ now,
162
+ mediaPublicDomain,
163
+ resolve: options.resolveContentUrl,
164
+ }));
165
+ }
166
+ catch (error) {
167
+ if (error instanceof FrontsPublishIntentError) {
168
+ // LOUD: an operator can see exactly which due item is unpublishable and why.
169
+ console.error(`[cms] fronts publish intent incomplete: ${error.message}`);
170
+ incomplete.push({ content_id: error.contentId, code: error.code, message: error.message });
171
+ continue;
172
+ }
173
+ throw error;
174
+ }
175
+ }
176
+ return { intents, incomplete };
177
+ }
178
+ /** Batched per-type + media lookups — a fixed query count, not one per item. */
179
+ async function loadContentRefSources(db, rows) {
180
+ const bodyBackedIds = rows
181
+ .filter((r) => r.type === 'article' || r.type === 'newsletter' || r.type === 'page')
182
+ .map((r) => r.id);
183
+ const videoIds = rows.filter((r) => r.type === 'video').map((r) => r.id);
184
+ const podcastIds = rows.filter((r) => r.type === 'podcast').map((r) => r.id);
185
+ const articles = new Map();
186
+ if (bodyBackedIds.length > 0) {
187
+ const res = await db
188
+ .prepare(`SELECT content_id, body_markdown, word_count FROM article_content
189
+ WHERE content_id IN (${placeholders(bodyBackedIds.length)})`)
190
+ .bind(...bodyBackedIds)
191
+ .all();
192
+ for (const r of res.results ?? [])
193
+ articles.set(r.content_id, r);
194
+ }
195
+ const videos = new Map();
196
+ if (videoIds.length > 0) {
197
+ const res = await db
198
+ .prepare(`SELECT content_id, hls_ready, hls_manifest_url, hls_poster_url, duration_seconds,
199
+ thumbnail_image_id
200
+ FROM video_content WHERE content_id IN (${placeholders(videoIds.length)})`)
201
+ .bind(...videoIds)
202
+ .all();
203
+ for (const r of res.results ?? [])
204
+ videos.set(r.content_id, r);
205
+ }
206
+ const podcasts = new Map();
207
+ if (podcastIds.length > 0) {
208
+ const res = await db
209
+ .prepare(`SELECT content_id, audio_r2_key, duration_seconds FROM podcast_content
210
+ WHERE content_id IN (${placeholders(podcastIds.length)})`)
211
+ .bind(...podcastIds)
212
+ .all();
213
+ for (const r of res.results ?? [])
214
+ podcasts.set(r.content_id, r);
215
+ }
216
+ const mediaIds = [];
217
+ const pushMediaId = (id) => {
218
+ const trimmed = id?.trim();
219
+ if (trimmed && !mediaIds.includes(trimmed))
220
+ mediaIds.push(trimmed);
221
+ };
222
+ for (const row of rows) {
223
+ pushMediaId(row.hero_image_id);
224
+ pushMediaId(row.social_image_id);
225
+ }
226
+ for (const video of videos.values())
227
+ pushMediaId(video.thumbnail_image_id);
228
+ const media = new Map();
229
+ if (mediaIds.length > 0) {
230
+ const res = await db
231
+ .prepare(`SELECT id, object_key, public_url, url, width, height FROM media_assets
232
+ WHERE id IN (${placeholders(mediaIds.length)})`)
233
+ .bind(...mediaIds)
234
+ .all();
235
+ for (const r of res.results ?? [])
236
+ media.set(r.id, r);
237
+ }
238
+ return { articles, videos, podcasts, media };
239
+ }
240
+ /** Build one intent, throwing FrontsPublishIntentError on any missing required value. */
241
+ async function buildIntent(row, refs, ctx) {
242
+ if (row.publish_at === null || !Number.isFinite(row.publish_at)) {
243
+ throw new FrontsPublishIntentError('missing_publish_at', row.id);
244
+ }
245
+ const version = row.canonical_version;
246
+ if (version === null || !Number.isInteger(version) || version < 1) {
247
+ throw new FrontsPublishIntentError('invalid_canonical_version', row.id, `canonical_version must be a positive integer, got ${String(version)}`);
248
+ }
249
+ const targets = parseFrontsChannelTargets(row.fronts_channel_targets);
250
+ if ('code' in targets) {
251
+ throw new FrontsPublishIntentError(targets.code, row.id, 'content_items.fronts_channel_targets must be a JSON array of target slugs');
252
+ }
253
+ const rawToken = row.fronts_publish_trigger_token?.trim();
254
+ if (!rawToken) {
255
+ throw new FrontsPublishIntentError('missing_trigger_token', row.id, 'item was never armed for the Fronts publish path (no fronts_publish_trigger_token)');
256
+ }
257
+ const deadline = Math.floor(row.publish_at);
258
+ return {
259
+ intent_id: frontsPublishIntentId(row.id, version),
260
+ content_id: row.id,
261
+ version,
262
+ type: row.type,
263
+ slug: row.slug,
264
+ title: row.title,
265
+ channel: row.channel,
266
+ channel_targets: targets.targets,
267
+ deadline,
268
+ trigger_at: deadline,
269
+ content_refs: buildContentRefs(row, refs, ctx),
270
+ trigger_token_hash: await frontsPublishTriggerTokenHash(rawToken),
271
+ overdue: deadline <= ctx.now,
272
+ fronts_publish_status: row.fronts_publish_status,
273
+ };
274
+ }
275
+ /** Per-type content refs, applying the absence rules in the module header. */
276
+ function buildContentRefs(row, refs, ctx) {
277
+ const publicUrl = ctx.resolve({ type: row.type, slug: row.slug });
278
+ const canonicalUrl = row.canonical_url?.trim() || null;
279
+ // --- article: required for body-backed types, validly absent otherwise.
280
+ let article = null;
281
+ const bodyBacked = row.type === 'article' || row.type === 'newsletter' || row.type === 'page';
282
+ if (bodyBacked) {
283
+ const found = refs.articles.get(row.id);
284
+ if (!found?.body_markdown || found.body_markdown.trim() === '') {
285
+ throw new FrontsPublishIntentError('missing_article_body', row.id, `a ${row.type} intent requires a non-empty article_content.body_markdown`);
286
+ }
287
+ article = {
288
+ url: canonicalUrl ?? publicUrl,
289
+ slug: row.slug,
290
+ word_count: found.word_count ?? null,
291
+ };
292
+ }
293
+ // --- video_hls: required for video, and only when genuinely ready.
294
+ let videoHls = null;
295
+ if (row.type === 'video') {
296
+ const found = refs.videos.get(row.id);
297
+ const manifest = found?.hls_manifest_url?.trim();
298
+ if (!found || found.hls_ready !== 1 || !manifest) {
299
+ throw new FrontsPublishIntentError('video_media_not_ready', row.id, 'a video intent requires video_content.hls_ready = 1 and a non-empty hls_manifest_url');
300
+ }
301
+ videoHls = {
302
+ kind: 'hls-manifest',
303
+ url: manifest,
304
+ poster_url: found.hls_poster_url?.trim() || null,
305
+ duration_seconds: found.duration_seconds ?? null,
306
+ };
307
+ }
308
+ // --- podcast_audio_hls: required for podcast. See the module header for why
309
+ // `kind` is 'r2-object' today rather than a manifest.
310
+ let podcastAudio = null;
311
+ if (row.type === 'podcast') {
312
+ const found = refs.podcasts.get(row.id);
313
+ const key = found?.audio_r2_key?.trim();
314
+ if (!found || !key) {
315
+ throw new FrontsPublishIntentError('missing_podcast_audio', row.id, 'a podcast intent requires a non-empty podcast_content.audio_r2_key');
316
+ }
317
+ podcastAudio = {
318
+ kind: 'r2-object',
319
+ url: objectKeyUrl(key, ctx.mediaPublicDomain),
320
+ duration_seconds: found.duration_seconds ?? null,
321
+ };
322
+ }
323
+ // --- images: always optional. [] means the item has no assets.
324
+ const images = [];
325
+ const pushImage = (id, role) => {
326
+ const assetId = id?.trim();
327
+ if (!assetId)
328
+ return;
329
+ const asset = refs.media.get(assetId);
330
+ if (!asset)
331
+ return;
332
+ const url = mediaUrl(asset, ctx.mediaPublicDomain);
333
+ if (!url)
334
+ return;
335
+ images.push({
336
+ id: assetId,
337
+ role,
338
+ url,
339
+ width: asset.width ?? null,
340
+ height: asset.height ?? null,
341
+ });
342
+ };
343
+ pushImage(row.hero_image_id, 'hero');
344
+ pushImage(row.social_image_id, 'social');
345
+ pushImage(refs.videos.get(row.id)?.thumbnail_image_id, 'thumbnail');
346
+ // --- links: always optional.
347
+ const links = [{ rel: 'public', url: publicUrl }];
348
+ if (canonicalUrl)
349
+ links.push({ rel: 'canonical', url: canonicalUrl });
350
+ return { article, images, video_hls: videoHls, podcast_audio_hls: podcastAudio, links };
351
+ }
352
+ //# sourceMappingURL=fronts-publish-intent.js.map