@openmixer/omx-contract 2.11.0 → 2.12.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/README.md +2 -2
- package/bin/omx-contract.mjs +1 -1
- package/data/hosting.json +93 -0
- package/data/kernels/chorus.json +5 -5
- package/data/kernels/comp.json +2 -2
- package/data/kernels/deesser.json +1 -1
- package/data/kernels/delay.json +1 -1
- package/data/kernels/drive.json +3 -3
- package/data/kernels/eq.json +1 -1
- package/data/kernels/fbs.json +5 -5
- package/data/kernels/flanger.json +4 -4
- package/data/kernels/geq.json +4 -4
- package/data/kernels/hrp.json +5 -5
- package/data/kernels/mixmatrix.json +1 -1
- package/data/kernels/phaser.json +7 -7
- package/data/kernels/pitch.json +12 -3
- package/data/kernels/rotary.json +1 -1
- package/data/kernels/transient.json +5 -5
- package/data/primitives.json +1 -1
- package/dist/ts/omx-contract.d.ts +124 -46
- package/dist/ts/omx-contract.js +127 -49
- package/include/omxcontract/clap_host_limits.h +66 -0
- package/include/omxcontract/omx_contract_limits.h +20 -0
- package/lib/data.mjs +3 -1
- package/lib/fmt.mjs +3 -2
- package/lib/semver.mjs +11 -3
- package/lib/validate.mjs +21 -1
- package/package.json +1 -1
- package/render/c/clap-host-limits.mjs +78 -0
- package/render/c.mjs +10 -5
- package/render/ts.mjs +1 -1
- package/schema/omx-contract.schema.json +38 -4
- package/share/omx-contract/omx-contract.json +149 -50
package/lib/fmt.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
2
|
// Copyright (C) 2026 Pau Aliagas <linuxnow@gmail.com>
|
|
3
3
|
/**
|
|
4
|
-
* The one formatting of a data file
|
|
4
|
+
* The one formatting of a data file: two-space indent, one key per line, keys in schema
|
|
5
5
|
* order, a trailing newline. Names (items, fields, sheet keys) keep the order the author gave them:
|
|
6
6
|
* a travels table's field order is its C block order. A kernel file's sections come in their order (controls,
|
|
7
7
|
* readouts, tables, aggregates, constants), its controls and readouts in the author's order, each one's keys in
|
|
@@ -12,7 +12,7 @@ const ORDER = {
|
|
|
12
12
|
control: ['name', 'kind', 'global', 'table', 'rearms', 'since', 'doc', 'why', 'unit', 'ids', 'default', 'labels', 'travel', 'c'],
|
|
13
13
|
sections: ['controls', 'readouts', 'tables', 'aggregates', 'constants'],
|
|
14
14
|
readout: ['name', 'doc', 'unit', 'min', 'max', 'scale', 'rateHz'],
|
|
15
|
-
item: ['kind', 'since', 'doc', 'why', 'unit', 'value', 'values', 'ids', 'default', 'labels', 'fields', 'travel', 'c'],
|
|
15
|
+
item: ['kind', 'since', 'doc', 'why', 'unit', 'value', 'docs', 'values', 'ids', 'default', 'labels', 'fields', 'travel', 'codes', 'c'],
|
|
16
16
|
travel: ['min', 'max', 'step', 'unit', 'default', 'defaultFrom', 'scale', 'byKind', 'doc', 'why'],
|
|
17
17
|
c: ['alias', 'render'],
|
|
18
18
|
alias: ['fact', 'field'],
|
|
@@ -48,6 +48,7 @@ function item(it) {
|
|
|
48
48
|
if (!isObj(it)) return it;
|
|
49
49
|
const o = ordered(it, ORDER.item);
|
|
50
50
|
if ('value' in o) o.value = o.kind === 'sheet' ? sheet(o.value) : number(o.value);
|
|
51
|
+
if (isObj(o.codes)) o.codes = Object.fromEntries(Object.entries(o.codes).map(([k, c]) => [k, ordered(c, ['code', 'doc'])]));
|
|
51
52
|
if (isObj(o.fields)) o.fields = Object.fromEntries(Object.entries(o.fields).map(([k, t]) => [k, isObj(t) ? travel(t) : t]));
|
|
52
53
|
if (isObj(o.travel)) o.travel = travel(o.travel);
|
|
53
54
|
if (isObj(o.c)) {
|
package/lib/semver.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
2
|
// Copyright (C) 2026 Pau Aliagas <linuxnow@gmail.com>
|
|
3
3
|
/**
|
|
4
|
-
* `omx-contract semver`: classify every change between two resolved declarations
|
|
4
|
+
* `omx-contract semver`: classify every change between two resolved declarations and
|
|
5
5
|
* refuse a version whose bump is shorter than the largest class found.
|
|
6
6
|
*
|
|
7
7
|
* major a field or alias removed; an item removed (MINOR in the development phase, below); a
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
const RANK = { none: 0, patch: 1, minor: 2, major: 3 };
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
* The development-phase rule
|
|
24
|
+
* The development-phase rule: while every consumer of
|
|
25
25
|
* omx-contract is a FreeMixer repository named here, removing an item is MINOR. The rule ends, and
|
|
26
26
|
* this becomes null so a removal is MAJOR again, in the release that names the first consumer
|
|
27
27
|
* outside the list. Until then a MAJOR class is released as a minor bump (see refusal).
|
|
@@ -97,7 +97,7 @@ export function classify(prev, next, { developmentPhase = DEVELOPMENT_PHASE } =
|
|
|
97
97
|
if (a.kind !== b.kind || (a.shape ?? '') !== (b.shape ?? '')) { say('major', `kind ${a.kind} -> ${b.kind}`); continue; }
|
|
98
98
|
// a set that gains values gains their unit with them (minor, below); any other unit change is major
|
|
99
99
|
if ((a.unit ?? '') !== (b.unit ?? '') && !(a.kind === 'set' && a.values === undefined)) say('major', `unit ${JSON.stringify(a.unit)} -> ${JSON.stringify(b.unit)}`);
|
|
100
|
-
if (a.doc !== b.doc || (a.why ?? '') !== (b.why ?? '')) say('patch', 'doc');
|
|
100
|
+
if (a.doc !== b.doc || (a.why ?? '') !== (b.why ?? '') || !same(a.docs, b.docs)) say('patch', 'doc');
|
|
101
101
|
if (!same(a.c?.alias, b.c?.alias)) say(a.c?.alias ? 'major' : 'minor', a.c?.alias ? 'c.alias removed or renamed' : 'c.alias added');
|
|
102
102
|
if (!same(a.c?.render, b.c?.render)) say(b.c?.render === false ? 'major' : 'minor', 'C render of the list changed');
|
|
103
103
|
switch (a.kind) {
|
|
@@ -138,6 +138,14 @@ export function classify(prev, next, { developmentPhase = DEVELOPMENT_PHASE } =
|
|
|
138
138
|
});
|
|
139
139
|
}
|
|
140
140
|
break;
|
|
141
|
+
case 'codes':
|
|
142
|
+
for (const k of Object.keys(a.value)) {
|
|
143
|
+
if (!(k in b.value)) say('major', `${name}.${k} removed`);
|
|
144
|
+
else if (a.value[k].code !== b.value[k].code) say('major', `${name}.${k}: ${JSON.stringify(a.value[k].code)} -> ${JSON.stringify(b.value[k].code)} (a code is read by verdicts, never reworded)`);
|
|
145
|
+
else if ((a.value[k].doc ?? '') !== (b.value[k].doc ?? '')) say('patch', `${name}.${k}: doc`);
|
|
146
|
+
}
|
|
147
|
+
for (const k of Object.keys(b.value)) if (!(k in a.value)) say('minor', `${name}.${k} added`);
|
|
148
|
+
break;
|
|
141
149
|
default:
|
|
142
150
|
break;
|
|
143
151
|
}
|
package/lib/validate.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
2
|
// Copyright (C) 2026 Pau Aliagas <linuxnow@gmail.com>
|
|
3
3
|
/**
|
|
4
|
-
* `omx-contract validate`: the schema, then the checks a schema cannot express
|
|
4
|
+
* `omx-contract validate`: the schema, then the checks a schema cannot express: every
|
|
5
5
|
* reference resolves with no cycle; every default inside its travel; every C name the render spells
|
|
6
6
|
* is unique; an item's `c.alias` names no other item. Each failure names file, item and rule.
|
|
7
7
|
*/
|
|
@@ -127,6 +127,26 @@ function semanticRules(resolved, say) {
|
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
// A sheet's prose names fields the sheet has; a code is declared once.
|
|
131
|
+
for (const r of resolved.values()) {
|
|
132
|
+
if (r.kind === 'sheet' && r.docs !== undefined) {
|
|
133
|
+
for (const f of Object.keys(r.docs)) if (!(f in r.value)) say(r.rel, r.name, 'sheet-docs', `docs.${f} describes no field of ${r.name}`);
|
|
134
|
+
}
|
|
135
|
+
if (r.kind === 'codes') {
|
|
136
|
+
const seenCodes = new Map();
|
|
137
|
+
for (const [k, c] of Object.entries(r.value)) {
|
|
138
|
+
if (seenCodes.has(c.code)) say(r.rel, r.name, 'code-unique', `${k} and ${seenCodes.get(c.code)} both carry ${JSON.stringify(c.code)}`);
|
|
139
|
+
seenCodes.set(c.code, k);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// The hosted stage's limits are finite numbers, the header of the CLAP core reads each as a define.
|
|
145
|
+
const stage = resolved.get('HOSTED_STAGE_LIMITS');
|
|
146
|
+
if (stage) {
|
|
147
|
+
for (const [f, v] of Object.entries(stage.value)) if (typeof v !== 'number' || !Number.isFinite(v)) say(stage.rel, stage.name, 'finite', `HOSTED_STAGE_LIMITS.${f} resolves to ${JSON.stringify(v)}`);
|
|
148
|
+
}
|
|
149
|
+
|
|
130
150
|
// A readout's numbers are finite, its range runs upward, its rate is positive and its scale is a PARAM_SCALES id.
|
|
131
151
|
for (const [k, { readouts }] of Object.entries(resolved.kernels ?? {})) {
|
|
132
152
|
for (const ro of readouts ?? []) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmixer/omx-contract",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.0",
|
|
4
4
|
"description": "The FreeMixer declarations as data: a JSON Schema, the data, and one CLI that renders them into C, TypeScript and JSON",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "GPL-3.0-or-later",
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
// Copyright (C) 2026 Pau Aliagas <linuxnow@gmail.com>
|
|
3
|
+
/**
|
|
4
|
+
* `clap_host_limits.h`: the header the CLAP core (omx-clap-core) builds against and installs, rendered from
|
|
5
|
+
* `HOSTED_STAGE_LIMITS` and `HOSTING_CODES`. It is the library's API documentation too, so every define carries
|
|
6
|
+
* the prose declared beside its number or code as a trailing `///<` comment; the frame below is the only prose
|
|
7
|
+
* here. The fields named `worker*` are the LV2 adapter's and are not rendered.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const constCase = (s) => String(s).replace(/([a-z0-9])([A-Z])/g, '$1_$2').toUpperCase();
|
|
11
|
+
|
|
12
|
+
/** The licence block every source file of omx-clap-core opens with: the SPDX line, the GPL notice, the copyright line, a blank line. */
|
|
13
|
+
const LICENCE = `/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
14
|
+
/*
|
|
15
|
+
* This file is part of omx-clap-host.
|
|
16
|
+
*
|
|
17
|
+
* omx-clap-host is free software: you can redistribute it and/or modify
|
|
18
|
+
* it under the terms of the GNU General Public License as published by
|
|
19
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
20
|
+
* (at your option) any later version.
|
|
21
|
+
*
|
|
22
|
+
* omx-clap-host is distributed in the hope that it will be useful,
|
|
23
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
24
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
25
|
+
* GNU General Public License for more details.
|
|
26
|
+
*
|
|
27
|
+
* You should have received a copy of the GNU General Public License
|
|
28
|
+
* along with omx-clap-host. If not, see <http://www.gnu.org/licenses/>.
|
|
29
|
+
*
|
|
30
|
+
* Copyright (C) 2026 Pau Aliagas <linuxnow@gmail.com>
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
const isLevel = (field) => field.endsWith('Dbfs');
|
|
36
|
+
|
|
37
|
+
/** A define with its prose as a trailing member comment; a one-line prose only, or the rest would be read as C. */
|
|
38
|
+
function documented(define, prose, where) {
|
|
39
|
+
if (prose === undefined) return define;
|
|
40
|
+
if (typeof prose !== 'string' || prose.length === 0 || /[\n\r]/.test(prose)) throw new Error(`clap-host-limits: ${where} is no one-line description`);
|
|
41
|
+
return `${define} ///< ${prose}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** The header of the resolved items `HOSTED_STAGE_LIMITS` and `HOSTING_CODES`. */
|
|
45
|
+
export function renderClapHostLimits(stage, codes) {
|
|
46
|
+
const numbers = Object.entries(stage.value)
|
|
47
|
+
.filter(([field]) => !field.startsWith('worker'))
|
|
48
|
+
.map(([field, v]) => {
|
|
49
|
+
if (typeof v !== 'number' || !Number.isFinite(v)) throw new Error(`clap-host-limits: HOSTED_STAGE_LIMITS.${field} is not a finite number`);
|
|
50
|
+
const literal = isLevel(field) ? `${Number.isInteger(v) ? `${v}.0` : v}f` : Number.isInteger(v) && v >= 0 ? `${v}u` : null;
|
|
51
|
+
if (literal === null) throw new Error(`clap-host-limits: HOSTED_STAGE_LIMITS.${field} is neither a level (Dbfs) nor a non-negative integer`);
|
|
52
|
+
return documented(`#define CLAP_HOST_${constCase(field)} ${literal}`, stage.docs?.[field], `HOSTED_STAGE_LIMITS.docs.${field}`);
|
|
53
|
+
});
|
|
54
|
+
const refusals = Object.entries(codes.value)
|
|
55
|
+
.map(([name, c]) => documented(`#define CLAP_HOST_CODE_${constCase(name)} ${JSON.stringify(c.code)}`, c.doc, `HOSTING_CODES.${name}.doc`));
|
|
56
|
+
return `${LICENCE}#ifndef CLAP_HOST_LIMITS_H
|
|
57
|
+
#define CLAP_HOST_LIMITS_H
|
|
58
|
+
/*
|
|
59
|
+
* GENERATED — DO NOT EDIT BY HAND.
|
|
60
|
+
* Rendered by \`omx-contract render --target c\` from FreeMixer/omx-contract's HOSTED_STAGE_LIMITS and
|
|
61
|
+
* HOSTING_CODES. The rendered file is kept in this repository so the library builds on its own; the numbers
|
|
62
|
+
* and codes are changed where they are declared and the file is re-rendered, never edited here.
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
/** @file
|
|
66
|
+
* @brief The limits and refusal codes of the hosting code. */
|
|
67
|
+
|
|
68
|
+
#include <stddef.h>
|
|
69
|
+
|
|
70
|
+
/** the stage and the host: every number the core reads */
|
|
71
|
+
${numbers.join('\n')}
|
|
72
|
+
|
|
73
|
+
/** the refusals the core returns, as the hosting codes the console's verdicts use */
|
|
74
|
+
${refusals.join('\n')}
|
|
75
|
+
|
|
76
|
+
#endif /* CLAP_HOST_LIMITS_H */
|
|
77
|
+
`;
|
|
78
|
+
}
|
package/render/c.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
2
|
// Copyright (C) 2026 Pau Aliagas <linuxnow@gmail.com>
|
|
3
3
|
/**
|
|
4
|
-
* The `c` target: `omxcontract/omx_contract_limits.h
|
|
4
|
+
* The `c` target: `omxcontract/omx_contract_limits.h`, and `omxcontract/clap_host_limits.h` where the data declares the hosting limits and codes (docs/hosting.md). Every define is spelled by the console engine's
|
|
5
5
|
* renderer, moved unchanged (c/contract-limits-render.mjs); only the frame (banner, section
|
|
6
6
|
* comments) is this repository's. A plugin's parameter header is rendered by omx-plugins from its
|
|
7
7
|
* declaration, by reference to the items here.
|
|
@@ -10,11 +10,12 @@ import {
|
|
|
10
10
|
cStringLiteral, genCoreLimitBlock, genDeclaredListsBlock, genDeclaredRates, genDeclaredScalarsBlock, genLimitBlock, renderContractLimits,
|
|
11
11
|
scalarLiteral,
|
|
12
12
|
} from './c/contract-limits-render.mjs';
|
|
13
|
+
import { renderClapHostLimits } from './c/clap-host-limits.mjs';
|
|
13
14
|
import { doors } from './doors.mjs';
|
|
14
15
|
|
|
15
16
|
const defined = (text) => new Set([...text.matchAll(/^#define (OMX_\w+)/gm)].map((m) => m[1]));
|
|
16
17
|
|
|
17
|
-
/** The rate tail of
|
|
18
|
+
/** The rate tail of the console engine's header, outside the include guard, byte for byte. */
|
|
18
19
|
function ratesTail(rates) {
|
|
19
20
|
return `
|
|
20
21
|
/* The sample rates the console declares (core's STANDARD_SAMPLE_RATES, row-codecs.ts): the set a
|
|
@@ -193,12 +194,16 @@ ${d.STANDARD_SAMPLE_RATES ? ratesTail(d.STANDARD_SAMPLE_RATES) : ''}`;
|
|
|
193
194
|
|
|
194
195
|
/** Every file of the `c` target: `[{ path, text }]`, paths relative to the output directory. */
|
|
195
196
|
export function renderC(resolved) {
|
|
196
|
-
|
|
197
|
+
const files = [{ path: 'omxcontract/omx_contract_limits.h', text: renderLimitsHeader(resolved) }];
|
|
198
|
+
const stage = resolved.get('HOSTED_STAGE_LIMITS');
|
|
199
|
+
const codes = resolved.get('HOSTING_CODES');
|
|
200
|
+
if (stage && codes) files.push({ path: 'omxcontract/clap_host_limits.h', text: renderClapHostLimits(stage, codes) });
|
|
201
|
+
return files;
|
|
197
202
|
}
|
|
198
203
|
|
|
199
204
|
/**
|
|
200
|
-
* The `c` target of a frozen
|
|
201
|
-
* renderer, for the round-trip proof
|
|
205
|
+
* The `c` target of a frozen sheet (test/fixtures/frozen-limits-sheet.json): `{ sheet }` rendered by the moved
|
|
206
|
+
* renderer, for the round-trip proof.
|
|
202
207
|
*/
|
|
203
208
|
export function renderSheetC(fixture) {
|
|
204
209
|
const s = fixture.sheet;
|
package/render/ts.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { eqDefaultBands, eqDefaultCentres, eqDefaultParams, eqDefaultTypes } fro
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* The `ts` target: one ESM module and its type declarations, every item exported under its own
|
|
7
|
-
* name with its literal type (`as const`), so a re-export
|
|
7
|
+
* name with its literal type (`as const`), so a consumer's re-export keeps names, values and types.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
/** The value a TypeScript reader sees for one resolved item. */
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"$defs": {
|
|
13
13
|
"unit": {
|
|
14
14
|
"description": "The closed list of units the items use; \"x\" is a multiplier, \"\" is a ratio or a fraction.",
|
|
15
|
-
"enum": ["dB", "dBFS", "ms", "s", "min", "Hz", "oct", "cents", "%", "x", ""]
|
|
15
|
+
"enum": ["dB", "dBFS", "ms", "s", "min", "Hz", "oct", "cents", "st", "%", "x", ""]
|
|
16
16
|
},
|
|
17
17
|
"text": { "type": "string", "minLength": 1 },
|
|
18
18
|
"since": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$", "description": "The release that first carried an item declared outside the kernels (rates.json, primitives.json); a kernel's items record it in recipes/answers/." },
|
|
@@ -156,7 +156,14 @@
|
|
|
156
156
|
"since": { "$ref": "#/$defs/since" },
|
|
157
157
|
"doc": { "$ref": "#/$defs/text" },
|
|
158
158
|
"why": { "$ref": "#/$defs/text" },
|
|
159
|
-
"value": { "$ref": "#/$defs/sheetValue" }
|
|
159
|
+
"value": { "$ref": "#/$defs/sheetValue" },
|
|
160
|
+
"docs": {
|
|
161
|
+
"description": "One line of prose per top-level field of `value`, rendered beside the field's define in a header that documents it. A field without one has none.",
|
|
162
|
+
"type": "object",
|
|
163
|
+
"propertyNames": { "pattern": "^[a-z][A-Za-z0-9]*$" },
|
|
164
|
+
"additionalProperties": { "$ref": "#/$defs/text" },
|
|
165
|
+
"minProperties": 1
|
|
166
|
+
}
|
|
160
167
|
},
|
|
161
168
|
"required": ["kind", "doc", "value"],
|
|
162
169
|
"additionalProperties": false
|
|
@@ -179,6 +186,32 @@
|
|
|
179
186
|
"required": ["kind", "doc", "unit", "values"],
|
|
180
187
|
"additionalProperties": false
|
|
181
188
|
},
|
|
189
|
+
"codes": {
|
|
190
|
+
"type": "object",
|
|
191
|
+
"description": "Refusal codes: a lowerCamel name per code, the code itself (`hosting.<group>.<reason>`, lower case, read by verdicts and never reworded) and optionally one line of prose.",
|
|
192
|
+
"properties": {
|
|
193
|
+
"kind": { "const": "codes" },
|
|
194
|
+
"since": { "$ref": "#/$defs/since" },
|
|
195
|
+
"doc": { "$ref": "#/$defs/text" },
|
|
196
|
+
"why": { "$ref": "#/$defs/text" },
|
|
197
|
+
"codes": {
|
|
198
|
+
"type": "object",
|
|
199
|
+
"propertyNames": { "pattern": "^[a-z][A-Za-z0-9]*$" },
|
|
200
|
+
"additionalProperties": {
|
|
201
|
+
"type": "object",
|
|
202
|
+
"properties": {
|
|
203
|
+
"code": { "type": "string", "pattern": "^hosting(\\.[a-z0-9]+(-[a-z0-9]+)*){2,}$" },
|
|
204
|
+
"doc": { "$ref": "#/$defs/text" }
|
|
205
|
+
},
|
|
206
|
+
"required": ["code"],
|
|
207
|
+
"additionalProperties": false
|
|
208
|
+
},
|
|
209
|
+
"minProperties": 1
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
"required": ["kind", "doc", "codes"],
|
|
213
|
+
"additionalProperties": false
|
|
214
|
+
},
|
|
182
215
|
"set": {
|
|
183
216
|
"type": "object",
|
|
184
217
|
"description": "Ordered ids with an optional default, labels and a value per id. The ids and their order are the contract: all strings of letters and digits (the id is the name, its position the value; an id may start with a digit, as 4x does) or all non-negative integers (the id is the value). Labels are face text and optional. The default may be a reference to the scalar that states it: a boolean names the second id of a two-id switch when true, the first when false.",
|
|
@@ -210,13 +243,14 @@
|
|
|
210
243
|
"item": {
|
|
211
244
|
"type": "object",
|
|
212
245
|
"required": ["kind"],
|
|
213
|
-
"properties": { "kind": { "enum": ["scalar", "travels", "sheet", "list", "set"] } },
|
|
246
|
+
"properties": { "kind": { "enum": ["scalar", "travels", "sheet", "list", "set", "codes"] } },
|
|
214
247
|
"allOf": [
|
|
215
248
|
{ "if": { "properties": { "kind": { "const": "scalar" } } }, "then": { "$ref": "#/$defs/scalar" } },
|
|
216
249
|
{ "if": { "properties": { "kind": { "const": "travels" } } }, "then": { "$ref": "#/$defs/travels" } },
|
|
217
250
|
{ "if": { "properties": { "kind": { "const": "sheet" } } }, "then": { "$ref": "#/$defs/sheet" } },
|
|
218
251
|
{ "if": { "properties": { "kind": { "const": "list" } } }, "then": { "$ref": "#/$defs/list" } },
|
|
219
|
-
{ "if": { "properties": { "kind": { "const": "set" } } }, "then": { "$ref": "#/$defs/set" } }
|
|
252
|
+
{ "if": { "properties": { "kind": { "const": "set" } } }, "then": { "$ref": "#/$defs/set" } },
|
|
253
|
+
{ "if": { "properties": { "kind": { "const": "codes" } } }, "then": { "$ref": "#/$defs/codes" } }
|
|
220
254
|
]
|
|
221
255
|
}
|
|
222
256
|
}
|