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