shared/types.ts (70 lines of code) (raw):
import { z } from 'zod';
const OptionalStringOrArrayAsArrayOfStrings = z
.union([z.string(), z.array(z.string())])
.optional()
.transform((val) => {
if (Array.isArray(val)) {
return val;
}
if (val === undefined || val.trim() === '') {
return [];
}
return [val];
});
const FingerpostFeedPayloadSchema = z.object({
uri: z.string().optional(),
'source-feed': z.string().optional(),
usn: z.string().optional(),
version: z.string().optional(),
type: z.string().optional(), // this is 'text' in every entry we have in the CODE db on 1st Nov 2024 (175553 entries at time of checking)
format: z.string().optional(), // "GOA-WIRES-NINJS" in all records
mimeType: z.string().optional(), // application/ninjs+json
firstVersion: z.string().optional(),
versionCreated: z.string().optional(),
dateTimeSent: z.string().optional(), // more than 1/2 of the time identical to versionCreated
originalUrn: z.string().optional(), // almost all entries have a string here, but not very unique (181803 values, only 53988 unique values)
slug: z.string().optional(), // fp set this
headline: z.string().optional(),
subhead: z.string().optional(),
byline: z.string().optional(),
priority: z.string().optional(), // 1-5 in all records
subjects: z
.object({
code: OptionalStringOrArrayAsArrayOfStrings,
})
.optional(),
destinations: z
.object({
code: OptionalStringOrArrayAsArrayOfStrings,
})
.optional(),
mediaCatCodes: z.string().optional(),
keywords: OptionalStringOrArrayAsArrayOfStrings,
organisation: z
.object({
symbols: OptionalStringOrArrayAsArrayOfStrings,
})
.optional(), // {"symbols": ""} {"symbols": [""]} in all records
tabVtxt: z.string().optional(), // always 'X' or null
status: z.string().optional(), // always one of: "" "usable" "canceled" "embargoed" "withheld"
usage: z.string().optional(),
ednote: z.string().optional(),
abstract: z.string().optional(), // only present in REUTERS and AAP items
language: z.string().optional(),
location: z.string().optional(), // just plain string
body_text: z.string().optional(),
copyrightHolder: z.string().optional(),
copyrightNotice: z.string().optional(),
});
export const IngestorInputBodySchema = FingerpostFeedPayloadSchema.extend({
originalContentText: z.string().optional(),
imageIds: z.array(z.string()).default([]),
});
const WireEntryContentSchema = IngestorInputBodySchema.omit({
keywords: true,
}).extend({
keywords: z.array(z.string()),
});
export type FingerpostFeedPayload = z.infer<typeof FingerpostFeedPayloadSchema>;
export type IngestorInputBody = z.infer<typeof IngestorInputBodySchema>;
export type WireEntryContent = z.infer<typeof WireEntryContentSchema>;