@rivi-software/radiotools-sdk 0.0.0-stage → 0.2.0

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/src/schemas.ts ADDED
@@ -0,0 +1,725 @@
1
+ // Customer API contract version 1.
2
+ // Radio-station shapes only: published reads, the public socket, preview, and playback ingest.
3
+ // Studio administration contracts are intentionally absent.
4
+ import { z } from "zod";
5
+
6
+ export const LISTENER_CONTRACT_VERSION = 1 as const;
7
+ export const ListenerContractVersion = z.literal(LISTENER_CONTRACT_VERSION);
8
+ export const PUBLIC_SOCKET_PROTOCOL = "radiotools.public.v1" as const;
9
+
10
+ export const RequestId = z.string().uuid();
11
+ export const ErrorCode = z.enum([
12
+ "UNAUTHENTICATED",
13
+ "FORBIDDEN",
14
+ "VALIDATION_ERROR",
15
+ "NOT_FOUND",
16
+ "METHOD_NOT_ALLOWED",
17
+ "REVISION_CONFLICT",
18
+ "IDEMPOTENCY_CONFLICT",
19
+ "RATE_LIMITED",
20
+ "PROVIDER_UNAVAILABLE",
21
+ "INTERNAL_ERROR",
22
+ ]);
23
+ export const ErrorResponse = z.object({
24
+ error: z.object({
25
+ code: ErrorCode,
26
+ message: z.string(),
27
+ requestId: RequestId,
28
+ issues: z.array(z.object({ path: z.string(), message: z.string() })).optional(),
29
+ }),
30
+ });
31
+ export const IngestErrorResponse = z.object({ error: z.string() });
32
+
33
+ export const Timezone = z
34
+ .string()
35
+ .min(1)
36
+ .max(100)
37
+ .refine((value) => {
38
+ if (/^[+-]/.test(value)) return false;
39
+ try {
40
+ new Intl.DateTimeFormat("en", { timeZone: value });
41
+ return true;
42
+ } catch {
43
+ return false;
44
+ }
45
+ }, "Use an IANA timezone");
46
+
47
+ const href = z.string().startsWith("/api/");
48
+
49
+ export const PublicStation = z.object({
50
+ contractVersion: ListenerContractVersion,
51
+ id: z.string().min(1).max(80),
52
+ slug: z.string().min(3).max(63),
53
+ name: z.string().min(1).max(120),
54
+ timezone: Timezone,
55
+ publishingLanguage: z.string().min(2).max(35),
56
+ resources: z.object({
57
+ schemas: href,
58
+ content: href.nullable(),
59
+ schedule: href.nullable(),
60
+ programs: href.nullable(),
61
+ broadcast: href.nullable(),
62
+ nowPlaying: href.nullable(),
63
+ trackHistory: href.nullable(),
64
+ socket: href.nullable(),
65
+ weather: href.nullable(),
66
+ traffic: href.nullable(),
67
+ media: href,
68
+ }),
69
+ delivery: z.object({
70
+ cache: z.literal("no-store"),
71
+ conditional: z.literal("etag"),
72
+ pollIntervalSeconds: z.number().int().positive(),
73
+ }),
74
+ });
75
+
76
+ const FIELD_KINDS = [
77
+ "text",
78
+ "rich_text",
79
+ "number",
80
+ "boolean",
81
+ "datetime",
82
+ "choices",
83
+ "media",
84
+ "content",
85
+ "group",
86
+ ] as const;
87
+
88
+ const SchemaKey = z
89
+ .string()
90
+ .regex(
91
+ /^[a-z][a-z0-9_]{0,47}$/,
92
+ "Use lowercase letters, digits and underscores; start with a letter.",
93
+ )
94
+ .refine(
95
+ (key) =>
96
+ ![
97
+ "constructor",
98
+ "prototype",
99
+ "__proto__",
100
+ "id",
101
+ "station_id",
102
+ "revision",
103
+ "schema_version",
104
+ "created_at",
105
+ "updated_at",
106
+ ].includes(key),
107
+ "Reserved identifier.",
108
+ );
109
+
110
+ const JsonValue = z.json();
111
+
112
+ export type DeliveryField = {
113
+ key: string;
114
+ label: string;
115
+ kind: (typeof FIELD_KINDS)[number];
116
+ required: boolean;
117
+ maxLength?: number;
118
+ min?: number;
119
+ max?: number;
120
+ choices?: { key: string; label: string }[];
121
+ targetType?: string;
122
+ maxItems?: number;
123
+ fields?: DeliveryField[];
124
+ };
125
+
126
+ function deliveryField(depth: number): z.ZodType<DeliveryField> {
127
+ return z
128
+ .object({
129
+ key: SchemaKey,
130
+ label: z.string(),
131
+ kind: z.enum(FIELD_KINDS),
132
+ required: z.boolean(),
133
+ maxLength: z.number().optional(),
134
+ min: z.number().optional(),
135
+ max: z.number().optional(),
136
+ choices: z.array(z.object({ key: SchemaKey, label: z.string() })).optional(),
137
+ targetType: SchemaKey.optional(),
138
+ maxItems: z.number().optional(),
139
+ fields:
140
+ depth < 3
141
+ ? z.array(deliveryField(depth + 1)).optional()
142
+ : z
143
+ .array(
144
+ z
145
+ .object({
146
+ key: SchemaKey,
147
+ label: z.string(),
148
+ kind: z.enum(FIELD_KINDS),
149
+ required: z.boolean(),
150
+ })
151
+ .strict(),
152
+ )
153
+ .max(0)
154
+ .optional(),
155
+ })
156
+ .strict();
157
+ }
158
+
159
+ export const PublicSchema = z.object({
160
+ key: SchemaKey,
161
+ label: z.string(),
162
+ version: z.number().int().positive(),
163
+ archived: z.boolean(),
164
+ fields: z.array(deliveryField(1)),
165
+ });
166
+ export const PublicSchemaList = z.object({
167
+ contractVersion: ListenerContractVersion,
168
+ types: z.array(PublicSchema).max(100),
169
+ });
170
+
171
+ const Span = z
172
+ .object({
173
+ text: z.string().max(20000),
174
+ marks: z
175
+ .array(z.enum(["bold", "italic", "underline", "strike"]))
176
+ .max(4)
177
+ .optional(),
178
+ })
179
+ .strict();
180
+ const inline = z.array(Span).max(1000);
181
+ const EditorialBlock = z.discriminatedUnion("type", [
182
+ z.object({ id: z.uuid(), type: z.literal("text"), content: inline }).strict(),
183
+ z
184
+ .object({
185
+ id: z.uuid(),
186
+ type: z.literal("heading"),
187
+ level: z.number().int().min(1).max(3),
188
+ content: inline,
189
+ })
190
+ .strict(),
191
+ z
192
+ .object({
193
+ id: z.uuid(),
194
+ type: z.literal("quote"),
195
+ content: inline,
196
+ attribution: z.string().max(300),
197
+ })
198
+ .strict(),
199
+ z
200
+ .object({
201
+ id: z.uuid(),
202
+ type: z.literal("image"),
203
+ assetId: z.uuid(),
204
+ alt: z.string().max(1000),
205
+ caption: z.string().max(1000),
206
+ })
207
+ .strict(),
208
+ z
209
+ .object({
210
+ id: z.uuid(),
211
+ type: z.literal("gallery"),
212
+ images: z
213
+ .array(z.object({ assetId: z.uuid(), alt: z.string().max(1000) }).strict())
214
+ .min(1)
215
+ .max(20),
216
+ })
217
+ .strict(),
218
+ z
219
+ .object({
220
+ id: z.uuid(),
221
+ type: z.literal("audio"),
222
+ assetId: z.uuid(),
223
+ caption: z.string().max(1000),
224
+ })
225
+ .strict(),
226
+ z
227
+ .object({
228
+ id: z.uuid(),
229
+ type: z.literal("video"),
230
+ assetId: z.uuid(),
231
+ caption: z.string().max(1000),
232
+ })
233
+ .strict(),
234
+ z
235
+ .object({
236
+ id: z.uuid(),
237
+ type: z.literal("embed"),
238
+ provider: z.enum(["youtube", "vimeo"]),
239
+ externalId: z.string().regex(/^[A-Za-z0-9_-]{1,80}$/),
240
+ title: z.string().min(1).max(300),
241
+ })
242
+ .strict(),
243
+ z
244
+ .object({
245
+ id: z.uuid(),
246
+ type: z.literal("related"),
247
+ recordIds: z.array(z.uuid()).min(1).max(20),
248
+ })
249
+ .strict(),
250
+ ]);
251
+ export const EditorialDocument = z
252
+ .object({
253
+ version: z.literal(1),
254
+ mode: z.enum(["document", "blocks"]),
255
+ blocks: z.array(EditorialBlock).max(100),
256
+ })
257
+ .strict()
258
+ .superRefine((doc, ctx) => {
259
+ if (new Set(doc.blocks.map((block) => block.id)).size !== doc.blocks.length) {
260
+ ctx.addIssue({ code: "custom", message: "Block IDs must be unique.", path: ["blocks"] });
261
+ }
262
+ if (new TextEncoder().encode(JSON.stringify(doc)).byteLength > 128 * 1024) {
263
+ ctx.addIssue({ code: "custom", message: "Document exceeds 128 KiB." });
264
+ }
265
+ });
266
+
267
+ export const PublicMedia = z.object({
268
+ assetId: z.uuid(),
269
+ versionId: z.uuid(),
270
+ kind: z.enum(["image", "audio", "video"]),
271
+ url: z.string(),
272
+ credits: z.string(),
273
+ mime: z.string(),
274
+ });
275
+ export const PublishedRecord = z.object({
276
+ contractVersion: ListenerContractVersion,
277
+ id: z.uuid(),
278
+ publicationId: z.uuid(),
279
+ revision: z.number().int().positive(),
280
+ type: SchemaKey,
281
+ schemaVersion: z.number().int().positive(),
282
+ publishedAt: z.string(),
283
+ values: z.record(z.string(), JsonValue),
284
+ document: EditorialDocument,
285
+ media: z.array(PublicMedia).max(100),
286
+ });
287
+ export const PublishedCollection = z.object({
288
+ contractVersion: ListenerContractVersion,
289
+ type: SchemaKey,
290
+ records: z.array(PublishedRecord).max(50),
291
+ nextCursor: z.uuid().nullable(),
292
+ page: z.object({
293
+ limit: z.number().int().min(1).max(50),
294
+ nextCursor: z.uuid().nullable(),
295
+ }),
296
+ });
297
+ export const PreviewInput = z
298
+ .object({
299
+ stationId: z.string().min(1).max(80),
300
+ recordId: z.uuid(),
301
+ revision: z.number().int().positive(),
302
+ audience: z.string().min(1).max(100),
303
+ })
304
+ .strict();
305
+ export const RevisionPreview = z.object({
306
+ media: z.array(PublicMedia).max(100),
307
+ id: z.uuid(),
308
+ revision: z.number().int().positive(),
309
+ schemaVersion: z.number().int().positive(),
310
+ values: z.record(z.string(), JsonValue),
311
+ document: EditorialDocument,
312
+ });
313
+
314
+ const LocalDate = z.iso.date();
315
+ export const PublicBroadcast = z.object({
316
+ id: z.uuid(),
317
+ title: z.string(),
318
+ startsAt: z.iso.datetime(),
319
+ endsAt: z.iso.datetime(),
320
+ timezone: Timezone,
321
+ localDate: LocalDate,
322
+ resolution: z.string(),
323
+ status: z.enum(["scheduled", "cancelled", "live", "completed"]),
324
+ cancellationReason: z.string(),
325
+ repeatOfId: z.uuid().nullable(),
326
+ program: z.object({ id: z.uuid(), title: z.string(), profileId: z.uuid().nullable() }),
327
+ presenters: z.array(z.object({ id: z.uuid(), title: z.string() })),
328
+ });
329
+ export const PublicSchedule = z.object({
330
+ contractVersion: ListenerContractVersion,
331
+ timezone: Timezone,
332
+ broadcasts: z.array(PublicBroadcast),
333
+ truncated: z.boolean(),
334
+ });
335
+ export const PublicProgram = z.object({
336
+ id: z.uuid(),
337
+ title: z.string(),
338
+ profileId: z.uuid().nullable(),
339
+ });
340
+ export const PublicPrograms = z.object({
341
+ contractVersion: ListenerContractVersion,
342
+ timezone: Timezone,
343
+ fromDate: LocalDate,
344
+ throughDate: LocalDate,
345
+ programs: z.array(PublicProgram).max(500),
346
+ truncated: z.boolean(),
347
+ });
348
+ export const PublicCurrentBroadcast = z.object({
349
+ contractVersion: ListenerContractVersion,
350
+ timezone: Timezone,
351
+ asOf: z.iso.datetime(),
352
+ broadcast: PublicBroadcast.nullable(),
353
+ });
354
+
355
+ export const PlaybackArtworkUrl = z
356
+ .string()
357
+ .trim()
358
+ .min(1)
359
+ .max(2048)
360
+ .url()
361
+ .refine((value) => {
362
+ const url = new URL(value);
363
+ return url.protocol === "https:" && !url.username && !url.password;
364
+ }, "artworkUrl must be an https URL");
365
+
366
+ const playbackDuration = z.number().int().min(0).max(86400).nullable();
367
+
368
+ /** Wire JSON for `POST /api/v1/integrations/playback`. */
369
+ export const PlaybackObservationIngestBody = z
370
+ .object({
371
+ eventId: z.string().trim().min(1).max(100).optional(),
372
+ observedAt: z.number().int().min(0).max(8_640_000_000_000_000).optional(),
373
+ artist: z.string().trim().min(1).max(500).optional(),
374
+ title: z.string().trim().min(1).max(500).optional(),
375
+ streamTitle: z.string().trim().min(1).max(1001).optional(),
376
+ album: z.string().trim().min(1).max(500).optional(),
377
+ sourceTrackId: z.string().trim().min(1).max(120).optional(),
378
+ artworkUrl: PlaybackArtworkUrl.optional(),
379
+ durationSeconds: playbackDuration.optional(),
380
+ duration: playbackDuration.optional(),
381
+ isPlaying: z.boolean().default(true),
382
+ })
383
+ .strict()
384
+ .superRefine((value, ctx) => {
385
+ const hasArtist = value.artist !== undefined;
386
+ const hasTitle = value.title !== undefined;
387
+ if (hasArtist !== hasTitle) {
388
+ ctx.addIssue({
389
+ code: "custom",
390
+ message: "artist and title must be supplied together",
391
+ path: hasArtist ? ["title"] : ["artist"],
392
+ });
393
+ }
394
+ if (!hasArtist && value.streamTitle === undefined) {
395
+ ctx.addIssue({
396
+ code: "custom",
397
+ message: "Provide artist and title or streamTitle",
398
+ path: ["streamTitle"],
399
+ });
400
+ }
401
+ if (hasArtist && value.streamTitle !== undefined) {
402
+ ctx.addIssue({
403
+ code: "custom",
404
+ message: "Use either artist and title or streamTitle",
405
+ path: ["streamTitle"],
406
+ });
407
+ }
408
+ if (
409
+ value.duration !== undefined &&
410
+ value.durationSeconds !== undefined &&
411
+ value.duration !== value.durationSeconds
412
+ ) {
413
+ ctx.addIssue({
414
+ code: "custom",
415
+ message: "duration and durationSeconds must match when both are set",
416
+ path: ["duration"],
417
+ });
418
+ }
419
+ });
420
+
421
+ export const PlaybackObservationResult = z.object({
422
+ accepted: z.boolean(),
423
+ outcome: z.enum([
424
+ "no-live-broadcast",
425
+ "on-air-not-following-rundown",
426
+ "not-playing",
427
+ "rejected",
428
+ "recorded",
429
+ "matched",
430
+ "uncertain",
431
+ "ignored",
432
+ ]),
433
+ });
434
+
435
+ const PlaybackFreshnessStatus = z.enum(["fresh", "stale", "empty"]);
436
+ const PublicPlaybackTrack = z
437
+ .object({
438
+ artist: z.string(),
439
+ title: z.string(),
440
+ album: z.string().nullable(),
441
+ durationSeconds: z.number().int().nonnegative().nullable(),
442
+ artworkUrl: PlaybackArtworkUrl.optional(),
443
+ isPlaying: z.boolean(),
444
+ observedAt: z.string().datetime(),
445
+ receivedAt: z.string().datetime(),
446
+ })
447
+ .strict();
448
+ const PlaybackFreshness = z
449
+ .object({
450
+ status: PlaybackFreshnessStatus,
451
+ thresholdSeconds: z.number().int().positive(),
452
+ ageSeconds: z.number().int().nonnegative().nullable(),
453
+ observedAt: z.string().datetime().nullable(),
454
+ receivedAt: z.string().datetime().nullable(),
455
+ })
456
+ .strict();
457
+ export const PublicNowPlaying = z
458
+ .object({
459
+ contractVersion: ListenerContractVersion,
460
+ stationId: z.string(),
461
+ timezone: Timezone,
462
+ track: PublicPlaybackTrack.nullable(),
463
+ elapsedSeconds: z.number().int().nonnegative().nullable(),
464
+ endsAt: z.string().datetime().nullable(),
465
+ freshness: PlaybackFreshness,
466
+ })
467
+ .strict();
468
+ const PublicHistoryEntry = PublicPlaybackTrack.extend({
469
+ id: z.string().min(1).max(80),
470
+ });
471
+ export const PublicTrackHistory = z
472
+ .object({
473
+ contractVersion: ListenerContractVersion,
474
+ stationId: z.string(),
475
+ items: z.array(PublicHistoryEntry),
476
+ nextCursor: z.string().max(512).nullable(),
477
+ })
478
+ .strict();
479
+
480
+ const WeatherConditionCode = z.enum([
481
+ "Clear",
482
+ "MostlyClear",
483
+ "PartlyCloudy",
484
+ "Cloudy",
485
+ "Fog",
486
+ "Drizzle",
487
+ "FreezingDrizzle",
488
+ "Rain",
489
+ "HeavyRain",
490
+ "FreezingRain",
491
+ "Snow",
492
+ "HeavySnow",
493
+ "Flurries",
494
+ "Showers",
495
+ "SnowShowers",
496
+ "Thunderstorm",
497
+ "SevereThunderstorm",
498
+ ]);
499
+ const WeatherRegionId = z.string().regex(/^[a-z]{2}:[a-z0-9][a-z0-9_-]{0,62}$/);
500
+ const WeatherCoverageArea = z
501
+ .object({
502
+ id: WeatherRegionId,
503
+ name: z.string(),
504
+ countryCode: z.string().regex(/^[a-z]{2}$/),
505
+ countryName: z.string(),
506
+ latitude: z.number(),
507
+ longitude: z.number(),
508
+ timezone: Timezone,
509
+ })
510
+ .strict();
511
+ const WeatherCurrent = z
512
+ .object({
513
+ asOf: z.string().datetime(),
514
+ conditionCode: WeatherConditionCode,
515
+ daylight: z.boolean(),
516
+ precipitation: z.number(),
517
+ temperature: z.number(),
518
+ temperatureApparent: z.number(),
519
+ uvIndex: z.number(),
520
+ windDirection: z.number(),
521
+ windGust: z.number(),
522
+ windSpeed: z.number(),
523
+ })
524
+ .strict();
525
+ const WeatherHour = z
526
+ .object({
527
+ forecastStart: z.string().datetime(),
528
+ conditionCode: WeatherConditionCode,
529
+ daylight: z.boolean(),
530
+ precipitation: z.number(),
531
+ precipitationChance: z.number().min(0).max(1),
532
+ temperature: z.number(),
533
+ temperatureApparent: z.number(),
534
+ windDirection: z.number(),
535
+ windGust: z.number(),
536
+ windSpeed: z.number(),
537
+ })
538
+ .strict();
539
+ const WeatherDay = z
540
+ .object({
541
+ forecastStart: z.string().datetime(),
542
+ conditionCode: WeatherConditionCode,
543
+ precipitationAmount: z.number(),
544
+ precipitationChance: z.number().min(0).max(1),
545
+ sunriseTime: z.string().datetime(),
546
+ sunsetTime: z.string().datetime(),
547
+ temperatureMax: z.number(),
548
+ temperatureMin: z.number(),
549
+ uvIndex: z.number(),
550
+ windDirection: z.number(),
551
+ windGust: z.number(),
552
+ windSpeed: z.number(),
553
+ })
554
+ .strict();
555
+ const WeatherFreshness = z
556
+ .object({
557
+ status: z.enum(["fresh", "stale", "unavailable", "empty"]),
558
+ thresholdSeconds: z.number().int().positive(),
559
+ ageSeconds: z.number().int().nonnegative().nullable(),
560
+ fetchedAt: z.string().datetime().nullable(),
561
+ observedAt: z.string().datetime().nullable(),
562
+ lastError: z.string().nullable(),
563
+ })
564
+ .strict();
565
+ const WeatherSnapshot = z
566
+ .object({
567
+ source: z.literal("open-meteo"),
568
+ coverage: WeatherCoverageArea,
569
+ freshness: WeatherFreshness,
570
+ current: WeatherCurrent.nullable(),
571
+ hourly: z.array(WeatherHour),
572
+ daily: z.array(WeatherDay),
573
+ })
574
+ .strict();
575
+ export const WeatherPublic = z
576
+ .object({
577
+ contractVersion: ListenerContractVersion,
578
+ stationId: z.string(),
579
+ timezone: Timezone,
580
+ snapshots: z.array(WeatherSnapshot),
581
+ })
582
+ .strict();
583
+
584
+ const TrafficRoadId = z.string().regex(/^nl:[an](?:[0-9]{1,3}|-all)$/, "Use nl:{road} watch ids");
585
+ const TrafficWatchRoad = z
586
+ .object({
587
+ id: TrafficRoadId,
588
+ code: z.string().min(2).max(8),
589
+ name: z.string(),
590
+ })
591
+ .strict();
592
+ const TrafficIncident = z
593
+ .object({
594
+ id: z.string(),
595
+ road: z.string(),
596
+ type: z.enum(["jam", "roadworks", "closure", "speed_check"]),
597
+ direction: z.enum([
598
+ "northbound",
599
+ "southbound",
600
+ "eastbound",
601
+ "westbound",
602
+ "both",
603
+ "unknown",
604
+ ]),
605
+ from: z.string(),
606
+ to: z.string(),
607
+ lengthMeters: z.number().int().nonnegative().nullable(),
608
+ delaySeconds: z.number().int().nonnegative().nullable(),
609
+ summary: z.string(),
610
+ updatedAt: z.string().datetime(),
611
+ })
612
+ .strict();
613
+ const TrafficSnapshot = z
614
+ .object({
615
+ source: z.literal("anwb"),
616
+ watch: z.object({ roads: z.array(TrafficWatchRoad) }).strict(),
617
+ freshness: z
618
+ .object({
619
+ status: z.enum(["fresh", "stale", "unavailable", "empty"]),
620
+ thresholdSeconds: z.number().int().positive(),
621
+ ageSeconds: z.number().int().nonnegative().nullable(),
622
+ fetchedAt: z.string().datetime().nullable(),
623
+ observedAt: z.string().datetime().nullable(),
624
+ lastError: z.string().nullable(),
625
+ })
626
+ .strict(),
627
+ incidents: z.array(TrafficIncident),
628
+ totals: z
629
+ .object({
630
+ incidentCount: z.number().int().nonnegative(),
631
+ jamDistanceMeters: z.number().int().nonnegative(),
632
+ delaySeconds: z.number().int().nonnegative(),
633
+ })
634
+ .strict(),
635
+ })
636
+ .strict();
637
+ export const TrafficPublic = z
638
+ .object({
639
+ contractVersion: ListenerContractVersion,
640
+ stationId: z.string(),
641
+ snapshot: TrafficSnapshot,
642
+ })
643
+ .strict();
644
+
645
+ export const PublicOnAir = z
646
+ .object({
647
+ broadcastId: z.uuid(),
648
+ status: z.enum(["scheduled", "live", "completed"]),
649
+ activeItemId: z.uuid().nullable(),
650
+ observation: z
651
+ .object({
652
+ observedAt: z.number(),
653
+ itemId: z.uuid().nullable(),
654
+ durationSeconds: z.number().nullable(),
655
+ outcome: z.enum(["matched", "uncertain", "ignored"]),
656
+ })
657
+ .strict()
658
+ .nullable(),
659
+ })
660
+ .strict();
661
+
662
+ const socketEnvelope = {
663
+ protocol: z.literal(PUBLIC_SOCKET_PROTOCOL),
664
+ stationId: z.string().min(1).max(80),
665
+ };
666
+ export const PublicSocketEvent = z.discriminatedUnion("type", [
667
+ z.object({ ...socketEnvelope, type: z.literal("schedule"), data: PublicSchedule }).strict(),
668
+ z.object({ ...socketEnvelope, type: z.literal("on-air"), data: PublicOnAir.nullable() }).strict(),
669
+ z
670
+ .object({ ...socketEnvelope, type: z.literal("now-playing"), data: PublicNowPlaying })
671
+ .strict(),
672
+ z
673
+ .object({
674
+ ...socketEnvelope,
675
+ type: z.literal("content"),
676
+ recordId: z.uuid(),
677
+ data: PublishedRecord.nullable(),
678
+ })
679
+ .strict(),
680
+ z.object({ ...socketEnvelope, type: z.literal("preview"), data: RevisionPreview }).strict(),
681
+ ]);
682
+
683
+ export const sdkSchemas = {
684
+ ErrorResponse,
685
+ IngestErrorResponse,
686
+ PublicStation,
687
+ PublicSchema,
688
+ PublicSchemaList,
689
+ PublishedRecord,
690
+ PublishedCollection,
691
+ PublicSchedule,
692
+ PublicPrograms,
693
+ PublicCurrentBroadcast,
694
+ WeatherPublic,
695
+ TrafficPublic,
696
+ PreviewInput,
697
+ RevisionPreview,
698
+ PlaybackObservationIngestBody,
699
+ PlaybackObservationResult,
700
+ PublicNowPlaying,
701
+ PublicTrackHistory,
702
+ PublicOnAir,
703
+ PublicSocketEvent,
704
+ } as const;
705
+
706
+ export type SdkErrorResponse = z.infer<typeof ErrorResponse>;
707
+ export type SdkErrorCode = z.infer<typeof ErrorCode>;
708
+ export type SdkPublicStation = z.infer<typeof PublicStation>;
709
+ export type SdkPublicSchema = z.infer<typeof PublicSchema>;
710
+ export type SdkPublicSchemaList = z.infer<typeof PublicSchemaList>;
711
+ export type SdkPublishedRecord = z.infer<typeof PublishedRecord>;
712
+ export type SdkPublishedCollection = z.infer<typeof PublishedCollection>;
713
+ export type SdkPublicSchedule = z.infer<typeof PublicSchedule>;
714
+ export type SdkPublicPrograms = z.infer<typeof PublicPrograms>;
715
+ export type SdkPublicCurrentBroadcast = z.infer<typeof PublicCurrentBroadcast>;
716
+ export type SdkWeatherPublic = z.infer<typeof WeatherPublic>;
717
+ export type SdkTrafficPublic = z.infer<typeof TrafficPublic>;
718
+ export type SdkPreviewInput = z.infer<typeof PreviewInput>;
719
+ export type SdkRevisionPreview = z.infer<typeof RevisionPreview>;
720
+ export type SdkPlaybackObservation = z.input<typeof PlaybackObservationIngestBody>;
721
+ export type SdkPlaybackObservationResult = z.infer<typeof PlaybackObservationResult>;
722
+ export type SdkPublicNowPlaying = z.infer<typeof PublicNowPlaying>;
723
+ export type SdkPublicTrackHistory = z.infer<typeof PublicTrackHistory>;
724
+ export type SdkPublicOnAir = z.infer<typeof PublicOnAir>;
725
+ export type SdkPublicSocketEvent = z.infer<typeof PublicSocketEvent>;