@norskvideo/ctl-product-template-schema 0.1.2 → 0.1.4
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 +10 -0
- package/index.js +3 -0
- package/package.json +5 -1
- package/read.d.ts +33 -0
- package/read.js +67 -0
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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@norskvideo/ctl-product-template-schema",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
"./params": {
|
|
15
15
|
"types": "./params.d.ts",
|
|
16
16
|
"default": "./params.js"
|
|
17
|
+
},
|
|
18
|
+
"./read": {
|
|
19
|
+
"types": "./read.d.ts",
|
|
20
|
+
"default": "./read.js"
|
|
17
21
|
}
|
|
18
22
|
},
|
|
19
23
|
"main": "./index.js",
|
package/read.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ProductTemplateParameter } from "./index.js";
|
|
2
|
+
import { type ProductTemplateManifest } from "./index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Error taxonomy for product-template lifecycle issues. Codes cover both the
|
|
5
|
+
* "store + extract" path (ctl's ProductTemplateService — extraction, manifest
|
|
6
|
+
* parsing, in-use guarding) and the simpler "store raw bytes" path (mgr —
|
|
7
|
+
* name validation + bytes persistence). Lives with the contract it reports
|
|
8
|
+
* on; @norskvideo/ctl-sdk re-exports it for existing importers.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ProductTemplateError extends Error {
|
|
11
|
+
code: "INVALID_NAME" | "NAME_CONFLICT" | "FILE_NOT_FOUND" | "EXTRACTION_FAILED" | "MANIFEST_MISSING" | "MANIFEST_INVALID" | "COMPOSE_MISSING" | "PARAMETERS_INVALID" | "NOT_FOUND" | "IN_USE" | "PRODUCT_MISMATCH";
|
|
12
|
+
constructor(code: "INVALID_NAME" | "NAME_CONFLICT" | "FILE_NOT_FOUND" | "EXTRACTION_FAILED" | "MANIFEST_MISSING" | "MANIFEST_INVALID" | "COMPOSE_MISSING" | "PARAMETERS_INVALID" | "NOT_FOUND" | "IN_USE" | "PRODUCT_MISMATCH", message: string);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Where the template's root members come from — the seam between the shared
|
|
16
|
+
* contract and each runner's read mechanics. Two real adapters: ctl reads an
|
|
17
|
+
* extracted directory; mgr streams single members with `tar -xOf`.
|
|
18
|
+
* Returns null for an absent member.
|
|
19
|
+
*/
|
|
20
|
+
export interface TarSource {
|
|
21
|
+
readMember(name: string): Promise<string | null> | string | null;
|
|
22
|
+
}
|
|
23
|
+
export interface ProductTemplateContents {
|
|
24
|
+
manifest: ProductTemplateManifest;
|
|
25
|
+
/** `[]` when the template ships no parameters.yaml. */
|
|
26
|
+
parameters: ProductTemplateParameter[];
|
|
27
|
+
/** Whether compose.yml exists at the root. Enforcement is caller policy. */
|
|
28
|
+
hasCompose: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** Validate a product template's members. Throws ProductTemplateError with
|
|
31
|
+
* the canonical taxonomy; forward compatibility (RFC 0001) rides on the
|
|
32
|
+
* schemas' strip-unknown behaviour, pinned in this package's tests. */
|
|
33
|
+
export declare function readProductTemplate(source: TarSource): Promise<ProductTemplateContents>;
|
package/read.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// The reading half of the ctl↔product tar contract: which members a product
|
|
2
|
+
// template carries, in what order they are validated, and what error each
|
|
3
|
+
// failure maps to. Both runners re-implemented this (ctl over an extracted
|
|
4
|
+
// dir, mgr over streamed tar members) with byte-identical error strings —
|
|
5
|
+
// the contract now lives beside the schemas it validates, and the read
|
|
6
|
+
// mechanics stay with the callers as TarSource adapters.
|
|
7
|
+
//
|
|
8
|
+
// Policy stays with the callers too: ctl's import refuses a compose-less tar,
|
|
9
|
+
// mgr (which stores raw bytes for relay) does not — so compose presence is a
|
|
10
|
+
// reported fact here, not a verdict.
|
|
11
|
+
import { ParametersFileSchema, ProductTemplateManifestSchema } from "./index.js";
|
|
12
|
+
/**
|
|
13
|
+
* Error taxonomy for product-template lifecycle issues. Codes cover both the
|
|
14
|
+
* "store + extract" path (ctl's ProductTemplateService — extraction, manifest
|
|
15
|
+
* parsing, in-use guarding) and the simpler "store raw bytes" path (mgr —
|
|
16
|
+
* name validation + bytes persistence). Lives with the contract it reports
|
|
17
|
+
* on; @norskvideo/ctl-sdk re-exports it for existing importers.
|
|
18
|
+
*/
|
|
19
|
+
export class ProductTemplateError extends Error {
|
|
20
|
+
code;
|
|
21
|
+
constructor(code, message) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.code = code;
|
|
24
|
+
this.name = "ProductTemplateError";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Validate a product template's members. Throws ProductTemplateError with
|
|
28
|
+
* the canonical taxonomy; forward compatibility (RFC 0001) rides on the
|
|
29
|
+
* schemas' strip-unknown behaviour, pinned in this package's tests. */
|
|
30
|
+
export async function readProductTemplate(source) {
|
|
31
|
+
const manifestText = await source.readMember("manifest.json");
|
|
32
|
+
if (manifestText === null) {
|
|
33
|
+
throw new ProductTemplateError("MANIFEST_MISSING", "product template tar must contain manifest.json at the root");
|
|
34
|
+
}
|
|
35
|
+
let manifestRaw;
|
|
36
|
+
try {
|
|
37
|
+
manifestRaw = JSON.parse(manifestText);
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
throw new ProductTemplateError("MANIFEST_INVALID", `manifest.json is not valid JSON: ${String(e)}`);
|
|
41
|
+
}
|
|
42
|
+
const manifestParsed = ProductTemplateManifestSchema.safeParse(manifestRaw);
|
|
43
|
+
if (!manifestParsed.success) {
|
|
44
|
+
throw new ProductTemplateError("MANIFEST_INVALID", `manifest.json failed schema validation: ${manifestParsed.error.message}`);
|
|
45
|
+
}
|
|
46
|
+
let parameters = [];
|
|
47
|
+
const parametersText = await source.readMember("parameters.yaml");
|
|
48
|
+
if (parametersText !== null) {
|
|
49
|
+
let parametersRaw;
|
|
50
|
+
try {
|
|
51
|
+
parametersRaw = Bun.YAML.parse(parametersText);
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
throw new ProductTemplateError("PARAMETERS_INVALID", `parameters.yaml is not valid YAML: ${String(e)}`);
|
|
55
|
+
}
|
|
56
|
+
const parametersParsed = ParametersFileSchema.safeParse(parametersRaw);
|
|
57
|
+
if (!parametersParsed.success) {
|
|
58
|
+
throw new ProductTemplateError("PARAMETERS_INVALID", `parameters.yaml failed schema validation: ${parametersParsed.error.message}`);
|
|
59
|
+
}
|
|
60
|
+
parameters = parametersParsed.data.parameters;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
manifest: manifestParsed.data,
|
|
64
|
+
parameters,
|
|
65
|
+
hasCompose: (await source.readMember("compose.yml")) !== null,
|
|
66
|
+
};
|
|
67
|
+
}
|