@norskvideo/ctl-product-template-schema 0.1.33 → 0.1.35
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/index.d.ts +47 -6
- package/index.js +26 -0
- package/interfaces.d.ts +67 -0
- package/interfaces.js +178 -0
- package/logs.d.ts +7 -7
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -150,6 +150,16 @@ export declare const ProductTemplateMigrationSchema: z.ZodObject<{
|
|
|
150
150
|
}>;
|
|
151
151
|
}, z.core.$strip>;
|
|
152
152
|
export type ProductTemplateMigration = z.infer<typeof ProductTemplateMigrationSchema>;
|
|
153
|
+
export declare const PRODUCT_TEMPLATE_WORKLOAD_ROLES: readonly ["viewer", "operator", "builder"];
|
|
154
|
+
export declare const ProductTemplateWorkloadSchema: z.ZodObject<{
|
|
155
|
+
role: z.ZodEnum<{
|
|
156
|
+
viewer: "viewer";
|
|
157
|
+
operator: "operator";
|
|
158
|
+
builder: "builder";
|
|
159
|
+
}>;
|
|
160
|
+
on: z.ZodLiteral<"product">;
|
|
161
|
+
}, z.core.$strip>;
|
|
162
|
+
export type ProductTemplateWorkload = z.infer<typeof ProductTemplateWorkloadSchema>;
|
|
153
163
|
export declare const ProductTemplateManifestSchema: z.ZodObject<{
|
|
154
164
|
productTemplateSchemaVersion: z.ZodLiteral<1>;
|
|
155
165
|
productName: z.ZodString;
|
|
@@ -307,15 +317,15 @@ export declare const ProductTemplateManifestSchema: z.ZodObject<{
|
|
|
307
317
|
}, z.core.$strip>>>;
|
|
308
318
|
logs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
309
319
|
stdout: z.ZodOptional<z.ZodEnum<{
|
|
310
|
-
json: "json";
|
|
311
|
-
text: "text";
|
|
312
320
|
none: "none";
|
|
321
|
+
text: "text";
|
|
322
|
+
json: "json";
|
|
313
323
|
}>>;
|
|
314
324
|
files: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
315
325
|
path: z.ZodString;
|
|
316
326
|
format: z.ZodEnum<{
|
|
317
|
-
json: "json";
|
|
318
327
|
text: "text";
|
|
328
|
+
json: "json";
|
|
319
329
|
}>;
|
|
320
330
|
}, z.core.$strict>>>;
|
|
321
331
|
fluentBit: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
|
|
@@ -330,6 +340,18 @@ export declare const ProductTemplateManifestSchema: z.ZodObject<{
|
|
|
330
340
|
hostTls: z.ZodOptional<z.ZodObject<{
|
|
331
341
|
services: z.ZodArray<z.ZodString>;
|
|
332
342
|
}, z.core.$strip>>;
|
|
343
|
+
workload: z.ZodOptional<z.ZodObject<{
|
|
344
|
+
role: z.ZodEnum<{
|
|
345
|
+
viewer: "viewer";
|
|
346
|
+
operator: "operator";
|
|
347
|
+
builder: "builder";
|
|
348
|
+
}>;
|
|
349
|
+
on: z.ZodLiteral<"product">;
|
|
350
|
+
}, z.core.$strip>>;
|
|
351
|
+
interfaces: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
352
|
+
name: z.ZodString;
|
|
353
|
+
version: z.ZodNumber;
|
|
354
|
+
}, z.core.$catchall<z.ZodUnknown>>>>;
|
|
333
355
|
migration: z.ZodOptional<z.ZodObject<{
|
|
334
356
|
strategy: z.ZodEnum<{
|
|
335
357
|
overlap: "overlap";
|
|
@@ -363,6 +385,8 @@ export declare const ProductTemplateParameterSchema: z.ZodObject<{
|
|
|
363
385
|
min: z.ZodOptional<z.ZodNumber>;
|
|
364
386
|
max: z.ZodOptional<z.ZodNumber>;
|
|
365
387
|
values: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
388
|
+
label: z.ZodOptional<z.ZodString>;
|
|
389
|
+
group: z.ZodOptional<z.ZodString>;
|
|
366
390
|
}, z.core.$strip>;
|
|
367
391
|
export type ProductTemplateParameter = z.infer<typeof ProductTemplateParameterSchema>;
|
|
368
392
|
export declare const ParametersFileSchema: z.ZodObject<{
|
|
@@ -382,6 +406,8 @@ export declare const ParametersFileSchema: z.ZodObject<{
|
|
|
382
406
|
min: z.ZodOptional<z.ZodNumber>;
|
|
383
407
|
max: z.ZodOptional<z.ZodNumber>;
|
|
384
408
|
values: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
409
|
+
label: z.ZodOptional<z.ZodString>;
|
|
410
|
+
group: z.ZodOptional<z.ZodString>;
|
|
385
411
|
}, z.core.$strip>>>;
|
|
386
412
|
}, z.core.$strip>;
|
|
387
413
|
export type ParametersFile = z.infer<typeof ParametersFileSchema>;
|
|
@@ -548,15 +574,15 @@ export declare const ProductTemplateMaterialsSchema: z.ZodObject<{
|
|
|
548
574
|
}, z.core.$strip>>>;
|
|
549
575
|
logs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
550
576
|
stdout: z.ZodOptional<z.ZodEnum<{
|
|
551
|
-
json: "json";
|
|
552
|
-
text: "text";
|
|
553
577
|
none: "none";
|
|
578
|
+
text: "text";
|
|
579
|
+
json: "json";
|
|
554
580
|
}>>;
|
|
555
581
|
files: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
556
582
|
path: z.ZodString;
|
|
557
583
|
format: z.ZodEnum<{
|
|
558
|
-
json: "json";
|
|
559
584
|
text: "text";
|
|
585
|
+
json: "json";
|
|
560
586
|
}>;
|
|
561
587
|
}, z.core.$strict>>>;
|
|
562
588
|
fluentBit: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
|
|
@@ -571,6 +597,18 @@ export declare const ProductTemplateMaterialsSchema: z.ZodObject<{
|
|
|
571
597
|
hostTls: z.ZodOptional<z.ZodObject<{
|
|
572
598
|
services: z.ZodArray<z.ZodString>;
|
|
573
599
|
}, z.core.$strip>>;
|
|
600
|
+
workload: z.ZodOptional<z.ZodObject<{
|
|
601
|
+
role: z.ZodEnum<{
|
|
602
|
+
viewer: "viewer";
|
|
603
|
+
operator: "operator";
|
|
604
|
+
builder: "builder";
|
|
605
|
+
}>;
|
|
606
|
+
on: z.ZodLiteral<"product">;
|
|
607
|
+
}, z.core.$strip>>;
|
|
608
|
+
interfaces: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
609
|
+
name: z.ZodString;
|
|
610
|
+
version: z.ZodNumber;
|
|
611
|
+
}, z.core.$catchall<z.ZodUnknown>>>>;
|
|
574
612
|
migration: z.ZodOptional<z.ZodObject<{
|
|
575
613
|
strategy: z.ZodEnum<{
|
|
576
614
|
overlap: "overlap";
|
|
@@ -597,6 +635,8 @@ export declare const ProductTemplateMaterialsSchema: z.ZodObject<{
|
|
|
597
635
|
min: z.ZodOptional<z.ZodNumber>;
|
|
598
636
|
max: z.ZodOptional<z.ZodNumber>;
|
|
599
637
|
values: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
638
|
+
label: z.ZodOptional<z.ZodString>;
|
|
639
|
+
group: z.ZodOptional<z.ZodString>;
|
|
600
640
|
}, z.core.$strip>>>;
|
|
601
641
|
}, z.core.$strip>>;
|
|
602
642
|
workflow: z.ZodOptional<z.ZodString>;
|
|
@@ -618,4 +658,5 @@ export declare const ProductTemplateMaterialsSchema: z.ZodObject<{
|
|
|
618
658
|
}, z.core.$strip>>>;
|
|
619
659
|
}, z.core.$strip>;
|
|
620
660
|
export type ProductTemplateMaterials = z.infer<typeof ProductTemplateMaterialsSchema>;
|
|
661
|
+
export { declareMediaConversion, interfaceDeclarationProblems, MEDIA_CONVERSION_INTERFACE, type MediaConversionV1, MediaConversionV1Schema, mediaConversionOf, type ProductTemplateInterface, ProductTemplateInterfaceSchema, } from "./interfaces.js";
|
|
621
662
|
export { composeServiceNamesOf, logsDeclarationProblems, NORSK_MEDIA_SERVICE_LOGS, type ProductTemplateLogs, ProductTemplateLogsSchema, productTemplateLogsProblems, type ServiceLogs, ServiceLogsSchema, STANDARD_LOGS, STUDIO_SERVICE_LOGS, servicesWithoutLogs, } from "./logs.js";
|
package/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { ProductTemplateInterfaceSchema } from "./interfaces.js";
|
|
2
3
|
import { ProductTemplateLogsSchema } from "./logs.js";
|
|
3
4
|
// ProductTemplate format v1. A product template is a tar file at the wire; the schemas
|
|
4
5
|
// below describe its parts before packing and how the runner validates
|
|
@@ -319,6 +320,16 @@ export const ProductTemplateMigrationSchema = z
|
|
|
319
320
|
}),
|
|
320
321
|
})
|
|
321
322
|
.meta({ id: "ProductTemplateMigration", outputId: "ProductTemplateMigration" });
|
|
323
|
+
// The roles a template may declare for its instances' workload: at most
|
|
324
|
+
// builder, since registering a template takes builder and that registration is
|
|
325
|
+
// the consent. Built-in names only, whose meaning every host agrees on.
|
|
326
|
+
export const PRODUCT_TEMPLATE_WORKLOAD_ROLES = ["viewer", "operator", "builder"];
|
|
327
|
+
export const ProductTemplateWorkloadSchema = z
|
|
328
|
+
.object({
|
|
329
|
+
role: z.enum(PRODUCT_TEMPLATE_WORKLOAD_ROLES),
|
|
330
|
+
on: z.literal("product"),
|
|
331
|
+
})
|
|
332
|
+
.meta({ id: "ProductTemplateWorkload", outputId: "ProductTemplateWorkload" });
|
|
322
333
|
export const ProductTemplateManifestSchema = z
|
|
323
334
|
.object({
|
|
324
335
|
productTemplateSchemaVersion: z.literal(1),
|
|
@@ -371,6 +382,12 @@ export const ProductTemplateManifestSchema = z
|
|
|
371
382
|
hostTls: ProductTemplateHostTlsSchema.optional().meta({
|
|
372
383
|
description: "Mount the host's TLS certificate and key, read-only, into the named compose services — for a product that serves TLS itself. Copied per launch, so a rotated certificate is picked up on relaunch. Absent = nothing mounted.",
|
|
373
384
|
}),
|
|
385
|
+
workload: ProductTemplateWorkloadSchema.optional().meta({
|
|
386
|
+
description: "The role each instance's own workload (`urn:norsk:workload:instance:<id>`) holds on its product (`product/<productName>`), granted when it launches, whoever launches it — for an instance that creates or manages its product's other instances (a capture launching recorders). At most `builder`: registering a template takes builder, and that registration is the consent. Absent = the workload holds nothing it was not passed at launch. An older ctl ignores it, and its instances hold nothing.",
|
|
387
|
+
}),
|
|
388
|
+
interfaces: z.array(ProductTemplateInterfaceSchema).optional().meta({
|
|
389
|
+
description: "Named, versioned contracts this template implements (e.g. norsk.io/media-conversion), so a client can find the templates that implement one whatever product built them. Each entry is carried verbatim; the runner checks only contracts it knows, so a template declaring one an older ctl does not know still launches. Absent = implements none.",
|
|
390
|
+
}),
|
|
374
391
|
migration: ProductTemplateMigrationSchema.optional().meta({
|
|
375
392
|
description: "How a running instance may be moved between nodes. `strategy` is the strongest promise the product keeps: `overlap` (target started, then source stopped — both may produce briefly), `stopThenStart` (the default when absent — target prepared but not started, source stopped, target started), or `handover` (the job speaks the worker's migration socket and hands over at an exact position). A migrate request may down-grade to a safer strategy but never up-grade past the declaration. Only the manager reads this; the worker never does.",
|
|
376
393
|
}),
|
|
@@ -408,6 +425,14 @@ export const ProductTemplateParameterSchema = z
|
|
|
408
425
|
max: z.number().optional(),
|
|
409
426
|
/** Allowed values for `enum` parameters (rendered as a dropdown). */
|
|
410
427
|
values: z.array(z.string()).optional(),
|
|
428
|
+
// Display metadata for a form built from the template. Declaration order is
|
|
429
|
+
// display order, so there is no `order` field to disagree with it.
|
|
430
|
+
label: z.string().min(1).optional().meta({
|
|
431
|
+
description: "Short name to show for the parameter in a form, e.g. `Width`. Absent = the form shows the parameter name. `purpose` stays the longer help text.",
|
|
432
|
+
}),
|
|
433
|
+
group: z.string().min(1).optional().meta({
|
|
434
|
+
description: "Section of a form the parameter belongs in, e.g. `Output`. Groups appear in the order their first parameter is declared; ungrouped parameters come first. Absent = ungrouped.",
|
|
435
|
+
}),
|
|
411
436
|
})
|
|
412
437
|
.superRefine((p, ctx) => {
|
|
413
438
|
if (p.type === "enum" && (!p.values || p.values.length === 0)) {
|
|
@@ -478,4 +503,5 @@ export const ProductTemplateMaterialsSchema = z
|
|
|
478
503
|
}),
|
|
479
504
|
})
|
|
480
505
|
.meta({ id: "ProductTemplateMaterials" });
|
|
506
|
+
export { declareMediaConversion, interfaceDeclarationProblems, MEDIA_CONVERSION_INTERFACE, MediaConversionV1Schema, mediaConversionOf, ProductTemplateInterfaceSchema, } from "./interfaces.js";
|
|
481
507
|
export { composeServiceNamesOf, logsDeclarationProblems, NORSK_MEDIA_SERVICE_LOGS, ProductTemplateLogsSchema, productTemplateLogsProblems, ServiceLogsSchema, STANDARD_LOGS, STUDIO_SERVICE_LOGS, servicesWithoutLogs, } from "./logs.js";
|
package/interfaces.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ProductTemplateManifest, ProductTemplateParameter } from "./index.js";
|
|
3
|
+
export declare const ProductTemplateInterfaceSchema: z.ZodObject<{
|
|
4
|
+
name: z.ZodString;
|
|
5
|
+
version: z.ZodNumber;
|
|
6
|
+
}, z.core.$catchall<z.ZodUnknown>>;
|
|
7
|
+
export type ProductTemplateInterface = z.infer<typeof ProductTemplateInterfaceSchema>;
|
|
8
|
+
export declare const MEDIA_CONVERSION_INTERFACE = "norsk.io/media-conversion";
|
|
9
|
+
export declare const MediaConversionV1Schema: z.ZodObject<{
|
|
10
|
+
name: z.ZodLiteral<"norsk.io/media-conversion">;
|
|
11
|
+
version: z.ZodLiteral<1>;
|
|
12
|
+
inputs: z.ZodArray<z.ZodObject<{
|
|
13
|
+
name: z.ZodString;
|
|
14
|
+
kinds: z.ZodRecord<z.ZodEnum<{
|
|
15
|
+
video: "video";
|
|
16
|
+
audio: "audio";
|
|
17
|
+
subtitle: "subtitle";
|
|
18
|
+
ancillary: "ancillary";
|
|
19
|
+
}> & z.core.$partial, z.ZodUnion<readonly [z.ZodNumber, z.ZodObject<{
|
|
20
|
+
min: z.ZodNumber;
|
|
21
|
+
max: z.ZodNumber;
|
|
22
|
+
}, z.core.$strip>]>>;
|
|
23
|
+
}, z.core.$strip>>;
|
|
24
|
+
outputs: z.ZodArray<z.ZodObject<{
|
|
25
|
+
name: z.ZodString;
|
|
26
|
+
kind: z.ZodEnum<{
|
|
27
|
+
video: "video";
|
|
28
|
+
audio: "audio";
|
|
29
|
+
subtitle: "subtitle";
|
|
30
|
+
ancillary: "ancillary";
|
|
31
|
+
}>;
|
|
32
|
+
count: z.ZodOptional<z.ZodNumber>;
|
|
33
|
+
format: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
34
|
+
}, z.core.$strip>>;
|
|
35
|
+
transports: z.ZodArray<z.ZodString>;
|
|
36
|
+
timeline: z.ZodString;
|
|
37
|
+
bindings: z.ZodObject<{
|
|
38
|
+
sourceUrl: z.ZodString;
|
|
39
|
+
returnName: z.ZodString;
|
|
40
|
+
engineVersion: z.ZodOptional<z.ZodString>;
|
|
41
|
+
conversionId: z.ZodOptional<z.ZodString>;
|
|
42
|
+
}, z.core.$strip>;
|
|
43
|
+
}, z.core.$strip>;
|
|
44
|
+
export type MediaConversionV1 = z.infer<typeof MediaConversionV1Schema>;
|
|
45
|
+
/** Everything wrong with a template's interface declarations, given the
|
|
46
|
+
* parameters it declares. Only known interfaces are checked. Empty = fine. */
|
|
47
|
+
export declare function interfaceDeclarationProblems(template: {
|
|
48
|
+
manifest: Pick<ProductTemplateManifest, "interfaces">;
|
|
49
|
+
parameters: Pick<ProductTemplateParameter, "name">[];
|
|
50
|
+
}): string[];
|
|
51
|
+
/** The template's norsk.io/media-conversion v1 declaration, typed, if it
|
|
52
|
+
* declares a valid one. */
|
|
53
|
+
export declare function mediaConversionOf(manifest: Pick<ProductTemplateManifest, "interfaces">): MediaConversionV1 | undefined;
|
|
54
|
+
type BindingRole = keyof MediaConversionV1["bindings"];
|
|
55
|
+
/** A norsk.io/media-conversion v1 declaration and the parameters its
|
|
56
|
+
* bindings name, built together so a template cannot bind a parameter it
|
|
57
|
+
* never declared. Spread `parameters` into the template's parameters.yaml and
|
|
58
|
+
* `interface` into its manifest's `interfaces`. Each binding defaults to a
|
|
59
|
+
* conventional parameter name; pass a name to use the template's own, or
|
|
60
|
+
* null to leave an optional role out. */
|
|
61
|
+
export declare function declareMediaConversion(shape: Omit<MediaConversionV1, "name" | "version" | "bindings"> & {
|
|
62
|
+
bindings?: Partial<Record<BindingRole, string | null>>;
|
|
63
|
+
}): {
|
|
64
|
+
interface: MediaConversionV1;
|
|
65
|
+
parameters: ProductTemplateParameter[];
|
|
66
|
+
};
|
|
67
|
+
export {};
|
package/interfaces.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
// Interfaces: named, versioned contracts a product template says it implements,
|
|
3
|
+
// so a client can find every template that implements one without knowing which
|
|
4
|
+
// product built it. The first is norsk.io/media-conversion: the relay lists the
|
|
5
|
+
// production templates whose runtime implements it, and any product or turnkey
|
|
6
|
+
// can provide one.
|
|
7
|
+
//
|
|
8
|
+
// The manifest field accepts ANY well-formed declaration and carries it
|
|
9
|
+
// verbatim. Only the interfaces (and versions) this package knows are checked,
|
|
10
|
+
// by interfaceDeclarationProblems: a newer product declaring something this ctl
|
|
11
|
+
// has never heard of must still launch (RFC 0001), and a client that does know
|
|
12
|
+
// it must still see it.
|
|
13
|
+
export const ProductTemplateInterfaceSchema = z
|
|
14
|
+
.object({
|
|
15
|
+
name: z
|
|
16
|
+
.string()
|
|
17
|
+
.regex(/^[a-z0-9]+(\.[a-z0-9]+)+\/[a-z0-9]+(-[a-z0-9]+)*$/, "<domain>/<name>, e.g. norsk.io/media-conversion")
|
|
18
|
+
.meta({ description: "The contract, as <domain>/<name>, e.g. norsk.io/media-conversion." }),
|
|
19
|
+
version: z.number().int().min(1).meta({ description: "The version of the contract this declaration follows." }),
|
|
20
|
+
})
|
|
21
|
+
.catchall(z.unknown())
|
|
22
|
+
.meta({
|
|
23
|
+
id: "ProductTemplateInterface",
|
|
24
|
+
outputId: "ProductTemplateInterface",
|
|
25
|
+
description: "A contract the template implements: its name and version, and the body that contract defines. Carried verbatim; a runner checks only the contracts it knows.",
|
|
26
|
+
});
|
|
27
|
+
export const MEDIA_CONVERSION_INTERFACE = "norsk.io/media-conversion";
|
|
28
|
+
const PARAMETER_NAME = /^[A-Z][A-Z0-9_]*$/;
|
|
29
|
+
const parameterName = z.string().regex(PARAMETER_NAME, "a parameter name");
|
|
30
|
+
const MediaKindSchema = z.enum(["video", "audio", "subtitle", "ancillary"]);
|
|
31
|
+
const KindCountSchema = z.union([
|
|
32
|
+
z.number().int().min(0),
|
|
33
|
+
z
|
|
34
|
+
.object({ min: z.number().int().min(0), max: z.number().int().min(0) })
|
|
35
|
+
.refine((r) => r.min <= r.max, { message: "min must be <= max" }),
|
|
36
|
+
]);
|
|
37
|
+
export const MediaConversionV1Schema = z.object({
|
|
38
|
+
name: z.literal(MEDIA_CONVERSION_INTERFACE),
|
|
39
|
+
version: z.literal(1),
|
|
40
|
+
// What the converter takes from the relay: per named input, how many tracks
|
|
41
|
+
// of each kind (a count, or a range).
|
|
42
|
+
inputs: z
|
|
43
|
+
.array(z.object({ name: z.string().min(1), kinds: z.partialRecord(MediaKindSchema, KindCountSchema) }))
|
|
44
|
+
.min(1),
|
|
45
|
+
// What it hands back. A format value may refer to a parameter as ${NAME}, so
|
|
46
|
+
// a client can show the result of a launch before it happens.
|
|
47
|
+
outputs: z
|
|
48
|
+
.array(z.object({
|
|
49
|
+
name: z.string().min(1),
|
|
50
|
+
kind: MediaKindSchema,
|
|
51
|
+
count: z.number().int().min(1).optional(),
|
|
52
|
+
format: z.record(z.string(), z.string()).optional(),
|
|
53
|
+
}))
|
|
54
|
+
.min(1),
|
|
55
|
+
// Free strings rather than an enum: a template offering a transport this ctl
|
|
56
|
+
// does not know (srt, moq) must still validate; the client picks one it speaks.
|
|
57
|
+
transports: z.array(z.string().min(1)).min(1),
|
|
58
|
+
timeline: z.string().min(1),
|
|
59
|
+
// The template's own parameters the client fills, by role.
|
|
60
|
+
bindings: z.object({
|
|
61
|
+
sourceUrl: parameterName,
|
|
62
|
+
returnName: parameterName,
|
|
63
|
+
engineVersion: parameterName.optional(),
|
|
64
|
+
conversionId: parameterName.optional(),
|
|
65
|
+
}),
|
|
66
|
+
});
|
|
67
|
+
const PARAMETER_REFERENCE = /\$\{([^}]*)\}/g;
|
|
68
|
+
/** Everything wrong with a template's interface declarations, given the
|
|
69
|
+
* parameters it declares. Only known interfaces are checked. Empty = fine. */
|
|
70
|
+
export function interfaceDeclarationProblems(template) {
|
|
71
|
+
const declared = new Set(template.parameters.map((p) => p.name));
|
|
72
|
+
const seen = new Set();
|
|
73
|
+
const problems = [];
|
|
74
|
+
(template.manifest.interfaces ?? []).forEach((decl, i) => {
|
|
75
|
+
if (decl.name !== MEDIA_CONVERSION_INTERFACE || decl.version !== 1)
|
|
76
|
+
return;
|
|
77
|
+
const at = `interfaces[${i}] (${decl.name} v${decl.version})`;
|
|
78
|
+
if (seen.has(`${decl.name}@${decl.version}`)) {
|
|
79
|
+
problems.push(`${at}: declared more than once`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
seen.add(`${decl.name}@${decl.version}`);
|
|
83
|
+
const parsed = MediaConversionV1Schema.safeParse(decl);
|
|
84
|
+
if (!parsed.success) {
|
|
85
|
+
for (const issue of parsed.error.issues) {
|
|
86
|
+
problems.push(`${at}: ${issue.path.join(".") || "(declaration)"}: ${issue.message}`);
|
|
87
|
+
}
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
for (const [role, param] of Object.entries(parsed.data.bindings)) {
|
|
91
|
+
if (param !== undefined && !declared.has(param)) {
|
|
92
|
+
problems.push(`${at}: binding ${role} names ${param}, which is not a declared parameter`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
parsed.data.outputs.forEach((output, o) => {
|
|
96
|
+
for (const [key, value] of Object.entries(output.format ?? {})) {
|
|
97
|
+
for (const [, ref] of value.matchAll(PARAMETER_REFERENCE)) {
|
|
98
|
+
if (!declared.has(ref)) {
|
|
99
|
+
problems.push(`${at}: outputs[${o}].format.${key} refers to ${ref}, which is not a declared parameter`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
return problems;
|
|
106
|
+
}
|
|
107
|
+
/** The template's norsk.io/media-conversion v1 declaration, typed, if it
|
|
108
|
+
* declares a valid one. */
|
|
109
|
+
export function mediaConversionOf(manifest) {
|
|
110
|
+
for (const decl of manifest.interfaces ?? []) {
|
|
111
|
+
if (decl.name !== MEDIA_CONVERSION_INTERFACE || decl.version !== 1)
|
|
112
|
+
continue;
|
|
113
|
+
const parsed = MediaConversionV1Schema.safeParse(decl);
|
|
114
|
+
if (parsed.success)
|
|
115
|
+
return parsed.data;
|
|
116
|
+
}
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
const BINDING_DEFAULTS = {
|
|
120
|
+
sourceUrl: {
|
|
121
|
+
name: "SOURCE_URL",
|
|
122
|
+
label: "Source",
|
|
123
|
+
purpose: "Where to pull the media to convert from, e.g. link://host:port/name. Filled by the caller.",
|
|
124
|
+
required: true,
|
|
125
|
+
},
|
|
126
|
+
returnName: {
|
|
127
|
+
name: "RETURN_NAME",
|
|
128
|
+
label: "Return name",
|
|
129
|
+
purpose: "The name to publish the converted media under, for the caller to take it back. Filled by the caller.",
|
|
130
|
+
required: true,
|
|
131
|
+
},
|
|
132
|
+
engineVersion: {
|
|
133
|
+
name: "ENGINE_VERSION",
|
|
134
|
+
label: "Engine version",
|
|
135
|
+
purpose: "The caller's engine build, which this template's must match while norsk-link needs one build at both ends. Filled by the caller.",
|
|
136
|
+
required: false,
|
|
137
|
+
},
|
|
138
|
+
conversionId: {
|
|
139
|
+
name: "CONVERSION_ID",
|
|
140
|
+
label: "Conversion id",
|
|
141
|
+
purpose: "The caller's id for this conversion, for logs and correlation. Filled by the caller.",
|
|
142
|
+
required: false,
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
const BINDING_GROUP = "Conversion link";
|
|
146
|
+
/** A norsk.io/media-conversion v1 declaration and the parameters its
|
|
147
|
+
* bindings name, built together so a template cannot bind a parameter it
|
|
148
|
+
* never declared. Spread `parameters` into the template's parameters.yaml and
|
|
149
|
+
* `interface` into its manifest's `interfaces`. Each binding defaults to a
|
|
150
|
+
* conventional parameter name; pass a name to use the template's own, or
|
|
151
|
+
* null to leave an optional role out. */
|
|
152
|
+
export function declareMediaConversion(shape) {
|
|
153
|
+
const { bindings: chosen = {}, ...rest } = shape;
|
|
154
|
+
const bindings = {};
|
|
155
|
+
const parameters = [];
|
|
156
|
+
for (const role of Object.keys(BINDING_DEFAULTS)) {
|
|
157
|
+
const fallback = BINDING_DEFAULTS[role];
|
|
158
|
+
const name = chosen[role] === undefined ? fallback.name : chosen[role];
|
|
159
|
+
if (name === null)
|
|
160
|
+
continue;
|
|
161
|
+
bindings[role] = name;
|
|
162
|
+
parameters.push({
|
|
163
|
+
name,
|
|
164
|
+
purpose: fallback.purpose,
|
|
165
|
+
type: "string",
|
|
166
|
+
label: fallback.label,
|
|
167
|
+
group: BINDING_GROUP,
|
|
168
|
+
...(fallback.required ? { required: true } : {}),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
const declaration = MediaConversionV1Schema.parse({
|
|
172
|
+
name: MEDIA_CONVERSION_INTERFACE,
|
|
173
|
+
version: 1,
|
|
174
|
+
...rest,
|
|
175
|
+
bindings,
|
|
176
|
+
});
|
|
177
|
+
return { interface: declaration, parameters };
|
|
178
|
+
}
|
package/logs.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { z } from "zod";
|
|
|
2
2
|
export declare const ServiceLogFileSchema: z.ZodObject<{
|
|
3
3
|
path: z.ZodString;
|
|
4
4
|
format: z.ZodEnum<{
|
|
5
|
-
json: "json";
|
|
6
5
|
text: "text";
|
|
6
|
+
json: "json";
|
|
7
7
|
}>;
|
|
8
8
|
}, z.core.$strict>;
|
|
9
9
|
export declare const ServiceFluentBitSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
@@ -13,15 +13,15 @@ export declare const ServiceFluentBitSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
13
13
|
}, z.core.$strict>]>;
|
|
14
14
|
export declare const ServiceLogsSchema: z.ZodObject<{
|
|
15
15
|
stdout: z.ZodOptional<z.ZodEnum<{
|
|
16
|
-
json: "json";
|
|
17
|
-
text: "text";
|
|
18
16
|
none: "none";
|
|
17
|
+
text: "text";
|
|
18
|
+
json: "json";
|
|
19
19
|
}>>;
|
|
20
20
|
files: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
21
21
|
path: z.ZodString;
|
|
22
22
|
format: z.ZodEnum<{
|
|
23
|
-
json: "json";
|
|
24
23
|
text: "text";
|
|
24
|
+
json: "json";
|
|
25
25
|
}>;
|
|
26
26
|
}, z.core.$strict>>>;
|
|
27
27
|
fluentBit: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
|
|
@@ -33,15 +33,15 @@ export declare const ServiceLogsSchema: z.ZodObject<{
|
|
|
33
33
|
export type ServiceLogs = z.infer<typeof ServiceLogsSchema>;
|
|
34
34
|
export declare const ProductTemplateLogsSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
35
35
|
stdout: z.ZodOptional<z.ZodEnum<{
|
|
36
|
-
json: "json";
|
|
37
|
-
text: "text";
|
|
38
36
|
none: "none";
|
|
37
|
+
text: "text";
|
|
38
|
+
json: "json";
|
|
39
39
|
}>>;
|
|
40
40
|
files: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
41
41
|
path: z.ZodString;
|
|
42
42
|
format: z.ZodEnum<{
|
|
43
|
-
json: "json";
|
|
44
43
|
text: "text";
|
|
44
|
+
json: "json";
|
|
45
45
|
}>;
|
|
46
46
|
}, z.core.$strict>>>;
|
|
47
47
|
fluentBit: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
|