@hyperfixi/core 2.8.0 → 2.9.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.
@@ -3754,6 +3754,9 @@ function isQuote(char) {
3754
3754
  function isDigit(char) {
3755
3755
  return /\d/.test(char);
3756
3756
  }
3757
+ function stripOptionalDiacritics(word) {
3758
+ return word.replace(/[ً-ْٰ]/g, "");
3759
+ }
3757
3760
  function isAsciiLetter(char) {
3758
3761
  return /[a-zA-Z]/.test(char);
3759
3762
  }
@@ -4425,7 +4428,7 @@ var _BaseTokenizer = class _BaseTokenizer {
4425
4428
  * @returns Word without diacritics
4426
4429
  */
4427
4430
  removeDiacritics(word) {
4428
- return word.replace(/[\u064B-\u0652\u0670]/g, "");
4431
+ return stripOptionalDiacritics(word);
4429
4432
  }
4430
4433
  /**
4431
4434
  * Try to match a keyword from profile at the current position.
@@ -4516,24 +4519,40 @@ var _BaseTokenizer = class _BaseTokenizer {
4516
4519
  });
4517
4520
  }
4518
4521
  /**
4519
- * Look up a keyword by native word (case-insensitive).
4522
+ * Look up a keyword by native word (case-insensitive, diacritic-insensitive).
4520
4523
  * O(1) lookup using the keyword map.
4521
4524
  *
4525
+ * The map is INDEXED both with and without diacritics (see
4526
+ * `initializeKeywordsFromProfile`), so a stripped QUERY is the other half of
4527
+ * that: it lets a surface form carrying harakat the profile does not happen to
4528
+ * spell still find its entry. Only consulted after the exact lookup misses, so
4529
+ * every previously-matching word resolves byte-identically.
4530
+ *
4531
+ * Half-implementing this — indexing stripped but querying exact — is what made
4532
+ * diacritized `بَدِّل` (toggle) tokenize as `kind=particle normalized=with`:
4533
+ * `isKeyword` returned false, so the guard in `ArabicProcliticExtractor` that
4534
+ * exists to prevent exactly that handed the word on, and the single-char `ب`
4535
+ * bi- proclitic claimed it. A wrong CONCEPT, not a failed parse.
4536
+ *
4522
4537
  * @param native - Native word to look up
4523
4538
  * @returns KeywordEntry if found, undefined otherwise
4524
4539
  */
4525
4540
  lookupKeyword(native) {
4526
- return this.profileKeywordMap.get(native.toLowerCase());
4541
+ const exact = this.profileKeywordMap.get(native.toLowerCase());
4542
+ if (exact) return exact;
4543
+ const stripped = this.removeDiacritics(native);
4544
+ if (stripped === native) return void 0;
4545
+ return this.profileKeywordMap.get(stripped.toLowerCase());
4527
4546
  }
4528
4547
  /**
4529
- * Check if a word is a known keyword (case-insensitive).
4530
- * O(1) lookup using the keyword map.
4548
+ * Check if a word is a known keyword (case-insensitive, diacritic-insensitive).
4549
+ * O(1) lookup using the keyword map. See {@link lookupKeyword}.
4531
4550
  *
4532
4551
  * @param native - Native word to check
4533
4552
  * @returns true if the word is a keyword
4534
4553
  */
4535
4554
  isKeyword(native) {
4536
- return this.profileKeywordMap.has(native.toLowerCase());
4555
+ return this.lookupKeyword(native) !== void 0;
4537
4556
  }
4538
4557
  /**
4539
4558
  * Set the morphological normalizer for this tokenizer.
@@ -5181,9 +5200,12 @@ var init_arabic = __esm({
5181
5200
  behavior: { primary: "\u0633\u0644\u0648\u0643", normalized: "behavior" },
5182
5201
  install: { primary: "\u062A\u062B\u0628\u064A\u062A", alternatives: ["\u062B\u0628\u0651\u062A"], normalized: "install" },
5183
5202
  // `قِس` is the imperative with the kasra diacritic; the i18n dict (and real
5184
- // Arabic prose) emits it undiacritized as `قس`, so list both — otherwise the
5185
- // generated `قس width`/`قس x` (behavior-draggable/resizable) parse to null and
5186
- // the whole `measure` command drops from the event-handler body (lossy).
5203
+ // Arabic prose) emits it undiacritized as `قس`. BOTH stay listed, and not
5204
+ // for the tokenizer's sake — keyword lookup is diacritic-insensitive now, so
5205
+ // either spelling resolves. It is the vocab gate's V1 check, which compares
5206
+ // the profile against the i18n DICTIONARY as strings: the dictionary says
5207
+ // `قس`, so dropping it here fails V1 (verified). Diacritic-insensitivity
5208
+ // would have to reach that comparison too before this pair can collapse.
5187
5209
  measure: { primary: "\u0642\u064A\u0627\u0633", alternatives: ["\u0642\u0650\u0633", "\u0642\u0633"], normalized: "measure" },
5188
5210
  beep: { primary: "\u0635\u0641\u0651\u0631", normalized: "beep" },
5189
5211
  break: { primary: "\u062A\u0648\u0642\u0641", normalized: "break" },
@@ -5867,6 +5889,15 @@ var init_spanish = __esm({
5867
5889
  patient: { primary: "", position: "before" },
5868
5890
  style: { primary: "con", position: "before" }
5869
5891
  },
5892
+ // Imperative command forms are accepted on INPUT only — `primary` stays the
5893
+ // dictionary form, so rendering is unchanged (see LanguageProfile.defaultVerbForm:
5894
+ // infinitive is the industry standard for UI localization). Hyperscript is a
5895
+ // command language, though, and a native speaker giving a command writes the
5896
+ // imperative, so the parser should read it.
5897
+ //
5898
+ // Only the IRREGULARS are listed. The regular ones reach their keyword through
5899
+ // the morphological normalizer's stem (see spanish-keyword.ts and siblings),
5900
+ // which also covers conjugations nobody enumerated here.
5870
5901
  keywords: {
5871
5902
  // Class/Attribute operations
5872
5903
  toggle: { primary: "alternar", alternatives: ["conmutar", "toggle"], normalized: "toggle" },
@@ -5886,19 +5917,23 @@ var init_spanish = __esm({
5886
5917
  swap: { primary: "intercambiar", alternatives: ["permutar"], normalized: "swap" },
5887
5918
  morph: { primary: "transformar", alternatives: ["convertir"], normalized: "morph" },
5888
5919
  // Variable operations
5889
- set: { primary: "establecer", alternatives: ["fijar", "definir"], normalized: "set" },
5890
- get: { primary: "obtener", alternatives: ["conseguir"], normalized: "get" },
5920
+ set: {
5921
+ primary: "establecer",
5922
+ alternatives: ["fijar", "definir", "establece"],
5923
+ normalized: "set"
5924
+ },
5925
+ get: { primary: "obtener", alternatives: ["conseguir", "obt\xE9n"], normalized: "get" },
5891
5926
  increment: { primary: "incrementar", alternatives: ["aumentar"], normalized: "increment" },
5892
5927
  decrement: { primary: "decrementar", alternatives: ["disminuir"], normalized: "decrement" },
5893
5928
  log: { primary: "registrar", alternatives: ["imprimir"], normalized: "log" },
5894
5929
  // Visibility
5895
- show: { primary: "mostrar", alternatives: ["ense\xF1ar"], normalized: "show" },
5930
+ show: { primary: "mostrar", alternatives: ["ense\xF1ar", "muestra"], normalized: "show" },
5896
5931
  hide: { primary: "ocultar", alternatives: ["esconder"], normalized: "hide" },
5897
5932
  transition: { primary: "transici\xF3n", alternatives: ["animar"], normalized: "transition" },
5898
5933
  // Events
5899
5934
  on: { primary: "en", alternatives: ["al"], normalized: "on" },
5900
5935
  trigger: { primary: "disparar", alternatives: ["activar"], normalized: "trigger" },
5901
- send: { primary: "enviar", normalized: "send" },
5936
+ send: { primary: "enviar", alternatives: ["env\xEDa"], normalized: "send" },
5902
5937
  // DOM focus
5903
5938
  focus: { primary: "enfocar", alternatives: ["enfoque"], normalized: "focus" },
5904
5939
  blur: { primary: "desenfocar", alternatives: ["desenfoque"], normalized: "blur" },
@@ -5935,7 +5970,7 @@ var init_spanish = __esm({
5935
5970
  mousedown: { primary: "rat\xF3nabajo", normalized: "mousedown" },
5936
5971
  mouseup: { primary: "rat\xF3narriba", normalized: "mouseup" },
5937
5972
  // Navigation
5938
- go: { primary: "ir", alternatives: ["navegar"], normalized: "go" },
5973
+ go: { primary: "ir", alternatives: ["navegar", "ve"], normalized: "go" },
5939
5974
  push: { primary: "empujar", alternatives: ["push"], normalized: "push" },
5940
5975
  replace: { primary: "reemplazar", alternatives: ["sustituir"], normalized: "replace" },
5941
5976
  process: { primary: "procesar", normalized: "process" },
@@ -6124,11 +6159,24 @@ var init_french = __esm({
6124
6159
  patient: { primary: "", position: "before" },
6125
6160
  style: { primary: "avec", position: "before" }
6126
6161
  },
6162
+ // Imperative command forms are accepted on INPUT only — `primary` stays the
6163
+ // dictionary form, so rendering is unchanged (see LanguageProfile.defaultVerbForm:
6164
+ // infinitive is the industry standard for UI localization). Hyperscript is a
6165
+ // command language, though, and a native speaker giving a command writes the
6166
+ // imperative, so the parser should read it.
6167
+ //
6168
+ // Only the IRREGULARS are listed. The regular ones reach their keyword through
6169
+ // the morphological normalizer's stem (see spanish-keyword.ts and siblings),
6170
+ // which also covers conjugations nobody enumerated here.
6127
6171
  keywords: {
6128
6172
  toggle: { primary: "basculer", alternatives: ["alterner"], normalized: "toggle" },
6129
6173
  add: { primary: "ajouter", normalized: "add" },
6130
- remove: { primary: "supprimer", alternatives: ["enlever", "retirer"], normalized: "remove" },
6131
- put: { primary: "mettre", alternatives: ["placer"], normalized: "put" },
6174
+ remove: {
6175
+ primary: "supprimer",
6176
+ alternatives: ["enlever", "retirer", "retire"],
6177
+ normalized: "remove"
6178
+ },
6179
+ put: { primary: "mettre", alternatives: ["placer", "mets"], normalized: "put" },
6132
6180
  append: { primary: "annexer", normalized: "append" },
6133
6181
  prepend: { primary: "pr\xE9fixer", normalized: "prepend" },
6134
6182
  take: { primary: "prendre", normalized: "take" },
@@ -6137,16 +6185,16 @@ var init_french = __esm({
6137
6185
  swap: { primary: "\xE9changer", alternatives: ["permuter"], normalized: "swap" },
6138
6186
  morph: { primary: "transformer", alternatives: ["m\xE9tamorphoser"], normalized: "morph" },
6139
6187
  set: { primary: "d\xE9finir", alternatives: ["\xE9tablir"], normalized: "set" },
6140
- get: { primary: "obtenir", normalized: "get" },
6188
+ get: { primary: "obtenir", alternatives: ["obtiens"], normalized: "get" },
6141
6189
  increment: { primary: "incr\xE9menter", alternatives: ["augmenter"], normalized: "increment" },
6142
6190
  decrement: { primary: "d\xE9cr\xE9menter", alternatives: ["diminuer"], normalized: "decrement" },
6143
6191
  log: { primary: "enregistrer", alternatives: ["journaliser"], normalized: "log" },
6144
- show: { primary: "montrer", alternatives: ["afficher"], normalized: "show" },
6192
+ show: { primary: "montrer", alternatives: ["afficher", "montre"], normalized: "show" },
6145
6193
  hide: { primary: "cacher", alternatives: ["masquer"], normalized: "hide" },
6146
6194
  transition: { primary: "transition", alternatives: ["animer"], normalized: "transition" },
6147
6195
  on: { primary: "sur", alternatives: ["lors"], normalized: "on" },
6148
6196
  trigger: { primary: "d\xE9clencher", normalized: "trigger" },
6149
- send: { primary: "envoyer", normalized: "send" },
6197
+ send: { primary: "envoyer", alternatives: ["envoie"], normalized: "send" },
6150
6198
  focus: { primary: "focaliser", alternatives: ["concentrer"], normalized: "focus" },
6151
6199
  blur: { primary: "d\xE9focaliser", normalized: "blur" },
6152
6200
  // Phase 1 (v0.9.90): DOM / form state / debug
@@ -6160,13 +6208,13 @@ var init_french = __esm({
6160
6208
  clear: { primary: "effacer", normalized: "clear" },
6161
6209
  reset: { primary: "r\xE9initialiser", alternatives: ["reinitialiser"], normalized: "reset" },
6162
6210
  breakpoint: { primary: "point-arr\xEAt", alternatives: ["point-arret"], normalized: "breakpoint" },
6163
- go: { primary: "aller", alternatives: ["naviguer"], normalized: "go" },
6211
+ go: { primary: "aller", alternatives: ["naviguer", "va"], normalized: "go" },
6164
6212
  scroll: { primary: "d\xE9filer", alternatives: ["faire-d\xE9filer"], normalized: "scroll" },
6165
6213
  push: { primary: "pousser", normalized: "push" },
6166
6214
  replace: { primary: "remplacer", normalized: "replace" },
6167
6215
  process: { primary: "traiter", normalized: "process" },
6168
6216
  wait: { primary: "attendre", normalized: "wait" },
6169
- fetch: { primary: "chercher", alternatives: ["r\xE9cup\xE9rer"], normalized: "fetch" },
6217
+ fetch: { primary: "chercher", alternatives: ["r\xE9cup\xE9rer", "r\xE9cup\xE8re"], normalized: "fetch" },
6170
6218
  settle: { primary: "stabiliser", normalized: "settle" },
6171
6219
  if: { primary: "si", normalized: "if" },
6172
6220
  unless: { primary: "saufsi", normalized: "unless" },
@@ -7537,16 +7585,25 @@ var init_korean = __esm({
7537
7585
  event: { primary: "\uC744", alternatives: ["\uB97C"], position: "after" }
7538
7586
  // Event as object marker
7539
7587
  },
7588
+ // Imperative command forms are accepted on INPUT only — `primary` stays the
7589
+ // dictionary form, so rendering is unchanged (see LanguageProfile.defaultVerbForm:
7590
+ // infinitive is the industry standard for UI localization). Hyperscript is a
7591
+ // command language, though, and a native speaker giving a command writes the
7592
+ // imperative, so the parser should read it.
7593
+ //
7594
+ // Only the IRREGULARS are listed. The regular ones reach their keyword through
7595
+ // the morphological normalizer's stem (see spanish-keyword.ts and siblings),
7596
+ // which also covers conjugations nobody enumerated here.
7540
7597
  keywords: {
7541
7598
  // Class/Attribute operations
7542
7599
  toggle: { primary: "\uD1A0\uAE00", normalized: "toggle" },
7543
7600
  add: { primary: "\uCD94\uAC00", normalized: "add" },
7544
7601
  remove: { primary: "\uC81C\uAC70", alternatives: ["\uC0AD\uC81C"], normalized: "remove" },
7545
7602
  // Content operations
7546
- put: { primary: "\uB123\uB2E4", alternatives: ["\uB123\uAE30", "\uB193\uAE30"], normalized: "put" },
7603
+ put: { primary: "\uB123\uB2E4", alternatives: ["\uB123\uAE30", "\uB193\uAE30", "\uB123\uC73C\uC138\uC694"], normalized: "put" },
7547
7604
  append: { primary: "\uB367\uBD99\uC774\uB2E4", alternatives: ["\uB05D\uC5D0\uCD94\uAC00"], normalized: "append" },
7548
7605
  prepend: { primary: "\uC55E\uC5D0\uCD94\uAC00", alternatives: ["\uC120\uB450\uCD94\uAC00"], normalized: "prepend" },
7549
- take: { primary: "\uAC00\uC838\uC624\uB2E4", normalized: "take" },
7606
+ take: { primary: "\uAC00\uC838\uC624\uB2E4", alternatives: ["\uAC00\uC838\uC624\uC138\uC694"], normalized: "take" },
7550
7607
  make: { primary: "\uB9CC\uB4E4\uB2E4", normalized: "make" },
7551
7608
  clone: { primary: "\uBCF5\uC81C", normalized: "clone" },
7552
7609
  // 복제=duplicate/clone, 복사=copy
@@ -7554,13 +7611,13 @@ var init_korean = __esm({
7554
7611
  morph: { primary: "\uBCC0\uD615", alternatives: ["\uBCC0\uD658"], normalized: "morph" },
7555
7612
  // Variable operations
7556
7613
  set: { primary: "\uC124\uC815", normalized: "set" },
7557
- get: { primary: "\uC5BB\uB2E4", normalized: "get" },
7614
+ get: { primary: "\uC5BB\uB2E4", alternatives: ["\uC5BB\uC73C\uC138\uC694"], normalized: "get" },
7558
7615
  increment: { primary: "\uC99D\uAC00", normalized: "increment" },
7559
7616
  decrement: { primary: "\uAC10\uC18C", normalized: "decrement" },
7560
7617
  log: { primary: "\uB85C\uADF8", normalized: "log" },
7561
7618
  // Visibility
7562
- show: { primary: "\uBCF4\uC774\uB2E4", alternatives: ["\uD45C\uC2DC", "\uBCF4\uC774\uAE30"], normalized: "show" },
7563
- hide: { primary: "\uC228\uAE30\uB2E4", alternatives: ["\uC228\uAE30\uAE30"], normalized: "hide" },
7619
+ show: { primary: "\uBCF4\uC774\uB2E4", alternatives: ["\uD45C\uC2DC", "\uBCF4\uC774\uAE30", "\uBCF4\uC774\uC138\uC694"], normalized: "show" },
7620
+ hide: { primary: "\uC228\uAE30\uB2E4", alternatives: ["\uC228\uAE30\uAE30", "\uC228\uAE30\uC138\uC694"], normalized: "hide" },
7564
7621
  // primary is the loanword 트랜지션; 전환 ("switch/transition") is the form the
7565
7622
  // i18n transformer emits — registered as an alternative (passthrough-alignment).
7566
7623
  // toggle uses 토글, so 전환 carries no collision.
@@ -7568,7 +7625,7 @@ var init_korean = __esm({
7568
7625
  // Events
7569
7626
  on: { primary: "\uC5D0", alternatives: ["\uC2DC", "\uD560 \uB54C"], normalized: "on" },
7570
7627
  trigger: { primary: "\uD2B8\uB9AC\uAC70", normalized: "trigger" },
7571
- send: { primary: "\uBCF4\uB0B4\uB2E4", normalized: "send" },
7628
+ send: { primary: "\uBCF4\uB0B4\uB2E4", alternatives: ["\uBCF4\uB0B4\uC138\uC694"], normalized: "send" },
7572
7629
  // DOM focus
7573
7630
  focus: { primary: "\uD3EC\uCEE4\uC2A4", normalized: "focus" },
7574
7631
  blur: { primary: "\uBE14\uB7EC", normalized: "blur" },
@@ -8350,25 +8407,38 @@ var init_portuguese = __esm({
8350
8407
  patient: { primary: "", position: "before" },
8351
8408
  style: { primary: "com", position: "before" }
8352
8409
  },
8410
+ // Imperative command forms are accepted on INPUT only — `primary` stays the
8411
+ // dictionary form, so rendering is unchanged (see LanguageProfile.defaultVerbForm:
8412
+ // infinitive is the industry standard for UI localization). Hyperscript is a
8413
+ // command language, though, and a native speaker giving a command writes the
8414
+ // imperative, so the parser should read it.
8415
+ //
8416
+ // Only the IRREGULARS are listed. The regular ones reach their keyword through
8417
+ // the morphological normalizer's stem (see spanish-keyword.ts and siblings),
8418
+ // which also covers conjugations nobody enumerated here.
8353
8419
  keywords: {
8354
8420
  toggle: { primary: "alternar", alternatives: [], normalized: "toggle" },
8355
8421
  add: { primary: "adicionar", alternatives: ["acrescentar"], normalized: "add" },
8356
- remove: { primary: "remover", alternatives: ["eliminar", "apagar"], normalized: "remove" },
8357
- put: { primary: "colocar", alternatives: ["p\xF4r", "por"], normalized: "put" },
8422
+ remove: {
8423
+ primary: "remover",
8424
+ alternatives: ["eliminar", "apagar", "remova"],
8425
+ normalized: "remove"
8426
+ },
8427
+ put: { primary: "colocar", alternatives: ["p\xF4r", "por", "coloque"], normalized: "put" },
8358
8428
  append: { primary: "anexar", normalized: "append" },
8359
8429
  prepend: { primary: "preceder", normalized: "prepend" },
8360
- take: { primary: "pegar", normalized: "take" },
8430
+ take: { primary: "pegar", alternatives: ["pegue"], normalized: "take" },
8361
8431
  make: { primary: "fazer", alternatives: ["criar"], normalized: "make" },
8362
8432
  clone: { primary: "clonar", alternatives: [], normalized: "clone" },
8363
8433
  swap: { primary: "trocar", alternatives: ["substituir"], normalized: "swap" },
8364
8434
  morph: { primary: "transformar", alternatives: ["converter"], normalized: "morph" },
8365
- set: { primary: "definir", alternatives: ["configurar"], normalized: "set" },
8366
- get: { primary: "obter", normalized: "get" },
8435
+ set: { primary: "definir", alternatives: ["configurar", "defina"], normalized: "set" },
8436
+ get: { primary: "obter", alternatives: ["obtenha"], normalized: "get" },
8367
8437
  increment: { primary: "incrementar", alternatives: ["aumentar"], normalized: "increment" },
8368
8438
  decrement: { primary: "decrementar", alternatives: ["diminuir"], normalized: "decrement" },
8369
8439
  log: { primary: "registrar", alternatives: ["imprimir"], normalized: "log" },
8370
8440
  show: { primary: "mostrar", alternatives: ["exibir"], normalized: "show" },
8371
- hide: { primary: "ocultar", alternatives: ["esconder"], normalized: "hide" },
8441
+ hide: { primary: "ocultar", alternatives: ["esconder", "esconda"], normalized: "hide" },
8372
8442
  transition: { primary: "transi\xE7\xE3o", alternatives: ["animar"], normalized: "transition" },
8373
8443
  on: { primary: "em", alternatives: ["ao"], normalized: "on" },
8374
8444
  trigger: { primary: "disparar", alternatives: ["ativar"], normalized: "trigger" },
@@ -8387,13 +8457,13 @@ var init_portuguese = __esm({
8387
8457
  alternatives: ["ponto-interrupcao"],
8388
8458
  normalized: "breakpoint"
8389
8459
  },
8390
- go: { primary: "ir", alternatives: ["navegar"], normalized: "go" },
8460
+ go: { primary: "ir", alternatives: ["navegar", "v\xE1"], normalized: "go" },
8391
8461
  scroll: { primary: "rolar", alternatives: ["scroll"], normalized: "scroll" },
8392
8462
  push: { primary: "empurrar", alternatives: ["push"], normalized: "push" },
8393
8463
  replace: { primary: "repor", alternatives: ["recolocar"], normalized: "replace" },
8394
8464
  process: { primary: "processar", normalized: "process" },
8395
8465
  wait: { primary: "esperar", alternatives: ["aguardar"], normalized: "wait" },
8396
- fetch: { primary: "buscar", normalized: "fetch" },
8466
+ fetch: { primary: "buscar", alternatives: ["busque"], normalized: "fetch" },
8397
8467
  settle: { primary: "estabilizar", normalized: "settle" },
8398
8468
  if: { primary: "se", normalized: "if" },
8399
8469
  // salvo — single token ('salvo se' = unless). a_menos kept as an
@@ -11464,8 +11534,53 @@ var init_command_schemas = __esm({
11464
11534
  default: { type: "reference", value: "me" },
11465
11535
  svoPosition: 2,
11466
11536
  sovPosition: 1,
11467
- markerOverride: { en: "to" }
11468
- // "add .class to #element"
11537
+ // `add` is directional, but every profile's `destination` marker is
11538
+ // LOCATIVE (en on, es en, ar على, zh 在, fr sur, de auf, pt em) because
11539
+ // it also serves `toggle`/`show`. Without a per-language override the
11540
+ // rendered text said "add .class ON #element" in every language but
11541
+ // English — the gap lokascript-learn corrects with 6 of its 16 override
11542
+ // entries. ja に / ko 에 / tr e are already directional, so they keep
11543
+ // the profile default.
11544
+ //
11545
+ // Tier B (2.9): he/id/it/sw were the remaining locatives that this
11546
+ // language actually distinguishes.
11547
+ // he — `על` is "ON"; Hebrew adds with the allative `אל` (`ל` is a bound
11548
+ // prefix, so it cannot stand as a separate marker token).
11549
+ // id — `pada` is "at/on"; `ke` is the directional, and it is what the
11550
+ // i18n corpus already renders for every id destination.
11551
+ // it — `in` is locative; Italian adds with `a` (`aggiungere a`).
11552
+ // sw — `kwenye` is not merely locative, it is sw's EVENT keyword
11553
+ // (`on: 'kwenye'` in the dictionary), so reusing it as a
11554
+ // destination marker collides. `kwa` is the corpus rendering.
11555
+ // hi `में` / ru+uk `в` / th `ใน` / vi `vào` are already the right
11556
+ // container-directional for "add to", and keep the profile default.
11557
+ markerOverride: {
11558
+ en: "to",
11559
+ es: "a",
11560
+ ar: "\u0625\u0644\u0649",
11561
+ zh: "\u5230",
11562
+ fr: "\xE0",
11563
+ de: "zu",
11564
+ pt: "a",
11565
+ he: "\u05D0\u05DC",
11566
+ id: "ke",
11567
+ it: "a",
11568
+ sw: "kwa"
11569
+ },
11570
+ // Each language's previous primary marker (and its alternates) still
11571
+ // parses, so source written against ≤2.8 keeps working.
11572
+ markerLegacy: {
11573
+ es: ["en", "sobre", "hacia"],
11574
+ ar: ["\u0639\u0644\u0649", "\u0641\u064A", "\u0628"],
11575
+ zh: ["\u5728", "\u4E8E"],
11576
+ fr: ["sur", "dans"],
11577
+ de: ["auf", "in"],
11578
+ pt: ["em", "para"],
11579
+ he: ["\u05E2\u05DC", "\u05D1", "\u05DC"],
11580
+ id: ["pada", "di"],
11581
+ it: ["in", "su"],
11582
+ sw: ["kwenye"]
11583
+ }
11469
11584
  }
11470
11585
  ],
11471
11586
  // Runtime error documentation
@@ -11555,12 +11670,39 @@ var init_command_schemas = __esm({
11555
11670
  svoPosition: 2,
11556
11671
  sovPosition: 2,
11557
11672
  // SOV: destination comes second (に/에/a marker)
11558
- markerOverride: { en: "into" },
11559
- // "put 'hello' into #output"
11673
+ // "put 'hello' into #output" — directional, so the same locative-default
11674
+ // correction as `add`. es `en` and pt `em` are already right for "into",
11675
+ // as are ja に / ko 에 / tr e; only ar/zh/fr/de need an override.
11676
+ //
11677
+ // Tier B (2.9): `put` is ILLATIVE, so it diverges from `add` where the
11678
+ // two senses differ. he takes `ב` ("in/into" — `שים ב`), NOT the allative
11679
+ // `אל` that `add`/`go` take. it keeps its locative `in` (`mettere in`) —
11680
+ // it is `add`/`go` that needed `a`. id/sw change for the same reason as
11681
+ // `add` (directional / event-keyword collision). hi `में`, ru+uk `в`,
11682
+ // th `ใน` and vi `vào` are all already the illative.
11683
+ markerOverride: {
11684
+ en: "into",
11685
+ ar: "\u0641\u064A",
11686
+ zh: "\u5230",
11687
+ fr: "dans",
11688
+ de: "in",
11689
+ he: "\u05D1",
11690
+ id: "ke",
11691
+ sw: "kwa"
11692
+ },
11560
11693
  // `before` / `after` are alternate position markers; the matched marker
11561
11694
  // is recorded as a literal in the `method` role (a derived role with no
11562
11695
  // surface form of its own — populated by schema-driven role inference).
11563
11696
  markerVariants: { en: ["before", "after"] },
11697
+ markerLegacy: {
11698
+ ar: ["\u0639\u0644\u0649", "\u0625\u0644\u0649", "\u0628"],
11699
+ zh: ["\u5728", "\u4E8E"],
11700
+ fr: ["sur", "\xE0"],
11701
+ de: ["auf", "zu"],
11702
+ he: ["\u05E2\u05DC", "\u05D0\u05DC", "\u05DC"],
11703
+ id: ["pada", "di"],
11704
+ sw: ["kwenye"]
11705
+ },
11564
11706
  methodCarrier: "method"
11565
11707
  }
11566
11708
  ],
@@ -11695,8 +11837,11 @@ var init_command_schemas = __esm({
11695
11837
  // ending in a vowel (`doğru ya` = "true" in set-attribute). markerOverride
11696
11838
  // is a single string, so the generated tr set patterns carried only `e`
11697
11839
  // and set-attribute fell to the role-scrambling generic SOV extraction.
11698
- // markerVariants supplies the allomorphs the SOV two-role generators merge
11699
- // in as marker alternatives. See STRUCTURAL_ARCS_ROADMAP.md (tr set-attribute).
11840
+ // markerVariants supplies the allomorphs, merged in as marker alternatives.
11841
+ // Until 2026-07-25 only the SOV two-role generators merged them, so this
11842
+ // worked ONLY inside an event handler: `@disabled i doğru ya ayarla` did
11843
+ // not parse as a bare command while `tıklama da @disabled i doğru ya
11844
+ // ayarla` did. See STRUCTURAL_ARCS_ROADMAP.md (tr set-attribute).
11700
11845
  markerVariants: {
11701
11846
  tr: ["e", "a", "ye", "ya"]
11702
11847
  }
@@ -12628,17 +12773,100 @@ var init_command_schemas = __esm({
12628
12773
  expectedTypes: ["literal", "expression"],
12629
12774
  svoPosition: 1,
12630
12775
  sovPosition: 1,
12631
- markerOverride: { en: "to" },
12632
- // "go to /page" (parsing)
12633
- renderOverride: { en: "" },
12634
- // "go /page" (rendering — no preposition)
12776
+ // "go to /page" (parsing). Directional, so the same locative-default
12777
+ // correction as `add`/`put`.
12778
+ //
12779
+ // Tier B (2.9): `go` is pure ALLATIVE — motion toward a target — so it
12780
+ // needs the directional in more languages than `add`/`put` do, including
12781
+ // ones where a container-locative was fine for those two.
12782
+ // he — `אל` ("toward"), as `add`; `לך על url` read "go ON url".
12783
+ // hi — `पर`: Hindi navigates to a page with `पर जाएं`; `में` is
12784
+ // "go INTO", which is entering a place, not opening a URL.
12785
+ // id — `ke`, as `add`.
12786
+ // it — `a`: `andare a` for a specific target (`andare in` is for
12787
+ // regions — `andare in Italia`).
12788
+ // ru/uk — `на`: `перейти на сторінку` is the navigation idiom; `в`
12789
+ // ("into") is right for `add`/`put` but not for opening a page.
12790
+ // sw — `kwa`, as `add`.
12791
+ // th is NOT here — it renders bare, with zh and vi; see below.
12792
+ markerOverride: {
12793
+ en: "to",
12794
+ es: "a",
12795
+ ar: "\u0625\u0644\u0649",
12796
+ fr: "\xE0",
12797
+ de: "zu",
12798
+ pt: "para",
12799
+ he: "\u05D0\u05DC",
12800
+ hi: "\u092A\u0930",
12801
+ id: "ke",
12802
+ it: "a",
12803
+ ru: "\u043D\u0430",
12804
+ sw: "kwa",
12805
+ uk: "\u043D\u0430"
12806
+ },
12807
+ // "go /page" (rendering — no preposition).
12808
+ //
12809
+ // zh, vi and th render BARE.
12810
+ //
12811
+ // zh and vi because their `go` keyword already encodes the direction, so
12812
+ // any destination marker is a second one: zh `前往` is "proceed-to"
12813
+ // (`前往 到 url` = "proceed-to to url") and vi `đi đến` is literally
12814
+ // "go to" (`đi đến vào url` = "go-to into url"). Both are corrected in
12815
+ // the i18n corpus in the same change
12816
+ // (`patterns-reference/scripts/fix-translations.sql`).
12817
+ //
12818
+ // th because Thai motion verbs take a BARE destination — `ไปบ้าน`
12819
+ // ("go home"), `ไปโรงเรียน` ("go school") — so `ไป url` is the idiomatic
12820
+ // form. The profile default rendered `ไป ใน url` ("go IN url"), which is
12821
+ // what needed fixing; the obvious replacement `ยัง` (giving the formal
12822
+ // `ไปยัง`) is rejected because `ยัง` is also the very common adverb
12823
+ // "still/yet", and the V4 vocab gate correctly refuses to classify it as
12824
+ // a particle — promoting it would mis-tokenize ordinary Thai.
12825
+ //
12826
+ // Parsing is unaffected for all three: none has a `markerOverride`, so
12827
+ // each stays on the profile-default branch and keeps accepting its old
12828
+ // markers (th `ใน` / `ไปยัง`) from the profile itself.
12829
+ renderOverride: { en: "", zh: "", vi: "", th: "" },
12635
12830
  // `go back` renders the destination bare in en (history nav has no `to`),
12636
12831
  // and he/zh render it with their PATIENT marker (לך את back / 前往 把 back)
12637
12832
  // while go-url keeps the destination marker (לך על url / 前往 到 url) —
12638
12833
  // the corpus is ground truth, so en's `to` is optional and he/zh accept
12639
12834
  // the patient particle as a destination-marker alternative, scoped to go.
12640
- markerOptional: { en: true },
12641
- markerVariants: { he: ["\u05D0\u05EA"], zh: ["\u628A"] }
12835
+ // The render side drops the preposition for these four, so the parse
12836
+ // side cannot require it: `go /page`, `前往 url`, `đi đến url`, `ไป url`
12837
+ // must parse alongside the marked forms the profile still accepts.
12838
+ markerOptional: { en: true, zh: true, vi: true, th: true },
12839
+ // zh renders `前往 把 back` with its PATIENT particle before go's
12840
+ // destination — a synonym here, not a distinct shape, so it is accepted as
12841
+ // a marker alternative scoped to go. he's `את` is the same thing and sits
12842
+ // in `markerLegacy` below: it moved there in #763 because the two fields
12843
+ // were then read by DIFFERENT branches, so leaving it here silently
12844
+ // stopped `לך את back` parsing the moment he gained a `markerOverride`.
12845
+ // Both fields now merge on both branches (`schemaMarkerAlternatives`), so
12846
+ // that trap is gone and the split is historical.
12847
+ markerVariants: { zh: ["\u628A"] },
12848
+ markerLegacy: {
12849
+ es: ["en", "sobre", "hacia"],
12850
+ ar: ["\u0639\u0644\u0649", "\u0641\u064A", "\u0628"],
12851
+ fr: ["sur", "dans"],
12852
+ de: ["auf", "in"],
12853
+ pt: ["em", "a"],
12854
+ // `את` is he's PATIENT particle, which the transformer renders before
12855
+ // go's destination in `go back` (`לך את back`) — a parse-only synonym
12856
+ // here, never rendered, which is exactly what markerLegacy is for.
12857
+ he: ["\u05E2\u05DC", "\u05D1", "\u05DC", "\u05D0\u05EA"],
12858
+ hi: ["\u092E\u0947\u0902"],
12859
+ id: ["pada", "di"],
12860
+ it: ["in", "su"],
12861
+ ru: ["\u0432", "\u043A"],
12862
+ sw: ["kwenye"],
12863
+ uk: ["\u0432", "\u0434\u043E"]
12864
+ // zh, vi and th are NOT listed: none has a markerOverride, so all three
12865
+ // stay on the profile-default branch and keep accepting their old
12866
+ // markers from the profile itself. Only their RENDERING changed.
12867
+ // Listing them here would be dead config — markerLegacy is read ONLY by
12868
+ // the override branch.
12869
+ }
12642
12870
  }
12643
12871
  ],
12644
12872
  // `go to url "/page"` — without this variant the destination captures the
@@ -13640,7 +13868,7 @@ var init_command_schemas = __esm({
13640
13868
  roles: []
13641
13869
  }
13642
13870
  };
13643
- if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
13871
+ if (typeof process !== "undefined" && process.env.LOKASCRIPT_SCHEMA_VALIDATION === "1") {
13644
13872
  Promise.resolve().then(() => (init_schema_validator(), schema_validator_exports)).then(({ validateAllSchemas: validateAllSchemas2, formatValidationResults: formatValidationResults2 }) => {
13645
13873
  const validations = validateAllSchemas2(commandSchemas);
13646
13874
  if (validations.size > 0) {
@@ -20197,12 +20425,16 @@ var init_spanish_keyword = __esm({
20197
20425
  const keywordEntry = this.context.lookupKeyword(word);
20198
20426
  const normalized2 = keywordEntry && keywordEntry.normalized !== keywordEntry.native ? keywordEntry.normalized : void 0;
20199
20427
  let morphNormalized;
20428
+ let morphStem;
20429
+ let morphConfidence;
20200
20430
  if (!keywordEntry && this.context.normalizer) {
20201
20431
  const morphResult = this.context.normalizer.normalize(word);
20202
20432
  if (morphResult.stem !== word && morphResult.confidence >= 0.7) {
20203
20433
  const stemEntry = this.context.lookupKeyword(morphResult.stem);
20204
20434
  if (stemEntry) {
20205
20435
  morphNormalized = stemEntry.normalized;
20436
+ morphStem = morphResult.stem;
20437
+ morphConfidence = morphResult.confidence;
20206
20438
  }
20207
20439
  }
20208
20440
  }
@@ -20211,6 +20443,8 @@ var init_spanish_keyword = __esm({
20211
20443
  length: pos2 - position,
20212
20444
  metadata: {
20213
20445
  normalized: normalized2 || morphNormalized,
20446
+ stem: morphStem,
20447
+ stemConfidence: morphConfidence,
20214
20448
  isPreposition
20215
20449
  }
20216
20450
  };
@@ -21330,12 +21564,16 @@ var init_portuguese_keyword = __esm({
21330
21564
  const keywordEntry = this.context.lookupKeyword(lower);
21331
21565
  const normalized2 = keywordEntry && keywordEntry.normalized !== keywordEntry.native ? keywordEntry.normalized : void 0;
21332
21566
  let morphNormalized;
21567
+ let morphStem;
21568
+ let morphConfidence;
21333
21569
  if (!keywordEntry && this.context.normalizer) {
21334
21570
  const morphResult = this.context.normalizer.normalize(word);
21335
21571
  if (morphResult.stem !== word && morphResult.confidence >= 0.7) {
21336
21572
  const stemEntry = this.context.lookupKeyword(morphResult.stem);
21337
21573
  if (stemEntry) {
21338
21574
  morphNormalized = stemEntry.normalized;
21575
+ morphStem = morphResult.stem;
21576
+ morphConfidence = morphResult.confidence;
21339
21577
  }
21340
21578
  }
21341
21579
  }
@@ -21344,6 +21582,8 @@ var init_portuguese_keyword = __esm({
21344
21582
  length: pos2 - position,
21345
21583
  metadata: {
21346
21584
  normalized: normalized2 || morphNormalized,
21585
+ stem: morphStem,
21586
+ stemConfidence: morphConfidence,
21347
21587
  isPreposition
21348
21588
  }
21349
21589
  };
@@ -21823,12 +22063,16 @@ var init_french_keyword = __esm({
21823
22063
  const keywordEntry = this.context.lookupKeyword(lower);
21824
22064
  const normalized2 = keywordEntry && keywordEntry.normalized !== keywordEntry.native ? keywordEntry.normalized : void 0;
21825
22065
  let morphNormalized;
22066
+ let morphStem;
22067
+ let morphConfidence;
21826
22068
  if (!keywordEntry && this.context.normalizer) {
21827
22069
  const morphResult = this.context.normalizer.normalize(word);
21828
22070
  if (morphResult.stem !== word && morphResult.confidence >= 0.7) {
21829
22071
  const stemEntry = this.context.lookupKeyword(morphResult.stem);
21830
22072
  if (stemEntry) {
21831
22073
  morphNormalized = stemEntry.normalized;
22074
+ morphStem = morphResult.stem;
22075
+ morphConfidence = morphResult.confidence;
21832
22076
  }
21833
22077
  }
21834
22078
  }
@@ -21837,6 +22081,8 @@ var init_french_keyword = __esm({
21837
22081
  length: pos2 - position,
21838
22082
  metadata: {
21839
22083
  normalized: normalized2 || morphNormalized,
22084
+ stem: morphStem,
22085
+ stemConfidence: morphConfidence,
21840
22086
  isPreposition
21841
22087
  }
21842
22088
  };
@@ -28419,8 +28665,10 @@ function resolveMarkerForRole(roleSpec, profile) {
28419
28665
  const overrideMarker = roleSpec.markerOverride?.[profile.code];
28420
28666
  const defaultMarker = profile.roleMarkers[roleSpec.role];
28421
28667
  if (overrideMarker !== void 0) {
28668
+ const alternatives = legacyMarkerAlternatives(roleSpec, profile.code, overrideMarker);
28422
28669
  return {
28423
28670
  primary: overrideMarker,
28671
+ ...alternatives && { alternatives },
28424
28672
  position: defaultMarker?.position ?? "before",
28425
28673
  isOverride: true
28426
28674
  };
@@ -28438,6 +28686,18 @@ function resolveMarkerForRole(roleSpec, profile) {
28438
28686
  }
28439
28687
  return null;
28440
28688
  }
28689
+ function legacyMarkerAlternatives(roleSpec, languageCode, overrideMarker) {
28690
+ const legacy = roleSpec.markerLegacy?.[languageCode];
28691
+ if (!legacy?.length) return void 0;
28692
+ const alternatives = [...new Set(legacy)].filter((a) => a && a !== overrideMarker);
28693
+ return alternatives.length ? alternatives : void 0;
28694
+ }
28695
+ function schemaMarkerAlternatives(roleSpec, languageCode, marker) {
28696
+ const legacy = roleSpec.markerLegacy?.[languageCode] ?? [];
28697
+ const variants = roleSpec.methodCarrier ? [] : roleSpec.markerVariants?.[languageCode] ?? [];
28698
+ const alternatives = [.../* @__PURE__ */ new Set([...legacy, ...variants])].filter((a) => a && a !== marker);
28699
+ return alternatives.length ? alternatives : void 0;
28700
+ }
28441
28701
  var init_marker_resolution = __esm({
28442
28702
  "src/parser/utils/marker-resolution.ts"() {
28443
28703
  }
@@ -28449,20 +28709,17 @@ function resolveRoleMarker(roleSpec, profile) {
28449
28709
  let alternatives;
28450
28710
  if (roleSpec.markerOverride && roleSpec.markerOverride[profile.code] !== void 0) {
28451
28711
  marker = roleSpec.markerOverride[profile.code];
28712
+ alternatives = marker ? schemaMarkerAlternatives(roleSpec, profile.code, marker) : void 0;
28452
28713
  } else {
28453
28714
  const roleMarker = profile.roleMarkers[roleSpec.role];
28454
28715
  if (roleMarker) {
28455
28716
  marker = roleMarker.primary;
28456
- alternatives = roleMarker.alternatives ? [...roleMarker.alternatives] : void 0;
28457
- }
28458
- }
28459
- const variants = roleSpec.markerVariants?.[profile.code];
28460
- if (variants && variants.length > 0) {
28461
- const merged = alternatives ? [...alternatives] : [];
28462
- for (const v of variants) {
28463
- if (v !== marker && !merged.includes(v)) merged.push(v);
28717
+ const schemaAlts = schemaMarkerAlternatives(roleSpec, profile.code, marker) ?? [];
28718
+ const merged = [.../* @__PURE__ */ new Set([...roleMarker.alternatives ?? [], ...schemaAlts])].filter(
28719
+ (a) => a !== marker
28720
+ );
28721
+ alternatives = merged.length ? merged : void 0;
28464
28722
  }
28465
- alternatives = merged;
28466
28723
  }
28467
28724
  return { marker, alternatives };
28468
28725
  }
@@ -28931,10 +29188,18 @@ function generateSOVTwoRoleDestFirstEventHandlerPattern(commandSchema, profile,
28931
29188
  var init_event_handlers_sov = __esm({
28932
29189
  "src/generators/event-handlers-sov.ts"() {
28933
29190
  init_command_schemas();
29191
+ init_marker_resolution();
28934
29192
  }
28935
29193
  });
28936
29194
 
28937
29195
  // src/generators/event-handlers-vso.ts
29196
+ function mergeSchemaAlternatives(roleSpec, profile, roleMarker) {
29197
+ const schemaAlts = schemaMarkerAlternatives(roleSpec, profile.code, roleMarker.primary) ?? [];
29198
+ const merged = [.../* @__PURE__ */ new Set([...roleMarker.alternatives ?? [], ...schemaAlts])].filter(
29199
+ (a) => a !== roleMarker.primary
29200
+ );
29201
+ return merged.length ? merged : void 0;
29202
+ }
28938
29203
  function generateVSOEventHandlerPattern(commandSchema, profile, keyword, eventMarker, config) {
28939
29204
  const tokens = [];
28940
29205
  if (eventMarker.position === "before") {
@@ -29047,11 +29312,12 @@ function generateVSOVerbFirstTwoRoleEventHandlerPattern(commandSchema, profile,
29047
29312
  let markerAlternatives;
29048
29313
  if (roleSpec.markerOverride && roleSpec.markerOverride[profile.code] !== void 0) {
29049
29314
  marker = roleSpec.markerOverride[profile.code];
29315
+ markerAlternatives = marker ? schemaMarkerAlternatives(roleSpec, profile.code, marker) : void 0;
29050
29316
  } else {
29051
29317
  const roleMarker = profile.roleMarkers[roleSpec.role];
29052
29318
  if (roleMarker) {
29053
29319
  marker = roleMarker.primary;
29054
- markerAlternatives = roleMarker.alternatives;
29320
+ markerAlternatives = mergeSchemaAlternatives(roleSpec, profile, roleMarker);
29055
29321
  }
29056
29322
  }
29057
29323
  if (marker) {
@@ -29104,11 +29370,12 @@ function generateVSOTwoRoleEventHandlerPattern(commandSchema, profile, keyword,
29104
29370
  let markerAlternatives;
29105
29371
  if (roleSpec.markerOverride && roleSpec.markerOverride[profile.code] !== void 0) {
29106
29372
  marker = roleSpec.markerOverride[profile.code];
29373
+ markerAlternatives = marker ? schemaMarkerAlternatives(roleSpec, profile.code, marker) : void 0;
29107
29374
  } else {
29108
29375
  const roleMarker = profile.roleMarkers[roleSpec.role];
29109
29376
  if (roleMarker) {
29110
29377
  marker = roleMarker.primary;
29111
- markerAlternatives = roleMarker.alternatives;
29378
+ markerAlternatives = mergeSchemaAlternatives(roleSpec, profile, roleMarker);
29112
29379
  }
29113
29380
  }
29114
29381
  if (marker) {
@@ -29241,6 +29508,7 @@ var init_event_handlers_vso = __esm({
29241
29508
  "src/generators/event-handlers-vso.ts"() {
29242
29509
  init_command_schemas();
29243
29510
  init_event_handlers_sov();
29511
+ init_marker_resolution();
29244
29512
  }
29245
29513
  });
29246
29514
  function generatePattern(schema, profile, config = defaultConfig) {
@@ -29613,7 +29881,13 @@ function buildRoleToken(roleSpec, profile) {
29613
29881
  const position = defaultMarker?.position ?? "before";
29614
29882
  const optionalMarker = roleSpec.markerOptional?.[profile.code] === true;
29615
29883
  const pushWord = (word) => {
29616
- const literal = suppressMarker ? { type: "literal", value: word, renderSuppress: true } : { type: "literal", value: word };
29884
+ const alternatives = markerWords.length === 1 ? schemaMarkerAlternatives(roleSpec, profile.code, word) ?? [] : [];
29885
+ const literal = {
29886
+ type: "literal",
29887
+ value: word,
29888
+ ...alternatives.length ? { alternatives } : {},
29889
+ ...suppressMarker ? { renderSuppress: true } : {}
29890
+ };
29617
29891
  tokens.push(optionalMarker ? { type: "group", optional: true, tokens: [literal] } : literal);
29618
29892
  };
29619
29893
  if (position === "before") {
@@ -29624,10 +29898,12 @@ function buildRoleToken(roleSpec, profile) {
29624
29898
  for (const word of markerWords) pushWord(word);
29625
29899
  }
29626
29900
  } else if (defaultMarker) {
29627
- const variantAlts = roleSpec.markerVariants?.[profile.code] ?? [];
29628
29901
  const asMarker = () => {
29629
29902
  const alternatives = [
29630
- .../* @__PURE__ */ new Set([...defaultMarker.alternatives ?? [], ...variantAlts])
29903
+ .../* @__PURE__ */ new Set([
29904
+ ...defaultMarker.alternatives ?? [],
29905
+ ...schemaMarkerAlternatives(roleSpec, profile.code, defaultMarker.primary) ?? []
29906
+ ])
29631
29907
  ].filter((a) => a !== defaultMarker.primary);
29632
29908
  return {
29633
29909
  type: "literal",
@@ -29663,11 +29939,19 @@ function buildExtractionRules(schema, profile) {
29663
29939
  if (roleSpec.valuePrefixLiteral?.[profile.code]) {
29664
29940
  rules[roleSpec.role] = { marker: roleSpec.valuePrefixLiteral[profile.code] };
29665
29941
  } else if (overrideMarker !== void 0) {
29666
- rules[roleSpec.role] = overrideMarker ? { marker: overrideMarker } : {};
29942
+ if (!overrideMarker) {
29943
+ rules[roleSpec.role] = {};
29944
+ } else {
29945
+ const isSingleWord = !/\s/.test(overrideMarker.trim());
29946
+ const markerAlternatives = isSingleWord ? schemaMarkerAlternatives(roleSpec, profile.code, overrideMarker) ?? [] : [];
29947
+ rules[roleSpec.role] = markerAlternatives.length ? { marker: overrideMarker, markerAlternatives } : { marker: overrideMarker };
29948
+ }
29667
29949
  } else if (defaultMarker && defaultMarker.primary) {
29668
- const variantAlts = roleSpec.markerVariants?.[profile.code] ?? [];
29669
29950
  const markerAlternatives = [
29670
- .../* @__PURE__ */ new Set([...defaultMarker.alternatives ?? [], ...variantAlts])
29951
+ .../* @__PURE__ */ new Set([
29952
+ ...defaultMarker.alternatives ?? [],
29953
+ ...schemaMarkerAlternatives(roleSpec, profile.code, defaultMarker.primary) ?? []
29954
+ ])
29671
29955
  ].filter((a) => a !== defaultMarker.primary);
29672
29956
  rules[roleSpec.role] = markerAlternatives.length ? { marker: defaultMarker.primary, markerAlternatives } : { marker: defaultMarker.primary };
29673
29957
  } else {
@@ -31673,6 +31957,9 @@ init_command_schemas();
31673
31957
  // src/utils/confidence-calculator.ts
31674
31958
  init_registry();
31675
31959
 
31960
+ // src/explicit/converter.ts
31961
+ init_registry();
31962
+
31676
31963
  // src/cache/semantic-cache.ts
31677
31964
  var SemanticCache = class {
31678
31965
  constructor(config = {}) {