@growth-labs/cms 0.5.30 → 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.
- package/dist/engine/foundry-dispatch.d.ts +7 -2
- package/dist/engine/foundry-dispatch.d.ts.map +1 -1
- package/dist/engine/foundry-dispatch.js +24 -6
- package/dist/engine/foundry-dispatch.js.map +1 -1
- package/dist/engine/fronts-publish-intent.d.ts +174 -0
- package/dist/engine/fronts-publish-intent.d.ts.map +1 -0
- package/dist/engine/fronts-publish-intent.js +352 -0
- package/dist/engine/fronts-publish-intent.js.map +1 -0
- package/dist/engine/fronts-publish.d.ts +80 -0
- package/dist/engine/fronts-publish.d.ts.map +1 -0
- package/dist/engine/fronts-publish.js +286 -0
- package/dist/engine/fronts-publish.js.map +1 -0
- package/dist/engine/index.d.ts +2 -0
- package/dist/engine/index.d.ts.map +1 -1
- package/dist/engine/index.js +5 -0
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/publisher.d.ts.map +1 -1
- package/dist/engine/publisher.js +21 -1
- package/dist/engine/publisher.js.map +1 -1
- package/dist/integration/index.d.ts.map +1 -1
- package/dist/integration/index.js +10 -0
- package/dist/integration/index.js.map +1 -1
- package/dist/routes/fronts-publish.d.ts +10 -0
- package/dist/routes/fronts-publish.d.ts.map +1 -0
- package/dist/routes/fronts-publish.js +250 -0
- package/dist/routes/fronts-publish.js.map +1 -0
- package/dist/routes/index.d.ts +3 -0
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +4 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/schema/layout.d.ts +2 -2
- package/dist/schema/migrations.d.ts.map +1 -1
- package/dist/schema/migrations.js +36 -0
- package/dist/schema/migrations.js.map +1 -1
- package/dist/schema/portable-text.d.ts +2 -2
- package/dist/schema/tables.d.ts +1 -1
- package/dist/schema/tables.d.ts.map +1 -1
- package/dist/schema/tables.js +2 -0
- package/dist/schema/tables.js.map +1 -1
- package/dist/schema/types.d.ts +59 -0
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js.map +1 -1
- package/dist/surveys/schema.d.ts +54 -54
- package/dist/ui/api/fronts/publish-callback.d.ts +3 -0
- package/dist/ui/api/fronts/publish-callback.d.ts.map +1 -0
- package/dist/ui/api/fronts/publish-callback.js +8 -0
- package/dist/ui/api/fronts/publish-callback.js.map +1 -0
- package/dist/ui/api/fronts/schedule.d.ts +3 -0
- package/dist/ui/api/fronts/schedule.d.ts.map +1 -0
- package/dist/ui/api/fronts/schedule.js +8 -0
- package/dist/ui/api/fronts/schedule.js.map +1 -0
- package/migrations/0026_fronts_publish.sql +31 -0
- package/migrations/0027_fronts_publish_intent.sql +44 -0
- package/package.json +1 -1
- package/src/engine/foundry-dispatch.ts +22 -6
- package/src/engine/fronts-publish-intent.ts +676 -0
- package/src/engine/fronts-publish.ts +417 -0
- package/src/engine/index.ts +27 -0
- package/src/engine/publisher.ts +21 -1
- package/src/integration/index.ts +10 -0
- package/src/routes/fronts-publish.ts +292 -0
- package/src/routes/index.ts +8 -0
- package/src/schema/migrations.ts +36 -0
- package/src/schema/tables.ts +2 -0
- package/src/schema/types.ts +61 -0
- package/src/ui/api/fronts/publish-callback.ts +18 -0
- package/src/ui/api/fronts/schedule.ts +18 -0
|
@@ -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
|
-
*
|
|
53
|
-
*
|
|
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
|
|
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
|
-
*
|
|
273
|
-
*
|
|
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 (
|
|
299
|
+
if (hex.length % 2 !== 0)
|
|
282
300
|
return false;
|
|
283
|
-
sigBytes = new Uint8Array(
|
|
284
|
-
for (let i = 0; i <
|
|
285
|
-
const byte = Number.parseInt(
|
|
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
|