@scalemule/news-publication 0.0.81 → 0.0.83
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/README.md +48 -9
- package/bin/scalemule-article.mjs +22 -5
- package/dist/article-workflow-client.cjs +27 -135
- package/dist/article-workflow-client.d.cts +73 -13
- package/dist/article-workflow-client.d.ts +73 -13
- package/dist/article-workflow-client.js +27 -135
- package/dist/index.cjs +201 -0
- package/dist/index.d.cts +51 -1
- package/dist/index.d.ts +51 -1
- package/dist/index.js +196 -1
- package/package.json +1 -1
- package/tokens/broadsheet.css +4 -1
package/README.md
CHANGED
|
@@ -328,15 +328,54 @@ The publisher-supplied `PawMark` preserves the approved SVG paths. Hosts may set
|
|
|
328
328
|
|
|
329
329
|
### Standard article submission
|
|
330
330
|
|
|
331
|
-
|
|
332
|
-
`@scalemule/news-publication/article-workflow
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
331
|
+
Submit through the server-owned API using `PublicationArticleClient` from
|
|
332
|
+
`@scalemule/news-publication/article-workflow`:
|
|
333
|
+
|
|
334
|
+
```ts
|
|
335
|
+
const receipt = await client.submitArticle({
|
|
336
|
+
operationKey: "story-intake-v1", authorProfileId: authorId,
|
|
337
|
+
document: { title, content_markdown: body, section: "Local Business" },
|
|
338
|
+
intent: "publish",
|
|
339
|
+
assets: [{ file: photo, filename: "venue.jpg", position: "hero",
|
|
340
|
+
metadata: { alt: "Exterior of the actual venue", credit: "Photographer",
|
|
341
|
+
rights_confirmed: true } }],
|
|
342
|
+
});
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
This sends **one POST** to `/v1/blog/publications/{id}/submissions`, with a
|
|
346
|
+
multipart manifest and its files. JSON without files is supported by
|
|
347
|
+
`client.submit(manifest, key)`. The same API accepts `article.operation: "save"`
|
|
348
|
+
with the existing article ID, expected generation, complete document and revision
|
|
349
|
+
reason. The server owns private Photo/Storage intake, scan checks, immutable
|
|
350
|
+
media attachment, required private narration and release. Optional narration
|
|
351
|
+
and missing-visual commissioning use the existing post-publication workers.
|
|
352
|
+
Existing subject/editorial/sensitivity approvals still apply. `draft` is the
|
|
353
|
+
safe default; `publish` requests release after the configured gates.
|
|
354
|
+
|
|
355
|
+
An accepted receipt is **not** a claim that media is ready or a page has been
|
|
356
|
+
verified. Read `client.submission(receipt.submission_id)` for holds, processing
|
|
357
|
+
and public readiness. After a timeout, replay the identical manifest, files and
|
|
358
|
+
operation key. Do not re-upload through other endpoints or create a new article.
|
|
359
|
+
A foreign revision, rejected media, expired replay or unconfirmed asset intake
|
|
360
|
+
stops with a retained receipt rather than silently overwriting or duplicating.
|
|
361
|
+
No client-side fallback performs a separate multi-service publishing sequence.
|
|
362
|
+
Discover `managed_submission_version: 1` before adopting this endpoint on an
|
|
363
|
+
older server. Supported files are still JPEG/PNG/WebP photographs, not video or
|
|
364
|
+
animation. Never use SQL, S3 paths, embedded binary or invented ready flags.
|
|
365
|
+
|
|
366
|
+
CLI equivalents:
|
|
367
|
+
|
|
368
|
+
```sh
|
|
369
|
+
scalemule-article submit --publication "$PUBLICATION_ID" \
|
|
370
|
+
--manifest article.json --asset hero=./venue.jpg --operation-key story-intake-v1
|
|
371
|
+
scalemule-article status --publication "$PUBLICATION_ID" --submission "$SUBMISSION_ID"
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
The manifest uses the API's snake_case fields: `article`, `intent`, and optional
|
|
375
|
+
`assets: [{slot: "hero", position: "hero", metadata: {...}}]`. Each `--asset`
|
|
376
|
+
matches one declared slot. Credentials are normal project/member credentials.
|
|
377
|
+
The operation key is retained for 30 days. A legacy article must first have an
|
|
378
|
+
explicitly selected collaboration policy; this endpoint never downgrades review.
|
|
340
379
|
|
|
341
380
|
### Edit article prose, cards and citations
|
|
342
381
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readFile, mkdir, writeFile, rename, open, unlink } from 'node:fs/promises';
|
|
2
|
+
import { readFile, stat, mkdir, writeFile, rename, open, unlink } from 'node:fs/promises';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { dirname, join, basename } from 'node:path';
|
|
5
5
|
import { createHash } from 'node:crypto';
|
|
@@ -7,6 +7,8 @@ import { parseArgs } from 'node:util';
|
|
|
7
7
|
import { PublicationArticleClient, articlePhotoHtmlMatches,articleEditHtmlMatches } from '../dist/article-workflow-client.js';
|
|
8
8
|
|
|
9
9
|
const help = `Usage:
|
|
10
|
+
scalemule-article submit --publication ID --manifest article.json --operation-key KEY [--asset hero=/path/photo.jpg]
|
|
11
|
+
scalemule-article status --publication ID --submission ID
|
|
10
12
|
scalemule-article inspect --publication ID --article ID
|
|
11
13
|
scalemule-article replace-cover --publication ID --article ID --file-id ID --expected-version N --metadata photo.json
|
|
12
14
|
scalemule-article replace-photo --url ARTICLE_URL --file PHOTO --metadata photo.json --operation-key KEY
|
|
@@ -54,7 +56,7 @@ that every website/CDN cache already displays the new image.
|
|
|
54
56
|
try {
|
|
55
57
|
const { values, positionals } = parseArgs({
|
|
56
58
|
allowPositionals: true, strict: true,
|
|
57
|
-
options: Object.fromEntries(['publication', 'article', 'file-id', 'expected-version', 'metadata', 'application-id', 'api-url', 'url', 'file', 'operation-key', 'state', 'timeout-ms','patch','expected-generation','enable-review-policy'].map(k => [k, { type: 'string' }]).concat([['help', { type: 'boolean' }],['release',{type:'boolean'}]])),
|
|
59
|
+
options: Object.fromEntries(['publication', 'article', 'file-id', 'expected-version', 'metadata', 'application-id', 'api-url', 'url', 'file', 'operation-key', 'state', 'timeout-ms','patch','expected-generation','enable-review-policy','manifest','submission'].map(k => [k, { type: 'string' }]).concat([['asset',{type:'string',multiple:true}],['help', { type: 'boolean' }],['release',{type:'boolean'}]])),
|
|
58
60
|
});
|
|
59
61
|
if (values.help || !positionals.length) {
|
|
60
62
|
console.log(help);
|
|
@@ -64,7 +66,7 @@ try {
|
|
|
64
66
|
const budget = Number(values['timeout-ms'] ?? (command==='edit'?10000:30000));
|
|
65
67
|
if (!Number.isFinite(budget) || budget < 100 || budget > 120000) throw Error('--timeout-ms must be between 100 and 120000');
|
|
66
68
|
const remaining = () => { const ms = Math.floor(budget - (performance.now()-commandStarted)); if (ms < 1) throw Error('Operation deadline reached; resume the same operation'); return ms; };
|
|
67
|
-
if (extra.length || !['inspect', 'replace-cover', 'replace-photo','edit'].includes(command)) throw Error('Use inspect, edit, replace-cover or replace-photo; see --help');
|
|
69
|
+
if (extra.length || !['submit','status','inspect', 'replace-cover', 'replace-photo','edit'].includes(command)) throw Error('Use submit, status, inspect, edit, replace-cover or replace-photo; see --help');
|
|
68
70
|
if (values.url) {
|
|
69
71
|
if (!process.env.SCALEMULE_API_KEY || !process.env.SCALEMULE_ACCESS_TOKEN || values['application-id']) throw Error('URL resolution requires normal publication member/API-key credentials');
|
|
70
72
|
const url = new URL(values.url);
|
|
@@ -83,7 +85,7 @@ try {
|
|
|
83
85
|
if ((values.publication && values.publication !== pub.id) || (values.article && values.article !== resolved.article.id)) throw Error('Article URL and supplied IDs disagree');
|
|
84
86
|
values.publication = pub.id; values.article = resolved.article.id;
|
|
85
87
|
}
|
|
86
|
-
for (const key of ['publication',
|
|
88
|
+
for (const key of (['submit','status'].includes(command)?['publication']:['publication','article'])) if (!values[key]) throw Error(`Missing --${key}`);
|
|
87
89
|
if (!process.env.SCALEMULE_ACCESS_TOKEN) throw Error('SCALEMULE_ACCESS_TOKEN is required; use normal member or platform admin authorization');
|
|
88
90
|
if (!values['application-id'] && !process.env.SCALEMULE_API_KEY) throw Error('Member calls also require SCALEMULE_API_KEY');
|
|
89
91
|
const client = new PublicationArticleClient({
|
|
@@ -94,7 +96,22 @@ try {
|
|
|
94
96
|
accessToken: process.env.SCALEMULE_ACCESS_TOKEN,
|
|
95
97
|
});
|
|
96
98
|
const started = performance.now();
|
|
97
|
-
if
|
|
99
|
+
if(command==='submit'){
|
|
100
|
+
for(const key of ['manifest','operation-key'])if(!values[key])throw Error('Missing --'+key);
|
|
101
|
+
const manifest=JSON.parse(await readFile(values.manifest,'utf8'));
|
|
102
|
+
const assets=[];
|
|
103
|
+
for(const entry of values.asset??[]){
|
|
104
|
+
const split=entry.indexOf('=');if(split<1)throw Error('--asset must be slot=/path/to/photo');
|
|
105
|
+
const slot=entry.slice(0,split),path=entry.slice(split+1);
|
|
106
|
+
const info=await stat(path);if(!info.isFile()||info.size<1||info.size>25*1024*1024)throw Error('Photo must be a regular file between 1 byte and 25 MiB');
|
|
107
|
+
assets.push({slot,filename:basename(path),file:new Blob([await readFile(path)])});
|
|
108
|
+
}
|
|
109
|
+
const receipt=await client.submit(manifest,values['operation-key'],assets,AbortSignal.timeout(remaining()));
|
|
110
|
+
console.log(JSON.stringify({...receipt,elapsed_ms:Math.round(performance.now()-started)},null,2));
|
|
111
|
+
}else if(command==='status'){
|
|
112
|
+
if(!values.submission)throw Error('Missing --submission');
|
|
113
|
+
console.log(JSON.stringify(await client.submission(values.submission,AbortSignal.timeout(remaining())),null,2));
|
|
114
|
+
}else if (command === 'inspect') {
|
|
98
115
|
const [options, processing, collaboration] = await Promise.all([
|
|
99
116
|
client.options(values.article), client.processing(values.article),
|
|
100
117
|
client.get(values.article).then(current => ({ enabled: true, generation: current.generation, status: current.status })).catch(error => {
|
|
@@ -900,143 +900,35 @@ var PublicationArticleClient = class {
|
|
|
900
900
|
"If-Match": String(generation)
|
|
901
901
|
});
|
|
902
902
|
}
|
|
903
|
-
/** One
|
|
904
|
-
*
|
|
905
|
-
*
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
if (
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
await asset.file.arrayBuffer()
|
|
931
|
-
)
|
|
932
|
-
)
|
|
933
|
-
).map((v) => v.toString(16).padStart(2, "0")).join("");
|
|
934
|
-
const existing = current.contributions.find(
|
|
935
|
-
(c) => c.kind === "MEDIA" && c.state !== "REJECTED" && c.state !== "WITHDRAWN" && c.payload.sha256 === digest3 && Object.entries(asset.metadata).every(
|
|
936
|
-
([k, v]) => c.payload[k] === v
|
|
937
|
-
)
|
|
938
|
-
);
|
|
939
|
-
const staged = existing?.payload.file_id ? { contribution_id: existing.id, file_id: existing.payload.file_id } : await this.upload(
|
|
940
|
-
created.article_id,
|
|
941
|
-
current.generation,
|
|
942
|
-
asset.file,
|
|
943
|
-
asset.filename,
|
|
944
|
-
asset.metadata
|
|
945
|
-
);
|
|
946
|
-
await this.registerPhoto(staged.file_id);
|
|
947
|
-
if (input.approveMedia) {
|
|
948
|
-
if (!asset.metadata.rights_confirmed || asset.metadata.private_preview_only)
|
|
949
|
-
throw new Error("Publication requires confirmed image rights");
|
|
950
|
-
if (existing?.state !== "APPROVED") {
|
|
951
|
-
current = await this.get(created.article_id);
|
|
952
|
-
await this.command(created.article_id, current.generation, {
|
|
953
|
-
action: "RESOLVE",
|
|
954
|
-
contribution_id: staged.contribution_id,
|
|
955
|
-
state: "APPROVED"
|
|
956
|
-
});
|
|
957
|
-
}
|
|
958
|
-
selected.push({
|
|
959
|
-
kind: "image",
|
|
960
|
-
contribution_id: staged.contribution_id,
|
|
961
|
-
file_id: staged.file_id,
|
|
962
|
-
caption: asset.metadata.caption,
|
|
963
|
-
credit: asset.metadata.credit,
|
|
964
|
-
alt: asset.metadata.alt,
|
|
965
|
-
position: asset.position
|
|
966
|
-
});
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
if (selected.length) {
|
|
970
|
-
const current = await this.get(created.article_id);
|
|
971
|
-
const document = { ...input.document, media: selected };
|
|
972
|
-
const saved = current.revisions.find(
|
|
973
|
-
(r) => r.id === current.current_revision_id
|
|
974
|
-
)?.document;
|
|
975
|
-
if (!saved) throw new Error("Current immutable revision is missing");
|
|
976
|
-
if (sameDocument(saved, document))
|
|
977
|
-
return {
|
|
978
|
-
...created,
|
|
979
|
-
revision_id: current.current_revision_id,
|
|
980
|
-
generation: current.generation,
|
|
981
|
-
editorial_status: current.status,
|
|
982
|
-
replayed: true
|
|
983
|
-
};
|
|
984
|
-
if (!sameDocument(
|
|
985
|
-
{ ...saved, media: [] },
|
|
986
|
-
{ ...input.document, media: [] }
|
|
987
|
-
) || saved.media?.length)
|
|
988
|
-
throw new ArticleWorkflowError(
|
|
989
|
-
"The article was edited after this package began; load its current revision before changing it",
|
|
990
|
-
409,
|
|
991
|
-
created.article_id
|
|
992
|
-
);
|
|
993
|
-
return await this.save(
|
|
994
|
-
`${input.operationKey}:assets`,
|
|
995
|
-
created.article_id,
|
|
996
|
-
current.generation,
|
|
997
|
-
document,
|
|
998
|
-
"MEDIA_UPDATE"
|
|
999
|
-
);
|
|
1000
|
-
}
|
|
1001
|
-
return created;
|
|
1002
|
-
} catch (error) {
|
|
1003
|
-
if (error instanceof ArticleWorkflowError) {
|
|
1004
|
-
error.articleId = created.article_id;
|
|
1005
|
-
throw error;
|
|
1006
|
-
}
|
|
1007
|
-
throw new ArticleWorkflowError(
|
|
1008
|
-
error instanceof Error ? error.message : "Article package interrupted",
|
|
1009
|
-
0,
|
|
1010
|
-
created.article_id
|
|
1011
|
-
);
|
|
1012
|
-
}
|
|
903
|
+
/** One server-owned submission. No client-side Photo/TTS orchestration and no
|
|
904
|
+
* fallback to a different workflow. A 202 receipt is acceptance, not readiness.
|
|
905
|
+
* Replay the identical manifest/files/key after an uncertain acknowledgement.
|
|
906
|
+
*/
|
|
907
|
+
submit(manifest, operationKey, files = [], signal) {
|
|
908
|
+
if (!operationKey) throw new Error("A stable operation key is required");
|
|
909
|
+
if (files.length > 10 || files.some((f) => f.file.size < 1 || f.file.size > 25 * 1024 * 1024) || files.reduce((n, f) => n + f.file.size, 0) > 50 * 1024 * 1024) throw new Error("Supply up to ten photographs, at most 25 MiB each and 50 MiB total");
|
|
910
|
+
const slots = new Set(files.map((f) => f.slot));
|
|
911
|
+
if (slots.size !== files.length || files.length !== (manifest.assets?.length ?? 0) || manifest.assets?.some((a) => !slots.has(a.slot))) throw new Error("Supply one file for each declared asset slot");
|
|
912
|
+
if (!files.length) return this.request("/submissions", "POST", manifest, { "Idempotency-Key": operationKey }, signal);
|
|
913
|
+
const form = new FormData();
|
|
914
|
+
form.append("manifest", JSON.stringify(manifest));
|
|
915
|
+
for (const asset of files) form.append(asset.slot, asset.file, asset.filename);
|
|
916
|
+
return this.request("/submissions", "POST", form, { "Idempotency-Key": operationKey }, signal);
|
|
917
|
+
}
|
|
918
|
+
submission(submissionId, signal) {
|
|
919
|
+
return this.request("/submissions/" + encodeURIComponent(submissionId), "GET", void 0, void 0, signal);
|
|
920
|
+
}
|
|
921
|
+
submitArticle(input) {
|
|
922
|
+
if (input.document.media?.length) throw new Error("Pass file assets separately when creating an article package");
|
|
923
|
+
const assets = (input.assets ?? []).map((a, i) => ({ ...a, slot: "asset_" + String.fromCharCode(97 + i) }));
|
|
924
|
+
return this.submit({
|
|
925
|
+
article: { operation: "create", author_profile_id: input.authorProfileId, document: input.document, review_policy: input.reviewPolicy },
|
|
926
|
+
intent: input.intent ?? "draft",
|
|
927
|
+
approve_media: input.approveMedia,
|
|
928
|
+
assets: assets.map((a) => ({ slot: a.slot, position: a.position, metadata: a.metadata }))
|
|
929
|
+
}, input.operationKey, assets);
|
|
1013
930
|
}
|
|
1014
931
|
};
|
|
1015
|
-
function sameDocument(a, b) {
|
|
1016
|
-
const sorted = (v) => Array.isArray(v) ? v.map(sorted) : v && typeof v === "object" ? Object.fromEntries(Object.keys(v).sort().map((k) => [k, sorted(v[k])])) : v;
|
|
1017
|
-
const canonical3 = (d) => [
|
|
1018
|
-
d.title,
|
|
1019
|
-
d.subtitle || "",
|
|
1020
|
-
d.content_markdown,
|
|
1021
|
-
d.excerpt ?? null,
|
|
1022
|
-
sorted(d.cards ?? null),
|
|
1023
|
-
sorted(d.citations ?? null),
|
|
1024
|
-
d.section,
|
|
1025
|
-
d.community || "",
|
|
1026
|
-
d.format || null,
|
|
1027
|
-
d.layout ? [d.layout.id, d.layout.version] : null,
|
|
1028
|
-
(d.media || []).map((m) => [
|
|
1029
|
-
m.kind || "image",
|
|
1030
|
-
m.contribution_id,
|
|
1031
|
-
m.file_id,
|
|
1032
|
-
m.caption,
|
|
1033
|
-
m.credit,
|
|
1034
|
-
m.alt,
|
|
1035
|
-
m.position
|
|
1036
|
-
])
|
|
1037
|
-
];
|
|
1038
|
-
return JSON.stringify(canonical3(a)) === JSON.stringify(canonical3(b));
|
|
1039
|
-
}
|
|
1040
932
|
|
|
1041
933
|
exports.ArticleEditOperationError = ArticleEditOperationError;
|
|
1042
934
|
exports.ArticlePhotoOperationError = ArticlePhotoOperationError;
|
|
@@ -112,13 +112,13 @@ type ArticleMedia = {
|
|
|
112
112
|
position: "hero" | "inline" | "gallery" | "attachment";
|
|
113
113
|
};
|
|
114
114
|
type AssetMetadata = {
|
|
115
|
-
caption
|
|
116
|
-
credit
|
|
117
|
-
alt
|
|
118
|
-
creator
|
|
119
|
-
who_is_pictured
|
|
120
|
-
when_taken
|
|
121
|
-
where_taken
|
|
115
|
+
caption?: string;
|
|
116
|
+
credit?: string;
|
|
117
|
+
alt?: string;
|
|
118
|
+
creator?: string;
|
|
119
|
+
who_is_pictured?: string;
|
|
120
|
+
when_taken?: string;
|
|
121
|
+
where_taken?: string;
|
|
122
122
|
rights_confirmed: boolean;
|
|
123
123
|
private_preview_only?: boolean;
|
|
124
124
|
};
|
|
@@ -195,6 +195,8 @@ type ArticleWorkflowOptions = {
|
|
|
195
195
|
prioritized_narration_processing?: boolean;
|
|
196
196
|
content_first_editor_document?: boolean;
|
|
197
197
|
versioned_editorial_fields?: boolean;
|
|
198
|
+
managed_submission_version?: number | null;
|
|
199
|
+
multipart_submission?: boolean;
|
|
198
200
|
};
|
|
199
201
|
};
|
|
200
202
|
type ArticleOperation = {
|
|
@@ -224,6 +226,56 @@ type Record = {
|
|
|
224
226
|
};
|
|
225
227
|
}[];
|
|
226
228
|
};
|
|
229
|
+
type ArticleSubmissionManifest = {
|
|
230
|
+
article: {
|
|
231
|
+
operation: "create";
|
|
232
|
+
author_profile_id: string;
|
|
233
|
+
document: ArticleDocument;
|
|
234
|
+
review_policy?: string;
|
|
235
|
+
celebration_type?: string;
|
|
236
|
+
} | {
|
|
237
|
+
operation: "save";
|
|
238
|
+
article_id: string;
|
|
239
|
+
expected_generation: number;
|
|
240
|
+
document: ArticleDocument;
|
|
241
|
+
reason: string;
|
|
242
|
+
};
|
|
243
|
+
intent?: "draft" | "review" | "publish";
|
|
244
|
+
approve_media?: boolean;
|
|
245
|
+
sensitivity_review?: {
|
|
246
|
+
policy_version: string;
|
|
247
|
+
rule_ids: string[];
|
|
248
|
+
reason: string;
|
|
249
|
+
};
|
|
250
|
+
assets?: {
|
|
251
|
+
slot: string;
|
|
252
|
+
position: "hero" | "gallery" | "attachment";
|
|
253
|
+
metadata: AssetMetadata;
|
|
254
|
+
}[];
|
|
255
|
+
};
|
|
256
|
+
type ArticleSubmissionReceipt = {
|
|
257
|
+
submission_id: string;
|
|
258
|
+
article_id: string | null;
|
|
259
|
+
revision_id: string | null;
|
|
260
|
+
status: "intake" | "queued" | "scanning" | "narrating" | "awaiting_review" | "held" | "complete" | "published";
|
|
261
|
+
error_code: string | null;
|
|
262
|
+
status_path: string;
|
|
263
|
+
permalink_path: string | null;
|
|
264
|
+
processing: ArticleProcessing | null;
|
|
265
|
+
progress: {
|
|
266
|
+
intent: string;
|
|
267
|
+
assets: {
|
|
268
|
+
[slot: string]: {
|
|
269
|
+
state: string;
|
|
270
|
+
contribution_id?: string;
|
|
271
|
+
file_id?: string;
|
|
272
|
+
};
|
|
273
|
+
};
|
|
274
|
+
};
|
|
275
|
+
accepted_is_not_published: true;
|
|
276
|
+
public_ready: boolean;
|
|
277
|
+
next_action: string;
|
|
278
|
+
};
|
|
227
279
|
declare class ArticleWorkflowError extends Error {
|
|
228
280
|
status: number;
|
|
229
281
|
articleId?: string | undefined;
|
|
@@ -303,22 +355,30 @@ declare class PublicationArticleClient {
|
|
|
303
355
|
kind: "image";
|
|
304
356
|
state: "OPEN";
|
|
305
357
|
}>;
|
|
306
|
-
/** One
|
|
307
|
-
*
|
|
308
|
-
*
|
|
358
|
+
/** One server-owned submission. No client-side Photo/TTS orchestration and no
|
|
359
|
+
* fallback to a different workflow. A 202 receipt is acceptance, not readiness.
|
|
360
|
+
* Replay the identical manifest/files/key after an uncertain acknowledgement.
|
|
361
|
+
*/
|
|
362
|
+
submit(manifest: ArticleSubmissionManifest, operationKey: string, files?: {
|
|
363
|
+
slot: string;
|
|
364
|
+
file: Blob;
|
|
365
|
+
filename: string;
|
|
366
|
+
}[], signal?: AbortSignal): Promise<ArticleSubmissionReceipt>;
|
|
367
|
+
submission(submissionId: string, signal?: AbortSignal): Promise<ArticleSubmissionReceipt>;
|
|
309
368
|
submitArticle(input: {
|
|
310
369
|
operationKey: string;
|
|
311
370
|
authorProfileId: string;
|
|
312
371
|
document: ArticleDocument;
|
|
313
372
|
reviewPolicy?: string;
|
|
373
|
+
intent?: "draft" | "review" | "publish";
|
|
314
374
|
assets?: {
|
|
315
375
|
file: Blob;
|
|
316
376
|
filename: string;
|
|
317
377
|
metadata: AssetMetadata;
|
|
318
|
-
position:
|
|
378
|
+
position: "hero" | "gallery" | "attachment";
|
|
319
379
|
}[];
|
|
320
380
|
approveMedia?: boolean;
|
|
321
|
-
}): Promise<
|
|
381
|
+
}): Promise<ArticleSubmissionReceipt>;
|
|
322
382
|
}
|
|
323
383
|
|
|
324
|
-
export { type ArticleDocument, type ArticleEditCheckpoint, type ArticleEditInput, ArticleEditOperationError, type ArticleEditPatch, type ArticleMedia, type ArticleOperation, type ArticlePhotoInput, ArticlePhotoOperationError, type ArticleProcessing, ArticleWorkflowError, type ArticleWorkflowOptions, type AssetMetadata, type PhotoOperationCheckpoint, PublicationArticleClient, type PublishedCoverReplacement, articleEditHtmlMatches, articlePhotoHtmlMatches };
|
|
384
|
+
export { type ArticleDocument, type ArticleEditCheckpoint, type ArticleEditInput, ArticleEditOperationError, type ArticleEditPatch, type ArticleMedia, type ArticleOperation, type ArticlePhotoInput, ArticlePhotoOperationError, type ArticleProcessing, type ArticleSubmissionManifest, type ArticleSubmissionReceipt, ArticleWorkflowError, type ArticleWorkflowOptions, type AssetMetadata, type PhotoOperationCheckpoint, PublicationArticleClient, type PublishedCoverReplacement, articleEditHtmlMatches, articlePhotoHtmlMatches };
|
|
@@ -112,13 +112,13 @@ type ArticleMedia = {
|
|
|
112
112
|
position: "hero" | "inline" | "gallery" | "attachment";
|
|
113
113
|
};
|
|
114
114
|
type AssetMetadata = {
|
|
115
|
-
caption
|
|
116
|
-
credit
|
|
117
|
-
alt
|
|
118
|
-
creator
|
|
119
|
-
who_is_pictured
|
|
120
|
-
when_taken
|
|
121
|
-
where_taken
|
|
115
|
+
caption?: string;
|
|
116
|
+
credit?: string;
|
|
117
|
+
alt?: string;
|
|
118
|
+
creator?: string;
|
|
119
|
+
who_is_pictured?: string;
|
|
120
|
+
when_taken?: string;
|
|
121
|
+
where_taken?: string;
|
|
122
122
|
rights_confirmed: boolean;
|
|
123
123
|
private_preview_only?: boolean;
|
|
124
124
|
};
|
|
@@ -195,6 +195,8 @@ type ArticleWorkflowOptions = {
|
|
|
195
195
|
prioritized_narration_processing?: boolean;
|
|
196
196
|
content_first_editor_document?: boolean;
|
|
197
197
|
versioned_editorial_fields?: boolean;
|
|
198
|
+
managed_submission_version?: number | null;
|
|
199
|
+
multipart_submission?: boolean;
|
|
198
200
|
};
|
|
199
201
|
};
|
|
200
202
|
type ArticleOperation = {
|
|
@@ -224,6 +226,56 @@ type Record = {
|
|
|
224
226
|
};
|
|
225
227
|
}[];
|
|
226
228
|
};
|
|
229
|
+
type ArticleSubmissionManifest = {
|
|
230
|
+
article: {
|
|
231
|
+
operation: "create";
|
|
232
|
+
author_profile_id: string;
|
|
233
|
+
document: ArticleDocument;
|
|
234
|
+
review_policy?: string;
|
|
235
|
+
celebration_type?: string;
|
|
236
|
+
} | {
|
|
237
|
+
operation: "save";
|
|
238
|
+
article_id: string;
|
|
239
|
+
expected_generation: number;
|
|
240
|
+
document: ArticleDocument;
|
|
241
|
+
reason: string;
|
|
242
|
+
};
|
|
243
|
+
intent?: "draft" | "review" | "publish";
|
|
244
|
+
approve_media?: boolean;
|
|
245
|
+
sensitivity_review?: {
|
|
246
|
+
policy_version: string;
|
|
247
|
+
rule_ids: string[];
|
|
248
|
+
reason: string;
|
|
249
|
+
};
|
|
250
|
+
assets?: {
|
|
251
|
+
slot: string;
|
|
252
|
+
position: "hero" | "gallery" | "attachment";
|
|
253
|
+
metadata: AssetMetadata;
|
|
254
|
+
}[];
|
|
255
|
+
};
|
|
256
|
+
type ArticleSubmissionReceipt = {
|
|
257
|
+
submission_id: string;
|
|
258
|
+
article_id: string | null;
|
|
259
|
+
revision_id: string | null;
|
|
260
|
+
status: "intake" | "queued" | "scanning" | "narrating" | "awaiting_review" | "held" | "complete" | "published";
|
|
261
|
+
error_code: string | null;
|
|
262
|
+
status_path: string;
|
|
263
|
+
permalink_path: string | null;
|
|
264
|
+
processing: ArticleProcessing | null;
|
|
265
|
+
progress: {
|
|
266
|
+
intent: string;
|
|
267
|
+
assets: {
|
|
268
|
+
[slot: string]: {
|
|
269
|
+
state: string;
|
|
270
|
+
contribution_id?: string;
|
|
271
|
+
file_id?: string;
|
|
272
|
+
};
|
|
273
|
+
};
|
|
274
|
+
};
|
|
275
|
+
accepted_is_not_published: true;
|
|
276
|
+
public_ready: boolean;
|
|
277
|
+
next_action: string;
|
|
278
|
+
};
|
|
227
279
|
declare class ArticleWorkflowError extends Error {
|
|
228
280
|
status: number;
|
|
229
281
|
articleId?: string | undefined;
|
|
@@ -303,22 +355,30 @@ declare class PublicationArticleClient {
|
|
|
303
355
|
kind: "image";
|
|
304
356
|
state: "OPEN";
|
|
305
357
|
}>;
|
|
306
|
-
/** One
|
|
307
|
-
*
|
|
308
|
-
*
|
|
358
|
+
/** One server-owned submission. No client-side Photo/TTS orchestration and no
|
|
359
|
+
* fallback to a different workflow. A 202 receipt is acceptance, not readiness.
|
|
360
|
+
* Replay the identical manifest/files/key after an uncertain acknowledgement.
|
|
361
|
+
*/
|
|
362
|
+
submit(manifest: ArticleSubmissionManifest, operationKey: string, files?: {
|
|
363
|
+
slot: string;
|
|
364
|
+
file: Blob;
|
|
365
|
+
filename: string;
|
|
366
|
+
}[], signal?: AbortSignal): Promise<ArticleSubmissionReceipt>;
|
|
367
|
+
submission(submissionId: string, signal?: AbortSignal): Promise<ArticleSubmissionReceipt>;
|
|
309
368
|
submitArticle(input: {
|
|
310
369
|
operationKey: string;
|
|
311
370
|
authorProfileId: string;
|
|
312
371
|
document: ArticleDocument;
|
|
313
372
|
reviewPolicy?: string;
|
|
373
|
+
intent?: "draft" | "review" | "publish";
|
|
314
374
|
assets?: {
|
|
315
375
|
file: Blob;
|
|
316
376
|
filename: string;
|
|
317
377
|
metadata: AssetMetadata;
|
|
318
|
-
position:
|
|
378
|
+
position: "hero" | "gallery" | "attachment";
|
|
319
379
|
}[];
|
|
320
380
|
approveMedia?: boolean;
|
|
321
|
-
}): Promise<
|
|
381
|
+
}): Promise<ArticleSubmissionReceipt>;
|
|
322
382
|
}
|
|
323
383
|
|
|
324
|
-
export { type ArticleDocument, type ArticleEditCheckpoint, type ArticleEditInput, ArticleEditOperationError, type ArticleEditPatch, type ArticleMedia, type ArticleOperation, type ArticlePhotoInput, ArticlePhotoOperationError, type ArticleProcessing, ArticleWorkflowError, type ArticleWorkflowOptions, type AssetMetadata, type PhotoOperationCheckpoint, PublicationArticleClient, type PublishedCoverReplacement, articleEditHtmlMatches, articlePhotoHtmlMatches };
|
|
384
|
+
export { type ArticleDocument, type ArticleEditCheckpoint, type ArticleEditInput, ArticleEditOperationError, type ArticleEditPatch, type ArticleMedia, type ArticleOperation, type ArticlePhotoInput, ArticlePhotoOperationError, type ArticleProcessing, type ArticleSubmissionManifest, type ArticleSubmissionReceipt, ArticleWorkflowError, type ArticleWorkflowOptions, type AssetMetadata, type PhotoOperationCheckpoint, PublicationArticleClient, type PublishedCoverReplacement, articleEditHtmlMatches, articlePhotoHtmlMatches };
|