ng-form-foundry-transformers 0.4.1 → 0.5.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 +10 -4
- package/dist/core/schema.d.ts +9 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/transformers/libconfig/index.d.ts +1 -1
- package/dist/transformers/libconfig/index.js +1 -2
- package/dist/transformers/libconfig/libconfig-transformer.d.ts +1 -3
- package/dist/transformers/libconfig/libconfig-transformer.js +0 -15
- package/dist/transformers/libconfig/schema.js +25 -5
- package/dist/transformers/yaml/yaml-transformer.js +63 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ or a CLI just as easily.
|
|
|
16
16
|
| `yang` | YANG model (via an engine) | RFC 7951 instance data | available |
|
|
17
17
|
| `yaml` | YAML config (optionally a JSON Schema) | YAML (comments preserved) | available |
|
|
18
18
|
| `json` | JSON config (optionally a JSON Schema) | JSON (indent preserved) | available |
|
|
19
|
-
| `libconfig` | libconfig document (optionally a JSON Schema) | libconfig (comments **and scalar types** preserved) |
|
|
19
|
+
| `libconfig` | libconfig document (optionally a JSON Schema) | libconfig (comments **and scalar types** preserved) | available |
|
|
20
20
|
|
|
21
21
|
The YAML and JSON transformers share the same format-agnostic form builders in
|
|
22
22
|
`core` (`inferNodeGroup`, `jsonSchemaToNodeGroup`) — a JSON Schema is an *option*
|
|
@@ -169,12 +169,10 @@ const { schema, binding, initialValue } = jsonTransformer.toSchema(configText);
|
|
|
169
169
|
const out = jsonTransformer.toSource({ ...initialValue, replicas: 5 }, binding);
|
|
170
170
|
```
|
|
171
171
|
|
|
172
|
-
## libconfig transformer
|
|
172
|
+
## libconfig transformer
|
|
173
173
|
|
|
174
174
|
Edit a **libconfig document** — the `.cfg`/`.conf` format of srsRAN, OAI, and
|
|
175
175
|
other C/C++ software using [libconfig](https://hyperrealm.github.io/libconfig/).
|
|
176
|
-
**Beta**: it logs a one-time warning on first use; diff `toSource` output
|
|
177
|
-
against the original before deploying a write-back.
|
|
178
176
|
|
|
179
177
|
```ts
|
|
180
178
|
import { libconfigTransformer } from 'ng-form-foundry-transformers';
|
|
@@ -294,6 +292,14 @@ npm test # node:test on the compiled output (no Python needed)
|
|
|
294
292
|
|
|
295
293
|
## Status
|
|
296
294
|
|
|
295
|
+
`0.5.0` — the **libconfig transformer is stable**: the one-time beta warning
|
|
296
|
+
is gone (and with it the `resetLibconfigBetaWarning` helper), and the format's
|
|
297
|
+
guarantees and known limitations are documented in the transformers guide.
|
|
298
|
+
Non-decimal integer literals (`0x`, `0b`, `0o`/`0q`) now set a `radix` display
|
|
299
|
+
hint on the generated schema — the paired `ng-form-foundry` release renders
|
|
300
|
+
those fields as hex/octal/binary editors, and the YAML transformer does the
|
|
301
|
+
same for its hex and octal literals.
|
|
302
|
+
|
|
297
303
|
`0.4.1` — libconfig hardening after a conformance battle test against the C
|
|
298
304
|
library's own scanner, test corpus, and real srsRAN/OAI configs: `0q`/`0Q`
|
|
299
305
|
octal, verbatim radix-prefix preservation on edits, sign accepted on decimal
|
package/dist/core/schema.d.ts
CHANGED
|
@@ -32,6 +32,13 @@ export interface Leaf {
|
|
|
32
32
|
min?: number;
|
|
33
33
|
max?: number;
|
|
34
34
|
multipleOf?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Present the value in this base (16 hex, 8 octal, 2 binary) instead of
|
|
37
|
+
* decimal — set when the source document wrote the literal that way. Purely
|
|
38
|
+
* a display hint: the value itself stays a plain number (or, on a string
|
|
39
|
+
* leaf, the exact decimal-digit carry of an integer beyond ±2^53).
|
|
40
|
+
*/
|
|
41
|
+
radix?: 2 | 8 | 16;
|
|
35
42
|
/** The value may be `null` (JSON Schema `type: [T, 'null']`). */
|
|
36
43
|
nullable?: boolean;
|
|
37
44
|
/** Optional scalar whose presence is itself data (on/off toggle). */
|
|
@@ -47,6 +54,8 @@ export interface LeafList {
|
|
|
47
54
|
label?: string;
|
|
48
55
|
minItems?: number;
|
|
49
56
|
maxItems?: number;
|
|
57
|
+
/** Present every item in this base — see {@link Leaf.radix}. */
|
|
58
|
+
radix?: 2 | 8 | 16;
|
|
50
59
|
}
|
|
51
60
|
export interface NodeGroup {
|
|
52
61
|
kind: 'nodeGroup';
|
package/dist/index.d.ts
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* - `json` — JSON config → form → JSON ({@link jsonTransformer}); same builders
|
|
14
14
|
* as `yaml`, indent preserved.
|
|
15
15
|
* - `libconfig` — libconfig document (srsRAN/OAI-style `.cfg`/`.conf`) → form →
|
|
16
|
-
* libconfig ({@link libconfigTransformer});
|
|
17
|
-
*
|
|
16
|
+
* libconfig ({@link libconfigTransformer}); comment- and
|
|
17
|
+
* type-preserving span splicing on revert.
|
|
18
18
|
*
|
|
19
19
|
* Look transformers up at runtime through {@link TransformerRegistry}, or import
|
|
20
20
|
* the one you need directly.
|
package/dist/index.js
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
* - `json` — JSON config → form → JSON ({@link jsonTransformer}); same builders
|
|
15
15
|
* as `yaml`, indent preserved.
|
|
16
16
|
* - `libconfig` — libconfig document (srsRAN/OAI-style `.cfg`/`.conf`) → form →
|
|
17
|
-
* libconfig ({@link libconfigTransformer});
|
|
18
|
-
*
|
|
17
|
+
* libconfig ({@link libconfigTransformer}); comment- and
|
|
18
|
+
* type-preserving span splicing on revert.
|
|
19
19
|
*
|
|
20
20
|
* Look transformers up at runtime through {@link TransformerRegistry}, or import
|
|
21
21
|
* the one you need directly.
|
|
@@ -41,4 +41,4 @@ __exportStar(require("./core"), exports);
|
|
|
41
41
|
__exportStar(require("./transformers/yang"), exports);
|
|
42
42
|
__exportStar(require("./transformers/yaml"), exports);
|
|
43
43
|
__exportStar(require("./transformers/json"), exports);
|
|
44
|
-
__exportStar(require("./transformers/libconfig"), exports);
|
|
44
|
+
__exportStar(require("./transformers/libconfig"), exports);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { libconfigTransformer,
|
|
1
|
+
export { libconfigTransformer, type LibconfigBinding, type LibconfigOptions, } from './libconfig-transformer';
|
|
2
2
|
export { parseLibconfig, LibconfigParseError } from './parser';
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LibconfigParseError = exports.parseLibconfig = exports.
|
|
3
|
+
exports.LibconfigParseError = exports.parseLibconfig = exports.libconfigTransformer = void 0;
|
|
4
4
|
var libconfig_transformer_1 = require("./libconfig-transformer");
|
|
5
5
|
Object.defineProperty(exports, "libconfigTransformer", { enumerable: true, get: function () { return libconfig_transformer_1.libconfigTransformer; } });
|
|
6
|
-
Object.defineProperty(exports, "resetLibconfigBetaWarning", { enumerable: true, get: function () { return libconfig_transformer_1.resetLibconfigBetaWarning; } });
|
|
7
6
|
var parser_1 = require("./parser");
|
|
8
7
|
Object.defineProperty(exports, "parseLibconfig", { enumerable: true, get: function () { return parser_1.parseLibconfig; } });
|
|
9
8
|
Object.defineProperty(exports, "LibconfigParseError", { enumerable: true, get: function () { return parser_1.LibconfigParseError; } });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A libconfig {@link Transformer}
|
|
2
|
+
* A libconfig {@link Transformer}: turn a libconfig document
|
|
3
3
|
* (srsRAN/OAI-style `.cfg`/`.conf`) into a form and write the edited value
|
|
4
4
|
* back with comments, formatting, and — critically — scalar *types*
|
|
5
5
|
* preserved (libconfig is statically typed; see {@link import('./revert')}).
|
|
@@ -47,8 +47,6 @@ export interface LibconfigBinding {
|
|
|
47
47
|
source: string;
|
|
48
48
|
root: CfgGroup;
|
|
49
49
|
}
|
|
50
|
-
/** Test seam: makes the one-time beta warning observable per test. */
|
|
51
|
-
export declare function resetLibconfigBetaWarning(): void;
|
|
52
50
|
export declare const libconfigTransformer: {
|
|
53
51
|
id: string;
|
|
54
52
|
toSchema(source: string, options?: LibconfigOptions): TransformResult<LibconfigBinding>;
|
|
@@ -1,28 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.libconfigTransformer = void 0;
|
|
4
|
-
exports.resetLibconfigBetaWarning = resetLibconfigBetaWarning;
|
|
5
4
|
const thesaurus_1 = require("../../core/thesaurus");
|
|
6
5
|
const json_schema_1 = require("../../core/json-schema");
|
|
7
6
|
const parser_1 = require("./parser");
|
|
8
7
|
const schema_1 = require("./schema");
|
|
9
8
|
const revert_1 = require("./revert");
|
|
10
|
-
let warnedBeta = false;
|
|
11
|
-
function warnBeta() {
|
|
12
|
-
if (warnedBeta)
|
|
13
|
-
return;
|
|
14
|
-
warnedBeta = true;
|
|
15
|
-
console.warn('[ng-form-foundry-transformers] The libconfig transformer is a BETA feature. ' +
|
|
16
|
-
'Verify every write-back (diff toSource output against the original file) before deploying it.');
|
|
17
|
-
}
|
|
18
|
-
/** Test seam: makes the one-time beta warning observable per test. */
|
|
19
|
-
function resetLibconfigBetaWarning() {
|
|
20
|
-
warnedBeta = false;
|
|
21
|
-
}
|
|
22
9
|
exports.libconfigTransformer = {
|
|
23
10
|
id: 'libconfig',
|
|
24
11
|
toSchema(source, options) {
|
|
25
|
-
warnBeta();
|
|
26
12
|
const root = (0, parser_1.parseLibconfig)(source, { includes: options?.includes });
|
|
27
13
|
const schema = options?.schema
|
|
28
14
|
? (0, json_schema_1.jsonSchemaToNodeGroup)(options.schema, options.rootName, options.schemaOptions)
|
|
@@ -32,7 +18,6 @@ exports.libconfigTransformer = {
|
|
|
32
18
|
return { schema: labeled, binding: { source, root }, initialValue };
|
|
33
19
|
},
|
|
34
20
|
toSource(value, binding) {
|
|
35
|
-
warnBeta();
|
|
36
21
|
return (0, revert_1.applyValueToSource)(binding.source, binding.root, value);
|
|
37
22
|
},
|
|
38
23
|
};
|
|
@@ -80,14 +80,21 @@ function scalarLeaf(name, scalar) {
|
|
|
80
80
|
case 'float':
|
|
81
81
|
return { kind: 'leaf', name, type: 'number' };
|
|
82
82
|
case 'int':
|
|
83
|
-
return { kind: 'leaf', name, type: 'number', integer: true };
|
|
83
|
+
return withRadix({ kind: 'leaf', name, type: 'number', integer: true }, scalar);
|
|
84
84
|
case 'int64':
|
|
85
85
|
// Beyond 2^53 the value rides as an exact decimal string.
|
|
86
86
|
return typeof scalar.value === 'string'
|
|
87
|
-
? { kind: 'leaf', name, type: 'string', pattern: INTEGER_STRING_PATTERN }
|
|
88
|
-
: { kind: 'leaf', name, type: 'number', integer: true };
|
|
87
|
+
? withRadix({ kind: 'leaf', name, type: 'string', pattern: INTEGER_STRING_PATTERN }, scalar)
|
|
88
|
+
: withRadix({ kind: 'leaf', name, type: 'number', integer: true }, scalar);
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
+
/** Carry a non-decimal literal's base onto the leaf as its display radix. */
|
|
92
|
+
function withRadix(leaf, scalar) {
|
|
93
|
+
const radix = scalar.int?.radix;
|
|
94
|
+
if (radix && radix !== 10)
|
|
95
|
+
leaf.radix = radix;
|
|
96
|
+
return leaf;
|
|
97
|
+
}
|
|
91
98
|
/**
|
|
92
99
|
* A homogeneous scalar collection → leafList. One int64 element beyond 2^53
|
|
93
100
|
* degrades the whole list to string carry: a leafList holds one scalar type,
|
|
@@ -97,10 +104,23 @@ function listLeaf(name, elements) {
|
|
|
97
104
|
if (elements.length === 0)
|
|
98
105
|
return rawLeaf(name, 'empty collection');
|
|
99
106
|
const f = (0, parser_1.family)(elements[0].type);
|
|
107
|
+
const radix = f === 'integer' ? sharedRadix(elements) : undefined;
|
|
100
108
|
if (f === 'integer' && elements.some((e) => typeof e.value === 'string')) {
|
|
101
|
-
return { kind: 'leafList', name, type: 'string' };
|
|
109
|
+
return { kind: 'leafList', name, type: 'string', ...(radix && { radix }) };
|
|
102
110
|
}
|
|
103
|
-
return {
|
|
111
|
+
return {
|
|
112
|
+
kind: 'leafList',
|
|
113
|
+
name,
|
|
114
|
+
type: f === 'integer' || f === 'float' ? 'number' : f === 'bool' ? 'boolean' : 'string',
|
|
115
|
+
...(radix && { radix }),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/** The display radix shared by every element, when uniform and non-decimal. */
|
|
119
|
+
function sharedRadix(elements) {
|
|
120
|
+
const first = elements[0]?.int?.radix ?? 10;
|
|
121
|
+
if (first === 10)
|
|
122
|
+
return undefined;
|
|
123
|
+
return elements.every((e) => (e.int?.radix ?? 10) === first) ? first : undefined;
|
|
104
124
|
}
|
|
105
125
|
function arrayValue(elements) {
|
|
106
126
|
const stringCarry = elements.length > 0 &&
|
|
@@ -30,6 +30,8 @@ exports.yamlTransformer = {
|
|
|
30
30
|
const schema = options?.schema
|
|
31
31
|
? (0, json_schema_1.jsonSchemaToNodeGroup)(options.schema, options.rootName, options.schemaOptions)
|
|
32
32
|
: (0, infer_1.inferNodeGroup)(data, options?.rootName);
|
|
33
|
+
if (!options?.schema && doc.contents)
|
|
34
|
+
annotateRadix(doc.contents, schema);
|
|
33
35
|
const labeled = options?.thesaurus ? (0, thesaurus_1.applyThesaurus)(schema, options.thesaurus) : schema;
|
|
34
36
|
return { schema: labeled, binding: doc, initialValue: data };
|
|
35
37
|
},
|
|
@@ -41,6 +43,67 @@ exports.yamlTransformer = {
|
|
|
41
43
|
// `satisfies` verifies conformance to the catalog contract while keeping the
|
|
42
44
|
// concrete sync return types, so direct callers need no `await`.
|
|
43
45
|
};
|
|
46
|
+
const RADIX_BY_FORMAT = { BIN: 2, OCT: 8, HEX: 16 };
|
|
47
|
+
/**
|
|
48
|
+
* Copy non-decimal integer presentation (`0x`/`0o`/`0b` literals) from the
|
|
49
|
+
* parsed document onto the inferred schema as leaf/leafList `radix` display
|
|
50
|
+
* hints — the plain-data inference cannot see them, because `toJS()` drops the
|
|
51
|
+
* scalar format. The revert needs nothing: scalars are mutated in place, so an
|
|
52
|
+
* edited value re-emits in its literal's own base. Across group-list items the
|
|
53
|
+
* first non-decimal occurrence wins — one shared item schema cannot vary the
|
|
54
|
+
* display per item. Schema-driven mode is untouched (JSON Schema has no radix
|
|
55
|
+
* vocabulary).
|
|
56
|
+
*/
|
|
57
|
+
function annotateRadix(node, schema) {
|
|
58
|
+
switch (schema.kind) {
|
|
59
|
+
case 'leaf': {
|
|
60
|
+
const radix = scalarRadix(node);
|
|
61
|
+
if (radix && !schema.radix)
|
|
62
|
+
schema.radix = radix;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
case 'leafList': {
|
|
66
|
+
if (!(0, yaml_1.isSeq)(node) || schema.radix)
|
|
67
|
+
return;
|
|
68
|
+
const radixes = node.items.map(scalarRadix);
|
|
69
|
+
if (radixes[0] && radixes.every((r) => r === radixes[0]))
|
|
70
|
+
schema.radix = radixes[0];
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
case 'nodeGroup': {
|
|
74
|
+
if (!(0, yaml_1.isMap)(node))
|
|
75
|
+
return;
|
|
76
|
+
for (const pair of node.items) {
|
|
77
|
+
const child = schema.children[pairKey(pair.key)];
|
|
78
|
+
if (child)
|
|
79
|
+
annotateRadix(pair.value, child);
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
case 'nodeGroupList': {
|
|
84
|
+
if (!(0, yaml_1.isSeq)(node))
|
|
85
|
+
return;
|
|
86
|
+
for (const item of node.items)
|
|
87
|
+
annotateRadix(item, schema.type);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
default:
|
|
91
|
+
return; // choice/map never come out of plain-data inference
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** The display radix of a non-decimal integer scalar node, else undefined. */
|
|
95
|
+
function scalarRadix(node) {
|
|
96
|
+
if (!(0, yaml_1.isScalar)(node))
|
|
97
|
+
return undefined;
|
|
98
|
+
if (typeof node.value !== 'number' && typeof node.value !== 'bigint')
|
|
99
|
+
return undefined;
|
|
100
|
+
return RADIX_BY_FORMAT[node.format ?? ''];
|
|
101
|
+
}
|
|
102
|
+
/** The string a key node takes as a JS object key, matching `doc.toJS()`. */
|
|
103
|
+
function pairKey(key) {
|
|
104
|
+
const v = (0, yaml_1.isScalar)(key) ? key.value : key;
|
|
105
|
+
return v == null ? '' : String(v);
|
|
106
|
+
}
|
|
44
107
|
/**
|
|
45
108
|
* Replace every BigInt from an `intAsBigInt` parse with a form-value scalar: a
|
|
46
109
|
* plain `number` when it fits the safe range, otherwise its decimal string (so
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ng-form-foundry-transformers",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Framework-agnostic Node + TypeScript catalog of source-format transformers: turn a YANG model (and more) into an ng-form-foundry schema and revert the edited form value back to the source format.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ng-form-foundry",
|