@norskvideo/ctl-product-template-schema 0.1.53 → 0.1.55

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 CHANGED
@@ -4,6 +4,11 @@ export declare const ProductTemplateTargetSchema: z.ZodEnum<{
4
4
  }>;
5
5
  export type ProductTemplateTarget = z.infer<typeof ProductTemplateTargetSchema>;
6
6
  export declare const RESERVED_INSTANCE_ROUTE_SEGMENTS: readonly ["studio", "media", "visualiser", "uvis", "status", "metrics"];
7
+ /** How a template's runtimes appear on fleet dashboards (RFC 0006 §4.1). */
8
+ export declare const ProductTemplateDashboardSchema: z.ZodObject<{
9
+ kind: z.ZodString;
10
+ previews: z.ZodOptional<z.ZodArray<z.ZodString>>;
11
+ }, z.core.$strip>;
7
12
  export declare const PRODUCT_TEMPLATE_ADVANCED_NETWORK_MODES: readonly ["docker", "hybrid"];
8
13
  export declare const ProductTemplateAdvancedSchema: z.ZodObject<{
9
14
  networkMode: z.ZodOptional<z.ZodObject<{
@@ -36,6 +41,9 @@ export declare const ProductTemplateAdvancedSchema: z.ZodObject<{
36
41
  openFileLimit: z.ZodOptional<z.ZodObject<{
37
42
  default: z.ZodOptional<z.ZodNumber>;
38
43
  }, z.core.$strip>>;
44
+ memoryLimit: z.ZodOptional<z.ZodObject<{
45
+ default: z.ZodOptional<z.ZodString>;
46
+ }, z.core.$strip>>;
39
47
  }, z.core.$strip>;
40
48
  export declare const ProductTemplateAllocatedPortSchema: z.ZodObject<{
41
49
  param: z.ZodString;
@@ -197,6 +205,14 @@ export declare const ProductTemplateManifestSchema: z.ZodObject<{
197
205
  websocket: z.ZodOptional<z.ZodBoolean>;
198
206
  auth: z.ZodOptional<z.ZodBoolean>;
199
207
  }, z.core.$strip>>>;
208
+ operations: z.ZodOptional<z.ZodArray<z.ZodObject<{
209
+ path: z.ZodString;
210
+ openapi: z.ZodString;
211
+ }, z.core.$strip>>>;
212
+ }, z.core.$strip>>;
213
+ dashboard: z.ZodOptional<z.ZodObject<{
214
+ kind: z.ZodString;
215
+ previews: z.ZodOptional<z.ZodArray<z.ZodString>>;
200
216
  }, z.core.$strip>>;
201
217
  allowWorkflowUploads: z.ZodOptional<z.ZodBoolean>;
202
218
  isStudio: z.ZodOptional<z.ZodBoolean>;
@@ -240,6 +256,9 @@ export declare const ProductTemplateManifestSchema: z.ZodObject<{
240
256
  openFileLimit: z.ZodOptional<z.ZodObject<{
241
257
  default: z.ZodOptional<z.ZodNumber>;
242
258
  }, z.core.$strip>>;
259
+ memoryLimit: z.ZodOptional<z.ZodObject<{
260
+ default: z.ZodOptional<z.ZodString>;
261
+ }, z.core.$strip>>;
243
262
  }, z.core.$strip>>;
244
263
  requirements: z.ZodOptional<z.ZodObject<{
245
264
  capacity: z.ZodOptional<z.ZodObject<{
@@ -458,6 +477,14 @@ export declare const ProductTemplateMaterialsSchema: z.ZodObject<{
458
477
  websocket: z.ZodOptional<z.ZodBoolean>;
459
478
  auth: z.ZodOptional<z.ZodBoolean>;
460
479
  }, z.core.$strip>>>;
480
+ operations: z.ZodOptional<z.ZodArray<z.ZodObject<{
481
+ path: z.ZodString;
482
+ openapi: z.ZodString;
483
+ }, z.core.$strip>>>;
484
+ }, z.core.$strip>>;
485
+ dashboard: z.ZodOptional<z.ZodObject<{
486
+ kind: z.ZodString;
487
+ previews: z.ZodOptional<z.ZodArray<z.ZodString>>;
461
488
  }, z.core.$strip>>;
462
489
  allowWorkflowUploads: z.ZodOptional<z.ZodBoolean>;
463
490
  isStudio: z.ZodOptional<z.ZodBoolean>;
@@ -501,6 +528,9 @@ export declare const ProductTemplateMaterialsSchema: z.ZodObject<{
501
528
  openFileLimit: z.ZodOptional<z.ZodObject<{
502
529
  default: z.ZodOptional<z.ZodNumber>;
503
530
  }, z.core.$strip>>;
531
+ memoryLimit: z.ZodOptional<z.ZodObject<{
532
+ default: z.ZodOptional<z.ZodString>;
533
+ }, z.core.$strip>>;
504
534
  }, z.core.$strip>>;
505
535
  requirements: z.ZodOptional<z.ZodObject<{
506
536
  capacity: z.ZodOptional<z.ZodObject<{
@@ -665,6 +695,10 @@ export declare const ProductTemplateMaterialsSchema: z.ZodObject<{
665
695
  path: z.ZodString;
666
696
  content: z.ZodUnion<readonly [z.ZodString, z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>]>;
667
697
  }, z.core.$strip>>>;
698
+ openapi: z.ZodOptional<z.ZodArray<z.ZodObject<{
699
+ path: z.ZodString;
700
+ content: z.ZodUnion<readonly [z.ZodString, z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>]>;
701
+ }, z.core.$strip>>>;
668
702
  workdirSeed: z.ZodDefault<z.ZodArray<z.ZodObject<{
669
703
  path: z.ZodString;
670
704
  content: z.ZodUnion<readonly [z.ZodString, z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>]>;
package/index.js CHANGED
@@ -87,6 +87,36 @@ const ProductTemplateRouteSchema = z.object({
87
87
  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.",
88
88
  }),
89
89
  });
90
+ // The operations a template serves, each described by an OpenAPI document it
91
+ // carries under openapi/ (RFC 0006 §4.1, norsk-ctl task 123). Keyed by the
92
+ // instance path the document describes, so it covers the runtime-screen
93
+ // catch-all as well as declared routes. An operation is reachable from a
94
+ // dashboard only when marked `x-norsk-dashboard: true`, and needs the
95
+ // platform rung its `x-norsk-role` names on the runtime.
96
+ const SAFE_SEGMENTS = (value) => !value.split("/").some((s) => s === ".." || s === ".");
97
+ const ProductTemplateOperationsSchema = z.object({
98
+ path: z
99
+ .string()
100
+ .regex(/^\/[A-Za-z0-9._~-]+(\/[A-Za-z0-9._~-]+)*$/, "an absolute path under the instance, no trailing slash")
101
+ .refine(SAFE_SEGMENTS, { message: "no . or .. segments" })
102
+ .meta({
103
+ description: "The path under /instance/<id>/ the document's paths are relative to, e.g. /live/api/probe-api.",
104
+ }),
105
+ openapi: z
106
+ .string()
107
+ .regex(/^openapi\/[A-Za-z0-9._-]+(\/[A-Za-z0-9._-]+)*\.(ya?ml|json)$/, "a YAML or JSON file under openapi/")
108
+ .refine(SAFE_SEGMENTS, { message: "no . or .. segments" })
109
+ .meta({ description: "The OpenAPI 3 document in the template tar, e.g. openapi/probe-api.yaml." }),
110
+ });
111
+ /** How a template's runtimes appear on fleet dashboards (RFC 0006 §4.1). */
112
+ export const ProductTemplateDashboardSchema = z.object({
113
+ kind: z.string().min(1).meta({
114
+ description: "The discovery kind a runtime of this template announces itself as, e.g. norsk-probe.monitor.",
115
+ }),
116
+ previews: z.array(z.string().min(1)).optional().meta({
117
+ description: "The roles of the MoQ previews it offers, e.g. main.",
118
+ }),
119
+ });
90
120
  const ProductTemplateProxySchema = z.object({
91
121
  expose: z.array(z.string()).default([]).meta({
92
122
  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.",
@@ -94,6 +124,9 @@ const ProductTemplateProxySchema = z.object({
94
124
  routes: z.array(ProductTemplateRouteSchema).optional().meta({
95
125
  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.",
96
126
  }),
127
+ operations: z.array(ProductTemplateOperationsSchema).optional().meta({
128
+ description: "The operations this template's runtimes serve, each set described by an OpenAPI document in the tar; the ones marked x-norsk-dashboard are what fleet dashboards may call, each decided per user by the platform. Additive: a runner that predates it ignores it.",
129
+ }),
97
130
  });
98
131
  // product-template-schema cannot import @norsk-ctl/shared (dependency direction), so
99
132
  // this is a literal copy of the runner's network-mode enum — and the runner
@@ -146,6 +179,12 @@ export const ProductTemplateAdvancedSchema = z
146
179
  // and not others) — so this is how a product that needs a HIGHER one says
147
180
  // so, not how it gets a ceiling at all. Instance-wide, not per-service.
148
181
  openFileLimit: z.object({ default: z.number().int().min(1024).max(1048576).optional() }).optional(),
182
+ // Memory ceiling for each of the instance's containers (docker `mem_limit`
183
+ // syntax, e.g. "24g"; swap is capped at the same value). The runner states
184
+ // one for every instance (16g) — an engine with none once drove a shared
185
+ // host into a global OOM kill — so this is how a product that needs MORE
186
+ // says so. "unlimited" opts the product out of a ceiling altogether.
187
+ memoryLimit: z.object({ default: z.string().optional() }).optional(),
149
188
  })
150
189
  .meta({ id: "ProductTemplateAdvanced", outputId: "ProductTemplateAdvanced" });
151
190
  // A host-port binding whose value rides a product-template parameter, opted in per
@@ -362,6 +401,9 @@ export const ProductTemplateManifestSchema = z
362
401
  description: "Per-instance Debug menu toggles. Each of `studio`/`visualiser` defaults to shown; set false to hide a redundant link (e.g. Studio hides its own 'Open Studio').",
363
402
  }),
364
403
  proxy: ProductTemplateProxySchema.optional(),
404
+ dashboard: ProductTemplateDashboardSchema.optional().meta({
405
+ description: "How this template's runtimes appear on fleet dashboards: the kind they announce, and their previews. Absent = not on dashboards.",
406
+ }),
365
407
  allowWorkflowUploads: z.boolean().optional().meta({
366
408
  description: "Whether the operator may upload workflow documents for this product. They land in the working directory's studio-save-files/, where Studio lists them for the operator to open — so this is for Norsk Studio itself (the editor), not for appliances built on it: an appliance (probe, playout) generates its one workflow from its config screen and has nothing to select. Absent = false. Assets are governed separately by `supportsCustomAssets`.",
367
409
  }),
@@ -519,6 +561,9 @@ export const ProductTemplateMaterialsSchema = z
519
561
  .array(ProductTemplateFileSchema)
520
562
  .default([])
521
563
  .meta({ description: "Free-form product files — packed under assets/ in the tar" }),
564
+ openapi: z.array(ProductTemplateFileSchema).optional().meta({
565
+ description: "The OpenAPI documents proxy.operations names, packed under openapi/ in the tar. Optional so materials built before it still typecheck.",
566
+ }),
522
567
  workdirSeed: z.array(ProductTemplateFileSchema).default([]).meta({
523
568
  description: "Files copied recursively into the launch workdir at launch time. Packed under workdir-seed/ in the tar. Existing files in the launch workdir win on conflict (the seed only fills gaps), so re-launches and operator-edits-in-place are non-destructive. Lets a product template ship a 'starter pack' of demo workflows / dashboards / components that the runtime sees in /data on first launch.",
524
569
  }),
package/pack.js CHANGED
@@ -33,6 +33,9 @@ export function productTemplateTarEntries(materials) {
33
33
  for (const a of materials.assets) {
34
34
  entries.push({ name: `assets/${a.path}`, content: a.content });
35
35
  }
36
+ for (const o of materials.openapi ?? []) {
37
+ entries.push({ name: `openapi/${o.path}`, content: o.content });
38
+ }
36
39
  for (const s of materials.workdirSeed) {
37
40
  entries.push({ name: `workdir-seed/${s.path}`, content: s.content });
38
41
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@norskvideo/ctl-product-template-schema",
3
- "version": "0.1.53",
3
+ "version": "0.1.55",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {