@hyperfixi/core 2.7.2 → 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.
Files changed (65) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/api/hyperscript-api.d.ts +1 -0
  3. package/dist/ast-utils/index.js +2227 -263
  4. package/dist/ast-utils/index.mjs +2227 -263
  5. package/dist/bundle-generator/index.d.ts +1 -1
  6. package/dist/bundle-generator/index.js +77 -68
  7. package/dist/bundle-generator/index.mjs +76 -69
  8. package/dist/bundle-generator/template-capabilities.d.ts +2 -0
  9. package/dist/chunks/bridge-D9JLmPkk.js +2 -0
  10. package/dist/chunks/browser-modular-CPiVQXM0.js +2 -0
  11. package/dist/chunks/{index-D2WUNSCR.js → index-6DUg7Qjm.js} +2 -2
  12. package/dist/commands/index.js +117 -5
  13. package/dist/commands/index.mjs +117 -5
  14. package/dist/compatibility/browser-modular.d.ts +2 -2
  15. package/dist/expressions/index.d.ts +1 -1
  16. package/dist/htmx/hcon.d.ts +9 -0
  17. package/dist/htmx/htmx-translator.d.ts +1 -0
  18. package/dist/hyperfixi-browser-classic-i18n.js +1 -1
  19. package/dist/hyperfixi-browser-minimal.js +1 -1
  20. package/dist/hyperfixi-browser-standard.js +1 -1
  21. package/dist/hyperfixi-browser.js +1 -1
  22. package/dist/hyperfixi-classic-i18n.js +1 -1
  23. package/dist/hyperfixi-hx-v4.js +1 -1
  24. package/dist/hyperfixi-hx.js +1 -1
  25. package/dist/hyperfixi-hybrid-complete.js +1 -1
  26. package/dist/hyperfixi-hybrid-hx.js +1 -1
  27. package/dist/hyperfixi-minimal.js +1 -1
  28. package/dist/hyperfixi-multilingual.js +1 -1
  29. package/dist/hyperfixi-standard.js +1 -1
  30. package/dist/hyperfixi.js +1 -1
  31. package/dist/hyperfixi.mjs +1 -1
  32. package/dist/index.js +5187 -727
  33. package/dist/index.min.js +1 -1
  34. package/dist/index.mjs +5187 -727
  35. package/dist/lokascript-browser-classic-i18n.js +1 -1
  36. package/dist/lokascript-browser-minimal.js +1 -1
  37. package/dist/lokascript-browser-standard.js +1 -1
  38. package/dist/lokascript-browser.js +1 -1
  39. package/dist/lokascript-hybrid-complete.js +1 -1
  40. package/dist/lokascript-hybrid-hx.js +1 -1
  41. package/dist/lokascript-multilingual.js +1 -1
  42. package/dist/lse/index.d.ts +7 -7
  43. package/dist/metadata.d.ts +1 -1
  44. package/dist/metadata.js +31 -14
  45. package/dist/metadata.mjs +31 -14
  46. package/dist/multilingual/index.js +8 -1
  47. package/dist/multilingual/index.mjs +8 -1
  48. package/dist/parser/command-parsers/animation-commands.d.ts +2 -2
  49. package/dist/parser/command-parsers/async-commands.d.ts +2 -2
  50. package/dist/parser/command-parsers/dom-commands.d.ts +5 -5
  51. package/dist/parser/command-parsers/navigation-commands.d.ts +4 -0
  52. package/dist/parser/command-parsers/utility-commands.d.ts +2 -1
  53. package/dist/parser/command-parsers/variable-commands.d.ts +2 -2
  54. package/dist/parser/full-parser.js +117 -5
  55. package/dist/parser/full-parser.mjs +117 -5
  56. package/dist/parser/semantic-integration.d.ts +1 -0
  57. package/dist/performance/integration.d.ts +1 -1
  58. package/dist/registry/index.js +117 -5
  59. package/dist/registry/index.mjs +117 -5
  60. package/package.json +14 -20
  61. package/dist/chunks/bridge-DuveK8T4.js +0 -2
  62. package/dist/chunks/browser-modular-DW4nC6lH.js +0 -2
  63. package/dist/compatibility/browser-bundle-animation-generated.d.ts +0 -16
  64. package/dist/compatibility/browser-bundle-forms-generated.d.ts +0 -16
  65. package/dist/compatibility/browser-bundle-minimal-generated.d.ts +0 -16
@@ -3756,6 +3756,9 @@ function isQuote(char) {
3756
3756
  function isDigit(char) {
3757
3757
  return /\d/.test(char);
3758
3758
  }
3759
+ function stripOptionalDiacritics(word) {
3760
+ return word.replace(/[ً-ْٰ]/g, "");
3761
+ }
3759
3762
  function isAsciiLetter(char) {
3760
3763
  return /[a-zA-Z]/.test(char);
3761
3764
  }
@@ -4262,7 +4265,39 @@ var _BaseTokenizer = class _BaseTokenizer {
4262
4265
  pos++;
4263
4266
  }
4264
4267
  }
4265
- return new TokenStreamImpl(tokens, this.language);
4268
+ return new TokenStreamImpl(this.mergeColonQualifiedNames(tokens), this.language);
4269
+ }
4270
+ /**
4271
+ * Fuse `name` + `:qualifier` into ONE identifier (`draggable:start`).
4272
+ *
4273
+ * `:name` is hyperscript's local-variable sigil, but a colon IMMEDIATELY
4274
+ * preceded by an identifier is a qualifier (custom event namespace), not a
4275
+ * sigil. The English tokenizer already merges these inside
4276
+ * EnglishKeywordExtractor; this post-pass gives the other 23 languages the
4277
+ * same stream. Strict position adjacency is the discriminator: whitespace
4278
+ * between the tokens (`trigger :start`) breaks `end === start`, so a spaced
4279
+ * local-variable reference survives untouched.
4280
+ *
4281
+ * Self-gating for non-hyperscript tokenizers (domain DSLs): their extractor
4282
+ * sets tokenize `:` as bare punctuation (length 1), which never matches
4283
+ * COLON_QUALIFIER, so this pass is a no-op for them.
4284
+ */
4285
+ mergeColonQualifiedNames(tokens) {
4286
+ const out = [];
4287
+ for (const tok of tokens) {
4288
+ const prev = out[out.length - 1];
4289
+ if (prev && _BaseTokenizer.ASCII_WORD.test(prev.value) && _BaseTokenizer.COLON_QUALIFIER.test(tok.value) && prev.position.end === tok.position.start) {
4290
+ const merged = prev.value + tok.value;
4291
+ out[out.length - 1] = createToken(
4292
+ merged,
4293
+ this.classifyToken(merged),
4294
+ createPosition(prev.position.start, tok.position.end)
4295
+ );
4296
+ continue;
4297
+ }
4298
+ out.push(tok);
4299
+ }
4300
+ return out;
4266
4301
  }
4267
4302
  /**
4268
4303
  * Classify an unknown character when no extractor matches.
@@ -4395,7 +4430,7 @@ var _BaseTokenizer = class _BaseTokenizer {
4395
4430
  * @returns Word without diacritics
4396
4431
  */
4397
4432
  removeDiacritics(word) {
4398
- return word.replace(/[\u064B-\u0652\u0670]/g, "");
4433
+ return stripOptionalDiacritics(word);
4399
4434
  }
4400
4435
  /**
4401
4436
  * Try to match a keyword from profile at the current position.
@@ -4486,24 +4521,40 @@ var _BaseTokenizer = class _BaseTokenizer {
4486
4521
  });
4487
4522
  }
4488
4523
  /**
4489
- * Look up a keyword by native word (case-insensitive).
4524
+ * Look up a keyword by native word (case-insensitive, diacritic-insensitive).
4490
4525
  * O(1) lookup using the keyword map.
4491
4526
  *
4527
+ * The map is INDEXED both with and without diacritics (see
4528
+ * `initializeKeywordsFromProfile`), so a stripped QUERY is the other half of
4529
+ * that: it lets a surface form carrying harakat the profile does not happen to
4530
+ * spell still find its entry. Only consulted after the exact lookup misses, so
4531
+ * every previously-matching word resolves byte-identically.
4532
+ *
4533
+ * Half-implementing this — indexing stripped but querying exact — is what made
4534
+ * diacritized `بَدِّل` (toggle) tokenize as `kind=particle normalized=with`:
4535
+ * `isKeyword` returned false, so the guard in `ArabicProcliticExtractor` that
4536
+ * exists to prevent exactly that handed the word on, and the single-char `ب`
4537
+ * bi- proclitic claimed it. A wrong CONCEPT, not a failed parse.
4538
+ *
4492
4539
  * @param native - Native word to look up
4493
4540
  * @returns KeywordEntry if found, undefined otherwise
4494
4541
  */
4495
4542
  lookupKeyword(native) {
4496
- return this.profileKeywordMap.get(native.toLowerCase());
4543
+ const exact = this.profileKeywordMap.get(native.toLowerCase());
4544
+ if (exact) return exact;
4545
+ const stripped = this.removeDiacritics(native);
4546
+ if (stripped === native) return void 0;
4547
+ return this.profileKeywordMap.get(stripped.toLowerCase());
4497
4548
  }
4498
4549
  /**
4499
- * Check if a word is a known keyword (case-insensitive).
4500
- * O(1) lookup using the keyword map.
4550
+ * Check if a word is a known keyword (case-insensitive, diacritic-insensitive).
4551
+ * O(1) lookup using the keyword map. See {@link lookupKeyword}.
4501
4552
  *
4502
4553
  * @param native - Native word to check
4503
4554
  * @returns true if the word is a keyword
4504
4555
  */
4505
4556
  isKeyword(native) {
4506
- return this.profileKeywordMap.has(native.toLowerCase());
4557
+ return this.lookupKeyword(native) !== void 0;
4507
4558
  }
4508
4559
  /**
4509
4560
  * Set the morphological normalizer for this tokenizer.
@@ -4768,6 +4819,14 @@ var _BaseTokenizer = class _BaseTokenizer {
4768
4819
  return null;
4769
4820
  }
4770
4821
  };
4822
+ /**
4823
+ * ASCII word of the shape the English word-walker produces. Excludes `:`, so a
4824
+ * token that already carries a qualifier never merges again — `a:b:c` yields
4825
+ * `a:b` + `:c`, byte-matching the English extractor's single-segment merge.
4826
+ */
4827
+ _BaseTokenizer.ASCII_WORD = /^[A-Za-z_][A-Za-z0-9_]*$/;
4828
+ /** `:name` — only a variable-ref-style extractor ever emits this token shape. */
4829
+ _BaseTokenizer.COLON_QUALIFIER = /^:[A-Za-z_][A-Za-z0-9_]*$/;
4771
4830
  /**
4772
4831
  * Configuration for native language time units.
4773
4832
  * Maps patterns to their standard suffix (ms, s, m, h).
@@ -4991,8 +5050,11 @@ var init_arabic = __esm({
4991
5050
  result: "\u0627\u0644\u0646\u062A\u064A\u062C\u0629",
4992
5051
  event: "\u0627\u0644\u062D\u062F\u062B",
4993
5052
  target: "\u0627\u0644\u0647\u062F\u0641",
4994
- body: "\u062C\u0633\u0645"
5053
+ body: "\u062C\u0633\u0645",
4995
5054
  // matches the i18n dict's emitted body word (corpus-canonical, parser must recognize it)
5055
+ document: "\u0648\u062B\u064A\u0642\u0629",
5056
+ window: "\u0646\u0627\u0641\u0630\u0629",
5057
+ detail: "\u062A\u0641\u0627\u0635\u064A\u0644"
4996
5058
  },
4997
5059
  possessive: {
4998
5060
  marker: "",
@@ -5101,6 +5163,30 @@ var init_arabic = __esm({
5101
5163
  return: { primary: "\u0627\u0631\u062C\u0639", alternatives: ["\u0639\u064F\u062F"], normalized: "return" },
5102
5164
  then: { primary: "\u062B\u0645", alternatives: ["\u0628\u0639\u062F\u0647\u0627", "\u062B\u0645\u0651"], normalized: "then" },
5103
5165
  and: { primary: "\u0648\u0623\u064A\u0636\u0627\u064B", alternatives: ["\u0623\u064A\u0636\u0627\u064B"], normalized: "and" },
5166
+ // Comparison operator (`target matches .x`). Deferred by the Phase 2 `matches`
5167
+ // slice because ar's operand ALSO leaked (`references.target` carried الهدف while
5168
+ // the dict emits هدف), and registering the operator without its operand is worse
5169
+ // than neither: modal-close-backdrop ar passed R2 only BY ACCIDENT — the unparsed
5170
+ // condition was dropped, so `hide` ran unconditionally and coincidentally matched
5171
+ // the en DOM effect. `matches` alone would parse the condition into a real
5172
+ // comparison whose operand هدف evaluates to undefined, stopping `hide` and
5173
+ // flipping R2 pass→fail at tolerance 0. Landing WITH the هدف EXTRAS entry
5174
+ // (arabic.ts tokenizer) renders `target matches .modal-backdrop`, byte-identical
5175
+ // to en. Not an ActionType and has no command schema, so no pattern is generated.
5176
+ matches: { primary: "\u064A\u0637\u0627\u0628\u0642", normalized: "matches" },
5177
+ // Existence operator (`if #modal exists`). Same seam as `matches`: without the
5178
+ // keyword the surface stays an identifier and leaks verbatim into the
5179
+ // condition's raw expression (if-exists). Neither an ActionType nor a command
5180
+ // schema, so no pattern is generated from it.
5181
+ exists: { primary: "\u0645\u0648\u062C\u0648\u062F", normalized: "exists" },
5182
+ // Negative-existence operator (`if no dragHandle set dragHandle to me`). Same
5183
+ // seam as `exists`: without the keyword the surface stays an identifier and
5184
+ // leaks verbatim into the condition's raw expression (behavior-draggable).
5185
+ // Neither an ActionType nor a command schema, so no pattern is generated from it.
5186
+ // Uses the dict's NATURAL spaced phrase `لا يوجد`, matched by the base
5187
+ // tokenizer's multi-word keyword walk (longest-phrase at a word boundary) —
5188
+ // the same mechanism hi `मेل खाता` uses. Does not collide with `not: 'ليس'`.
5189
+ no: { primary: "\u0644\u0627 \u064A\u0648\u062C\u062F", normalized: "no" },
5104
5190
  // آخر is deliberately ABSENT: it is the positional `last` keyword
5105
5191
  // (آخر <button/> في .modal — see pattern-matcher's positional handling).
5106
5192
  // Listing it as an end-alternative made parseBodyWithClauses chop every
@@ -5116,9 +5202,12 @@ var init_arabic = __esm({
5116
5202
  behavior: { primary: "\u0633\u0644\u0648\u0643", normalized: "behavior" },
5117
5203
  install: { primary: "\u062A\u062B\u0628\u064A\u062A", alternatives: ["\u062B\u0628\u0651\u062A"], normalized: "install" },
5118
5204
  // `قِس` is the imperative with the kasra diacritic; the i18n dict (and real
5119
- // Arabic prose) emits it undiacritized as `قس`, so list both — otherwise the
5120
- // generated `قس width`/`قس x` (behavior-draggable/resizable) parse to null and
5121
- // the whole `measure` command drops from the event-handler body (lossy).
5205
+ // Arabic prose) emits it undiacritized as `قس`. BOTH stay listed, and not
5206
+ // for the tokenizer's sake — keyword lookup is diacritic-insensitive now, so
5207
+ // either spelling resolves. It is the vocab gate's V1 check, which compares
5208
+ // the profile against the i18n DICTIONARY as strings: the dictionary says
5209
+ // `قس`, so dropping it here fails V1 (verified). Diacritic-insensitivity
5210
+ // would have to reach that comparison too before this pair can collapse.
5122
5211
  measure: { primary: "\u0642\u064A\u0627\u0633", alternatives: ["\u0642\u0650\u0633", "\u0642\u0633"], normalized: "measure" },
5123
5212
  beep: { primary: "\u0635\u0641\u0651\u0631", normalized: "beep" },
5124
5213
  break: { primary: "\u062A\u0648\u0642\u0641", normalized: "break" },
@@ -5304,6 +5393,11 @@ var init_bengali = __esm({
5304
5393
  return: { primary: "\u09AB\u09BF\u09B0\u09C1\u09A8", alternatives: ["\u09AB\u09C7\u09B0\u09A4 \u09A6\u09BF\u09A8"], normalized: "return" },
5305
5394
  then: { primary: "\u09A4\u09BE\u09B0\u09AA\u09B0", alternatives: ["\u09A4\u0996\u09A8"], normalized: "then" },
5306
5395
  and: { primary: "\u098F\u09AC\u0982", alternatives: [], normalized: "and" },
5396
+ // Copula (`if result is false`, `if my value is empty`). Without the keyword the
5397
+ // surface stays an identifier and leaks verbatim into the condition's raw
5398
+ // expression, which the core expression parser reads as English. Neither an
5399
+ // ActionType nor a command schema, so no pattern is generated from it.
5400
+ is: { primary: "\u09B9\u09AF\u09BC", normalized: "is" },
5307
5401
  end: { primary: "\u09B6\u09C7\u09B7", alternatives: ["\u09B8\u09AE\u09BE\u09AA\u09CD\u09A4"], normalized: "end" },
5308
5402
  // Advanced
5309
5403
  js: { primary: "\u099C\u09C7\u098F\u09B8", alternatives: ["js"], normalized: "js" },
@@ -5401,7 +5495,10 @@ var init_german = __esm({
5401
5495
  result: "Ergebnis",
5402
5496
  event: "Ereignis",
5403
5497
  target: "Ziel",
5404
- body: "K\xF6rper"
5498
+ body: "K\xF6rper",
5499
+ document: "dokument",
5500
+ window: "fenster",
5501
+ detail: "detail"
5405
5502
  },
5406
5503
  possessive: {
5407
5504
  marker: "",
@@ -5496,6 +5593,22 @@ var init_german = __esm({
5496
5593
  // Predicate keywords (conditionals) — mirrors the Spanish profile, the only
5497
5594
  // language that previously parsed `is empty`-style predicates.
5498
5595
  is: { primary: "ist", normalized: "is" },
5596
+ // Comparison operator (`target matches .x`). Without this keyword the surface
5597
+ // stays an identifier and leaks verbatim into the condition's raw expression,
5598
+ // which the core expression parser reads as English (modal-close-backdrop /
5599
+ // focus-trap drop their then-branch). Not an ActionType and has no command
5600
+ // schema, so no pattern is generated from it.
5601
+ matches: { primary: "passt", normalized: "matches" },
5602
+ // Existence operator (`if #modal exists`). Same seam as `matches`: without the
5603
+ // keyword the surface stays an identifier and leaks verbatim into the
5604
+ // condition's raw expression (if-exists). Neither an ActionType nor a command
5605
+ // schema, so no pattern is generated from it.
5606
+ exists: { primary: "existiert", normalized: "exists" },
5607
+ // Negative-existence operator (`if no dragHandle set dragHandle to me`). Same
5608
+ // seam as `exists`: without the keyword the surface stays an identifier and
5609
+ // leaks verbatim into the condition's raw expression (behavior-draggable).
5610
+ // Neither an ActionType nor a command schema, so no pattern is generated from it.
5611
+ no: { primary: "kein", normalized: "no" },
5499
5612
  end: { primary: "ende", alternatives: ["fertig"], normalized: "end" },
5500
5613
  js: { primary: "js", alternatives: ["javascript"], normalized: "js" },
5501
5614
  async: { primary: "asynchron", normalized: "async" },
@@ -5590,7 +5703,10 @@ var init_english = __esm({
5590
5703
  result: "result",
5591
5704
  event: "event",
5592
5705
  target: "target",
5593
- body: "body"
5706
+ body: "body",
5707
+ document: "document",
5708
+ window: "window",
5709
+ detail: "detail"
5594
5710
  },
5595
5711
  possessive: {
5596
5712
  marker: "'s",
@@ -5741,7 +5857,10 @@ var init_spanish = __esm({
5741
5857
  event: "evento",
5742
5858
  target: "objetivo",
5743
5859
  // destino is a synonym
5744
- body: "cuerpo"
5860
+ body: "cuerpo",
5861
+ document: "documento",
5862
+ window: "ventana",
5863
+ detail: "detalle"
5745
5864
  },
5746
5865
  possessive: {
5747
5866
  marker: "de",
@@ -5764,11 +5883,23 @@ var init_spanish = __esm({
5764
5883
  }
5765
5884
  },
5766
5885
  roleMarkers: {
5767
- destination: { primary: "en", alternatives: ["sobre", "a"], position: "before" },
5886
+ // `hacia` is the i18n grammar's optional destination render form ("towards");
5887
+ // without it here a rendered/user `hacia` clause silently dropped the
5888
+ // destination (add → default `me`, put → null parse). Vocab Batch 1 (V2+V4).
5889
+ destination: { primary: "en", alternatives: ["sobre", "a", "hacia"], position: "before" },
5768
5890
  source: { primary: "de", alternatives: ["desde"], position: "before" },
5769
5891
  patient: { primary: "", position: "before" },
5770
5892
  style: { primary: "con", position: "before" }
5771
5893
  },
5894
+ // Imperative command forms are accepted on INPUT only — `primary` stays the
5895
+ // dictionary form, so rendering is unchanged (see LanguageProfile.defaultVerbForm:
5896
+ // infinitive is the industry standard for UI localization). Hyperscript is a
5897
+ // command language, though, and a native speaker giving a command writes the
5898
+ // imperative, so the parser should read it.
5899
+ //
5900
+ // Only the IRREGULARS are listed. The regular ones reach their keyword through
5901
+ // the morphological normalizer's stem (see spanish-keyword.ts and siblings),
5902
+ // which also covers conjugations nobody enumerated here.
5772
5903
  keywords: {
5773
5904
  // Class/Attribute operations
5774
5905
  toggle: { primary: "alternar", alternatives: ["conmutar", "toggle"], normalized: "toggle" },
@@ -5788,19 +5919,23 @@ var init_spanish = __esm({
5788
5919
  swap: { primary: "intercambiar", alternatives: ["permutar"], normalized: "swap" },
5789
5920
  morph: { primary: "transformar", alternatives: ["convertir"], normalized: "morph" },
5790
5921
  // Variable operations
5791
- set: { primary: "establecer", alternatives: ["fijar", "definir"], normalized: "set" },
5792
- get: { primary: "obtener", alternatives: ["conseguir"], normalized: "get" },
5922
+ set: {
5923
+ primary: "establecer",
5924
+ alternatives: ["fijar", "definir", "establece"],
5925
+ normalized: "set"
5926
+ },
5927
+ get: { primary: "obtener", alternatives: ["conseguir", "obt\xE9n"], normalized: "get" },
5793
5928
  increment: { primary: "incrementar", alternatives: ["aumentar"], normalized: "increment" },
5794
5929
  decrement: { primary: "decrementar", alternatives: ["disminuir"], normalized: "decrement" },
5795
5930
  log: { primary: "registrar", alternatives: ["imprimir"], normalized: "log" },
5796
5931
  // Visibility
5797
- show: { primary: "mostrar", alternatives: ["ense\xF1ar"], normalized: "show" },
5932
+ show: { primary: "mostrar", alternatives: ["ense\xF1ar", "muestra"], normalized: "show" },
5798
5933
  hide: { primary: "ocultar", alternatives: ["esconder"], normalized: "hide" },
5799
5934
  transition: { primary: "transici\xF3n", alternatives: ["animar"], normalized: "transition" },
5800
5935
  // Events
5801
5936
  on: { primary: "en", alternatives: ["al"], normalized: "on" },
5802
5937
  trigger: { primary: "disparar", alternatives: ["activar"], normalized: "trigger" },
5803
- send: { primary: "enviar", normalized: "send" },
5938
+ send: { primary: "enviar", alternatives: ["env\xEDa"], normalized: "send" },
5804
5939
  // DOM focus
5805
5940
  focus: { primary: "enfocar", alternatives: ["enfoque"], normalized: "focus" },
5806
5941
  blur: { primary: "desenfocar", alternatives: ["desenfoque"], normalized: "blur" },
@@ -5837,7 +5972,7 @@ var init_spanish = __esm({
5837
5972
  mousedown: { primary: "rat\xF3nabajo", normalized: "mousedown" },
5838
5973
  mouseup: { primary: "rat\xF3narriba", normalized: "mouseup" },
5839
5974
  // Navigation
5840
- go: { primary: "ir", alternatives: ["navegar"], normalized: "go" },
5975
+ go: { primary: "ir", alternatives: ["navegar", "ve"], normalized: "go" },
5841
5976
  push: { primary: "empujar", alternatives: ["push"], normalized: "push" },
5842
5977
  replace: { primary: "reemplazar", alternatives: ["sustituir"], normalized: "replace" },
5843
5978
  process: { primary: "procesar", normalized: "process" },
@@ -5874,6 +6009,19 @@ var init_spanish = __esm({
5874
6009
  is: { primary: "es", normalized: "is" },
5875
6010
  exists: { primary: "existe", normalized: "exists" },
5876
6011
  empty: { primary: "vac\xEDo", alternatives: ["vacio"], normalized: "empty" },
6012
+ // Comparison operator (`target matches .x`). Without this keyword the surface
6013
+ // stays an identifier and leaks verbatim into the condition's raw expression,
6014
+ // which the core expression parser reads as English (modal-close-backdrop /
6015
+ // focus-trap drop their then-branch). Not an ActionType and has no command
6016
+ // schema, so no pattern is generated from it.
6017
+ matches: { primary: "coincide", normalized: "matches" },
6018
+ // Negative-existence operator (`if no dragHandle set dragHandle to me`). Same
6019
+ // seam as `exists`: without the keyword the surface stays an identifier and
6020
+ // leaks verbatim into the condition's raw expression (behavior-draggable).
6021
+ // Neither an ActionType nor a command schema, so no pattern is generated from it.
6022
+ // Does NOT collide with `not: { primary: 'no' }`: the keyword map is keyed by
6023
+ // SURFACE, so this registers `ningún` and leaves the `no` surface untouched.
6024
+ no: { primary: "ning\xFAn", normalized: "no" },
5877
6025
  end: { primary: "fin", alternatives: ["final", "terminar"], normalized: "end" },
5878
6026
  // Advanced
5879
6027
  js: { primary: "js", normalized: "js" },
@@ -5977,7 +6125,10 @@ var init_french = __esm({
5977
6125
  result: "r\xE9sultat",
5978
6126
  event: "\xE9v\xE9nement",
5979
6127
  target: "cible",
5980
- body: "corps"
6128
+ body: "corps",
6129
+ document: "document",
6130
+ window: "fen\xEAtre",
6131
+ detail: "d\xE9tail"
5981
6132
  },
5982
6133
  possessive: {
5983
6134
  marker: "de",
@@ -6010,11 +6161,24 @@ var init_french = __esm({
6010
6161
  patient: { primary: "", position: "before" },
6011
6162
  style: { primary: "avec", position: "before" }
6012
6163
  },
6164
+ // Imperative command forms are accepted on INPUT only — `primary` stays the
6165
+ // dictionary form, so rendering is unchanged (see LanguageProfile.defaultVerbForm:
6166
+ // infinitive is the industry standard for UI localization). Hyperscript is a
6167
+ // command language, though, and a native speaker giving a command writes the
6168
+ // imperative, so the parser should read it.
6169
+ //
6170
+ // Only the IRREGULARS are listed. The regular ones reach their keyword through
6171
+ // the morphological normalizer's stem (see spanish-keyword.ts and siblings),
6172
+ // which also covers conjugations nobody enumerated here.
6013
6173
  keywords: {
6014
6174
  toggle: { primary: "basculer", alternatives: ["alterner"], normalized: "toggle" },
6015
6175
  add: { primary: "ajouter", normalized: "add" },
6016
- remove: { primary: "supprimer", alternatives: ["enlever", "retirer"], normalized: "remove" },
6017
- put: { primary: "mettre", alternatives: ["placer"], normalized: "put" },
6176
+ remove: {
6177
+ primary: "supprimer",
6178
+ alternatives: ["enlever", "retirer", "retire"],
6179
+ normalized: "remove"
6180
+ },
6181
+ put: { primary: "mettre", alternatives: ["placer", "mets"], normalized: "put" },
6018
6182
  append: { primary: "annexer", normalized: "append" },
6019
6183
  prepend: { primary: "pr\xE9fixer", normalized: "prepend" },
6020
6184
  take: { primary: "prendre", normalized: "take" },
@@ -6023,16 +6187,16 @@ var init_french = __esm({
6023
6187
  swap: { primary: "\xE9changer", alternatives: ["permuter"], normalized: "swap" },
6024
6188
  morph: { primary: "transformer", alternatives: ["m\xE9tamorphoser"], normalized: "morph" },
6025
6189
  set: { primary: "d\xE9finir", alternatives: ["\xE9tablir"], normalized: "set" },
6026
- get: { primary: "obtenir", normalized: "get" },
6190
+ get: { primary: "obtenir", alternatives: ["obtiens"], normalized: "get" },
6027
6191
  increment: { primary: "incr\xE9menter", alternatives: ["augmenter"], normalized: "increment" },
6028
6192
  decrement: { primary: "d\xE9cr\xE9menter", alternatives: ["diminuer"], normalized: "decrement" },
6029
6193
  log: { primary: "enregistrer", alternatives: ["journaliser"], normalized: "log" },
6030
- show: { primary: "montrer", alternatives: ["afficher"], normalized: "show" },
6194
+ show: { primary: "montrer", alternatives: ["afficher", "montre"], normalized: "show" },
6031
6195
  hide: { primary: "cacher", alternatives: ["masquer"], normalized: "hide" },
6032
6196
  transition: { primary: "transition", alternatives: ["animer"], normalized: "transition" },
6033
6197
  on: { primary: "sur", alternatives: ["lors"], normalized: "on" },
6034
6198
  trigger: { primary: "d\xE9clencher", normalized: "trigger" },
6035
- send: { primary: "envoyer", normalized: "send" },
6199
+ send: { primary: "envoyer", alternatives: ["envoie"], normalized: "send" },
6036
6200
  focus: { primary: "focaliser", alternatives: ["concentrer"], normalized: "focus" },
6037
6201
  blur: { primary: "d\xE9focaliser", normalized: "blur" },
6038
6202
  // Phase 1 (v0.9.90): DOM / form state / debug
@@ -6046,13 +6210,13 @@ var init_french = __esm({
6046
6210
  clear: { primary: "effacer", normalized: "clear" },
6047
6211
  reset: { primary: "r\xE9initialiser", alternatives: ["reinitialiser"], normalized: "reset" },
6048
6212
  breakpoint: { primary: "point-arr\xEAt", alternatives: ["point-arret"], normalized: "breakpoint" },
6049
- go: { primary: "aller", alternatives: ["naviguer"], normalized: "go" },
6213
+ go: { primary: "aller", alternatives: ["naviguer", "va"], normalized: "go" },
6050
6214
  scroll: { primary: "d\xE9filer", alternatives: ["faire-d\xE9filer"], normalized: "scroll" },
6051
6215
  push: { primary: "pousser", normalized: "push" },
6052
6216
  replace: { primary: "remplacer", normalized: "replace" },
6053
6217
  process: { primary: "traiter", normalized: "process" },
6054
6218
  wait: { primary: "attendre", normalized: "wait" },
6055
- fetch: { primary: "chercher", alternatives: ["r\xE9cup\xE9rer"], normalized: "fetch" },
6219
+ fetch: { primary: "chercher", alternatives: ["r\xE9cup\xE9rer", "r\xE9cup\xE8re"], normalized: "fetch" },
6056
6220
  settle: { primary: "stabiliser", normalized: "settle" },
6057
6221
  if: { primary: "si", normalized: "if" },
6058
6222
  unless: { primary: "saufsi", normalized: "unless" },
@@ -6072,6 +6236,27 @@ var init_french = __esm({
6072
6236
  return: { primary: "retourner", alternatives: ["renvoyer"], normalized: "return" },
6073
6237
  then: { primary: "puis", alternatives: ["ensuite", "alors"], normalized: "then" },
6074
6238
  and: { primary: "et", alternatives: ["aussi", "\xE9galement"], normalized: "and" },
6239
+ // Comparison operator (`target matches .x`). Without this keyword the surface
6240
+ // stays an identifier and leaks verbatim into the condition's raw expression,
6241
+ // which the core expression parser reads as English (modal-close-backdrop /
6242
+ // focus-trap drop their then-branch). Not an ActionType and has no command
6243
+ // schema, so no pattern is generated from it.
6244
+ matches: { primary: "correspond", normalized: "matches" },
6245
+ // Existence operator (`if #modal exists`). Same seam as `matches`: without the
6246
+ // keyword the surface stays an identifier and leaks verbatim into the
6247
+ // condition's raw expression (if-exists). Neither an ActionType nor a command
6248
+ // schema, so no pattern is generated from it.
6249
+ exists: { primary: "existe", normalized: "exists" },
6250
+ // Copula (`if result is false`, `if my value is empty`). Without the keyword the
6251
+ // surface stays an identifier and leaks verbatim into the condition's raw
6252
+ // expression, which the core expression parser reads as English. Neither an
6253
+ // ActionType nor a command schema, so no pattern is generated from it.
6254
+ is: { primary: "est", normalized: "is" },
6255
+ // Negative-existence operator (`if no dragHandle set dragHandle to me`). Same
6256
+ // seam as `exists`: without the keyword the surface stays an identifier and
6257
+ // leaks verbatim into the condition's raw expression (behavior-draggable).
6258
+ // Neither an ActionType nor a command schema, so no pattern is generated from it.
6259
+ no: { primary: "aucun", normalized: "no" },
6075
6260
  end: { primary: "fin", alternatives: ["terminer", "finir"], normalized: "end" },
6076
6261
  js: { primary: "js", normalized: "js" },
6077
6262
  async: { primary: "asynchrone", normalized: "async" },
@@ -6377,7 +6562,10 @@ var init_hindi = __esm({
6377
6562
  result: "\u092A\u0930\u093F\u0923\u093E\u092E",
6378
6563
  event: "\u0918\u091F\u0928\u093E",
6379
6564
  target: "\u0932\u0915\u094D\u0937\u094D\u092F",
6380
- body: "\u092C\u0949\u0921\u0940"
6565
+ body: "\u092C\u0949\u0921\u0940",
6566
+ document: "\u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C",
6567
+ window: "\u0935\u093F\u0902\u0921\u094B",
6568
+ detail: "\u0935\u093F\u0935\u0930\u0923"
6381
6569
  },
6382
6570
  possessive: {
6383
6571
  marker: "\u0915\u093E",
@@ -6527,6 +6715,11 @@ var init_hindi = __esm({
6527
6715
  // parser. (History: `मेल_खाता` underscore-split to मेल/_/खाता; the concatenated
6528
6716
  // `मेलखाता` parsed but isn't how Hindi is written.)
6529
6717
  matches: { primary: "\u092E\u0947\u0932 \u0916\u093E\u0924\u093E", normalized: "matches" },
6718
+ // Existence operator (`if #modal exists`). Same seam as `matches`: without the
6719
+ // keyword the surface stays an identifier and leaks verbatim into the
6720
+ // condition's raw expression (if-exists). Neither an ActionType nor a command
6721
+ // schema, so no pattern is generated from it.
6722
+ exists: { primary: "\u092E\u094C\u091C\u0942\u0926", normalized: "exists" },
6530
6723
  end: { primary: "\u0938\u092E\u093E\u092A\u094D\u0924", alternatives: ["\u0905\u0902\u0924"], normalized: "end" },
6531
6724
  // Advanced
6532
6725
  js: { primary: "\u091C\u0947\u090F\u0938", alternatives: ["js"], normalized: "js" },
@@ -6626,8 +6819,11 @@ var init_indonesian = __esm({
6626
6819
  result: "hasil",
6627
6820
  event: "peristiwa",
6628
6821
  target: "target",
6629
- body: "badan"
6822
+ body: "badan",
6630
6823
  // matches the i18n dict's emitted body word (corpus-canonical; tubuh = anatomical body)
6824
+ document: "dokumen",
6825
+ window: "jendela",
6826
+ detail: "detail"
6631
6827
  },
6632
6828
  possessive: {
6633
6829
  marker: "",
@@ -6748,6 +6944,12 @@ var init_indonesian = __esm({
6748
6944
  return: { primary: "kembalikan", alternatives: ["kembali"], normalized: "return" },
6749
6945
  then: { primary: "lalu", alternatives: ["kemudian", "setelah itu"], normalized: "then" },
6750
6946
  and: { primary: "dan", alternatives: ["juga", "serta"], normalized: "and" },
6947
+ // Comparison operator (`target matches .x`). Without this keyword the surface
6948
+ // stays an identifier and leaks verbatim into the condition's raw expression,
6949
+ // which the core expression parser reads as English (modal-close-backdrop /
6950
+ // focus-trap drop their then-branch). Not an ActionType and has no command
6951
+ // schema, so no pattern is generated from it.
6952
+ matches: { primary: "cocok", normalized: "matches" },
6751
6953
  end: { primary: "selesai", alternatives: ["akhir", "tamat"], normalized: "end" },
6752
6954
  js: { primary: "js", alternatives: ["javascript"], normalized: "js" },
6753
6955
  async: { primary: "asinkron", normalized: "async" },
@@ -6854,7 +7056,10 @@ var init_italian = __esm({
6854
7056
  result: "risultato",
6855
7057
  event: "evento",
6856
7058
  target: "obiettivo",
6857
- body: "corpo"
7059
+ body: "corpo",
7060
+ document: "documento",
7061
+ window: "finestra",
7062
+ detail: "dettaglio"
6858
7063
  },
6859
7064
  possessive: {
6860
7065
  marker: "di",
@@ -6961,6 +7166,17 @@ var init_italian = __esm({
6961
7166
  return: { primary: "ritornare", normalized: "return" },
6962
7167
  then: { primary: "allora", alternatives: ["poi", "quindi"], normalized: "then" },
6963
7168
  and: { primary: "e", alternatives: ["anche"], normalized: "and" },
7169
+ // Comparison operator (`target matches .x`). Without this keyword the surface
7170
+ // stays an identifier and leaks verbatim into the condition's raw expression,
7171
+ // which the core expression parser reads as English (modal-close-backdrop /
7172
+ // focus-trap drop their then-branch). Not an ActionType and has no command
7173
+ // schema, so no pattern is generated from it.
7174
+ matches: { primary: "corrisponde", normalized: "matches" },
7175
+ // Negative-existence operator (`if no dragHandle set dragHandle to me`). Same
7176
+ // seam as `exists`: without the keyword the surface stays an identifier and
7177
+ // leaks verbatim into the condition's raw expression (behavior-draggable).
7178
+ // Neither an ActionType nor a command schema, so no pattern is generated from it.
7179
+ no: { primary: "nessun", normalized: "no" },
6964
7180
  end: { primary: "fine", normalized: "end" },
6965
7181
  // Advanced
6966
7182
  js: { primary: "js", normalized: "js" },
@@ -7074,7 +7290,10 @@ var init_japanese = __esm({
7074
7290
  result: "\u7D50\u679C",
7075
7291
  event: "\u30A4\u30D9\u30F3\u30C8",
7076
7292
  target: "\u30BF\u30FC\u30B2\u30C3\u30C8",
7077
- body: "\u30DC\u30C7\u30A3"
7293
+ body: "\u30DC\u30C7\u30A3",
7294
+ document: "\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8",
7295
+ window: "\u30A6\u30A3\u30F3\u30C9\u30A6",
7296
+ detail: "\u8A73\u7D30"
7078
7297
  },
7079
7298
  possessive: {
7080
7299
  marker: "\u306E",
@@ -7152,6 +7371,10 @@ var init_japanese = __esm({
7152
7371
  focus: { primary: "\u30D5\u30A9\u30FC\u30AB\u30B9", alternatives: ["\u96C6\u4E2D"], normalized: "focus" },
7153
7372
  blur: { primary: "\u307C\u304B\u3057", alternatives: ["\u30D5\u30A9\u30FC\u30AB\u30B9\u89E3\u9664", "\u30D6\u30E9\u30FC"], normalized: "blur" },
7154
7373
  // Phase 1 (v0.9.90): DOM / form state / debug
7374
+ // Batch 3: do NOT add bare 空 here — probed: registering it as an empty
7375
+ // keyword injects a phantom `empty` command into the corpus-hot `is empty`
7376
+ // expression rows (である 空), an R0-precision regression. The empty-COMMAND
7377
+ // render gap (dict renders 空, parses null) is waived instead.
7155
7378
  empty: { primary: "\u7A7A\u306B", alternatives: ["\u7A7A\u306B\u3059\u308B"], normalized: "empty" },
7156
7379
  open: { primary: "\u958B\u304F", alternatives: ["\u30AA\u30FC\u30D7\u30F3"], normalized: "open" },
7157
7380
  close: { primary: "\u9589\u3058\u308B", alternatives: ["\u30AF\u30ED\u30FC\u30BA"], normalized: "close" },
@@ -7195,6 +7418,32 @@ var init_japanese = __esm({
7195
7418
  return: { primary: "\u623B\u308B", alternatives: ["\u8FD4\u3059", "\u30EA\u30BF\u30FC\u30F3"], normalized: "return" },
7196
7419
  then: { primary: "\u305D\u308C\u304B\u3089", alternatives: ["\u6B21\u306B", "\u306A\u3089\u3070", "\u306A\u3089"], normalized: "then" },
7197
7420
  and: { primary: "\u307E\u305F", alternatives: ["\u3068", "\u305D\u3057\u3066"], normalized: "and" },
7421
+ // Comparison operator (`target matches .x`). Deferred by the Phase 2 `matches`
7422
+ // slice because ja's operand ALSO leaked (`references.target` carried ターゲット
7423
+ // while the dict emits 対象), and registering the operator without its operand is
7424
+ // worse than neither: modal-close-backdrop ja passed R2 only BY ACCIDENT — the
7425
+ // unparsed condition was dropped, so `hide` ran unconditionally and coincidentally
7426
+ // matched the en DOM effect. `matches` alone would parse the condition into a real
7427
+ // comparison whose operand 対象 evaluates to undefined, stopping `hide` and
7428
+ // flipping R2 pass→fail at tolerance 0. Landing WITH the 対象 EXTRAS entry
7429
+ // (japanese.ts tokenizer) renders `target matches .modal-backdrop`, byte-identical
7430
+ // to en. Not an ActionType and has no command schema, so no pattern is generated.
7431
+ matches: { primary: "\u4E00\u81F4\u3059\u308B", normalized: "matches" },
7432
+ // Existence operator (`if #modal exists`). Same seam as `matches`: without the
7433
+ // keyword the surface stays an identifier and leaks verbatim into the
7434
+ // condition's raw expression (if-exists). Neither an ActionType nor a command
7435
+ // schema, so no pattern is generated from it.
7436
+ exists: { primary: "\u5B58\u5728\u3059\u308B", normalized: "exists" },
7437
+ // Copula (`if result is false`, `if my value is empty`). Without the keyword the
7438
+ // surface stays an identifier and leaks verbatim into the condition's raw
7439
+ // expression, which the core expression parser reads as English. Neither an
7440
+ // ActionType nor a command schema, so no pattern is generated from it.
7441
+ is: { primary: "\u3067\u3042\u308B", normalized: "is" },
7442
+ // Negative-existence operator (`if no dragHandle set dragHandle to me`). Same
7443
+ // seam as `exists`: without the keyword the surface stays an identifier and
7444
+ // leaks verbatim into the condition's raw expression (behavior-draggable).
7445
+ // Neither an ActionType nor a command schema, so no pattern is generated from it.
7446
+ no: { primary: "\u306A\u3044", normalized: "no" },
7198
7447
  // 終了 removed: it is the i18n dict's `exit` emission (ja.ts), so listing it
7199
7448
  // as an `end` alternative made an `exit` inside `if … exit … end` read as the
7200
7449
  // block terminator and collapse the handler body (behavior-sortable). 終わり is
@@ -7297,8 +7546,11 @@ var init_korean = __esm({
7297
7546
  result: "\uACB0\uACFC",
7298
7547
  event: "\uC774\uBCA4\uD2B8",
7299
7548
  target: "\uB300\uC0C1",
7300
- body: "\uBC14\uB514"
7549
+ body: "\uBC14\uB514",
7301
7550
  // matches the i18n dict's emitted body word (본문 = "main text", wrong for the DOM body element)
7551
+ document: "\uBB38\uC11C",
7552
+ window: "\uCC3D",
7553
+ detail: "\uC138\uBD80"
7302
7554
  },
7303
7555
  possessive: {
7304
7556
  marker: "\uC758",
@@ -7335,16 +7587,25 @@ var init_korean = __esm({
7335
7587
  event: { primary: "\uC744", alternatives: ["\uB97C"], position: "after" }
7336
7588
  // Event as object marker
7337
7589
  },
7590
+ // Imperative command forms are accepted on INPUT only — `primary` stays the
7591
+ // dictionary form, so rendering is unchanged (see LanguageProfile.defaultVerbForm:
7592
+ // infinitive is the industry standard for UI localization). Hyperscript is a
7593
+ // command language, though, and a native speaker giving a command writes the
7594
+ // imperative, so the parser should read it.
7595
+ //
7596
+ // Only the IRREGULARS are listed. The regular ones reach their keyword through
7597
+ // the morphological normalizer's stem (see spanish-keyword.ts and siblings),
7598
+ // which also covers conjugations nobody enumerated here.
7338
7599
  keywords: {
7339
7600
  // Class/Attribute operations
7340
7601
  toggle: { primary: "\uD1A0\uAE00", normalized: "toggle" },
7341
7602
  add: { primary: "\uCD94\uAC00", normalized: "add" },
7342
7603
  remove: { primary: "\uC81C\uAC70", alternatives: ["\uC0AD\uC81C"], normalized: "remove" },
7343
7604
  // Content operations
7344
- put: { primary: "\uB123\uB2E4", alternatives: ["\uB123\uAE30", "\uB193\uAE30"], normalized: "put" },
7605
+ put: { primary: "\uB123\uB2E4", alternatives: ["\uB123\uAE30", "\uB193\uAE30", "\uB123\uC73C\uC138\uC694"], normalized: "put" },
7345
7606
  append: { primary: "\uB367\uBD99\uC774\uB2E4", alternatives: ["\uB05D\uC5D0\uCD94\uAC00"], normalized: "append" },
7346
7607
  prepend: { primary: "\uC55E\uC5D0\uCD94\uAC00", alternatives: ["\uC120\uB450\uCD94\uAC00"], normalized: "prepend" },
7347
- take: { primary: "\uAC00\uC838\uC624\uB2E4", normalized: "take" },
7608
+ take: { primary: "\uAC00\uC838\uC624\uB2E4", alternatives: ["\uAC00\uC838\uC624\uC138\uC694"], normalized: "take" },
7348
7609
  make: { primary: "\uB9CC\uB4E4\uB2E4", normalized: "make" },
7349
7610
  clone: { primary: "\uBCF5\uC81C", normalized: "clone" },
7350
7611
  // 복제=duplicate/clone, 복사=copy
@@ -7352,13 +7613,13 @@ var init_korean = __esm({
7352
7613
  morph: { primary: "\uBCC0\uD615", alternatives: ["\uBCC0\uD658"], normalized: "morph" },
7353
7614
  // Variable operations
7354
7615
  set: { primary: "\uC124\uC815", normalized: "set" },
7355
- get: { primary: "\uC5BB\uB2E4", normalized: "get" },
7616
+ get: { primary: "\uC5BB\uB2E4", alternatives: ["\uC5BB\uC73C\uC138\uC694"], normalized: "get" },
7356
7617
  increment: { primary: "\uC99D\uAC00", normalized: "increment" },
7357
7618
  decrement: { primary: "\uAC10\uC18C", normalized: "decrement" },
7358
7619
  log: { primary: "\uB85C\uADF8", normalized: "log" },
7359
7620
  // Visibility
7360
- show: { primary: "\uBCF4\uC774\uB2E4", alternatives: ["\uD45C\uC2DC", "\uBCF4\uC774\uAE30"], normalized: "show" },
7361
- hide: { primary: "\uC228\uAE30\uB2E4", alternatives: ["\uC228\uAE30\uAE30"], normalized: "hide" },
7621
+ show: { primary: "\uBCF4\uC774\uB2E4", alternatives: ["\uD45C\uC2DC", "\uBCF4\uC774\uAE30", "\uBCF4\uC774\uC138\uC694"], normalized: "show" },
7622
+ hide: { primary: "\uC228\uAE30\uB2E4", alternatives: ["\uC228\uAE30\uAE30", "\uC228\uAE30\uC138\uC694"], normalized: "hide" },
7362
7623
  // primary is the loanword 트랜지션; 전환 ("switch/transition") is the form the
7363
7624
  // i18n transformer emits — registered as an alternative (passthrough-alignment).
7364
7625
  // toggle uses 토글, so 전환 carries no collision.
@@ -7366,12 +7627,14 @@ var init_korean = __esm({
7366
7627
  // Events
7367
7628
  on: { primary: "\uC5D0", alternatives: ["\uC2DC", "\uD560 \uB54C"], normalized: "on" },
7368
7629
  trigger: { primary: "\uD2B8\uB9AC\uAC70", normalized: "trigger" },
7369
- send: { primary: "\uBCF4\uB0B4\uB2E4", normalized: "send" },
7630
+ send: { primary: "\uBCF4\uB0B4\uB2E4", alternatives: ["\uBCF4\uB0B4\uC138\uC694"], normalized: "send" },
7370
7631
  // DOM focus
7371
7632
  focus: { primary: "\uD3EC\uCEE4\uC2A4", normalized: "focus" },
7372
7633
  blur: { primary: "\uBE14\uB7EC", normalized: "blur" },
7373
7634
  // Phase 1 (v0.9.90): DOM / form state / debug
7374
- empty: { primary: "\uBE44\uC6B0\uAE30", normalized: "empty" },
7635
+ // Batch 3: 비어있는 added — the i18n dict renders the empty COMMAND with its
7636
+ // `is empty` adjective (category-shadowed), which parsed null.
7637
+ empty: { primary: "\uBE44\uC6B0\uAE30", alternatives: ["\uBE44\uC5B4\uC788\uB294"], normalized: "empty" },
7375
7638
  open: { primary: "\uC5F4\uAE30", normalized: "open" },
7376
7639
  close: { primary: "\uB2EB\uAE30", normalized: "close" },
7377
7640
  select: { primary: "\uACE0\uB974\uAE30", normalized: "select" },
@@ -7428,6 +7691,16 @@ var init_korean = __esm({
7428
7691
  // matches .x`. Without this keyword `일치` stays an identifier and the
7429
7692
  // condition is unevaluable (modal-close-backdrop drops its then-branch).
7430
7693
  matches: { primary: "\uC77C\uCE58", normalized: "matches" },
7694
+ // Existence operator (`if #modal exists`). Same seam as `matches`: without the
7695
+ // keyword the surface stays an identifier and leaks verbatim into the
7696
+ // condition's raw expression (if-exists). Neither an ActionType nor a command
7697
+ // schema, so no pattern is generated from it.
7698
+ exists: { primary: "\uC874\uC7AC", normalized: "exists" },
7699
+ // Negative-existence operator (`if no dragHandle set dragHandle to me`). Same
7700
+ // seam as `exists`: without the keyword the surface stays an identifier and
7701
+ // leaks verbatim into the condition's raw expression (behavior-draggable).
7702
+ // Neither an ActionType nor a command schema, so no pattern is generated from it.
7703
+ no: { primary: "\uC5C6\uC74C", normalized: "no" },
7431
7704
  end: { primary: "\uB05D", alternatives: ["\uB9C8\uCE68"], normalized: "end" },
7432
7705
  // Advanced
7433
7706
  js: { primary: "JS\uC2E4\uD589", alternatives: ["js"], normalized: "js" },
@@ -7519,7 +7792,10 @@ var init_ms = __esm({
7519
7792
  result: "hasil",
7520
7793
  event: "peristiwa",
7521
7794
  target: "sasaran",
7522
- body: "badan"
7795
+ body: "badan",
7796
+ document: "dokumen",
7797
+ window: "tetingkap",
7798
+ detail: "perincian"
7523
7799
  },
7524
7800
  possessive: {
7525
7801
  marker: "",
@@ -7642,6 +7918,27 @@ var init_ms = __esm({
7642
7918
  return: { primary: "pulang", alternatives: ["kembali"], normalized: "return" },
7643
7919
  then: { primary: "kemudian", alternatives: ["lepas_itu"], normalized: "then" },
7644
7920
  and: { primary: "dan", normalized: "and" },
7921
+ // Comparison operator (`target matches .x`). Without this keyword the surface
7922
+ // stays an identifier and leaks verbatim into the condition's raw expression,
7923
+ // which the core expression parser reads as English (modal-close-backdrop /
7924
+ // focus-trap drop their then-branch). Not an ActionType and has no command
7925
+ // schema, so no pattern is generated from it.
7926
+ matches: { primary: "sepadan", normalized: "matches" },
7927
+ // Existence operator (`if #modal exists`). Same seam as `matches`: without the
7928
+ // keyword the surface stays an identifier and leaks verbatim into the
7929
+ // condition's raw expression (if-exists). Neither an ActionType nor a command
7930
+ // schema, so no pattern is generated from it.
7931
+ exists: { primary: "wujud", normalized: "exists" },
7932
+ // Copula (`if result is false`, `if my value is empty`). Without the keyword the
7933
+ // surface stays an identifier and leaks verbatim into the condition's raw
7934
+ // expression, which the core expression parser reads as English. Neither an
7935
+ // ActionType nor a command schema, so no pattern is generated from it.
7936
+ is: { primary: "adalah", normalized: "is" },
7937
+ // Negative-existence operator (`if no dragHandle set dragHandle to me`). Same
7938
+ // seam as `exists`: without the keyword the surface stays an identifier and
7939
+ // leaks verbatim into the condition's raw expression (behavior-draggable).
7940
+ // Neither an ActionType nor a command schema, so no pattern is generated from it.
7941
+ no: { primary: "tiada", normalized: "no" },
7645
7942
  end: { primary: "tamat", alternatives: ["habis"], normalized: "end" },
7646
7943
  // Advanced
7647
7944
  js: { primary: "js", normalized: "js" },
@@ -7728,7 +8025,10 @@ var init_polish = __esm({
7728
8025
  result: "wynik",
7729
8026
  event: "zdarzenie",
7730
8027
  target: "cel",
7731
- body: "body"
8028
+ body: "body",
8029
+ document: "dokument",
8030
+ window: "okno",
8031
+ detail: "szczeg\xF3\u0142"
7732
8032
  },
7733
8033
  possessive: {
7734
8034
  marker: "",
@@ -7961,6 +8261,17 @@ var init_polish = __esm({
7961
8261
  normalized: "then"
7962
8262
  },
7963
8263
  and: { primary: "i", alternatives: ["oraz"], normalized: "and" },
8264
+ // Comparison operator (`target matches .x`). Without this keyword the surface
8265
+ // stays an identifier and leaks verbatim into the condition's raw expression,
8266
+ // which the core expression parser reads as English (modal-close-backdrop /
8267
+ // focus-trap drop their then-branch). Not an ActionType and has no command
8268
+ // schema, so no pattern is generated from it.
8269
+ matches: { primary: "pasuje", normalized: "matches" },
8270
+ // Negative-existence operator (`if no dragHandle set dragHandle to me`). Same
8271
+ // seam as `exists`: without the keyword the surface stays an identifier and
8272
+ // leaks verbatim into the condition's raw expression (behavior-draggable).
8273
+ // Neither an ActionType nor a command schema, so no pattern is generated from it.
8274
+ no: { primary: "brak", normalized: "no" },
7964
8275
  end: { primary: "koniec", normalized: "end" },
7965
8276
  // Advanced
7966
8277
  js: { primary: "js", normalized: "js" },
@@ -8065,7 +8376,10 @@ var init_portuguese = __esm({
8065
8376
  result: "resultado",
8066
8377
  event: "evento",
8067
8378
  target: "alvo",
8068
- body: "corpo"
8379
+ body: "corpo",
8380
+ document: "documento",
8381
+ window: "janela",
8382
+ detail: "detalhe"
8069
8383
  },
8070
8384
  possessive: {
8071
8385
  marker: "de",
@@ -8095,25 +8409,38 @@ var init_portuguese = __esm({
8095
8409
  patient: { primary: "", position: "before" },
8096
8410
  style: { primary: "com", position: "before" }
8097
8411
  },
8412
+ // Imperative command forms are accepted on INPUT only — `primary` stays the
8413
+ // dictionary form, so rendering is unchanged (see LanguageProfile.defaultVerbForm:
8414
+ // infinitive is the industry standard for UI localization). Hyperscript is a
8415
+ // command language, though, and a native speaker giving a command writes the
8416
+ // imperative, so the parser should read it.
8417
+ //
8418
+ // Only the IRREGULARS are listed. The regular ones reach their keyword through
8419
+ // the morphological normalizer's stem (see spanish-keyword.ts and siblings),
8420
+ // which also covers conjugations nobody enumerated here.
8098
8421
  keywords: {
8099
8422
  toggle: { primary: "alternar", alternatives: [], normalized: "toggle" },
8100
8423
  add: { primary: "adicionar", alternatives: ["acrescentar"], normalized: "add" },
8101
- remove: { primary: "remover", alternatives: ["eliminar", "apagar"], normalized: "remove" },
8102
- put: { primary: "colocar", alternatives: ["p\xF4r", "por"], normalized: "put" },
8424
+ remove: {
8425
+ primary: "remover",
8426
+ alternatives: ["eliminar", "apagar", "remova"],
8427
+ normalized: "remove"
8428
+ },
8429
+ put: { primary: "colocar", alternatives: ["p\xF4r", "por", "coloque"], normalized: "put" },
8103
8430
  append: { primary: "anexar", normalized: "append" },
8104
8431
  prepend: { primary: "preceder", normalized: "prepend" },
8105
- take: { primary: "pegar", normalized: "take" },
8432
+ take: { primary: "pegar", alternatives: ["pegue"], normalized: "take" },
8106
8433
  make: { primary: "fazer", alternatives: ["criar"], normalized: "make" },
8107
8434
  clone: { primary: "clonar", alternatives: [], normalized: "clone" },
8108
8435
  swap: { primary: "trocar", alternatives: ["substituir"], normalized: "swap" },
8109
8436
  morph: { primary: "transformar", alternatives: ["converter"], normalized: "morph" },
8110
- set: { primary: "definir", alternatives: ["configurar"], normalized: "set" },
8111
- get: { primary: "obter", normalized: "get" },
8437
+ set: { primary: "definir", alternatives: ["configurar", "defina"], normalized: "set" },
8438
+ get: { primary: "obter", alternatives: ["obtenha"], normalized: "get" },
8112
8439
  increment: { primary: "incrementar", alternatives: ["aumentar"], normalized: "increment" },
8113
8440
  decrement: { primary: "decrementar", alternatives: ["diminuir"], normalized: "decrement" },
8114
8441
  log: { primary: "registrar", alternatives: ["imprimir"], normalized: "log" },
8115
8442
  show: { primary: "mostrar", alternatives: ["exibir"], normalized: "show" },
8116
- hide: { primary: "ocultar", alternatives: ["esconder"], normalized: "hide" },
8443
+ hide: { primary: "ocultar", alternatives: ["esconder", "esconda"], normalized: "hide" },
8117
8444
  transition: { primary: "transi\xE7\xE3o", alternatives: ["animar"], normalized: "transition" },
8118
8445
  on: { primary: "em", alternatives: ["ao"], normalized: "on" },
8119
8446
  trigger: { primary: "disparar", alternatives: ["ativar"], normalized: "trigger" },
@@ -8132,13 +8459,13 @@ var init_portuguese = __esm({
8132
8459
  alternatives: ["ponto-interrupcao"],
8133
8460
  normalized: "breakpoint"
8134
8461
  },
8135
- go: { primary: "ir", alternatives: ["navegar"], normalized: "go" },
8462
+ go: { primary: "ir", alternatives: ["navegar", "v\xE1"], normalized: "go" },
8136
8463
  scroll: { primary: "rolar", alternatives: ["scroll"], normalized: "scroll" },
8137
8464
  push: { primary: "empurrar", alternatives: ["push"], normalized: "push" },
8138
8465
  replace: { primary: "repor", alternatives: ["recolocar"], normalized: "replace" },
8139
8466
  process: { primary: "processar", normalized: "process" },
8140
8467
  wait: { primary: "esperar", alternatives: ["aguardar"], normalized: "wait" },
8141
- fetch: { primary: "buscar", normalized: "fetch" },
8468
+ fetch: { primary: "buscar", alternatives: ["busque"], normalized: "fetch" },
8142
8469
  settle: { primary: "estabilizar", normalized: "settle" },
8143
8470
  if: { primary: "se", normalized: "if" },
8144
8471
  // salvo — single token ('salvo se' = unless). a_menos kept as an
@@ -8161,6 +8488,27 @@ var init_portuguese = __esm({
8161
8488
  return: { primary: "retornar", alternatives: ["devolver"], normalized: "return" },
8162
8489
  then: { primary: "ent\xE3o", alternatives: ["logo"], normalized: "then" },
8163
8490
  and: { primary: "e", alternatives: ["tamb\xE9m", "al\xE9m disso"], normalized: "and" },
8491
+ // Comparison operator (`target matches .x`). Without this keyword the surface
8492
+ // stays an identifier and leaks verbatim into the condition's raw expression,
8493
+ // which the core expression parser reads as English (modal-close-backdrop /
8494
+ // focus-trap drop their then-branch). Not an ActionType and has no command
8495
+ // schema, so no pattern is generated from it.
8496
+ matches: { primary: "corresponde", normalized: "matches" },
8497
+ // Existence operator (`if #modal exists`). Same seam as `matches`: without the
8498
+ // keyword the surface stays an identifier and leaks verbatim into the
8499
+ // condition's raw expression (if-exists). Neither an ActionType nor a command
8500
+ // schema, so no pattern is generated from it.
8501
+ exists: { primary: "existe", normalized: "exists" },
8502
+ // Copula (`if result is false`, `if my value is empty`). Without the keyword the
8503
+ // surface stays an identifier and leaks verbatim into the condition's raw
8504
+ // expression, which the core expression parser reads as English. Neither an
8505
+ // ActionType nor a command schema, so no pattern is generated from it.
8506
+ is: { primary: "\xE9", normalized: "is" },
8507
+ // Negative-existence operator (`if no dragHandle set dragHandle to me`). Same
8508
+ // seam as `exists`: without the keyword the surface stays an identifier and
8509
+ // leaks verbatim into the condition's raw expression (behavior-draggable).
8510
+ // Neither an ActionType nor a command schema, so no pattern is generated from it.
8511
+ no: { primary: "nenhum", normalized: "no" },
8164
8512
  end: { primary: "fim", alternatives: ["final", "t\xE9rmino"], normalized: "end" },
8165
8513
  js: { primary: "js", normalized: "js" },
8166
8514
  async: { primary: "ass\xEDncrono", normalized: "async" },
@@ -8267,7 +8615,10 @@ var init_quechua = __esm({
8267
8615
  result: "rurasqa",
8268
8616
  event: "ruwakuq",
8269
8617
  target: "punta",
8270
- body: "kurku"
8618
+ body: "kurku",
8619
+ document: "qillqa",
8620
+ window: "k_iri",
8621
+ detail: "sut_iy"
8271
8622
  },
8272
8623
  possessive: {
8273
8624
  marker: "-pa",
@@ -8338,7 +8689,10 @@ var init_quechua = __esm({
8338
8689
  focus: { primary: "qhawachiy", alternatives: ["qhaway"], normalized: "focus" },
8339
8690
  blur: { primary: "paqariy", alternatives: ["mana qhawachiy"], normalized: "blur" },
8340
8691
  // Phase 1 (v0.9.90): DOM / form state / debug
8341
- empty: { primary: "ch'usaq", normalized: "empty" },
8692
+ // Batch 3: apostrophe-less chusaq added — the i18n dict renders the empty
8693
+ // COMMAND with it (its `is empty` expression word), which parsed null against
8694
+ // the ch'usaq-only command patterns.
8695
+ empty: { primary: "ch'usaq", alternatives: ["chusaq"], normalized: "empty" },
8342
8696
  open: { primary: "paskay", normalized: "open" },
8343
8697
  close: { primary: "wichqay", normalized: "close" },
8344
8698
  select: { primary: "marcay", normalized: "select" },
@@ -8376,6 +8730,22 @@ var init_quechua = __esm({
8376
8730
  return: { primary: "kutichiy", alternatives: ["kutimuy"], normalized: "return" },
8377
8731
  then: { primary: "chaymantataq", alternatives: ["hinaspa", "chaymanta"], normalized: "then" },
8378
8732
  and: { primary: "hinallataq", alternatives: ["ima", "chaymantawan"], normalized: "and" },
8733
+ // Comparison operator (`target matches .x`). Without this keyword the surface
8734
+ // stays an identifier and leaks verbatim into the condition's raw expression,
8735
+ // which the core expression parser reads as English (modal-close-backdrop /
8736
+ // focus-trap drop their then-branch). Not an ActionType and has no command
8737
+ // schema, so no pattern is generated from it.
8738
+ matches: { primary: "tupan", normalized: "matches" },
8739
+ // Existence operator (`if #modal exists`). Same seam as `matches`: without the
8740
+ // keyword the surface stays an identifier and leaks verbatim into the
8741
+ // condition's raw expression (if-exists). Neither an ActionType nor a command
8742
+ // schema, so no pattern is generated from it.
8743
+ exists: { primary: "tiyan", normalized: "exists" },
8744
+ // Copula (`if result is false`, `if my value is empty`). Without the keyword the
8745
+ // surface stays an identifier and leaks verbatim into the condition's raw
8746
+ // expression, which the core expression parser reads as English. Neither an
8747
+ // ActionType nor a command schema, so no pattern is generated from it.
8748
+ is: { primary: "kanqa", normalized: "is" },
8379
8749
  end: { primary: "tukukuy", alternatives: ["tukuy", "puchukay"], normalized: "end" },
8380
8750
  js: { primary: "js", normalized: "js" },
8381
8751
  async: { primary: "mana waqtalla", normalized: "async" },
@@ -8471,8 +8841,11 @@ var init_russian = __esm({
8471
8841
  result: "\u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442",
8472
8842
  event: "\u0441\u043E\u0431\u044B\u0442\u0438\u0435",
8473
8843
  target: "\u0446\u0435\u043B\u044C",
8474
- body: "\u0442\u0435\u043B\u043E"
8844
+ body: "\u0442\u0435\u043B\u043E",
8475
8845
  // was an English placeholder; the i18n dict emits the Russian word
8846
+ document: "\u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442",
8847
+ window: "\u043E\u043A\u043D\u043E",
8848
+ detail: "\u0434\u0435\u0442\u0430\u043B\u0438"
8476
8849
  },
8477
8850
  possessive: {
8478
8851
  marker: "",
@@ -8718,6 +9091,21 @@ var init_russian = __esm({
8718
9091
  // so `target соответствует .x` must normalize to `target matches .x`; otherwise
8719
9092
  // `соответствует` stays an identifier and modal-close-backdrop drops its then-branch.
8720
9093
  matches: { primary: "\u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442", normalized: "matches" },
9094
+ // Existence operator (`if #modal exists`). Same seam as `matches`: without the
9095
+ // keyword the surface stays an identifier and leaks verbatim into the
9096
+ // condition's raw expression (if-exists). Neither an ActionType nor a command
9097
+ // schema, so no pattern is generated from it.
9098
+ exists: { primary: "\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442", normalized: "exists" },
9099
+ // Copula (`if result is false`, `if my value is empty`). Without the keyword the
9100
+ // surface stays an identifier and leaks verbatim into the condition's raw
9101
+ // expression, which the core expression parser reads as English. Neither an
9102
+ // ActionType nor a command schema, so no pattern is generated from it.
9103
+ is: { primary: "\u0435\u0441\u0442\u044C", normalized: "is" },
9104
+ // Negative-existence operator (`if no dragHandle set dragHandle to me`). Same
9105
+ // seam as `exists`: without the keyword the surface stays an identifier and
9106
+ // leaks verbatim into the condition's raw expression (behavior-draggable).
9107
+ // Neither an ActionType nor a command schema, so no pattern is generated from it.
9108
+ no: { primary: "\u043D\u0435\u0442", normalized: "no" },
8721
9109
  end: { primary: "\u043A\u043E\u043D\u0435\u0446", normalized: "end" },
8722
9110
  // Advanced
8723
9111
  js: { primary: "js", normalized: "js" },
@@ -8834,7 +9222,10 @@ var init_swahili = __esm({
8834
9222
  result: "matokeo",
8835
9223
  event: "tukio",
8836
9224
  target: "lengo",
8837
- body: "mwili"
9225
+ body: "mwili",
9226
+ document: "hati",
9227
+ window: "dirisha",
9228
+ detail: "maelezo"
8838
9229
  },
8839
9230
  possessive: {
8840
9231
  marker: "",
@@ -8946,6 +9337,17 @@ var init_swahili = __esm({
8946
9337
  // Swahili copula ("is"); only recognized in predicate position (after a value,
8947
9338
  // before an adjective like `tupu`), so it doesn't disturb command parsing.
8948
9339
  is: { primary: "ni", normalized: "is" },
9340
+ // Comparison operator (`target matches .x`). Without this keyword the surface
9341
+ // stays an identifier and leaks verbatim into the condition's raw expression,
9342
+ // which the core expression parser reads as English (modal-close-backdrop /
9343
+ // focus-trap drop their then-branch). Not an ActionType and has no command
9344
+ // schema, so no pattern is generated from it.
9345
+ matches: { primary: "inafanana", normalized: "matches" },
9346
+ // Negative-existence operator (`if no dragHandle set dragHandle to me`). Same
9347
+ // seam as `exists`: without the keyword the surface stays an identifier and
9348
+ // leaks verbatim into the condition's raw expression (behavior-draggable).
9349
+ // Neither an ActionType nor a command schema, so no pattern is generated from it.
9350
+ no: { primary: "hakuna", normalized: "no" },
8949
9351
  end: { primary: "mwisho", alternatives: ["maliza", "tamati"], normalized: "end" },
8950
9352
  js: { primary: "js", alternatives: ["javascript"], normalized: "js" },
8951
9353
  async: { primary: "isiyo sawia", normalized: "async" },
@@ -9139,6 +9541,11 @@ var init_thai = __esm({
9139
9541
  return: { primary: "\u0E04\u0E37\u0E19\u0E04\u0E48\u0E32", alternatives: ["\u0E01\u0E25\u0E31\u0E1A"], normalized: "return" },
9140
9542
  then: { primary: "\u0E41\u0E25\u0E49\u0E27", alternatives: [], normalized: "then" },
9141
9543
  and: { primary: "\u0E41\u0E25\u0E30", alternatives: [], normalized: "and" },
9544
+ // Existence operator (`if #modal exists`). Same seam as `matches`: without the
9545
+ // keyword the surface stays an identifier and leaks verbatim into the
9546
+ // condition's raw expression (if-exists). Neither an ActionType nor a command
9547
+ // schema, so no pattern is generated from it.
9548
+ exists: { primary: "\u0E21\u0E35\u0E2D\u0E22\u0E39\u0E48", normalized: "exists" },
9142
9549
  end: { primary: "\u0E08\u0E1A", alternatives: [], normalized: "end" },
9143
9550
  // Advanced
9144
9551
  js: { primary: "\u0E40\u0E08\u0E40\u0E2D\u0E2A", alternatives: ["js"], normalized: "js" },
@@ -9242,8 +9649,11 @@ var init_tl = __esm({
9242
9649
  // "event"
9243
9650
  target: "target",
9244
9651
  // "target"
9245
- body: "katawan"
9652
+ body: "katawan",
9246
9653
  // was an English placeholder; the i18n dict emits the Tagalog word
9654
+ document: "dokumento",
9655
+ window: "bintana",
9656
+ detail: "detalye"
9247
9657
  },
9248
9658
  possessive: {
9249
9659
  marker: "ng",
@@ -9351,6 +9761,17 @@ var init_tl = __esm({
9351
9761
  return: { primary: "ibalik", alternatives: ["bumalik"], normalized: "return" },
9352
9762
  then: { primary: "pagkatapos", alternatives: ["saka"], normalized: "then" },
9353
9763
  and: { primary: "at", normalized: "and" },
9764
+ // Comparison operator (`target matches .x`). Without this keyword the surface
9765
+ // stays an identifier and leaks verbatim into the condition's raw expression,
9766
+ // which the core expression parser reads as English (modal-close-backdrop /
9767
+ // focus-trap drop their then-branch). Not an ActionType and has no command
9768
+ // schema, so no pattern is generated from it.
9769
+ matches: { primary: "tumutugma", normalized: "matches" },
9770
+ // Copula (`if result is false`, `if my value is empty`). Without the keyword the
9771
+ // surface stays an identifier and leaks verbatim into the condition's raw
9772
+ // expression, which the core expression parser reads as English. Neither an
9773
+ // ActionType nor a command schema, so no pattern is generated from it.
9774
+ is: { primary: "ay", normalized: "is" },
9354
9775
  end: { primary: "wakas", alternatives: ["tapos"], normalized: "end" },
9355
9776
  // Advanced
9356
9777
  js: { primary: "js", normalized: "js" },
@@ -9450,7 +9871,10 @@ var init_turkish = __esm({
9450
9871
  result: "sonu\xE7",
9451
9872
  event: "olay",
9452
9873
  target: "hedef",
9453
- body: "g\xF6vde"
9874
+ body: "g\xF6vde",
9875
+ document: "belge",
9876
+ window: "pencere",
9877
+ detail: "detay"
9454
9878
  },
9455
9879
  possessive: {
9456
9880
  // Genitive suffix, spaced for tokenization like Turkish's other case
@@ -9512,7 +9936,10 @@ var init_turkish = __esm({
9512
9936
  // Dative/Locative + Genitive (with buffer consonants)
9513
9937
  source: { primary: "den", alternatives: ["dan", "ten", "tan"], position: "after" },
9514
9938
  // Ablative
9515
- style: { primary: "le", alternatives: ["la", "yle", "yla"], position: "after" },
9939
+ // `ile` is the free-standing instrumental the transformer actually emits
9940
+ // for with-phrases (`getir method:"POST" body:form ile`); the suffix
9941
+ // forms cover hand-written agglutinated variants.
9942
+ style: { primary: "le", alternatives: ["la", "yle", "yla", "ile"], position: "after" },
9516
9943
  // Instrumental
9517
9944
  event: { primary: "i", alternatives: ["\u0131", "u", "\xFC"], position: "after" }
9518
9945
  // Event as accusative
@@ -9609,6 +10036,24 @@ var init_turkish = __esm({
9609
10036
  and: { primary: "ve", alternatives: ["ayr\u0131ca", "hem de"], normalized: "and" },
9610
10037
  or: { primary: "veya", normalized: "or" },
9611
10038
  not: { primary: "de\u011Fil", alternatives: ["degil"], normalized: "not" },
10039
+ // Comparison operator (`target matches .x`). Without this keyword the surface
10040
+ // stays an identifier and leaks verbatim into the condition's raw expression,
10041
+ // which the core expression parser reads as English (modal-close-backdrop /
10042
+ // focus-trap drop their then-branch). Not an ActionType and has no command
10043
+ // schema, so no pattern is generated from it.
10044
+ matches: { primary: "e\u015Fle\u015Fir", normalized: "matches" },
10045
+ // Copula (`if result is false`, `if my value is empty`). Without the keyword the
10046
+ // surface stays an identifier and leaks verbatim into the condition's raw
10047
+ // expression, which the core expression parser reads as English. Neither an
10048
+ // ActionType nor a command schema, so no pattern is generated from it.
10049
+ is: { primary: "dir", normalized: "is" },
10050
+ // Negative-existence operator (`if no dragHandle set dragHandle to me`). Same
10051
+ // seam as `exists`: without the keyword the surface stays an identifier and
10052
+ // leaks verbatim into the condition's raw expression (behavior-draggable).
10053
+ // Neither an ActionType nor a command schema, so no pattern is generated from it.
10054
+ // `yok` is a prefix of `else: 'yoksa'`; the keyword walk sorts longest-first, so
10055
+ // `yoksa` still wins where it appears.
10056
+ no: { primary: "yok", normalized: "no" },
9612
10057
  end: { primary: "son", alternatives: ["biti\u015F", "bitti"], normalized: "end" },
9613
10058
  // Advanced
9614
10059
  js: { primary: "js", normalized: "js" },
@@ -9703,8 +10148,11 @@ var init_ukrainian = __esm({
9703
10148
  result: "\u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442",
9704
10149
  event: "\u043F\u043E\u0434\u0456\u044F",
9705
10150
  target: "\u0446\u0456\u043B\u044C",
9706
- body: "\u0442\u0456\u043B\u043E"
10151
+ body: "\u0442\u0456\u043B\u043E",
9707
10152
  // was an English placeholder; the i18n dict emits the Ukrainian word
10153
+ document: "\u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442",
10154
+ window: "\u0432\u0456\u043A\u043D\u043E",
10155
+ detail: "\u0434\u0435\u0442\u0430\u043B\u0456"
9708
10156
  },
9709
10157
  possessive: {
9710
10158
  marker: "",
@@ -9968,6 +10416,21 @@ var init_ukrainian = __esm({
9968
10416
  // so `target відповідає .x` must normalize to `target matches .x`; otherwise
9969
10417
  // `відповідає` stays an identifier and modal-close-backdrop drops its then-branch.
9970
10418
  matches: { primary: "\u0432\u0456\u0434\u043F\u043E\u0432\u0456\u0434\u0430\u0454", normalized: "matches" },
10419
+ // Existence operator (`if #modal exists`). Same seam as `matches`: without the
10420
+ // keyword the surface stays an identifier and leaks verbatim into the
10421
+ // condition's raw expression (if-exists). Neither an ActionType nor a command
10422
+ // schema, so no pattern is generated from it.
10423
+ exists: { primary: "\u0456\u0441\u043D\u0443\u0454", normalized: "exists" },
10424
+ // Copula (`if result is false`, `if my value is empty`). Without the keyword the
10425
+ // surface stays an identifier and leaks verbatim into the condition's raw
10426
+ // expression, which the core expression parser reads as English. Neither an
10427
+ // ActionType nor a command schema, so no pattern is generated from it.
10428
+ is: { primary: "\u0454", normalized: "is" },
10429
+ // Negative-existence operator (`if no dragHandle set dragHandle to me`). Same
10430
+ // seam as `exists`: without the keyword the surface stays an identifier and
10431
+ // leaks verbatim into the condition's raw expression (behavior-draggable).
10432
+ // Neither an ActionType nor a command schema, so no pattern is generated from it.
10433
+ no: { primary: "\u043D\u0456", normalized: "no" },
9971
10434
  end: { primary: "\u043A\u0456\u043D\u0435\u0446\u044C", normalized: "end" },
9972
10435
  // Advanced
9973
10436
  js: { primary: "js", normalized: "js" },
@@ -10212,6 +10675,12 @@ var init_vietnamese = __esm({
10212
10675
  return: { primary: "tr\u1EA3 v\u1EC1", normalized: "return" },
10213
10676
  then: { primary: "r\u1ED3i", alternatives: ["sau \u0111\xF3", "th\xEC"], normalized: "then" },
10214
10677
  and: { primary: "v\xE0", normalized: "and" },
10678
+ // Comparison operator (`target matches .x`). Without this keyword the surface
10679
+ // stays an identifier and leaks verbatim into the condition's raw expression,
10680
+ // which the core expression parser reads as English (modal-close-backdrop /
10681
+ // focus-trap drop their then-branch). Not an ActionType and has no command
10682
+ // schema, so no pattern is generated from it.
10683
+ matches: { primary: "kh\u1EDBp", normalized: "matches" },
10215
10684
  end: { primary: "k\u1EBFt th\xFAc", normalized: "end" },
10216
10685
  // Advanced
10217
10686
  js: { primary: "js", normalized: "js" },
@@ -10306,7 +10775,10 @@ var init_chinese = __esm({
10306
10775
  result: "\u7ED3\u679C",
10307
10776
  event: "\u4E8B\u4EF6",
10308
10777
  target: "\u76EE\u6807",
10309
- body: "\u4E3B\u4F53"
10778
+ body: "\u4E3B\u4F53",
10779
+ document: "\u6587\u6863",
10780
+ window: "\u7A97\u53E3",
10781
+ detail: "\u8BE6\u60C5"
10310
10782
  },
10311
10783
  possessive: {
10312
10784
  marker: "\u7684",
@@ -10413,6 +10885,11 @@ var init_chinese = __esm({
10413
10885
  return: { primary: "\u8FD4\u56DE", normalized: "return" },
10414
10886
  then: { primary: "\u7136\u540E", alternatives: ["\u63A5\u7740", "\u90A3\u4E48"], normalized: "then" },
10415
10887
  and: { primary: "\u5E76\u4E14", alternatives: ["\u548C", "\u800C\u4E14"], normalized: "and" },
10888
+ // Existence operator (`if #modal exists`). Same seam as `matches`: without the
10889
+ // keyword the surface stays an identifier and leaks verbatim into the
10890
+ // condition's raw expression (if-exists). Neither an ActionType nor a command
10891
+ // schema, so no pattern is generated from it.
10892
+ exists: { primary: "\u5B58\u5728", normalized: "exists" },
10416
10893
  end: { primary: "\u7ED3\u675F", alternatives: ["\u7EC8\u6B62", "\u5B8C"], normalized: "end" },
10417
10894
  // Advanced
10418
10895
  js: { primary: "JS\u6267\u884C", alternatives: ["js"], normalized: "js" },
@@ -10908,8 +11385,22 @@ var init_schema_validator = __esm({
10908
11385
  "select",
10909
11386
  "clear",
10910
11387
  "reset",
10911
- "breakpoint"
11388
+ "breakpoint",
10912
11389
  // Zero-arg debug command
11390
+ // Feature blocks. Their meaning lives in the BODY, not in a head role: `live`
11391
+ // and `intercept` have no head at all, and eventsource/socket/worker's name and
11392
+ // url are structural, not semantic arguments. Giving them roles purely to make
11393
+ // `scoreRoleCoverage` return a non-vacuous number would inject new
11394
+ // `action.role:valueType` entries into the English R1 reference that all 23
11395
+ // other languages must also capture, or the role-fidelity ratchet fires. The
11396
+ // structural layer (`tryParseFeatureBlock`) parses them instead, and derives
11397
+ // confidence from the body — so the `maxScore === 0 → 1` shortcut is never the
11398
+ // thing that scores them.
11399
+ "live",
11400
+ "eventsource",
11401
+ "socket",
11402
+ "worker",
11403
+ "intercept"
10913
11404
  ]);
10914
11405
  }
10915
11406
  });
@@ -10953,7 +11444,7 @@ function getSchema(action) {
10953
11444
  function getDefinedSchemas() {
10954
11445
  return Object.values(commandSchemas).filter((s) => s.roles.length > 0 || s.bareKeyword === true);
10955
11446
  }
10956
- var toggleSchema, addSchema, removeSchema, putSchema, setSchema, bindSchema, liveSchema, eventsourceSchema, socketSchema, workerSchema, interceptSchema, showSchema, hideSchema, onSchema, triggerSchema, waitSchema, fetchSchema, incrementSchema, decrementSchema, appendSchema, prependSchema, logSchema, getCommandSchema, takeSchema, makeSchema, haltSchema, settleSchema, throwSchema, sendSchema, ifSchema, unlessSchema, elseSchema, repeatSchema, forSchema, whileSchema, continueSchema, goSchema, transitionSchema, cloneSchema, focusSchema, blurSchema, emptySchema, openSchema, closeSchema, selectSchema, clearSchema, resetSchema, breakpointSchema, callSchema, returnSchema, jsSchema, asyncSchema, tellSchema, defaultSchema, initSchema, behaviorSchema, installSchema, measureSchema, swapSchema, morphSchema, beepSchema, breakSchema, copySchema, exitSchema, pickSchema, scrollSchema, URL_MARKER_ALL_LANGS, PARTIALS_IN_MARKER_ALL_LANGS, pushSchema, replaceSchema, processSchema, renderSchema, commandSchemas;
11447
+ var toggleSchema, addSchema, removeSchema, putSchema, setSchema, bindSchema, liveSchema, eventsourceSchema, socketSchema, workerSchema, interceptSchema, showSchema, hideSchema, onSchema, triggerSchema, waitSchema, fetchSchema, incrementSchema, decrementSchema, appendSchema, prependSchema, logSchema, getCommandSchema, takeSchema, makeSchema, haltSchema, settleSchema, throwSchema, sendSchema, ifSchema, unlessSchema, elseSchema, repeatSchema, forSchema, whileSchema, continueSchema, URL_MARKER_ALL_LANGS, goSchema, transitionSchema, cloneSchema, focusSchema, blurSchema, emptySchema, openSchema, closeSchema, selectSchema, clearSchema, resetSchema, breakpointSchema, callSchema, returnSchema, jsSchema, asyncSchema, tellSchema, defaultSchema, initSchema, behaviorSchema, installSchema, measureSchema, swapSchema, morphSchema, beepSchema, breakSchema, copySchema, exitSchema, pickSchema, scrollSchema, PARTIALS_IN_MARKER_ALL_LANGS, pushSchema, replaceSchema, processSchema, renderSchema, commandSchemas;
10957
11448
  var init_command_schemas = __esm({
10958
11449
  "src/generators/command-schemas.ts"() {
10959
11450
  toggleSchema = {
@@ -11045,8 +11536,53 @@ var init_command_schemas = __esm({
11045
11536
  default: { type: "reference", value: "me" },
11046
11537
  svoPosition: 2,
11047
11538
  sovPosition: 1,
11048
- markerOverride: { en: "to" }
11049
- // "add .class to #element"
11539
+ // `add` is directional, but every profile's `destination` marker is
11540
+ // LOCATIVE (en on, es en, ar على, zh 在, fr sur, de auf, pt em) because
11541
+ // it also serves `toggle`/`show`. Without a per-language override the
11542
+ // rendered text said "add .class ON #element" in every language but
11543
+ // English — the gap lokascript-learn corrects with 6 of its 16 override
11544
+ // entries. ja に / ko 에 / tr e are already directional, so they keep
11545
+ // the profile default.
11546
+ //
11547
+ // Tier B (2.9): he/id/it/sw were the remaining locatives that this
11548
+ // language actually distinguishes.
11549
+ // he — `על` is "ON"; Hebrew adds with the allative `אל` (`ל` is a bound
11550
+ // prefix, so it cannot stand as a separate marker token).
11551
+ // id — `pada` is "at/on"; `ke` is the directional, and it is what the
11552
+ // i18n corpus already renders for every id destination.
11553
+ // it — `in` is locative; Italian adds with `a` (`aggiungere a`).
11554
+ // sw — `kwenye` is not merely locative, it is sw's EVENT keyword
11555
+ // (`on: 'kwenye'` in the dictionary), so reusing it as a
11556
+ // destination marker collides. `kwa` is the corpus rendering.
11557
+ // hi `में` / ru+uk `в` / th `ใน` / vi `vào` are already the right
11558
+ // container-directional for "add to", and keep the profile default.
11559
+ markerOverride: {
11560
+ en: "to",
11561
+ es: "a",
11562
+ ar: "\u0625\u0644\u0649",
11563
+ zh: "\u5230",
11564
+ fr: "\xE0",
11565
+ de: "zu",
11566
+ pt: "a",
11567
+ he: "\u05D0\u05DC",
11568
+ id: "ke",
11569
+ it: "a",
11570
+ sw: "kwa"
11571
+ },
11572
+ // Each language's previous primary marker (and its alternates) still
11573
+ // parses, so source written against ≤2.8 keeps working.
11574
+ markerLegacy: {
11575
+ es: ["en", "sobre", "hacia"],
11576
+ ar: ["\u0639\u0644\u0649", "\u0641\u064A", "\u0628"],
11577
+ zh: ["\u5728", "\u4E8E"],
11578
+ fr: ["sur", "dans"],
11579
+ de: ["auf", "in"],
11580
+ pt: ["em", "para"],
11581
+ he: ["\u05E2\u05DC", "\u05D1", "\u05DC"],
11582
+ id: ["pada", "di"],
11583
+ it: ["in", "su"],
11584
+ sw: ["kwenye"]
11585
+ }
11050
11586
  }
11051
11587
  ],
11052
11588
  // Runtime error documentation
@@ -11136,12 +11672,39 @@ var init_command_schemas = __esm({
11136
11672
  svoPosition: 2,
11137
11673
  sovPosition: 2,
11138
11674
  // SOV: destination comes second (に/에/a marker)
11139
- markerOverride: { en: "into" },
11140
- // "put 'hello' into #output"
11675
+ // "put 'hello' into #output" — directional, so the same locative-default
11676
+ // correction as `add`. es `en` and pt `em` are already right for "into",
11677
+ // as are ja に / ko 에 / tr e; only ar/zh/fr/de need an override.
11678
+ //
11679
+ // Tier B (2.9): `put` is ILLATIVE, so it diverges from `add` where the
11680
+ // two senses differ. he takes `ב` ("in/into" — `שים ב`), NOT the allative
11681
+ // `אל` that `add`/`go` take. it keeps its locative `in` (`mettere in`) —
11682
+ // it is `add`/`go` that needed `a`. id/sw change for the same reason as
11683
+ // `add` (directional / event-keyword collision). hi `में`, ru+uk `в`,
11684
+ // th `ใน` and vi `vào` are all already the illative.
11685
+ markerOverride: {
11686
+ en: "into",
11687
+ ar: "\u0641\u064A",
11688
+ zh: "\u5230",
11689
+ fr: "dans",
11690
+ de: "in",
11691
+ he: "\u05D1",
11692
+ id: "ke",
11693
+ sw: "kwa"
11694
+ },
11141
11695
  // `before` / `after` are alternate position markers; the matched marker
11142
11696
  // is recorded as a literal in the `method` role (a derived role with no
11143
11697
  // surface form of its own — populated by schema-driven role inference).
11144
11698
  markerVariants: { en: ["before", "after"] },
11699
+ markerLegacy: {
11700
+ ar: ["\u0639\u0644\u0649", "\u0625\u0644\u0649", "\u0628"],
11701
+ zh: ["\u5728", "\u4E8E"],
11702
+ fr: ["sur", "\xE0"],
11703
+ de: ["auf", "zu"],
11704
+ he: ["\u05E2\u05DC", "\u05D0\u05DC", "\u05DC"],
11705
+ id: ["pada", "di"],
11706
+ sw: ["kwenye"]
11707
+ },
11145
11708
  methodCarrier: "method"
11146
11709
  }
11147
11710
  ],
@@ -11276,8 +11839,11 @@ var init_command_schemas = __esm({
11276
11839
  // ending in a vowel (`doğru ya` = "true" in set-attribute). markerOverride
11277
11840
  // is a single string, so the generated tr set patterns carried only `e`
11278
11841
  // and set-attribute fell to the role-scrambling generic SOV extraction.
11279
- // markerVariants supplies the allomorphs the SOV two-role generators merge
11280
- // in as marker alternatives. See STRUCTURAL_ARCS_ROADMAP.md (tr set-attribute).
11842
+ // markerVariants supplies the allomorphs, merged in as marker alternatives.
11843
+ // Until 2026-07-25 only the SOV two-role generators merged them, so this
11844
+ // worked ONLY inside an event handler: `@disabled i doğru ya ayarla` did
11845
+ // not parse as a bare command while `tıklama da @disabled i doğru ya
11846
+ // ayarla` did. See STRUCTURAL_ARCS_ROADMAP.md (tr set-attribute).
11281
11847
  markerVariants: {
11282
11848
  tr: ["e", "a", "ye", "ya"]
11283
11849
  }
@@ -11402,7 +11968,13 @@ var init_command_schemas = __esm({
11402
11968
  role: "source",
11403
11969
  description: "The element or property to bind to",
11404
11970
  required: true,
11405
- expectedTypes: ["selector", "reference", "expression"],
11971
+ // 'property-path' opts this role into the "of"-possessive matcher, so the
11972
+ // property-first render of `bind $x to #y's prop` (es `valor de #picker`,
11973
+ // ar `قيمة لـ #picker`) keeps its owner selector instead of collapsing to
11974
+ // the bare property word; see pattern-matcher tryMatchOfPossessiveExpression.
11975
+ // The selector-first languages (en `#picker's value`, ja `#pickerの 値`)
11976
+ // already reached property-path through tryMatchPossessiveSelectorExpression.
11977
+ expectedTypes: ["selector", "reference", "expression", "property-path"],
11406
11978
  svoPosition: 2,
11407
11979
  sovPosition: 2,
11408
11980
  // Element mirrors `set`/`add`/`put`'s value ("to") marking per language.
@@ -11589,7 +12161,15 @@ var init_command_schemas = __esm({
11589
12161
  expectedTypes: ["literal", "expression"],
11590
12162
  // expression for custom/namespaced event names
11591
12163
  svoPosition: 1,
11592
- sovPosition: 2
12164
+ sovPosition: 2,
12165
+ // hi/qu/bn mark trigger's event ACCUSATIVELY (`draggable:start को ट्रिगर`,
12166
+ // `draggable:start ta kichay`, `draggable:start কে ট্রিগার` — the corpus
12167
+ // renderings), but their profile-wide event marker is the on-handler one
12168
+ // (hi पर, qu locative pi, bn এ), so the generated SOV pattern never
12169
+ // matched and the whole line fell through to the on-handler reading (hi)
12170
+ // or failed outright (qu/bn). ja/ko were immune only because their event
12171
+ // marker IS the object particle (を / 을·를). #588 markerVariants machinery.
12172
+ markerVariants: { hi: ["\u0915\u094B"], qu: ["ta"], bn: ["\u0995\u09C7"] }
11593
12173
  },
11594
12174
  {
11595
12175
  role: "destination",
@@ -11635,14 +12215,26 @@ var init_command_schemas = __esm({
11635
12215
  renderOverride: { en: "" }
11636
12216
  // "fetch /api" (rendering — no preposition)
11637
12217
  },
12218
+ {
12219
+ role: "style",
12220
+ description: "Request options object (method, headers, body, credentials\u2026)",
12221
+ required: false,
12222
+ // expression-ONLY: the pattern matcher routes a `{ … }` run in an
12223
+ // expression-only slot through its object-literal fold, which preserves the
12224
+ // source text so the expression parser can build a real objectLiteral.
12225
+ // `style` is the role whose marker is `with` in every language profile.
12226
+ expectedTypes: ["expression"],
12227
+ svoPosition: 2,
12228
+ sovPosition: 2
12229
+ },
11638
12230
  {
11639
12231
  role: "responseType",
11640
12232
  description: "Response format (json, text, html, blob, etc.)",
11641
12233
  required: false,
11642
12234
  expectedTypes: ["literal", "expression"],
11643
12235
  // json/text/html are identifiers → expression type
11644
- svoPosition: 2,
11645
- sovPosition: 2,
12236
+ svoPosition: 3,
12237
+ sovPosition: 3,
11646
12238
  markerOverride: { en: "as" }
11647
12239
  // "fetch /api as json" — needed by schema-driven role inference
11648
12240
  },
@@ -11651,16 +12243,16 @@ var init_command_schemas = __esm({
11651
12243
  description: "HTTP method (GET, POST, etc.)",
11652
12244
  required: false,
11653
12245
  expectedTypes: ["literal"],
11654
- svoPosition: 3,
11655
- sovPosition: 3
12246
+ svoPosition: 4,
12247
+ sovPosition: 4
11656
12248
  },
11657
12249
  {
11658
12250
  role: "destination",
11659
12251
  description: "Where to store the result",
11660
12252
  required: false,
11661
12253
  expectedTypes: ["selector", "reference"],
11662
- svoPosition: 4,
11663
- sovPosition: 4
12254
+ svoPosition: 5,
12255
+ sovPosition: 5
11664
12256
  }
11665
12257
  ]
11666
12258
  };
@@ -12144,6 +12736,32 @@ var init_command_schemas = __esm({
12144
12736
  roles: []
12145
12737
  // No roles
12146
12738
  };
12739
+ URL_MARKER_ALL_LANGS = {
12740
+ en: "url",
12741
+ es: "url",
12742
+ pt: "url",
12743
+ fr: "url",
12744
+ de: "url",
12745
+ it: "url",
12746
+ ja: "url",
12747
+ ko: "url",
12748
+ zh: "url",
12749
+ ar: "url",
12750
+ he: "url",
12751
+ hi: "url",
12752
+ bn: "url",
12753
+ tr: "url",
12754
+ ru: "url",
12755
+ uk: "url",
12756
+ pl: "url",
12757
+ id: "url",
12758
+ vi: "url",
12759
+ th: "url",
12760
+ ms: "url",
12761
+ tl: "url",
12762
+ sw: "url",
12763
+ qu: "url"
12764
+ };
12147
12765
  goSchema = {
12148
12766
  action: "go",
12149
12767
  description: "Navigate to a URL",
@@ -12157,17 +12775,113 @@ var init_command_schemas = __esm({
12157
12775
  expectedTypes: ["literal", "expression"],
12158
12776
  svoPosition: 1,
12159
12777
  sovPosition: 1,
12160
- markerOverride: { en: "to" },
12161
- // "go to /page" (parsing)
12162
- renderOverride: { en: "" },
12163
- // "go /page" (rendering — no preposition)
12778
+ // "go to /page" (parsing). Directional, so the same locative-default
12779
+ // correction as `add`/`put`.
12780
+ //
12781
+ // Tier B (2.9): `go` is pure ALLATIVE — motion toward a target — so it
12782
+ // needs the directional in more languages than `add`/`put` do, including
12783
+ // ones where a container-locative was fine for those two.
12784
+ // he — `אל` ("toward"), as `add`; `לך על url` read "go ON url".
12785
+ // hi — `पर`: Hindi navigates to a page with `पर जाएं`; `में` is
12786
+ // "go INTO", which is entering a place, not opening a URL.
12787
+ // id — `ke`, as `add`.
12788
+ // it — `a`: `andare a` for a specific target (`andare in` is for
12789
+ // regions — `andare in Italia`).
12790
+ // ru/uk — `на`: `перейти на сторінку` is the navigation idiom; `в`
12791
+ // ("into") is right for `add`/`put` but not for opening a page.
12792
+ // sw — `kwa`, as `add`.
12793
+ // th is NOT here — it renders bare, with zh and vi; see below.
12794
+ markerOverride: {
12795
+ en: "to",
12796
+ es: "a",
12797
+ ar: "\u0625\u0644\u0649",
12798
+ fr: "\xE0",
12799
+ de: "zu",
12800
+ pt: "para",
12801
+ he: "\u05D0\u05DC",
12802
+ hi: "\u092A\u0930",
12803
+ id: "ke",
12804
+ it: "a",
12805
+ ru: "\u043D\u0430",
12806
+ sw: "kwa",
12807
+ uk: "\u043D\u0430"
12808
+ },
12809
+ // "go /page" (rendering — no preposition).
12810
+ //
12811
+ // zh, vi and th render BARE.
12812
+ //
12813
+ // zh and vi because their `go` keyword already encodes the direction, so
12814
+ // any destination marker is a second one: zh `前往` is "proceed-to"
12815
+ // (`前往 到 url` = "proceed-to to url") and vi `đi đến` is literally
12816
+ // "go to" (`đi đến vào url` = "go-to into url"). Both are corrected in
12817
+ // the i18n corpus in the same change
12818
+ // (`patterns-reference/scripts/fix-translations.sql`).
12819
+ //
12820
+ // th because Thai motion verbs take a BARE destination — `ไปบ้าน`
12821
+ // ("go home"), `ไปโรงเรียน` ("go school") — so `ไป url` is the idiomatic
12822
+ // form. The profile default rendered `ไป ใน url` ("go IN url"), which is
12823
+ // what needed fixing; the obvious replacement `ยัง` (giving the formal
12824
+ // `ไปยัง`) is rejected because `ยัง` is also the very common adverb
12825
+ // "still/yet", and the V4 vocab gate correctly refuses to classify it as
12826
+ // a particle — promoting it would mis-tokenize ordinary Thai.
12827
+ //
12828
+ // Parsing is unaffected for all three: none has a `markerOverride`, so
12829
+ // each stays on the profile-default branch and keeps accepting its old
12830
+ // markers (th `ใน` / `ไปยัง`) from the profile itself.
12831
+ renderOverride: { en: "", zh: "", vi: "", th: "" },
12164
12832
  // `go back` renders the destination bare in en (history nav has no `to`),
12165
12833
  // and he/zh render it with their PATIENT marker (לך את back / 前往 把 back)
12166
12834
  // while go-url keeps the destination marker (לך על url / 前往 到 url) —
12167
12835
  // the corpus is ground truth, so en's `to` is optional and he/zh accept
12168
12836
  // the patient particle as a destination-marker alternative, scoped to go.
12169
- markerOptional: { en: true },
12170
- markerVariants: { he: ["\u05D0\u05EA"], zh: ["\u628A"] }
12837
+ // The render side drops the preposition for these four, so the parse
12838
+ // side cannot require it: `go /page`, `前往 url`, `đi đến url`, `ไป url`
12839
+ // must parse alongside the marked forms the profile still accepts.
12840
+ markerOptional: { en: true, zh: true, vi: true, th: true },
12841
+ // zh renders `前往 把 back` with its PATIENT particle before go's
12842
+ // destination — a synonym here, not a distinct shape, so it is accepted as
12843
+ // a marker alternative scoped to go. he's `את` is the same thing and sits
12844
+ // in `markerLegacy` below: it moved there in #763 because the two fields
12845
+ // were then read by DIFFERENT branches, so leaving it here silently
12846
+ // stopped `לך את back` parsing the moment he gained a `markerOverride`.
12847
+ // Both fields now merge on both branches (`schemaMarkerAlternatives`), so
12848
+ // that trap is gone and the split is historical.
12849
+ markerVariants: { zh: ["\u628A"] },
12850
+ markerLegacy: {
12851
+ es: ["en", "sobre", "hacia"],
12852
+ ar: ["\u0639\u0644\u0649", "\u0641\u064A", "\u0628"],
12853
+ fr: ["sur", "dans"],
12854
+ de: ["auf", "in"],
12855
+ pt: ["em", "a"],
12856
+ // `את` is he's PATIENT particle, which the transformer renders before
12857
+ // go's destination in `go back` (`לך את back`) — a parse-only synonym
12858
+ // here, never rendered, which is exactly what markerLegacy is for.
12859
+ he: ["\u05E2\u05DC", "\u05D1", "\u05DC", "\u05D0\u05EA"],
12860
+ hi: ["\u092E\u0947\u0902"],
12861
+ id: ["pada", "di"],
12862
+ it: ["in", "su"],
12863
+ ru: ["\u0432", "\u043A"],
12864
+ sw: ["kwenye"],
12865
+ uk: ["\u0432", "\u0434\u043E"]
12866
+ // zh, vi and th are NOT listed: none has a markerOverride, so all three
12867
+ // stay on the profile-default branch and keep accepting their old
12868
+ // markers from the profile itself. Only their RENDERING changed.
12869
+ // Listing them here would be dead config — markerLegacy is read ONLY by
12870
+ // the override branch.
12871
+ }
12872
+ }
12873
+ ],
12874
+ // `go to url "/page"` — without this variant the destination captures the
12875
+ // bare word `url` and the actual URL is dropped as tolerated-trailing text,
12876
+ // in en and therefore in every render (the go-url corpus row). The required
12877
+ // `url` literal keeps the variant inert for `go back` / scroll forms.
12878
+ rolePrefixLiteralVariants: [
12879
+ {
12880
+ role: "destination",
12881
+ literal: URL_MARKER_ALL_LANGS,
12882
+ idSuffix: "url",
12883
+ priorityDelta: 5,
12884
+ methodCarrier: "method"
12171
12885
  }
12172
12886
  ]
12173
12887
  };
@@ -12799,7 +13513,27 @@ var init_command_schemas = __esm({
12799
13513
  th: "\u0E14\u0E49\u0E27\u0E22",
12800
13514
  vi: "v\u1EDBi",
12801
13515
  he: "\u05E2\u05DD",
12802
- zh: "\u7528"
13516
+ zh: "\u7528",
13517
+ // SOV/postpositional with-words. These follow the patient (`#b से`,
13518
+ // `#b দিয়ে`), matching the i18n `with` emission. Without them the SOV
13519
+ // patient-first swap pattern's trailing group (which binds the second
13520
+ // element to `destination`) had only the locative dest-marker (hi में,
13521
+ // bn তে) as its alternatives, so `#b <with-word>` never bound and #b
13522
+ // dropped — hi/bn/tr/qu rendered the invalid `swap with #a`. ja/ko
13523
+ // escaped only because their dest-marker alternatives already carry the
13524
+ // instrumental (で / 로). See generateSOVPatientFirstEventHandlerPattern.
13525
+ hi: "\u0938\u0947",
13526
+ bn: "\u09A6\u09BF\u09AF\u09BC\u09C7",
13527
+ tr: "ile",
13528
+ qu: "wan",
13529
+ // VSO with-words. The corpus puts the with-element AFTER the event
13530
+ // (`استبدل #a عند نقر بـ#b`, `palitan_pwesto #a kapag click nang #b`);
13531
+ // the vso-verb-first generator's swap-gated trailing group binds it to
13532
+ // `destination` via these words. ar's `بـ` is the bi-proclitic + tatweel
13533
+ // exactly as the ArabicProcliticExtractor emits it (glued to a selector
13534
+ // sigil). See generateVSOVerbFirstEventHandlerPattern.
13535
+ ar: "\u0628\u0640",
13536
+ tl: "nang"
12803
13537
  }
12804
13538
  }
12805
13539
  ]
@@ -12888,13 +13622,13 @@ var init_command_schemas = __esm({
12888
13622
  };
12889
13623
  pickSchema = {
12890
13624
  action: "pick",
12891
- description: "Select a random element from a collection",
13625
+ description: "Select item(s), character(s), a range, first/last/random N, or regex matches from a root",
12892
13626
  category: "variable",
12893
13627
  primaryRole: "patient",
12894
13628
  roles: [
12895
13629
  {
12896
13630
  role: "patient",
12897
- description: "The items to pick from",
13631
+ description: "The range/count/index/regex argument to pick",
12898
13632
  required: true,
12899
13633
  expectedTypes: ["literal", "expression", "reference"],
12900
13634
  svoPosition: 1,
@@ -12902,7 +13636,7 @@ var init_command_schemas = __esm({
12902
13636
  },
12903
13637
  {
12904
13638
  role: "source",
12905
- description: 'The array to pick from (with "from" keyword)',
13639
+ description: 'The root to pick from (with "of"/"from" keyword)',
12906
13640
  required: false,
12907
13641
  expectedTypes: ["reference", "expression"],
12908
13642
  svoPosition: 2,
@@ -12944,32 +13678,6 @@ var init_command_schemas = __esm({
12944
13678
  }
12945
13679
  ]
12946
13680
  };
12947
- URL_MARKER_ALL_LANGS = {
12948
- en: "url",
12949
- es: "url",
12950
- pt: "url",
12951
- fr: "url",
12952
- de: "url",
12953
- it: "url",
12954
- ja: "url",
12955
- ko: "url",
12956
- zh: "url",
12957
- ar: "url",
12958
- he: "url",
12959
- hi: "url",
12960
- bn: "url",
12961
- tr: "url",
12962
- ru: "url",
12963
- uk: "url",
12964
- pl: "url",
12965
- id: "url",
12966
- vi: "url",
12967
- th: "url",
12968
- ms: "url",
12969
- tl: "url",
12970
- sw: "url",
12971
- qu: "url"
12972
- };
12973
13681
  PARTIALS_IN_MARKER_ALL_LANGS = {
12974
13682
  en: "partials in",
12975
13683
  es: "partials in",
@@ -13162,7 +13870,7 @@ var init_command_schemas = __esm({
13162
13870
  roles: []
13163
13871
  }
13164
13872
  };
13165
- if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
13873
+ if (typeof process !== "undefined" && process.env.LOKASCRIPT_SCHEMA_VALIDATION === "1") {
13166
13874
  Promise.resolve().then(() => (init_schema_validator(), schema_validator_exports)).then(({ validateAllSchemas: validateAllSchemas2, formatValidationResults: formatValidationResults2 }) => {
13167
13875
  const validations = validateAllSchemas2(commandSchemas);
13168
13876
  if (validations.size > 0) {
@@ -14643,17 +15351,48 @@ var init_generic_extractors = __esm({
14643
15351
  });
14644
15352
 
14645
15353
  // src/tokenizers/extractors/css-selector.ts
15354
+ function consumePseudoSegments(input, pos2) {
15355
+ let end = pos2;
15356
+ while (end < input.length && input[end] === ":") {
15357
+ const m = input.slice(end).match(/^::?[a-zA-Z][a-zA-Z0-9-]*/);
15358
+ if (!m) break;
15359
+ let segEnd = end + m[0].length;
15360
+ if (input[segEnd] === "(") {
15361
+ let depth = 0;
15362
+ let p = segEnd;
15363
+ while (p < input.length) {
15364
+ if (input[p] === "(") depth++;
15365
+ else if (input[p] === ")") {
15366
+ depth--;
15367
+ if (depth === 0) {
15368
+ p++;
15369
+ break;
15370
+ }
15371
+ }
15372
+ p++;
15373
+ }
15374
+ if (depth !== 0) break;
15375
+ segEnd = p;
15376
+ }
15377
+ end = segEnd;
15378
+ }
15379
+ return end;
15380
+ }
14646
15381
  function extractCssSelector(input, position) {
14647
15382
  const char = input[position];
14648
15383
  if (char === "#") {
14649
15384
  const match = input.slice(position).match(/^#[a-zA-Z_][\w-]*/);
14650
- return match ? match[0] : null;
15385
+ if (!match) return null;
15386
+ const end = consumePseudoSegments(input, position + match[0].length);
15387
+ return input.slice(position, end);
14651
15388
  }
14652
15389
  if (char === ".") {
14653
15390
  const dynamic = input.slice(position).match(/^\.\{[a-zA-Z_$][\w$]*\}/);
14654
15391
  if (dynamic) return dynamic[0];
14655
15392
  const match = input.slice(position).match(/^\.[a-zA-Z_][\w-]*/);
14656
- return match ? match[0] : null;
15393
+ if (!match) return null;
15394
+ const end = consumePseudoSegments(input, position + match[0].length);
15395
+ return input.slice(position, end);
14657
15396
  }
14658
15397
  if (char === "@") {
14659
15398
  const match = input.slice(position).match(/^@[a-zA-Z_][\w-]*/);
@@ -14671,7 +15410,8 @@ function extractCssSelector(input, position) {
14671
15410
  if (input[end] === "]") {
14672
15411
  depth--;
14673
15412
  if (depth === 0) {
14674
- return input.slice(position, end + 1);
15413
+ const pseudoEnd = consumePseudoSegments(input, end + 1);
15414
+ return input.slice(position, pseudoEnd);
14675
15415
  }
14676
15416
  }
14677
15417
  end++;
@@ -14679,7 +15419,9 @@ function extractCssSelector(input, position) {
14679
15419
  return null;
14680
15420
  }
14681
15421
  if (char === "<") {
14682
- const match = input.slice(position).match(/^<(?=[\w.#[])[\w-]*(?:[#.][\w-]+|\[[^\]]+\])*\s*\/>/);
15422
+ const match = input.slice(position).match(
15423
+ /^<(?=[\w.#[])[\w-]*(?:[#.][\w-]+|\[[^\]]+\]|::?[a-zA-Z][a-zA-Z0-9-]*(?:\([^)]*\))?)*\s*\/>/
15424
+ );
14683
15425
  return match ? match[0] : null;
14684
15426
  }
14685
15427
  return null;
@@ -14745,29 +15487,38 @@ var init_event_modifier = __esm({
14745
15487
  });
14746
15488
 
14747
15489
  // src/tokenizers/extractors/url.ts
15490
+ function findInterpolationEnd(input, start) {
15491
+ let depth = 1;
15492
+ for (let i = start; i < input.length; i++) {
15493
+ const ch = input[i];
15494
+ if (ch === "{") depth++;
15495
+ else if (ch === "}" && --depth === 0) return i + 1;
15496
+ }
15497
+ return -1;
15498
+ }
14748
15499
  function extractUrl(input, position) {
14749
15500
  const remaining = input.slice(position);
14750
- if (remaining.startsWith("http://") || remaining.startsWith("https://")) {
14751
- const match = remaining.match(/^https?:\/\/[^\s]*/);
14752
- return match ? match[0] : null;
14753
- }
14754
- if (remaining.startsWith("//")) {
14755
- const match = remaining.match(/^\/\/[^\s]*/);
14756
- return match ? match[0] : null;
14757
- }
14758
- if (remaining.startsWith("./") || remaining.startsWith("../")) {
14759
- const match = remaining.match(/^\.\.?\/[^\s]*/);
14760
- return match ? match[0] : null;
14761
- }
14762
- if (remaining.startsWith("/")) {
14763
- const match = remaining.match(/^\/[^\s]*/);
14764
- return match ? match[0] : null;
15501
+ const prefix = URL_PREFIXES.find((p) => remaining.startsWith(p));
15502
+ if (!prefix) return null;
15503
+ let i = prefix.length;
15504
+ while (i < remaining.length) {
15505
+ const ch = remaining[i];
15506
+ if (ch === "$" && remaining[i + 1] === "{") {
15507
+ const end = findInterpolationEnd(remaining, i + 2);
15508
+ if (end !== -1) {
15509
+ i = end;
15510
+ continue;
15511
+ }
15512
+ }
15513
+ if (/\s/.test(ch)) break;
15514
+ i++;
14765
15515
  }
14766
- return null;
15516
+ return remaining.slice(0, i);
14767
15517
  }
14768
- var UrlExtractor;
15518
+ var URL_PREFIXES, UrlExtractor;
14769
15519
  var init_url = __esm({
14770
15520
  "src/tokenizers/extractors/url.ts"() {
15521
+ URL_PREFIXES = ["http://", "https://", "//", "./", "../", "/"];
14771
15522
  UrlExtractor = class {
14772
15523
  constructor() {
14773
15524
  this.name = "url";
@@ -15860,6 +16611,18 @@ var init_arabic_proclitic = __esm({
15860
16611
  checkPos++;
15861
16612
  }
15862
16613
  if (remainingLength < 2) {
16614
+ const runIsTatweelOnly = remainingLength >= 1 && input.slice(nextPos, checkPos).split("").every((c) => c === "\u0640");
16615
+ const followChar = input[checkPos];
16616
+ if (entry.type === "preposition" && runIsTatweelOnly && (followChar === "#" || followChar === ".")) {
16617
+ return {
16618
+ value: input.slice(position, checkPos),
16619
+ length: checkPos - position,
16620
+ metadata: {
16621
+ procliticType: entry.type,
16622
+ normalized: entry.normalized
16623
+ }
16624
+ };
16625
+ }
15863
16626
  return null;
15864
16627
  }
15865
16628
  return {
@@ -16240,6 +17003,17 @@ var init_hindi_keyword = __esm({
16240
17003
  pos2 = extPos;
16241
17004
  }
16242
17005
  }
17006
+ if (this.context && input[pos2] === "_" && pos2 + 1 < input.length && isDevanagari(input[pos2 + 1])) {
17007
+ let extPos = pos2;
17008
+ let ext = word;
17009
+ while (extPos < input.length && (input[extPos] === "_" || isDevanagari(input[extPos]))) {
17010
+ ext += input[extPos++];
17011
+ }
17012
+ if (this.context.lookupKeyword(ext)) {
17013
+ word = ext;
17014
+ pos2 = extPos;
17015
+ }
17016
+ }
16243
17017
  if (!word) return null;
16244
17018
  const keywordEntry = this.context.lookupKeyword(word);
16245
17019
  const normalized2 = keywordEntry && keywordEntry.normalized !== keywordEntry.native ? keywordEntry.normalized : void 0;
@@ -16301,9 +17075,11 @@ var init_hindi_particle = __esm({
16301
17075
  }
16302
17076
  setContext(context) {
16303
17077
  this._context = context;
16304
- void this._context;
16305
17078
  }
16306
17079
  canExtract(input, position) {
17080
+ if (this.underscoreJoinedKeyword(input, position)) {
17081
+ return false;
17082
+ }
16307
17083
  for (const [particle] of COMPOUND_POSTPOSITIONS) {
16308
17084
  if (input.startsWith(particle, position)) {
16309
17085
  return true;
@@ -16317,7 +17093,27 @@ var init_hindi_particle = __esm({
16317
17093
  }
16318
17094
  return SINGLE_POSTPOSITIONS.has(word);
16319
17095
  }
17096
+ /**
17097
+ * True when the Devanagari run at `position` is `_`-joined into a keyword the
17098
+ * profile/EXTRAS registered (के_रूप_में). See the note in canExtract.
17099
+ */
17100
+ underscoreJoinedKeyword(input, position) {
17101
+ if (!this._context) return false;
17102
+ let pos2 = position;
17103
+ while (pos2 < input.length && this.isDevanagari(input[pos2])) pos2++;
17104
+ if (input[pos2] !== "_" || pos2 + 1 >= input.length || !this.isDevanagari(input[pos2 + 1])) {
17105
+ return false;
17106
+ }
17107
+ let ext = input.slice(position, pos2);
17108
+ while (pos2 < input.length && (input[pos2] === "_" || this.isDevanagari(input[pos2]))) {
17109
+ ext += input[pos2++];
17110
+ }
17111
+ return Boolean(this._context.lookupKeyword(ext));
17112
+ }
16320
17113
  extract(input, position) {
17114
+ if (this.underscoreJoinedKeyword(input, position)) {
17115
+ return null;
17116
+ }
16321
17117
  for (const [particle, metadata2] of COMPOUND_POSTPOSITIONS) {
16322
17118
  if (input.startsWith(particle, position)) {
16323
17119
  return {
@@ -16941,6 +17737,17 @@ var init_indonesian_keyword = __esm({
16941
17737
  while (pos2 < input.length && isIndonesianIdentifierChar(input[pos2])) {
16942
17738
  word += input[pos2++];
16943
17739
  }
17740
+ if (this.context && pos2 < input.length && input[pos2] === "_") {
17741
+ let extPos = pos2;
17742
+ let ext = word;
17743
+ while (extPos < input.length && (input[extPos] === "_" || isIndonesianIdentifierChar(input[extPos]))) {
17744
+ ext += input[extPos++];
17745
+ }
17746
+ if (this.context.lookupKeyword(ext.toLowerCase())) {
17747
+ word = ext;
17748
+ pos2 = extPos;
17749
+ }
17750
+ }
16944
17751
  if (!word) return null;
16945
17752
  const lower = word.toLowerCase();
16946
17753
  const isPreposition = PREPOSITIONS5.has(lower);
@@ -17391,14 +18198,16 @@ var init_quechua_keyword = __esm({
17391
18198
  metadata: { suffixValue: hyphenSuffix.toLowerCase() }
17392
18199
  };
17393
18200
  }
17394
- const maxKeywordLen = 12;
18201
+ const maxKeywordLen = 13;
17395
18202
  for (let len = Math.min(maxKeywordLen, input.length - startPos); len >= 2; len--) {
17396
18203
  const candidate = input.slice(startPos, startPos + len);
17397
18204
  const after = input[startPos + len];
17398
18205
  if (after !== void 0 && isQuechuaLetter(after)) continue;
17399
18206
  let allQuechua = true;
17400
18207
  for (let i = 0; i < candidate.length; i++) {
17401
- if (!isQuechuaLetter(candidate[i])) {
18208
+ const ch = candidate[i];
18209
+ if (ch === "_" && i > 0 && i < candidate.length - 1) continue;
18210
+ if (!isQuechuaLetter(ch)) {
17402
18211
  allQuechua = false;
17403
18212
  break;
17404
18213
  }
@@ -18285,6 +19094,12 @@ var init_japanese2 = __esm({
18285
19094
  { native: "\u524D", normalized: "previous" },
18286
19095
  { native: "\u6700\u3082\u8FD1\u3044", normalized: "closest" },
18287
19096
  { native: "\u89AA", normalized: "parent" },
19097
+ // Containment (`first <button/> in .modal`): the i18n dict emits の中, which
19098
+ // otherwise splits の(particle) + 中(identifier) — the stray identifier broke
19099
+ // the generated focus pattern's operand run (focus-trap Family G; tr/bn/hi
19100
+ // work because their in-word is one token). Whole-token entry mirrors en's
19101
+ // keyword `in` mid-run geometry.
19102
+ { native: "\u306E\u4E2D", normalized: "in" },
18288
19103
  // Events
18289
19104
  { native: "\u30AF\u30EA\u30C3\u30AF", normalized: "click" },
18290
19105
  { native: "\u5909\u66F4", normalized: "change" },
@@ -18313,6 +19128,14 @@ var init_japanese2 = __esm({
18313
19128
  // References (alternative forms not in profile)
18314
19129
  { native: "\u79C1", normalized: "me" },
18315
19130
  // Alternative to 自分 (jibun)
19131
+ // The i18n dict emits 対象 for `target` while the profile carries ターゲット, so the
19132
+ // word the authored corpus actually uses did not lex as a keyword and leaked into
19133
+ // the condition's raw expression (`if 対象 一致する .modal-backdrop`). Additive: the
19134
+ // profile's ターゲット stays registered. Must land WITH the `matches` keyword —
19135
+ // fixing the operand alone leaves the operator leaking and vice versa (see the
19136
+ // R2 note in japanese.ts's profile `matches` entry).
19137
+ { native: "\u5BFE\u8C61", normalized: "target" },
19138
+ // Alternative to ターゲット (the dict's word)
18316
19139
  // Note: Attached particle forms (を切り替え, を追加, etc.) are intentionally NOT included
18317
19140
  // because they would cause ambiguous parsing. The separate particle + verb pattern
18318
19141
  // (を + 切り替え) is preferred for consistent semantic analysis.
@@ -18324,7 +19147,11 @@ var init_japanese2 = __esm({
18324
19147
  { native: "\u79D2", normalized: "s" },
18325
19148
  { native: "\u30DF\u30EA\u79D2", normalized: "ms" },
18326
19149
  { native: "\u5206", normalized: "m" },
18327
- { native: "\u6642\u9593", normalized: "h" }
19150
+ { native: "\u6642\u9593", normalized: "h" },
19151
+ { native: "\u542B\u3080", normalized: "inclusive" },
19152
+ { native: "\u9664\u304F", normalized: "exclusive" },
19153
+ { native: "\u6587\u5B57", normalized: "characters" },
19154
+ { native: "\u30E9\u30F3\u30C0\u30E0", normalized: "random" }
18328
19155
  ];
18329
19156
  JapaneseTokenizer = class extends BaseTokenizer {
18330
19157
  constructor() {
@@ -18758,6 +19585,11 @@ var init_korean2 = __esm({
18758
19585
  { native: "\uAC70\uC9D3", normalized: "false" },
18759
19586
  { native: "\uB110", normalized: "null" },
18760
19587
  { native: "\uBBF8\uC815\uC758", normalized: "undefined" },
19588
+ // The corpus authors 정의안됨 ("not defined") for undefined (behavior-removable/
19589
+ // sortable `만약 X 이다 정의안됨`); without a whole-token entry it shatters into
19590
+ // 정 + 의안됨, leaking the invalid `is 정 의안됨`. Longest-first scan (cap 6)
19591
+ // matches the 4-char compound whole, like 마우스다운 above.
19592
+ { native: "\uC815\uC758\uC548\uB428", normalized: "undefined" },
18761
19593
  // Positional
18762
19594
  { native: "\uCCAB\uBC88\uC9F8", normalized: "first" },
18763
19595
  { native: "\uB9C8\uC9C0\uB9C9", normalized: "last" },
@@ -18765,6 +19597,11 @@ var init_korean2 = __esm({
18765
19597
  { native: "\uC774\uC804", normalized: "previous" },
18766
19598
  { native: "\uAC00\uC7A5\uAC00\uAE4C\uC6B4", normalized: "closest" },
18767
19599
  { native: "\uBD80\uBAA8", normalized: "parent" },
19600
+ // Containment (`first <button/> in .modal`): the i18n dict emits 안에, which
19601
+ // otherwise splits 안(identifier) + 에(particle) — the stray identifier broke
19602
+ // the generated focus pattern's operand run (focus-trap Family G). Whole-token
19603
+ // entry mirrors en's keyword `in` mid-run geometry.
19604
+ { native: "\uC548\uC5D0", normalized: "in" },
18768
19605
  // Events
18769
19606
  { native: "\uD074\uB9AD", normalized: "click" },
18770
19607
  { native: "\uB354\uBE14\uD074\uB9AD", normalized: "dblclick" },
@@ -18797,7 +19634,11 @@ var init_korean2 = __esm({
18797
19634
  { native: "\uCD08", normalized: "s" },
18798
19635
  { native: "\uBC00\uB9AC\uCD08", normalized: "ms" },
18799
19636
  { native: "\uBD84", normalized: "m" },
18800
- { native: "\uC2DC\uAC04", normalized: "h" }
19637
+ { native: "\uC2DC\uAC04", normalized: "h" },
19638
+ { native: "\uD3EC\uD568", normalized: "inclusive" },
19639
+ { native: "\uC81C\uC678", normalized: "exclusive" },
19640
+ { native: "\uBB38\uC790", normalized: "characters" },
19641
+ { native: "\uBB34\uC791\uC704", normalized: "random" }
18801
19642
  ];
18802
19643
  KoreanTokenizer = class extends BaseTokenizer {
18803
19644
  constructor() {
@@ -19066,6 +19907,17 @@ var init_arabic2 = __esm({
19066
19907
  // ka- (like, as)
19067
19908
  ]);
19068
19909
  ARABIC_EXTRAS = [
19910
+ // References (alternative forms not in profile). The i18n dict emits the BARE
19911
+ // nouns هدف/نتيجة while the profile carries the definite-article forms
19912
+ // الهدف/النتيجة, so the words the authored corpus actually uses did not lex as
19913
+ // keywords and leaked into the condition's raw expression (`if هدف يطابق …`).
19914
+ // Additive: the profile's الهدف/النتيجة stay registered. Same direction as the
19915
+ // profile's `body: 'جسم'` note — align to what the dict emits, never the reverse
19916
+ // (the dict wins on regeneration, so profile→dict is the convergent direction).
19917
+ { native: "\u0647\u062F\u0641", normalized: "target" },
19918
+ // Alternative to الهدف (the dict's word)
19919
+ { native: "\u0646\u062A\u064A\u062C\u0629", normalized: "result" },
19920
+ // Alternative to النتيجة (the dict's word)
19069
19921
  // Values/Literals
19070
19922
  { native: "\u0635\u062D\u064A\u062D", normalized: "true" },
19071
19923
  { native: "\u062E\u0637\u0623", normalized: "false" },
@@ -19130,13 +19982,17 @@ var init_arabic2 = __esm({
19130
19982
  { native: "\u062D\u064A\u0646", normalized: "on" },
19131
19983
  { native: "\u0644\u0645\u0651\u0627", normalized: "on" },
19132
19984
  { native: "\u0644\u0645\u0627", normalized: "on" },
19133
- { native: "\u0644\u062F\u0649", normalized: "on" }
19985
+ { native: "\u0644\u062F\u0649", normalized: "on" },
19134
19986
  //
19135
19987
  // Command spelling variants are now in the profile alternatives:
19136
19988
  // - toggle: بدل, غيّر, غير (in profile)
19137
19989
  // - add: اضف, زِد (in profile)
19138
19990
  // - remove: أزل, امسح (in profile)
19139
19991
  // - etc.
19992
+ { native: "\u0634\u0627\u0645\u0644", normalized: "inclusive" },
19993
+ { native: "\u062D\u0635\u0631\u064A", normalized: "exclusive" },
19994
+ { native: "\u062D\u0631\u0648\u0641", normalized: "characters" },
19995
+ { native: "\u0639\u0634\u0648\u0627\u0626\u064A", normalized: "random" }
19140
19996
  ];
19141
19997
  ArabicTokenizer = class extends BaseTokenizer {
19142
19998
  constructor() {
@@ -19220,7 +20076,7 @@ var init_arabic2 = __esm({
19220
20076
  pos2++;
19221
20077
  }
19222
20078
  }
19223
- return new TokenStreamImpl(tokens, this.language);
20079
+ return new TokenStreamImpl(this.mergeColonQualifiedNames(tokens), this.language);
19224
20080
  }
19225
20081
  classifyToken(token) {
19226
20082
  if (CONJUNCTIONS2.has(token)) return "conjunction";
@@ -19571,12 +20427,16 @@ var init_spanish_keyword = __esm({
19571
20427
  const keywordEntry = this.context.lookupKeyword(word);
19572
20428
  const normalized2 = keywordEntry && keywordEntry.normalized !== keywordEntry.native ? keywordEntry.normalized : void 0;
19573
20429
  let morphNormalized;
20430
+ let morphStem;
20431
+ let morphConfidence;
19574
20432
  if (!keywordEntry && this.context.normalizer) {
19575
20433
  const morphResult = this.context.normalizer.normalize(word);
19576
20434
  if (morphResult.stem !== word && morphResult.confidence >= 0.7) {
19577
20435
  const stemEntry = this.context.lookupKeyword(morphResult.stem);
19578
20436
  if (stemEntry) {
19579
20437
  morphNormalized = stemEntry.normalized;
20438
+ morphStem = morphResult.stem;
20439
+ morphConfidence = morphResult.confidence;
19580
20440
  }
19581
20441
  }
19582
20442
  }
@@ -19585,6 +20445,8 @@ var init_spanish_keyword = __esm({
19585
20445
  length: pos2 - position,
19586
20446
  metadata: {
19587
20447
  normalized: normalized2 || morphNormalized,
20448
+ stem: morphStem,
20449
+ stemConfidence: morphConfidence,
19588
20450
  isPreposition
19589
20451
  }
19590
20452
  };
@@ -19664,8 +20526,12 @@ var init_spanish2 = __esm({
19664
20526
  // Reference alternatives (accent variation, synonym)
19665
20527
  { native: "m\xED", normalized: "me" },
19666
20528
  // Accented form of mi
19667
- { native: "destino", normalized: "target" }
20529
+ { native: "destino", normalized: "target" },
19668
20530
  // Synonym for objetivo
20531
+ { native: "inclusivo", normalized: "inclusive" },
20532
+ { native: "exclusivo", normalized: "exclusive" },
20533
+ { native: "caracteres", normalized: "characters" },
20534
+ { native: "aleatorio", normalized: "random" }
19669
20535
  ];
19670
20536
  SpanishTokenizer = class extends BaseTokenizer {
19671
20537
  constructor() {
@@ -20122,6 +20988,19 @@ var init_turkish2 = __esm({
20122
20988
  { native: "farebirak", normalized: "mouseup" },
20123
20989
  { native: "kayd\u0131r", normalized: "scroll" },
20124
20990
  { native: "kaydir", normalized: "scroll" },
20991
+ // resize/scroll nominal forms: listed in eventNameTranslations (which only
20992
+ // the SOV-extraction path consults) but not registered as keywords — so a
20993
+ // fused *-sov-simple match captured them RAW (`boyutlandırma de çağır` →
20994
+ // event:expression:boyutlandırma, the window-resize R1 flip once the
20995
+ // debounced-head junk no longer forced the SOV-extraction path). Keyword
20996
+ // entries normalize them at the token, the same route the healthy natives
20997
+ // (tıklama→click) take.
20998
+ { native: "boyutland\u0131rma", normalized: "resize" },
20999
+ { native: "boyutlandirma", normalized: "resize" },
21000
+ { native: "boyutland\u0131r", normalized: "resize" },
21001
+ { native: "boyutlandir", normalized: "resize" },
21002
+ { native: "kayd\u0131rma", normalized: "scroll" },
21003
+ { native: "kaydirma", normalized: "scroll" },
20125
21004
  { native: "tu\u015F_bas", normalized: "keydown" },
20126
21005
  { native: "tus_bas", normalized: "keydown" },
20127
21006
  { native: "tu\u015F_b\u0131rak", normalized: "keyup" },
@@ -20130,7 +21009,11 @@ var init_turkish2 = __esm({
20130
21009
  { native: "saniye", normalized: "s" },
20131
21010
  { native: "milisaniye", normalized: "ms" },
20132
21011
  { native: "dakika", normalized: "m" },
20133
- { native: "saat", normalized: "h" }
21012
+ { native: "saat", normalized: "h" },
21013
+ { native: "dahil", normalized: "inclusive" },
21014
+ { native: "hari\xE7", normalized: "exclusive" },
21015
+ { native: "karakterler", normalized: "characters" },
21016
+ { native: "rastgele", normalized: "random" }
20134
21017
  ];
20135
21018
  TurkishTokenizer = class extends BaseTokenizer {
20136
21019
  constructor() {
@@ -20309,7 +21192,16 @@ var init_chinese2 = __esm({
20309
21192
  { native: "\u524D", normalized: "before" },
20310
21193
  { native: "\u540E", normalized: "after" },
20311
21194
  { native: "\u90A3\u4E48", normalized: "then" },
20312
- { native: "\u5B8C", normalized: "end" }
21195
+ { native: "\u5B8C", normalized: "end" },
21196
+ // Connectives. Whole-token so the greedy longest-first walk claims the 2-char
21197
+ // 作为 (`as`) before its 1-char tail 为 can match the `for` command primary —
21198
+ // without it `作为 Number` shattered into `作` + `为`→`for` (`computed-value`).
21199
+ // The reverse render (CONNECTIVE_LEXICON.zh) already maps 作为→as.
21200
+ { native: "\u4F5C\u4E3A", normalized: "as" },
21201
+ { native: "\u5305\u542B", normalized: "inclusive" },
21202
+ { native: "\u6392\u9664", normalized: "exclusive" },
21203
+ { native: "\u5B57\u7B26", normalized: "characters" },
21204
+ { native: "\u968F\u673A", normalized: "random" }
20313
21205
  ];
20314
21206
  ChineseTokenizer = class extends BaseTokenizer {
20315
21207
  constructor() {
@@ -20674,12 +21566,16 @@ var init_portuguese_keyword = __esm({
20674
21566
  const keywordEntry = this.context.lookupKeyword(lower);
20675
21567
  const normalized2 = keywordEntry && keywordEntry.normalized !== keywordEntry.native ? keywordEntry.normalized : void 0;
20676
21568
  let morphNormalized;
21569
+ let morphStem;
21570
+ let morphConfidence;
20677
21571
  if (!keywordEntry && this.context.normalizer) {
20678
21572
  const morphResult = this.context.normalizer.normalize(word);
20679
21573
  if (morphResult.stem !== word && morphResult.confidence >= 0.7) {
20680
21574
  const stemEntry = this.context.lookupKeyword(morphResult.stem);
20681
21575
  if (stemEntry) {
20682
21576
  morphNormalized = stemEntry.normalized;
21577
+ morphStem = morphResult.stem;
21578
+ morphConfidence = morphResult.confidence;
20683
21579
  }
20684
21580
  }
20685
21581
  }
@@ -20688,6 +21584,8 @@ var init_portuguese_keyword = __esm({
20688
21584
  length: pos2 - position,
20689
21585
  metadata: {
20690
21586
  normalized: normalized2 || morphNormalized,
21587
+ stem: morphStem,
21588
+ stemConfidence: morphConfidence,
20691
21589
  isPreposition
20692
21590
  }
20693
21591
  };
@@ -20811,7 +21709,11 @@ var init_portuguese2 = __esm({
20811
21709
  { native: "padrao", normalized: "default" },
20812
21710
  { native: "at\xE9 que", normalized: "until" },
20813
21711
  // Multi-word phrases
20814
- { native: "dentro de", normalized: "into" }
21712
+ { native: "dentro de", normalized: "into" },
21713
+ { native: "inclusivo", normalized: "inclusive" },
21714
+ { native: "exclusivo", normalized: "exclusive" },
21715
+ { native: "caracteres", normalized: "characters" },
21716
+ { native: "aleat\xF3rio", normalized: "random" }
20815
21717
  ];
20816
21718
  PortugueseTokenizer = class extends BaseTokenizer {
20817
21719
  constructor() {
@@ -21163,12 +22065,16 @@ var init_french_keyword = __esm({
21163
22065
  const keywordEntry = this.context.lookupKeyword(lower);
21164
22066
  const normalized2 = keywordEntry && keywordEntry.normalized !== keywordEntry.native ? keywordEntry.normalized : void 0;
21165
22067
  let morphNormalized;
22068
+ let morphStem;
22069
+ let morphConfidence;
21166
22070
  if (!keywordEntry && this.context.normalizer) {
21167
22071
  const morphResult = this.context.normalizer.normalize(word);
21168
22072
  if (morphResult.stem !== word && morphResult.confidence >= 0.7) {
21169
22073
  const stemEntry = this.context.lookupKeyword(morphResult.stem);
21170
22074
  if (stemEntry) {
21171
22075
  morphNormalized = stemEntry.normalized;
22076
+ morphStem = morphResult.stem;
22077
+ morphConfidence = morphResult.confidence;
21172
22078
  }
21173
22079
  }
21174
22080
  }
@@ -21177,6 +22083,8 @@ var init_french_keyword = __esm({
21177
22083
  length: pos2 - position,
21178
22084
  metadata: {
21179
22085
  normalized: normalized2 || morphNormalized,
22086
+ stem: morphStem,
22087
+ stemConfidence: morphConfidence,
21180
22088
  isPreposition
21181
22089
  }
21182
22090
  };
@@ -21275,7 +22183,11 @@ var init_french2 = __esm({
21275
22183
  // Additional morph synonym
21276
22184
  { native: "transmuter", normalized: "morph" },
21277
22185
  // Multi-word phrases
21278
- { native: "tant que", normalized: "while" }
22186
+ { native: "tant que", normalized: "while" },
22187
+ { native: "inclusif", normalized: "inclusive" },
22188
+ { native: "exclusif", normalized: "exclusive" },
22189
+ { native: "caract\xE8res", normalized: "characters" },
22190
+ { native: "al\xE9atoire", normalized: "random" }
21279
22191
  ];
21280
22192
  FrenchTokenizer = class extends BaseTokenizer {
21281
22193
  constructor() {
@@ -21716,7 +22628,11 @@ var init_german2 = __esm({
21716
22628
  // Verb conjugation variants (imperatives for test cases)
21717
22629
  { native: "erh\xF6he", normalized: "increment" },
21718
22630
  { native: "erhohe", normalized: "increment" },
21719
- { native: "verringere", normalized: "decrement" }
22631
+ { native: "verringere", normalized: "decrement" },
22632
+ { native: "inklusiv", normalized: "inclusive" },
22633
+ { native: "exklusiv", normalized: "exclusive" },
22634
+ { native: "Zeichen", normalized: "characters" },
22635
+ { native: "zuf\xE4llig", normalized: "random" }
21720
22636
  ];
21721
22637
  GermanTokenizer = class extends BaseTokenizer {
21722
22638
  constructor() {
@@ -21813,12 +22729,27 @@ var init_indonesian2 = __esm({
21813
22729
  // outside
21814
22730
  ]);
21815
22731
  INDONESIAN_EXTRAS = [
22732
+ // window-resize compound: the dict emits underscore-joined ubah_ukuran
22733
+ // (resize), which the `_` split shattered into ubah(→change) + _ + ukuran —
22734
+ // the event slot normalized to `change` and `_ ukuran` dropped unconsumed
22735
+ // (Arc F). Whole-token entry mirrors qu's hatun_kay precedent (quechua.ts).
22736
+ { native: "ubah_ukuran", normalized: "resize" },
22737
+ // behavior-draggable's `no` operator: the dict emits underscore-joined
22738
+ // tidak_ada, which the `_` split shattered into tidak(→not) + _ + ada(→exists).
22739
+ // Whole-token entry mirrors ubah_ukuran above; the keyword walk sorts
22740
+ // longest-first, so `tidak_ada` (9) beats `tidak` (5).
22741
+ { native: "tidak_ada", normalized: "no" },
21816
22742
  // Values/Literals
21817
22743
  { native: "benar", normalized: "true" },
21818
22744
  { native: "salah", normalized: "false" },
21819
22745
  { native: "null", normalized: "null" },
21820
22746
  { native: "kosong", normalized: "null" },
21821
22747
  { native: "tidakdidefinisikan", normalized: "undefined" },
22748
+ // The corpus authors `tidak_terdefinisi` for undefined (behavior-removable/
22749
+ // sortable `jika X adalah tidak_terdefinisi`); without a whole-token entry the
22750
+ // `_` split shatters it into tidak(→not) + `_ terdefinisi`, leaking the
22751
+ // invalid `is not _ terdefinisi`. Same shape as tidak_ada above.
22752
+ { native: "tidak_terdefinisi", normalized: "undefined" },
21822
22753
  // Positional
21823
22754
  { native: "pertama", normalized: "first" },
21824
22755
  { native: "terakhir", normalized: "last" },
@@ -21849,7 +22780,11 @@ var init_indonesian2 = __esm({
21849
22780
  { native: "atau", normalized: "or" },
21850
22781
  { native: "tidak", normalized: "not" },
21851
22782
  { native: "adalah", normalized: "is" },
21852
- { native: "ada", normalized: "exists" }
22783
+ { native: "ada", normalized: "exists" },
22784
+ { native: "inklusif", normalized: "inclusive" },
22785
+ { native: "eksklusif", normalized: "exclusive" },
22786
+ { native: "karakter", normalized: "characters" },
22787
+ { native: "acak", normalized: "random" }
21853
22788
  ];
21854
22789
  IndonesianTokenizer = class extends BaseTokenizer {
21855
22790
  constructor() {
@@ -22064,7 +22999,7 @@ var init_quechua2 = __esm({
22064
22999
  this.name = "quechua-string-literal";
22065
23000
  }
22066
23001
  canExtract(input, position) {
22067
- return input[position] === '"' || input[position] === "'";
23002
+ return input[position] === '"' || input[position] === "'" || input[position] === "`";
22068
23003
  }
22069
23004
  extract(input, position) {
22070
23005
  const quote = input[position];
@@ -22123,6 +23058,8 @@ var init_quechua2 = __esm({
22123
23058
  // (set-attribute `@disabled ta cheqaq man …`); without it the value tokenized
22124
23059
  // as a bare identifier and `set @disabled to <undefined>` ran. arí/ari ("yes")
22125
23060
  // are the colloquial alternates, kept for input tolerance.
23061
+ // Pick unit word (arc 3) — mirrors the i18n dict's `characters: 'sanampa'`.
23062
+ { native: "sanampa", normalized: "characters" },
22126
23063
  { native: "cheqaq", normalized: "true" },
22127
23064
  { native: "ar\xED", normalized: "true" },
22128
23065
  { native: "ari", normalized: "true" },
@@ -22157,6 +23094,31 @@ var init_quechua2 = __esm({
22157
23094
  // aswan-prefixed compound splits (the suffix extractor strips -wan from
22158
23095
  // 'aswan'). The i18n dict emits bare 'kaylla' (near/close).
22159
23096
  { native: "kaylla", normalized: "closest" },
23097
+ // Containment (`first <button/> in .modal`): the i18n dict emits ukupi,
23098
+ // which otherwise splits uku(identifier) + pi — and the stranded `pi`
23099
+ // mis-reads as the EVENT marker (the ñawpaqpi/qhepapi class above; same
23100
+ // longest-first cure). Whole-token entry mirrors en's keyword `in` mid-run
23101
+ // geometry (focus-trap Family G).
23102
+ { native: "ukupi", normalized: "in" },
23103
+ // window-resize compounds: the dict emits underscore-joined k_iri (window)
23104
+ // and hatun_kay (resize), which the `_` split shattered into junk role
23105
+ // fragments (call.source:literal="k_iri" destination:literal="hatun_" —
23106
+ // the qu window-resize R1 row; hatun_kay sits in eventNameTranslations but
23107
+ // never arrived whole). The ñawpaq_kaq entry above is the precedent.
23108
+ { native: "k_iri", normalized: "window" },
23109
+ { native: "hatun_kay", normalized: "resize" },
23110
+ // behavior-draggable's `no` operator: the dict emits underscore-joined
23111
+ // mana_kanchu, which the `_` split shattered into mana(→not/without) + _ +
23112
+ // kanchu. Same whole-token shape as hatun_kay; longest-first makes
23113
+ // `mana_kanchu` (11) beat `mana` (4).
23114
+ { native: "mana_kanchu", normalized: "no" },
23115
+ // `undefined`: the dict emits underscore-joined `mana_riqsisqa` ("not known"),
23116
+ // which the `_` split shattered into mana(→false) + _ + riqsisqa — rendering
23117
+ // `is false _ riqsisqa` and breaking the canonical parse (behavior-removable/qu,
23118
+ // behavior-sortable/qu `if triggerEl is undefined`). The bare `mana riqsisqa`
23119
+ // (space) entry above never fires — the corpus authors the underscore form.
23120
+ // Same whole-token shape as mana_kanchu; longest-first makes it beat `mana`.
23121
+ { native: "mana_riqsisqa", normalized: "undefined" },
22160
23122
  { native: "qaylla", normalized: "closest" },
22161
23123
  { native: "tayta", normalized: "parent" },
22162
23124
  // Events
@@ -22218,7 +23180,8 @@ var init_quechua2 = __esm({
22218
23180
  { native: "qhawachiy", normalized: "focus" },
22219
23181
  { native: "mana qhawachiy", normalized: "blur" },
22220
23182
  // Suffix modifiers
22221
- { native: "-manta", normalized: "from" }
23183
+ { native: "-manta", normalized: "from" },
23184
+ { native: "imaymanata", normalized: "random" }
22222
23185
  ];
22223
23186
  QuechuaTokenizer = class extends BaseTokenizer {
22224
23187
  constructor() {
@@ -22246,7 +23209,7 @@ var init_quechua2 = __esm({
22246
23209
  return "event-modifier";
22247
23210
  if (token.startsWith("#") || token.startsWith(".") || token.startsWith("[") || token.startsWith("*") || token.startsWith("<"))
22248
23211
  return "selector";
22249
- if (token.startsWith('"')) return "literal";
23212
+ if (token.startsWith('"') || token.startsWith("'")) return "literal";
22250
23213
  if (/^\d/.test(token)) return "literal";
22251
23214
  if (["==", "!=", "<=", ">=", "<", ">", "&&", "||", "!"].includes(token)) return "operator";
22252
23215
  return "identifier";
@@ -22315,6 +23278,12 @@ var init_swahili2 = __esm({
22315
23278
  // between
22316
23279
  ]);
22317
23280
  SWAHILI_EXTRAS = [
23281
+ // window-resize compound: the dict emits underscore-joined badilisha_ukubwa
23282
+ // (resize), which the `_` split shattered into badilisha(→toggle!) + _ +
23283
+ // ukubwa — the event slot normalized to `toggle` and `_ ukubwa` dropped
23284
+ // unconsumed (Arc F). Whole-token entry mirrors qu's hatun_kay precedent
23285
+ // (quechua.ts).
23286
+ { native: "badilisha_ukubwa", normalized: "resize" },
22318
23287
  // Values/Literals
22319
23288
  { native: "kweli", normalized: "true" },
22320
23289
  { native: "uongo", normalized: "false" },
@@ -22388,7 +23357,9 @@ var init_swahili2 = __esm({
22388
23357
  { native: "si", normalized: "not" },
22389
23358
  { native: "ni", normalized: "is" },
22390
23359
  { native: "ipo", normalized: "exists" },
22391
- { native: "tupu", normalized: "empty" }
23360
+ { native: "tupu", normalized: "empty" },
23361
+ { native: "herufi", normalized: "characters" },
23362
+ { native: "nasibu", normalized: "random" }
22392
23363
  ];
22393
23364
  SwahiliTokenizer = class extends BaseTokenizer {
22394
23365
  constructor() {
@@ -23072,7 +24043,11 @@ var init_italian2 = __esm({
23072
24043
  { native: "vuoto", normalized: "empty" },
23073
24044
  // Synonyms not in profile
23074
24045
  { native: "toggle", normalized: "toggle" },
23075
- { native: "di", normalized: "tell" }
24046
+ { native: "di", normalized: "tell" },
24047
+ { native: "inclusivo", normalized: "inclusive" },
24048
+ { native: "esclusivo", normalized: "exclusive" },
24049
+ { native: "caratteri", normalized: "characters" },
24050
+ { native: "casuale", normalized: "random" }
23076
24051
  ];
23077
24052
  ItalianTokenizer = class extends BaseTokenizer {
23078
24053
  constructor() {
@@ -23177,7 +24152,11 @@ var init_vietnamese2 = __esm({
23177
24152
  { native: "t\u1ED3n t\u1EA1i", normalized: "exists" },
23178
24153
  { native: "r\u1ED7ng", normalized: "empty" },
23179
24154
  // English synonyms
23180
- { native: "javascript", normalized: "js" }
24155
+ { native: "javascript", normalized: "js" },
24156
+ { native: "bao g\u1ED3m", normalized: "inclusive" },
24157
+ { native: "lo\u1EA1i tr\u1EEB", normalized: "exclusive" },
24158
+ { native: "k\xFD t\u1EF1", normalized: "characters" },
24159
+ { native: "ng\u1EABu nhi\xEAn", normalized: "random" }
23181
24160
  ];
23182
24161
  VietnameseTokenizer = class extends BaseTokenizer {
23183
24162
  constructor() {
@@ -23560,7 +24539,11 @@ var init_polish2 = __esm({
23560
24539
  { native: "jest", normalized: "is" },
23561
24540
  { native: "istnieje", normalized: "exists" },
23562
24541
  { native: "pusty", normalized: "empty" },
23563
- { native: "puste", normalized: "empty" }
24542
+ { native: "puste", normalized: "empty" },
24543
+ { native: "w\u0142\u0105cznie", normalized: "inclusive" },
24544
+ { native: "wy\u0142\u0105cznie", normalized: "exclusive" },
24545
+ { native: "znaki", normalized: "characters" },
24546
+ { native: "losowy", normalized: "random" }
23564
24547
  ];
23565
24548
  PolishTokenizer = class extends BaseTokenizer {
23566
24549
  constructor() {
@@ -23990,6 +24973,12 @@ var init_russian2 = __esm({
23990
24973
  { native: "\u043B\u043E\u0436\u044C", normalized: "false" },
23991
24974
  { native: "null", normalized: "null" },
23992
24975
  { native: "\u043D\u0435\u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u043E", normalized: "undefined" },
24976
+ // `ничего` ("nothing") is the word the corpus author uses for a null
24977
+ // comparison (`если item есть ничего` → `if item is null`). Without it the
24978
+ // literal leaked verbatim and the canonical parser rejected the render
24979
+ // (behavior-sortable/ru). Its sibling `неопределено`→undefined was already
24980
+ // registered; this closes the null half.
24981
+ { native: "\u043D\u0438\u0447\u0435\u0433\u043E", normalized: "null" },
23993
24982
  // Time units (not in profile - handled by number parser)
23994
24983
  { native: "\u0441\u0435\u043A\u0443\u043D\u0434\u0430", normalized: "s" },
23995
24984
  { native: "\u0441\u0435\u043A\u0443\u043D\u0434\u044B", normalized: "s" },
@@ -24035,8 +25024,11 @@ var init_russian2 = __esm({
24035
25024
  // feminine
24036
25025
  { native: "\u043C\u043E\u0451", normalized: "my" },
24037
25026
  // neuter
24038
- { native: "\u043C\u043E\u0438", normalized: "my" }
25027
+ { native: "\u043C\u043E\u0438", normalized: "my" },
24039
25028
  // plural
25029
+ { native: "\u0432\u043A\u043B\u044E\u0447\u0438\u0442\u0435\u043B\u044C\u043D\u043E", normalized: "inclusive" },
25030
+ { native: "\u0438\u0441\u043A\u043B\u044E\u0447\u0438\u0442\u0435\u043B\u044C\u043D\u043E", normalized: "exclusive" },
25031
+ { native: "\u0441\u0438\u043C\u0432\u043E\u043B\u044B", normalized: "characters" }
24040
25032
  ];
24041
25033
  RussianTokenizer = class extends BaseTokenizer {
24042
25034
  constructor() {
@@ -24445,6 +25437,11 @@ var init_ukrainian2 = __esm({
24445
25437
  { native: "\u0445\u0438\u0431\u043D\u0456\u0441\u0442\u044C", normalized: "false" },
24446
25438
  { native: "null", normalized: "null" },
24447
25439
  { native: "\u043D\u0435\u0432\u0438\u0437\u043D\u0430\u0447\u0435\u043D\u043E", normalized: "undefined" },
25440
+ // `нічого` ("nothing") is the corpus author's word for a null comparison
25441
+ // (`якщо item є нічого` → `if item is null`); without it the literal leaked
25442
+ // verbatim and the canonical parser rejected the render (behavior-sortable/uk).
25443
+ // Sibling of the already-registered `невизначено`→undefined.
25444
+ { native: "\u043D\u0456\u0447\u043E\u0433\u043E", normalized: "null" },
24448
25445
  // Time units (not in profile - handled by number parser)
24449
25446
  { native: "\u0441\u0435\u043A\u0443\u043D\u0434\u0430", normalized: "s" },
24450
25447
  { native: "\u0441\u0435\u043A\u0443\u043D\u0434\u0438", normalized: "s" },
@@ -24490,8 +25487,11 @@ var init_ukrainian2 = __esm({
24490
25487
  // feminine
24491
25488
  { native: "\u043C\u043E\u0454", normalized: "my" },
24492
25489
  // neuter
24493
- { native: "\u043C\u043E\u0457", normalized: "my" }
25490
+ { native: "\u043C\u043E\u0457", normalized: "my" },
24494
25491
  // plural
25492
+ { native: "\u0432\u043A\u043B\u044E\u0447\u043D\u043E", normalized: "inclusive" },
25493
+ { native: "\u0432\u0438\u043A\u043B\u044E\u0447\u043D\u043E", normalized: "exclusive" },
25494
+ { native: "\u0441\u0438\u043C\u0432\u043E\u043B\u0438", normalized: "characters" }
24495
25495
  ];
24496
25496
  UkrainianTokenizer = class extends BaseTokenizer {
24497
25497
  constructor() {
@@ -24631,7 +25631,11 @@ var init_he2 = __esm({
24631
25631
  { native: "\u05D3\u05E7\u05D4", normalized: "m" },
24632
25632
  { native: "\u05D3\u05E7\u05D5\u05EA", normalized: "m" },
24633
25633
  { native: "\u05E9\u05E2\u05D4", normalized: "h" },
24634
- { native: "\u05E9\u05E2\u05D5\u05EA", normalized: "h" }
25634
+ { native: "\u05E9\u05E2\u05D5\u05EA", normalized: "h" },
25635
+ { native: "\u05DB\u05D5\u05DC\u05DC", normalized: "inclusive" },
25636
+ { native: "\u05D1\u05DC\u05E2\u05D3\u05D9", normalized: "exclusive" },
25637
+ { native: "\u05EA\u05D5\u05D5\u05D9\u05DD", normalized: "characters" },
25638
+ { native: "\u05D0\u05E7\u05E8\u05D0\u05D9", normalized: "random" }
24635
25639
  ];
24636
25640
  HebrewTokenizer = class extends BaseTokenizer {
24637
25641
  constructor() {
@@ -24788,6 +25792,12 @@ var init_hindi2 = __esm({
24788
25792
  // splits on it — see hi.ts events note). repeat-until-event / handler events.
24789
25793
  { native: "\u092E\u093E\u0909\u0938\u0928\u0940\u091A\u0947", normalized: "mousedown" },
24790
25794
  { native: "\u092E\u093E\u0909\u0938\u090A\u092A\u0930", normalized: "mouseup" },
25795
+ // window-resize compound: the dict emits underscore-joined आकार_बदलें
25796
+ // (resize), which the `_` split shattered into आकार + _ + बदलें — and the
25797
+ // stranded बदलें (toggle verb) anchored a PHANTOM toggle command while the
25798
+ // event slot grabbed the call target (the hi window-resize mis-parse,
25799
+ // Arc F). Whole-token entry mirrors qu's hatun_kay precedent (quechua.ts).
25800
+ { native: "\u0906\u0915\u093E\u0930_\u092C\u0926\u0932\u0947\u0902", normalized: "resize" },
24791
25801
  // Values
24792
25802
  { native: "\u0938\u091A", normalized: "true" },
24793
25803
  { native: "\u0938\u0924\u094D\u092F", normalized: "true" },
@@ -24811,7 +25821,26 @@ var init_hindi2 = __esm({
24811
25821
  { native: "\u0938\u094D\u0915\u094D\u0930\u0949\u0932", normalized: "scroll" },
24812
25822
  // Additional modifiers not in profile
24813
25823
  { native: "\u0915\u094B", normalized: "to" },
24814
- { native: "\u0915\u0947 \u0938\u093E\u0925", normalized: "with" }
25824
+ { native: "\u0915\u0947 \u0938\u093E\u0925", normalized: "with" },
25825
+ // Connectives. Whole-token underscore-joined surface, mirroring आकार_बदलें
25826
+ // above: the `_` split shattered के_रूप_में (`as`) into के + _ + रूप + _ + में
25827
+ // (`computed-value`). Registering it lets the tokenizer's underscore-recovery
25828
+ // block adopt the whole run. The reverse render (CONNECTIVE_LEXICON.hi) already
25829
+ // maps के_रूप_में→as; it was a documented dead entry awaiting exactly this.
25830
+ { native: "\u0915\u0947_\u0930\u0942\u092A_\u092E\u0947\u0902", normalized: "as" },
25831
+ // `या` (or) — dict hi.ts `or`; already matched by surface in the parser's
25832
+ // OR_KEYWORDS (event-adjacent `or` was absorbed), but every raw-expression
25833
+ // occurrence leaked verbatim (when-multiple-changes). Phantom-safe: `or` is
25834
+ // neither an ActionType nor a command schema.
25835
+ { native: "\u092F\u093E", normalized: "or" },
25836
+ // `बदलने पर` (changes / "on changing") — dict hi.ts `changes`, SPACED whole
25837
+ // phrase via the multi-word keyword walk (`के साथ` precedent above). NEVER
25838
+ // register bare `बदलने`: the stem `बदल` is a registered toggle-verb
25839
+ // alternative (patterns/toggle.ts) and the morphological normalizer strips
25840
+ // conjugations — a bare entry re-opens the आकार_बदलें phantom-toggle class.
25841
+ { native: "\u092C\u0926\u0932\u0928\u0947 \u092A\u0930", normalized: "changes" },
25842
+ { native: "\u0905\u0915\u094D\u0937\u0930", normalized: "characters" },
25843
+ { native: "\u092F\u093E\u0926\u0943\u091A\u094D\u091B\u093F\u0915", normalized: "random" }
24815
25844
  ];
24816
25845
  HindiTokenizer = class extends BaseTokenizer {
24817
25846
  constructor() {
@@ -24993,7 +26022,17 @@ var init_bengali2 = __esm({
24993
26022
  { native: "\u09B8\u09CD\u0995\u09CD\u09B0\u09CB\u09B2", normalized: "scroll" },
24994
26023
  // Additional modifiers not in profile
24995
26024
  { native: "\u0995\u09C7", normalized: "to" },
24996
- { native: "\u09B8\u09BE\u09A5\u09C7", normalized: "with" }
26025
+ { native: "\u09B8\u09BE\u09A5\u09C7", normalized: "with" },
26026
+ // Conjunctions. `অথবা` (or) — dict bn.ts `or`. Already matched by surface in the
26027
+ // parser's OR_KEYWORDS (event-adjacent `or` was absorbed); registering it lets
26028
+ // surfaceOf emit `or` inside raw expressions (the wait-for event list in
26029
+ // behavior-draggable/sortable). Phantom-safe: `or` is neither an ActionType nor
26030
+ // a command schema.
26031
+ { native: "\u0985\u09A5\u09AC\u09BE", normalized: "or" },
26032
+ { native: "\u0985\u09A8\u09CD\u09A4\u09B0\u09CD\u09AD\u09C1\u0995\u09CD\u09A4", normalized: "inclusive" },
26033
+ { native: "\u09AC\u09BE\u09A6", normalized: "exclusive" },
26034
+ { native: "\u0985\u0995\u09CD\u09B7\u09B0", normalized: "characters" },
26035
+ { native: "\u098F\u09B2\u09CB\u09AE\u09C7\u09B2\u09CB", normalized: "random" }
24997
26036
  ];
24998
26037
  BengaliTokenizer = class extends BaseTokenizer {
24999
26038
  constructor() {
@@ -25067,11 +26106,19 @@ var init_thai2 = __esm({
25067
26106
  { native: "\u0E2D\u0E34\u0E19\u0E1E\u0E38\u0E15", normalized: "input" },
25068
26107
  { native: "\u0E42\u0E2B\u0E25\u0E14", normalized: "load" },
25069
26108
  { native: "\u0E40\u0E25\u0E37\u0E48\u0E2D\u0E19", normalized: "scroll" },
26109
+ // `ปรับขนาด` (resize) — dict th.ts `resize`; without it the greedy scan
26110
+ // shattered it into ป + รับ(→take) + ขนาด (window-resize/th rendered
26111
+ // `on ป take ขนาด …`). Precedent: hi आकार_बदलें, tr boyutlandırma.
26112
+ { native: "\u0E1B\u0E23\u0E31\u0E1A\u0E02\u0E19\u0E32\u0E14", normalized: "resize" },
25070
26113
  // Additional modifiers
25071
26114
  { native: "\u0E40\u0E27\u0E25\u0E32", normalized: "when" },
25072
26115
  { native: "\u0E44\u0E1B\u0E22\u0E31\u0E07", normalized: "to" },
25073
26116
  { native: "\u0E14\u0E49\u0E27\u0E22", normalized: "with" },
25074
- { native: "\u0E41\u0E25\u0E30", normalized: "and" }
26117
+ { native: "\u0E41\u0E25\u0E30", normalized: "and" },
26118
+ { native: "\u0E23\u0E27\u0E21", normalized: "inclusive" },
26119
+ { native: "\u0E22\u0E01\u0E40\u0E27\u0E49\u0E19", normalized: "exclusive" },
26120
+ { native: "\u0E2D\u0E31\u0E01\u0E02\u0E23\u0E30", normalized: "characters" },
26121
+ { native: "\u0E2A\u0E38\u0E48\u0E21", normalized: "random" }
25075
26122
  ];
25076
26123
  ThaiTokenizer = class extends BaseTokenizer {
25077
26124
  constructor() {
@@ -25143,8 +26190,12 @@ var init_ms2 = __esm({
25143
26190
  // Alternative for input (means "enter")
25144
26191
  { native: "muat", normalized: "load" },
25145
26192
  { native: "tatal", normalized: "scroll" },
25146
- { native: "hover", normalized: "hover" }
26193
+ { native: "hover", normalized: "hover" },
25147
26194
  // English loanword commonly used
26195
+ { native: "inklusif", normalized: "inclusive" },
26196
+ { native: "eksklusif", normalized: "exclusive" },
26197
+ { native: "aksara", normalized: "characters" },
26198
+ { native: "rawak", normalized: "random" }
25148
26199
  ];
25149
26200
  MalayTokenizer = class extends BaseTokenizer {
25150
26201
  constructor() {
@@ -25403,7 +26454,11 @@ var init_tl2 = __esm({
25403
26454
  { native: "isumite", normalized: "submit" },
25404
26455
  { native: "input", normalized: "input" },
25405
26456
  { native: "karga", normalized: "load" },
25406
- { native: "mag_scroll", normalized: "scroll" }
26457
+ { native: "mag_scroll", normalized: "scroll" },
26458
+ { native: "kasama", normalized: "inclusive" },
26459
+ { native: "bukod", normalized: "exclusive" },
26460
+ { native: "karakter", normalized: "characters" },
26461
+ { native: "random", normalized: "random" }
25407
26462
  ];
25408
26463
  TagalogTokenizer = class extends BaseTokenizer {
25409
26464
  constructor() {
@@ -25983,6 +27038,28 @@ function getEventHandlerPatternsHi() {
25983
27038
  event: { marker: "\u0938\u0947", position: 2 }
25984
27039
  }
25985
27040
  },
27041
+ // Prefix reactive `when` — the hi member of the ja/tr/ar/he when-family
27042
+ // below (`जब $firstName या $lastName बदलने पर …`). Without it,
27043
+ // `event-hi-bare` captured the जब token itself as the event (render
27044
+ // `on when put …`) and dropped the subject list; en's `event-en-when`
27045
+ // captures the first subject as the event. The event role is
27046
+ // type-constrained so the `जब तक` while/until compound (repeat-while,
27047
+ // unless-condition) never matches — तक lexes as a keyword/literal and
27048
+ // declines, falling through to the repeat patterns unchanged.
27049
+ {
27050
+ id: "event-hi-when",
27051
+ language: "hi",
27052
+ command: "on",
27053
+ priority: 95,
27054
+ template: {
27055
+ format: "\u091C\u092C {event} {body}",
27056
+ tokens: [
27057
+ { type: "literal", value: "\u091C\u092C" },
27058
+ { type: "role", role: "event", expectedTypes: ["reference", "expression", "selector"] }
27059
+ ]
27060
+ },
27061
+ extraction: { event: { position: 1 } }
27062
+ },
25986
27063
  // Bare event name: क्लिक
25987
27064
  {
25988
27065
  id: "event-hi-bare",
@@ -27135,7 +28212,15 @@ var init_event_handler = __esm({
27135
28212
  \uBE14\uB7EC: "blur",
27136
28213
  \uB85C\uB4DC: "load",
27137
28214
  \uB9AC\uC0AC\uC774\uC988: "resize",
27138
- \uC2A4\uD06C\uB864: "scroll"
28215
+ \uC2A4\uD06C\uB864: "scroll",
28216
+ // V3c burn-down (2026-07-14): dictionary event words S5b never covered —
28217
+ // parse-side registration so the i18n dict forms resolve (round-trip-tested).
28218
+ \uB9C8\uC6B0\uC2A4\uC5D4\uD130: "mouseenter",
28219
+ \uB9C8\uC6B0\uC2A4\uB9AC\uBE0C: "mouseleave",
28220
+ \uB9C8\uC6B0\uC2A4\uBB34\uBE0C: "mousemove",
28221
+ \uD0A4\uD504\uB808\uC2A4: "keypress",
28222
+ \uD130\uCE58\uC885\uB8CC: "touchend",
28223
+ \uD130\uCE58\uCDE8\uC18C: "touchcancel"
27139
28224
  },
27140
28225
  // Japanese event names → English
27141
28226
  ja: {
@@ -27155,7 +28240,12 @@ var init_event_handler = __esm({
27155
28240
  \u30ED\u30FC\u30C9: "load",
27156
28241
  \u8AAD\u307F\u8FBC\u307F: "load",
27157
28242
  \u30B5\u30A4\u30BA\u5909\u66F4: "resize",
27158
- \u30B9\u30AF\u30ED\u30FC\u30EB: "scroll"
28243
+ \u30B9\u30AF\u30ED\u30FC\u30EB: "scroll",
28244
+ // V3 Batch 2 alias: i18n dictionary form the ja tokenizer already
28245
+ // normalizes (probe-verified).
28246
+ \u307C\u304B\u3057: "blur"
28247
+ // V3c burn-down (2026-07-14): dictionary event words S5b never covered —
28248
+ // parse-side registration so the i18n dict forms resolve (round-trip-tested).
27159
28249
  },
27160
28250
  // Arabic event names → English
27161
28251
  ar: {
@@ -27172,7 +28262,19 @@ var init_event_handler = __esm({
27172
28262
  "\u062A\u0645\u0631\u064A\u0631 \u0627\u0644\u0645\u0627\u0648\u0633": "mouseover",
27173
28263
  \u0627\u0644\u062A\u0631\u0643\u064A\u0632: "focus",
27174
28264
  \u062A\u062D\u0645\u064A\u0644: "load",
27175
- \u062A\u0645\u0631\u064A\u0631: "scroll"
28265
+ \u062A\u0645\u0631\u064A\u0631: "scroll",
28266
+ // V3 Batch 2 aliases: i18n dictionary forms the ar tokenizer already
28267
+ // normalizes (probe-verified captured values). Appended so first-wins
28268
+ // localization canonicals above are unchanged.
28269
+ \u062A\u0631\u0643\u064A\u0632: "focus",
28270
+ "\u0645\u0641\u062A\u0627\u062D \u0623\u0633\u0641\u0644": "keydown",
28271
+ "\u0645\u0641\u062A\u0627\u062D \u0623\u0639\u0644\u0649": "keyup",
28272
+ "\u0641\u0623\u0631\u0629 \u0641\u0648\u0642": "mouseover",
28273
+ // Arc F: the dict renders resize as the two-word تغيير حجم; the event
28274
+ // slot captures only تغيير (→change) and حجم drops. The compound key is
28275
+ // matched by the parser's event-compound reclaim (offset-exact join of
28276
+ // the captured event word + the dangling fragment).
28277
+ "\u062A\u063A\u064A\u064A\u0631 \u062D\u062C\u0645": "resize"
27176
28278
  },
27177
28279
  // Spanish event names → English
27178
28280
  es: {
@@ -27189,7 +28291,26 @@ var init_event_handler = __esm({
27189
28291
  enfoque: "focus",
27190
28292
  desenfoque: "blur",
27191
28293
  carga: "load",
27192
- desplazamiento: "scroll"
28294
+ desplazamiento: "scroll",
28295
+ // V3 Batch 2 aliases: i18n dictionary verb forms the es tokenizer already
28296
+ // normalizes (probe-verified). Appended — localization canonicals unchanged.
28297
+ cambiar: "change",
28298
+ enfocar: "focus",
28299
+ desenfocar: "blur",
28300
+ cargar: "load",
28301
+ desplazar: "scroll",
28302
+ // V3c burn-down (2026-07-14): dictionary event words S5b never covered —
28303
+ // parse-side registration so the i18n dict forms resolve (round-trip-tested).
28304
+ dobleclic: "dblclick",
28305
+ rat\u00F3nentrar: "mouseenter",
28306
+ rat\u00F3nsalir: "mouseleave",
28307
+ rat\u00F3nmover: "mousemove",
28308
+ teclapresar: "keypress",
28309
+ descargar: "unload",
28310
+ toqueempezar: "touchstart",
28311
+ toqueterminar: "touchend",
28312
+ toquemover: "touchmove",
28313
+ toquecancelar: "touchcancel"
27193
28314
  },
27194
28315
  // Turkish event names → English
27195
28316
  tr: {
@@ -27221,7 +28342,16 @@ var init_event_handler = __esm({
27221
28342
  // the `kaydır`/`kaydırma` scroll precedent) keeps the event token whole.
27222
28343
  boyutland\u0131rma: "resize",
27223
28344
  boyutland\u0131r: "resize",
27224
- kayd\u0131rma: "scroll"
28345
+ kayd\u0131rma: "scroll",
28346
+ // V3 Batch 2 aliases: i18n dictionary forms the tr tokenizer already
28347
+ // normalizes (probe-verified; farebas/farebırak are the deliberately fused
28348
+ // dict forms — the table's own fare_bas/fare_bırak `_` entries shatter).
28349
+ bulan\u0131k: "blur",
28350
+ farebas: "mousedown",
28351
+ fareb\u0131rak: "mouseup",
28352
+ kayd\u0131r: "scroll"
28353
+ // V3c burn-down (2026-07-14): dictionary event words S5b never covered —
28354
+ // parse-side registration so the i18n dict forms resolve (round-trip-tested).
27225
28355
  },
27226
28356
  // Portuguese event names → English
27227
28357
  pt: {
@@ -27248,7 +28378,19 @@ var init_event_handler = __esm({
27248
28378
  carregar: "load",
27249
28379
  carregamento: "load",
27250
28380
  rolagem: "scroll",
27251
- rolar: "scroll"
28381
+ rolar: "scroll",
28382
+ // V3c burn-down (2026-07-14): dictionary event words S5b never covered —
28383
+ // parse-side registration so the i18n dict forms resolve (round-trip-tested).
28384
+ duploClique: "dblclick",
28385
+ mouseEntrar: "mouseenter",
28386
+ mouseSair: "mouseleave",
28387
+ mouseMover: "mousemove",
28388
+ teclaPressionar: "keypress",
28389
+ descarregar: "unload",
28390
+ toqueIn\u00EDcio: "touchstart",
28391
+ toqueFim: "touchend",
28392
+ toqueMover: "touchmove",
28393
+ toqueCancelar: "touchcancel"
27252
28394
  },
27253
28395
  // Chinese event names → English
27254
28396
  zh: {
@@ -27274,7 +28416,18 @@ var init_event_handler = __esm({
27274
28416
  \u6A21\u7CCA: "blur",
27275
28417
  \u52A0\u8F7D: "load",
27276
28418
  \u8F7D\u5165: "load",
27277
- \u6EDA\u52A8: "scroll"
28419
+ \u6EDA\u52A8: "scroll",
28420
+ // V3 Batch 2 alias: the i18n dictionary keydown form (captures keydown via
28421
+ // the registered 按键 prefix; probe-verified — kept over bare 按键 to avoid
28422
+ // colliding with the dict's keypress entry).
28423
+ \u6309\u952E\u6309\u4E0B: "keydown",
28424
+ // V3c burn-down (2026-07-14): dictionary event words S5b never covered —
28425
+ // parse-side registration so the i18n dict forms resolve (round-trip-tested).
28426
+ \u9F20\u6807\u79FB\u52A8: "mousemove",
28427
+ \u5378\u8F7D: "unload",
28428
+ \u8C03\u6574\u5927\u5C0F: "resize",
28429
+ \u89E6\u6478\u5F00\u59CB: "touchstart",
28430
+ \u89E6\u6478\u79FB\u52A8: "touchmove"
27278
28431
  },
27279
28432
  // French event names → English
27280
28433
  fr: {
@@ -27299,7 +28452,22 @@ var init_event_handler = __esm({
27299
28452
  chargement: "load",
27300
28453
  charger: "load",
27301
28454
  d\u00E9filement: "scroll",
27302
- d\u00E9filer: "scroll"
28455
+ d\u00E9filer: "scroll",
28456
+ // V3 Batch 2 alias: i18n dictionary form the fr tokenizer already
28457
+ // normalizes (probe-verified).
28458
+ flou: "blur",
28459
+ // V3c burn-down (2026-07-14): dictionary event words S5b never covered —
28460
+ // parse-side registration so the i18n dict forms resolve (round-trip-tested).
28461
+ doubleclic: "dblclick",
28462
+ sourisentrer: "mouseenter",
28463
+ sourissortir: "mouseleave",
28464
+ sourisbouger: "mousemove",
28465
+ touchepress\u00E9e: "keypress",
28466
+ d\u00E9charger: "unload",
28467
+ touchercommencer: "touchstart",
28468
+ toucherfin: "touchend",
28469
+ toucherbouger: "touchmove",
28470
+ toucherannuler: "touchcancel"
27303
28471
  },
27304
28472
  // German event names → English
27305
28473
  de: {
@@ -27323,7 +28491,26 @@ var init_event_handler = __esm({
27323
28491
  laden: "load",
27324
28492
  ladung: "load",
27325
28493
  scrollen: "scroll",
27326
- bl\u00E4ttern: "scroll"
28494
+ bl\u00E4ttern: "scroll",
28495
+ // V3 Batch 2 aliases: the de tokenizer's registered multi-word event forms
28496
+ // (probe-verified; the table's older `taste runter`/`taste hoch`/`maus
28497
+ // über`/`maus raus` entries are aspirational — they do not tokenize).
28498
+ "taste unten": "keydown",
28499
+ "taste oben": "keyup",
28500
+ "maus dr\xFCber": "mouseover",
28501
+ "maus weg": "mouseout",
28502
+ // V3c burn-down (2026-07-14): dictionary event words S5b never covered —
28503
+ // parse-side registration so the i18n dict forms resolve (round-trip-tested).
28504
+ doppelklick: "dblclick",
28505
+ mauseintreten: "mouseenter",
28506
+ mausverlassen: "mouseleave",
28507
+ mausbewegen: "mousemove",
28508
+ tastedr\u00FCcken: "keypress",
28509
+ entladen: "unload",
28510
+ ber\u00FChrungstart: "touchstart",
28511
+ ber\u00FChrungend: "touchend",
28512
+ ber\u00FChrungbewegen: "touchmove",
28513
+ ber\u00FChrungabbrechen: "touchcancel"
27327
28514
  },
27328
28515
  // Indonesian event names → English
27329
28516
  id: {
@@ -27343,7 +28530,18 @@ var init_event_handler = __esm({
27343
28530
  muat: "load",
27344
28531
  memuat: "load",
27345
28532
  gulir: "scroll",
27346
- menggulir: "scroll"
28533
+ menggulir: "scroll",
28534
+ // V3 Batch 2 aliases: tekan_tombol captures keydown via the registered
28535
+ // `tekan`; arahkan/tinggalkan are the tokenizer's registered natives;
28536
+ // keyup is English passthrough (no parseable id native — `lepas` is
28537
+ // unregistered). All probe-verified.
28538
+ tekan_tombol: "keydown",
28539
+ keyup: "keyup",
28540
+ arahkan: "mouseover",
28541
+ tinggalkan: "mouseout",
28542
+ // V3c burn-down (2026-07-14): dictionary event words S5b never covered —
28543
+ // parse-side registration so the i18n dict forms resolve (round-trip-tested).
28544
+ bongkar: "unload"
27347
28545
  },
27348
28546
  // Bengali event names → English
27349
28547
  bn: {
@@ -27356,6 +28554,8 @@ var init_event_handler = __esm({
27356
28554
  \u099D\u09BE\u09AA\u09B8\u09BE: "blur",
27357
28555
  \u09AB\u09CB\u0995\u09BE\u09B8: "focus",
27358
28556
  \u09AA\u09B0\u09BF\u09AC\u09B0\u09CD\u09A4\u09A8: "change"
28557
+ // V3c burn-down (2026-07-14): dictionary event words S5b never covered —
28558
+ // parse-side registration so the i18n dict forms resolve (round-trip-tested).
27359
28559
  },
27360
28560
  // Quechua event names → English (loanwords with native adaptations)
27361
28561
  qu: {
@@ -27366,8 +28566,14 @@ var init_event_handler = __esm({
27366
28566
  yaykuy: "input",
27367
28567
  tikray: "change",
27368
28568
  "t'ikray": "change",
28569
+ // Batch 3 aliases (appended so first-wins localization canonicals are
28570
+ // unchanged): the dict now renders kambiay/apaykachay — probe-verified to
28571
+ // capture the canonical event via the tokenizer keyword table, unlike
28572
+ // tikray (captures 'toggle') and kachay ('send' in one corpus slot).
28573
+ kambiay: "change",
27369
28574
  apachiy: "submit",
27370
28575
  kachay: "submit",
28576
+ apaykachay: "submit",
27371
28577
  "llave uray": "keydown",
27372
28578
  "llave hawa": "keyup",
27373
28579
  "q'away": "focus",
@@ -27380,6 +28586,8 @@ var init_event_handler = __esm({
27380
28586
  kunray: "scroll",
27381
28587
  muyuy: "scroll",
27382
28588
  hatun_kay: "resize"
28589
+ // V3c burn-down (2026-07-14): dictionary event words S5b never covered —
28590
+ // parse-side registration so the i18n dict forms resolve (round-trip-tested).
27383
28591
  },
27384
28592
  // Swahili event names → English
27385
28593
  sw: {
@@ -27401,7 +28609,31 @@ var init_event_handler = __esm({
27401
28609
  pakia: "load",
27402
28610
  kupakia: "load",
27403
28611
  sogeza: "scroll",
27404
- kusogeza: "scroll"
28612
+ kusogeza: "scroll",
28613
+ // V3 Batch 2 aliases: i18n dictionary forms the sw tokenizer already
28614
+ // normalizes (probe-verified; bonyeza is corpus-hot — 106 rows), plus the
28615
+ // tokenizer's registered `sogeza juu` for mouseover (the table's `panya
28616
+ // juu` is mouseup's dict form and maps there).
28617
+ bonyeza: "click",
28618
+ ingizo: "input",
28619
+ kitufe_shuka: "keydown",
28620
+ kitufe_juu: "keyup",
28621
+ panya_nje: "mouseout",
28622
+ wasilisha: "submit",
28623
+ "sogeza juu": "mouseover",
28624
+ // V3c burn-down (2026-07-14): dictionary event words S5b never covered —
28625
+ // parse-side registration so the i18n dict forms resolve (round-trip-tested).
28626
+ shuka: "unload"
28627
+ },
28628
+ // Vietnamese event names → English. Minimal section: the dict renders
28629
+ // resize as the three-word đổi kích thước; the event slot captures only
28630
+ // đổi (tokenizer-normalized → change) and `kích thước` drops. The compound
28631
+ // key is matched by the parser's event-compound reclaim (Arc F,
28632
+ // offset-exact join of the captured event word + the dangling fragment).
28633
+ vi: {
28634
+ "\u0111\u1ED5i k\xEDch th\u01B0\u1EDBc": "resize"
28635
+ // V3c burn-down (2026-07-14): dictionary event words S5b never covered —
28636
+ // parse-side registration so the i18n dict forms resolve (round-trip-tested).
27405
28637
  }
27406
28638
  };
27407
28639
  Object.fromEntries(
@@ -27435,8 +28667,10 @@ function resolveMarkerForRole(roleSpec, profile) {
27435
28667
  const overrideMarker = roleSpec.markerOverride?.[profile.code];
27436
28668
  const defaultMarker = profile.roleMarkers[roleSpec.role];
27437
28669
  if (overrideMarker !== void 0) {
28670
+ const alternatives = legacyMarkerAlternatives(roleSpec, profile.code, overrideMarker);
27438
28671
  return {
27439
28672
  primary: overrideMarker,
28673
+ ...alternatives && { alternatives },
27440
28674
  position: defaultMarker?.position ?? "before",
27441
28675
  isOverride: true
27442
28676
  };
@@ -27454,6 +28688,18 @@ function resolveMarkerForRole(roleSpec, profile) {
27454
28688
  }
27455
28689
  return null;
27456
28690
  }
28691
+ function legacyMarkerAlternatives(roleSpec, languageCode, overrideMarker) {
28692
+ const legacy = roleSpec.markerLegacy?.[languageCode];
28693
+ if (!legacy?.length) return void 0;
28694
+ const alternatives = [...new Set(legacy)].filter((a) => a && a !== overrideMarker);
28695
+ return alternatives.length ? alternatives : void 0;
28696
+ }
28697
+ function schemaMarkerAlternatives(roleSpec, languageCode, marker) {
28698
+ const legacy = roleSpec.markerLegacy?.[languageCode] ?? [];
28699
+ const variants = roleSpec.methodCarrier ? [] : roleSpec.markerVariants?.[languageCode] ?? [];
28700
+ const alternatives = [.../* @__PURE__ */ new Set([...legacy, ...variants])].filter((a) => a && a !== marker);
28701
+ return alternatives.length ? alternatives : void 0;
28702
+ }
27457
28703
  var init_marker_resolution = __esm({
27458
28704
  "src/parser/utils/marker-resolution.ts"() {
27459
28705
  }
@@ -27465,20 +28711,17 @@ function resolveRoleMarker(roleSpec, profile) {
27465
28711
  let alternatives;
27466
28712
  if (roleSpec.markerOverride && roleSpec.markerOverride[profile.code] !== void 0) {
27467
28713
  marker = roleSpec.markerOverride[profile.code];
28714
+ alternatives = marker ? schemaMarkerAlternatives(roleSpec, profile.code, marker) : void 0;
27468
28715
  } else {
27469
28716
  const roleMarker = profile.roleMarkers[roleSpec.role];
27470
28717
  if (roleMarker) {
27471
28718
  marker = roleMarker.primary;
27472
- alternatives = roleMarker.alternatives ? [...roleMarker.alternatives] : void 0;
27473
- }
27474
- }
27475
- const variants = roleSpec.markerVariants?.[profile.code];
27476
- if (variants && variants.length > 0) {
27477
- const merged = alternatives ? [...alternatives] : [];
27478
- for (const v of variants) {
27479
- if (v !== marker && !merged.includes(v)) merged.push(v);
28719
+ const schemaAlts = schemaMarkerAlternatives(roleSpec, profile.code, marker) ?? [];
28720
+ const merged = [.../* @__PURE__ */ new Set([...roleMarker.alternatives ?? [], ...schemaAlts])].filter(
28721
+ (a) => a !== marker
28722
+ );
28723
+ alternatives = merged.length ? merged : void 0;
27480
28724
  }
27481
- alternatives = merged;
27482
28725
  }
27483
28726
  return { marker, alternatives };
27484
28727
  }
@@ -27574,7 +28817,17 @@ function generateSOVPatientFirstEventHandlerPattern(commandSchema, profile, keyw
27574
28817
  const verbToken = keyword.alternatives ? { type: "literal", value: keyword.primary, alternatives: keyword.alternatives } : { type: "literal", value: keyword.primary };
27575
28818
  tokens.push(verbToken);
27576
28819
  tokens.push(...eventHandlerSourceGroup(commandSchema, profile.roleMarkers.source));
27577
- tokens.push(...eventHandlerDestinationGroup(commandSchema, profile.roleMarkers.destination));
28820
+ let trailingDestMarker = profile.roleMarkers.destination;
28821
+ if (commandSchema.action === "swap" && trailingDestMarker) {
28822
+ const withWord = commandSchema.roles.find((r) => r.role === "patient")?.markerOverride?.[profile.code];
28823
+ if (withWord && withWord !== trailingDestMarker.primary) {
28824
+ const existing = trailingDestMarker.alternatives ?? [];
28825
+ if (!existing.includes(withWord)) {
28826
+ trailingDestMarker = { ...trailingDestMarker, alternatives: [...existing, withWord] };
28827
+ }
28828
+ }
28829
+ }
28830
+ tokens.push(...eventHandlerDestinationGroup(commandSchema, trailingDestMarker));
27578
28831
  return {
27579
28832
  id: `${commandSchema.action}-event-${profile.code}-sov-patient-first`,
27580
28833
  language: profile.code,
@@ -27937,10 +29190,18 @@ function generateSOVTwoRoleDestFirstEventHandlerPattern(commandSchema, profile,
27937
29190
  var init_event_handlers_sov = __esm({
27938
29191
  "src/generators/event-handlers-sov.ts"() {
27939
29192
  init_command_schemas();
29193
+ init_marker_resolution();
27940
29194
  }
27941
29195
  });
27942
29196
 
27943
29197
  // src/generators/event-handlers-vso.ts
29198
+ function mergeSchemaAlternatives(roleSpec, profile, roleMarker) {
29199
+ const schemaAlts = schemaMarkerAlternatives(roleSpec, profile.code, roleMarker.primary) ?? [];
29200
+ const merged = [.../* @__PURE__ */ new Set([...roleMarker.alternatives ?? [], ...schemaAlts])].filter(
29201
+ (a) => a !== roleMarker.primary
29202
+ );
29203
+ return merged.length ? merged : void 0;
29204
+ }
27944
29205
  function generateVSOEventHandlerPattern(commandSchema, profile, keyword, eventMarker, config) {
27945
29206
  const tokens = [];
27946
29207
  if (eventMarker.position === "before") {
@@ -28006,6 +29267,19 @@ function generateVSOVerbFirstEventHandlerPattern(commandSchema, profile, keyword
28006
29267
  tokens.push(markerToken);
28007
29268
  }
28008
29269
  tokens.push({ type: "role", role: "event", optional: false });
29270
+ if (commandSchema.action === "swap") {
29271
+ const withWord = commandSchema.roles.find((r) => r.role === "patient")?.markerOverride?.[profile.code];
29272
+ if (withWord) {
29273
+ tokens.push({
29274
+ type: "group",
29275
+ optional: true,
29276
+ tokens: [
29277
+ { type: "literal", value: withWord },
29278
+ { type: "role", role: "destination", optional: false }
29279
+ ]
29280
+ });
29281
+ }
29282
+ }
28009
29283
  return {
28010
29284
  id: `${commandSchema.action}-event-${profile.code}-vso-verb-first`,
28011
29285
  language: profile.code,
@@ -28040,11 +29314,12 @@ function generateVSOVerbFirstTwoRoleEventHandlerPattern(commandSchema, profile,
28040
29314
  let markerAlternatives;
28041
29315
  if (roleSpec.markerOverride && roleSpec.markerOverride[profile.code] !== void 0) {
28042
29316
  marker = roleSpec.markerOverride[profile.code];
29317
+ markerAlternatives = marker ? schemaMarkerAlternatives(roleSpec, profile.code, marker) : void 0;
28043
29318
  } else {
28044
29319
  const roleMarker = profile.roleMarkers[roleSpec.role];
28045
29320
  if (roleMarker) {
28046
29321
  marker = roleMarker.primary;
28047
- markerAlternatives = roleMarker.alternatives;
29322
+ markerAlternatives = mergeSchemaAlternatives(roleSpec, profile, roleMarker);
28048
29323
  }
28049
29324
  }
28050
29325
  if (marker) {
@@ -28097,11 +29372,12 @@ function generateVSOTwoRoleEventHandlerPattern(commandSchema, profile, keyword,
28097
29372
  let markerAlternatives;
28098
29373
  if (roleSpec.markerOverride && roleSpec.markerOverride[profile.code] !== void 0) {
28099
29374
  marker = roleSpec.markerOverride[profile.code];
29375
+ markerAlternatives = marker ? schemaMarkerAlternatives(roleSpec, profile.code, marker) : void 0;
28100
29376
  } else {
28101
29377
  const roleMarker = profile.roleMarkers[roleSpec.role];
28102
29378
  if (roleMarker) {
28103
29379
  marker = roleMarker.primary;
28104
- markerAlternatives = roleMarker.alternatives;
29380
+ markerAlternatives = mergeSchemaAlternatives(roleSpec, profile, roleMarker);
28105
29381
  }
28106
29382
  }
28107
29383
  if (marker) {
@@ -28234,6 +29510,7 @@ var init_event_handlers_vso = __esm({
28234
29510
  "src/generators/event-handlers-vso.ts"() {
28235
29511
  init_command_schemas();
28236
29512
  init_event_handlers_sov();
29513
+ init_marker_resolution();
28237
29514
  }
28238
29515
  });
28239
29516
  function generatePattern(schema, profile, config = defaultConfig) {
@@ -28284,12 +29561,16 @@ function generateVerbFirstPattern(schema, profile, config = defaultConfig) {
28284
29561
  const keyword = profile.keywords[schema.action];
28285
29562
  if (!keyword) return null;
28286
29563
  const verbToken = keyword.alternatives ? { type: "literal", value: keyword.primary, alternatives: keyword.alternatives } : { type: "literal", value: keyword.primary };
28287
- const roleTokens = requiredRoles.map((r) => ({
28288
- type: "role",
28289
- role: r.role,
28290
- optional: false,
28291
- expectedTypes: r.expectedTypes
28292
- }));
29564
+ const roleTokens = requiredRoles.flatMap((r) => {
29565
+ const prefix = r.valuePrefixLiteral?.[profile.code];
29566
+ const roleToken = {
29567
+ type: "role",
29568
+ role: r.role,
29569
+ optional: false,
29570
+ expectedTypes: r.expectedTypes
29571
+ };
29572
+ return prefix ? [{ type: "literal", value: prefix }, roleToken] : [roleToken];
29573
+ });
28293
29574
  return {
28294
29575
  id: `${schema.action}-${profile.code}-generated-verb-first`,
28295
29576
  language: profile.code,
@@ -28331,6 +29612,37 @@ function generatePatternVariants(schema, profile, config = defaultConfig) {
28331
29612
  patterns.push(verbFirst);
28332
29613
  }
28333
29614
  }
29615
+ for (const v of schema.rolePrefixLiteralVariants ?? []) {
29616
+ const literal = v.literal[profile.code];
29617
+ if (!literal) continue;
29618
+ const { rolePrefixLiteralVariants: _omitted, ...baseSchema } = schema;
29619
+ const cloneSchema2 = {
29620
+ ...baseSchema,
29621
+ roles: schema.roles.map(
29622
+ (r) => r.role === v.role ? { ...r, valuePrefixLiteral: { [profile.code]: literal } } : r
29623
+ )
29624
+ };
29625
+ const delta = v.priorityDelta ?? 5;
29626
+ const carrier = v.methodCarrier ? { [v.methodCarrier]: { value: literal } } : {};
29627
+ const main = generatePattern(cloneSchema2, profile, config);
29628
+ patterns.push({
29629
+ ...main,
29630
+ id: `${schema.action}-${profile.code}-generated-${v.idSuffix}`,
29631
+ priority: (config.basePriority ?? 100) + delta,
29632
+ extraction: { ...main.extraction, ...carrier }
29633
+ });
29634
+ if (config.generateVerbFirstVariants !== false) {
29635
+ const verbFirstUrl = generateVerbFirstPattern(cloneSchema2, profile, config);
29636
+ if (verbFirstUrl) {
29637
+ patterns.push({
29638
+ ...verbFirstUrl,
29639
+ id: `${schema.action}-${profile.code}-generated-verb-first-${v.idSuffix}`,
29640
+ priority: (config.basePriority ?? 100) - 20 + delta,
29641
+ extraction: { ...verbFirstUrl.extraction, ...carrier }
29642
+ });
29643
+ }
29644
+ }
29645
+ }
28334
29646
  return patterns;
28335
29647
  }
28336
29648
  function generatePatternsForLanguage(profile, config = defaultConfig) {
@@ -28554,34 +29866,53 @@ function buildRoleToken(roleSpec, profile) {
28554
29866
  const tokens = [];
28555
29867
  const overrideMarker = roleSpec.markerOverride?.[profile.code];
28556
29868
  const defaultMarker = profile.roleMarkers[roleSpec.role];
29869
+ const suppressMarker = roleSpec.renderOverride?.[profile.code] === "";
28557
29870
  const roleValueToken = {
28558
29871
  type: "role",
28559
29872
  role: roleSpec.role,
28560
29873
  optional: !roleSpec.required,
28561
29874
  expectedTypes: roleSpec.expectedTypes
28562
29875
  };
29876
+ const prefixLiteral = roleSpec.valuePrefixLiteral?.[profile.code];
29877
+ const pushPrefixed = () => {
29878
+ if (prefixLiteral) tokens.push({ type: "literal", value: prefixLiteral });
29879
+ tokens.push(roleValueToken);
29880
+ };
28563
29881
  if (overrideMarker !== void 0) {
28564
29882
  const markerWords = overrideMarker ? overrideMarker.split(/\s+/).filter(Boolean) : [];
28565
29883
  const position = defaultMarker?.position ?? "before";
28566
29884
  const optionalMarker = roleSpec.markerOptional?.[profile.code] === true;
28567
29885
  const pushWord = (word) => {
28568
- const literal = { type: "literal", value: word };
29886
+ const alternatives = markerWords.length === 1 ? schemaMarkerAlternatives(roleSpec, profile.code, word) ?? [] : [];
29887
+ const literal = {
29888
+ type: "literal",
29889
+ value: word,
29890
+ ...alternatives.length ? { alternatives } : {},
29891
+ ...suppressMarker ? { renderSuppress: true } : {}
29892
+ };
28569
29893
  tokens.push(optionalMarker ? { type: "group", optional: true, tokens: [literal] } : literal);
28570
29894
  };
28571
29895
  if (position === "before") {
28572
29896
  for (const word of markerWords) pushWord(word);
28573
- tokens.push(roleValueToken);
29897
+ pushPrefixed();
28574
29898
  } else {
28575
- tokens.push(roleValueToken);
29899
+ pushPrefixed();
28576
29900
  for (const word of markerWords) pushWord(word);
28577
29901
  }
28578
29902
  } else if (defaultMarker) {
28579
- const variantAlts = roleSpec.markerVariants?.[profile.code] ?? [];
28580
29903
  const asMarker = () => {
28581
29904
  const alternatives = [
28582
- .../* @__PURE__ */ new Set([...defaultMarker.alternatives ?? [], ...variantAlts])
29905
+ .../* @__PURE__ */ new Set([
29906
+ ...defaultMarker.alternatives ?? [],
29907
+ ...schemaMarkerAlternatives(roleSpec, profile.code, defaultMarker.primary) ?? []
29908
+ ])
28583
29909
  ].filter((a) => a !== defaultMarker.primary);
28584
- return alternatives.length ? { type: "literal", value: defaultMarker.primary, alternatives } : { type: "literal", value: defaultMarker.primary };
29910
+ return {
29911
+ type: "literal",
29912
+ value: defaultMarker.primary,
29913
+ ...alternatives.length ? { alternatives } : {},
29914
+ ...suppressMarker ? { renderSuppress: true } : {}
29915
+ };
28585
29916
  };
28586
29917
  const pushMarker = (marker) => {
28587
29918
  tokens.push(
@@ -28592,13 +29923,13 @@ function buildRoleToken(roleSpec, profile) {
28592
29923
  if (defaultMarker.primary) {
28593
29924
  pushMarker(asMarker());
28594
29925
  }
28595
- tokens.push(roleValueToken);
29926
+ pushPrefixed();
28596
29927
  } else {
28597
- tokens.push(roleValueToken);
29928
+ pushPrefixed();
28598
29929
  pushMarker(asMarker());
28599
29930
  }
28600
29931
  } else {
28601
- tokens.push(roleValueToken);
29932
+ pushPrefixed();
28602
29933
  }
28603
29934
  return tokens;
28604
29935
  }
@@ -28607,12 +29938,22 @@ function buildExtractionRules(schema, profile) {
28607
29938
  for (const roleSpec of schema.roles) {
28608
29939
  const overrideMarker = roleSpec.markerOverride?.[profile.code];
28609
29940
  const defaultMarker = profile.roleMarkers[roleSpec.role];
28610
- if (overrideMarker !== void 0) {
28611
- rules[roleSpec.role] = overrideMarker ? { marker: overrideMarker } : {};
29941
+ if (roleSpec.valuePrefixLiteral?.[profile.code]) {
29942
+ rules[roleSpec.role] = { marker: roleSpec.valuePrefixLiteral[profile.code] };
29943
+ } else if (overrideMarker !== void 0) {
29944
+ if (!overrideMarker) {
29945
+ rules[roleSpec.role] = {};
29946
+ } else {
29947
+ const isSingleWord = !/\s/.test(overrideMarker.trim());
29948
+ const markerAlternatives = isSingleWord ? schemaMarkerAlternatives(roleSpec, profile.code, overrideMarker) ?? [] : [];
29949
+ rules[roleSpec.role] = markerAlternatives.length ? { marker: overrideMarker, markerAlternatives } : { marker: overrideMarker };
29950
+ }
28612
29951
  } else if (defaultMarker && defaultMarker.primary) {
28613
- const variantAlts = roleSpec.markerVariants?.[profile.code] ?? [];
28614
29952
  const markerAlternatives = [
28615
- .../* @__PURE__ */ new Set([...defaultMarker.alternatives ?? [], ...variantAlts])
29953
+ .../* @__PURE__ */ new Set([
29954
+ ...defaultMarker.alternatives ?? [],
29955
+ ...schemaMarkerAlternatives(roleSpec, profile.code, defaultMarker.primary) ?? []
29956
+ ])
28616
29957
  ].filter((a) => a !== defaultMarker.primary);
28617
29958
  rules[roleSpec.role] = markerAlternatives.length ? { marker: defaultMarker.primary, markerAlternatives } : { marker: defaultMarker.primary };
28618
29959
  } else {
@@ -28681,6 +30022,135 @@ var init_pattern_generator = __esm({
28681
30022
  }
28682
30023
  });
28683
30024
 
30025
+ // src/patterns/languages/en/fetch.ts
30026
+ var fetchWithResponseTypeEnglish, fetchWithOptionsAndResponseTypeEnglish, fetchWithOptionsEnglish, fetchSimpleEnglish, fetchPatternsEn;
30027
+ var init_fetch = __esm({
30028
+ "src/patterns/languages/en/fetch.ts"() {
30029
+ fetchWithResponseTypeEnglish = {
30030
+ id: "fetch-en-with-response-type",
30031
+ language: "en",
30032
+ command: "fetch",
30033
+ priority: 90,
30034
+ // Higher than simple pattern (80) to capture "as" modifier first
30035
+ template: {
30036
+ format: "fetch {source} as {responseType}",
30037
+ tokens: [
30038
+ { type: "literal", value: "fetch" },
30039
+ { type: "role", role: "source", expectedTypes: ["literal", "expression"] },
30040
+ { type: "literal", value: "as" },
30041
+ // json/text/html are identifiers not keywords, so we need to accept 'expression' type
30042
+ { type: "role", role: "responseType", expectedTypes: ["literal", "expression"] }
30043
+ ]
30044
+ },
30045
+ extraction: {
30046
+ source: { position: 1 },
30047
+ responseType: { marker: "as" }
30048
+ }
30049
+ };
30050
+ fetchWithOptionsAndResponseTypeEnglish = {
30051
+ id: "fetch-en-with-options-as",
30052
+ language: "en",
30053
+ command: "fetch",
30054
+ priority: 95,
30055
+ template: {
30056
+ format: "fetch {source} with {style} as {responseType}",
30057
+ tokens: [
30058
+ { type: "literal", value: "fetch" },
30059
+ { type: "role", role: "source", expectedTypes: ["literal", "expression"] },
30060
+ { type: "literal", value: "with", alternatives: ["by", "using"] },
30061
+ // expression-ONLY: routes `{ … }` to the object-literal fold, which keeps
30062
+ // the source text intact for the expression parser.
30063
+ { type: "role", role: "style", expectedTypes: ["expression"] },
30064
+ { type: "literal", value: "as" },
30065
+ { type: "role", role: "responseType", expectedTypes: ["literal", "expression"] }
30066
+ ]
30067
+ },
30068
+ extraction: {
30069
+ source: { position: 1 },
30070
+ style: { marker: "with" },
30071
+ responseType: { marker: "as" }
30072
+ }
30073
+ };
30074
+ fetchWithOptionsEnglish = {
30075
+ id: "fetch-en-with-options",
30076
+ language: "en",
30077
+ command: "fetch",
30078
+ priority: 93,
30079
+ // Below the with+as pattern, above the response-type pattern (90)
30080
+ template: {
30081
+ format: "fetch {source} with {style}",
30082
+ tokens: [
30083
+ { type: "literal", value: "fetch" },
30084
+ { type: "role", role: "source", expectedTypes: ["literal", "expression"] },
30085
+ { type: "literal", value: "with", alternatives: ["by", "using"] },
30086
+ { type: "role", role: "style", expectedTypes: ["expression"] }
30087
+ ]
30088
+ },
30089
+ extraction: {
30090
+ source: { position: 1 },
30091
+ style: { marker: "with" }
30092
+ }
30093
+ };
30094
+ fetchSimpleEnglish = {
30095
+ id: "fetch-en-simple",
30096
+ language: "en",
30097
+ command: "fetch",
30098
+ priority: 80,
30099
+ // Lower than response type pattern (90) - fallback when "as" not present
30100
+ template: {
30101
+ format: "fetch {source}",
30102
+ tokens: [
30103
+ { type: "literal", value: "fetch" },
30104
+ { type: "role", role: "source" }
30105
+ ]
30106
+ },
30107
+ extraction: {
30108
+ source: { position: 1 }
30109
+ }
30110
+ };
30111
+ fetchPatternsEn = [
30112
+ fetchWithOptionsAndResponseTypeEnglish,
30113
+ fetchWithOptionsEnglish,
30114
+ fetchWithResponseTypeEnglish,
30115
+ fetchSimpleEnglish
30116
+ ];
30117
+ }
30118
+ });
30119
+
30120
+ // src/patterns/languages/en/pick.ts
30121
+ var pickVariantEnglish, pickPatternsEn;
30122
+ var init_pick = __esm({
30123
+ "src/patterns/languages/en/pick.ts"() {
30124
+ pickVariantEnglish = {
30125
+ id: "pick-en-variant",
30126
+ language: "en",
30127
+ command: "pick",
30128
+ priority: 110,
30129
+ template: {
30130
+ format: "pick {method} {patient} of {source}",
30131
+ tokens: [
30132
+ { type: "literal", value: "pick" },
30133
+ // Variant word: `characters`/`items`/`match` tokenize as identifiers
30134
+ // (expression), `first`/`last`/`random` as keywords.
30135
+ { type: "role", role: "method", expectedTypes: ["literal", "expression"] },
30136
+ // Range/count/index. The pick-range assembler folds `<a> to <b>
30137
+ // [inclusive|exclusive]` into one expression value here; a lone count
30138
+ // (`3`) is captured as a single literal.
30139
+ { type: "role", role: "patient", expectedTypes: ["literal", "expression"] },
30140
+ { type: "literal", value: "of", alternatives: ["from"] },
30141
+ { type: "role", role: "source", expectedTypes: ["selector", "reference", "expression"] }
30142
+ ]
30143
+ },
30144
+ extraction: {
30145
+ method: { position: 1 },
30146
+ patient: { position: 2 },
30147
+ source: { marker: "of", markerAlternatives: ["from"] }
30148
+ }
30149
+ };
30150
+ pickPatternsEn = [pickVariantEnglish];
30151
+ }
30152
+ });
30153
+
28684
30154
  // src/patterns/toggle.ts
28685
30155
  function getTogglePatternsBn() {
28686
30156
  return [
@@ -29101,6 +30571,33 @@ function getTogglePatternsQu() {
29101
30571
  destination: { position: 0 },
29102
30572
  patient: { position: 2 }
29103
30573
  }
30574
+ },
30575
+ // Patient-first with trailing destination: .open ta qhipantin .panel man
30576
+ // t'ikray — the i18n full verb-final order (#636 qu canonicalOrder) puts
30577
+ // the destination AFTER the patient, but every dest-bearing variant above
30578
+ // is destination-first, so the shape fell to the verb-anchoring fallback,
30579
+ // which glued the positional run (destination:literal="qhipantin.panel"
30580
+ // vs en destination:expression="next .panel") — toggle-aria-expanded,
30581
+ // R1 deferred-tail qu tail.
30582
+ {
30583
+ id: "toggle-qu-patient-first-dest",
30584
+ language: "qu",
30585
+ command: "toggle",
30586
+ priority: 102,
30587
+ template: {
30588
+ format: "{patient} ta {destination} man t'ikray",
30589
+ tokens: [
30590
+ { type: "role", role: "patient" },
30591
+ { type: "literal", value: "ta" },
30592
+ { type: "role", role: "destination" },
30593
+ { type: "literal", value: "man", alternatives: ["pa"] },
30594
+ { type: "literal", value: "t'ikray", alternatives: ["tikray", "kutichiy"] }
30595
+ ]
30596
+ },
30597
+ extraction: {
30598
+ patient: { position: 0 },
30599
+ destination: { position: 2 }
30600
+ }
29104
30601
  }
29105
30602
  ];
29106
30603
  }
@@ -29468,11 +30965,15 @@ function repeatForInHead(language, spec) {
29468
30965
  // matches the verb's normalized form
29469
30966
  ];
29470
30967
  if (spec.forWords && spec.forWords.length > 0) {
29471
- tokens.push({
29472
- type: "group",
29473
- optional: true,
29474
- tokens: spec.forWords.map((w) => ({ type: "literal", value: w }))
29475
- });
30968
+ if (spec.requireForWords) {
30969
+ for (const w of spec.forWords) tokens.push({ type: "literal", value: w });
30970
+ } else {
30971
+ tokens.push({
30972
+ type: "group",
30973
+ optional: true,
30974
+ tokens: spec.forWords.map((w) => ({ type: "literal", value: w }))
30975
+ });
30976
+ }
29476
30977
  }
29477
30978
  tokens.push({ type: "role", role: "patient", expectedTypes: ["expression", "reference"] });
29478
30979
  for (const w of spec.inWords) tokens.push({ type: "literal", value: w });
@@ -29581,10 +31082,63 @@ function repeatUntilHeadSOV(language, spec) {
29581
31082
  }
29582
31083
  };
29583
31084
  }
31085
+ function repeatUntilHeadSOVVerbFinal(language, spec) {
31086
+ return {
31087
+ id: `repeat-${language}-until-head-verb-final`,
31088
+ language,
31089
+ command: "repeat",
31090
+ priority: 111,
31091
+ // above the post-verb variant so the correct shape wins
31092
+ template: {
31093
+ format: `${spec.untilWord} ${spec.eventWord} {event} ${spec.objMarker} {source} ${spec.fromWord} repeat`,
31094
+ tokens: [
31095
+ { type: "literal", value: spec.untilWord },
31096
+ { type: "literal", value: spec.eventWord },
31097
+ { type: "role", role: "event", expectedTypes: ["literal", "expression"] },
31098
+ { type: "literal", value: spec.objMarker },
31099
+ {
31100
+ type: "role",
31101
+ role: "source",
31102
+ expectedTypes: ["selector", "reference", "expression"]
31103
+ },
31104
+ { type: "literal", value: spec.fromWord },
31105
+ { type: "literal", value: "repeat" }
31106
+ ]
31107
+ },
31108
+ extraction: {
31109
+ loopType: { default: { type: "literal", value: "until-event" } }
31110
+ }
31111
+ };
31112
+ }
31113
+ function sovForBindingHead(language, spec) {
31114
+ return {
31115
+ id: `for-${language}-sov-basic`,
31116
+ language,
31117
+ command: "for",
31118
+ priority: 105,
31119
+ template: {
31120
+ format: `{patient} ${spec.inWords.join(" ")} {source} [${spec.objMarker}] ${spec.forVerb}`,
31121
+ tokens: [
31122
+ { type: "role", role: "patient", expectedTypes: ["expression", "reference"] },
31123
+ ...spec.inWords.map((w) => ({ type: "literal", value: w })),
31124
+ { type: "role", role: "source", expectedTypes: ["selector", "expression", "reference"] },
31125
+ {
31126
+ type: "group",
31127
+ optional: true,
31128
+ tokens: [{ type: "literal", value: spec.objMarker }]
31129
+ },
31130
+ { type: "literal", value: spec.forVerb }
31131
+ ]
31132
+ },
31133
+ extraction: {
31134
+ patient: { position: 0 }
31135
+ }
31136
+ };
31137
+ }
29584
31138
  function getRepeatPatternsForLanguage(language) {
29585
31139
  return BY_LANG.get(language) ?? [];
29586
31140
  }
29587
- var VERB_FIRST_REPEAT_TIMES, SOV_REPEAT_TIMES, FOR_IN_HEADS, WHILE_HEADS, VERB_FIRST_UNTIL_HEADS, repeatUntilHeadQuMidClause, SOV_UNTIL_HEADS, repeatUntilHeadQu, BY_LANG, addPattern;
31141
+ var VERB_FIRST_REPEAT_TIMES, SOV_REPEAT_TIMES, FOR_IN_HEADS, WHILE_HEADS, VERB_FIRST_UNTIL_HEADS, repeatUntilHeadQuMidClause, SOV_UNTIL_HEADS, repeatUntilHeadQu, SOV_FOR_BINDING_HEADS, BY_LANG, addPattern;
29588
31142
  var init_repeat = __esm({
29589
31143
  "src/patterns/repeat.ts"() {
29590
31144
  VERB_FIRST_REPEAT_TIMES = [
@@ -29599,7 +31153,7 @@ var init_repeat = __esm({
29599
31153
  ["ar", "\u0643\u0631\u0631", "times"],
29600
31154
  ["he", "\u05D7\u05D6\u05D5\u05E8", "times", "\u05D0\u05EA"],
29601
31155
  ["id", "ulangi", "times"],
29602
- ["ms", "ulang", "times"],
31156
+ ["ms", "ulang", "kali"],
29603
31157
  ["sw", "rudia", "times"],
29604
31158
  ["th", "\u0E17\u0E33\u0E0B\u0E49\u0E33", "\u0E04\u0E23\u0E31\u0E49\u0E07"],
29605
31159
  ["vi", "l\u1EB7p l\u1EA1i", "l\u1EA7n"],
@@ -29615,7 +31169,7 @@ var init_repeat = __esm({
29615
31169
  ["qu", "times", "ta"]
29616
31170
  ];
29617
31171
  FOR_IN_HEADS = [
29618
- ["en", { forWords: ["for"], inWords: ["in"] }],
31172
+ ["en", { forWords: ["for"], inWords: ["in"], requireForWords: true }],
29619
31173
  ["es", { forWords: ["para"], inWords: ["en"] }],
29620
31174
  ["pt", { forWords: ["para"], inWords: ["dentro"] }],
29621
31175
  ["fr", { forWords: ["pour"], inWords: ["en"] }],
@@ -29629,8 +31183,11 @@ var init_repeat = __esm({
29629
31183
  ["he", { forWords: ["\u05E2\u05D1\u05D5\u05E8", "\u05D0\u05EA"], inWords: ["in"] }],
29630
31184
  ["hi", { inWords: ["\u092E\u0947\u0902"] }],
29631
31185
  ["bn", { inWords: ["\u098F"] }],
29632
- ["ja", { inWords: ["\u306E", "\u4E2D"] }],
29633
- ["ko", { inWords: ["\uC548", "\uC5D0"] }],
31186
+ // ja/ko/qu containment words tokenize WHOLE (keyword→in entries added for
31187
+ // the focus-trap Family G operand run) — the old split forms (の+中, 안+에,
31188
+ // uku+pi) no longer appear in the stream.
31189
+ ["ja", { inWords: ["\u306E\u4E2D"] }],
31190
+ ["ko", { inWords: ["\uC548\uC5D0"] }],
29634
31191
  ["zh", { forWords: ["\u4E3A", "\u628A"], inWords: ["\u5728"] }],
29635
31192
  ["tr", { inWords: ["i\xE7inde"] }],
29636
31193
  ["id", { forWords: ["untuk"], inWords: ["dalam"] }],
@@ -29639,7 +31196,7 @@ var init_repeat = __esm({
29639
31196
  ["th", { forWords: ["\u0E2A\u0E33\u0E2B\u0E23\u0E31\u0E1A"], inWords: ["\u0E43\u0E19"] }],
29640
31197
  ["vi", { forWords: ["v\u1EDBi m\u1ED7i"], inWords: ["trong"] }],
29641
31198
  ["tl", { forWords: ["para_sa"], inWords: ["sa_loob"] }],
29642
- ["qu", { inWords: ["uku", "pi"] }]
31199
+ ["qu", { inWords: ["ukupi"] }]
29643
31200
  ];
29644
31201
  WHILE_HEADS = [
29645
31202
  ["en", { whileWord: "while" }],
@@ -29735,6 +31292,16 @@ var init_repeat = __esm({
29735
31292
  loopType: { default: { type: "literal", value: "until-event" } }
29736
31293
  }
29737
31294
  };
31295
+ SOV_FOR_BINDING_HEADS = [
31296
+ // ja/ko/qu in-words are single whole tokens now (keyword→in entries — see
31297
+ // the FOR_IN_HEADS note); the split forms are gone from the stream.
31298
+ ["ja", { inWords: ["\u306E\u4E2D"], objMarker: "\u3092", forVerb: "\u305F\u3081\u306B" }],
31299
+ ["ko", { inWords: ["\uC548\uC5D0"], objMarker: "\uB97C", forVerb: "\uAC01\uAC01" }],
31300
+ ["tr", { inWords: ["i\xE7inde"], objMarker: "i", forVerb: "i\xE7in" }],
31301
+ ["qu", { inWords: ["ukupi"], objMarker: "ta", forVerb: "sapankaq" }],
31302
+ ["bn", { inWords: ["\u098F"], objMarker: "\u0995\u09C7", forVerb: "\u099C\u09A8\u09CD\u09AF" }],
31303
+ ["hi", { inWords: ["\u092E\u0947\u0902"], objMarker: "\u0915\u094B", forVerb: "\u0939\u0947\u0924\u0941" }]
31304
+ ];
29738
31305
  BY_LANG = /* @__PURE__ */ new Map();
29739
31306
  addPattern = (lang, p) => {
29740
31307
  const list = BY_LANG.get(lang);
@@ -29750,6 +31317,9 @@ var init_repeat = __esm({
29750
31317
  for (const [lang, spec] of FOR_IN_HEADS) {
29751
31318
  addPattern(lang, repeatForInHead(lang, spec));
29752
31319
  }
31320
+ for (const [lang, spec] of SOV_FOR_BINDING_HEADS) {
31321
+ addPattern(lang, sovForBindingHead(lang, spec));
31322
+ }
29753
31323
  for (const [lang, spec] of WHILE_HEADS) {
29754
31324
  addPattern(lang, repeatWhileHead(lang, spec));
29755
31325
  }
@@ -29758,6 +31328,9 @@ var init_repeat = __esm({
29758
31328
  }
29759
31329
  for (const [lang, spec] of SOV_UNTIL_HEADS) {
29760
31330
  addPattern(lang, repeatUntilHeadSOV(lang, spec));
31331
+ if (lang === "tr") {
31332
+ addPattern(lang, repeatUntilHeadSOVVerbFinal(lang, spec));
31333
+ }
29761
31334
  }
29762
31335
  addPattern("qu", repeatUntilHeadQu);
29763
31336
  addPattern("qu", repeatUntilHeadQuMidClause);
@@ -29877,6 +31450,121 @@ function getWaitPatternsTl() {
29877
31450
  }
29878
31451
  ];
29879
31452
  }
31453
+ function verbFinalOrRunWait(id, language, verb, sourceMarker, orWord, parenArgCount, sourceMarkerAlternatives) {
31454
+ const parenGroup = () => ({
31455
+ type: "group",
31456
+ optional: true,
31457
+ tokens: [
31458
+ { type: "literal", value: "(" },
31459
+ ...Array.from({ length: parenArgCount }, (_, i) => [
31460
+ ...i > 0 ? [{ type: "literal", value: "," }] : [],
31461
+ {
31462
+ type: "role",
31463
+ role: "condition",
31464
+ expectedTypes: ["expression", "literal", "reference"]
31465
+ }
31466
+ ]).flat(),
31467
+ { type: "literal", value: ")" }
31468
+ ]
31469
+ });
31470
+ return {
31471
+ id,
31472
+ language,
31473
+ command: "wait",
31474
+ priority: 105,
31475
+ template: {
31476
+ format: `{source} ${sourceMarker} {duration} ${orWord} {patient} ${verb}`,
31477
+ tokens: [
31478
+ { type: "role", role: "source", expectedTypes: ["expression", "reference"] },
31479
+ {
31480
+ type: "literal",
31481
+ value: sourceMarker,
31482
+ ...sourceMarkerAlternatives ? { alternatives: sourceMarkerAlternatives } : {}
31483
+ },
31484
+ { type: "role", role: "duration", expectedTypes: ["expression", "literal"] },
31485
+ parenGroup(),
31486
+ { type: "literal", value: orWord },
31487
+ { type: "role", role: "patient", expectedTypes: ["expression", "literal"] },
31488
+ parenGroup(),
31489
+ { type: "literal", value: verb }
31490
+ ]
31491
+ },
31492
+ extraction: {
31493
+ source: { position: 0 },
31494
+ duration: { position: 2 }
31495
+ }
31496
+ };
31497
+ }
31498
+ function verbFirstOrRunWait(id, language, verb, orWord, forWord, sourceMarker, parenArgCount) {
31499
+ const parenGroup = () => ({
31500
+ type: "group",
31501
+ optional: true,
31502
+ tokens: [
31503
+ { type: "literal", value: "(" },
31504
+ ...Array.from({ length: parenArgCount }, (_, i) => [
31505
+ ...i > 0 ? [{ type: "literal", value: "," }] : [],
31506
+ {
31507
+ type: "role",
31508
+ role: "condition",
31509
+ expectedTypes: ["expression", "literal", "reference"]
31510
+ }
31511
+ ]).flat(),
31512
+ { type: "literal", value: ")" }
31513
+ ]
31514
+ });
31515
+ const forGroup = () => ({
31516
+ type: "group",
31517
+ optional: true,
31518
+ tokens: [{ type: "literal", value: forWord }]
31519
+ });
31520
+ return {
31521
+ id,
31522
+ language,
31523
+ command: "wait",
31524
+ priority: 105,
31525
+ template: {
31526
+ format: `${verb} {duration} ${orWord} [${forWord}] {patient} [${forWord}] {source} ${sourceMarker}`,
31527
+ tokens: [
31528
+ { type: "literal", value: verb },
31529
+ { type: "role", role: "duration", expectedTypes: ["expression", "literal"] },
31530
+ parenGroup(),
31531
+ { type: "literal", value: orWord },
31532
+ forGroup(),
31533
+ { type: "role", role: "patient", expectedTypes: ["expression", "literal"] },
31534
+ parenGroup(),
31535
+ forGroup(),
31536
+ { type: "role", role: "source", expectedTypes: ["expression", "reference"] },
31537
+ { type: "literal", value: sourceMarker }
31538
+ ]
31539
+ },
31540
+ extraction: {
31541
+ duration: { position: 1 },
31542
+ source: { position: 8 }
31543
+ }
31544
+ };
31545
+ }
31546
+ function getWaitPatternsBn() {
31547
+ return [
31548
+ verbFirstOrRunWait("wait-bn-or-run", "bn", "\u0985\u09AA\u09C7\u0995\u09CD\u09B7\u09BE", "\u0985\u09A5\u09AC\u09BE", "\u099C\u09A8\u09CD\u09AF", "\u09A5\u09C7\u0995\u09C7", 1),
31549
+ verbFirstOrRunWait("wait-bn-or-run-2arg", "bn", "\u0985\u09AA\u09C7\u0995\u09CD\u09B7\u09BE", "\u0985\u09A5\u09AC\u09BE", "\u099C\u09A8\u09CD\u09AF", "\u09A5\u09C7\u0995\u09C7", 2)
31550
+ ];
31551
+ }
31552
+ function getWaitPatternsTr() {
31553
+ return [
31554
+ verbFinalOrRunWait("wait-tr-or-run", "tr", "bekle", "den", "veya", 1, ["dan", "ten", "tan"]),
31555
+ verbFinalOrRunWait("wait-tr-or-run-2arg", "tr", "bekle", "den", "veya", 2, [
31556
+ "dan",
31557
+ "ten",
31558
+ "tan"
31559
+ ])
31560
+ ];
31561
+ }
31562
+ function getWaitPatternsQu() {
31563
+ return [
31564
+ verbFinalOrRunWait("wait-qu-or-run", "qu", "suyay", "manta", "utaq", 1),
31565
+ verbFinalOrRunWait("wait-qu-or-run-2arg", "qu", "suyay", "manta", "utaq", 2)
31566
+ ];
31567
+ }
29880
31568
  function getWaitPatternsForLanguage(language) {
29881
31569
  switch (language) {
29882
31570
  case "en":
@@ -29887,8 +31575,14 @@ function getWaitPatternsForLanguage(language) {
29887
31575
  return getWaitPatternsHe();
29888
31576
  case "ar":
29889
31577
  return getWaitPatternsAr();
31578
+ case "bn":
31579
+ return getWaitPatternsBn();
29890
31580
  case "tl":
29891
31581
  return getWaitPatternsTl();
31582
+ case "tr":
31583
+ return getWaitPatternsTr();
31584
+ case "qu":
31585
+ return getWaitPatternsQu();
29892
31586
  default:
29893
31587
  return [];
29894
31588
  }
@@ -29911,8 +31605,8 @@ function buildEnglishPatterns() {
29911
31605
  patterns.push(...getRepeatPatternsForLanguage("en"));
29912
31606
  patterns.push(...getWaitPatternsForLanguage("en"));
29913
31607
  patterns.push(
29914
- fetchWithResponseTypeEnglish,
29915
- fetchSimpleEnglish,
31608
+ ...fetchPatternsEn,
31609
+ ...pickPatternsEn,
29916
31610
  swapElementEnglish,
29917
31611
  swapSimpleEnglish,
29918
31612
  repeatUntilEventFromEnglish,
@@ -29930,51 +31624,18 @@ function buildEnglishPatterns() {
29930
31624
  patterns.push(...generatedPatterns);
29931
31625
  return patterns;
29932
31626
  }
29933
- var fetchWithResponseTypeEnglish, fetchSimpleEnglish, swapSimpleEnglish, swapElementEnglish, repeatUntilEventFromEnglish, repeatUntilEventEnglish, repeatTimesEnglish, repeatForeverEnglish, setPossessiveEnglish, forEnglish, ifEnglish, unlessEnglish, temporalInEnglish, temporalAfterEnglish;
31627
+ var swapSimpleEnglish, swapElementEnglish, repeatUntilEventFromEnglish, repeatUntilEventEnglish, repeatTimesEnglish, repeatForeverEnglish, setPossessiveEnglish, forEnglish, ifEnglish, unlessEnglish, temporalInEnglish, temporalAfterEnglish;
29934
31628
  var init_en = __esm({
29935
31629
  "src/patterns/en.ts"() {
29936
31630
  init_english();
29937
31631
  init_pattern_generator();
31632
+ init_fetch();
31633
+ init_pick();
29938
31634
  init_toggle();
29939
31635
  init_put();
29940
31636
  init_event_handler();
29941
31637
  init_repeat();
29942
31638
  init_wait();
29943
- fetchWithResponseTypeEnglish = {
29944
- id: "fetch-en-with-response-type",
29945
- language: "en",
29946
- command: "fetch",
29947
- priority: 90,
29948
- template: {
29949
- format: "fetch {source} as {responseType}",
29950
- tokens: [
29951
- { type: "literal", value: "fetch" },
29952
- { type: "role", role: "source", expectedTypes: ["literal", "expression"] },
29953
- { type: "literal", value: "as" },
29954
- { type: "role", role: "responseType", expectedTypes: ["literal", "expression"] }
29955
- ]
29956
- },
29957
- extraction: {
29958
- source: { position: 1 },
29959
- responseType: { marker: "as" }
29960
- }
29961
- };
29962
- fetchSimpleEnglish = {
29963
- id: "fetch-en-simple",
29964
- language: "en",
29965
- command: "fetch",
29966
- priority: 80,
29967
- template: {
29968
- format: "fetch {source}",
29969
- tokens: [
29970
- { type: "literal", value: "fetch" },
29971
- { type: "role", role: "source" }
29972
- ]
29973
- },
29974
- extraction: {
29975
- source: { position: 1 }
29976
- }
29977
- };
29978
31639
  swapSimpleEnglish = {
29979
31640
  id: "swap-en-handcrafted",
29980
31641
  language: "en",
@@ -30246,6 +31907,15 @@ init_chinese();
30246
31907
  // src/parser/pattern-matcher.ts
30247
31908
  init_command_schemas();
30248
31909
 
31910
+ // src/parser/utils/possessive-keywords.ts
31911
+ init_english();
31912
+
31913
+ // src/parser/utils/expression-lexicon.ts
31914
+ init_command_schemas();
31915
+ new Set(
31916
+ Object.keys(commandSchemas).map((a) => a.toLowerCase())
31917
+ );
31918
+
30249
31919
  // src/parser/pattern-matcher.ts
30250
31920
  init_registry();
30251
31921
  init_put();
@@ -30254,15 +31924,6 @@ init_put();
30254
31924
  new Set(
30255
31925
  Object.values(commandSchemas).filter((s) => s.bareKeyword === true).map((s) => s.action)
30256
31926
  );
30257
- /**
30258
- * Normalized command-action keywords (the schema registry's action names).
30259
- * Tokenizers normalize every language's command verbs to these forms, so the
30260
- * set is language-independent. Used to keep the positional source clause
30261
- * from consuming a following command's verb as a locative marker.
30262
- */
30263
- new Set(
30264
- Object.keys(commandSchemas).map((a) => a.toLowerCase())
30265
- );
30266
31927
 
30267
31928
  // src/tokenizers/index.ts
30268
31929
  init_registry();
@@ -30298,6 +31959,9 @@ init_command_schemas();
30298
31959
  // src/utils/confidence-calculator.ts
30299
31960
  init_registry();
30300
31961
 
31962
+ // src/explicit/converter.ts
31963
+ init_registry();
31964
+
30301
31965
  // src/cache/semantic-cache.ts
30302
31966
  var SemanticCache = class {
30303
31967
  constructor(config = {}) {
@@ -30621,6 +32285,231 @@ init_wait();
30621
32285
  // src/patterns/builders.ts
30622
32286
  init_repeat();
30623
32287
 
32288
+ // src/patterns/languages/en/index.ts
32289
+ init_fetch();
32290
+
32291
+ // src/patterns/languages/en/swap.ts
32292
+ var swapSimpleEnglish2 = {
32293
+ id: "swap-en-handcrafted",
32294
+ language: "en",
32295
+ command: "swap",
32296
+ priority: 110,
32297
+ // Higher than generated patterns
32298
+ template: {
32299
+ format: "swap {method} {destination}",
32300
+ tokens: [
32301
+ { type: "literal", value: "swap" },
32302
+ { type: "role", role: "method" },
32303
+ { type: "role", role: "destination" }
32304
+ ]
32305
+ },
32306
+ extraction: {
32307
+ method: { position: 1 },
32308
+ destination: { position: 2 }
32309
+ }
32310
+ };
32311
+ var swapElementEnglish2 = {
32312
+ id: "swap-en-element",
32313
+ language: "en",
32314
+ command: "swap",
32315
+ priority: 120,
32316
+ template: {
32317
+ format: "swap {destination} with {patient}",
32318
+ tokens: [
32319
+ { type: "literal", value: "swap" },
32320
+ { type: "role", role: "destination" },
32321
+ { type: "literal", value: "with" },
32322
+ { type: "role", role: "patient" }
32323
+ ]
32324
+ },
32325
+ extraction: {}
32326
+ };
32327
+ var swapPatternsEn = [swapElementEnglish2, swapSimpleEnglish2];
32328
+
32329
+ // src/patterns/languages/en/repeat.ts
32330
+ var repeatUntilEventFromEnglish2 = {
32331
+ id: "repeat-en-until-event-from",
32332
+ language: "en",
32333
+ command: "repeat",
32334
+ priority: 120,
32335
+ // Highest priority - most specific pattern
32336
+ template: {
32337
+ format: "repeat until event {event} from {source}",
32338
+ tokens: [
32339
+ { type: "literal", value: "repeat" },
32340
+ { type: "literal", value: "until" },
32341
+ { type: "literal", value: "event" },
32342
+ { type: "role", role: "event", expectedTypes: ["literal", "expression"] },
32343
+ { type: "literal", value: "from" },
32344
+ { type: "role", role: "source", expectedTypes: ["selector", "reference", "expression"] }
32345
+ ]
32346
+ },
32347
+ extraction: {
32348
+ event: { marker: "event" },
32349
+ source: { marker: "from" },
32350
+ loopType: { default: { type: "literal", value: "until-event" } }
32351
+ }
32352
+ };
32353
+ var repeatUntilEventEnglish2 = {
32354
+ id: "repeat-en-until-event",
32355
+ language: "en",
32356
+ command: "repeat",
32357
+ priority: 110,
32358
+ // Lower than "from" variant, but higher than quantity-based repeat
32359
+ template: {
32360
+ format: "repeat until event {event}",
32361
+ tokens: [
32362
+ { type: "literal", value: "repeat" },
32363
+ { type: "literal", value: "until" },
32364
+ { type: "literal", value: "event" },
32365
+ { type: "role", role: "event", expectedTypes: ["literal", "expression"] }
32366
+ ]
32367
+ },
32368
+ extraction: {
32369
+ event: { marker: "event" },
32370
+ loopType: { default: { type: "literal", value: "until-event" } }
32371
+ }
32372
+ };
32373
+ var repeatPatternsEn = [
32374
+ repeatUntilEventFromEnglish2,
32375
+ repeatUntilEventEnglish2
32376
+ ];
32377
+
32378
+ // src/patterns/languages/en/set.ts
32379
+ var setPossessiveEnglish2 = {
32380
+ id: "set-en-possessive",
32381
+ language: "en",
32382
+ command: "set",
32383
+ priority: 100,
32384
+ // Higher than generated setSchema (80)
32385
+ template: {
32386
+ format: "set {destination} to {patient}",
32387
+ tokens: [
32388
+ { type: "literal", value: "set" },
32389
+ // Role token with property-path support for possessive syntax
32390
+ {
32391
+ type: "role",
32392
+ role: "destination",
32393
+ expectedTypes: ["property-path", "selector", "reference", "expression"]
32394
+ },
32395
+ { type: "literal", value: "to" },
32396
+ { type: "role", role: "patient", expectedTypes: ["literal", "expression", "reference"] }
32397
+ ]
32398
+ },
32399
+ extraction: {
32400
+ destination: { position: 1 },
32401
+ patient: { marker: "to" }
32402
+ }
32403
+ };
32404
+ var setPatternsEn = [setPossessiveEnglish2];
32405
+
32406
+ // src/patterns/languages/en/control-flow.ts
32407
+ var forEnglish2 = {
32408
+ id: "for-en-basic",
32409
+ language: "en",
32410
+ command: "for",
32411
+ priority: 100,
32412
+ template: {
32413
+ format: "for {patient} in {source}",
32414
+ tokens: [
32415
+ { type: "literal", value: "for" },
32416
+ { type: "role", role: "patient", expectedTypes: ["expression", "reference"] },
32417
+ // Loop variable
32418
+ { type: "literal", value: "in" },
32419
+ { type: "role", role: "source", expectedTypes: ["selector", "expression", "reference"] }
32420
+ // Collection
32421
+ ]
32422
+ },
32423
+ extraction: {
32424
+ patient: { position: 1 },
32425
+ source: { marker: "in" }
32426
+ // NOTE: no `loopType` default — see the rationale in patterns/en.ts
32427
+ // `forEnglish` (the `for` schema has no loopType role; a `loopType:literal="for"`
32428
+ // here only duplicates the action name and is the R1 outlier no translation
32429
+ // reproduces). R2-safe (forMapper reads only patient+source). Kept in sync.
32430
+ }
32431
+ };
32432
+ var ifEnglish2 = {
32433
+ id: "if-en-basic",
32434
+ language: "en",
32435
+ command: "if",
32436
+ priority: 100,
32437
+ template: {
32438
+ format: "if {condition}",
32439
+ tokens: [
32440
+ { type: "literal", value: "if" },
32441
+ { type: "role", role: "condition", expectedTypes: ["expression", "reference", "selector"] }
32442
+ ]
32443
+ },
32444
+ extraction: {
32445
+ condition: { position: 1 }
32446
+ }
32447
+ };
32448
+ var unlessEnglish2 = {
32449
+ id: "unless-en-basic",
32450
+ language: "en",
32451
+ command: "unless",
32452
+ priority: 100,
32453
+ template: {
32454
+ format: "unless {condition}",
32455
+ tokens: [
32456
+ { type: "literal", value: "unless" },
32457
+ { type: "role", role: "condition", expectedTypes: ["expression", "reference", "selector"] }
32458
+ ]
32459
+ },
32460
+ extraction: {
32461
+ condition: { position: 1 }
32462
+ }
32463
+ };
32464
+ var controlFlowPatternsEn = [forEnglish2, ifEnglish2, unlessEnglish2];
32465
+
32466
+ // src/patterns/languages/en/temporal.ts
32467
+ var temporalInEnglish2 = {
32468
+ id: "temporal-en-in",
32469
+ language: "en",
32470
+ command: "wait",
32471
+ priority: 95,
32472
+ // Lower than standard wait patterns
32473
+ template: {
32474
+ format: "in {duration}",
32475
+ tokens: [
32476
+ { type: "literal", value: "in" },
32477
+ { type: "role", role: "duration", expectedTypes: ["literal", "expression"] }
32478
+ ]
32479
+ },
32480
+ extraction: {
32481
+ duration: { position: 1 }
32482
+ }
32483
+ };
32484
+ var temporalAfterEnglish2 = {
32485
+ id: "temporal-en-after",
32486
+ language: "en",
32487
+ command: "wait",
32488
+ priority: 95,
32489
+ // Lower than standard wait patterns
32490
+ template: {
32491
+ format: "after {duration}",
32492
+ tokens: [
32493
+ { type: "literal", value: "after" },
32494
+ { type: "role", role: "duration", expectedTypes: ["literal", "expression"] }
32495
+ ]
32496
+ },
32497
+ extraction: {
32498
+ duration: { position: 1 }
32499
+ }
32500
+ };
32501
+ var temporalPatternsEn = [temporalInEnglish2, temporalAfterEnglish2];
32502
+
32503
+ // src/patterns/languages/en/index.ts
32504
+ [
32505
+ ...fetchPatternsEn,
32506
+ ...swapPatternsEn,
32507
+ ...repeatPatternsEn,
32508
+ ...setPatternsEn,
32509
+ ...controlFlowPatternsEn,
32510
+ ...temporalPatternsEn
32511
+ ];
32512
+
30624
32513
  // src/patterns/builders.ts
30625
32514
  init_pattern_generator();
30626
32515
  init_registry();
@@ -31025,6 +32914,81 @@ function inferRoles(name, args, modifiers, target) {
31025
32914
  }
31026
32915
  break;
31027
32916
  }
32917
+ case 'go': {
32918
+ const kw = (n) => {
32919
+ if (!n || typeof n !== 'object')
32920
+ return undefined;
32921
+ const v = n;
32922
+ if (v.type === 'identifier') {
32923
+ if (typeof v.name === 'string' && v.name !== '')
32924
+ return v.name;
32925
+ return typeof v.value === 'string' ? v.value : undefined;
32926
+ }
32927
+ if (v.type === 'literal' && typeof v.value === 'string')
32928
+ return v.value;
32929
+ return undefined;
32930
+ };
32931
+ const asNode = (x) => x && typeof x === 'object' && 'type' in x ? x : undefined;
32932
+ let destination;
32933
+ let method;
32934
+ const onMod = asNode(modifiers?.on);
32935
+ if (args.length === 0 && onMod) {
32936
+ destination = onMod;
32937
+ if (kw(asNode(modifiers?.method)) === 'url') {
32938
+ method = { type: 'literal', value: 'url' };
32939
+ }
32940
+ }
32941
+ else {
32942
+ const words = args.map(kw);
32943
+ const urlIdx = words.indexOf('url');
32944
+ if (urlIdx !== -1 && args[urlIdx + 1]) {
32945
+ destination = args[urlIdx + 1];
32946
+ method = { type: 'literal', value: 'url' };
32947
+ }
32948
+ else {
32949
+ const SKIP = new Set(['to', 'the']);
32950
+ const POSITION = new Set([
32951
+ 'top',
32952
+ 'middle',
32953
+ 'bottom',
32954
+ 'left',
32955
+ 'center',
32956
+ 'right',
32957
+ 'smoothly',
32958
+ 'instantly',
32959
+ 'in',
32960
+ 'new',
32961
+ 'window',
32962
+ ]);
32963
+ const headIdx = args.findIndex((_, i) => {
32964
+ const w = words[i];
32965
+ return w === undefined || !SKIP.has(w);
32966
+ });
32967
+ const headWord = headIdx !== -1 ? words[headIdx] : undefined;
32968
+ const ofIdx = words.indexOf('of');
32969
+ if (headWord === 'back' || headWord === 'forward') {
32970
+ destination = { type: 'identifier', value: headWord, name: headWord };
32971
+ }
32972
+ else if (ofIdx !== -1 && args[ofIdx + 1]) {
32973
+ destination = kw(args[ofIdx + 1]) === 'the' ? args[ofIdx + 2] : args[ofIdx + 1];
32974
+ }
32975
+ else if (headIdx !== -1 && !POSITION.has(headWord ?? '')) {
32976
+ destination = args[headIdx];
32977
+ }
32978
+ }
32979
+ }
32980
+ const destWord = kw(destination);
32981
+ if ((destWord === 'back' || destWord === 'forward') && destination?.type !== 'identifier') {
32982
+ destination = { type: 'identifier', value: destWord, name: destWord };
32983
+ }
32984
+ if (!destination && target)
32985
+ destination = target;
32986
+ if (destination)
32987
+ roles.destination = destination;
32988
+ if (method)
32989
+ roles.method = method;
32990
+ break;
32991
+ }
31028
32992
  default: {
31029
32993
  const schema = getSchema(name);
31030
32994
  if (!schema)