@norskvideo/ctl-product-template-schema 0.1.1 → 0.1.3

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
@@ -50,6 +50,11 @@ export declare const ProductTemplateManifestSchema: z.ZodObject<{
50
50
  runtimeScreenUrl: z.ZodString;
51
51
  runtimeScreenLabel: z.ZodOptional<z.ZodString>;
52
52
  runtimeScreenService: z.ZodOptional<z.ZodString>;
53
+ runtimeScreenReadyWhen: z.ZodOptional<z.ZodEnum<{
54
+ running: "running";
55
+ healthy: "healthy";
56
+ never: "never";
57
+ }>>;
53
58
  }, z.core.$strip>>;
54
59
  debug: z.ZodOptional<z.ZodObject<{
55
60
  studio: z.ZodOptional<z.ZodBoolean>;
@@ -154,6 +159,11 @@ export declare const ProductTemplateMaterialsSchema: z.ZodObject<{
154
159
  runtimeScreenUrl: z.ZodString;
155
160
  runtimeScreenLabel: z.ZodOptional<z.ZodString>;
156
161
  runtimeScreenService: z.ZodOptional<z.ZodString>;
162
+ runtimeScreenReadyWhen: z.ZodOptional<z.ZodEnum<{
163
+ running: "running";
164
+ healthy: "healthy";
165
+ never: "never";
166
+ }>>;
157
167
  }, z.core.$strip>>;
158
168
  debug: z.ZodOptional<z.ZodObject<{
159
169
  studio: z.ZodOptional<z.ZodBoolean>;
package/index.js CHANGED
@@ -31,6 +31,9 @@ const ProductTemplateUiSchema = z.object({
31
31
  runtimeScreenService: z.string().optional().meta({
32
32
  description: "Compose service that hosts the runtime screen. Omit for the conventional `studio`; a product that serves its dashboard from a different service names it here so the runner routes the catch-all there.",
33
33
  }),
34
+ runtimeScreenReadyWhen: z.enum(["running", "healthy", "never"]).optional().meta({
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
+ }),
34
37
  });
35
38
  // Per-instance Debug menu toggles. The runner always offers raw Studio +
36
39
  // Visualiser debug surfaces for an instance; a product opts a link out when
@@ -127,7 +130,7 @@ export const ProductTemplateManifestSchema = z
127
130
  description: "Settings-backed runner knobs the operator may override per instance. Key presence opts the knob into the launch form; `default` pre-fills it (else the runner's daemon-wide default applies).",
128
131
  }),
129
132
  allocatedPorts: z.array(ProductTemplateAllocatedPortSchema).optional().meta({
130
- description: "Host-port bindings whose value rides a product-template parameter. Opt-in per launch via the param value: absent = closed, 'auto' = allocate a free port from `preferred`, number = use it. When opened the port is published as a managed host binding and surfaced to the containers via compose interpolation.",
133
+ description: "Host-port bindings whose value rides a product-template parameter. Opt-in per launch via the param value: absent = closed, 'auto' = allocate a free port from `preferred`, number = use it. When opened the port is published as a managed host binding and surfaced to the containers via compose interpolation. Alongside the per-param scalars the runner sets NORSK_ALLOCATED_PORTS: a JSON object keyed by param, each value {port, protocol, service?}, describing every opened port ('{}' when none opted in; unset when nothing is declared) — so a product needing the allocation as data maps one var instead of folding scalars into JSON via interpolation.",
131
134
  }),
132
135
  })
133
136
  .meta({ id: "ProductTemplateManifest", outputId: "ProductTemplateManifest" });
@@ -191,7 +194,8 @@ const ProductTemplateFileSchema = z.object({
191
194
  // accepts both via the same union.
192
195
  content: z.union([z.string(), z.instanceof(Uint8Array)]),
193
196
  });
194
- // In-memory bundle the producer builds; the tar packer turns this into bytes.
197
+ // In-memory bundle the producer builds; packProductTemplate (the `./pack`
198
+ // subpath) turns this into tar bytes at the paths below.
195
199
  //
196
200
  // `workflow`, `components` and `dashboards` are known top-level paths in the
197
201
  // v1 tar format. The runner detects studio in the compose stack and mounts
package/pack.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { type TarEntry } from "@norskvideo/ctl-foundation/browser";
2
+ import type { ProductTemplateMaterials } from "./index.js";
3
+ export declare function productTemplateTarEntries(materials: ProductTemplateMaterials): TarEntry[];
4
+ /** `mtime` stamps every tar entry's header with a fixed modification time.
5
+ * Pass it (alongside a fixed manifest `generatedAt`) for a reproducible
6
+ * build — without it packTar defaults each entry to the wall clock, so the
7
+ * bytes differ run to run. */
8
+ export declare function packProductTemplate(materials: ProductTemplateMaterials, mtime?: Date): Uint8Array;
package/pack.js ADDED
@@ -0,0 +1,48 @@
1
+ // The producer half of the product-template wire format: materials -> tar
2
+ // entries -> tar bytes, at the v1 paths documented on
3
+ // ProductTemplateMaterialsSchema in index.ts. Every product repo used to carry
4
+ // its own copy of this layout and the copies drifted (several silently dropped
5
+ // workdir-seed/); this is the single source. The runner consumes the same
6
+ // paths on the reader side (manifest.json / parameters.yaml / compose.yml
7
+ // validation, workflow.yml + components/ + dashboards/ mounts, workdir-seed/
8
+ // launch copy), so layout changes here are contract changes — additive only.
9
+ //
10
+ // Browser-safe: foundation's packTar and the yaml stringifier are both pure.
11
+ import { packTar } from "@norskvideo/ctl-foundation/browser";
12
+ import { stringify as yamlStringify } from "yaml";
13
+ export function productTemplateTarEntries(materials) {
14
+ const entries = [
15
+ { name: "manifest.json", content: `${JSON.stringify(materials.manifest, null, 2)}\n` },
16
+ { name: "compose.yml", content: materials.composeYaml },
17
+ ];
18
+ if (materials.workflow !== undefined) {
19
+ entries.push({ name: "workflow.yml", content: materials.workflow });
20
+ }
21
+ if (materials.parameters) {
22
+ entries.push({
23
+ name: "parameters.yaml",
24
+ content: yamlStringify(materials.parameters, { aliasDuplicateObjects: false }),
25
+ });
26
+ }
27
+ for (const c of materials.components) {
28
+ entries.push({ name: `components/${c.path}`, content: c.content });
29
+ }
30
+ for (const d of materials.dashboards) {
31
+ entries.push({ name: `dashboards/${d.path}`, content: d.content });
32
+ }
33
+ for (const a of materials.assets) {
34
+ entries.push({ name: `assets/${a.path}`, content: a.content });
35
+ }
36
+ for (const s of materials.workdirSeed) {
37
+ entries.push({ name: `workdir-seed/${s.path}`, content: s.content });
38
+ }
39
+ return entries;
40
+ }
41
+ /** `mtime` stamps every tar entry's header with a fixed modification time.
42
+ * Pass it (alongside a fixed manifest `generatedAt`) for a reproducible
43
+ * build — without it packTar defaults each entry to the wall clock, so the
44
+ * bytes differ run to run. */
45
+ export function packProductTemplate(materials, mtime) {
46
+ const entries = productTemplateTarEntries(materials);
47
+ return packTar(mtime ? entries.map((e) => ({ ...e, mtime })) : entries);
48
+ }
package/package.json CHANGED
@@ -1,12 +1,16 @@
1
1
  {
2
2
  "name": "@norskvideo/ctl-product-template-schema",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
7
7
  "types": "./index.d.ts",
8
8
  "default": "./index.js"
9
9
  },
10
+ "./pack": {
11
+ "types": "./pack.d.ts",
12
+ "default": "./pack.js"
13
+ },
10
14
  "./params": {
11
15
  "types": "./params.d.ts",
12
16
  "default": "./params.js"
@@ -15,6 +19,8 @@
15
19
  "main": "./index.js",
16
20
  "types": "./index.d.ts",
17
21
  "dependencies": {
22
+ "@norskvideo/ctl-foundation": "^0.1.0",
23
+ "yaml": "^2.8.0",
18
24
  "zod": "^4.3.6"
19
25
  },
20
26
  "publishConfig": {