@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.js CHANGED
@@ -1351,6 +1351,7 @@ var propertyKeys = {
1351
1351
  property_dateEnd: "@dateEnd",
1352
1352
  property_decimalPlaces: "@decimalPlaces",
1353
1353
  property_deeplink: "@deeplink",
1354
+ property_displayName: "@displayName",
1354
1355
  property_diffContext: "@diffContext",
1355
1356
  property_diffOp: "@diffOp",
1356
1357
  property_diffRef: "@diffRef",
@@ -2500,9 +2501,30 @@ var CARDSETS = {
2500
2501
  [CardSetConfigKey.definitionList]: {
2501
2502
  jsonKey: "definitions",
2502
2503
  exportJsonKey: { definitions: "$" },
2504
+ // PLAN-141 F1 (NISO import): each <def-item> is one card; the sides
2505
+ // read <term>/<def>; the enclosing <legend>'s <title> lands on the
2506
+ // term side's heading tag ([#Key] header card).
2507
+ // PLAN-141 F3: a <tbx:langSet> is ALSO one card (a terminology entry —
2508
+ // its enclosing <tbx:termEntry> is a transparent claim wrapper); its
2509
+ // children compose the term/definition sides via the side rules below.
2510
+ mappingKeys: {
2511
+ "xml-niso-iec": [
2512
+ { "@el": "def-item", "@children": "$" },
2513
+ { "@el": "tbx:langSet", "@children": "$" }
2514
+ ]
2515
+ },
2503
2516
  sides: [
2504
2517
  {
2505
2518
  name: "term",
2519
+ // F3: every <tbx:tig>'s <tbx:term> STACKS one-per-line onto the
2520
+ // single term side (the "$\n" template's authored separator; the
2521
+ // trailing one is trimmed) — the PDF's Maus/Mus/M/Müsli stack.
2522
+ mappingKeys: {
2523
+ "xml-niso-iec": [
2524
+ { "@el": "term", "@text": "$" },
2525
+ { "@el": "tbx:tig", "@children": { "@el": "tbx:term", "@text": "$\n" } }
2526
+ ]
2527
+ },
2506
2528
  variants: [
2507
2529
  {
2508
2530
  jsonKey: "term.text",
@@ -2518,7 +2540,8 @@ var CARDSETS = {
2518
2540
  format: TagFormat.plainText,
2519
2541
  nullable: true,
2520
2542
  jsonKey: "^heading.forKeys",
2521
- exportJsonKey: { "@bit": { heading: { forKeys: "$" } } }
2543
+ exportJsonKey: { "@bit": { heading: { forKeys: "$" } } },
2544
+ mappingKeys: { "xml-niso-iec": { "@el": "title", "@text": "$" } }
2522
2545
  },
2523
2546
  {
2524
2547
  key: ConfigKey.group_resourceIcon,
@@ -2544,6 +2567,24 @@ var CARDSETS = {
2544
2567
  },
2545
2568
  {
2546
2569
  name: "definition",
2570
+ // F3: the definition side COMPOSES from the entry's fields in
2571
+ // document order, with the ES-rendered locale affixes AUTHORED as
2572
+ // value-position templates (never engine text): the subject field
2573
+ // in ⟨⟩ glues (trailing space) onto the definition line; example /
2574
+ // notes / source follow as blank-line-separated lines. The TBX
2575
+ // grammar flags (normativeAuthorization, termType, partOfSpeech,
2576
+ // grammaticalNumber) are unrendered machine-only data — dropped and
2577
+ // recorded (catalog R-2, reviewed under D7 max-preservation).
2578
+ mappingKeys: {
2579
+ "xml-niso-iec": [
2580
+ { "@el": "def", "@children": "$" },
2581
+ { "@el": "tbx:subjectField", "@text": "\u27E8$\u27E9 " },
2582
+ { "@el": "tbx:definition", "@children": "$" },
2583
+ { "@el": "tbx:example", "@children": "BEISPIEL $" },
2584
+ { "@el": "tbx:note", "@children": "Anmerkung zu Begriff: $" },
2585
+ { "@el": "tbx:source", "@text": "[QUELLE: $]" }
2586
+ ]
2587
+ },
2547
2588
  variants: [
2548
2589
  {
2549
2590
  jsonKey: "definition.text",
@@ -3934,6 +3975,11 @@ var CARDSETS = {
3934
3975
  { table: { header: { rows: ["$"] } } }
3935
3976
  ],
3936
3977
  htmlKey: { "@el": "thead", "@children": { "@el": "tr", "@children": "$" } },
3978
+ // PLAN-141 F2 (NISO import): the XHTML table model NISO uses — a
3979
+ // <thead> SECTION whose <tr> rows are the header cards.
3980
+ mappingKeys: {
3981
+ "xml-niso-iec": { "@el": "thead", "@children": { "@el": "tr", "@children": "$" } }
3982
+ },
3937
3983
  sideJsonKey: "cells[{s}]|set(title=true)",
3938
3984
  sideExportJsonKey: { cells: { $s: { title: true, $: "$" } } },
3939
3985
  // Header cells are `<th>`. The `width` attribute is contributed by the
@@ -3945,12 +3991,21 @@ var CARDSETS = {
3945
3991
  jsonKey: "table.body.rows",
3946
3992
  exportJsonKey: { table: { body: { rows: "$" } } },
3947
3993
  htmlKey: { "@el": "tbody", "@children": { "@el": "tr", "@children": "$" } },
3994
+ // PLAN-141 F2 (NISO import): <tbody> rows are the (default) data cards.
3995
+ mappingKeys: {
3996
+ "xml-niso-iec": { "@el": "tbody", "@children": { "@el": "tr", "@children": "$" } }
3997
+ },
3948
3998
  isDefault: true
3949
3999
  },
3950
4000
  "table-footer": {
3951
4001
  jsonKey: "table.footer.rows",
3952
4002
  exportJsonKey: { table: { footer: { rows: "$" } } },
3953
4003
  htmlKey: { "@el": "tfoot", "@children": { "@el": "tr", "@children": "$" } },
4004
+ // PLAN-141 F2 (NISO import): NISO has no <tfoot> — the footer card's
4005
+ // source is the <table-wrap-foot> block (CONTENT-shaped: no rows; its
4006
+ // prose/notes flatten into one footer card — catalog table-wrap-foot
4007
+ // row; a contained <def-list> promotes as a sibling legend bit).
4008
+ mappingKeys: { "xml-niso-iec": { "@el": "table-wrap-foot", "@children": "$" } },
3954
4009
  sideJsonKey: "cells[{s}]|set(title=true)",
3955
4010
  sideExportJsonKey: { cells: { $s: { title: true, $: "$" } } },
3956
4011
  // Footer cells render as `<th>` (mirrors the header `title: true`
@@ -3967,6 +4022,8 @@ var CARDSETS = {
3967
4022
  // Default (body) cell → `<td>`; the header/footer sections override the
3968
4023
  // side to `<th>` via their sideHtmlKey (HTML.md §8).
3969
4024
  htmlKey: { "@el": "td", "@children": "$" },
4025
+ // PLAN-141 F2 (NISO import): a <td> row child is a body cell side.
4026
+ mappingKeys: { "xml-niso-iec": { "@el": "td", "@children": "$" } },
3970
4027
  variants: [
3971
4028
  {
3972
4029
  jsonKey: "content",
@@ -3984,7 +4041,11 @@ var CARDSETS = {
3984
4041
  // cell with `title: true` automatically.
3985
4042
  jsonKey: "content",
3986
4043
  exportJsonKey: { content: "$" },
3987
- description: "Title text of the table cell (header rows)."
4044
+ description: "Title text of the table cell (header rows).",
4045
+ // PLAN-141 F2 (NISO import): a <th> row child is a TITLE cell —
4046
+ // its content lands in the `[#…]` tag (an empty <th> keeps its
4047
+ // slot as a bare `[#]`).
4048
+ mappingKeys: { "xml-niso-iec": { "@el": "th", "@children": "$" } }
3988
4049
  },
3989
4050
  {
3990
4051
  key: ConfigKey.property_tableCellType,
@@ -4003,6 +4064,9 @@ var CARDSETS = {
4003
4064
  // HTML-C2: contribute a `rowspan` attribute onto the enclosing
4004
4065
  // cell element (`<th>`/`<td>`).
4005
4066
  htmlKey: { "@el": { "@attr": { rowspan: "$" } } },
4067
+ // PLAN-141 F2 (NISO import): recover the source cell's own
4068
+ // `rowspan` attribute (element-scope attr form).
4069
+ mappingKeys: { "xml-niso-iec": { "@el": { "@attr": { rowspan: "$" } } } },
4006
4070
  description: "Number of rows the cell spans.",
4007
4071
  format: TagFormat.number
4008
4072
  },
@@ -4013,6 +4077,9 @@ var CARDSETS = {
4013
4077
  // HTML-C2: contribute a `colspan` attribute onto the enclosing
4014
4078
  // cell element (`<th>`/`<td>`).
4015
4079
  htmlKey: { "@el": { "@attr": { colspan: "$" } } },
4080
+ // PLAN-141 F2 (NISO import): recover the source cell's own
4081
+ // `colspan` attribute (element-scope attr form).
4082
+ mappingKeys: { "xml-niso-iec": { "@el": { "@attr": { colspan: "$" } } } },
4016
4083
  description: "Number of columns the cell spans.",
4017
4084
  format: TagFormat.number
4018
4085
  },
@@ -4034,6 +4101,13 @@ var CARDSETS = {
4034
4101
  // HTML.md §8 + HTML-C2: contribute a `width` attribute onto the
4035
4102
  // enclosing cell element (`<th>`/`<td>`).
4036
4103
  htmlKey: { "@el": { "@attr": { width: "$" } } },
4104
+ // PLAN-141 F2 (NISO import): NISO carries widths as PER-COLUMN
4105
+ // <col width="…"> descriptors at container level, not on cells.
4106
+ // The matched values distribute positionally onto the first
4107
+ // (header) card's sides. Number format ⇒ only an integral
4108
+ // percentage survives ("20.0000%" → 20); a fractional one
4109
+ // (renderer-equalised "4.7619%") is dropped — catalog `col` row.
4110
+ mappingKeys: { "xml-niso-iec": { "@el": "col", "@attr": { width: "$" } } },
4037
4111
  description: "Width for the column.",
4038
4112
  format: TagFormat.number
4039
4113
  }
@@ -4407,7 +4481,22 @@ var GROUPS = {
4407
4481
  key: ConfigKey.property_externalId,
4408
4482
  description: "The external identifier for the bit",
4409
4483
  format: TagFormat.plainText,
4410
- maxCount: Count.infinity
4484
+ maxCount: Count.infinity,
4485
+ // PLAN-140 refs family (NISO import): the machine identifier of a
4486
+ // referenced standard. NESTED content slot — the tag reads (and
4487
+ // consumes) ONLY the <std-id> inside a <std> child; the remaining
4488
+ // designation/title text stays bit body. A <std> without <std-id>
4489
+ // does not fire the key.
4490
+ // PLAN-141 F5: on the [.book] bit, the DATED <std-ref> designation
4491
+ // is the document's external id. Safe rule order: <std-ref> is a
4492
+ // direct bit-element child only under <std-meta> (everywhere else
4493
+ // it nests inside <std>, which the tag spine never reaches).
4494
+ mappingKeys: {
4495
+ "xml-niso-iec": [
4496
+ { "@el": "std", "@children": { "@el": "std-id", "@text": "$" } },
4497
+ { "@el": "std-ref", "@attr": { type: "dated" }, "@text": "$" }
4498
+ ]
4499
+ }
4411
4500
  },
4412
4501
  {
4413
4502
  key: ConfigKey.property_sourceRL,
@@ -4548,7 +4637,8 @@ var GROUPS = {
4548
4637
  {
4549
4638
  key: ConfigKey.property_diffTime,
4550
4639
  description: "The diff time for the bit",
4551
- format: TagFormat.number
4640
+ format: TagFormat.number,
4641
+ nullable: true
4552
4642
  },
4553
4643
  {
4554
4644
  key: ConfigKey.property_ageRange,
@@ -4559,7 +4649,18 @@ var GROUPS = {
4559
4649
  {
4560
4650
  key: ConfigKey.property_lang,
4561
4651
  description: "The language for the bit",
4562
- format: TagFormat.plainText
4652
+ format: TagFormat.plainText,
4653
+ // PLAN-141 F3 (NISO import): a term-sec's language rides
4654
+ // <tbx:termEntry><tbx:langSet xml:lang> — a nested content slot
4655
+ // whose leaf is a $-in-ATTR-position slot (the nested counterpart
4656
+ // of the value-attr slot). Only termEntry elements match, so the
4657
+ // shared tag is inert everywhere else.
4658
+ mappingKeys: {
4659
+ "xml-niso-iec": {
4660
+ "@el": "tbx:termEntry",
4661
+ "@children": { "@el": "tbx:langSet", "@attr": { "xml:lang": "$" } }
4662
+ }
4663
+ }
4563
4664
  },
4564
4665
  {
4565
4666
  key: ConfigKey.property_publisher,
@@ -4758,6 +4859,9 @@ var GROUPS = {
4758
4859
  key: ConfigKey.tag_item,
4759
4860
  jsonKey: "item",
4760
4861
  exportJsonKey: { item: "$" },
4862
+ // PLAN-140 W3 (NISO import): the clause/note label of a mapped
4863
+ // source element ([%1], [%ANMERKUNG 1]).
4864
+ mappingKeys: { "xml-niso-iec": { "@el": "label", "@text": "$" } },
4761
4865
  description: "The item for the bit",
4762
4866
  chain: [
4763
4867
  {
@@ -5720,7 +5824,20 @@ var GROUPS = {
5720
5824
  key: ConfigKey.property_language,
5721
5825
  description: "The language of the book",
5722
5826
  format: TagFormat.plainText,
5723
- maxCount: Count.infinity
5827
+ maxCount: Count.infinity,
5828
+ // PLAN-141 F5 (NISO import): only the PRIMARY (first)
5829
+ // <content-language> survives — the maxEmits cap realises the
5830
+ // catalog's document-language rule (extras drop, R-4).
5831
+ mappingKeys: {
5832
+ "xml-niso-iec": [
5833
+ { predicates: [], maxEmits: 1, rule: { "@el": "content-language", "@text": "$" } }
5834
+ ]
5835
+ }
5836
+ },
5837
+ {
5838
+ key: ConfigKey.property_displayName,
5839
+ description: "The display name of the book",
5840
+ format: TagFormat.plainText
5724
5841
  },
5725
5842
  {
5726
5843
  key: ConfigKey.property_customerExternalId,
@@ -5796,7 +5913,18 @@ var GROUPS = {
5796
5913
  description: "The title of the book",
5797
5914
  maxCount: 2,
5798
5915
  jsonKey: "title|multi(count=2, key=subtitle)",
5799
- exportJsonKey: [{ "@level=1": { title: "$" } }, { "@level=2": { subtitle: "$" } }]
5916
+ exportJsonKey: [{ "@level=1": { title: "$" } }, { "@level=2": { subtitle: "$" } }],
5917
+ // PLAN-141 F5 (NISO import): the PRIMARY (first) <title-wrap>'s
5918
+ // assembled <full> title → [#]; the per-language extras drop (R-4).
5919
+ mappingKeys: {
5920
+ "xml-niso-iec": [
5921
+ {
5922
+ predicates: [],
5923
+ maxEmits: 1,
5924
+ rule: { "@el": "title-wrap", "@children": { "@el": "full", "@text": "$" } }
5925
+ }
5926
+ ]
5927
+ }
5800
5928
  },
5801
5929
  {
5802
5930
  key: ConfigKey.property_subtype,
@@ -6109,17 +6237,20 @@ var GROUPS = {
6109
6237
  {
6110
6238
  key: ConfigKey.property_platformMargin,
6111
6239
  description: "The platform margin",
6112
- format: TagFormat.number
6240
+ format: TagFormat.number,
6241
+ nullable: true
6113
6242
  },
6114
6243
  {
6115
6244
  key: ConfigKey.property_platformBorderRadius,
6116
6245
  description: "The platform border radius",
6117
- format: TagFormat.number
6246
+ format: TagFormat.number,
6247
+ nullable: true
6118
6248
  },
6119
6249
  {
6120
6250
  key: ConfigKey.property_platformSelectionBorderRadius,
6121
6251
  description: "The platform selection border radius",
6122
- format: TagFormat.number
6252
+ format: TagFormat.number,
6253
+ nullable: true
6123
6254
  },
6124
6255
  {
6125
6256
  key: ConfigKey.property_platformNeedsShadow,
@@ -6226,7 +6357,13 @@ var GROUPS = {
6226
6357
  {
6227
6358
  key: ConfigKey.property_caption,
6228
6359
  description: "The caption for the resource",
6229
- format: TagFormat.bitmarkText
6360
+ format: TagFormat.bitmarkText,
6361
+ // PLAN-141 F1 (NISO import): a figure's <caption><title> — the
6362
+ // nested content slot reads the title text; the caption rides the
6363
+ // resource chain ([&image:…][@caption:…], catalog D5).
6364
+ mappingKeys: {
6365
+ "xml-niso-iec": { "@el": "caption", "@children": { "@el": "title", "@text": "$" } }
6366
+ }
6230
6367
  },
6231
6368
  {
6232
6369
  key: ConfigKey.property_showInIndex,
@@ -6500,6 +6637,10 @@ var GROUPS = {
6500
6637
  {
6501
6638
  key: ConfigKey.resource_image,
6502
6639
  exportJsonKey: { resource: { type: "image", image: { src: "$" } } },
6640
+ // PLAN-141 F1 (NISO import): the image URL rides the `$`-in-attr
6641
+ // value slot — a <graphic xlink:href> child of the mapped bit
6642
+ // element roots the [&image:URL] chain.
6643
+ mappingKeys: { "xml-niso-iec": { "@el": "graphic", "@attr": { "xlink:href": "$" } } },
6503
6644
  description: "The image resource",
6504
6645
  chain: [
6505
6646
  {
@@ -7374,6 +7515,8 @@ var BITS = {
7374
7515
  maxCount: 2,
7375
7516
  jsonKey: "title|multi(count=2, key=subtitle)",
7376
7517
  exportJsonKey: [{ "@level=1": { title: "$" } }, { "@level=2": { subtitle: "$" } }],
7518
+ // PLAN-140 W3 (NISO import): the title of a mapped source element.
7519
+ mappingKeys: { "xml-niso-iec": { "@el": "title", "@text": "$" } },
7377
7520
  key: ConfigKey.tag_title,
7378
7521
  description: "The title of the article"
7379
7522
  }
@@ -7415,12 +7558,56 @@ var BITS = {
7415
7558
  [BitType.smartStandardArticleNormative]: {
7416
7559
  since: "1.28.0",
7417
7560
  baseBitType: BitType.standardArticleNormative,
7418
- description: "Smart standard normative article bit"
7561
+ description: "Smart standard normative article bit",
7562
+ // PLAN-140 W3 (NISO import, tranche 1): one article bit per NISO
7563
+ // paragraph (catalog D1). CONTENT keys (@children): the subtree is the
7564
+ // bit's body; nothing inside it promotes. (Ancestor-context normativity
7565
+ // is the non-normative sibling's @ancestor key — plan Q4, resolved.)
7566
+ //
7567
+ // Second rule (catalog D1 push-down): a NUMBERED-PARAGRAPH sec is an
7568
+ // article, not a chapter — the sec-type literal outranks the chapter
7569
+ // key's plain sec|app rule (most-specific-wins). Content key: the sec's
7570
+ // label rides [%] via the label tag key, its inner <p> is the body, its
7571
+ // id is the bit's customerId, and the anchor slot marks it
7572
+ // anchor-bearing.
7573
+ mappingKeys: {
7574
+ "xml-niso-iec": [
7575
+ { "@el": "p", "@attr": { id: "$customerId" }, "@children": "$" },
7576
+ {
7577
+ "@el": "sec",
7578
+ "@attr": { "sec-type": "numbered-paragraph", id: "$customerId" },
7579
+ "@children": "$",
7580
+ "\u25BC": "$anchor"
7581
+ }
7582
+ ]
7583
+ }
7419
7584
  },
7420
7585
  [BitType.smartStandardArticleNonNormative]: {
7421
7586
  since: "1.28.0",
7422
7587
  baseBitType: BitType.standardArticleNonNormative,
7423
- description: "Smart standard non-normative article bit"
7588
+ description: "Smart standard non-normative article bit",
7589
+ // PLAN-140 Q4 (normativity inheritance): a <p> is non-normative because
7590
+ // of an ANCESTOR element — a foreword/intro sec or an informative annex.
7591
+ // @ancestor is a reverse-match-only constraint (forward emission ignores
7592
+ // it); the rules are alternatives, first-satisfied wins, and the
7593
+ // @ancestor literals give this key higher specificity than the plain
7594
+ // normative-article <p> key wherever the context applies.
7595
+ mappingKeys: {
7596
+ "xml-niso-iec": [
7597
+ {
7598
+ "@el": "p",
7599
+ "@attr": { id: "$customerId" },
7600
+ "@ancestor": { "@attr": { "sec-type": "foreword|intro" } },
7601
+ "@children": "$"
7602
+ },
7603
+ {
7604
+ "@el": "p",
7605
+ "@attr": { id: "$customerId" },
7606
+ "@ancestor": { "@attr": { "content-type": "informative" } },
7607
+ "@children": "$"
7608
+ }
7609
+ ]
7610
+ }
7424
7611
  },
7425
7612
  [BitType.smartStandardArticleNormativeCollapsible]: {
7426
7613
  since: "1.28.0",
@@ -8051,7 +8238,8 @@ var BITS = {
8051
8238
  {
8052
8239
  key: ConfigKey.property_maxCreatedBits,
8053
8240
  description: "The maximum number of bits that can be created from this bit",
8054
- format: TagFormat.number
8241
+ format: TagFormat.number,
8242
+ nullable: true
8055
8243
  }
8056
8244
  ],
8057
8245
  textFormatDefault: TextFormat.plainText
@@ -8181,6 +8369,13 @@ var BITS = {
8181
8369
  since: "1.3.0",
8182
8370
  baseBitType: BitType._standard,
8183
8371
  description: "Book bit, used to represent a book",
8372
+ // PLAN-141 F5 (NISO import): the <std-meta> front-matter container is
8373
+ // the [.book] bit (STRUCTURAL, first in document order): primary
8374
+ // title-wrap → [#], primary content-language → [@language], dated
8375
+ // std-ref → [@externalId] (tag keys with maxEmits caps — the NISO
8376
+ // convention puts the document language first). std-meta has no id, so
8377
+ // no anchor/customerId. Remaining leaves drop as recorded loss (R-4).
8378
+ mappingKeys: { "xml-niso-iec": { "@el": "std-meta" } },
8184
8379
  tags: [
8185
8380
  {
8186
8381
  key: ConfigKey.group_bookCommon,
@@ -8467,12 +8662,14 @@ var BITS = {
8467
8662
  {
8468
8663
  key: ConfigKey.property_focusX,
8469
8664
  description: "The X coordinate for focusing the image in the browser",
8470
- format: TagFormat.number
8665
+ format: TagFormat.number,
8666
+ nullable: true
8471
8667
  },
8472
8668
  {
8473
8669
  key: ConfigKey.property_focusY,
8474
8670
  description: "The Y coordinate for focusing the image in the browser",
8475
- format: TagFormat.number
8671
+ format: TagFormat.number,
8672
+ nullable: true
8476
8673
  }
8477
8674
  ]
8478
8675
  },
@@ -8503,6 +8700,15 @@ var BITS = {
8503
8700
  since: "1.3.0",
8504
8701
  baseBitType: BitType._standard,
8505
8702
  description: "Chapter bit, used to define chapters in books or articles",
8703
+ // PLAN-140 W3 (NISO import, tranche 1): recover a chapter from a NISO
8704
+ // section or annex. STRUCTURAL key (no @children content slot): the
8705
+ // element carries no body of its own — tag children (label/title) map
8706
+ // via tag keys, every mapped descendant promotes to a following sibling
8707
+ // bit, and the chapter level derives from nesting depth (catalog D1).
8708
+ // The source id lands as [@customerId] (two-id model, D2).
8709
+ mappingKeys: {
8710
+ "xml-niso-iec": { "@el": "sec|app", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" }
8711
+ },
8506
8712
  tags: [
8507
8713
  {
8508
8714
  exportJsonKey: { anchor: "$" },
@@ -8514,7 +8720,10 @@ var BITS = {
8514
8720
  key: ConfigKey.tag_title,
8515
8721
  description: "The title of the chapter",
8516
8722
  jsonKey: "title|setMulti(level)",
8517
- exportJsonKey: { title: "$", level: "$level" }
8723
+ exportJsonKey: { title: "$", level: "$level" },
8724
+ // PLAN-140 W3 (NISO import): the section/annex title; the heading
8725
+ // level rides the normalizer-derived nesting depth (plan Q2).
8726
+ mappingKeys: { "xml-niso-iec": { "@el": "title", "@text": "$" } }
8518
8727
  },
8519
8728
  {
8520
8729
  key: ConfigKey.property_toc,
@@ -8720,11 +8929,28 @@ var BITS = {
8720
8929
  since: "1.3.0",
8721
8930
  baseBitType: BitType._standard,
8722
8931
  description: "Code bit, used for code snippets in articles or documents",
8932
+ // PLAN-141 F4 (NISO import): a <code> block is a CONTENT bit with a
8933
+ // literal text body; @language recovers as [@computerLanguage] (absent
8934
+ // attr → no tag). Anchor = the source id (D2 revised).
8935
+ mappingKeys: {
8936
+ "xml-niso-iec": {
8937
+ "@el": "code",
8938
+ "@attr": { id: "$customerId", language: "$computerLanguage" },
8939
+ "\u25BC": "$anchor",
8940
+ "@text": "$"
8941
+ }
8942
+ },
8723
8943
  tags: [
8724
8944
  {
8725
8945
  key: ConfigKey.property_computerLanguage,
8726
8946
  description: "The programming language of the code snippet",
8727
- format: TagFormat.plainText
8947
+ format: TagFormat.plainText,
8948
+ // PLAN-141 F4 (NISO import): the bit key's `language` sigil recovers
8949
+ // a present attr; this `@absent` rule supplies the AUTHORED default
8950
+ // when the source carries none (catalog code row — an untyped block
8951
+ // is `[@computerLanguage:text]`). Fires only on foreign imports,
8952
+ // never the carrier round-trip.
8953
+ mappingKeys: { "xml-niso-iec": [{ "@absent": "text" }] }
8728
8954
  },
8729
8955
  {
8730
8956
  key: ConfigKey.property_codeLineNumbers,
@@ -8763,7 +8989,20 @@ var BITS = {
8763
8989
  [BitType.smartStandardFormulaNormative]: {
8764
8990
  since: "3.11.0",
8765
8991
  baseBitType: BitType.formula,
8766
- description: "Smart standard normative formula bit, used for mathematical formulas in smart standards that are normative"
8992
+ description: "Smart standard normative formula bit, used for mathematical formulas in smart standards that are normative",
8993
+ // PLAN-141 F4 (NISO import): a <disp-formula> is a CONTENT bit whose
8994
+ // latex-format body carries the <mml:math> subtree VERBATIM (G1 — no
8995
+ // MathML↔LaTeX transform; ids stripped); <label> → [%]; a contained
8996
+ // <legend> PROMOTES as a sibling legend bit (cards cannot nest in a
8997
+ // body). Anchor = the source id (D2 revised).
8998
+ mappingKeys: {
8999
+ "xml-niso-iec": {
9000
+ "@el": "disp-formula",
9001
+ "@attr": { id: "$customerId" },
9002
+ "\u25BC": "$anchor",
9003
+ "@children": "$"
9004
+ }
9005
+ }
8767
9006
  },
8768
9007
  [BitType.smartStandardRemarkFormula]: {
8769
9008
  since: "3.11.0",
@@ -9290,7 +9529,8 @@ var BITS = {
9290
9529
  {
9291
9530
  key: ConfigKey.property_reasonableNumOfChars,
9292
9531
  description: "The reasonable number of characters for the essay",
9293
- format: TagFormat.number
9532
+ format: TagFormat.number,
9533
+ nullable: true
9294
9534
  },
9295
9535
  {
9296
9536
  key: ConfigKey.property_sampleSolution,
@@ -9723,7 +9963,14 @@ var BITS = {
9723
9963
  [BitType.smartStandardList]: {
9724
9964
  since: "1.28.0",
9725
9965
  baseBitType: BitType.standardList,
9726
- description: "Smart standard list bit, used to create smart standard lists in articles or books"
9966
+ description: "Smart standard list bit, used to create smart standard lists in articles or books",
9967
+ // PLAN-140 refs family (NISO import): a reference list is a STRUCTURAL
9968
+ // bit (no @children — it takes a path ordinal anchor and its <ref>
9969
+ // children promote to sibling list-item bits); its <title> rides [#]
9970
+ // via the title tag key.
9971
+ mappingKeys: {
9972
+ "xml-niso-iec": { "@el": "ref-list", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" }
9973
+ }
9727
9974
  },
9728
9975
  [BitType.smartStandardListCollapsible]: {
9729
9976
  since: "1.28.0",
@@ -9768,7 +10015,17 @@ var BITS = {
9768
10015
  [BitType.smartStandardNoteNonNormative]: {
9769
10016
  since: "1.28.0",
9770
10017
  baseBitType: BitType.standardNoteNonNormative,
9771
- description: "Smart standard non-normative note bit, used to provide non-normative notes in smart standards"
10018
+ description: "Smart standard non-normative note bit, used to provide non-normative notes in smart standards",
10019
+ // PLAN-140 W3 (NISO import, tranche 1). CONTENT key (@children): a
10020
+ // note's inner paragraphs are note body, never separate bits.
10021
+ mappingKeys: {
10022
+ "xml-niso-iec": {
10023
+ "@el": "non-normative-note",
10024
+ "@attr": { id: "$customerId" },
10025
+ "@children": "$",
10026
+ "\u25BC": "$anchor"
10027
+ }
10028
+ }
9772
10029
  },
9773
10030
  [BitType.smartStandardNoteNormativeCollapsible]: {
9774
10031
  since: "1.28.0",
@@ -9875,7 +10132,19 @@ var BITS = {
9875
10132
  [BitType.smartStandardRemarkNonNormative]: {
9876
10133
  since: "1.28.0",
9877
10134
  baseBitType: BitType.standardRemarkNonNormative,
9878
- description: "Smart standard non-normative remark bit, used to provide non-normative remarks in smart standards"
10135
+ description: "Smart standard non-normative remark bit, used to provide non-normative remarks in smart standards",
10136
+ // PLAN-140 Q4 rider: a warning/caution note routes to the remark bit —
10137
+ // the same element as the note key, distinguished by the content-type
10138
+ // attr literal (alias value: forward emits the first alternative,
10139
+ // reverse accepts any). Most-specific-wins beats the plain note key.
10140
+ mappingKeys: {
10141
+ "xml-niso-iec": {
10142
+ "@el": "non-normative-note",
10143
+ "@attr": { "content-type": "warning|caution", id: "$customerId" },
10144
+ "@children": "$",
10145
+ "\u25BC": "$anchor"
10146
+ }
10147
+ }
9879
10148
  },
9880
10149
  [BitType.smartStandardRemarkNormativeCollapsible]: {
9881
10150
  since: "1.28.0",
@@ -10331,7 +10600,8 @@ var BITS = {
10331
10600
  {
10332
10601
  key: ConfigKey.property_pageNo,
10333
10602
  description: "Page number for the block, used to indicate the page on which the block appears",
10334
- format: TagFormat.number
10603
+ format: TagFormat.number,
10604
+ nullable: true
10335
10605
  },
10336
10606
  {
10337
10607
  key: ConfigKey.property_x,
@@ -10348,17 +10618,20 @@ var BITS = {
10348
10618
  {
10349
10619
  key: ConfigKey.property_width,
10350
10620
  description: "Width of the block, used to define the size of the block on the page",
10351
- format: TagFormat.number
10621
+ format: TagFormat.number,
10622
+ nullable: true
10352
10623
  },
10353
10624
  {
10354
10625
  key: ConfigKey.property_height,
10355
10626
  description: "Height of the block, used to define the size of the block on the page",
10356
- format: TagFormat.number
10627
+ format: TagFormat.number,
10628
+ nullable: true
10357
10629
  },
10358
10630
  {
10359
10631
  key: ConfigKey.property_index,
10360
10632
  description: "Index of the block, used to order blocks within the extractor page",
10361
- format: TagFormat.number
10633
+ format: TagFormat.number,
10634
+ nullable: true
10362
10635
  },
10363
10636
  {
10364
10637
  key: ConfigKey.property_classification,
@@ -10668,7 +10941,20 @@ var BITS = {
10668
10941
  [BitType.smartStandardLegendNormative]: {
10669
10942
  since: "3.12.0",
10670
10943
  baseBitType: BitType.legend,
10671
- description: "Smart standard normative legend bit, used to provide normative smart standard legends in articles or books"
10944
+ description: "Smart standard normative legend bit, used to provide normative smart standard legends in articles or books",
10945
+ // PLAN-141 F2 (NISO import): a free-standing <legend> (a formula's) or
10946
+ // bare <def-list> (a table footer's — no <legend> wrapper there) that is
10947
+ // NOT the claimed card content of its enclosing bit is a legend bit:
10948
+ // STRUCTURAL-shaped, its <title> and <def-item>s build the
10949
+ // definition-list cards via the card set's authored keys. A <legend>
10950
+ // inside a figure/table bit stays that bit's claimed card content and
10951
+ // never reaches this key. Anchor = the source id (D2 revised).
10952
+ mappingKeys: {
10953
+ "xml-niso-iec": [
10954
+ { "@el": "legend", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" },
10955
+ { "@el": "def-list", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" }
10956
+ ]
10957
+ }
10672
10958
  },
10673
10959
  [BitType.smartStandardRemarkLegend]: {
10674
10960
  since: "3.12.0",
@@ -10723,7 +11009,16 @@ var BITS = {
10723
11009
  [BitType.smartStandardDefinitionListNormative]: {
10724
11010
  since: "5.24.0",
10725
11011
  baseBitType: BitType.standardDefinitionListNormative,
10726
- description: "Smart standard normative definition list bit, used to create normative smart standard definition lists in articles or books"
11012
+ description: "Smart standard normative definition list bit, used to create normative smart standard definition lists in articles or books",
11013
+ // PLAN-141 F3 (NISO import): a terms-and-definitions clause. STRUCTURAL-
11014
+ // shaped: <label> → [%], the tbx:termEntry/langSet subtree builds the
11015
+ // definition-list card via the card set's authored keys (all tig terms
11016
+ // stacked on the term side; definition/example/notes/source composed on
11017
+ // the definition side with authored locale affixes), langSet @xml:lang →
11018
+ // [@lang] (nested attr slot on the tag key). Anchor = the source id.
11019
+ mappingKeys: {
11020
+ "xml-niso-iec": { "@el": "term-sec", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" }
11021
+ }
10727
11022
  },
10728
11023
  [BitType.smartStandardDefinitionListNonNormative]: {
10729
11024
  since: "5.24.0",
@@ -10792,12 +11087,14 @@ var BITS = {
10792
11087
  {
10793
11088
  key: ConfigKey.property_focusX,
10794
11089
  description: "X-coordinate for the focus point, used to define the focus area in the image",
10795
- format: TagFormat.number
11090
+ format: TagFormat.number,
11091
+ nullable: true
10796
11092
  },
10797
11093
  {
10798
11094
  key: ConfigKey.property_focusY,
10799
11095
  description: "Y-coordinate for the focus point, used to define the focus area in the image",
10800
- format: TagFormat.number
11096
+ format: TagFormat.number,
11097
+ nullable: true
10801
11098
  }
10802
11099
  ]
10803
11100
  },
@@ -10904,7 +11201,15 @@ var BITS = {
10904
11201
  [BitType.smartStandardImageFigureNormative]: {
10905
11202
  since: "1.28.0",
10906
11203
  baseBitType: BitType.standardImageFigureNormative,
10907
- description: "Smart standard normative image figure bit, used to create smart standard normative image figures in articles or books"
11204
+ description: "Smart standard normative image figure bit, used to create smart standard normative image figures in articles or books",
11205
+ // PLAN-141 F1 (NISO import): a figure is STRUCTURAL-shaped (no
11206
+ // @children — no body): <label> → [%], <caption>/<graphic> ride the
11207
+ // [&image] resource chain, the <legend> def-list becomes the bit's
11208
+ // definition-list cards, and an inner note PROMOTES as a sibling bit.
11209
+ // Anchor = the source id (D2 revised).
11210
+ mappingKeys: {
11211
+ "xml-niso-iec": { "@el": "fig", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" }
11212
+ }
10908
11213
  },
10909
11214
  [BitType.smartStandardImageFigureNonNormative]: {
10910
11215
  since: "1.28.0",
@@ -11632,7 +11937,19 @@ var BITS = {
11632
11937
  [BitType.smartStandardListItem]: {
11633
11938
  since: "1.28.0",
11634
11939
  baseBitType: BitType.standardListItem,
11635
- description: "Smart standard list item bit, used to create smart standard list items in articles or books"
11940
+ description: "Smart standard list item bit, used to create smart standard list items in articles or books",
11941
+ // PLAN-140 refs family (NISO import): one list-item bit per <ref> —
11942
+ // CONTENT key (its citation subtree is the body), counter anchor scoped
11943
+ // to the containing list, <label> → [%], nested <std-id> → [@externalId]
11944
+ // (the externalId tag key's nested content slot).
11945
+ mappingKeys: {
11946
+ "xml-niso-iec": {
11947
+ "@el": "ref",
11948
+ "@attr": { id: "$customerId" },
11949
+ "@children": "$",
11950
+ "\u25BC": "$anchor"
11951
+ }
11952
+ }
11636
11953
  },
11637
11954
  [BitType.smartStandardListItemCollapsible]: {
11638
11955
  since: "1.28.0",
@@ -12739,7 +13056,13 @@ var BITS = {
12739
13056
  // `<caption>` — placed inside the idiomatic `<table>` container as
12740
13057
  // its first child. Outside a table context it falls back to the
12741
13058
  // residual carrier.
12742
- htmlKey: { "@el": "caption", "@children": "$" }
13059
+ htmlKey: { "@el": "caption", "@children": "$" },
13060
+ // PLAN-141 F2 (NISO import): a table-wrap's <caption><title> — the
13061
+ // nested content slot reads the title text (catalog D5: the caption
13062
+ // is a TOP-LEVEL bit property on table bits).
13063
+ mappingKeys: {
13064
+ "xml-niso-iec": { "@el": "caption", "@children": { "@el": "title", "@text": "$" } }
13065
+ }
12743
13066
  },
12744
13067
  {
12745
13068
  key: ConfigKey.property_tableFixedHeader,
@@ -12912,7 +13235,16 @@ var BITS = {
12912
13235
  [BitType.smartStandardTableExtendedNormative]: {
12913
13236
  since: "1.28.0",
12914
13237
  baseBitType: BitType.standardTableExtendedNormative,
12915
- description: "Smart standard normative extended table bit, used to create smart standard normative extended tables in articles or books"
13238
+ description: "Smart standard normative extended table bit, used to create smart standard normative extended tables in articles or books",
13239
+ // PLAN-141 F2 (NISO import): a table-wrap is STRUCTURAL-shaped (no
13240
+ // @children — no body): <label> → [%], <caption><title> → the bit-level
13241
+ // [@caption] (D5), the inner <table>/<table-wrap-foot> build the
13242
+ // table-extended card grid via the card set's authored keys, and a
13243
+ // footer <def-list> PROMOTES as a sibling legend bit. Anchor = the
13244
+ // source id (D2 revised).
13245
+ mappingKeys: {
13246
+ "xml-niso-iec": { "@el": "table-wrap", "@attr": { id: "$customerId" }, "\u25BC": "$anchor" }
13247
+ }
12916
13248
  },
12917
13249
  [BitType.smartStandardTableExtendedNonNormative]: {
12918
13250
  since: "1.28.0",
@@ -12987,7 +13319,8 @@ var BITS = {
12987
13319
  {
12988
13320
  key: ConfigKey.property_maxDisplayLevel,
12989
13321
  description: "Maximum display level for the chapter in the table of contents",
12990
- format: TagFormat.number
13322
+ format: TagFormat.number,
13323
+ nullable: true
12991
13324
  }
12992
13325
  ]
12993
13326
  },
@@ -12999,7 +13332,8 @@ var BITS = {
12999
13332
  {
13000
13333
  key: ConfigKey.property_maxTocChapterLevel,
13001
13334
  description: "Maximum chapter level for the inline table of contents",
13002
- format: TagFormat.number
13335
+ format: TagFormat.number,
13336
+ nullable: true
13003
13337
  }
13004
13338
  ]
13005
13339
  },
@@ -13422,13 +13756,15 @@ var BITS = {
13422
13756
  key: ConfigKey.property_width,
13423
13757
  // Same as image
13424
13758
  description: "Width for the embedded content, used to define the width in pixels",
13425
- format: TagFormat.number
13759
+ format: TagFormat.number,
13760
+ nullable: true
13426
13761
  },
13427
13762
  {
13428
13763
  key: ConfigKey.property_height,
13429
13764
  // Same as image
13430
13765
  description: "Height for the embedded content, used to define the height in pixels",
13431
- format: TagFormat.number
13766
+ format: TagFormat.number,
13767
+ nullable: true
13432
13768
  },
13433
13769
  {
13434
13770
  key: ConfigKey.property_vendorUrl,
@@ -13466,7 +13802,8 @@ var BITS = {
13466
13802
  {
13467
13803
  key: ConfigKey.property_jupyterExecutionCount,
13468
13804
  description: "Execution count for the Jupyter output, used to define the execution order",
13469
- format: TagFormat.number
13805
+ format: TagFormat.number,
13806
+ nullable: true
13470
13807
  }
13471
13808
  ],
13472
13809
  textFormatDefault: TextFormat.plainText
@@ -14016,7 +14353,7 @@ var instance2 = new Config();
14016
14353
  // src/generated/package_info.ts
14017
14354
  var PACKAGE_INFO = {
14018
14355
  "name": "@gmb/bitmark-parser-generator",
14019
- "version": "5.35.0",
14356
+ "version": "5.36.0",
14020
14357
  "author": "Get More Brain Ltd",
14021
14358
  "license": "ISC",
14022
14359
  "description": "A bitmark parser and generator using Peggy.js"
@@ -14545,6 +14882,8 @@ var NodeType = {
14545
14882
  definition: "definition",
14546
14883
  definitions: "definitions",
14547
14884
  definitionsValue: "definitionsValue",
14885
+ displayName: "displayName",
14886
+ displayNameValue: "displayNameValue",
14548
14887
  diffContext: "diffContext",
14549
14888
  diffContextValue: "diffContextValue",
14550
14889
  diffOp: "diffOp",
@@ -28467,6 +28806,12 @@ var Builder = class extends BaseBuilder {
28467
28806
  data.publisherName,
28468
28807
  options
28469
28808
  ),
28809
+ displayName: this.toAstProperty(
28810
+ bitType,
28811
+ ConfigKey.property_displayName,
28812
+ data.displayName,
28813
+ options
28814
+ ),
28470
28815
  theme: this.toAstProperty(bitType, ConfigKey.property_theme, data.theme, options),
28471
28816
  rtl: this.toAstProperty(bitType, ConfigKey.property_rtl, data.rtl, options),
28472
28817
  computerLanguage: this.toAstProperty(
@@ -44629,7 +44974,7 @@ var ConfigBuilder = class {
44629
44974
  }
44630
44975
  const outputFolder = opts.outputDir ?? "assets/config";
44631
44976
  const outputFolderBits = path3.join(outputFolder, "bits");
44632
- const outputFolderGroups = path3.join(outputFolder, "partials");
44977
+ const outputFolderGroups = path3.join(outputFolder, "groups");
44633
44978
  const outputFolderCards = path3.join(outputFolder, "cards");
44634
44979
  fs3.ensureDirSync(outputFolderBits);
44635
44980
  fs3.ensureDirSync(outputFolderGroups);
@@ -44664,6 +45009,15 @@ var ConfigBuilder = class {
44664
45009
  }
44665
45010
  return [{ key: groupKey }];
44666
45011
  };
45012
+ const mappingKeysField = (base, json, html) => {
45013
+ const record = {};
45014
+ for (const [id, value] of Object.entries(base ?? {})) {
45015
+ if (value != null) record[id] = value;
45016
+ }
45017
+ if (json.present && json.value != null) record.json = json.value;
45018
+ if (html.present && html.value != null) record.html = html.value;
45019
+ return Object.keys(record).length > 0 ? { mappingKeys: record } : {};
45020
+ };
44667
45021
  const bareKeyForProperty = (key) => {
44668
45022
  if (key.startsWith("@") || key.startsWith("&")) return key.substring(1);
44669
45023
  return void 0;
@@ -44684,22 +45038,20 @@ var ConfigBuilder = class {
44684
45038
  if (tag.format === TagFormat.plainText) {
44685
45039
  format = "string";
44686
45040
  } else if (tag.format === TagFormat.boolean) {
44687
- format = "bool";
44688
- } else if (tag.format === TagFormat.bitmarkText) {
44689
- format = "bitmark";
45041
+ format = "boolean";
44690
45042
  } else if (tag.format === TagFormat.number) {
44691
45043
  format = "number";
44692
45044
  } else {
44693
- format = "bitmark--";
45045
+ format = "bitmark+";
44694
45046
  }
44695
45047
  } else if (tagType === BitTagConfigKeyType.property) {
44696
45048
  tagName = tag.key;
44697
45049
  if (tag.format === TagFormat.plainText) {
44698
45050
  format = "string";
44699
45051
  } else if (tag.format === TagFormat.boolean) {
44700
- format = "bool";
45052
+ format = "boolean";
44701
45053
  } else if (tag.format === TagFormat.bitmarkText) {
44702
- format = "bitmark";
45054
+ format = "bitmark+";
44703
45055
  } else if (tag.format === TagFormat.number) {
44704
45056
  format = "number";
44705
45057
  } else if (tag.format === TagFormat.coordinates) {
@@ -44731,8 +45083,11 @@ var ConfigBuilder = class {
44731
45083
  tags2.push({
44732
45084
  type: "group",
44733
45085
  key: ref.key,
44734
- ...hasExportChosen ? { jsonKey: exportJsonKeyChosen } : {},
44735
- ...hasHtmlChosen ? { htmlKey: htmlKeyChosen } : {},
45086
+ ...mappingKeysField(
45087
+ { ...ref.extraKeys ?? {}, ...tag.mappingKeys ?? {} },
45088
+ { value: exportJsonKeyChosen, present: hasExportChosen },
45089
+ { value: htmlKeyChosen, present: hasHtmlChosen }
45090
+ ),
44736
45091
  ...min != null ? { min } : {},
44737
45092
  ...max != null ? { max } : {},
44738
45093
  ...description ? { description } : {}
@@ -44749,53 +45104,48 @@ var ConfigBuilder = class {
44749
45104
  chain = chainTags;
44750
45105
  }
44751
45106
  const hasExport = Object.prototype.hasOwnProperty.call(tag, "exportJsonKey");
44752
- let exportJsonKeyField;
44753
- if (hasExport) {
44754
- exportJsonKeyField = { jsonKey: tag.exportJsonKey };
44755
- } else if (legacyIsTrivialDefault(tag)) {
44756
- exportJsonKeyField = {};
44757
- } else {
45107
+ if (!hasExport && !legacyIsTrivialDefault(tag)) {
44758
45108
  const headingPath = [...pathStack, `tags.${tag.key}`].join(" / ");
44759
45109
  exportJsonKeyErrors.push(
44760
45110
  `Missing exportJsonKey for non-default jsonKey at ${headingPath}` + (tag.jsonKey ? ` (legacy: \`${tag.jsonKey}\`)` : ` (symbol-mapped tag \`${tag.key}\`)`)
44761
45111
  );
44762
- exportJsonKeyField = {};
44763
45112
  }
44764
45113
  const hasHtml = Object.prototype.hasOwnProperty.call(tag, "htmlKey");
44765
- const htmlKeyField = hasHtml ? { htmlKey: tag.htmlKey } : {};
44766
45114
  const t = {
44767
45115
  type: "tag",
44768
45116
  key: tagName,
44769
- ...exportJsonKeyField,
44770
- ...htmlKeyField,
44771
- format,
44772
- default: tag.defaultValue ?? null,
45117
+ ...mappingKeysField(
45118
+ tag.mappingKeys,
45119
+ { value: tag.exportJsonKey, present: hasExport },
45120
+ { value: tag.htmlKey, present: hasHtml }
45121
+ ),
45122
+ ...format && format !== "string" ? { format } : {},
45123
+ ...tag.defaultValue != null ? { default: tag.defaultValue } : {},
44773
45124
  ...tag.nullable ? { nullable: true } : {},
44774
- min: tag.minCount == null ? 0 : tag.minCount,
44775
- max: tag.maxCount == null ? 1 : tag.maxCount,
45125
+ ...tag.minCount != null && tag.minCount !== 0 ? { min: tag.minCount } : {},
45126
+ ...tag.maxCount != null && tag.maxCount !== 1 ? { max: tag.maxCount } : {},
44776
45127
  description: tag.description ?? "",
44777
45128
  tags: chain
44778
45129
  };
44779
45130
  tags2.push(t);
44780
45131
  return tags2;
44781
45132
  };
44782
- const cardJsonKeyField = (container, legacyKey, exportKey, pathHeading) => {
45133
+ const cardMappingKeysField = (container, base, legacyKey, exportKey, htmlExportKey, pathHeading) => {
44783
45134
  const c = container;
44784
45135
  const hasExport = Object.prototype.hasOwnProperty.call(c, exportKey);
44785
- if (hasExport) return { jsonKey: c[exportKey] };
44786
- const legacy = c[legacyKey];
44787
- if (legacy == null) return {};
44788
- exportJsonKeyErrors.push(
44789
- `Missing exportJsonKey for non-default jsonKey at ${pathHeading} (legacy: \`${legacy}\`)`
44790
- );
44791
- return {};
44792
- };
44793
- const cardHtmlKeyField = (container, htmlExportKey) => {
44794
- const c = container;
44795
- if (Object.prototype.hasOwnProperty.call(c, htmlExportKey)) {
44796
- return { htmlKey: c[htmlExportKey] };
45136
+ if (!hasExport && c[legacyKey] != null) {
45137
+ exportJsonKeyErrors.push(
45138
+ `Missing exportJsonKey for non-default jsonKey at ${pathHeading} (legacy: \`${c[legacyKey]}\`)`
45139
+ );
44797
45140
  }
44798
- return {};
45141
+ return mappingKeysField(
45142
+ base,
45143
+ { value: c[exportKey], present: hasExport },
45144
+ {
45145
+ value: c[htmlExportKey],
45146
+ present: Object.prototype.hasOwnProperty.call(c, htmlExportKey)
45147
+ }
45148
+ );
44799
45149
  };
44800
45150
  const serializeCardSet = (cardSetKey) => {
44801
45151
  const variantBodyFormat = (variant) => variant.format ?? TextFormat.bitmarkText;
@@ -44826,11 +45176,17 @@ var ConfigBuilder = class {
44826
45176
  variantTags.push(...processTagEntries(variantTag, [variantPath]));
44827
45177
  }
44828
45178
  return {
44829
- ...cardJsonKeyField(variant, "jsonKey", "exportJsonKey", variantPath),
44830
- ...cardHtmlKeyField(variant, "htmlKey"),
44831
- format: variantBodyFormat(variant),
45179
+ ...cardMappingKeysField(
45180
+ variant,
45181
+ void 0,
45182
+ "jsonKey",
45183
+ "exportJsonKey",
45184
+ "htmlKey",
45185
+ variantPath
45186
+ ),
45187
+ bodyFormat: variantBodyFormat(variant),
44832
45188
  tags: variantTags,
44833
- repeatCount: variant.repeatCount ?? 1,
45189
+ ...variant.repeatCount != null && variant.repeatCount !== 1 ? { repeatCount: variant.repeatCount } : {},
44834
45190
  ...variant.bodyRequired ? { bodyRequired: true } : {},
44835
45191
  ...variant.bodyAllowed === false ? { bodyForbidden: true } : {}
44836
45192
  };
@@ -44838,8 +45194,14 @@ var ConfigBuilder = class {
44838
45194
  return {
44839
45195
  name: side.name,
44840
45196
  ...side.repeat ? { repeat: side.repeat } : {},
44841
- ...cardJsonKeyField(side, "jsonKey", "exportJsonKey", sidePath),
44842
- ...cardHtmlKeyField(side, "htmlKey"),
45197
+ ...cardMappingKeysField(
45198
+ side,
45199
+ side.mappingKeys,
45200
+ "jsonKey",
45201
+ "exportJsonKey",
45202
+ "htmlKey",
45203
+ sidePath
45204
+ ),
44843
45205
  variants
44844
45206
  };
44845
45207
  });
@@ -44850,20 +45212,40 @@ var ConfigBuilder = class {
44850
45212
  const hasSideHtml = Object.prototype.hasOwnProperty.call(section, "sideHtmlKey");
44851
45213
  let cardSides = sides;
44852
45214
  if (hasSideExport || section.sideJsonKey != null || hasSideHtml) {
44853
- const sideOverride = cardJsonKeyField(
44854
- section,
44855
- "sideJsonKey",
44856
- "sideExportJsonKey",
44857
- `${sectionPath} / sides.*`
44858
- );
44859
- const sideHtmlOverride = cardHtmlKeyField(section, "sideHtmlKey");
44860
- cardSides = sides.map((s) => ({ ...s, ...sideOverride, ...sideHtmlOverride }));
45215
+ if (!hasSideExport && section.sideJsonKey != null) {
45216
+ exportJsonKeyErrors.push(
45217
+ `Missing exportJsonKey for non-default jsonKey at ${sectionPath} / sides.* (legacy: \`${section.sideJsonKey}\`)`
45218
+ );
45219
+ }
45220
+ cardSides = sides.map((s) => {
45221
+ const record = { ...s.mappingKeys ?? {} };
45222
+ if (hasSideExport) {
45223
+ if (section.sideExportJsonKey != null) record.json = section.sideExportJsonKey;
45224
+ else delete record.json;
45225
+ }
45226
+ if (hasSideHtml) {
45227
+ if (section.sideHtmlKey != null) record.html = section.sideHtmlKey;
45228
+ else delete record.html;
45229
+ }
45230
+ return {
45231
+ name: s.name,
45232
+ ...s.repeat ? { repeat: s.repeat } : {},
45233
+ ...Object.keys(record).length > 0 ? { mappingKeys: record } : {},
45234
+ variants: s.variants
45235
+ };
45236
+ });
44861
45237
  }
44862
45238
  return {
44863
45239
  name: sectionName,
44864
45240
  ...section.isDefault ? { isDefault: true } : {},
44865
- ...cardJsonKeyField(section, "jsonKey", "exportJsonKey", sectionPath),
44866
- ...cardHtmlKeyField(section, "htmlKey"),
45241
+ ...cardMappingKeysField(
45242
+ section,
45243
+ section.mappingKeys,
45244
+ "jsonKey",
45245
+ "exportJsonKey",
45246
+ "htmlKey",
45247
+ sectionPath
45248
+ ),
44867
45249
  // PLAN-085: per-section cardinality. Emit only when non-default
44868
45250
  // (treat `0` / undefined as "unbounded — omit").
44869
45251
  ...section.minCount != null && section.minCount !== 0 ? { min: section.minCount } : {},
@@ -44871,7 +45253,18 @@ var ConfigBuilder = class {
44871
45253
  sides: cardSides
44872
45254
  };
44873
45255
  });
44874
- return { name: normalizedKey, ...cardHtmlKeyField(cardSetConfig, "htmlKey"), cards };
45256
+ return {
45257
+ name: normalizedKey,
45258
+ ...mappingKeysField(
45259
+ void 0,
45260
+ { present: false },
45261
+ {
45262
+ value: cardSetConfig.htmlKey,
45263
+ present: Object.prototype.hasOwnProperty.call(cardSetConfig, "htmlKey")
45264
+ }
45265
+ ),
45266
+ cards
45267
+ };
44875
45268
  }
44876
45269
  const cardSetPath = `cardSets.${normalizedKey} / cards.default`;
44877
45270
  return {
@@ -44880,8 +45273,14 @@ var ConfigBuilder = class {
44880
45273
  {
44881
45274
  name: "default",
44882
45275
  isDefault: true,
44883
- ...cardJsonKeyField(cardSetConfig, "jsonKey", "exportJsonKey", cardSetPath),
44884
- ...cardHtmlKeyField(cardSetConfig, "htmlKey"),
45276
+ ...cardMappingKeysField(
45277
+ cardSetConfig,
45278
+ cardSetConfig.mappingKeys,
45279
+ "jsonKey",
45280
+ "exportJsonKey",
45281
+ "htmlKey",
45282
+ cardSetPath
45283
+ ),
44885
45284
  sides
44886
45285
  }
44887
45286
  ]
@@ -44917,14 +45316,19 @@ var ConfigBuilder = class {
44917
45316
  ...t.max != null ? { max: t.max } : {},
44918
45317
  ...t.description ? { description: t.description } : {}
44919
45318
  };
44920
- if (Object.prototype.hasOwnProperty.call(t, "jsonKey")) {
44921
- ref.exportJsonKey = t.jsonKey;
45319
+ const record = t.mappingKeys ?? {};
45320
+ if (Object.prototype.hasOwnProperty.call(record, "json")) {
45321
+ ref.exportJsonKey = record.json;
44922
45322
  ref.hasExportJsonKey = true;
44923
45323
  }
44924
- if (Object.prototype.hasOwnProperty.call(t, "htmlKey")) {
44925
- ref.htmlKey = t.htmlKey;
45324
+ if (Object.prototype.hasOwnProperty.call(record, "html")) {
45325
+ ref.htmlKey = record.html;
44926
45326
  ref.hasHtmlKey = true;
44927
45327
  }
45328
+ const extras = Object.fromEntries(
45329
+ Object.entries(record).filter(([id]) => id !== "json" && id !== "html")
45330
+ );
45331
+ if (Object.keys(extras).length > 0) ref.extraKeys = extras;
44928
45332
  replacements.push(ref);
44929
45333
  }
44930
45334
  squashedGroups.set(groupKey, replacements);
@@ -44958,8 +45362,11 @@ var ConfigBuilder = class {
44958
45362
  pushOrReplace({
44959
45363
  type: "group",
44960
45364
  key: ref.key,
44961
- ...ref.hasExportJsonKey ? { jsonKey: ref.exportJsonKey } : {},
44962
- ...ref.hasHtmlKey ? { htmlKey: ref.htmlKey } : {},
45365
+ ...mappingKeysField(
45366
+ ref.extraKeys,
45367
+ { value: ref.exportJsonKey, present: !!ref.hasExportJsonKey },
45368
+ { value: ref.htmlKey, present: !!ref.hasHtmlKey }
45369
+ ),
44963
45370
  ...ref.min != null ? { min: ref.min } : {},
44964
45371
  ...ref.max != null ? { max: ref.max } : {},
44965
45372
  ...ref.description ? { description: ref.description } : {}
@@ -44972,8 +45379,11 @@ var ConfigBuilder = class {
44972
45379
  pushOrReplace({
44973
45380
  type: "group",
44974
45381
  key: ref.key,
44975
- ...ref.hasExportJsonKey ? { jsonKey: ref.exportJsonKey } : {},
44976
- ...ref.hasHtmlKey ? { htmlKey: ref.htmlKey } : {},
45382
+ ...mappingKeysField(
45383
+ ref.extraKeys,
45384
+ { value: ref.exportJsonKey, present: !!ref.hasExportJsonKey },
45385
+ { value: ref.htmlKey, present: !!ref.hasHtmlKey }
45386
+ ),
44977
45387
  ...ref.min != null ? { min: ref.min } : {},
44978
45388
  ...ref.max != null ? { max: ref.max } : {},
44979
45389
  ...ref.description ? { description: ref.description } : {}
@@ -45000,20 +45410,17 @@ var ConfigBuilder = class {
45000
45410
  description: b.description ?? "",
45001
45411
  since: resolvedBitConfig.since,
45002
45412
  deprecated: resolvedBitConfig.deprecated,
45003
- history: [
45004
- {
45005
- version: resolvedBitConfig.since,
45006
- changes: ["Initial version"]
45007
- }
45008
- ],
45009
- format: resolvedBitConfig.textFormatDefault ?? "bitmark--",
45010
- bodyRequired: resolvedBitConfig.bodyRequired ?? false,
45011
- bodyForbidden: !(resolvedBitConfig.bodyAllowed ?? true),
45012
- footerRequired: resolvedBitConfig.footerRequired ?? false,
45013
- footerForbidden: !(resolvedBitConfig.footerAllowed ?? true),
45014
- resourceAttachmentAllowed: resolvedBitConfig.resourceAttachmentAllowed ?? true,
45413
+ bodyFormat: resolvedBitConfig.textFormatDefault ?? "bitmark--",
45414
+ ...resolvedBitConfig.bodyRequired ? { bodyRequired: true } : {},
45415
+ ...resolvedBitConfig.bodyAllowed === false ? { bodyForbidden: true } : {},
45416
+ ...resolvedBitConfig.footerRequired ? { footerRequired: true } : {},
45417
+ ...resolvedBitConfig.footerAllowed === false ? { footerForbidden: true } : {},
45418
+ ...resolvedBitConfig.resourceAttachmentAllowed === false ? { resourceAttachmentAllowed: false } : {},
45419
+ // Bit-level named key mappings (element keys for markup import),
45420
+ // carried verbatim from the raw config.
45421
+ ...mappingKeysField(b.mappingKeys, { present: false }, { present: false }),
45015
45422
  tags: tags2,
45016
- ...cardRef ? { card: cardRef } : {}
45423
+ ...cardRef ? { cardSet: cardRef } : {}
45017
45424
  };
45018
45425
  const output = path3.join(outputFolderBits, `${b.bitType}.jsonc`);
45019
45426
  const str = JSON.stringify(bitJson, null, 2);
@@ -45041,14 +45448,7 @@ var ConfigBuilder = class {
45041
45448
  const bitJson = {
45042
45449
  name: groupKey,
45043
45450
  description: g.description ?? "",
45044
- since: "UNKNOWN",
45045
45451
  deprecated: g.deprecated,
45046
- history: [
45047
- {
45048
- version: "UNKNOWN",
45049
- changes: ["Initial version"]
45050
- }
45051
- ],
45052
45452
  tags: tags2
45053
45453
  // cards: [
45054
45454
  // {
@@ -45102,8 +45502,11 @@ var ConfigBuilder = class {
45102
45502
  tags2.push({
45103
45503
  type: "group",
45104
45504
  key: ref.key,
45105
- ...ref.hasExportJsonKey ? { jsonKey: ref.exportJsonKey } : {},
45106
- ...ref.hasHtmlKey ? { htmlKey: ref.htmlKey } : {},
45505
+ ...mappingKeysField(
45506
+ ref.extraKeys,
45507
+ { value: ref.exportJsonKey, present: !!ref.hasExportJsonKey },
45508
+ { value: ref.htmlKey, present: !!ref.hasHtmlKey }
45509
+ ),
45107
45510
  ...ref.min != null ? { min: ref.min } : {},
45108
45511
  ...ref.max != null ? { max: ref.max } : {},
45109
45512
  ...ref.description ? { description: ref.description } : {}
@@ -45124,14 +45527,7 @@ var ConfigBuilder = class {
45124
45527
  const bitJson = {
45125
45528
  name: groupKey,
45126
45529
  description: b.description ?? "",
45127
- since: "UNKNOWN",
45128
45530
  deprecated: b.deprecated,
45129
- history: [
45130
- {
45131
- version: "UNKNOWN",
45132
- changes: ["Initial version"]
45133
- }
45134
- ],
45135
45531
  tags: tags2
45136
45532
  };
45137
45533
  const output = path3.join(outputFolderGroups, `${groupKey}.jsonc`);
@@ -45172,7 +45568,7 @@ var ConfigBuilder = class {
45172
45568
  validateConfigTree(outputFolder) {
45173
45569
  const errors = [];
45174
45570
  const outputFolderBits = path3.join(outputFolder, "bits");
45175
- const outputFolderGroups = path3.join(outputFolder, "partials");
45571
+ const outputFolderGroups = path3.join(outputFolder, "groups");
45176
45572
  const outputFolderCards = path3.join(outputFolder, "cards");
45177
45573
  const availableGroups = /* @__PURE__ */ new Set();
45178
45574
  if (fs3.existsSync(outputFolderGroups)) {
@@ -45246,7 +45642,7 @@ var ConfigBuilder = class {
45246
45642
  const content = fs3.readFileSync(filePath, "utf-8");
45247
45643
  const config = JSON.parse(content);
45248
45644
  if (config.tags && Array.isArray(config.tags)) {
45249
- checkTags(config.tags, `partials/${file}`, 15);
45645
+ checkTags(config.tags, `groups/${file}`, 15);
45250
45646
  }
45251
45647
  } catch (err) {
45252
45648
  errors.push(`Failed to parse ${file}: ${err}`);