@echovisionlab/geul-common 0.4.0 → 0.6.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.
@@ -1,191 +1,4 @@
1
- import { z } from "zod";
2
-
3
- export type RuntimeEntityType =
4
- | "post"
5
- | "page"
6
- | "work"
7
- | "artist"
8
- | "label"
9
- | "release"
10
- | "series"
11
- | "form"
12
- | "program_event"
13
- | "menu"
14
- | "site"
15
- | "site_setting"
16
- | "email_template"
17
- | "email_layout"
18
- | "campaign"
19
- | "privacy"
20
- | "terms";
21
-
22
- export type TranslationLifecycleRuntimeStatus = "failed";
23
-
24
- export type MediaProcessingRuntimeStatus = "processing" | "ready" | "failed";
25
-
26
- export type OgGenerationRuntimeEntityType =
27
- | "post"
28
- | "page"
29
- | "work"
30
- | "artist"
31
- | "label"
32
- | "release"
33
- | "series"
34
- | "form"
35
- | "site"
36
- | "privacy"
37
- | "terms";
38
-
39
- export type OgGenerationRuntimeStatus =
40
- "queued" | "processing" | "ready" | "failed" | "superseded" | "cancelled";
41
-
42
- export function isOgGenerationRuntimeTerminal(
43
- status: OgGenerationRuntimeStatus,
44
- ): boolean {
45
- return (
46
- status === "ready" ||
47
- status === "failed" ||
48
- status === "superseded" ||
49
- status === "cancelled"
50
- );
51
- }
52
-
53
- export type FileIngestRuntimeSource = "upload" | "embed";
54
-
55
- export type FileIngestRuntimeStage =
56
- | "uploading"
57
- | "downloading"
58
- | "finalized"
59
- | "attached"
60
- | "completed"
61
- | "expired"
62
- | "failed"
63
- | "unknown";
64
-
65
- export interface RuntimeEventEnvelope<K extends string, P> {
66
- version: 1;
67
- kind: K;
68
- entityType: RuntimeEntityType;
69
- entityId: string;
70
- locale?: string;
71
- correlationId?: string;
72
- sequence?: number;
73
- timestampMs: number;
74
- payload: P;
75
- }
76
-
77
- export interface OgGenerationLifecycleRuntimePayload {
78
- generationId: string;
79
- runId: string;
80
- status: OgGenerationRuntimeStatus;
81
- assetId?: string;
82
- assetUrl?: string;
83
- errorCode?: string;
84
- error?: string;
85
- replacementGenerationId?: string;
86
- }
87
-
88
- export interface RenderCompleteRuntimePayload {
89
- contentHash: string;
90
- success: boolean;
91
- error?: string;
92
- }
93
-
94
- export interface MediaProcessingRuntimeOutputs {
95
- spectrogramAssetId?: string;
96
- thumbnailAssetId?: string;
97
- hlsGenerationId?: string;
98
- durationSeconds?: number;
99
- waveformAssetId?: string;
100
- }
101
-
102
- export interface MediaProcessingLifecycleRuntimePayload {
103
- fileId: string;
104
- slotId?: string;
105
- attemptId?: string;
106
- status: MediaProcessingRuntimeStatus;
107
- percentage?: number;
108
- outputs?: MediaProcessingRuntimeOutputs;
109
- error?: string;
110
- trackId?: string;
111
- releaseId?: string;
112
- }
113
-
114
- export function isMediaProcessingRuntimeReady(payload: {
115
- status?: MediaProcessingRuntimeStatus | null;
116
- }): boolean {
117
- return payload.status === "ready";
118
- }
119
-
120
- export interface FileIngestLifecycleRuntimePayload {
121
- fileId: string;
122
- uploadId?: string;
123
- mediaKind?: string;
124
- slotId?: string;
125
- attemptId?: string;
126
- source: FileIngestRuntimeSource;
127
- stage: FileIngestRuntimeStage;
128
- progress: number;
129
- bytesCompleted?: number;
130
- bytesTotal?: number;
131
- fileName?: string;
132
- mimeType?: string;
133
- fileSize?: number;
134
- error?: string;
135
- trackId?: string;
136
- releaseId?: string;
137
- }
138
-
139
- export interface TranslationLifecycleRuntimePayload {
140
- jobId: string;
141
- targetLocale: string;
142
- status: TranslationLifecycleRuntimeStatus;
143
- error?: string;
144
- }
145
-
146
- export interface RuntimeErrorPayload {
147
- code: string;
148
- message: string;
149
- retryable?: boolean;
150
- source?: "backend" | "collab" | "worker";
151
- details?: Record<string, string | number | boolean | null>;
152
- }
153
-
154
- export interface OgGenerationLifecycleRuntimeEvent extends RuntimeEventEnvelope<
155
- "og.lifecycle",
156
- OgGenerationLifecycleRuntimePayload
157
- > {
158
- entityType: OgGenerationRuntimeEntityType;
159
- correlationId: string;
160
- }
161
- export type RenderCompleteRuntimeEvent = RuntimeEventEnvelope<
162
- "render.complete",
163
- RenderCompleteRuntimePayload
164
- >;
165
- export type MediaProcessingLifecycleRuntimeEvent = RuntimeEventEnvelope<
166
- "media.processing.lifecycle",
167
- MediaProcessingLifecycleRuntimePayload
168
- >;
169
- export type FileIngestLifecycleRuntimeEvent = RuntimeEventEnvelope<
170
- "file.ingest.lifecycle",
171
- FileIngestLifecycleRuntimePayload
172
- >;
173
- export type TranslationLifecycleRuntimeEvent = RuntimeEventEnvelope<
174
- "translation.lifecycle",
175
- TranslationLifecycleRuntimePayload
176
- >;
177
- export type RuntimeErrorEvent = RuntimeEventEnvelope<
178
- "runtime.error",
179
- RuntimeErrorPayload
180
- >;
181
-
182
- export type EditorRuntimeEvent =
183
- | OgGenerationLifecycleRuntimeEvent
184
- | RenderCompleteRuntimeEvent
185
- | MediaProcessingLifecycleRuntimeEvent
186
- | FileIngestLifecycleRuntimeEvent
187
- | TranslationLifecycleRuntimeEvent
188
- | RuntimeErrorEvent;
1
+ import * as z from "zod/mini";
189
2
 
190
3
  export const runtimeEntityTypeSchema = z.enum([
191
4
  "post",
@@ -237,311 +50,332 @@ export const ogGenerationRuntimeStatusSchema = z.enum([
237
50
  ]);
238
51
 
239
52
  export const ogGenerationLifecycleRuntimePayloadSchema = z
240
- .object({
241
- generationId: z.string().min(1),
242
- runId: z.string().min(1),
53
+ .strictObject({
54
+ generationId: z.string().check(z.minLength(1)),
55
+ runId: z.string().check(z.minLength(1)),
243
56
  status: ogGenerationRuntimeStatusSchema,
244
- assetId: z.string().min(1).optional(),
245
- assetUrl: z.string().min(1).optional(),
246
- errorCode: z.string().min(1).optional(),
247
- error: z.string().min(1).optional(),
248
- replacementGenerationId: z.string().min(1).optional(),
57
+ assetId: z.optional(z.string().check(z.minLength(1))),
58
+ assetUrl: z.optional(z.string().check(z.minLength(1))),
59
+ errorCode: z.optional(z.string().check(z.minLength(1))),
60
+ error: z.optional(z.string().check(z.minLength(1))),
61
+ replacementGenerationId: z.optional(z.string().check(z.minLength(1))),
249
62
  })
250
- .strict()
251
- .superRefine((payload, ctx) => {
252
- if (payload.status === "ready" && !payload.assetId) {
253
- ctx.addIssue({
254
- code: z.ZodIssueCode.custom,
255
- path: ["assetId"],
256
- message: "ready OG lifecycle requires assetId",
257
- });
258
- }
259
- if (payload.status === "failed" && !payload.error) {
260
- ctx.addIssue({
261
- code: z.ZodIssueCode.custom,
262
- path: ["error"],
263
- message: "failed OG lifecycle requires error",
264
- });
265
- }
266
- if (payload.status === "superseded" && !payload.replacementGenerationId) {
267
- ctx.addIssue({
268
- code: z.ZodIssueCode.custom,
269
- path: ["replacementGenerationId"],
270
- message: "superseded OG lifecycle requires replacementGenerationId",
271
- });
272
- }
273
- if (payload.status !== "ready" && (payload.assetId || payload.assetUrl)) {
274
- ctx.addIssue({
275
- code: z.ZodIssueCode.custom,
276
- path: ["assetId"],
277
- message: "only ready OG lifecycle may include an asset",
278
- });
279
- }
280
- if (payload.status !== "superseded" && payload.replacementGenerationId) {
281
- ctx.addIssue({
282
- code: z.ZodIssueCode.custom,
283
- path: ["replacementGenerationId"],
284
- message: "only superseded OG lifecycle may include a replacement",
285
- });
286
- }
287
- });
63
+ .check(
64
+ z.superRefine((payload, ctx) => {
65
+ if (payload.status === "ready" && !payload.assetId) {
66
+ ctx.addIssue({
67
+ code: "custom",
68
+ path: ["assetId"],
69
+ message: "ready OG lifecycle requires assetId",
70
+ });
71
+ }
72
+ if (payload.status === "failed" && !payload.error) {
73
+ ctx.addIssue({
74
+ code: "custom",
75
+ path: ["error"],
76
+ message: "failed OG lifecycle requires error",
77
+ });
78
+ }
79
+ if (payload.status === "superseded" && !payload.replacementGenerationId) {
80
+ ctx.addIssue({
81
+ code: "custom",
82
+ path: ["replacementGenerationId"],
83
+ message: "superseded OG lifecycle requires replacementGenerationId",
84
+ });
85
+ }
86
+ if (payload.status !== "ready" && (payload.assetId || payload.assetUrl)) {
87
+ ctx.addIssue({
88
+ code: "custom",
89
+ path: ["assetId"],
90
+ message: "only ready OG lifecycle may include an asset",
91
+ });
92
+ }
93
+ if (payload.status !== "superseded" && payload.replacementGenerationId) {
94
+ ctx.addIssue({
95
+ code: "custom",
96
+ path: ["replacementGenerationId"],
97
+ message: "only superseded OG lifecycle may include a replacement",
98
+ });
99
+ }
100
+ }),
101
+ );
288
102
 
289
- export const mediaProcessingRuntimeOutputsSchema = z
290
- .object({
291
- spectrogramAssetId: z.string().optional(),
292
- thumbnailAssetId: z.string().optional(),
293
- hlsGenerationId: z.string().optional(),
294
- durationSeconds: z.number().int().nonnegative().optional(),
295
- waveformAssetId: z.string().optional(),
296
- })
297
- .strict();
103
+ export const mediaProcessingRuntimeOutputsSchema = z.strictObject({
104
+ spectrogramAssetId: z.optional(z.string()),
105
+ thumbnailAssetId: z.optional(z.string()),
106
+ hlsGenerationId: z.optional(z.string()),
107
+ durationSeconds: z.optional(z.int().check(z.nonnegative())),
108
+ waveformAssetId: z.optional(z.string()),
109
+ });
298
110
 
299
111
  export const mediaProcessingLifecycleRuntimePayloadSchema = z
300
- .object({
301
- fileId: z.string().min(1),
302
- slotId: z.string().optional(),
303
- attemptId: z.string().optional(),
112
+ .strictObject({
113
+ fileId: z.string().check(z.minLength(1)),
114
+ slotId: z.optional(z.string()),
115
+ attemptId: z.optional(z.string()),
304
116
  status: mediaProcessingRuntimeStatusSchema,
305
- percentage: z.number().int().min(0).max(100).optional(),
306
- outputs: mediaProcessingRuntimeOutputsSchema.optional(),
307
- error: z.string().optional(),
308
- trackId: z.string().optional(),
309
- releaseId: z.string().optional(),
117
+ percentage: z.optional(z.int().check(z.minimum(0), z.maximum(100))),
118
+ outputs: z.optional(mediaProcessingRuntimeOutputsSchema),
119
+ error: z.optional(z.string()),
120
+ trackId: z.optional(z.string()),
121
+ releaseId: z.optional(z.string()),
310
122
  })
311
- .strict()
312
- .superRefine((payload, ctx) => {
313
- if (
314
- payload.status === "processing" &&
315
- typeof payload.percentage !== "number"
316
- ) {
317
- ctx.addIssue({
318
- code: z.ZodIssueCode.custom,
319
- path: ["percentage"],
320
- message: "processing media lifecycle requires percentage",
321
- });
322
- }
323
- if (payload.status === "processing" && payload.outputs) {
324
- ctx.addIssue({
325
- code: z.ZodIssueCode.custom,
326
- path: ["outputs"],
327
- message: "processing media lifecycle must not include ready outputs",
328
- });
329
- }
330
- if (payload.status === "ready" && !payload.outputs) {
331
- ctx.addIssue({
332
- code: z.ZodIssueCode.custom,
333
- path: ["outputs"],
334
- message: "ready media lifecycle requires outputs",
335
- });
336
- }
337
- if (payload.status === "ready" && typeof payload.percentage === "number") {
338
- ctx.addIssue({
339
- code: z.ZodIssueCode.custom,
340
- path: ["percentage"],
341
- message: "ready media lifecycle must not include processing percentage",
342
- });
343
- }
344
- if (payload.status === "failed" && payload.outputs) {
345
- ctx.addIssue({
346
- code: z.ZodIssueCode.custom,
347
- path: ["outputs"],
348
- message: "failed media lifecycle must not include ready outputs",
349
- });
350
- }
351
- if (payload.status === "failed" && typeof payload.percentage === "number") {
352
- ctx.addIssue({
353
- code: z.ZodIssueCode.custom,
354
- path: ["percentage"],
355
- message:
356
- "failed media lifecycle must not include processing percentage",
357
- });
358
- }
359
- });
360
-
361
- export const fileIngestLifecycleRuntimePayloadSchema = z
362
- .object({
363
- fileId: z.string().min(1),
364
- uploadId: z.string().optional(),
365
- mediaKind: z.string().optional(),
366
- slotId: z.string().optional(),
367
- attemptId: z.string().optional(),
368
- source: z.enum(["upload", "embed"]),
369
- stage: z.enum([
370
- "uploading",
371
- "downloading",
372
- "finalized",
373
- "attached",
374
- "completed",
375
- "expired",
376
- "failed",
377
- "unknown",
378
- ]),
379
- progress: z.number().int().min(0).max(100),
380
- bytesCompleted: z.number().int().nonnegative().optional(),
381
- bytesTotal: z.number().int().nonnegative().optional(),
382
- fileName: z.string().optional(),
383
- mimeType: z.string().optional(),
384
- fileSize: z.number().int().nonnegative().optional(),
385
- error: z.string().optional(),
386
- trackId: z.string().optional(),
387
- releaseId: z.string().optional(),
388
- })
389
- .strict();
123
+ .check(
124
+ z.superRefine((payload, ctx) => {
125
+ if (
126
+ payload.status === "processing" &&
127
+ typeof payload.percentage !== "number"
128
+ ) {
129
+ ctx.addIssue({
130
+ code: "custom",
131
+ path: ["percentage"],
132
+ message: "processing media lifecycle requires percentage",
133
+ });
134
+ }
135
+ if (payload.status === "processing" && payload.outputs) {
136
+ ctx.addIssue({
137
+ code: "custom",
138
+ path: ["outputs"],
139
+ message: "processing media lifecycle must not include ready outputs",
140
+ });
141
+ }
142
+ if (payload.status === "ready" && !payload.outputs) {
143
+ ctx.addIssue({
144
+ code: "custom",
145
+ path: ["outputs"],
146
+ message: "ready media lifecycle requires outputs",
147
+ });
148
+ }
149
+ if (
150
+ payload.status === "ready" &&
151
+ typeof payload.percentage === "number"
152
+ ) {
153
+ ctx.addIssue({
154
+ code: "custom",
155
+ path: ["percentage"],
156
+ message:
157
+ "ready media lifecycle must not include processing percentage",
158
+ });
159
+ }
160
+ if (payload.status === "failed" && payload.outputs) {
161
+ ctx.addIssue({
162
+ code: "custom",
163
+ path: ["outputs"],
164
+ message: "failed media lifecycle must not include ready outputs",
165
+ });
166
+ }
167
+ if (
168
+ payload.status === "failed" &&
169
+ typeof payload.percentage === "number"
170
+ ) {
171
+ ctx.addIssue({
172
+ code: "custom",
173
+ path: ["percentage"],
174
+ message:
175
+ "failed media lifecycle must not include processing percentage",
176
+ });
177
+ }
178
+ }),
179
+ );
390
180
 
391
- const localeScopedOgEntityTypes = new Set<OgGenerationRuntimeEntityType>([
392
- "post",
393
- "page",
394
- "form",
395
- "privacy",
396
- "terms",
181
+ export const fileIngestRuntimeSourceSchema = z.enum(["upload", "embed"]);
182
+ export const fileIngestRuntimeStageSchema = z.enum([
183
+ "uploading",
184
+ "downloading",
185
+ "finalized",
186
+ "attached",
187
+ "completed",
188
+ "expired",
189
+ "failed",
190
+ "unknown",
397
191
  ]);
398
192
 
399
- const ogGenerationLifecycleRuntimeEventSchema = z
400
- .object({
193
+ export const fileIngestLifecycleRuntimePayloadSchema = z.strictObject({
194
+ fileId: z.string().check(z.minLength(1)),
195
+ uploadId: z.optional(z.string()),
196
+ mediaKind: z.optional(z.string()),
197
+ slotId: z.optional(z.string()),
198
+ attemptId: z.optional(z.string()),
199
+ source: fileIngestRuntimeSourceSchema,
200
+ stage: fileIngestRuntimeStageSchema,
201
+ progress: z.int().check(z.minimum(0), z.maximum(100)),
202
+ bytesCompleted: z.optional(z.int().check(z.nonnegative())),
203
+ bytesTotal: z.optional(z.int().check(z.nonnegative())),
204
+ fileName: z.optional(z.string()),
205
+ mimeType: z.optional(z.string()),
206
+ fileSize: z.optional(z.int().check(z.nonnegative())),
207
+ error: z.optional(z.string()),
208
+ trackId: z.optional(z.string()),
209
+ releaseId: z.optional(z.string()),
210
+ });
211
+
212
+ const localeScopedOgEntityTypes = new Set<
213
+ z.infer<typeof ogGenerationRuntimeEntityTypeSchema>
214
+ >(["post", "page", "form", "privacy", "terms"]);
215
+
216
+ export const ogGenerationLifecycleRuntimeEventSchema = z
217
+ .strictObject({
401
218
  version: z.literal(1),
402
219
  kind: z.literal("og.lifecycle"),
403
220
  entityType: ogGenerationRuntimeEntityTypeSchema,
404
- entityId: z.string().min(1),
405
- locale: z.string().min(1).optional(),
406
- correlationId: z.string().min(1),
407
- sequence: z.number().int().optional(),
408
- timestampMs: z.number().int(),
221
+ entityId: z.string().check(z.minLength(1)),
222
+ locale: z.optional(z.string().check(z.minLength(1))),
223
+ correlationId: z.string().check(z.minLength(1)),
224
+ sequence: z.optional(z.int()),
225
+ timestampMs: z.int(),
409
226
  payload: ogGenerationLifecycleRuntimePayloadSchema,
410
227
  })
411
- .strict()
412
- .superRefine((event, ctx) => {
413
- const requiresLocale = localeScopedOgEntityTypes.has(event.entityType);
414
- if (requiresLocale && !event.locale) {
415
- ctx.addIssue({
416
- code: z.ZodIssueCode.custom,
417
- path: ["locale"],
418
- message: `${event.entityType} OG lifecycle requires a locale target`,
419
- });
420
- }
421
- if (!requiresLocale && event.locale) {
422
- ctx.addIssue({
423
- code: z.ZodIssueCode.custom,
424
- path: ["locale"],
425
- message: `${event.entityType} OG lifecycle requires an entity target`,
426
- });
427
- }
428
- if (event.correlationId !== event.payload.generationId) {
429
- ctx.addIssue({
430
- code: z.ZodIssueCode.custom,
431
- path: ["correlationId"],
432
- message: "OG lifecycle correlationId must equal payload.generationId",
433
- });
434
- }
435
- });
228
+ .check(
229
+ z.superRefine((event, ctx) => {
230
+ const requiresLocale = localeScopedOgEntityTypes.has(event.entityType);
231
+ if (requiresLocale && !event.locale) {
232
+ ctx.addIssue({
233
+ code: "custom",
234
+ path: ["locale"],
235
+ message: `${event.entityType} OG lifecycle requires a locale target`,
236
+ });
237
+ }
238
+ if (!requiresLocale && event.locale) {
239
+ ctx.addIssue({
240
+ code: "custom",
241
+ path: ["locale"],
242
+ message: `${event.entityType} OG lifecycle requires an entity target`,
243
+ });
244
+ }
245
+ if (event.correlationId !== event.payload.generationId) {
246
+ ctx.addIssue({
247
+ code: "custom",
248
+ path: ["correlationId"],
249
+ message: "OG lifecycle correlationId must equal payload.generationId",
250
+ });
251
+ }
252
+ }),
253
+ );
436
254
 
437
- export const translationLifecycleRuntimePayloadSchema = z
438
- .object({
439
- jobId: z.string().min(1),
440
- targetLocale: z.string().min(1),
441
- status: z.literal("failed"),
442
- error: z.string().optional(),
443
- })
444
- .strict();
255
+ export const translationLifecycleRuntimeStatusSchema = z.literal("failed");
256
+
257
+ export const translationLifecycleRuntimePayloadSchema = z.strictObject({
258
+ jobId: z.string().check(z.minLength(1)),
259
+ targetLocale: z.string().check(z.minLength(1)),
260
+ status: translationLifecycleRuntimeStatusSchema,
261
+ error: z.optional(z.string()),
262
+ });
445
263
 
446
264
  export const editorRuntimeEventSchema = z.discriminatedUnion("kind", [
447
265
  ogGenerationLifecycleRuntimeEventSchema,
448
- z
449
- .object({
450
- version: z.literal(1),
451
- kind: z.literal("render.complete"),
452
- entityType: runtimeEntityTypeSchema,
453
- entityId: z.string().min(1),
454
- locale: z.string().optional(),
455
- correlationId: z.string().optional(),
456
- sequence: z.number().int().optional(),
457
- timestampMs: z.number().int(),
458
- payload: z
459
- .object({
460
- contentHash: z.string(),
461
- success: z.boolean(),
462
- error: z.string().optional(),
463
- })
464
- .strict(),
465
- })
466
- .strict(),
467
- z
468
- .object({
469
- version: z.literal(1),
470
- kind: z.literal("media.processing.lifecycle"),
471
- entityType: runtimeEntityTypeSchema,
472
- entityId: z.string().min(1),
473
- locale: z.string().optional(),
474
- correlationId: z.string().optional(),
475
- sequence: z.number().int().optional(),
476
- timestampMs: z.number().int(),
477
- payload: mediaProcessingLifecycleRuntimePayloadSchema,
478
- })
479
- .strict(),
480
- z
481
- .object({
482
- version: z.literal(1),
483
- kind: z.literal("file.ingest.lifecycle"),
484
- entityType: runtimeEntityTypeSchema,
485
- entityId: z.string().min(1),
486
- locale: z.string().optional(),
487
- correlationId: z.string().optional(),
488
- sequence: z.number().int().optional(),
489
- timestampMs: z.number().int(),
490
- payload: fileIngestLifecycleRuntimePayloadSchema,
491
- })
492
- .strict(),
493
- z
494
- .object({
495
- version: z.literal(1),
496
- kind: z.literal("translation.lifecycle"),
497
- entityType: runtimeEntityTypeSchema,
498
- entityId: z.string().min(1),
499
- locale: z.string().optional(),
500
- correlationId: z.string().optional(),
501
- sequence: z.number().int().optional(),
502
- timestampMs: z.number().int(),
503
- payload: translationLifecycleRuntimePayloadSchema,
504
- })
505
- .strict(),
506
- z
507
- .object({
508
- version: z.literal(1),
509
- kind: z.literal("runtime.error"),
510
- entityType: runtimeEntityTypeSchema,
511
- entityId: z.string().min(1),
512
- locale: z.string().optional(),
513
- correlationId: z.string().optional(),
514
- sequence: z.number().int().optional(),
515
- timestampMs: z.number().int(),
516
- payload: z
517
- .object({
518
- code: z.string().min(1),
519
- message: z.string(),
520
- retryable: z.boolean().optional(),
521
- source: z.enum(["backend", "collab", "worker"]).optional(),
522
- details: z
523
- .record(
524
- z.string(),
525
- z.union([z.string(), z.number(), z.boolean(), z.null()]),
526
- )
527
- .optional(),
528
- })
529
- .strict(),
530
- })
531
- .strict(),
266
+ z.strictObject({
267
+ version: z.literal(1),
268
+ kind: z.literal("media.processing.lifecycle"),
269
+ entityType: runtimeEntityTypeSchema,
270
+ entityId: z.string().check(z.minLength(1)),
271
+ locale: z.optional(z.string()),
272
+ correlationId: z.optional(z.string()),
273
+ sequence: z.optional(z.int()),
274
+ timestampMs: z.int(),
275
+ payload: mediaProcessingLifecycleRuntimePayloadSchema,
276
+ }),
277
+ z.strictObject({
278
+ version: z.literal(1),
279
+ kind: z.literal("file.ingest.lifecycle"),
280
+ entityType: runtimeEntityTypeSchema,
281
+ entityId: z.string().check(z.minLength(1)),
282
+ locale: z.optional(z.string()),
283
+ correlationId: z.optional(z.string()),
284
+ sequence: z.optional(z.int()),
285
+ timestampMs: z.int(),
286
+ payload: fileIngestLifecycleRuntimePayloadSchema,
287
+ }),
288
+ z.strictObject({
289
+ version: z.literal(1),
290
+ kind: z.literal("translation.lifecycle"),
291
+ entityType: runtimeEntityTypeSchema,
292
+ entityId: z.string().check(z.minLength(1)),
293
+ locale: z.optional(z.string()),
294
+ correlationId: z.optional(z.string()),
295
+ sequence: z.optional(z.int()),
296
+ timestampMs: z.int(),
297
+ payload: translationLifecycleRuntimePayloadSchema,
298
+ }),
532
299
  ]);
533
300
 
534
301
  export function parseEditorRuntimeEventMessage(
535
302
  payload: string,
536
303
  ): EditorRuntimeEvent | null {
537
304
  try {
538
- const data = JSON.parse(payload) as unknown;
539
- const result = editorRuntimeEventSchema.safeParse(data);
540
- if (!result.success) {
541
- return null;
542
- }
543
- return result.data as EditorRuntimeEvent;
305
+ const result = editorRuntimeEventSchema.safeParse(JSON.parse(payload));
306
+ return result.success ? result.data : null;
544
307
  } catch {
545
308
  return null;
546
309
  }
547
310
  }
311
+
312
+ // ---- Types: inferred from the schemas above, the single definition of every runtime event ----
313
+
314
+ export type RuntimeEntityType = z.infer<typeof runtimeEntityTypeSchema>;
315
+ export type TranslationLifecycleRuntimeStatus = z.infer<
316
+ typeof translationLifecycleRuntimeStatusSchema
317
+ >;
318
+ export type MediaProcessingRuntimeStatus = z.infer<
319
+ typeof mediaProcessingRuntimeStatusSchema
320
+ >;
321
+ export type OgGenerationRuntimeEntityType = z.infer<
322
+ typeof ogGenerationRuntimeEntityTypeSchema
323
+ >;
324
+ export type OgGenerationRuntimeStatus = z.infer<
325
+ typeof ogGenerationRuntimeStatusSchema
326
+ >;
327
+ export type FileIngestRuntimeSource = z.infer<
328
+ typeof fileIngestRuntimeSourceSchema
329
+ >;
330
+ export type FileIngestRuntimeStage = z.infer<
331
+ typeof fileIngestRuntimeStageSchema
332
+ >;
333
+ export type OgGenerationLifecycleRuntimePayload = z.infer<
334
+ typeof ogGenerationLifecycleRuntimePayloadSchema
335
+ >;
336
+ export type MediaProcessingRuntimeOutputs = z.infer<
337
+ typeof mediaProcessingRuntimeOutputsSchema
338
+ >;
339
+ export type MediaProcessingLifecycleRuntimePayload = z.infer<
340
+ typeof mediaProcessingLifecycleRuntimePayloadSchema
341
+ >;
342
+ export type FileIngestLifecycleRuntimePayload = z.infer<
343
+ typeof fileIngestLifecycleRuntimePayloadSchema
344
+ >;
345
+ export type TranslationLifecycleRuntimePayload = z.infer<
346
+ typeof translationLifecycleRuntimePayloadSchema
347
+ >;
348
+ export type EditorRuntimeEvent = z.infer<typeof editorRuntimeEventSchema>;
349
+ export type OgGenerationLifecycleRuntimeEvent = Extract<
350
+ EditorRuntimeEvent,
351
+ { kind: "og.lifecycle" }
352
+ >;
353
+ export type MediaProcessingLifecycleRuntimeEvent = Extract<
354
+ EditorRuntimeEvent,
355
+ { kind: "media.processing.lifecycle" }
356
+ >;
357
+ export type FileIngestLifecycleRuntimeEvent = Extract<
358
+ EditorRuntimeEvent,
359
+ { kind: "file.ingest.lifecycle" }
360
+ >;
361
+ export type TranslationLifecycleRuntimeEvent = Extract<
362
+ EditorRuntimeEvent,
363
+ { kind: "translation.lifecycle" }
364
+ >;
365
+
366
+ export function isMediaProcessingRuntimeReady(payload: {
367
+ status?: MediaProcessingRuntimeStatus | null;
368
+ }): boolean {
369
+ return payload.status === "ready";
370
+ }
371
+
372
+ export function isOgGenerationRuntimeTerminal(
373
+ status: OgGenerationRuntimeStatus,
374
+ ): boolean {
375
+ return (
376
+ status === "ready" ||
377
+ status === "failed" ||
378
+ status === "superseded" ||
379
+ status === "cancelled"
380
+ );
381
+ }