ng-form-foundry-transformers 0.5.5 → 0.6.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 CHANGED
@@ -149,11 +149,16 @@ string / number constraints (`pattern`, `minLength`, `minimum`, `multipleOf`,
149
149
  `format`, …) carried onto the leaves as validators, with
150
150
  `minProperties`/`maxProperties` becoming present-children bounds on closed
151
151
  objects (`minPresent`/`maxPresent`) or entry bounds on open maps.
152
- Non-`required` properties become **`presence` nodes** absent from the form
153
- and the serialized value until
154
- enabled, so the output validates against the source schema (opt out with
155
- `schemaOptions: { optionalPresence: false }`); a required property mapping to a
156
- choice is marked `mandatory`.
152
+ Non-`required` properties (leaves, groups, choices, maps, **and lists**) become
153
+ **`presence` nodes** — absent from the form and the serialized value until
154
+ enabled, so the output validates against the source schema and a zero-edit
155
+ rebuild never injects a key the source lacked (opt out with `schemaOptions: {
156
+ optionalPresence: false }`); a required property mapping to a choice is marked
157
+ `mandatory`. Pass `schemaOptions: { advisoryRequired: true }` to also treat
158
+ *required* properties as presence nodes — they keep `required: true` for
159
+ validation but stay absent until enabled, so an over-broad `required` list (one
160
+ schema covering several config flavours) does not force-write keys the source
161
+ omitted.
157
162
 
158
163
  **The schema must agree with the document on container shapes.** A covered
159
164
  key whose schema says array where the document holds an object (or scalar
@@ -309,6 +314,13 @@ npm test # node:test on the compiled output (no Python needed)
309
314
 
310
315
  ## Status
311
316
 
317
+ `0.6.0` — optional **array** properties now map to `presence` nodes (leaf-lists
318
+ and node-group lists included), so a zero-edit rebuild never injects a list the
319
+ source lacked; new `schemaOptions: { advisoryRequired: true }` treats a schema's
320
+ over-broad `required` list as advisory (required keys become presence nodes,
321
+ keeping `required` for validation only). The paired `ng-form-foundry` 0.6.0
322
+ drives a list's add/remove affordance from required-vs-optional.
323
+
312
324
  `0.5.5` — version-alignment release with the workspace (the paired
313
325
  `ng-form-foundry` 0.5.5 aligns the standalone form and config editor on list
314
326
  cardinality, adds `setNodePresence`, and reflects external form mutations in
@@ -56,6 +56,20 @@ export interface JsonSchemaOptions {
56
56
  * unconditionally.
57
57
  */
58
58
  optionalPresence?: boolean;
59
+ /**
60
+ * Treat the schema's `required` list as *advisory* rather than structural
61
+ * (default `false`). A required property then becomes a presence node too, so
62
+ * presence follows the data: a key present in the initial value materializes,
63
+ * a key absent stays absent — a round-trip never injects a key the source
64
+ * lacked, even one the schema marks `required`. `required` still drives
65
+ * *validation* (a materialized required field must hold a value), it just no
66
+ * longer forces the key to exist. Use for byte-exact editing of an existing
67
+ * document against a schema whose `required` list is over-broad (e.g. one
68
+ * schema covering several config flavors); leave off for new-form authoring,
69
+ * where a required field should render on a blank form so the user can fill
70
+ * it.
71
+ */
72
+ advisoryRequired?: boolean;
59
73
  /**
60
74
  * Display metadata injected into the produced schema (`label`,
61
75
  * `description`, choice `caseLabels`) — see {@link applyThesaurus}. Fills
@@ -23,7 +23,10 @@ const ROOT = '__root__';
23
23
  function jsonSchemaToNodeGroup(schema, name = ROOT, options) {
24
24
  const documents = [schema, ...(options?.refDocuments ?? [])];
25
25
  const resolver = new RefResolver(schema, documents);
26
- const ctx = { optionalPresence: options?.optionalPresence ?? true };
26
+ const ctx = {
27
+ optionalPresence: options?.optionalPresence ?? true,
28
+ advisoryRequired: options?.advisoryRequired ?? false,
29
+ };
27
30
  const { schema: root, scope } = resolver.resolve(schema);
28
31
  const group = objectToNodeGroup(root, name, scope, ctx, root.title);
29
32
  group.root = true;
@@ -32,15 +35,25 @@ function jsonSchemaToNodeGroup(schema, name = ROOT, options) {
32
35
  /**
33
36
  * Apply {@link JsonSchemaOptions.optionalPresence} to one *property* node: a
34
37
  * non-required property becomes a presence node, so it stays absent from the
35
- * form value until enabled. Only called at property positions
38
+ * form value until enabled lists included, so an absent optional array is
39
+ * not injected as an empty one on save. Only called at property positions
36
40
  * ({@link objectToNodeGroup} children and {@link branchToCase} fields) — a map's
37
41
  * `value` template, an array's item type, and a leaf-bodied case are not
38
- * properties and must never be marked. Lists cannot carry `presence`.
42
+ * properties and must never be marked. Under
43
+ * {@link JsonSchemaOptions.advisoryRequired}, a `required` property is marked
44
+ * too (presence then seeds from the data; `required` drives validation only).
39
45
  */
40
46
  function markOptionalProperty(node, required, ctx) {
41
- if (!ctx.optionalPresence || required)
47
+ if (!ctx.optionalPresence)
42
48
  return node;
43
- if (node.kind === 'leaf' || node.kind === 'nodeGroup' || node.kind === 'choice' || node.kind === 'map') {
49
+ if (required && !ctx.advisoryRequired)
50
+ return node;
51
+ if (node.kind === 'leaf' ||
52
+ node.kind === 'nodeGroup' ||
53
+ node.kind === 'choice' ||
54
+ node.kind === 'map' ||
55
+ node.kind === 'leafList' ||
56
+ node.kind === 'nodeGroupList') {
44
57
  node.presence = true;
45
58
  }
46
59
  return node;
@@ -56,6 +56,11 @@ export interface LeafList {
56
56
  maxItems?: number;
57
57
  /** Present every item in this base — see {@link Leaf.radix}. */
58
58
  radix?: 2 | 8 | 16;
59
+ /**
60
+ * Optional list whose presence is itself data — an absent key, distinct from
61
+ * a present-but-empty list. Mirrors {@link NodeGroup.presence}.
62
+ */
63
+ presence?: boolean;
59
64
  }
60
65
  export interface NodeGroup {
61
66
  kind: 'nodeGroup';
@@ -85,6 +90,8 @@ export interface NodeGroupList {
85
90
  type: NodeGroup;
86
91
  minItems?: number;
87
92
  maxItems?: number;
93
+ /** Optional list whose presence is itself data — see {@link LeafList.presence}. */
94
+ presence?: boolean;
88
95
  }
89
96
  /** One case body: a record of named fields, or a single node (a leaf-bodied case). */
90
97
  export type ChoiceCase = Record<string, NodeType> | NodeType;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ng-form-foundry-transformers",
3
- "version": "0.5.5",
3
+ "version": "0.6.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",