ng-form-foundry-transformers 0.2.1

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 (50) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +186 -0
  3. package/dist/core/index.d.ts +14 -0
  4. package/dist/core/index.js +19 -0
  5. package/dist/core/infer.d.ts +15 -0
  6. package/dist/core/infer.js +67 -0
  7. package/dist/core/json-schema.d.ts +26 -0
  8. package/dist/core/json-schema.js +87 -0
  9. package/dist/core/registry.d.ts +18 -0
  10. package/dist/core/registry.js +40 -0
  11. package/dist/core/schema.d.ts +69 -0
  12. package/dist/core/schema.js +15 -0
  13. package/dist/core/transformer.d.ts +48 -0
  14. package/dist/core/transformer.js +2 -0
  15. package/dist/index.d.ts +22 -0
  16. package/dist/index.js +40 -0
  17. package/dist/transformers/json/index.d.ts +8 -0
  18. package/dist/transformers/json/index.js +11 -0
  19. package/dist/transformers/json/json-transformer.d.ts +38 -0
  20. package/dist/transformers/json/json-transformer.js +41 -0
  21. package/dist/transformers/yaml/index.d.ts +11 -0
  22. package/dist/transformers/yaml/index.js +15 -0
  23. package/dist/transformers/yaml/revert.d.ts +13 -0
  24. package/dist/transformers/yaml/revert.js +74 -0
  25. package/dist/transformers/yaml/yaml-transformer.d.ts +32 -0
  26. package/dist/transformers/yaml/yaml-transformer.js +37 -0
  27. package/dist/transformers/yang/adapter.d.ts +40 -0
  28. package/dist/transformers/yang/adapter.js +68 -0
  29. package/dist/transformers/yang/cache.d.ts +18 -0
  30. package/dist/transformers/yang/cache.js +19 -0
  31. package/dist/transformers/yang/engine.d.ts +41 -0
  32. package/dist/transformers/yang/engine.js +2 -0
  33. package/dist/transformers/yang/engines/fake-engine.d.ts +15 -0
  34. package/dist/transformers/yang/engines/fake-engine.js +26 -0
  35. package/dist/transformers/yang/engines/subprocess-engine.d.ts +30 -0
  36. package/dist/transformers/yang/engines/subprocess-engine.js +64 -0
  37. package/dist/transformers/yang/index.d.ts +20 -0
  38. package/dist/transformers/yang/index.js +27 -0
  39. package/dist/transformers/yang/mapper.d.ts +14 -0
  40. package/dist/transformers/yang/mapper.js +96 -0
  41. package/dist/transformers/yang/model.d.ts +111 -0
  42. package/dist/transformers/yang/model.js +13 -0
  43. package/dist/transformers/yang/revert.d.ts +6 -0
  44. package/dist/transformers/yang/revert.js +173 -0
  45. package/dist/transformers/yang/rfc7951.d.ts +38 -0
  46. package/dist/transformers/yang/rfc7951.js +80 -0
  47. package/dist/transformers/yang/yang-transformer.d.ts +16 -0
  48. package/dist/transformers/yang/yang-transformer.js +29 -0
  49. package/package.json +55 -0
  50. package/python/emit_effective_tree.py +227 -0
@@ -0,0 +1,18 @@
1
+ import { CompiledModel } from './model';
2
+ /**
3
+ * Pluggable store for compiled models, keyed by a caller-chosen id (typically a
4
+ * hash of the yang-library so the cache invalidates when the device's module
5
+ * set changes). Swap the in-memory default for Redis or similar in production.
6
+ */
7
+ export interface ArtifactCache {
8
+ get(key: string): Promise<CompiledModel | undefined>;
9
+ set(key: string, model: CompiledModel): Promise<void>;
10
+ delete(key: string): Promise<void>;
11
+ }
12
+ /** A process-local {@link ArtifactCache}. Fine for a single instance; not shared across replicas. */
13
+ export declare class InMemoryCache implements ArtifactCache {
14
+ private readonly store;
15
+ get(key: string): Promise<CompiledModel | undefined>;
16
+ set(key: string, model: CompiledModel): Promise<void>;
17
+ delete(key: string): Promise<void>;
18
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InMemoryCache = void 0;
4
+ /** A process-local {@link ArtifactCache}. Fine for a single instance; not shared across replicas. */
5
+ class InMemoryCache {
6
+ constructor() {
7
+ this.store = new Map();
8
+ }
9
+ async get(key) {
10
+ return this.store.get(key);
11
+ }
12
+ async set(key, model) {
13
+ this.store.set(key, model);
14
+ }
15
+ async delete(key) {
16
+ this.store.delete(key);
17
+ }
18
+ }
19
+ exports.InMemoryCache = InMemoryCache;
@@ -0,0 +1,41 @@
1
+ import { EffectiveModel } from './model';
2
+ /**
3
+ * A request to resolve a YANG module set into an {@link EffectiveModel}.
4
+ *
5
+ * `source` locates the raw `.yang` files (a directory, or an in-memory map of
6
+ * module name → text). `yangLibrary` pins exactly which modules, revisions,
7
+ * features, and deviations are in effect (RFC 8525 ietf-yang-library), so the
8
+ * resolved model matches the target datastore. `datastore` selects whether
9
+ * `config false` state nodes are included.
10
+ */
11
+ export interface CompileRequest {
12
+ entryModule: string;
13
+ source: YangSource;
14
+ yangLibrary?: unknown;
15
+ datastore?: 'config' | 'operational';
16
+ }
17
+ export type YangSource = {
18
+ kind: 'dir';
19
+ path: string;
20
+ } | {
21
+ kind: 'inline';
22
+ modules: Record<string, string>;
23
+ };
24
+ export interface ValidationResult {
25
+ valid: boolean;
26
+ errors: string[];
27
+ }
28
+ /**
29
+ * The pluggable YANG engine — the only part of the adapter that touches YANG
30
+ * semantics or the environment.
31
+ *
32
+ * Implementations resolve a module set into an {@link EffectiveModel} and
33
+ * validate RFC 7951 instance data against it. The recommended implementation
34
+ * shells out to pyang/yangson (`SubprocessEngine`); tests use `FakeEngine`.
35
+ * Keeping this an interface is what lets the adapter stay framework- and
36
+ * tooling-agnostic.
37
+ */
38
+ export interface YangEngine {
39
+ resolve(req: CompileRequest): Promise<EffectiveModel>;
40
+ validate(data: unknown, model: EffectiveModel): Promise<ValidationResult>;
41
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,15 @@
1
+ import { CompileRequest, ValidationResult, YangEngine } from '../engine';
2
+ import { EffectiveModel } from '../model';
3
+ /**
4
+ * A {@link YangEngine} backed by pre-resolved effective models, keyed by entry
5
+ * module name. Used in tests and for local development so the adapter runs
6
+ * without a Python toolchain. An optional `validator` lets a test assert the
7
+ * validation branch; by default validation always passes.
8
+ */
9
+ export declare class FakeEngine implements YangEngine {
10
+ private readonly models;
11
+ private readonly validator?;
12
+ constructor(models: Record<string, EffectiveModel>, validator?: ((data: unknown, model: EffectiveModel) => ValidationResult) | undefined);
13
+ resolve(req: CompileRequest): Promise<EffectiveModel>;
14
+ validate(data: unknown, model: EffectiveModel): Promise<ValidationResult>;
15
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FakeEngine = void 0;
4
+ /**
5
+ * A {@link YangEngine} backed by pre-resolved effective models, keyed by entry
6
+ * module name. Used in tests and for local development so the adapter runs
7
+ * without a Python toolchain. An optional `validator` lets a test assert the
8
+ * validation branch; by default validation always passes.
9
+ */
10
+ class FakeEngine {
11
+ constructor(models, validator) {
12
+ this.models = models;
13
+ this.validator = validator;
14
+ }
15
+ async resolve(req) {
16
+ const model = this.models[req.entryModule];
17
+ if (!model) {
18
+ throw new Error(`FakeEngine has no effective model for entry module '${req.entryModule}'`);
19
+ }
20
+ return model;
21
+ }
22
+ async validate(data, model) {
23
+ return this.validator ? this.validator(data, model) : { valid: true, errors: [] };
24
+ }
25
+ }
26
+ exports.FakeEngine = FakeEngine;
@@ -0,0 +1,30 @@
1
+ import { CompileRequest, ValidationResult, YangEngine } from '../engine';
2
+ import { EffectiveModel } from '../model';
3
+ export interface SubprocessEngineOptions {
4
+ /** Python interpreter. Defaults to `python3`. */
5
+ python?: string;
6
+ /** Directory holding the bundled helper scripts. Defaults to the packaged `python/`. */
7
+ scriptDir?: string;
8
+ /** Extra `--path` entries passed to the resolver for imported modules. */
9
+ yangPath?: string[];
10
+ }
11
+ /**
12
+ * A {@link YangEngine} that resolves and validates by shelling out to the
13
+ * bundled Python helpers, which wrap pyang/yangson (the maintained YANG
14
+ * toolchain). Requires Python plus `pyang` and `yangson` on the host — ship them
15
+ * in the deployment image, or run this behind a sidecar.
16
+ *
17
+ * `resolve` runs `emit_effective_tree.py` and parses its JSON as an
18
+ * {@link EffectiveModel}. `validate` is currently a no-op — full RFC 7951
19
+ * validation with yangson needs the target's yang-library.
20
+ * Counterpart of `FakeEngine`, which serves canned models for tests.
21
+ */
22
+ export declare class SubprocessEngine implements YangEngine {
23
+ private readonly python;
24
+ private readonly scriptDir;
25
+ private readonly yangPath;
26
+ constructor(opts?: SubprocessEngineOptions);
27
+ resolve(req: CompileRequest): Promise<EffectiveModel>;
28
+ validate(_data: unknown, _model: EffectiveModel): Promise<ValidationResult>;
29
+ private run;
30
+ }
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SubprocessEngine = void 0;
4
+ const node_child_process_1 = require("node:child_process");
5
+ const node_path_1 = require("node:path");
6
+ /**
7
+ * A {@link YangEngine} that resolves and validates by shelling out to the
8
+ * bundled Python helpers, which wrap pyang/yangson (the maintained YANG
9
+ * toolchain). Requires Python plus `pyang` and `yangson` on the host — ship them
10
+ * in the deployment image, or run this behind a sidecar.
11
+ *
12
+ * `resolve` runs `emit_effective_tree.py` and parses its JSON as an
13
+ * {@link EffectiveModel}. `validate` is currently a no-op — full RFC 7951
14
+ * validation with yangson needs the target's yang-library.
15
+ * Counterpart of `FakeEngine`, which serves canned models for tests.
16
+ */
17
+ class SubprocessEngine {
18
+ constructor(opts = {}) {
19
+ this.python = opts.python ?? 'python3';
20
+ // Compiled to dist/transformers/yang/engines/; the bundled python/ sits at
21
+ // the package root, four levels up.
22
+ this.scriptDir = opts.scriptDir ?? (0, node_path_1.join)(__dirname, '..', '..', '..', '..', 'python');
23
+ this.yangPath = opts.yangPath ?? [];
24
+ }
25
+ async resolve(req) {
26
+ if (req.source.kind !== 'dir') {
27
+ throw new Error('SubprocessEngine currently supports only { kind: "dir" } sources');
28
+ }
29
+ const args = [
30
+ (0, node_path_1.join)(this.scriptDir, 'emit_effective_tree.py'),
31
+ '--entry', req.entryModule,
32
+ '--source', req.source.path,
33
+ '--datastore', req.datastore ?? 'config',
34
+ ...this.yangPath.flatMap((p) => ['--path', p]),
35
+ ];
36
+ const stdout = await this.run(args);
37
+ return JSON.parse(stdout);
38
+ }
39
+ async validate(_data, _model) {
40
+ // No-op: RFC 7951 validation via yangson requires the target's yang-library.
41
+ return { valid: true, errors: [] };
42
+ }
43
+ run(args, stdin) {
44
+ return new Promise((resolve, reject) => {
45
+ const child = (0, node_child_process_1.spawn)(this.python, args, { stdio: ['pipe', 'pipe', 'pipe'] });
46
+ let out = '';
47
+ let err = '';
48
+ child.stdout.on('data', (d) => (out += d));
49
+ child.stderr.on('data', (d) => (err += d));
50
+ child.on('error', reject);
51
+ child.on('close', (code) => {
52
+ if (code === 0)
53
+ resolve(out);
54
+ else
55
+ reject(new Error(`${this.python} exited ${code}: ${err.trim()}`));
56
+ });
57
+ if (stdin !== undefined)
58
+ child.stdin.end(stdin);
59
+ else
60
+ child.stdin.end();
61
+ });
62
+ }
63
+ }
64
+ exports.SubprocessEngine = SubprocessEngine;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * YANG transformer — turn a YANG model into an ng-form-foundry schema and revert
3
+ * the edited form value back to RFC 7951 instance data.
4
+ *
5
+ * {@link createYangTransformer} is the catalog-conformant entry point;
6
+ * {@link YangFormAdapter} is the fuller stateful orchestrator (caching,
7
+ * engine-side validation, RFC 7951 → form-value conversion).
8
+ */
9
+ export { createYangTransformer } from './yang-transformer';
10
+ export { YangFormAdapter, YangValidationError } from './adapter';
11
+ export type { CompileOptions } from './adapter';
12
+ export type { YangEngine, CompileRequest, YangSource, ValidationResult } from './engine';
13
+ export { SubprocessEngine } from './engines/subprocess-engine';
14
+ export type { SubprocessEngineOptions } from './engines/subprocess-engine';
15
+ export { FakeEngine } from './engines/fake-engine';
16
+ export { InMemoryCache } from './cache';
17
+ export type { ArtifactCache } from './cache';
18
+ export { mapToSchema } from './mapper';
19
+ export { toFormValue, toYangData } from './revert';
20
+ export type { EffectiveModel, CompiledModel, EffNode, EffLeaf, EffLeafList, EffContainer, EffList, YangType, YangBase, ModuleInfo, } from './model';
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ /**
3
+ * YANG transformer — turn a YANG model into an ng-form-foundry schema and revert
4
+ * the edited form value back to RFC 7951 instance data.
5
+ *
6
+ * {@link createYangTransformer} is the catalog-conformant entry point;
7
+ * {@link YangFormAdapter} is the fuller stateful orchestrator (caching,
8
+ * engine-side validation, RFC 7951 → form-value conversion).
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.toYangData = exports.toFormValue = exports.mapToSchema = exports.InMemoryCache = exports.FakeEngine = exports.SubprocessEngine = exports.YangValidationError = exports.YangFormAdapter = exports.createYangTransformer = void 0;
12
+ var yang_transformer_1 = require("./yang-transformer");
13
+ Object.defineProperty(exports, "createYangTransformer", { enumerable: true, get: function () { return yang_transformer_1.createYangTransformer; } });
14
+ var adapter_1 = require("./adapter");
15
+ Object.defineProperty(exports, "YangFormAdapter", { enumerable: true, get: function () { return adapter_1.YangFormAdapter; } });
16
+ Object.defineProperty(exports, "YangValidationError", { enumerable: true, get: function () { return adapter_1.YangValidationError; } });
17
+ var subprocess_engine_1 = require("./engines/subprocess-engine");
18
+ Object.defineProperty(exports, "SubprocessEngine", { enumerable: true, get: function () { return subprocess_engine_1.SubprocessEngine; } });
19
+ var fake_engine_1 = require("./engines/fake-engine");
20
+ Object.defineProperty(exports, "FakeEngine", { enumerable: true, get: function () { return fake_engine_1.FakeEngine; } });
21
+ var cache_1 = require("./cache");
22
+ Object.defineProperty(exports, "InMemoryCache", { enumerable: true, get: function () { return cache_1.InMemoryCache; } });
23
+ var mapper_1 = require("./mapper");
24
+ Object.defineProperty(exports, "mapToSchema", { enumerable: true, get: function () { return mapper_1.mapToSchema; } });
25
+ var revert_1 = require("./revert");
26
+ Object.defineProperty(exports, "toFormValue", { enumerable: true, get: function () { return revert_1.toFormValue; } });
27
+ Object.defineProperty(exports, "toYangData", { enumerable: true, get: function () { return revert_1.toYangData; } });
@@ -0,0 +1,14 @@
1
+ import { EffectiveModel } from './model';
2
+ import { NodeGroup } from '../../core/schema';
3
+ /**
4
+ * Derive the frontend `NodeGroup` schema from a resolved {@link EffectiveModel}.
5
+ *
6
+ * container → nodeGroup (presence containers flagged `presence`), list →
7
+ * nodeGroupList, leaf-list → leafList, choice → a `Choice` node, and leaf → leaf
8
+ * (with `bits` becoming a group of boolean checkboxes and identityref/enum
9
+ * carrying their options). Wire-encoding concerns stay in the binding (the
10
+ * effective model); this output is the pure render contract handed to the app.
11
+ *
12
+ * Counterpart of the revert in `revert.ts`, which walks the same effective model.
13
+ */
14
+ export declare function mapToSchema(model: EffectiveModel): NodeGroup;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mapToSchema = mapToSchema;
4
+ const rfc7951_1 = require("./rfc7951");
5
+ /**
6
+ * Derive the frontend `NodeGroup` schema from a resolved {@link EffectiveModel}.
7
+ *
8
+ * container → nodeGroup (presence containers flagged `presence`), list →
9
+ * nodeGroupList, leaf-list → leafList, choice → a `Choice` node, and leaf → leaf
10
+ * (with `bits` becoming a group of boolean checkboxes and identityref/enum
11
+ * carrying their options). Wire-encoding concerns stay in the binding (the
12
+ * effective model); this output is the pure render contract handed to the app.
13
+ *
14
+ * Counterpart of the revert in `revert.ts`, which walks the same effective model.
15
+ */
16
+ function mapToSchema(model) {
17
+ return {
18
+ kind: 'nodeGroup',
19
+ name: '__root__',
20
+ root: true,
21
+ children: mapChildren(model.roots),
22
+ };
23
+ }
24
+ function mapChildren(nodes) {
25
+ const children = {};
26
+ for (const node of nodes) {
27
+ children[node.name] = mapNode(node);
28
+ }
29
+ return children;
30
+ }
31
+ function mapNode(node) {
32
+ switch (node.kind) {
33
+ case 'leaf': {
34
+ // `bits` has no scalar form control; render it as a group of checkboxes,
35
+ // one boolean per flag. The revert collapses the group back to the wire
36
+ // string.
37
+ if (node.type.base === 'bits' && node.type.bits) {
38
+ return bitsGroup(node, node.type.bits);
39
+ }
40
+ const leaf = { kind: 'leaf', name: node.name, type: (0, rfc7951_1.toFormLeafType)(node.type) };
41
+ if (node.mandatory)
42
+ leaf.required = true;
43
+ if (node.default !== undefined)
44
+ leaf.default = node.default;
45
+ const options = node.type.enums ?? node.type.identities?.map((i) => i.name);
46
+ if (options)
47
+ leaf.enum = [...options];
48
+ return leaf;
49
+ }
50
+ case 'leaf-list': {
51
+ const list = { kind: 'leafList', name: node.name, type: (0, rfc7951_1.toFormLeafType)(node.type) };
52
+ if (node.minElements !== undefined)
53
+ list.minItems = node.minElements;
54
+ if (node.maxElements !== undefined)
55
+ list.maxItems = node.maxElements;
56
+ return list;
57
+ }
58
+ case 'container': {
59
+ const group = { kind: 'nodeGroup', name: node.name, children: mapChildren(node.children) };
60
+ if (node.presence)
61
+ group.presence = true;
62
+ return group;
63
+ }
64
+ case 'list': {
65
+ const groupList = {
66
+ kind: 'nodeGroupList',
67
+ name: node.name,
68
+ type: { kind: 'nodeGroup', name: node.name, children: mapChildren(node.children) },
69
+ };
70
+ if (node.minElements !== undefined)
71
+ groupList.minItems = node.minElements;
72
+ if (node.maxElements !== undefined)
73
+ groupList.maxItems = node.maxElements;
74
+ return groupList;
75
+ }
76
+ case 'choice': {
77
+ const cases = {};
78
+ for (const c of node.cases) {
79
+ cases[c.name] = mapChildren(c.children);
80
+ }
81
+ const choice = { kind: 'choice', name: node.name, cases };
82
+ if (node.default)
83
+ choice.default = node.default;
84
+ if (node.mandatory)
85
+ choice.mandatory = true;
86
+ return choice;
87
+ }
88
+ }
89
+ }
90
+ function bitsGroup(node, bits) {
91
+ const children = {};
92
+ for (const bit of bits) {
93
+ children[bit] = { kind: 'leaf', name: bit, type: 'boolean' };
94
+ }
95
+ return { kind: 'nodeGroup', name: node.name, children };
96
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * The resolved YANG schema — the effective model the engine emits and the
3
+ * "binding" the adapter keeps server-side.
4
+ *
5
+ * A `YangEngine` resolves a raw YANG module set (with `uses`/`augment`/`typedef`
6
+ * already collapsed) into this normalized tree. It carries everything the plain
7
+ * form value drops but the RFC 7951 round-trip needs: the resolved base type,
8
+ * the owning module (for name qualification), list keys, and the config/state
9
+ * flag. `mapToSchema` derives the frontend `NodeGroup` from it; the revert walks
10
+ * it directly. Kept in the adapter, never sent to the client.
11
+ */
12
+ /** YANG built-in base types this version recognizes (RFC 7950 §9). */
13
+ export type YangBase = 'string' | 'boolean' | 'int8' | 'int16' | 'int32' | 'int64' | 'uint8' | 'uint16' | 'uint32' | 'uint64' | 'decimal64' | 'enumeration' | 'binary' | 'identityref' | 'leafref' | 'empty' | 'instance-identifier' | 'union' | 'bits';
14
+ export interface YangType {
15
+ base: YangBase;
16
+ /** Allowed names for `enumeration`. */
17
+ enums?: string[];
18
+ /** Digit count for `decimal64`. */
19
+ fractionDigits?: number;
20
+ /** Derived identities for `identityref`, each tagged with its defining module. */
21
+ identities?: {
22
+ name: string;
23
+ module: string;
24
+ }[];
25
+ /** Named flags for `bits`, in schema order. */
26
+ bits?: string[];
27
+ /** Member types for `union` (ordered; RFC 7950 §9.12). */
28
+ members?: YangType[];
29
+ /** Schema-node path a `leafref` points at (RFC 7950 §9.9). */
30
+ leafrefPath?: string;
31
+ }
32
+ export interface EffLeaf {
33
+ kind: 'leaf';
34
+ name: string;
35
+ module: string;
36
+ type: YangType;
37
+ default?: string | number | boolean;
38
+ mandatory?: boolean;
39
+ /** `false` = read-only operational state; excluded from write-back. Defaults to config (true). */
40
+ config?: boolean;
41
+ description?: string;
42
+ }
43
+ export interface EffLeafList {
44
+ kind: 'leaf-list';
45
+ name: string;
46
+ module: string;
47
+ type: YangType;
48
+ minElements?: number;
49
+ maxElements?: number;
50
+ orderedByUser?: boolean;
51
+ config?: boolean;
52
+ description?: string;
53
+ }
54
+ export interface EffContainer {
55
+ kind: 'container';
56
+ name: string;
57
+ module: string;
58
+ presence?: boolean;
59
+ config?: boolean;
60
+ children: EffNode[];
61
+ description?: string;
62
+ }
63
+ export interface EffList {
64
+ kind: 'list';
65
+ name: string;
66
+ module: string;
67
+ keys: string[];
68
+ minElements?: number;
69
+ maxElements?: number;
70
+ orderedByUser?: boolean;
71
+ config?: boolean;
72
+ children: EffNode[];
73
+ description?: string;
74
+ }
75
+ export interface EffCase {
76
+ name: string;
77
+ children: EffNode[];
78
+ }
79
+ /**
80
+ * A YANG choice (RFC 7950 §7.9): mutually-exclusive cases. The choice itself has
81
+ * no instance data — the selected case's children serialize *inline* at the
82
+ * choice's location, with no choice/case wrapper.
83
+ */
84
+ export interface EffChoice {
85
+ kind: 'choice';
86
+ name: string;
87
+ module: string;
88
+ cases: EffCase[];
89
+ default?: string;
90
+ mandatory?: boolean;
91
+ description?: string;
92
+ }
93
+ export type EffNode = EffLeaf | EffLeafList | EffContainer | EffList | EffChoice;
94
+ export interface ModuleInfo {
95
+ name: string;
96
+ namespace: string;
97
+ revision?: string;
98
+ }
99
+ /**
100
+ * A fully-resolved YANG model: the top-level data nodes plus module metadata.
101
+ * This is the value stored as a compiled model's `binding`.
102
+ */
103
+ export interface EffectiveModel {
104
+ modules: ModuleInfo[];
105
+ roots: EffNode[];
106
+ }
107
+ /** The compiled artifact: the frontend schema plus the server-side binding. */
108
+ export interface CompiledModel {
109
+ schema: import('../../core/schema').NodeGroup;
110
+ binding: EffectiveModel;
111
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ /**
3
+ * The resolved YANG schema — the effective model the engine emits and the
4
+ * "binding" the adapter keeps server-side.
5
+ *
6
+ * A `YangEngine` resolves a raw YANG module set (with `uses`/`augment`/`typedef`
7
+ * already collapsed) into this normalized tree. It carries everything the plain
8
+ * form value drops but the RFC 7951 round-trip needs: the resolved base type,
9
+ * the owning module (for name qualification), list keys, and the config/state
10
+ * flag. `mapToSchema` derives the frontend `NodeGroup` from it; the revert walks
11
+ * it directly. Kept in the adapter, never sent to the client.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,6 @@
1
+ import { EffectiveModel } from './model';
2
+ import { FormValue } from '../../core/schema';
3
+ /** RFC 7951 JSON instance data → a plain form value keyed by bare names. */
4
+ export declare function toFormValue(rfc7951: unknown, model: EffectiveModel): FormValue;
5
+ /** A plain form value → RFC 7951 JSON instance data ready for write-back. */
6
+ export declare function toYangData(value: FormValue, model: EffectiveModel): Record<string, unknown>;