@norskvideo/ctl-product-template-schema 0.1.14 → 0.1.16
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 +17 -0
- package/index.js +41 -0
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export declare const ProductTemplateTargetSchema: z.ZodEnum<{
|
|
|
3
3
|
"docker-compose": "docker-compose";
|
|
4
4
|
}>;
|
|
5
5
|
export type ProductTemplateTarget = z.infer<typeof ProductTemplateTargetSchema>;
|
|
6
|
+
export declare const RESERVED_INSTANCE_ROUTE_SEGMENTS: readonly ["studio", "media", "visualiser", "uvis", "status", "metrics"];
|
|
6
7
|
export declare const PRODUCT_TEMPLATE_ADVANCED_NETWORK_MODES: readonly ["docker", "hybrid"];
|
|
7
8
|
export declare const ProductTemplateAdvancedSchema: z.ZodObject<{
|
|
8
9
|
networkMode: z.ZodOptional<z.ZodObject<{
|
|
@@ -115,6 +116,7 @@ export declare const ProductTemplateManifestSchema: z.ZodObject<{
|
|
|
115
116
|
healthy: "healthy";
|
|
116
117
|
never: "never";
|
|
117
118
|
}>>;
|
|
119
|
+
runtimeReadyPath: z.ZodOptional<z.ZodString>;
|
|
118
120
|
}, z.core.$strip>>;
|
|
119
121
|
debug: z.ZodOptional<z.ZodObject<{
|
|
120
122
|
studio: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -122,6 +124,13 @@ export declare const ProductTemplateManifestSchema: z.ZodObject<{
|
|
|
122
124
|
}, z.core.$strip>>;
|
|
123
125
|
proxy: z.ZodOptional<z.ZodObject<{
|
|
124
126
|
expose: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
127
|
+
routes: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
128
|
+
segment: z.ZodString;
|
|
129
|
+
service: z.ZodString;
|
|
130
|
+
port: z.ZodOptional<z.ZodNumber>;
|
|
131
|
+
websocket: z.ZodOptional<z.ZodBoolean>;
|
|
132
|
+
auth: z.ZodOptional<z.ZodBoolean>;
|
|
133
|
+
}, z.core.$strip>>>;
|
|
125
134
|
}, z.core.$strip>>;
|
|
126
135
|
requiresWorkingDirectory: z.ZodOptional<z.ZodBoolean>;
|
|
127
136
|
sideload: z.ZodOptional<z.ZodObject<{
|
|
@@ -282,6 +291,7 @@ export declare const ProductTemplateMaterialsSchema: z.ZodObject<{
|
|
|
282
291
|
healthy: "healthy";
|
|
283
292
|
never: "never";
|
|
284
293
|
}>>;
|
|
294
|
+
runtimeReadyPath: z.ZodOptional<z.ZodString>;
|
|
285
295
|
}, z.core.$strip>>;
|
|
286
296
|
debug: z.ZodOptional<z.ZodObject<{
|
|
287
297
|
studio: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -289,6 +299,13 @@ export declare const ProductTemplateMaterialsSchema: z.ZodObject<{
|
|
|
289
299
|
}, z.core.$strip>>;
|
|
290
300
|
proxy: z.ZodOptional<z.ZodObject<{
|
|
291
301
|
expose: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
302
|
+
routes: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
303
|
+
segment: z.ZodString;
|
|
304
|
+
service: z.ZodString;
|
|
305
|
+
port: z.ZodOptional<z.ZodNumber>;
|
|
306
|
+
websocket: z.ZodOptional<z.ZodBoolean>;
|
|
307
|
+
auth: z.ZodOptional<z.ZodBoolean>;
|
|
308
|
+
}, z.core.$strip>>>;
|
|
292
309
|
}, z.core.$strip>>;
|
|
293
310
|
requiresWorkingDirectory: z.ZodOptional<z.ZodBoolean>;
|
|
294
311
|
sideload: z.ZodOptional<z.ZodObject<{
|
package/index.js
CHANGED
|
@@ -34,6 +34,9 @@ const ProductTemplateUiSchema = z.object({
|
|
|
34
34
|
runtimeScreenReadyWhen: z.enum(["running", "healthy", "never"]).optional().meta({
|
|
35
35
|
description: "When the runner may offer the runtime-screen link, judged on `runtimeScreenService`'s own container state. Omit for the default `running` — the link goes live as soon as the service is up, which is the only state a service without a healthcheck ever reaches. `healthy` holds it back until the service's healthcheck passes, for a product whose screen 404s or misbehaves before it is fully warm. `never` suppresses the link entirely.",
|
|
36
36
|
}),
|
|
37
|
+
runtimeReadyPath: z.string().startsWith("/").optional().meta({
|
|
38
|
+
description: "HTTP path on `runtimeScreenService` that answers 2xx only once the product's workflow is actually running — for a Studio-hosted product, `/live/api/components`, which 503s until the runner is up. Container health cannot tell: Studio's web server stays healthy after its runner has exited, so a workflow refused at start reads as `healthy` without this. The runner polls the path into the instance's `workflow` facet (`starting` | `running` | `refused`). Omit and the facet is absent — never guessed.",
|
|
39
|
+
}),
|
|
37
40
|
});
|
|
38
41
|
// Per-instance Debug menu toggles. The runner always offers raw Studio +
|
|
39
42
|
// Visualiser debug surfaces for an instance; a product opts a link out when
|
|
@@ -47,10 +50,48 @@ const ProductTemplateDebugSchema = z.object({
|
|
|
47
50
|
.optional()
|
|
48
51
|
.meta({ description: "Show the Debug menu's 'Open Visualiser' link. Default true." }),
|
|
49
52
|
});
|
|
53
|
+
// Path segments the runner owns under /instance/<id>/ and a product may never
|
|
54
|
+
// claim. They are the six base routes instance-routes.ts always emits; a
|
|
55
|
+
// declared route colliding with one would be shadowed by nginx's more-specific
|
|
56
|
+
// match and silently never serve, so the schema rejects it instead.
|
|
57
|
+
export const RESERVED_INSTANCE_ROUTE_SEGMENTS = ["studio", "media", "visualiser", "uvis", "status", "metrics"];
|
|
58
|
+
// One additional HTTP service a product wants behind the runner's front door,
|
|
59
|
+
// beside the runtime-screen catch-all. WHY THIS EXISTS: a product's second UI
|
|
60
|
+
// otherwise needs a published host port, and a plain http host port is a secure
|
|
61
|
+
// context on localhost only — browser APIs like WebCodecs fail from any other
|
|
62
|
+
// machine. The catch-all is a single slot, so a product with two UIs used to
|
|
63
|
+
// have to reverse-proxy one behind the other itself, websocket bridging
|
|
64
|
+
// included.
|
|
65
|
+
const ProductTemplateRouteSchema = z.object({
|
|
66
|
+
segment: z
|
|
67
|
+
.string()
|
|
68
|
+
.regex(/^[a-z0-9][a-z0-9-]*$/, "one lowercase path segment: letters, digits and dashes")
|
|
69
|
+
.refine((s) => !RESERVED_INSTANCE_ROUTE_SEGMENTS.includes(s), {
|
|
70
|
+
message: `segment is reserved by the runner (${RESERVED_INSTANCE_ROUTE_SEGMENTS.join(", ")})`,
|
|
71
|
+
})
|
|
72
|
+
.meta({
|
|
73
|
+
description: "Single path segment under the instance root. The runner serves it at /instance/<id>/<segment>/. Must not be one of the runner's reserved segments (studio, media, visualiser, uvis, status, metrics).",
|
|
74
|
+
}),
|
|
75
|
+
service: z.string().min(1).meta({
|
|
76
|
+
description: "Compose service that serves this route. Must be a service in the template's own compose.yml; the runner dials it by its compose container name.",
|
|
77
|
+
}),
|
|
78
|
+
port: z.number().int().positive().optional().meta({
|
|
79
|
+
description: "Container port to dial. Omit for the conventional 8000.",
|
|
80
|
+
}),
|
|
81
|
+
websocket: z.boolean().optional().meta({
|
|
82
|
+
description: "Emit the Upgrade/Connection proxy headers so this route can carry websockets. Omit for a plain HTTP route. HTTP is always served; this only adds the upgrade path, so a route carrying both needs just this flag.",
|
|
83
|
+
}),
|
|
84
|
+
auth: z.boolean().optional().meta({
|
|
85
|
+
description: "Whether the route sits behind the runner's oauth2 auth_request. Omit for the default `true`. Set false only for a surface that must be reachable unauthenticated, as the media route is.",
|
|
86
|
+
}),
|
|
87
|
+
});
|
|
50
88
|
const ProductTemplateProxySchema = z.object({
|
|
51
89
|
expose: z.array(z.string()).default([]).meta({
|
|
52
90
|
description: "Paths (or globs) the runner's nginx routes through to this instance. Anything not in the list returns 404 from the proxy. Glob-shaped strings (`/static/*`) are accepted as hints — current implementation treats the whole set as a signal to mount a catch-all on the instance root; per-path filtering can come later.",
|
|
53
91
|
}),
|
|
92
|
+
routes: z.array(ProductTemplateRouteSchema).optional().meta({
|
|
93
|
+
description: "Extra named services to route under the instance root, beside the runtime-screen catch-all. Additive and optional: a template that omits it routes exactly as before, so an older runner ignoring the field still launches the product.",
|
|
94
|
+
}),
|
|
54
95
|
});
|
|
55
96
|
// product-template-schema cannot import @norsk-ctl/shared (dependency direction), so
|
|
56
97
|
// this is a literal copy of the runner's network-mode enum — and the runner
|