@norskvideo/ctl-product-template-schema 0.1.11 → 0.1.13

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.
Files changed (3) hide show
  1. package/index.d.ts +148 -0
  2. package/index.js +66 -0
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -48,6 +48,56 @@ export declare const ProductTemplateSideloadSchema: z.ZodObject<{
48
48
  llmVenv: z.ZodOptional<z.ZodBoolean>;
49
49
  }, z.core.$strip>;
50
50
  export type ProductTemplateSideload = z.infer<typeof ProductTemplateSideloadSchema>;
51
+ export declare const ProductTemplateRequirementsSchema: z.ZodObject<{
52
+ capacity: z.ZodOptional<z.ZodObject<{
53
+ mode: z.ZodEnum<{
54
+ default: "default";
55
+ fixed: "fixed";
56
+ required: "required";
57
+ }>;
58
+ value: z.ZodOptional<z.ZodNumber>;
59
+ }, z.core.$strip>>;
60
+ cores: z.ZodOptional<z.ZodObject<{
61
+ mode: z.ZodEnum<{
62
+ default: "default";
63
+ fixed: "fixed";
64
+ required: "required";
65
+ }>;
66
+ value: z.ZodOptional<z.ZodNumber>;
67
+ }, z.core.$strip>>;
68
+ gpu: z.ZodOptional<z.ZodObject<{
69
+ mode: z.ZodEnum<{
70
+ default: "default";
71
+ fixed: "fixed";
72
+ required: "required";
73
+ }>;
74
+ value: z.ZodOptional<z.ZodObject<{
75
+ capacity: z.ZodOptional<z.ZodNumber>;
76
+ model: z.ZodOptional<z.ZodString>;
77
+ }, z.core.$strip>>;
78
+ }, z.core.$strip>>;
79
+ capabilities: z.ZodOptional<z.ZodObject<{
80
+ mode: z.ZodEnum<{
81
+ default: "default";
82
+ fixed: "fixed";
83
+ required: "required";
84
+ }>;
85
+ value: z.ZodOptional<z.ZodArray<z.ZodObject<{
86
+ name: z.ZodString;
87
+ count: z.ZodOptional<z.ZodNumber>;
88
+ attributeMatches: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
89
+ }, z.core.$strip>>>;
90
+ }, z.core.$strip>>;
91
+ hostPorts: z.ZodOptional<z.ZodObject<{
92
+ mode: z.ZodEnum<{
93
+ default: "default";
94
+ fixed: "fixed";
95
+ required: "required";
96
+ }>;
97
+ value: z.ZodOptional<z.ZodArray<z.ZodString>>;
98
+ }, z.core.$strip>>;
99
+ }, z.core.$strip>;
100
+ export type ProductTemplateRequirements = z.infer<typeof ProductTemplateRequirementsSchema>;
51
101
  export declare const ProductTemplateManifestSchema: z.ZodObject<{
52
102
  productTemplateSchemaVersion: z.ZodLiteral<1>;
53
103
  productName: z.ZodString;
@@ -106,6 +156,55 @@ export declare const ProductTemplateManifestSchema: z.ZodObject<{
106
156
  default: z.ZodOptional<z.ZodNumber>;
107
157
  }, z.core.$strip>>;
108
158
  }, z.core.$strip>>;
159
+ requirements: z.ZodOptional<z.ZodObject<{
160
+ capacity: z.ZodOptional<z.ZodObject<{
161
+ mode: z.ZodEnum<{
162
+ default: "default";
163
+ fixed: "fixed";
164
+ required: "required";
165
+ }>;
166
+ value: z.ZodOptional<z.ZodNumber>;
167
+ }, z.core.$strip>>;
168
+ cores: z.ZodOptional<z.ZodObject<{
169
+ mode: z.ZodEnum<{
170
+ default: "default";
171
+ fixed: "fixed";
172
+ required: "required";
173
+ }>;
174
+ value: z.ZodOptional<z.ZodNumber>;
175
+ }, z.core.$strip>>;
176
+ gpu: z.ZodOptional<z.ZodObject<{
177
+ mode: z.ZodEnum<{
178
+ default: "default";
179
+ fixed: "fixed";
180
+ required: "required";
181
+ }>;
182
+ value: z.ZodOptional<z.ZodObject<{
183
+ capacity: z.ZodOptional<z.ZodNumber>;
184
+ model: z.ZodOptional<z.ZodString>;
185
+ }, z.core.$strip>>;
186
+ }, z.core.$strip>>;
187
+ capabilities: z.ZodOptional<z.ZodObject<{
188
+ mode: z.ZodEnum<{
189
+ default: "default";
190
+ fixed: "fixed";
191
+ required: "required";
192
+ }>;
193
+ value: z.ZodOptional<z.ZodArray<z.ZodObject<{
194
+ name: z.ZodString;
195
+ count: z.ZodOptional<z.ZodNumber>;
196
+ attributeMatches: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
197
+ }, z.core.$strip>>>;
198
+ }, z.core.$strip>>;
199
+ hostPorts: z.ZodOptional<z.ZodObject<{
200
+ mode: z.ZodEnum<{
201
+ default: "default";
202
+ fixed: "fixed";
203
+ required: "required";
204
+ }>;
205
+ value: z.ZodOptional<z.ZodArray<z.ZodString>>;
206
+ }, z.core.$strip>>;
207
+ }, z.core.$strip>>;
109
208
  allocatedPorts: z.ZodOptional<z.ZodArray<z.ZodObject<{
110
209
  param: z.ZodString;
111
210
  protocol: z.ZodEnum<{
@@ -224,6 +323,55 @@ export declare const ProductTemplateMaterialsSchema: z.ZodObject<{
224
323
  default: z.ZodOptional<z.ZodNumber>;
225
324
  }, z.core.$strip>>;
226
325
  }, z.core.$strip>>;
326
+ requirements: z.ZodOptional<z.ZodObject<{
327
+ capacity: z.ZodOptional<z.ZodObject<{
328
+ mode: z.ZodEnum<{
329
+ default: "default";
330
+ fixed: "fixed";
331
+ required: "required";
332
+ }>;
333
+ value: z.ZodOptional<z.ZodNumber>;
334
+ }, z.core.$strip>>;
335
+ cores: z.ZodOptional<z.ZodObject<{
336
+ mode: z.ZodEnum<{
337
+ default: "default";
338
+ fixed: "fixed";
339
+ required: "required";
340
+ }>;
341
+ value: z.ZodOptional<z.ZodNumber>;
342
+ }, z.core.$strip>>;
343
+ gpu: z.ZodOptional<z.ZodObject<{
344
+ mode: z.ZodEnum<{
345
+ default: "default";
346
+ fixed: "fixed";
347
+ required: "required";
348
+ }>;
349
+ value: z.ZodOptional<z.ZodObject<{
350
+ capacity: z.ZodOptional<z.ZodNumber>;
351
+ model: z.ZodOptional<z.ZodString>;
352
+ }, z.core.$strip>>;
353
+ }, z.core.$strip>>;
354
+ capabilities: z.ZodOptional<z.ZodObject<{
355
+ mode: z.ZodEnum<{
356
+ default: "default";
357
+ fixed: "fixed";
358
+ required: "required";
359
+ }>;
360
+ value: z.ZodOptional<z.ZodArray<z.ZodObject<{
361
+ name: z.ZodString;
362
+ count: z.ZodOptional<z.ZodNumber>;
363
+ attributeMatches: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
364
+ }, z.core.$strip>>>;
365
+ }, z.core.$strip>>;
366
+ hostPorts: z.ZodOptional<z.ZodObject<{
367
+ mode: z.ZodEnum<{
368
+ default: "default";
369
+ fixed: "fixed";
370
+ required: "required";
371
+ }>;
372
+ value: z.ZodOptional<z.ZodArray<z.ZodString>>;
373
+ }, z.core.$strip>>;
374
+ }, z.core.$strip>>;
227
375
  allocatedPorts: z.ZodOptional<z.ZodArray<z.ZodObject<{
228
376
  param: z.ZodString;
229
377
  protocol: z.ZodEnum<{
package/index.js CHANGED
@@ -138,6 +138,69 @@ export const ProductTemplateSideloadSchema = z
138
138
  }),
139
139
  })
140
140
  .meta({ id: "ProductTemplateSideload", outputId: "ProductTemplateSideload" });
141
+ // Placement requirements guidance (requirements-authoring.md). Each dimension
142
+ // the placer reasons over — capacity (vCPUs), cores, GPU, capabilities, host
143
+ // ports — is optionally declared with a `mode`:
144
+ // fixed — pinned; the operator can't change it (value required).
145
+ // default — a pre-filled starting value the operator may override.
146
+ // required — the product can't know it (e.g. a runtime-authored Studio
147
+ // workflow); the operator MUST supply it at create (value forbidden).
148
+ // An absent dimension is never asked for. The manifest only offers guidance;
149
+ // authoritative values are resolved at create time (template-baked or
150
+ // operator-entered) and projected into the job's JobRequirements.
151
+ const ProductTemplateRequirementModeSchema = z.enum(["fixed", "default", "required"]);
152
+ // Wrap a dimension's value schema with the mode/value contract: fixed/default
153
+ // must carry a value, required must not (the operator supplies it).
154
+ function requirementDimension(value, hasValue = () => true) {
155
+ return z.object({ mode: ProductTemplateRequirementModeSchema, value: value.optional() }).superRefine((d, ctx) => {
156
+ if (d.mode === "required" && d.value !== undefined) {
157
+ ctx.addIssue({
158
+ code: "custom",
159
+ path: ["value"],
160
+ message: "a `required` dimension must not declare a value — the operator supplies it at create",
161
+ });
162
+ }
163
+ const present = d.value !== undefined && hasValue(d.value);
164
+ if ((d.mode === "fixed" || d.mode === "default") && !present) {
165
+ ctx.addIssue({
166
+ code: "custom",
167
+ path: ["value"],
168
+ message: "a `fixed`/`default` dimension must declare a value",
169
+ });
170
+ }
171
+ });
172
+ }
173
+ const ProductTemplateCapabilityRequirementSchema = z.object({
174
+ name: z.string(),
175
+ count: z.number().int().min(1).optional().meta({ description: "How many of this capability; default 1" }),
176
+ attributeMatches: z
177
+ .record(z.string(), z.string())
178
+ .optional()
179
+ .meta({ description: "Each key must equal the matched capability's attribute, e.g. { model: 'RTX 4090' }" }),
180
+ });
181
+ const ProductTemplateGpuRequirementSchema = z.object({
182
+ capacity: z.number().positive().optional().meta({ description: "Fractional GPU reference units" }),
183
+ model: z.string().optional().meta({ description: "GPU model match, e.g. 'RTX 4090'" }),
184
+ });
185
+ export const ProductTemplateRequirementsSchema = z
186
+ .object({
187
+ capacity: requirementDimension(z.number().positive()).optional().meta({
188
+ description: "CPU capacity in vCPUs — the primary placement dimension (capacity ≡ vCPUs).",
189
+ }),
190
+ cores: requirementDimension(z.number().int().positive()).optional().meta({
191
+ description: "Pinned cpuset core count (distinct from capacity).",
192
+ }),
193
+ gpu: requirementDimension(ProductTemplateGpuRequirementSchema, (v) => v.capacity !== undefined || v.model !== undefined)
194
+ .optional()
195
+ .meta({ description: "Fractional GPU need (capacity and/or model)." }),
196
+ capabilities: requirementDimension(z.array(ProductTemplateCapabilityRequirementSchema).min(1))
197
+ .optional()
198
+ .meta({ description: "Counted/exclusive hardware & software capabilities (nvidia-gpu, quadra, decklink, …)." }),
199
+ hostPorts: requirementDimension(z.array(z.string().regex(/^\d+\/(tcp|udp)$/i, "canonical PORT/proto token, e.g. 5001/udp")).min(1))
200
+ .optional()
201
+ .meta({ description: "Fixed host sockets the job binds (exclusive)." }),
202
+ })
203
+ .meta({ id: "ProductTemplateRequirements", outputId: "ProductTemplateRequirements" });
141
204
  export const ProductTemplateManifestSchema = z
142
205
  .object({
143
206
  productTemplateSchemaVersion: z.literal(1),
@@ -161,6 +224,9 @@ export const ProductTemplateManifestSchema = z
161
224
  advanced: ProductTemplateAdvancedSchema.optional().meta({
162
225
  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).",
163
226
  }),
227
+ requirements: ProductTemplateRequirementsSchema.optional().meta({
228
+ description: "Placement requirements guidance. Each dimension (capacity/cores/gpu/capabilities/hostPorts) declares fixed (pinned) / default (pre-filled, overridable) / required (operator must supply at create). Absent dimensions are never asked for. Authoritative values are resolved at create time and drive placement; this is guidance only.",
229
+ }),
164
230
  allocatedPorts: z.array(ProductTemplateAllocatedPortSchema).optional().meta({
165
231
  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.",
166
232
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@norskvideo/ctl-product-template-schema",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {