@gmb/bitmark-parser-generator 5.35.0 → 5.36.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/dist/index.cjs CHANGED
@@ -1416,6 +1416,7 @@ var propertyKeys = {
1416
1416
  property_dateEnd: "@dateEnd",
1417
1417
  property_decimalPlaces: "@decimalPlaces",
1418
1418
  property_deeplink: "@deeplink",
1419
+ property_displayName: "@displayName",
1419
1420
  property_diffContext: "@diffContext",
1420
1421
  property_diffOp: "@diffOp",
1421
1422
  property_diffRef: "@diffRef",
@@ -2565,9 +2566,30 @@ var CARDSETS = {
2565
2566
  [CardSetConfigKey.definitionList]: {
2566
2567
  jsonKey: "definitions",
2567
2568
  exportJsonKey: { definitions: "$" },
2569
+ // PLAN-141 F1 (NISO import): each <def-item> is one card; the sides
2570
+ // read <term>/<def>; the enclosing <legend>'s <title> lands on the
2571
+ // term side's heading tag ([#Key] header card).
2572
+ // PLAN-141 F3: a <tbx:langSet> is ALSO one card (a terminology entry —
2573
+ // its enclosing <tbx:termEntry> is a transparent claim wrapper); its
2574
+ // children compose the term/definition sides via the side rules below.
2575
+ mappingKeys: {
2576
+ "xml-niso-iec": [
2577
+ { "@el": "def-item", "@children": "$" },
2578
+ { "@el": "tbx:langSet", "@children": "$" }
2579
+ ]
2580
+ },
2568
2581
  sides: [
2569
2582
  {
2570
2583
  name: "term",
2584
+ // F3: every <tbx:tig>'s <tbx:term> STACKS one-per-line onto the
2585
+ // single term side (the "$\n" template's authored separator; the
2586
+ // trailing one is trimmed) — the PDF's Maus/Mus/M/Müsli stack.
2587
+ mappingKeys: {
2588
+ "xml-niso-iec": [
2589
+ { "@el": "term", "@text": "$" },
2590
+ { "@el": "tbx:tig", "@children": { "@el": "tbx:term", "@text": "$\n" } }
2591
+ ]
2592
+ },
2571
2593
  variants: [
2572
2594
  {
2573
2595
  jsonKey: "term.text",
@@ -2583,7 +2605,8 @@ var CARDSETS = {
2583
2605
  format: TagFormat.plainText,
2584
2606
  nullable: true,
2585
2607
  jsonKey: "^heading.forKeys",
2586
- exportJsonKey: { "@bit": { heading: { forKeys: "$" } } }
2608
+ exportJsonKey: { "@bit": { heading: { forKeys: "$" } } },
2609
+ mappingKeys: { "xml-niso-iec": { "@el": "title", "@text": "$" } }
2587
2610
  },
2588
2611
  {
2589
2612
  key: ConfigKey.group_resourceIcon,
@@ -2609,6 +2632,24 @@ var CARDSETS = {
2609
2632
  },
2610
2633
  {
2611
2634
  name: "definition",
2635
+ // F3: the definition side COMPOSES from the entry's fields in
2636
+ // document order, with the ES-rendered locale affixes AUTHORED as
2637
+ // value-position templates (never engine text): the subject field
2638
+ // in ⟨⟩ glues (trailing space) onto the definition line; example /
2639
+ // notes / source follow as blank-line-separated lines. The TBX
2640
+ // grammar flags (normativeAuthorization, termType, partOfSpeech,
2641
+ // grammaticalNumber) are unrendered machine-only data — dropped and
2642
+ // recorded (catalog R-2, reviewed under D7 max-preservation).
2643
+ mappingKeys: {
2644
+ "xml-niso-iec": [
2645
+ { "@el": "def", "@children": "$" },
2646
+ { "@el": "tbx:subjectField", "@text": "\u27E8$\u27E9 " },
2647
+ { "@el": "tbx:definition", "@children": "$" },
2648
+ { "@el": "tbx:example", "@children": "BEISPIEL $" },
2649
+ { "@el": "tbx:note", "@children": "Anmerkung zu Begriff: $" },
2650
+ { "@el": "tbx:source", "@text": "[QUELLE: $]" }
2651
+ ]
2652
+ },
2612
2653
  variants: [
2613
2654
  {
2614
2655
  jsonKey: "definition.text",
@@ -3999,6 +4040,11 @@ var CARDSETS = {
3999
4040
  { table: { header: { rows: ["$"] } } }
4000
4041
  ],
4001
4042
  htmlKey: { "@el": "thead", "@children": { "@el": "tr", "@children": "$" } },
4043
+ // PLAN-141 F2 (NISO import): the XHTML table model NISO uses — a
4044
+ // <thead> SECTION whose <tr> rows are the header cards.
4045
+ mappingKeys: {
4046
+ "xml-niso-iec": { "@el": "thead", "@children": { "@el": "tr", "@children": "$" } }
4047
+ },
4002
4048
  sideJsonKey: "cells[{s}]|set(title=true)",
4003
4049
  sideExportJsonKey: { cells: { $s: { title: true, $: "$" } } },
4004
4050
  // Header cells are `<th>`. The `width` attribute is contributed by the
@@ -4010,12 +4056,21 @@ var CARDSETS = {
4010
4056
  jsonKey: "table.body.rows",
4011
4057
  exportJsonKey: { table: { body: { rows: "$" } } },
4012
4058
  htmlKey: { "@el": "tbody", "@children": { "@el": "tr", "@children": "$" } },
4059
+ // PLAN-141 F2 (NISO import): <tbody> rows are the (default) data cards.
4060
+ mappingKeys: {
4061
+ "xml-niso-iec": { "@el": "tbody", "@children": { "@el": "tr", "@children": "$" } }
4062
+ },
4013
4063
  isDefault: true
4014
4064
  },
4015
4065
  "table-footer": {
4016
4066
  jsonKey: "table.footer.rows",
4017
4067
  exportJsonKey: { table: { footer: { rows: "$" } } },
4018
4068
  htmlKey: { "@el": "tfoot", "@children": { "@el": "tr", "@children": "$" } },
4069
+ // PLAN-141 F2 (NISO import): NISO has no <tfoot> — the footer card's
4070
+ // source is the <table-wrap-foot> block (CONTENT-shaped: no rows; its
4071
+ // prose/notes flatten into one footer card — catalog table-wrap-foot
4072
+ // row; a contained <def-list> promotes as a sibling legend bit).
4073
+ mappingKeys: { "xml-niso-iec": { "@el": "table-wrap-foot", "@children": "$" } },
4019
4074
  sideJsonKey: "cells[{s}]|set(title=true)",
4020
4075
  sideExportJsonKey: { cells: { $s: { title: true, $: "$" } } },
4021
4076
  // Footer cells render as `<th>` (mirrors the header `title: true`
@@ -4032,6 +4087,8 @@ var CARDSETS = {
4032
4087
  // Default (body) cell → `<td>`; the header/footer sections override the
4033
4088
  // side to `<th>` via their sideHtmlKey (HTML.md §8).
4034
4089
  htmlKey: { "@el": "td", "@children": "$" },
4090
+ // PLAN-141 F2 (NISO import): a <td> row child is a body cell side.
4091
+ mappingKeys: { "xml-niso-iec": { "@el": "td", "@children": "$" } },
4035
4092
  variants: [
4036
4093
  {
4037
4094
  jsonKey: "content",
@@ -4049,7 +4106,11 @@ var CARDSETS = {
4049
4106
  // cell with `title: true` automatically.
4050
4107
  jsonKey: "content",
4051
4108
  exportJsonKey: { content: "$" },
4052
- description: "Title text of the table cell (header rows)."
4109
+ description: "Title text of the table cell (header rows).",
4110
+ // PLAN-141 F2 (NISO import): a <th> row child is a TITLE cell —
4111
+ // its content lands in the `[#…]` tag (an empty <th> keeps its
4112
+ // slot as a bare `[#]`).
4113
+ mappingKeys: { "xml-niso-iec": { "@el": "th", "@children": "$" } }
4053
4114
  },
4054
4115
  {
4055
4116
  key: ConfigKey.property_tableCellType,
@@ -4068,6 +4129,9 @@ var CARDSETS = {
4068
4129
  // HTML-C2: contribute a `rowspan` attribute onto the enclosing
4069
4130
  // cell element (`<th>`/`<td>`).
4070
4131
  htmlKey: { "@el": { "@attr": { rowspan: "$" } } },
4132
+ // PLAN-141 F2 (NISO import): recover the source cell's own
4133
+ // `rowspan` attribute (element-scope attr form).
4134
+ mappingKeys: { "xml-niso-iec": { "@el": { "@attr": { rowspan: "$" } } } },
4071
4135
  description: "Number of rows the cell spans.",
4072
4136
  format: TagFormat.number
4073
4137
  },
@@ -4078,6 +4142,9 @@ var CARDSETS = {
4078
4142
  // HTML-C2: contribute a `colspan` attribute onto the enclosing
4079
4143
  // cell element (`<th>`/`<td>`).
4080
4144
  htmlKey: { "@el": { "@attr": { colspan: "$" } } },
4145
+ // PLAN-141 F2 (NISO import): recover the source cell's own
4146
+ // `colspan` attribute (element-scope attr form).
4147
+ mappingKeys: { "xml-niso-iec": { "@el": { "@attr": { colspan: "$" } } } },
4081
4148
  description: "Number of columns the cell spans.",
4082
4149
  format: TagFormat.number
4083
4150
  },
@@ -4099,6 +4166,13 @@ var CARDSETS = {
4099
4166
  // HTML.md §8 + HTML-C2: contribute a `width` attribute onto the
4100
4167
  // enclosing cell element (`<th>`/`<td>`).
4101
4168
  htmlKey: { "@el": { "@attr": { width: "$" } } },
4169
+ // PLAN-141 F2 (NISO import): NISO carries widths as PER-COLUMN
4170
+ // <col width="…"> descriptors at container level, not on cells.
4171
+ // The matched values distribute positionally onto the first
4172
+ // (header) card's sides. Number format ⇒ only an integral
4173
+ // percentage survives ("20.0000%" → 20); a fractional one
4174
+ // (renderer-equalised "4.7619%") is dropped — catalog `col` row.
4175
+ mappingKeys: { "xml-niso-iec": { "@el": "col", "@attr": { width: "$" } } },
4102
4176
  description: "Width for the column.",
4103
4177
  format: TagFormat.number
4104
4178
  }
@@ -4472,7 +4546,22 @@ var GROUPS = {
4472
4546
  key: ConfigKey.property_externalId,
4473
4547
  description: "The external identifier for the bit",
4474
4548
  format: TagFormat.plainText,
4475
- maxCount: Count.infinity
4549
+ maxCount: Count.infinity,
4550
+ // PLAN-140 refs family (NISO import): the machine identifier of a
4551
+ // referenced standard. NESTED content slot — the tag reads (and
4552
+ // consumes) ONLY the <std-id> inside a <std> child; the remaining
4553
+ // designation/title text stays bit body. A <std> without <std-id>
4554
+ // does not fire the key.
4555
+ // PLAN-141 F5: on the [.book] bit, the DATED <std-ref> designation
4556
+ // is the document's external id. Safe rule order: <std-ref> is a
4557
+ // direct bit-element child only under <std-meta> (everywhere else
4558
+ // it nests inside <std>, which the tag spine never reaches).
4559
+ mappingKeys: {
4560
+ "xml-niso-iec": [
4561
+ { "@el": "std", "@children": { "@el": "std-id", "@text": "$" } },
4562
+ { "@el": "std-ref", "@attr": { type: "dated" }, "@text": "$" }
4563
+ ]
4564
+ }
4476
4565
  },
4477
4566
  {
4478
4567
  key: ConfigKey.property_sourceRL,
@@ -4613,7 +4702,8 @@ var GROUPS = {
4613
4702
  {
4614
4703
  key: ConfigKey.property_diffTime,
4615
4704
  description: "The diff time for the bit",
4616
- format: TagFormat.number
4705
+ format: TagFormat.number,
4706
+ nullable: true
4617
4707
  },
4618
4708
  {
4619
4709
  key: ConfigKey.property_ageRange,
@@ -4624,7 +4714,18 @@ var GROUPS = {
4624
4714
  {
4625
4715
  key: ConfigKey.property_lang,
4626
4716
  description: "The language for the bit",
4627
- format: TagFormat.plainText
4717
+ format: TagFormat.plainText,
4718
+ // PLAN-141 F3 (NISO import): a term-sec's language rides
4719
+ // <tbx:termEntry><tbx:langSet xml:lang> — a nested content slot
4720
+ // whose leaf is a $-in-ATTR-position slot (the nested counterpart
4721
+ // of the value-attr slot). Only termEntry elements match, so the
4722
+ // shared tag is inert everywhere else.
4723
+ mappingKeys: {
4724
+ "xml-niso-iec": {
4725
+ "@el": "tbx:termEntry",
4726
+ "@children": { "@el": "tbx:langSet", "@attr": { "xml:lang": "$" } }
4727
+ }
4728
+ }
4628
4729
  },
4629
4730
  {
4630
4731
  key: ConfigKey.property_publisher,
@@ -4823,6 +4924,9 @@ var GROUPS = {
4823
4924
  key: ConfigKey.tag_item,
4824
4925
  jsonKey: "item",
4825
4926
  exportJsonKey: { item: "$" },
4927
+ // PLAN-140 W3 (NISO import): the clause/note label of a mapped
4928
+ // source element ([%1], [%ANMERKUNG 1]).
4929
+ mappingKeys: { "xml-niso-iec": { "@el": "label", "@text": "$" } },
4826
4930
  description: "The item for the bit",
4827
4931
  chain: [
4828
4932
  {
@@ -5785,7 +5889,20 @@ var GROUPS = {
5785
5889
  key: ConfigKey.property_language,
5786
5890
  description: "The language of the book",
5787
5891
  format: TagFormat.plainText,
5788
- maxCount: Count.infinity
5892
+ maxCount: Count.infinity,
5893
+ // PLAN-141 F5 (NISO import): only the PRIMARY (first)
5894
+ // <content-language> survives — the maxEmits cap realises the
5895
+ // catalog's document-language rule (extras drop, R-4).
5896
+ mappingKeys: {
5897
+ "xml-niso-iec": [
5898
+ { predicates: [], maxEmits: 1, rule: { "@el": "content-language", "@text": "$" } }
5899
+ ]
5900
+ }
5901
+ },
5902
+ {
5903
+ key: ConfigKey.property_displayName,
5904
+ description: "The display name of the book",
5905
+ format: TagFormat.plainText
5789
5906
  },
5790
5907
  {
5791
5908
  key: ConfigKey.property_customerExternalId,
@@ -5861,7 +5978,18 @@ var GROUPS = {
5861
5978
  description: "The title of the book",
5862
5979
  maxCount: 2,
5863
5980
  jsonKey: "title|multi(count=2, key=subtitle)",
5864
- exportJsonKey: [{ "@level=1": { title: "$" } }, { "@level=2": { subtitle: "$" } }]
5981
+ exportJsonKey: [{ "@level=1": { title: "$" } }, { "@level=2": { subtitle: "$" } }],
5982
+ // PLAN-141 F5 (NISO import): the PRIMARY (first) <title-wrap>'s
5983
+ // assembled <full> title → [#]; the per-language extras drop (R-4).
5984
+ mappingKeys: {
5985
+ "xml-niso-iec": [
5986
+ {
5987
+ predicates: [],
5988
+ maxEmits: 1,
5989
+ rule: { "@el": "title-wrap", "@children": { "@el": "full", "@text": "$" } }
5990
+ }
5991
+ ]
5992
+ }
5865
5993
  },
5866
5994
  {
5867
5995
  key: ConfigKey.property_subtype,
@@ -6174,17 +6302,20 @@ var GROUPS = {
6174
6302
  {
6175
6303
  key: ConfigKey.property_platformMargin,
6176
6304
  description: "The platform margin",
6177
- format: TagFormat.number
6305
+ format: TagFormat.number,
6306
+ nullable: true
6178
6307
  },
6179
6308
  {
6180
6309
  key: ConfigKey.property_platformBorderRadius,
6181
6310
  description: "The platform border radius",
6182
- format: TagFormat.number
6311
+ format: TagFormat.number,
6312
+ nullable: true
6183
6313
  },
6184
6314
  {
6185
6315
  key: ConfigKey.property_platformSelectionBorderRadius,
6186
6316
  description: "The platform selection border radius",
6187
- format: TagFormat.number
6317
+ format: TagFormat.number,
6318
+ nullable: true
6188
6319
  },
6189
6320
  {
6190
6321
  key: ConfigKey.property_platformNeedsShadow,
@@ -6291,7 +6422,13 @@ var GROUPS = {
6291
6422
  {
6292
6423
  key: ConfigKey.property_caption,
6293
6424
  description: "The caption for the resource",
6294
- format: TagFormat.bitmarkText
6425
+ format: TagFormat.bitmarkText,
6426
+ // PLAN-141 F1 (NISO import): a figure's <caption><title> — the
6427
+ // nested content slot reads the title text; the caption rides the
6428
+ // resource chain ([&image:…][@caption:…], catalog D5).
6429
+ mappingKeys: {
6430
+ "xml-niso-iec": { "@el": "caption", "@children": { "@el": "title", "@text": "$" } }
6431
+ }
6295
6432
  },
6296
6433
  {
6297
6434
  key: ConfigKey.property_showInIndex,
@@ -6565,6 +6702,10 @@ var GROUPS = {
6565
6702
  {
6566
6703
  key: ConfigKey.resource_image,
6567
6704
  exportJsonKey: { resource: { type: "image", image: { src: "$" } } },
6705
+ // PLAN-141 F1 (NISO import): the image URL rides the `$`-in-attr
6706
+ // value slot — a <graphic xlink:href> child of the mapped bit
6707
+ // element roots the [&image:URL] chain.
6708
+ mappingKeys: { "xml-niso-iec": { "@el": "graphic", "@attr": { "xlink:href": "$" } } },
6568
6709
  description: "The image resource",
6569
6710
  chain: [
6570
6711
  {
@@ -7439,6 +7580,8 @@ var BITS = {
7439
7580
  maxCount: 2,
7440
7581
  jsonKey: "title|multi(count=2, key=subtitle)",
7441
7582
  exportJsonKey: [{ "@level=1": { title: "$" } }, { "@level=2": { subtitle: "$" } }],
7583
+ // PLAN-140 W3 (NISO import): the title of a mapped source element.
7584
+ mappingKeys: { "xml-niso-iec": { "@el": "title", "@text": "$" } },
7442
7585
  key: ConfigKey.tag_title,
7443
7586
  description: "The title of the article"
7444
7587
  }
@@ -7480,12 +7623,56 @@ var BITS = {
7480
7623
  [BitType.smartStandardArticleNormative]: {
7481
7624
  since: "1.28.0",
7482
7625
  baseBitType: BitType.standardArticleNormative,
7483
- description: "Smart standard normative article bit"
7626
+ description: "Smart standard normative article bit",
7627
+ // PLAN-140 W3 (NISO import, tranche 1): one article bit per NISO
7628
+ // paragraph (catalog D1). CONTENT keys (@children): the subtree is the
7629
+ // bit's body; nothing inside it promotes. (Ancestor-context normativity
7630
+ // is the non-normative sibling's @ancestor key — plan Q4, resolved.)
7631
+ //
7632
+ // Second rule (catalog D1 push-down): a NUMBERED-PARAGRAPH sec is an
7633
+ // article, not a chapter — the sec-type literal outranks the chapter
7634
+ // key's plain sec|app rule (most-specific-wins). Content key: the sec's
7635
+ // label rides [%] via the label tag key, its inner <p> is the body, its
7636
+ // id is the bit's customerId, and the anchor slot marks it
7637
+ // anchor-bearing.
7638
+ mappingKeys: {
7639
+ "xml-niso-iec": [
7640
+ { "@el": "p", "@attr": { id: "$customerId" }, "@children": "$" },
7641
+ {
7642
+ "@el": "sec",
7643
+ "@attr": { "sec-type": "numbered-paragraph", id: "$customerId" },
7644
+ "@children": "$",
7645
+ "\u25BC": "$anchor"
7646
+ }
7647
+ ]
7648
+ }
7484
7649
  },
7485
7650
  [BitType.smartStandardArticleNonNormative]: {
7486
7651
  since: "1.28.0",
7487
7652
  baseBitType: BitType.standardArticleNonNormative,
7488
- description: "Smart standard non-normative article bit"
7653
+ description: "Smart standard non-normative article bit",
7654
+ // PLAN-140 Q4 (normativity inheritance): a <p> is non-normative because
7655
+ // of an ANCESTOR element — a foreword/intro sec or an informative annex.
7656
+ // @ancestor is a reverse-match-only constraint (forward emission ignores
7657
+ // it); the rules are alternatives, first-satisfied wins, and the
7658
+ // @ancestor literals give this key higher specificity than the plain
7659
+ // normative-article <p> key wherever the context applies.
7660
+ mappingKeys: {
7661
+ "xml-niso-iec": [
7662
+ {
7663
+ "@el": "p",
7664
+ "@attr": { id: "$customerId" },
7665
+ "@ancestor": { "@attr": { "sec-type": "foreword|intro" } },
7666
+ "@children": "$"
7667
+ },
7668
+ {
7669
+ "@el": "p",
7670
+ "@attr": { id: "$customerId" },
7671
+ "@ancestor": { "@attr": { "content-type": "informative" } },
7672
+ "@children": "$"
7673
+ }
7674
+ ]
7675
+ }
7489
7676
  },
7490
7677
  [BitType.smartStandardArticleNormativeCollapsible]: {
7491
7678
  since: "1.28.0",
@@ -8116,7 +8303,8 @@ var BITS = {
8116
8303
  {
8117
8304
  key: ConfigKey.property_maxCreatedBits,
8118
8305
  description: "The maximum number of bits that can be created from this bit",
8119
- format: TagFormat.number
8306
+ format: TagFormat.number,
8307
+ nullable: true
8120
8308
  }
8121
8309
  ],
8122
8310
  textFormatDefault: TextFormat.plainText
@@ -8246,6 +8434,13 @@ var BITS = {
8246
8434
  since: "1.3.0",
8247
8435
  baseBitType: BitType._standard,
8248
8436
  description: "Book bit, used to represent a book",
8437
+ // PLAN-141 F5 (NISO import): the <std-meta> front-matter container is
8438
+ // the [.book] bit (STRUCTURAL, first in document order): primary
8439
+ // title-wrap → [#], primary content-language → [@language], dated
8440
+ // std-ref → [@externalId] (tag keys with maxEmits caps — the NISO
8441
+ // convention puts the document language first). std-meta has no id, so
8442
+ // no anchor/customerId. Remaining leaves drop as recorded loss (R-4).
8443
+ mappingKeys: { "xml-niso-iec": { "@el": "std-meta" } },
8249
8444
  tags: [
8250
8445
  {
8251
8446
  key: ConfigKey.group_bookCommon,
@@ -8532,12 +8727,14 @@ var BITS = {
8532
8727
  {
8533
8728
  key: ConfigKey.property_focusX,
8534
8729
  description: "The X coordinate for focusing the image in the browser",
8535
- format: TagFormat.number
8730
+ format: TagFormat.number,
8731
+ nullable: true
8536
8732
  },
8537
8733
  {
8538
8734
  key: ConfigKey.property_focusY,
8539
8735
  description: "The Y coordinate for focusing the image in the browser",
8540
- format: TagFormat.number
8736
+ format: TagFormat.number,
8737
+ nullable: true
8541
8738
  }
8542
8739
  ]
8543
8740
  },
@@ -8568,6 +8765,15 @@ var BITS = {
8568
8765
  since: "1.3.0",
8569
8766
  baseBitType: BitType._standard,
8570
8767
  description: "Chapter bit, used to define chapters in books or articles",
8768
+ // PLAN-140 W3 (NISO import, tranche 1): recover a chapter from a NISO
8769
+ // section or annex. STRUCTURAL key (no @children content slot): the
8770
+ // element carries no body of its own — tag children (label/title) map
8771
+ // via tag keys, every mapped descendant promotes to a following sibling
8772
+ // bit, and the chapter level derives from nesting depth (catalog D1).
8773
+ // The source id lands as [@customerId] (two-id model, D2).
8774
+ mappingKeys: {
8775
+ "xml-niso-iec": { "@el": "sec|app", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" }
8776
+ },
8571
8777
  tags: [
8572
8778
  {
8573
8779
  exportJsonKey: { anchor: "$" },
@@ -8579,7 +8785,10 @@ var BITS = {
8579
8785
  key: ConfigKey.tag_title,
8580
8786
  description: "The title of the chapter",
8581
8787
  jsonKey: "title|setMulti(level)",
8582
- exportJsonKey: { title: "$", level: "$level" }
8788
+ exportJsonKey: { title: "$", level: "$level" },
8789
+ // PLAN-140 W3 (NISO import): the section/annex title; the heading
8790
+ // level rides the normalizer-derived nesting depth (plan Q2).
8791
+ mappingKeys: { "xml-niso-iec": { "@el": "title", "@text": "$" } }
8583
8792
  },
8584
8793
  {
8585
8794
  key: ConfigKey.property_toc,
@@ -8785,11 +8994,28 @@ var BITS = {
8785
8994
  since: "1.3.0",
8786
8995
  baseBitType: BitType._standard,
8787
8996
  description: "Code bit, used for code snippets in articles or documents",
8997
+ // PLAN-141 F4 (NISO import): a <code> block is a CONTENT bit with a
8998
+ // literal text body; @language recovers as [@computerLanguage] (absent
8999
+ // attr → no tag). Anchor = the source id (D2 revised).
9000
+ mappingKeys: {
9001
+ "xml-niso-iec": {
9002
+ "@el": "code",
9003
+ "@attr": { id: "$customerId", language: "$computerLanguage" },
9004
+ "\u25BC": "$anchor",
9005
+ "@text": "$"
9006
+ }
9007
+ },
8788
9008
  tags: [
8789
9009
  {
8790
9010
  key: ConfigKey.property_computerLanguage,
8791
9011
  description: "The programming language of the code snippet",
8792
- format: TagFormat.plainText
9012
+ format: TagFormat.plainText,
9013
+ // PLAN-141 F4 (NISO import): the bit key's `language` sigil recovers
9014
+ // a present attr; this `@absent` rule supplies the AUTHORED default
9015
+ // when the source carries none (catalog code row — an untyped block
9016
+ // is `[@computerLanguage:text]`). Fires only on foreign imports,
9017
+ // never the carrier round-trip.
9018
+ mappingKeys: { "xml-niso-iec": [{ "@absent": "text" }] }
8793
9019
  },
8794
9020
  {
8795
9021
  key: ConfigKey.property_codeLineNumbers,
@@ -8828,7 +9054,20 @@ var BITS = {
8828
9054
  [BitType.smartStandardFormulaNormative]: {
8829
9055
  since: "3.11.0",
8830
9056
  baseBitType: BitType.formula,
8831
- description: "Smart standard normative formula bit, used for mathematical formulas in smart standards that are normative"
9057
+ description: "Smart standard normative formula bit, used for mathematical formulas in smart standards that are normative",
9058
+ // PLAN-141 F4 (NISO import): a <disp-formula> is a CONTENT bit whose
9059
+ // latex-format body carries the <mml:math> subtree VERBATIM (G1 — no
9060
+ // MathML↔LaTeX transform; ids stripped); <label> → [%]; a contained
9061
+ // <legend> PROMOTES as a sibling legend bit (cards cannot nest in a
9062
+ // body). Anchor = the source id (D2 revised).
9063
+ mappingKeys: {
9064
+ "xml-niso-iec": {
9065
+ "@el": "disp-formula",
9066
+ "@attr": { id: "$customerId" },
9067
+ "\u25BC": "$anchor",
9068
+ "@children": "$"
9069
+ }
9070
+ }
8832
9071
  },
8833
9072
  [BitType.smartStandardRemarkFormula]: {
8834
9073
  since: "3.11.0",
@@ -9355,7 +9594,8 @@ var BITS = {
9355
9594
  {
9356
9595
  key: ConfigKey.property_reasonableNumOfChars,
9357
9596
  description: "The reasonable number of characters for the essay",
9358
- format: TagFormat.number
9597
+ format: TagFormat.number,
9598
+ nullable: true
9359
9599
  },
9360
9600
  {
9361
9601
  key: ConfigKey.property_sampleSolution,
@@ -9788,7 +10028,14 @@ var BITS = {
9788
10028
  [BitType.smartStandardList]: {
9789
10029
  since: "1.28.0",
9790
10030
  baseBitType: BitType.standardList,
9791
- description: "Smart standard list bit, used to create smart standard lists in articles or books"
10031
+ description: "Smart standard list bit, used to create smart standard lists in articles or books",
10032
+ // PLAN-140 refs family (NISO import): a reference list is a STRUCTURAL
10033
+ // bit (no @children — it takes a path ordinal anchor and its <ref>
10034
+ // children promote to sibling list-item bits); its <title> rides [#]
10035
+ // via the title tag key.
10036
+ mappingKeys: {
10037
+ "xml-niso-iec": { "@el": "ref-list", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" }
10038
+ }
9792
10039
  },
9793
10040
  [BitType.smartStandardListCollapsible]: {
9794
10041
  since: "1.28.0",
@@ -9833,7 +10080,17 @@ var BITS = {
9833
10080
  [BitType.smartStandardNoteNonNormative]: {
9834
10081
  since: "1.28.0",
9835
10082
  baseBitType: BitType.standardNoteNonNormative,
9836
- description: "Smart standard non-normative note bit, used to provide non-normative notes in smart standards"
10083
+ description: "Smart standard non-normative note bit, used to provide non-normative notes in smart standards",
10084
+ // PLAN-140 W3 (NISO import, tranche 1). CONTENT key (@children): a
10085
+ // note's inner paragraphs are note body, never separate bits.
10086
+ mappingKeys: {
10087
+ "xml-niso-iec": {
10088
+ "@el": "non-normative-note",
10089
+ "@attr": { id: "$customerId" },
10090
+ "@children": "$",
10091
+ "\u25BC": "$anchor"
10092
+ }
10093
+ }
9837
10094
  },
9838
10095
  [BitType.smartStandardNoteNormativeCollapsible]: {
9839
10096
  since: "1.28.0",
@@ -9940,7 +10197,19 @@ var BITS = {
9940
10197
  [BitType.smartStandardRemarkNonNormative]: {
9941
10198
  since: "1.28.0",
9942
10199
  baseBitType: BitType.standardRemarkNonNormative,
9943
- description: "Smart standard non-normative remark bit, used to provide non-normative remarks in smart standards"
10200
+ description: "Smart standard non-normative remark bit, used to provide non-normative remarks in smart standards",
10201
+ // PLAN-140 Q4 rider: a warning/caution note routes to the remark bit —
10202
+ // the same element as the note key, distinguished by the content-type
10203
+ // attr literal (alias value: forward emits the first alternative,
10204
+ // reverse accepts any). Most-specific-wins beats the plain note key.
10205
+ mappingKeys: {
10206
+ "xml-niso-iec": {
10207
+ "@el": "non-normative-note",
10208
+ "@attr": { "content-type": "warning|caution", id: "$customerId" },
10209
+ "@children": "$",
10210
+ "\u25BC": "$anchor"
10211
+ }
10212
+ }
9944
10213
  },
9945
10214
  [BitType.smartStandardRemarkNormativeCollapsible]: {
9946
10215
  since: "1.28.0",
@@ -10396,7 +10665,8 @@ var BITS = {
10396
10665
  {
10397
10666
  key: ConfigKey.property_pageNo,
10398
10667
  description: "Page number for the block, used to indicate the page on which the block appears",
10399
- format: TagFormat.number
10668
+ format: TagFormat.number,
10669
+ nullable: true
10400
10670
  },
10401
10671
  {
10402
10672
  key: ConfigKey.property_x,
@@ -10413,17 +10683,20 @@ var BITS = {
10413
10683
  {
10414
10684
  key: ConfigKey.property_width,
10415
10685
  description: "Width of the block, used to define the size of the block on the page",
10416
- format: TagFormat.number
10686
+ format: TagFormat.number,
10687
+ nullable: true
10417
10688
  },
10418
10689
  {
10419
10690
  key: ConfigKey.property_height,
10420
10691
  description: "Height of the block, used to define the size of the block on the page",
10421
- format: TagFormat.number
10692
+ format: TagFormat.number,
10693
+ nullable: true
10422
10694
  },
10423
10695
  {
10424
10696
  key: ConfigKey.property_index,
10425
10697
  description: "Index of the block, used to order blocks within the extractor page",
10426
- format: TagFormat.number
10698
+ format: TagFormat.number,
10699
+ nullable: true
10427
10700
  },
10428
10701
  {
10429
10702
  key: ConfigKey.property_classification,
@@ -10733,7 +11006,20 @@ var BITS = {
10733
11006
  [BitType.smartStandardLegendNormative]: {
10734
11007
  since: "3.12.0",
10735
11008
  baseBitType: BitType.legend,
10736
- description: "Smart standard normative legend bit, used to provide normative smart standard legends in articles or books"
11009
+ description: "Smart standard normative legend bit, used to provide normative smart standard legends in articles or books",
11010
+ // PLAN-141 F2 (NISO import): a free-standing <legend> (a formula's) or
11011
+ // bare <def-list> (a table footer's — no <legend> wrapper there) that is
11012
+ // NOT the claimed card content of its enclosing bit is a legend bit:
11013
+ // STRUCTURAL-shaped, its <title> and <def-item>s build the
11014
+ // definition-list cards via the card set's authored keys. A <legend>
11015
+ // inside a figure/table bit stays that bit's claimed card content and
11016
+ // never reaches this key. Anchor = the source id (D2 revised).
11017
+ mappingKeys: {
11018
+ "xml-niso-iec": [
11019
+ { "@el": "legend", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" },
11020
+ { "@el": "def-list", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" }
11021
+ ]
11022
+ }
10737
11023
  },
10738
11024
  [BitType.smartStandardRemarkLegend]: {
10739
11025
  since: "3.12.0",
@@ -10788,7 +11074,16 @@ var BITS = {
10788
11074
  [BitType.smartStandardDefinitionListNormative]: {
10789
11075
  since: "5.24.0",
10790
11076
  baseBitType: BitType.standardDefinitionListNormative,
10791
- description: "Smart standard normative definition list bit, used to create normative smart standard definition lists in articles or books"
11077
+ description: "Smart standard normative definition list bit, used to create normative smart standard definition lists in articles or books",
11078
+ // PLAN-141 F3 (NISO import): a terms-and-definitions clause. STRUCTURAL-
11079
+ // shaped: <label> → [%], the tbx:termEntry/langSet subtree builds the
11080
+ // definition-list card via the card set's authored keys (all tig terms
11081
+ // stacked on the term side; definition/example/notes/source composed on
11082
+ // the definition side with authored locale affixes), langSet @xml:lang →
11083
+ // [@lang] (nested attr slot on the tag key). Anchor = the source id.
11084
+ mappingKeys: {
11085
+ "xml-niso-iec": { "@el": "term-sec", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" }
11086
+ }
10792
11087
  },
10793
11088
  [BitType.smartStandardDefinitionListNonNormative]: {
10794
11089
  since: "5.24.0",
@@ -10857,12 +11152,14 @@ var BITS = {
10857
11152
  {
10858
11153
  key: ConfigKey.property_focusX,
10859
11154
  description: "X-coordinate for the focus point, used to define the focus area in the image",
10860
- format: TagFormat.number
11155
+ format: TagFormat.number,
11156
+ nullable: true
10861
11157
  },
10862
11158
  {
10863
11159
  key: ConfigKey.property_focusY,
10864
11160
  description: "Y-coordinate for the focus point, used to define the focus area in the image",
10865
- format: TagFormat.number
11161
+ format: TagFormat.number,
11162
+ nullable: true
10866
11163
  }
10867
11164
  ]
10868
11165
  },
@@ -10969,7 +11266,15 @@ var BITS = {
10969
11266
  [BitType.smartStandardImageFigureNormative]: {
10970
11267
  since: "1.28.0",
10971
11268
  baseBitType: BitType.standardImageFigureNormative,
10972
- description: "Smart standard normative image figure bit, used to create smart standard normative image figures in articles or books"
11269
+ description: "Smart standard normative image figure bit, used to create smart standard normative image figures in articles or books",
11270
+ // PLAN-141 F1 (NISO import): a figure is STRUCTURAL-shaped (no
11271
+ // @children — no body): <label> → [%], <caption>/<graphic> ride the
11272
+ // [&image] resource chain, the <legend> def-list becomes the bit's
11273
+ // definition-list cards, and an inner note PROMOTES as a sibling bit.
11274
+ // Anchor = the source id (D2 revised).
11275
+ mappingKeys: {
11276
+ "xml-niso-iec": { "@el": "fig", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" }
11277
+ }
10973
11278
  },
10974
11279
  [BitType.smartStandardImageFigureNonNormative]: {
10975
11280
  since: "1.28.0",
@@ -11697,7 +12002,19 @@ var BITS = {
11697
12002
  [BitType.smartStandardListItem]: {
11698
12003
  since: "1.28.0",
11699
12004
  baseBitType: BitType.standardListItem,
11700
- description: "Smart standard list item bit, used to create smart standard list items in articles or books"
12005
+ description: "Smart standard list item bit, used to create smart standard list items in articles or books",
12006
+ // PLAN-140 refs family (NISO import): one list-item bit per <ref> —
12007
+ // CONTENT key (its citation subtree is the body), counter anchor scoped
12008
+ // to the containing list, <label> → [%], nested <std-id> → [@externalId]
12009
+ // (the externalId tag key's nested content slot).
12010
+ mappingKeys: {
12011
+ "xml-niso-iec": {
12012
+ "@el": "ref",
12013
+ "@attr": { id: "$customerId" },
12014
+ "@children": "$",
12015
+ "\u25BC": "$anchor"
12016
+ }
12017
+ }
11701
12018
  },
11702
12019
  [BitType.smartStandardListItemCollapsible]: {
11703
12020
  since: "1.28.0",
@@ -12804,7 +13121,13 @@ var BITS = {
12804
13121
  // `<caption>` — placed inside the idiomatic `<table>` container as
12805
13122
  // its first child. Outside a table context it falls back to the
12806
13123
  // residual carrier.
12807
- htmlKey: { "@el": "caption", "@children": "$" }
13124
+ htmlKey: { "@el": "caption", "@children": "$" },
13125
+ // PLAN-141 F2 (NISO import): a table-wrap's <caption><title> — the
13126
+ // nested content slot reads the title text (catalog D5: the caption
13127
+ // is a TOP-LEVEL bit property on table bits).
13128
+ mappingKeys: {
13129
+ "xml-niso-iec": { "@el": "caption", "@children": { "@el": "title", "@text": "$" } }
13130
+ }
12808
13131
  },
12809
13132
  {
12810
13133
  key: ConfigKey.property_tableFixedHeader,
@@ -12977,7 +13300,16 @@ var BITS = {
12977
13300
  [BitType.smartStandardTableExtendedNormative]: {
12978
13301
  since: "1.28.0",
12979
13302
  baseBitType: BitType.standardTableExtendedNormative,
12980
- description: "Smart standard normative extended table bit, used to create smart standard normative extended tables in articles or books"
13303
+ description: "Smart standard normative extended table bit, used to create smart standard normative extended tables in articles or books",
13304
+ // PLAN-141 F2 (NISO import): a table-wrap is STRUCTURAL-shaped (no
13305
+ // @children — no body): <label> → [%], <caption><title> → the bit-level
13306
+ // [@caption] (D5), the inner <table>/<table-wrap-foot> build the
13307
+ // table-extended card grid via the card set's authored keys, and a
13308
+ // footer <def-list> PROMOTES as a sibling legend bit. Anchor = the
13309
+ // source id (D2 revised).
13310
+ mappingKeys: {
13311
+ "xml-niso-iec": { "@el": "table-wrap", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" }
13312
+ }
12981
13313
  },
12982
13314
  [BitType.smartStandardTableExtendedNonNormative]: {
12983
13315
  since: "1.28.0",
@@ -13052,7 +13384,8 @@ var BITS = {
13052
13384
  {
13053
13385
  key: ConfigKey.property_maxDisplayLevel,
13054
13386
  description: "Maximum display level for the chapter in the table of contents",
13055
- format: TagFormat.number
13387
+ format: TagFormat.number,
13388
+ nullable: true
13056
13389
  }
13057
13390
  ]
13058
13391
  },
@@ -13064,7 +13397,8 @@ var BITS = {
13064
13397
  {
13065
13398
  key: ConfigKey.property_maxTocChapterLevel,
13066
13399
  description: "Maximum chapter level for the inline table of contents",
13067
- format: TagFormat.number
13400
+ format: TagFormat.number,
13401
+ nullable: true
13068
13402
  }
13069
13403
  ]
13070
13404
  },
@@ -13487,13 +13821,15 @@ var BITS = {
13487
13821
  key: ConfigKey.property_width,
13488
13822
  // Same as image
13489
13823
  description: "Width for the embedded content, used to define the width in pixels",
13490
- format: TagFormat.number
13824
+ format: TagFormat.number,
13825
+ nullable: true
13491
13826
  },
13492
13827
  {
13493
13828
  key: ConfigKey.property_height,
13494
13829
  // Same as image
13495
13830
  description: "Height for the embedded content, used to define the height in pixels",
13496
- format: TagFormat.number
13831
+ format: TagFormat.number,
13832
+ nullable: true
13497
13833
  },
13498
13834
  {
13499
13835
  key: ConfigKey.property_vendorUrl,
@@ -13531,7 +13867,8 @@ var BITS = {
13531
13867
  {
13532
13868
  key: ConfigKey.property_jupyterExecutionCount,
13533
13869
  description: "Execution count for the Jupyter output, used to define the execution order",
13534
- format: TagFormat.number
13870
+ format: TagFormat.number,
13871
+ nullable: true
13535
13872
  }
13536
13873
  ],
13537
13874
  textFormatDefault: TextFormat.plainText
@@ -14081,7 +14418,7 @@ var instance2 = new Config();
14081
14418
  // src/generated/package_info.ts
14082
14419
  var PACKAGE_INFO = {
14083
14420
  "name": "@gmb/bitmark-parser-generator",
14084
- "version": "5.35.0",
14421
+ "version": "5.36.0",
14085
14422
  "author": "Get More Brain Ltd",
14086
14423
  "license": "ISC",
14087
14424
  "description": "A bitmark parser and generator using Peggy.js"
@@ -14610,6 +14947,8 @@ var NodeType = {
14610
14947
  definition: "definition",
14611
14948
  definitions: "definitions",
14612
14949
  definitionsValue: "definitionsValue",
14950
+ displayName: "displayName",
14951
+ displayNameValue: "displayNameValue",
14613
14952
  diffContext: "diffContext",
14614
14953
  diffContextValue: "diffContextValue",
14615
14954
  diffOp: "diffOp",
@@ -28532,6 +28871,12 @@ var Builder = class extends BaseBuilder {
28532
28871
  data.publisherName,
28533
28872
  options
28534
28873
  ),
28874
+ displayName: this.toAstProperty(
28875
+ bitType,
28876
+ ConfigKey.property_displayName,
28877
+ data.displayName,
28878
+ options
28879
+ ),
28535
28880
  theme: this.toAstProperty(bitType, ConfigKey.property_theme, data.theme, options),
28536
28881
  rtl: this.toAstProperty(bitType, ConfigKey.property_rtl, data.rtl, options),
28537
28882
  computerLanguage: this.toAstProperty(
@@ -44694,7 +45039,7 @@ var ConfigBuilder = class {
44694
45039
  }
44695
45040
  const outputFolder = opts.outputDir ?? "assets/config";
44696
45041
  const outputFolderBits = import_node_path3.default.join(outputFolder, "bits");
44697
- const outputFolderGroups = import_node_path3.default.join(outputFolder, "partials");
45042
+ const outputFolderGroups = import_node_path3.default.join(outputFolder, "groups");
44698
45043
  const outputFolderCards = import_node_path3.default.join(outputFolder, "cards");
44699
45044
  import_fs_extra3.default.ensureDirSync(outputFolderBits);
44700
45045
  import_fs_extra3.default.ensureDirSync(outputFolderGroups);
@@ -44729,6 +45074,15 @@ var ConfigBuilder = class {
44729
45074
  }
44730
45075
  return [{ key: groupKey }];
44731
45076
  };
45077
+ const mappingKeysField = (base, json, html) => {
45078
+ const record = {};
45079
+ for (const [id, value] of Object.entries(base ?? {})) {
45080
+ if (value != null) record[id] = value;
45081
+ }
45082
+ if (json.present && json.value != null) record.json = json.value;
45083
+ if (html.present && html.value != null) record.html = html.value;
45084
+ return Object.keys(record).length > 0 ? { mappingKeys: record } : {};
45085
+ };
44732
45086
  const bareKeyForProperty = (key) => {
44733
45087
  if (key.startsWith("@") || key.startsWith("&")) return key.substring(1);
44734
45088
  return void 0;
@@ -44749,22 +45103,20 @@ var ConfigBuilder = class {
44749
45103
  if (tag.format === TagFormat.plainText) {
44750
45104
  format = "string";
44751
45105
  } else if (tag.format === TagFormat.boolean) {
44752
- format = "bool";
44753
- } else if (tag.format === TagFormat.bitmarkText) {
44754
- format = "bitmark";
45106
+ format = "boolean";
44755
45107
  } else if (tag.format === TagFormat.number) {
44756
45108
  format = "number";
44757
45109
  } else {
44758
- format = "bitmark--";
45110
+ format = "bitmark+";
44759
45111
  }
44760
45112
  } else if (tagType === BitTagConfigKeyType.property) {
44761
45113
  tagName = tag.key;
44762
45114
  if (tag.format === TagFormat.plainText) {
44763
45115
  format = "string";
44764
45116
  } else if (tag.format === TagFormat.boolean) {
44765
- format = "bool";
45117
+ format = "boolean";
44766
45118
  } else if (tag.format === TagFormat.bitmarkText) {
44767
- format = "bitmark";
45119
+ format = "bitmark+";
44768
45120
  } else if (tag.format === TagFormat.number) {
44769
45121
  format = "number";
44770
45122
  } else if (tag.format === TagFormat.coordinates) {
@@ -44796,8 +45148,11 @@ var ConfigBuilder = class {
44796
45148
  tags2.push({
44797
45149
  type: "group",
44798
45150
  key: ref.key,
44799
- ...hasExportChosen ? { jsonKey: exportJsonKeyChosen } : {},
44800
- ...hasHtmlChosen ? { htmlKey: htmlKeyChosen } : {},
45151
+ ...mappingKeysField(
45152
+ { ...ref.extraKeys ?? {}, ...tag.mappingKeys ?? {} },
45153
+ { value: exportJsonKeyChosen, present: hasExportChosen },
45154
+ { value: htmlKeyChosen, present: hasHtmlChosen }
45155
+ ),
44801
45156
  ...min != null ? { min } : {},
44802
45157
  ...max != null ? { max } : {},
44803
45158
  ...description ? { description } : {}
@@ -44814,53 +45169,48 @@ var ConfigBuilder = class {
44814
45169
  chain = chainTags;
44815
45170
  }
44816
45171
  const hasExport = Object.prototype.hasOwnProperty.call(tag, "exportJsonKey");
44817
- let exportJsonKeyField;
44818
- if (hasExport) {
44819
- exportJsonKeyField = { jsonKey: tag.exportJsonKey };
44820
- } else if (legacyIsTrivialDefault(tag)) {
44821
- exportJsonKeyField = {};
44822
- } else {
45172
+ if (!hasExport && !legacyIsTrivialDefault(tag)) {
44823
45173
  const headingPath = [...pathStack, `tags.${tag.key}`].join(" / ");
44824
45174
  exportJsonKeyErrors.push(
44825
45175
  `Missing exportJsonKey for non-default jsonKey at ${headingPath}` + (tag.jsonKey ? ` (legacy: \`${tag.jsonKey}\`)` : ` (symbol-mapped tag \`${tag.key}\`)`)
44826
45176
  );
44827
- exportJsonKeyField = {};
44828
45177
  }
44829
45178
  const hasHtml = Object.prototype.hasOwnProperty.call(tag, "htmlKey");
44830
- const htmlKeyField = hasHtml ? { htmlKey: tag.htmlKey } : {};
44831
45179
  const t = {
44832
45180
  type: "tag",
44833
45181
  key: tagName,
44834
- ...exportJsonKeyField,
44835
- ...htmlKeyField,
44836
- format,
44837
- default: tag.defaultValue ?? null,
45182
+ ...mappingKeysField(
45183
+ tag.mappingKeys,
45184
+ { value: tag.exportJsonKey, present: hasExport },
45185
+ { value: tag.htmlKey, present: hasHtml }
45186
+ ),
45187
+ ...format && format !== "string" ? { format } : {},
45188
+ ...tag.defaultValue != null ? { default: tag.defaultValue } : {},
44838
45189
  ...tag.nullable ? { nullable: true } : {},
44839
- min: tag.minCount == null ? 0 : tag.minCount,
44840
- max: tag.maxCount == null ? 1 : tag.maxCount,
45190
+ ...tag.minCount != null && tag.minCount !== 0 ? { min: tag.minCount } : {},
45191
+ ...tag.maxCount != null && tag.maxCount !== 1 ? { max: tag.maxCount } : {},
44841
45192
  description: tag.description ?? "",
44842
45193
  tags: chain
44843
45194
  };
44844
45195
  tags2.push(t);
44845
45196
  return tags2;
44846
45197
  };
44847
- const cardJsonKeyField = (container, legacyKey, exportKey, pathHeading) => {
45198
+ const cardMappingKeysField = (container, base, legacyKey, exportKey, htmlExportKey, pathHeading) => {
44848
45199
  const c = container;
44849
45200
  const hasExport = Object.prototype.hasOwnProperty.call(c, exportKey);
44850
- if (hasExport) return { jsonKey: c[exportKey] };
44851
- const legacy = c[legacyKey];
44852
- if (legacy == null) return {};
44853
- exportJsonKeyErrors.push(
44854
- `Missing exportJsonKey for non-default jsonKey at ${pathHeading} (legacy: \`${legacy}\`)`
44855
- );
44856
- return {};
44857
- };
44858
- const cardHtmlKeyField = (container, htmlExportKey) => {
44859
- const c = container;
44860
- if (Object.prototype.hasOwnProperty.call(c, htmlExportKey)) {
44861
- return { htmlKey: c[htmlExportKey] };
45201
+ if (!hasExport && c[legacyKey] != null) {
45202
+ exportJsonKeyErrors.push(
45203
+ `Missing exportJsonKey for non-default jsonKey at ${pathHeading} (legacy: \`${c[legacyKey]}\`)`
45204
+ );
44862
45205
  }
44863
- return {};
45206
+ return mappingKeysField(
45207
+ base,
45208
+ { value: c[exportKey], present: hasExport },
45209
+ {
45210
+ value: c[htmlExportKey],
45211
+ present: Object.prototype.hasOwnProperty.call(c, htmlExportKey)
45212
+ }
45213
+ );
44864
45214
  };
44865
45215
  const serializeCardSet = (cardSetKey) => {
44866
45216
  const variantBodyFormat = (variant) => variant.format ?? TextFormat.bitmarkText;
@@ -44891,11 +45241,17 @@ var ConfigBuilder = class {
44891
45241
  variantTags.push(...processTagEntries(variantTag, [variantPath]));
44892
45242
  }
44893
45243
  return {
44894
- ...cardJsonKeyField(variant, "jsonKey", "exportJsonKey", variantPath),
44895
- ...cardHtmlKeyField(variant, "htmlKey"),
44896
- format: variantBodyFormat(variant),
45244
+ ...cardMappingKeysField(
45245
+ variant,
45246
+ void 0,
45247
+ "jsonKey",
45248
+ "exportJsonKey",
45249
+ "htmlKey",
45250
+ variantPath
45251
+ ),
45252
+ bodyFormat: variantBodyFormat(variant),
44897
45253
  tags: variantTags,
44898
- repeatCount: variant.repeatCount ?? 1,
45254
+ ...variant.repeatCount != null && variant.repeatCount !== 1 ? { repeatCount: variant.repeatCount } : {},
44899
45255
  ...variant.bodyRequired ? { bodyRequired: true } : {},
44900
45256
  ...variant.bodyAllowed === false ? { bodyForbidden: true } : {}
44901
45257
  };
@@ -44903,8 +45259,14 @@ var ConfigBuilder = class {
44903
45259
  return {
44904
45260
  name: side.name,
44905
45261
  ...side.repeat ? { repeat: side.repeat } : {},
44906
- ...cardJsonKeyField(side, "jsonKey", "exportJsonKey", sidePath),
44907
- ...cardHtmlKeyField(side, "htmlKey"),
45262
+ ...cardMappingKeysField(
45263
+ side,
45264
+ side.mappingKeys,
45265
+ "jsonKey",
45266
+ "exportJsonKey",
45267
+ "htmlKey",
45268
+ sidePath
45269
+ ),
44908
45270
  variants
44909
45271
  };
44910
45272
  });
@@ -44915,20 +45277,40 @@ var ConfigBuilder = class {
44915
45277
  const hasSideHtml = Object.prototype.hasOwnProperty.call(section, "sideHtmlKey");
44916
45278
  let cardSides = sides;
44917
45279
  if (hasSideExport || section.sideJsonKey != null || hasSideHtml) {
44918
- const sideOverride = cardJsonKeyField(
44919
- section,
44920
- "sideJsonKey",
44921
- "sideExportJsonKey",
44922
- `${sectionPath} / sides.*`
44923
- );
44924
- const sideHtmlOverride = cardHtmlKeyField(section, "sideHtmlKey");
44925
- cardSides = sides.map((s) => ({ ...s, ...sideOverride, ...sideHtmlOverride }));
45280
+ if (!hasSideExport && section.sideJsonKey != null) {
45281
+ exportJsonKeyErrors.push(
45282
+ `Missing exportJsonKey for non-default jsonKey at ${sectionPath} / sides.* (legacy: \`${section.sideJsonKey}\`)`
45283
+ );
45284
+ }
45285
+ cardSides = sides.map((s) => {
45286
+ const record = { ...s.mappingKeys ?? {} };
45287
+ if (hasSideExport) {
45288
+ if (section.sideExportJsonKey != null) record.json = section.sideExportJsonKey;
45289
+ else delete record.json;
45290
+ }
45291
+ if (hasSideHtml) {
45292
+ if (section.sideHtmlKey != null) record.html = section.sideHtmlKey;
45293
+ else delete record.html;
45294
+ }
45295
+ return {
45296
+ name: s.name,
45297
+ ...s.repeat ? { repeat: s.repeat } : {},
45298
+ ...Object.keys(record).length > 0 ? { mappingKeys: record } : {},
45299
+ variants: s.variants
45300
+ };
45301
+ });
44926
45302
  }
44927
45303
  return {
44928
45304
  name: sectionName,
44929
45305
  ...section.isDefault ? { isDefault: true } : {},
44930
- ...cardJsonKeyField(section, "jsonKey", "exportJsonKey", sectionPath),
44931
- ...cardHtmlKeyField(section, "htmlKey"),
45306
+ ...cardMappingKeysField(
45307
+ section,
45308
+ section.mappingKeys,
45309
+ "jsonKey",
45310
+ "exportJsonKey",
45311
+ "htmlKey",
45312
+ sectionPath
45313
+ ),
44932
45314
  // PLAN-085: per-section cardinality. Emit only when non-default
44933
45315
  // (treat `0` / undefined as "unbounded — omit").
44934
45316
  ...section.minCount != null && section.minCount !== 0 ? { min: section.minCount } : {},
@@ -44936,7 +45318,18 @@ var ConfigBuilder = class {
44936
45318
  sides: cardSides
44937
45319
  };
44938
45320
  });
44939
- return { name: normalizedKey, ...cardHtmlKeyField(cardSetConfig, "htmlKey"), cards };
45321
+ return {
45322
+ name: normalizedKey,
45323
+ ...mappingKeysField(
45324
+ void 0,
45325
+ { present: false },
45326
+ {
45327
+ value: cardSetConfig.htmlKey,
45328
+ present: Object.prototype.hasOwnProperty.call(cardSetConfig, "htmlKey")
45329
+ }
45330
+ ),
45331
+ cards
45332
+ };
44940
45333
  }
44941
45334
  const cardSetPath = `cardSets.${normalizedKey} / cards.default`;
44942
45335
  return {
@@ -44945,8 +45338,14 @@ var ConfigBuilder = class {
44945
45338
  {
44946
45339
  name: "default",
44947
45340
  isDefault: true,
44948
- ...cardJsonKeyField(cardSetConfig, "jsonKey", "exportJsonKey", cardSetPath),
44949
- ...cardHtmlKeyField(cardSetConfig, "htmlKey"),
45341
+ ...cardMappingKeysField(
45342
+ cardSetConfig,
45343
+ cardSetConfig.mappingKeys,
45344
+ "jsonKey",
45345
+ "exportJsonKey",
45346
+ "htmlKey",
45347
+ cardSetPath
45348
+ ),
44950
45349
  sides
44951
45350
  }
44952
45351
  ]
@@ -44982,14 +45381,19 @@ var ConfigBuilder = class {
44982
45381
  ...t.max != null ? { max: t.max } : {},
44983
45382
  ...t.description ? { description: t.description } : {}
44984
45383
  };
44985
- if (Object.prototype.hasOwnProperty.call(t, "jsonKey")) {
44986
- ref.exportJsonKey = t.jsonKey;
45384
+ const record = t.mappingKeys ?? {};
45385
+ if (Object.prototype.hasOwnProperty.call(record, "json")) {
45386
+ ref.exportJsonKey = record.json;
44987
45387
  ref.hasExportJsonKey = true;
44988
45388
  }
44989
- if (Object.prototype.hasOwnProperty.call(t, "htmlKey")) {
44990
- ref.htmlKey = t.htmlKey;
45389
+ if (Object.prototype.hasOwnProperty.call(record, "html")) {
45390
+ ref.htmlKey = record.html;
44991
45391
  ref.hasHtmlKey = true;
44992
45392
  }
45393
+ const extras = Object.fromEntries(
45394
+ Object.entries(record).filter(([id]) => id !== "json" && id !== "html")
45395
+ );
45396
+ if (Object.keys(extras).length > 0) ref.extraKeys = extras;
44993
45397
  replacements.push(ref);
44994
45398
  }
44995
45399
  squashedGroups.set(groupKey, replacements);
@@ -45023,8 +45427,11 @@ var ConfigBuilder = class {
45023
45427
  pushOrReplace({
45024
45428
  type: "group",
45025
45429
  key: ref.key,
45026
- ...ref.hasExportJsonKey ? { jsonKey: ref.exportJsonKey } : {},
45027
- ...ref.hasHtmlKey ? { htmlKey: ref.htmlKey } : {},
45430
+ ...mappingKeysField(
45431
+ ref.extraKeys,
45432
+ { value: ref.exportJsonKey, present: !!ref.hasExportJsonKey },
45433
+ { value: ref.htmlKey, present: !!ref.hasHtmlKey }
45434
+ ),
45028
45435
  ...ref.min != null ? { min: ref.min } : {},
45029
45436
  ...ref.max != null ? { max: ref.max } : {},
45030
45437
  ...ref.description ? { description: ref.description } : {}
@@ -45037,8 +45444,11 @@ var ConfigBuilder = class {
45037
45444
  pushOrReplace({
45038
45445
  type: "group",
45039
45446
  key: ref.key,
45040
- ...ref.hasExportJsonKey ? { jsonKey: ref.exportJsonKey } : {},
45041
- ...ref.hasHtmlKey ? { htmlKey: ref.htmlKey } : {},
45447
+ ...mappingKeysField(
45448
+ ref.extraKeys,
45449
+ { value: ref.exportJsonKey, present: !!ref.hasExportJsonKey },
45450
+ { value: ref.htmlKey, present: !!ref.hasHtmlKey }
45451
+ ),
45042
45452
  ...ref.min != null ? { min: ref.min } : {},
45043
45453
  ...ref.max != null ? { max: ref.max } : {},
45044
45454
  ...ref.description ? { description: ref.description } : {}
@@ -45065,20 +45475,17 @@ var ConfigBuilder = class {
45065
45475
  description: b.description ?? "",
45066
45476
  since: resolvedBitConfig.since,
45067
45477
  deprecated: resolvedBitConfig.deprecated,
45068
- history: [
45069
- {
45070
- version: resolvedBitConfig.since,
45071
- changes: ["Initial version"]
45072
- }
45073
- ],
45074
- format: resolvedBitConfig.textFormatDefault ?? "bitmark--",
45075
- bodyRequired: resolvedBitConfig.bodyRequired ?? false,
45076
- bodyForbidden: !(resolvedBitConfig.bodyAllowed ?? true),
45077
- footerRequired: resolvedBitConfig.footerRequired ?? false,
45078
- footerForbidden: !(resolvedBitConfig.footerAllowed ?? true),
45079
- resourceAttachmentAllowed: resolvedBitConfig.resourceAttachmentAllowed ?? true,
45478
+ bodyFormat: resolvedBitConfig.textFormatDefault ?? "bitmark--",
45479
+ ...resolvedBitConfig.bodyRequired ? { bodyRequired: true } : {},
45480
+ ...resolvedBitConfig.bodyAllowed === false ? { bodyForbidden: true } : {},
45481
+ ...resolvedBitConfig.footerRequired ? { footerRequired: true } : {},
45482
+ ...resolvedBitConfig.footerAllowed === false ? { footerForbidden: true } : {},
45483
+ ...resolvedBitConfig.resourceAttachmentAllowed === false ? { resourceAttachmentAllowed: false } : {},
45484
+ // Bit-level named key mappings (element keys for markup import),
45485
+ // carried verbatim from the raw config.
45486
+ ...mappingKeysField(b.mappingKeys, { present: false }, { present: false }),
45080
45487
  tags: tags2,
45081
- ...cardRef ? { card: cardRef } : {}
45488
+ ...cardRef ? { cardSet: cardRef } : {}
45082
45489
  };
45083
45490
  const output = import_node_path3.default.join(outputFolderBits, `${b.bitType}.jsonc`);
45084
45491
  const str = JSON.stringify(bitJson, null, 2);
@@ -45106,14 +45513,7 @@ var ConfigBuilder = class {
45106
45513
  const bitJson = {
45107
45514
  name: groupKey,
45108
45515
  description: g.description ?? "",
45109
- since: "UNKNOWN",
45110
45516
  deprecated: g.deprecated,
45111
- history: [
45112
- {
45113
- version: "UNKNOWN",
45114
- changes: ["Initial version"]
45115
- }
45116
- ],
45117
45517
  tags: tags2
45118
45518
  // cards: [
45119
45519
  // {
@@ -45167,8 +45567,11 @@ var ConfigBuilder = class {
45167
45567
  tags2.push({
45168
45568
  type: "group",
45169
45569
  key: ref.key,
45170
- ...ref.hasExportJsonKey ? { jsonKey: ref.exportJsonKey } : {},
45171
- ...ref.hasHtmlKey ? { htmlKey: ref.htmlKey } : {},
45570
+ ...mappingKeysField(
45571
+ ref.extraKeys,
45572
+ { value: ref.exportJsonKey, present: !!ref.hasExportJsonKey },
45573
+ { value: ref.htmlKey, present: !!ref.hasHtmlKey }
45574
+ ),
45172
45575
  ...ref.min != null ? { min: ref.min } : {},
45173
45576
  ...ref.max != null ? { max: ref.max } : {},
45174
45577
  ...ref.description ? { description: ref.description } : {}
@@ -45189,14 +45592,7 @@ var ConfigBuilder = class {
45189
45592
  const bitJson = {
45190
45593
  name: groupKey,
45191
45594
  description: b.description ?? "",
45192
- since: "UNKNOWN",
45193
45595
  deprecated: b.deprecated,
45194
- history: [
45195
- {
45196
- version: "UNKNOWN",
45197
- changes: ["Initial version"]
45198
- }
45199
- ],
45200
45596
  tags: tags2
45201
45597
  };
45202
45598
  const output = import_node_path3.default.join(outputFolderGroups, `${groupKey}.jsonc`);
@@ -45237,7 +45633,7 @@ var ConfigBuilder = class {
45237
45633
  validateConfigTree(outputFolder) {
45238
45634
  const errors = [];
45239
45635
  const outputFolderBits = import_node_path3.default.join(outputFolder, "bits");
45240
- const outputFolderGroups = import_node_path3.default.join(outputFolder, "partials");
45636
+ const outputFolderGroups = import_node_path3.default.join(outputFolder, "groups");
45241
45637
  const outputFolderCards = import_node_path3.default.join(outputFolder, "cards");
45242
45638
  const availableGroups = /* @__PURE__ */ new Set();
45243
45639
  if (import_fs_extra3.default.existsSync(outputFolderGroups)) {
@@ -45311,7 +45707,7 @@ var ConfigBuilder = class {
45311
45707
  const content = import_fs_extra3.default.readFileSync(filePath, "utf-8");
45312
45708
  const config = JSON.parse(content);
45313
45709
  if (config.tags && Array.isArray(config.tags)) {
45314
- checkTags(config.tags, `partials/${file}`, 15);
45710
+ checkTags(config.tags, `groups/${file}`, 15);
45315
45711
  }
45316
45712
  } catch (err) {
45317
45713
  errors.push(`Failed to parse ${file}: ${err}`);