@imferno/schema 2.4.1 → 3.0.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imferno/schema",
3
- "version": "2.4.1",
3
+ "version": "3.0.0",
4
4
  "description": "JSON Schema definitions for SMPTE ST 2067 IMF types — validates imferno export output",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "title": "CompositionPlaylist",
4
- "description": "Root CPL structure - defines a complete IMF composition",
4
+ "description": "Root CPL structure defines a complete IMF composition.\n\n# Spec-required vs `Option<T>` policy (FIX-7 audit)\n\nThe parser is intentionally lenient: every spec-required element that isn't strictly necessary to **construct** a valid `CompositionPlaylist` is exposed as `Option<T>` (or via a `default = \"…\"` serde attribute). Missing-required-field violations are surfaced as catalogue diagnostics by the validator (`validate_cpl`) rather than as parse errors, so a caller can still inspect the parsed structure of a non-conformant CPL.\n\nField-by-field map against ST 2067-3 §6 / §7 (2013 / 2016 — the 2020 edition reuses the 2016 schema verbatim):\n\n| Field | Type | Spec status | |---------------------------|------------------|-------------------------------------------| | `id` | `ImfUuid` | required §6.1 — parse error if missing | | `annotation` | `Option<…>` | optional §6.2 | | `issue_date` | `String` | required §6.3 — parse error if missing | | `issuer` | `Option<…>` | optional §6.4 | | `creator` | `Option<…>` | optional §6.5 | | `content_originator` | `Option<…>` | optional §6.6 | | `content_title` | `LanguageString` | required §6.7 — parse error if missing | | `content_kind` | concrete (default) | required §6.8 — `default_content_kind` | | `content_version_list` | `Option<…>` | optional §6.10 | | `essence_descriptor_list` | `Option<…>` | **required** per ST 2067-2 §6.1.5 — | | | | parser-lenient; absence is reported by | | | | `validate_cpl` as Error | | `edit_rate` | `Option<…>` | required §6.13 — parser-lenient; absence | | | | reported by `validate_cpl` | | `total_running_time` | `Option<String>` | optional §6.14 | | `locale_list` | `Option<…>` | optional §6.15 | | `extension_properties` | `Option<…>` | optional §6.16 | | `composition_timecode` | `Option<…>` | optional §6.9 | | `segment_list` | `SegmentList` | required §6.17 — parse error if missing | | `has_signer`/`has_signature` | `bool` | reflect presence in raw XML (§8 signatures unparsed) | | `source_xml` | `Option<String>` | retained when parsed from XML; absent for JSON-deserialised |\n\nFive fields are spec-required but stored as `Option<T>` (with `default` on the serde side) to support the parser-lenient model: `content_kind` (defaults via `default_content_kind`), `content_version_list`, `essence_descriptor_list`, `edit_rate`, `locale_list`. The validator surfaces missing-required findings against the ST 2067-3 prose.",
5
5
  "type": "object",
6
6
  "required": [
7
7
  "ContentTitle",
@@ -1013,6 +1013,32 @@
1013
1013
  }
1014
1014
  }
1015
1015
  },
1016
+ "IABChannelSubDescriptor": {
1017
+ "description": "Presence-only stub for ST 2067-201:2026 Annex E `IABChannelSubDescriptor`.\n\nThe 2026 spec defines the full item set in Table E.1 (`IABBedMetaID`, `IABChannelID`, `IABAudioDescription`, `IABAudioDescriptionText`); imferno's CPL parser only needs to count occurrences to fire the `IabChannelSubDescriptorRecommended` warning, so the inner shape is intentionally permissive — any nested content deserialises into the catch-all map without affecting presence.",
1018
+ "type": "object",
1019
+ "properties": {
1020
+ "IABBedMetaID": {
1021
+ "description": "Annex E §E.2 — IAB Bed MetaID of the associated BedDefinition.",
1022
+ "default": null,
1023
+ "type": [
1024
+ "integer",
1025
+ "null"
1026
+ ],
1027
+ "format": "uint32",
1028
+ "minimum": 0.0
1029
+ },
1030
+ "IABChannelID": {
1031
+ "description": "Annex E §E.2 — Channel ID within the bed.",
1032
+ "default": null,
1033
+ "type": [
1034
+ "integer",
1035
+ "null"
1036
+ ],
1037
+ "format": "uint32",
1038
+ "minimum": 0.0
1039
+ }
1040
+ }
1041
+ },
1016
1042
  "IABEssenceDescriptor": {
1017
1043
  "description": "IAB (Immersive Audio Bitstream) essence descriptor — Dolby Atmos",
1018
1044
  "type": "object",
@@ -1193,6 +1219,14 @@
1193
1219
  "description": "SubDescriptors for IAB essence",
1194
1220
  "type": "object",
1195
1221
  "properties": {
1222
+ "IABChannelSubDescriptor": {
1223
+ "description": "ST 2067-201:2026 Annex E — IAB Channel SubDescriptor entries. Optional in 2021 (and earlier — silently dropped), recommended in 2026 (\"should contain one instance for each channel of each BedDefinition\"). Captured as a raw count via a bag struct so downstream code can probe presence without the parser needing to model every field defined in Annex E Table E.1.",
1224
+ "default": [],
1225
+ "type": "array",
1226
+ "items": {
1227
+ "$ref": "#/definitions/IABChannelSubDescriptor"
1228
+ }
1229
+ },
1196
1230
  "IABSoundfieldLabelSubDescriptor": {
1197
1231
  "default": null,
1198
1232
  "anyOf": [
@@ -1619,11 +1653,9 @@
1619
1653
  "LocaleList": {
1620
1654
  "description": "LocaleList - Content locale information",
1621
1655
  "type": "object",
1622
- "required": [
1623
- "Locale"
1624
- ],
1625
1656
  "properties": {
1626
1657
  "Locale": {
1658
+ "default": [],
1627
1659
  "type": "array",
1628
1660
  "items": {
1629
1661
  "$ref": "#/definitions/Locale"
@@ -2344,11 +2376,9 @@
2344
2376
  },
2345
2377
  "SegmentList": {
2346
2378
  "type": "object",
2347
- "required": [
2348
- "Segment"
2349
- ],
2350
2379
  "properties": {
2351
2380
  "Segment": {
2381
+ "default": [],
2352
2382
  "type": "array",
2353
2383
  "items": {
2354
2384
  "$ref": "#/definitions/Segment"
@@ -63,12 +63,19 @@
63
63
  ]
64
64
  },
65
65
  {
66
- "description": "Encoding and codec issues",
66
+ "description": "Codec-level encoding issues (J2K profiles, JPEG-XS, AAC params, PCM bit depth, etc.). For *container*-level concerns (MXF wrapping, partition layout) use `Container`.",
67
67
  "type": "string",
68
68
  "enum": [
69
69
  "Encoding"
70
70
  ]
71
71
  },
72
+ {
73
+ "description": "MXF / wrapping container constraints — distinct from `Encoding` which covers the codec carried inside the container.",
74
+ "type": "string",
75
+ "enum": [
76
+ "Container"
77
+ ]
78
+ },
72
79
  {
73
80
  "description": "Audio configuration issues",
74
81
  "type": "string",
@@ -90,6 +97,13 @@
90
97
  "Subtitle"
91
98
  ]
92
99
  },
100
+ {
101
+ "description": "Data essence (ISXD, dynamic metadata sidecars, ancillary data tracks). NOT for *metadata about* essence — that's `Metadata`.",
102
+ "type": "string",
103
+ "enum": [
104
+ "Data"
105
+ ]
106
+ },
93
107
  {
94
108
  "description": "Metadata and labeling issues",
95
109
  "type": "string",
@@ -320,6 +334,32 @@
320
334
  "type": "string",
321
335
  "format": "uuid"
322
336
  },
337
+ "IssueSource": {
338
+ "description": "Which authority produced a validation finding.\n\nEncodes the \"prose vs XSD\" provenance distinction that SMPTE ST 2067-3 §5.1 calls out (\"the prose document takes precedence on conflict\"), so callers can filter, group, or apply per-source severity overrides without inspecting the code string.\n\nCurrently *inferred* from the code prefix on a `ValidationIssue` (see `ValidationIssue::source`) — no engine code needs to set it explicitly. If inference ever becomes ambiguous (e.g. a non-XSD engine internal rule that wants the `XSD/` prefix for catalogue reasons), we can add a `with_source()` builder without changing the public shape.",
339
+ "oneOf": [
340
+ {
341
+ "description": "Came from the runtime XSD validator (uppsala). Schema-layer: the structural subset of the spec that the XSD DSL can express.",
342
+ "type": "string",
343
+ "enum": [
344
+ "XsdLayer"
345
+ ]
346
+ },
347
+ {
348
+ "description": "Came from a hand-rolled prose-cited rule. Semantic/cross-field/ value-set checks that XSD can't express; the rule's code typically cites a SMPTE prose section (e.g. `ST2067-2:2020:6.4.2`).",
349
+ "type": "string",
350
+ "enum": [
351
+ "ProseRule"
352
+ ]
353
+ },
354
+ {
355
+ "description": "Came from imferno engine internals — parse failures, package structure checks, manifest issues — not directly traceable to a single SMPTE spec section.",
356
+ "type": "string",
357
+ "enum": [
358
+ "EngineInternal"
359
+ ]
360
+ }
361
+ ]
362
+ },
323
363
  "Location": {
324
364
  "description": "Location where the issue was found",
325
365
  "type": "object",
@@ -534,6 +574,13 @@
534
574
  "severity"
535
575
  ],
536
576
  "properties": {
577
+ "additional_instances": {
578
+ "description": "Other `Location`s when this issue represents an aggregation of multiple identical-code occurrences (see [`ValidationReport::aggregate`]). Empty for fresh, un-aggregated issues. Skipped during serialisation when empty so the on-wire shape stays back-compatible for callers that don't aggregate.",
579
+ "type": "array",
580
+ "items": {
581
+ "$ref": "#/definitions/Location"
582
+ }
583
+ },
537
584
  "category": {
538
585
  "description": "Category of issue",
539
586
  "allOf": [
@@ -573,6 +620,15 @@
573
620
  }
574
621
  ]
575
622
  },
623
+ "source": {
624
+ "description": "Authority that emitted this issue (XSD layer, prose rule, engine internal). Inferred from `code` at construction time so the field is always consistent with the code prefix — JS consumers can filter on `issue.source` directly without re-implementing the inference. Deserialised with a default so old reports without the field round-trip cleanly.",
625
+ "default": "EngineInternal",
626
+ "allOf": [
627
+ {
628
+ "$ref": "#/definitions/IssueSource"
629
+ }
630
+ ]
631
+ },
576
632
  "suggestion": {
577
633
  "description": "Suggestion for how to fix",
578
634
  "type": [
@@ -659,6 +715,13 @@
659
715
  }
660
716
  ]
661
717
  },
718
+ "suppressed": {
719
+ "description": "Issues that were suppressed by a `RuleSeverity::Off` override. These are not counted toward `is_playable`/`is_compliant` or surfaced by `has_errors`/`summary` — they exist so operators can debug their `RulesConfig` (`--show-suppressed`) without re-running validation. Each carries a `context[\"suppressed_by\"]` annotation naming the rule key that matched.\n\nSkipped during serialisation when empty so reports without suppressed rules keep the previous on-wire shape.",
720
+ "type": "array",
721
+ "items": {
722
+ "$ref": "#/definitions/ValidationIssue"
723
+ }
724
+ },
662
725
  "timestamp": {
663
726
  "description": "Timestamp of validation",
664
727
  "type": "string"
@@ -51,6 +51,13 @@
51
51
  }
52
52
  ]
53
53
  },
54
+ "suppressed": {
55
+ "description": "Issues that were suppressed by a `RuleSeverity::Off` override. These are not counted toward `is_playable`/`is_compliant` or surfaced by `has_errors`/`summary` — they exist so operators can debug their `RulesConfig` (`--show-suppressed`) without re-running validation. Each carries a `context[\"suppressed_by\"]` annotation naming the rule key that matched.\n\nSkipped during serialisation when empty so reports without suppressed rules keep the previous on-wire shape.",
56
+ "type": "array",
57
+ "items": {
58
+ "$ref": "#/definitions/ValidationIssue"
59
+ }
60
+ },
54
61
  "timestamp": {
55
62
  "description": "Timestamp of validation",
56
63
  "type": "string"
@@ -103,12 +110,19 @@
103
110
  ]
104
111
  },
105
112
  {
106
- "description": "Encoding and codec issues",
113
+ "description": "Codec-level encoding issues (J2K profiles, JPEG-XS, AAC params, PCM bit depth, etc.). For *container*-level concerns (MXF wrapping, partition layout) use `Container`.",
107
114
  "type": "string",
108
115
  "enum": [
109
116
  "Encoding"
110
117
  ]
111
118
  },
119
+ {
120
+ "description": "MXF / wrapping container constraints — distinct from `Encoding` which covers the codec carried inside the container.",
121
+ "type": "string",
122
+ "enum": [
123
+ "Container"
124
+ ]
125
+ },
112
126
  {
113
127
  "description": "Audio configuration issues",
114
128
  "type": "string",
@@ -130,6 +144,13 @@
130
144
  "Subtitle"
131
145
  ]
132
146
  },
147
+ {
148
+ "description": "Data essence (ISXD, dynamic metadata sidecars, ancillary data tracks). NOT for *metadata about* essence — that's `Metadata`.",
149
+ "type": "string",
150
+ "enum": [
151
+ "Data"
152
+ ]
153
+ },
133
154
  {
134
155
  "description": "Metadata and labeling issues",
135
156
  "type": "string",
@@ -164,6 +185,32 @@
164
185
  "type": "string",
165
186
  "format": "uuid"
166
187
  },
188
+ "IssueSource": {
189
+ "description": "Which authority produced a validation finding.\n\nEncodes the \"prose vs XSD\" provenance distinction that SMPTE ST 2067-3 §5.1 calls out (\"the prose document takes precedence on conflict\"), so callers can filter, group, or apply per-source severity overrides without inspecting the code string.\n\nCurrently *inferred* from the code prefix on a `ValidationIssue` (see `ValidationIssue::source`) — no engine code needs to set it explicitly. If inference ever becomes ambiguous (e.g. a non-XSD engine internal rule that wants the `XSD/` prefix for catalogue reasons), we can add a `with_source()` builder without changing the public shape.",
190
+ "oneOf": [
191
+ {
192
+ "description": "Came from the runtime XSD validator (uppsala). Schema-layer: the structural subset of the spec that the XSD DSL can express.",
193
+ "type": "string",
194
+ "enum": [
195
+ "XsdLayer"
196
+ ]
197
+ },
198
+ {
199
+ "description": "Came from a hand-rolled prose-cited rule. Semantic/cross-field/ value-set checks that XSD can't express; the rule's code typically cites a SMPTE prose section (e.g. `ST2067-2:2020:6.4.2`).",
200
+ "type": "string",
201
+ "enum": [
202
+ "ProseRule"
203
+ ]
204
+ },
205
+ {
206
+ "description": "Came from imferno engine internals — parse failures, package structure checks, manifest issues — not directly traceable to a single SMPTE spec section.",
207
+ "type": "string",
208
+ "enum": [
209
+ "EngineInternal"
210
+ ]
211
+ }
212
+ ]
213
+ },
167
214
  "Location": {
168
215
  "description": "Location where the issue was found",
169
216
  "type": "object",
@@ -293,6 +340,13 @@
293
340
  "severity"
294
341
  ],
295
342
  "properties": {
343
+ "additional_instances": {
344
+ "description": "Other `Location`s when this issue represents an aggregation of multiple identical-code occurrences (see [`ValidationReport::aggregate`]). Empty for fresh, un-aggregated issues. Skipped during serialisation when empty so the on-wire shape stays back-compatible for callers that don't aggregate.",
345
+ "type": "array",
346
+ "items": {
347
+ "$ref": "#/definitions/Location"
348
+ }
349
+ },
296
350
  "category": {
297
351
  "description": "Category of issue",
298
352
  "allOf": [
@@ -332,6 +386,15 @@
332
386
  }
333
387
  ]
334
388
  },
389
+ "source": {
390
+ "description": "Authority that emitted this issue (XSD layer, prose rule, engine internal). Inferred from `code` at construction time so the field is always consistent with the code prefix — JS consumers can filter on `issue.source` directly without re-implementing the inference. Deserialised with a default so old reports without the field round-trip cleanly.",
391
+ "default": "EngineInternal",
392
+ "allOf": [
393
+ {
394
+ "$ref": "#/definitions/IssueSource"
395
+ }
396
+ ]
397
+ },
335
398
  "suggestion": {
336
399
  "description": "Suggestion for how to fix",
337
400
  "type": [