@lokascript/compilation-service 2.8.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26086,6 +26086,26 @@ function filterBySeverity(diagnostics, severity) {
26086
26086
  return diagnostics.filter((d) => d.severity === severity);
26087
26087
  }
26088
26088
  function defineCommand(schema) {
26089
+ if (typeof schema !== "object" || schema === null) {
26090
+ throw new TypeError(`defineCommand: expected a schema object, got ${describeValue(schema)}.`);
26091
+ }
26092
+ if (typeof schema.action !== "string" || schema.action.length === 0) {
26093
+ throw new TypeError(
26094
+ `defineCommand: 'action' must be a non-empty string (got ${describeValue(schema.action)}).`
26095
+ );
26096
+ }
26097
+ if (!Array.isArray(schema.roles)) {
26098
+ throw new TypeError(
26099
+ `defineCommand("${schema.action}"): 'roles' must be an array of RoleSpec (got ${describeValue(schema.roles)}). Build entries with defineRole().`
26100
+ );
26101
+ }
26102
+ for (const [index2, role] of schema.roles.entries()) {
26103
+ if (typeof role !== "object" || role === null || typeof role.role !== "string") {
26104
+ throw new TypeError(
26105
+ `defineCommand("${schema.action}"): roles[${index2}] must be a RoleSpec with a string 'role' field (got ${describeValue(role)}). Build it with defineRole().`
26106
+ );
26107
+ }
26108
+ }
26089
26109
  return {
26090
26110
  description: schema.description || `${schema.action} command`,
26091
26111
  category: schema.category || "general",
@@ -26105,8 +26125,16 @@ function defineRole(role) {
26105
26125
  };
26106
26126
  }
26107
26127
  function getRoleSpec(schema, role) {
26128
+ if (!schema || !Array.isArray(schema.roles)) return void 0;
26108
26129
  return schema.roles.find((r) => r.role === role);
26109
26130
  }
26131
+ function describeValue(value) {
26132
+ if (value === null) return "null";
26133
+ if (Array.isArray(value)) return `an array of length ${value.length}`;
26134
+ if (typeof value === "string") return `the string ${JSON.stringify(value)}`;
26135
+ if (typeof value === "object") return "an object";
26136
+ return typeof value === "undefined" ? "undefined" : String(value);
26137
+ }
26110
26138
  function inferRolesFromSchema(schema, args, modifiers, target, adapter) {
26111
26139
  const roles = {};
26112
26140
  const skipTokens = new Set(schema.argSkipTokens ?? []);
@@ -28634,10 +28662,12 @@ function buildTokens$1(schema, profile, keyword, alternatives) {
28634
28662
  addRoleWithMarker(tokens, role, profile);
28635
28663
  }
28636
28664
  } else if (profile.wordOrder === "SOV") {
28665
+ const preVerb = [];
28666
+ const postVerb = [];
28637
28667
  for (const role of sortedRoles) {
28638
- addRoleWithMarker(tokens, role, profile);
28668
+ addRoleWithMarker(role.sovSlot === "postVerb" ? postVerb : preVerb, role, profile);
28639
28669
  }
28640
- tokens.push(keywordToken);
28670
+ tokens.push(...preVerb, keywordToken, ...postVerb);
28641
28671
  } else if (profile.wordOrder === "VSO") {
28642
28672
  tokens.push(keywordToken);
28643
28673
  for (const role of sortedRoles) {
@@ -28720,35 +28750,145 @@ function buildExtractionRules$1(schema, profile) {
28720
28750
  return rules;
28721
28751
  }
28722
28752
  function buildFormatString$1(schema, profile, keyword) {
28723
- const parts = [];
28724
- if (profile.wordOrder === "SVO" || profile.wordOrder === "VSO") {
28725
- parts.push(keyword);
28726
- }
28753
+ const isSOV = profile.wordOrder === "SOV";
28754
+ const preVerb = [];
28755
+ const postVerb = [];
28727
28756
  const sortedRoles = sortRolesByWordOrder$1(schema.roles, profile.wordOrder);
28728
28757
  for (const role of sortedRoles) {
28729
28758
  const marker = getMarkerForRole(role, profile);
28730
28759
  const roleName = `{${role.role}}`;
28760
+ const bucket = isSOV && role.sovSlot === "postVerb" ? postVerb : preVerb;
28731
28761
  if (marker) {
28732
28762
  const markerInfo = profile.roleMarkers?.[role.role];
28733
28763
  if (markerInfo?.position === "after") {
28734
- parts.push(`${roleName} ${marker}`);
28764
+ bucket.push(`${roleName} ${marker}`);
28735
28765
  } else {
28736
- parts.push(`${marker} ${roleName}`);
28766
+ bucket.push(`${marker} ${roleName}`);
28737
28767
  }
28738
28768
  } else {
28739
- parts.push(roleName);
28769
+ bucket.push(roleName);
28740
28770
  }
28741
28771
  }
28742
- if (profile.wordOrder === "SOV") {
28743
- parts.push(keyword);
28744
- }
28745
- return parts.join(" ");
28772
+ return (isSOV ? [...preVerb, keyword, ...postVerb] : [keyword, ...preVerb, ...postVerb]).join(
28773
+ " "
28774
+ );
28746
28775
  }
28747
28776
  function generatePatternVariants$1(schema, profile, config2 = defaultConfig$1) {
28748
28777
  const patterns = [];
28749
28778
  patterns.push(generatePattern$1(schema, profile, config2));
28750
28779
  return patterns;
28751
28780
  }
28781
+ function lookupKeyword(keywords, action, language) {
28782
+ return keywords[action]?.[language] ?? action;
28783
+ }
28784
+ function lookupMarker(markers, marker, language) {
28785
+ return markers[marker]?.[language] ?? marker;
28786
+ }
28787
+ function buildPhrase(...parts) {
28788
+ return parts.filter(Boolean).join(" ");
28789
+ }
28790
+ function buildTablesFromProfiles(schemas, profiles2) {
28791
+ const keywords = {};
28792
+ const markers = {};
28793
+ for (const profile of profiles2) {
28794
+ for (const [action, kw] of Object.entries(profile.keywords)) {
28795
+ if (!keywords[action]) keywords[action] = {};
28796
+ keywords[action][profile.code] = kw.primary;
28797
+ }
28798
+ }
28799
+ for (const schema of schemas) {
28800
+ for (const role of schema.roles) {
28801
+ if (role.markerOverride) {
28802
+ const markerKey = role.role;
28803
+ if (!markers[markerKey]) markers[markerKey] = {};
28804
+ for (const [lang, markerText] of Object.entries(role.markerOverride)) {
28805
+ markers[markerKey][lang] = markerText;
28806
+ }
28807
+ }
28808
+ }
28809
+ }
28810
+ for (const profile of profiles2) {
28811
+ if (profile.roleMarkers) {
28812
+ for (const [markerKey, markerDef] of Object.entries(profile.roleMarkers)) {
28813
+ if (!markers[markerKey]) markers[markerKey] = {};
28814
+ markers[markerKey][profile.code] = markerDef.primary;
28815
+ }
28816
+ }
28817
+ }
28818
+ return { keywords, markers };
28819
+ }
28820
+ function detectWordOrders(profiles2) {
28821
+ const sovLanguages = /* @__PURE__ */ new Set();
28822
+ const vsoLanguages = /* @__PURE__ */ new Set();
28823
+ for (const profile of profiles2) {
28824
+ if (profile.wordOrder === "SOV") sovLanguages.add(profile.code);
28825
+ if (profile.wordOrder === "VSO") vsoLanguages.add(profile.code);
28826
+ }
28827
+ return { sovLanguages, vsoLanguages };
28828
+ }
28829
+ function createSchemaRenderer(schemas, profiles2) {
28830
+ const tables = buildRenderTables(schemas, profiles2);
28831
+ return {
28832
+ render(node, language) {
28833
+ return renderFromSchema(node, language, tables) ?? node.action;
28834
+ }
28835
+ };
28836
+ }
28837
+ function createDomainRenderer(config2) {
28838
+ const { schemas, profiles: profiles2, overrides } = config2;
28839
+ let tables;
28840
+ return (node, language) => {
28841
+ const override = overrides?.[node.action];
28842
+ if (override) return override(node, language);
28843
+ tables ?? (tables = buildRenderTables(schemas, profiles2));
28844
+ return renderFromSchema(node, language, tables);
28845
+ };
28846
+ }
28847
+ function buildRenderTables(schemas, profiles2) {
28848
+ const { keywords, markers } = buildTablesFromProfiles(schemas, profiles2);
28849
+ const { sovLanguages } = detectWordOrders(profiles2);
28850
+ const markerPositions = {};
28851
+ for (const profile of profiles2) {
28852
+ for (const [role, markerDef] of Object.entries(profile.roleMarkers ?? {})) {
28853
+ if (!markerDef.position) continue;
28854
+ markerPositions[role] ?? (markerPositions[role] = {});
28855
+ markerPositions[role][profile.code] = markerDef.position;
28856
+ }
28857
+ }
28858
+ const schemaMap = /* @__PURE__ */ new Map();
28859
+ for (const s of schemas) schemaMap.set(s.action, s);
28860
+ return { keywords, markers, markerPositions, sovLanguages, schemaMap };
28861
+ }
28862
+ function renderFromSchema(node, language, tables) {
28863
+ const schema = tables.schemaMap.get(node.action);
28864
+ if (!schema) return null;
28865
+ const keyword = lookupKeyword(tables.keywords, node.action, language);
28866
+ const isSOV = tables.sovLanguages.has(language);
28867
+ const ordered = sortRolesByWordOrder$1([...schema.roles], isSOV ? "SOV" : "SVO");
28868
+ const preVerb = [];
28869
+ const postVerb = [];
28870
+ for (const role of ordered) {
28871
+ let value = extractRoleValue(node, role.role);
28872
+ if (!value && role.default !== void 0) {
28873
+ value = String(extractValue(role.default));
28874
+ }
28875
+ if (!value) continue;
28876
+ if (role.quoteMultiword && /\s/.test(value) && !/^(["']).*\1$/.test(value)) {
28877
+ value = `"${value}"`;
28878
+ }
28879
+ const markerText = role.renderOverride?.[language] ?? role.markerOverride?.[language] ?? role.renderOverride?.["*"] ?? tables.markers[role.role]?.[language];
28880
+ const markerPosition = role.markerPositionOverride?.[language] ?? role.markerPosition ?? tables.markerPositions[role.role]?.[language] ?? (isSOV ? "after" : "before");
28881
+ const bucket = isSOV && role.sovSlot === "postVerb" ? postVerb : preVerb;
28882
+ if (markerText && markerPosition === "before") {
28883
+ bucket.push(markerText, value);
28884
+ } else if (markerText) {
28885
+ bucket.push(value, markerText);
28886
+ } else {
28887
+ bucket.push(value);
28888
+ }
28889
+ }
28890
+ return isSOV ? buildPhrase(...preVerb, keyword, ...postVerb) : buildPhrase(keyword, ...preVerb, ...postVerb);
28891
+ }
28752
28892
  function reorderRoles(roles, targetOrder) {
28753
28893
  const result = [];
28754
28894
  const usedRoles = /* @__PURE__ */ new Set();
@@ -29176,6 +29316,31 @@ var LatinExtendedIdentifierExtractor = class {
29176
29316
  return { value: input.slice(position, end), length: end - position };
29177
29317
  }
29178
29318
  };
29319
+ var SELECTOR_BODY_CHAR = /[\p{L}\p{N}_-]/u;
29320
+ var PARTICLE_SCRIPT_CHAR = /[\p{sc=Han}\p{sc=Hiragana}\p{sc=Katakana}\p{sc=Hangul}]/u;
29321
+ function isSelectorBodyChar(char) {
29322
+ return SELECTOR_BODY_CHAR.test(char) && !PARTICLE_SCRIPT_CHAR.test(char);
29323
+ }
29324
+ var CssSelectorExtractor$1 = class CssSelectorExtractor {
29325
+ constructor() {
29326
+ this.name = "css-selector";
29327
+ }
29328
+ canExtract(input, position) {
29329
+ const char = input[position];
29330
+ if (char !== "#" && char !== ".") return false;
29331
+ const next = input[position + 1];
29332
+ if (next === void 0) return false;
29333
+ return next === "_" || next === "-" || /\p{L}/u.test(next) && !PARTICLE_SCRIPT_CHAR.test(next);
29334
+ }
29335
+ extract(input, position) {
29336
+ let end = position + 1;
29337
+ while (end < input.length && isSelectorBodyChar(input[end])) {
29338
+ end++;
29339
+ }
29340
+ if (end === position + 1) return null;
29341
+ return { value: input.slice(position, end), length: end - position };
29342
+ }
29343
+ };
29179
29344
  var WhitespaceExtractor = class {
29180
29345
  constructor() {
29181
29346
  this.name = "whitespace";
@@ -29895,10 +30060,17 @@ function semanticValueToAST(value) {
29895
30060
  }
29896
30061
  }
29897
30062
  var DSLRegistry = class {
29898
- constructor(config2) {
30063
+ /**
30064
+ * @param partiallyTranslatedActions Actions permitted to lack a keyword in
30065
+ * some configured languages — extension commands, which may be added for a
30066
+ * subset of the DSL's languages. A built-in command missing a keyword stays
30067
+ * an error, since that is a domain-authoring mistake.
30068
+ */
30069
+ constructor(config2, partiallyTranslatedActions = /* @__PURE__ */ new Set()) {
29899
30070
  this.patterns = /* @__PURE__ */ new Map();
29900
30071
  this.tokenizers = /* @__PURE__ */ new Map();
29901
30072
  this.schemas = config2.schemas;
30073
+ this.partiallyTranslatedActions = partiallyTranslatedActions;
29902
30074
  for (const lang of config2.languages) {
29903
30075
  this.registerLanguage(lang);
29904
30076
  }
@@ -29907,6 +30079,9 @@ var DSLRegistry = class {
29907
30079
  this.tokenizers.set(lang.code, lang.tokenizer);
29908
30080
  const patterns = [];
29909
30081
  for (const schema of this.schemas) {
30082
+ if (this.partiallyTranslatedActions.has(schema.action) && !lang.patternProfile.keywords[schema.action]) {
30083
+ continue;
30084
+ }
29910
30085
  const pattern = generatePattern$1(schema, lang.patternProfile);
29911
30086
  patterns.push(pattern);
29912
30087
  }
@@ -29923,13 +30098,25 @@ var DSLRegistry = class {
29923
30098
  }
29924
30099
  };
29925
30100
  var MultilingualDSLImpl = class {
29926
- constructor(config2, registry, transformer) {
30101
+ constructor(config2, registry, transformer, profileProvider) {
29927
30102
  this.registry = registry;
29928
30103
  this.matcher = new PatternMatcher$1();
29929
30104
  this.transformer = transformer;
30105
+ this.profileProvider = profileProvider;
29930
30106
  if (config2.codeGenerator) {
29931
30107
  this.codeGenerator = config2.codeGenerator;
29932
30108
  }
30109
+ if (config2.renderer) {
30110
+ this.domainRenderer = config2.renderer;
30111
+ }
30112
+ this.extensionRenderers = /* @__PURE__ */ new Map();
30113
+ for (const extension of config2.extensions ?? []) {
30114
+ if (extension.render) this.extensionRenderers.set(extension.schema.action, extension.render);
30115
+ }
30116
+ this.schemaFallbackRenderer = createDomainRenderer({
30117
+ schemas: config2.schemas,
30118
+ profiles: config2.languages.map((l) => l.patternProfile)
30119
+ });
29933
30120
  const schemaMap = new Map(config2.schemas.map((s) => [s.action, s]));
29934
30121
  this.schemaLookup = {
29935
30122
  getSchema: (action) => schemaMap.get(action)
@@ -29991,6 +30178,13 @@ var MultilingualDSLImpl = class {
29991
30178
  }
29992
30179
  translate(input, fromLanguage, toLanguage) {
29993
30180
  if (isExplicitSyntax$1(input)) return input;
30181
+ for (const language of [fromLanguage, toLanguage]) {
30182
+ if (!this.profileProvider.getProfile(language)) {
30183
+ throw new Error(
30184
+ `translate() requires a grammar profile for language "${language}", but none is configured. Set 'grammarProfile' on the LanguageConfig for "${language}" in createMultilingualDSL() (parse/validate/compile do not need it), or inject a custom 'profileProvider'.`
30185
+ );
30186
+ }
30187
+ }
29994
30188
  return this.transformer.transform(input, fromLanguage, toLanguage);
29995
30189
  }
29996
30190
  compile(input, language) {
@@ -30019,6 +30213,18 @@ var MultilingualDSLImpl = class {
30019
30213
  };
30020
30214
  }
30021
30215
  }
30216
+ render(node, language) {
30217
+ const extensionRenderer = this.extensionRenderers.get(node.action);
30218
+ if (extensionRenderer) {
30219
+ const rendered = extensionRenderer(node, language);
30220
+ if (rendered != null) return rendered;
30221
+ }
30222
+ if (this.domainRenderer) {
30223
+ const rendered = this.domainRenderer(node, language);
30224
+ if (rendered != null) return rendered;
30225
+ }
30226
+ return this.schemaFallbackRenderer(node, language);
30227
+ }
30022
30228
  getSupportedLanguages() {
30023
30229
  return this.registry.getSupportedLanguages();
30024
30230
  }
@@ -30043,15 +30249,80 @@ function createDefaultProfileProvider(config2) {
30043
30249
  }
30044
30250
  return new InMemoryProfileProvider(profiles2);
30045
30251
  }
30252
+ function applyExtensions(config2) {
30253
+ const extensions = config2.extensions;
30254
+ if (!extensions || extensions.length === 0) return config2;
30255
+ const configuredLanguages = new Set(config2.languages.map((l) => l.code));
30256
+ const actions = new Set(config2.schemas.map((s) => s.action));
30257
+ for (const extension of extensions) {
30258
+ const { action } = extension.schema;
30259
+ if (actions.has(action)) {
30260
+ throw new Error(
30261
+ `Extension command "${action}" collides with a command this DSL already defines. Pick a different action name.`
30262
+ );
30263
+ }
30264
+ actions.add(action);
30265
+ for (const code of Object.keys(extension.vocabulary)) {
30266
+ if (!configuredLanguages.has(code)) {
30267
+ throw new Error(
30268
+ `Extension command "${action}" supplies vocabulary for language "${code}", which is not configured on this DSL. Configured languages: ${[...configuredLanguages].join(", ")}.`
30269
+ );
30270
+ }
30271
+ }
30272
+ }
30273
+ const schemas = [...config2.schemas, ...extensions.map((e) => e.schema)];
30274
+ const languages = config2.languages.map((lang) => {
30275
+ const keywords = { ...lang.patternProfile.keywords };
30276
+ let roleMarkers = lang.patternProfile.roleMarkers;
30277
+ for (const extension of extensions) {
30278
+ const vocabulary = extension.vocabulary[lang.code];
30279
+ if (!vocabulary) continue;
30280
+ keywords[extension.schema.action] = { ...vocabulary.keyword };
30281
+ if (vocabulary.roleMarkers) {
30282
+ roleMarkers = { ...roleMarkers, ...vocabulary.roleMarkers };
30283
+ }
30284
+ }
30285
+ return {
30286
+ ...lang,
30287
+ patternProfile: {
30288
+ ...lang.patternProfile,
30289
+ keywords,
30290
+ ...roleMarkers !== void 0 && { roleMarkers }
30291
+ }
30292
+ };
30293
+ });
30294
+ const extensionGenerators = new Map(
30295
+ extensions.filter((e) => e.generate).map((e) => [e.schema.action, e.generate])
30296
+ );
30297
+ const baseGenerator = config2.codeGenerator;
30298
+ const codeGenerator = extensionGenerators.size > 0 ? {
30299
+ generate(node) {
30300
+ const generate = extensionGenerators.get(node.action);
30301
+ if (generate) return generate(node);
30302
+ if (!baseGenerator) {
30303
+ throw new Error(`No code generator for action "${node.action}"`);
30304
+ }
30305
+ return baseGenerator.generate(node);
30306
+ }
30307
+ } : baseGenerator;
30308
+ return {
30309
+ ...config2,
30310
+ schemas,
30311
+ languages,
30312
+ ...codeGenerator !== void 0 && { codeGenerator }
30313
+ };
30314
+ }
30046
30315
  function createMultilingualDSL(config2) {
30047
- const dictionary = config2.dictionary ?? createDefaultDictionary(config2);
30048
- const profileProvider = config2.profileProvider ?? createDefaultProfileProvider(config2);
30316
+ const effectiveConfig = applyExtensions(config2);
30317
+ const extensionActions = new Set((config2.extensions ?? []).map((e) => e.schema.action));
30318
+ const dictionary = effectiveConfig.dictionary ?? createDefaultDictionary(effectiveConfig);
30319
+ const profileProvider = effectiveConfig.profileProvider ?? createDefaultProfileProvider(effectiveConfig);
30049
30320
  const transformer = new GrammarTransformer({
30050
30321
  dictionary,
30051
30322
  profileProvider
30052
30323
  });
30053
- const registry = new DSLRegistry(config2);
30054
- return new MultilingualDSLImpl(config2, registry, transformer);
30324
+ const registry = new DSLRegistry(effectiveConfig, extensionActions);
30325
+ return new MultilingualDSLImpl(effectiveConfig, registry, transformer, profileProvider);
30055
30326
  }
30056
30327
  function generatePrompt(config2) {
30057
30328
  const outputFormat = config2.outputFormat ?? "both";
@@ -31014,6 +31285,11 @@ var DomainRegistry = class {
31014
31285
  rendered = typeof renderer === "function" ? renderer(node, to) : renderer.render(node, to);
31015
31286
  } catch {
31016
31287
  }
31288
+ } else if (dsl.render) {
31289
+ try {
31290
+ rendered = dsl.render(node, to);
31291
+ } catch {
31292
+ }
31017
31293
  }
31018
31294
  const roles = {};
31019
31295
  for (const [key, value] of node.roles) {
@@ -31559,94 +31835,6 @@ async function registryToAOTBackends(registry) {
31559
31835
  }
31560
31836
  return backends;
31561
31837
  }
31562
- function lookupKeyword(keywords, action, language) {
31563
- return keywords[action]?.[language] ?? action;
31564
- }
31565
- function lookupMarker(markers, marker, language) {
31566
- return markers[marker]?.[language] ?? marker;
31567
- }
31568
- function buildPhrase(...parts) {
31569
- return parts.filter(Boolean).join(" ");
31570
- }
31571
- function buildTablesFromProfiles(schemas, profiles2) {
31572
- const keywords = {};
31573
- const markers = {};
31574
- for (const profile of profiles2) {
31575
- for (const [action, kw] of Object.entries(profile.keywords)) {
31576
- if (!keywords[action]) keywords[action] = {};
31577
- keywords[action][profile.code] = kw.primary;
31578
- }
31579
- }
31580
- for (const schema of schemas) {
31581
- for (const role of schema.roles) {
31582
- if (role.markerOverride) {
31583
- const markerKey = role.role;
31584
- if (!markers[markerKey]) markers[markerKey] = {};
31585
- for (const [lang, markerText] of Object.entries(role.markerOverride)) {
31586
- markers[markerKey][lang] = markerText;
31587
- }
31588
- }
31589
- }
31590
- }
31591
- for (const profile of profiles2) {
31592
- if (profile.roleMarkers) {
31593
- for (const [markerKey, markerDef] of Object.entries(profile.roleMarkers)) {
31594
- if (!markers[markerKey]) markers[markerKey] = {};
31595
- markers[markerKey][profile.code] = markerDef.primary;
31596
- }
31597
- }
31598
- }
31599
- return { keywords, markers };
31600
- }
31601
- function detectWordOrders(profiles2) {
31602
- const sovLanguages = /* @__PURE__ */ new Set();
31603
- const vsoLanguages = /* @__PURE__ */ new Set();
31604
- for (const profile of profiles2) {
31605
- if (profile.wordOrder === "SOV") sovLanguages.add(profile.code);
31606
- if (profile.wordOrder === "VSO") vsoLanguages.add(profile.code);
31607
- }
31608
- return { sovLanguages, vsoLanguages };
31609
- }
31610
- function createSchemaRenderer(schemas, profiles2) {
31611
- const { keywords, markers } = buildTablesFromProfiles(schemas, profiles2);
31612
- const { sovLanguages } = detectWordOrders(profiles2);
31613
- const schemaMap = /* @__PURE__ */ new Map();
31614
- for (const s of schemas) schemaMap.set(s.action, s);
31615
- return {
31616
- render(node, language) {
31617
- const schema = schemaMap.get(node.action);
31618
- if (!schema) return node.action;
31619
- const keyword = lookupKeyword(keywords, node.action, language);
31620
- const isSOV = sovLanguages.has(language);
31621
- const roleParts = [];
31622
- for (const role of schema.roles) {
31623
- const value = extractRoleValue(node, role.role);
31624
- if (!value && !role.required) continue;
31625
- const markerText = role.markerOverride?.[language] ?? markers[role.role]?.[language] ?? void 0;
31626
- roleParts.push({
31627
- ...markerText != null && { marker: markerText },
31628
- value: value || "",
31629
- role
31630
- });
31631
- }
31632
- const parts = [];
31633
- if (isSOV) {
31634
- for (const rp of roleParts) {
31635
- if (rp.value) parts.push(rp.value);
31636
- if (rp.marker) parts.push(rp.marker);
31637
- }
31638
- parts.push(keyword);
31639
- } else {
31640
- parts.push(keyword);
31641
- for (const rp of roleParts) {
31642
- if (rp.marker) parts.push(rp.marker);
31643
- if (rp.value) parts.push(rp.value);
31644
- }
31645
- }
31646
- return buildPhrase(...parts);
31647
- }
31648
- };
31649
- }
31650
31838
  var TokenStreamImpl = class {
31651
31839
  constructor(tokens, language) {
31652
31840
  this.pos = 0;
@@ -31752,6 +31940,9 @@ function isQuote(char) {
31752
31940
  function isDigit(char) {
31753
31941
  return /\d/.test(char);
31754
31942
  }
31943
+ function stripOptionalDiacritics(word) {
31944
+ return word.replace(/[ً-ْٰ]/g, "");
31945
+ }
31755
31946
  function isAsciiLetter(char) {
31756
31947
  return /[a-zA-Z]/.test(char);
31757
31948
  }
@@ -32457,7 +32648,7 @@ var _BaseTokenizer = class _BaseTokenizer2 {
32457
32648
  * @returns Word without diacritics
32458
32649
  */
32459
32650
  removeDiacritics(word) {
32460
- return word.replace(/[\u064B-\u0652\u0670]/g, "");
32651
+ return stripOptionalDiacritics(word);
32461
32652
  }
32462
32653
  /**
32463
32654
  * Try to match a keyword from profile at the current position.
@@ -32548,24 +32739,40 @@ var _BaseTokenizer = class _BaseTokenizer2 {
32548
32739
  });
32549
32740
  }
32550
32741
  /**
32551
- * Look up a keyword by native word (case-insensitive).
32742
+ * Look up a keyword by native word (case-insensitive, diacritic-insensitive).
32552
32743
  * O(1) lookup using the keyword map.
32553
32744
  *
32745
+ * The map is INDEXED both with and without diacritics (see
32746
+ * `initializeKeywordsFromProfile`), so a stripped QUERY is the other half of
32747
+ * that: it lets a surface form carrying harakat the profile does not happen to
32748
+ * spell still find its entry. Only consulted after the exact lookup misses, so
32749
+ * every previously-matching word resolves byte-identically.
32750
+ *
32751
+ * Half-implementing this — indexing stripped but querying exact — is what made
32752
+ * diacritized `بَدِّل` (toggle) tokenize as `kind=particle normalized=with`:
32753
+ * `isKeyword` returned false, so the guard in `ArabicProcliticExtractor` that
32754
+ * exists to prevent exactly that handed the word on, and the single-char `ب`
32755
+ * bi- proclitic claimed it. A wrong CONCEPT, not a failed parse.
32756
+ *
32554
32757
  * @param native - Native word to look up
32555
32758
  * @returns KeywordEntry if found, undefined otherwise
32556
32759
  */
32557
32760
  lookupKeyword(native) {
32558
- return this.profileKeywordMap.get(native.toLowerCase());
32761
+ const exact = this.profileKeywordMap.get(native.toLowerCase());
32762
+ if (exact) return exact;
32763
+ const stripped = this.removeDiacritics(native);
32764
+ if (stripped === native) return void 0;
32765
+ return this.profileKeywordMap.get(stripped.toLowerCase());
32559
32766
  }
32560
32767
  /**
32561
- * Check if a word is a known keyword (case-insensitive).
32562
- * O(1) lookup using the keyword map.
32768
+ * Check if a word is a known keyword (case-insensitive, diacritic-insensitive).
32769
+ * O(1) lookup using the keyword map. See {@link lookupKeyword}.
32563
32770
  *
32564
32771
  * @param native - Native word to check
32565
32772
  * @returns true if the word is a keyword
32566
32773
  */
32567
32774
  isKeyword(native) {
32568
- return this.profileKeywordMap.has(native.toLowerCase());
32775
+ return this.lookupKeyword(native) !== void 0;
32569
32776
  }
32570
32777
  /**
32571
32778
  * Set the morphological normalizer for this tokenizer.
@@ -33517,6 +33724,7 @@ var index$2 = /* @__PURE__ */ Object.freeze({
33517
33724
  BaseMorphologicalNormalizer,
33518
33725
  BaseTokenizer,
33519
33726
  CrossDomainDispatcher,
33727
+ CssSelectorExtractor: CssSelectorExtractor$1,
33520
33728
  DEFAULT_OPERATORS: DEFAULT_OPERATORS$1,
33521
33729
  DEFAULT_PUNCTUATION: DEFAULT_PUNCTUATION$1,
33522
33730
  DEFAULT_REFERENCES,
@@ -33553,6 +33761,7 @@ var index$2 = /* @__PURE__ */ Object.freeze({
33553
33761
  createCompoundNode: createCompoundNode$1,
33554
33762
  createConditionalNode: createConditionalNode$1,
33555
33763
  createDiagnosticCollector,
33764
+ createDomainRenderer,
33556
33765
  createEventHandlerNode,
33557
33766
  createExpression,
33558
33767
  createFlag,
@@ -33644,6 +33853,8 @@ var index$2 = /* @__PURE__ */ Object.freeze({
33644
33853
  semanticNodeToJSON,
33645
33854
  semanticNodeToRuntimeAST: semanticNodeToRuntimeAST$1,
33646
33855
  semanticValueToAST,
33856
+ sortRolesByWordOrder: sortRolesByWordOrder$1,
33857
+ stripOptionalDiacritics,
33647
33858
  synthesizeFromSchemas,
33648
33859
  toEnvelopeJSON: toEnvelopeJSON$1,
33649
33860
  toJSONL,
@@ -34005,9 +34216,12 @@ var init_arabic = __esm({
34005
34216
  behavior: { primary: "\u0633\u0644\u0648\u0643", normalized: "behavior" },
34006
34217
  install: { primary: "\u062A\u062B\u0628\u064A\u062A", alternatives: ["\u062B\u0628\u0651\u062A"], normalized: "install" },
34007
34218
  // `قِس` is the imperative with the kasra diacritic; the i18n dict (and real
34008
- // Arabic prose) emits it undiacritized as `قس`, so list both — otherwise the
34009
- // generated `قس width`/`قس x` (behavior-draggable/resizable) parse to null and
34010
- // the whole `measure` command drops from the event-handler body (lossy).
34219
+ // Arabic prose) emits it undiacritized as `قس`. BOTH stay listed, and not
34220
+ // for the tokenizer's sake — keyword lookup is diacritic-insensitive now, so
34221
+ // either spelling resolves. It is the vocab gate's V1 check, which compares
34222
+ // the profile against the i18n DICTIONARY as strings: the dictionary says
34223
+ // `قس`, so dropping it here fails V1 (verified). Diacritic-insensitivity
34224
+ // would have to reach that comparison too before this pair can collapse.
34011
34225
  measure: { primary: "\u0642\u064A\u0627\u0633", alternatives: ["\u0642\u0650\u0633", "\u0642\u0633"], normalized: "measure" },
34012
34226
  beep: { primary: "\u0635\u0641\u0651\u0631", normalized: "beep" },
34013
34227
  break: { primary: "\u062A\u0648\u0642\u0641", normalized: "break" },
@@ -34683,6 +34897,15 @@ var init_spanish = __esm({
34683
34897
  patient: { primary: "", position: "before" },
34684
34898
  style: { primary: "con", position: "before" }
34685
34899
  },
34900
+ // Imperative command forms are accepted on INPUT only — `primary` stays the
34901
+ // dictionary form, so rendering is unchanged (see LanguageProfile.defaultVerbForm:
34902
+ // infinitive is the industry standard for UI localization). Hyperscript is a
34903
+ // command language, though, and a native speaker giving a command writes the
34904
+ // imperative, so the parser should read it.
34905
+ //
34906
+ // Only the IRREGULARS are listed. The regular ones reach their keyword through
34907
+ // the morphological normalizer's stem (see spanish-keyword.ts and siblings),
34908
+ // which also covers conjugations nobody enumerated here.
34686
34909
  keywords: {
34687
34910
  // Class/Attribute operations
34688
34911
  toggle: { primary: "alternar", alternatives: ["conmutar", "toggle"], normalized: "toggle" },
@@ -34702,19 +34925,23 @@ var init_spanish = __esm({
34702
34925
  swap: { primary: "intercambiar", alternatives: ["permutar"], normalized: "swap" },
34703
34926
  morph: { primary: "transformar", alternatives: ["convertir"], normalized: "morph" },
34704
34927
  // Variable operations
34705
- set: { primary: "establecer", alternatives: ["fijar", "definir"], normalized: "set" },
34706
- get: { primary: "obtener", alternatives: ["conseguir"], normalized: "get" },
34928
+ set: {
34929
+ primary: "establecer",
34930
+ alternatives: ["fijar", "definir", "establece"],
34931
+ normalized: "set"
34932
+ },
34933
+ get: { primary: "obtener", alternatives: ["conseguir", "obt\xE9n"], normalized: "get" },
34707
34934
  increment: { primary: "incrementar", alternatives: ["aumentar"], normalized: "increment" },
34708
34935
  decrement: { primary: "decrementar", alternatives: ["disminuir"], normalized: "decrement" },
34709
34936
  log: { primary: "registrar", alternatives: ["imprimir"], normalized: "log" },
34710
34937
  // Visibility
34711
- show: { primary: "mostrar", alternatives: ["ense\xF1ar"], normalized: "show" },
34938
+ show: { primary: "mostrar", alternatives: ["ense\xF1ar", "muestra"], normalized: "show" },
34712
34939
  hide: { primary: "ocultar", alternatives: ["esconder"], normalized: "hide" },
34713
34940
  transition: { primary: "transici\xF3n", alternatives: ["animar"], normalized: "transition" },
34714
34941
  // Events
34715
34942
  on: { primary: "en", alternatives: ["al"], normalized: "on" },
34716
34943
  trigger: { primary: "disparar", alternatives: ["activar"], normalized: "trigger" },
34717
- send: { primary: "enviar", normalized: "send" },
34944
+ send: { primary: "enviar", alternatives: ["env\xEDa"], normalized: "send" },
34718
34945
  // DOM focus
34719
34946
  focus: { primary: "enfocar", alternatives: ["enfoque"], normalized: "focus" },
34720
34947
  blur: { primary: "desenfocar", alternatives: ["desenfoque"], normalized: "blur" },
@@ -34751,7 +34978,7 @@ var init_spanish = __esm({
34751
34978
  mousedown: { primary: "rat\xF3nabajo", normalized: "mousedown" },
34752
34979
  mouseup: { primary: "rat\xF3narriba", normalized: "mouseup" },
34753
34980
  // Navigation
34754
- go: { primary: "ir", alternatives: ["navegar"], normalized: "go" },
34981
+ go: { primary: "ir", alternatives: ["navegar", "ve"], normalized: "go" },
34755
34982
  push: { primary: "empujar", alternatives: ["push"], normalized: "push" },
34756
34983
  replace: { primary: "reemplazar", alternatives: ["sustituir"], normalized: "replace" },
34757
34984
  process: { primary: "procesar", normalized: "process" },
@@ -34938,11 +35165,24 @@ var init_french = __esm({
34938
35165
  patient: { primary: "", position: "before" },
34939
35166
  style: { primary: "avec", position: "before" }
34940
35167
  },
35168
+ // Imperative command forms are accepted on INPUT only — `primary` stays the
35169
+ // dictionary form, so rendering is unchanged (see LanguageProfile.defaultVerbForm:
35170
+ // infinitive is the industry standard for UI localization). Hyperscript is a
35171
+ // command language, though, and a native speaker giving a command writes the
35172
+ // imperative, so the parser should read it.
35173
+ //
35174
+ // Only the IRREGULARS are listed. The regular ones reach their keyword through
35175
+ // the morphological normalizer's stem (see spanish-keyword.ts and siblings),
35176
+ // which also covers conjugations nobody enumerated here.
34941
35177
  keywords: {
34942
35178
  toggle: { primary: "basculer", alternatives: ["alterner"], normalized: "toggle" },
34943
35179
  add: { primary: "ajouter", normalized: "add" },
34944
- remove: { primary: "supprimer", alternatives: ["enlever", "retirer"], normalized: "remove" },
34945
- put: { primary: "mettre", alternatives: ["placer"], normalized: "put" },
35180
+ remove: {
35181
+ primary: "supprimer",
35182
+ alternatives: ["enlever", "retirer", "retire"],
35183
+ normalized: "remove"
35184
+ },
35185
+ put: { primary: "mettre", alternatives: ["placer", "mets"], normalized: "put" },
34946
35186
  append: { primary: "annexer", normalized: "append" },
34947
35187
  prepend: { primary: "pr\xE9fixer", normalized: "prepend" },
34948
35188
  take: { primary: "prendre", normalized: "take" },
@@ -34951,16 +35191,16 @@ var init_french = __esm({
34951
35191
  swap: { primary: "\xE9changer", alternatives: ["permuter"], normalized: "swap" },
34952
35192
  morph: { primary: "transformer", alternatives: ["m\xE9tamorphoser"], normalized: "morph" },
34953
35193
  set: { primary: "d\xE9finir", alternatives: ["\xE9tablir"], normalized: "set" },
34954
- get: { primary: "obtenir", normalized: "get" },
35194
+ get: { primary: "obtenir", alternatives: ["obtiens"], normalized: "get" },
34955
35195
  increment: { primary: "incr\xE9menter", alternatives: ["augmenter"], normalized: "increment" },
34956
35196
  decrement: { primary: "d\xE9cr\xE9menter", alternatives: ["diminuer"], normalized: "decrement" },
34957
35197
  log: { primary: "enregistrer", alternatives: ["journaliser"], normalized: "log" },
34958
- show: { primary: "montrer", alternatives: ["afficher"], normalized: "show" },
35198
+ show: { primary: "montrer", alternatives: ["afficher", "montre"], normalized: "show" },
34959
35199
  hide: { primary: "cacher", alternatives: ["masquer"], normalized: "hide" },
34960
35200
  transition: { primary: "transition", alternatives: ["animer"], normalized: "transition" },
34961
35201
  on: { primary: "sur", alternatives: ["lors"], normalized: "on" },
34962
35202
  trigger: { primary: "d\xE9clencher", normalized: "trigger" },
34963
- send: { primary: "envoyer", normalized: "send" },
35203
+ send: { primary: "envoyer", alternatives: ["envoie"], normalized: "send" },
34964
35204
  focus: { primary: "focaliser", alternatives: ["concentrer"], normalized: "focus" },
34965
35205
  blur: { primary: "d\xE9focaliser", normalized: "blur" },
34966
35206
  // Phase 1 (v0.9.90): DOM / form state / debug
@@ -34974,13 +35214,13 @@ var init_french = __esm({
34974
35214
  clear: { primary: "effacer", normalized: "clear" },
34975
35215
  reset: { primary: "r\xE9initialiser", alternatives: ["reinitialiser"], normalized: "reset" },
34976
35216
  breakpoint: { primary: "point-arr\xEAt", alternatives: ["point-arret"], normalized: "breakpoint" },
34977
- go: { primary: "aller", alternatives: ["naviguer"], normalized: "go" },
35217
+ go: { primary: "aller", alternatives: ["naviguer", "va"], normalized: "go" },
34978
35218
  scroll: { primary: "d\xE9filer", alternatives: ["faire-d\xE9filer"], normalized: "scroll" },
34979
35219
  push: { primary: "pousser", normalized: "push" },
34980
35220
  replace: { primary: "remplacer", normalized: "replace" },
34981
35221
  process: { primary: "traiter", normalized: "process" },
34982
35222
  wait: { primary: "attendre", normalized: "wait" },
34983
- fetch: { primary: "chercher", alternatives: ["r\xE9cup\xE9rer"], normalized: "fetch" },
35223
+ fetch: { primary: "chercher", alternatives: ["r\xE9cup\xE9rer", "r\xE9cup\xE8re"], normalized: "fetch" },
34984
35224
  settle: { primary: "stabiliser", normalized: "settle" },
34985
35225
  if: { primary: "si", normalized: "if" },
34986
35226
  unless: { primary: "saufsi", normalized: "unless" },
@@ -36339,16 +36579,25 @@ var init_korean = __esm({
36339
36579
  event: { primary: "\uC744", alternatives: ["\uB97C"], position: "after" }
36340
36580
  // Event as object marker
36341
36581
  },
36582
+ // Imperative command forms are accepted on INPUT only — `primary` stays the
36583
+ // dictionary form, so rendering is unchanged (see LanguageProfile.defaultVerbForm:
36584
+ // infinitive is the industry standard for UI localization). Hyperscript is a
36585
+ // command language, though, and a native speaker giving a command writes the
36586
+ // imperative, so the parser should read it.
36587
+ //
36588
+ // Only the IRREGULARS are listed. The regular ones reach their keyword through
36589
+ // the morphological normalizer's stem (see spanish-keyword.ts and siblings),
36590
+ // which also covers conjugations nobody enumerated here.
36342
36591
  keywords: {
36343
36592
  // Class/Attribute operations
36344
36593
  toggle: { primary: "\uD1A0\uAE00", normalized: "toggle" },
36345
36594
  add: { primary: "\uCD94\uAC00", normalized: "add" },
36346
36595
  remove: { primary: "\uC81C\uAC70", alternatives: ["\uC0AD\uC81C"], normalized: "remove" },
36347
36596
  // Content operations
36348
- put: { primary: "\uB123\uB2E4", alternatives: ["\uB123\uAE30", "\uB193\uAE30"], normalized: "put" },
36597
+ put: { primary: "\uB123\uB2E4", alternatives: ["\uB123\uAE30", "\uB193\uAE30", "\uB123\uC73C\uC138\uC694"], normalized: "put" },
36349
36598
  append: { primary: "\uB367\uBD99\uC774\uB2E4", alternatives: ["\uB05D\uC5D0\uCD94\uAC00"], normalized: "append" },
36350
36599
  prepend: { primary: "\uC55E\uC5D0\uCD94\uAC00", alternatives: ["\uC120\uB450\uCD94\uAC00"], normalized: "prepend" },
36351
- take: { primary: "\uAC00\uC838\uC624\uB2E4", normalized: "take" },
36600
+ take: { primary: "\uAC00\uC838\uC624\uB2E4", alternatives: ["\uAC00\uC838\uC624\uC138\uC694"], normalized: "take" },
36352
36601
  make: { primary: "\uB9CC\uB4E4\uB2E4", normalized: "make" },
36353
36602
  clone: { primary: "\uBCF5\uC81C", normalized: "clone" },
36354
36603
  // 복제=duplicate/clone, 복사=copy
@@ -36356,13 +36605,13 @@ var init_korean = __esm({
36356
36605
  morph: { primary: "\uBCC0\uD615", alternatives: ["\uBCC0\uD658"], normalized: "morph" },
36357
36606
  // Variable operations
36358
36607
  set: { primary: "\uC124\uC815", normalized: "set" },
36359
- get: { primary: "\uC5BB\uB2E4", normalized: "get" },
36608
+ get: { primary: "\uC5BB\uB2E4", alternatives: ["\uC5BB\uC73C\uC138\uC694"], normalized: "get" },
36360
36609
  increment: { primary: "\uC99D\uAC00", normalized: "increment" },
36361
36610
  decrement: { primary: "\uAC10\uC18C", normalized: "decrement" },
36362
36611
  log: { primary: "\uB85C\uADF8", normalized: "log" },
36363
36612
  // Visibility
36364
- show: { primary: "\uBCF4\uC774\uB2E4", alternatives: ["\uD45C\uC2DC", "\uBCF4\uC774\uAE30"], normalized: "show" },
36365
- hide: { primary: "\uC228\uAE30\uB2E4", alternatives: ["\uC228\uAE30\uAE30"], normalized: "hide" },
36613
+ show: { primary: "\uBCF4\uC774\uB2E4", alternatives: ["\uD45C\uC2DC", "\uBCF4\uC774\uAE30", "\uBCF4\uC774\uC138\uC694"], normalized: "show" },
36614
+ hide: { primary: "\uC228\uAE30\uB2E4", alternatives: ["\uC228\uAE30\uAE30", "\uC228\uAE30\uC138\uC694"], normalized: "hide" },
36366
36615
  // primary is the loanword 트랜지션; 전환 ("switch/transition") is the form the
36367
36616
  // i18n transformer emits — registered as an alternative (passthrough-alignment).
36368
36617
  // toggle uses 토글, so 전환 carries no collision.
@@ -36370,7 +36619,7 @@ var init_korean = __esm({
36370
36619
  // Events
36371
36620
  on: { primary: "\uC5D0", alternatives: ["\uC2DC", "\uD560 \uB54C"], normalized: "on" },
36372
36621
  trigger: { primary: "\uD2B8\uB9AC\uAC70", normalized: "trigger" },
36373
- send: { primary: "\uBCF4\uB0B4\uB2E4", normalized: "send" },
36622
+ send: { primary: "\uBCF4\uB0B4\uB2E4", alternatives: ["\uBCF4\uB0B4\uC138\uC694"], normalized: "send" },
36374
36623
  // DOM focus
36375
36624
  focus: { primary: "\uD3EC\uCEE4\uC2A4", normalized: "focus" },
36376
36625
  blur: { primary: "\uBE14\uB7EC", normalized: "blur" },
@@ -37146,25 +37395,38 @@ var init_portuguese = __esm({
37146
37395
  patient: { primary: "", position: "before" },
37147
37396
  style: { primary: "com", position: "before" }
37148
37397
  },
37398
+ // Imperative command forms are accepted on INPUT only — `primary` stays the
37399
+ // dictionary form, so rendering is unchanged (see LanguageProfile.defaultVerbForm:
37400
+ // infinitive is the industry standard for UI localization). Hyperscript is a
37401
+ // command language, though, and a native speaker giving a command writes the
37402
+ // imperative, so the parser should read it.
37403
+ //
37404
+ // Only the IRREGULARS are listed. The regular ones reach their keyword through
37405
+ // the morphological normalizer's stem (see spanish-keyword.ts and siblings),
37406
+ // which also covers conjugations nobody enumerated here.
37149
37407
  keywords: {
37150
37408
  toggle: { primary: "alternar", alternatives: [], normalized: "toggle" },
37151
37409
  add: { primary: "adicionar", alternatives: ["acrescentar"], normalized: "add" },
37152
- remove: { primary: "remover", alternatives: ["eliminar", "apagar"], normalized: "remove" },
37153
- put: { primary: "colocar", alternatives: ["p\xF4r", "por"], normalized: "put" },
37410
+ remove: {
37411
+ primary: "remover",
37412
+ alternatives: ["eliminar", "apagar", "remova"],
37413
+ normalized: "remove"
37414
+ },
37415
+ put: { primary: "colocar", alternatives: ["p\xF4r", "por", "coloque"], normalized: "put" },
37154
37416
  append: { primary: "anexar", normalized: "append" },
37155
37417
  prepend: { primary: "preceder", normalized: "prepend" },
37156
- take: { primary: "pegar", normalized: "take" },
37418
+ take: { primary: "pegar", alternatives: ["pegue"], normalized: "take" },
37157
37419
  make: { primary: "fazer", alternatives: ["criar"], normalized: "make" },
37158
37420
  clone: { primary: "clonar", alternatives: [], normalized: "clone" },
37159
37421
  swap: { primary: "trocar", alternatives: ["substituir"], normalized: "swap" },
37160
37422
  morph: { primary: "transformar", alternatives: ["converter"], normalized: "morph" },
37161
- set: { primary: "definir", alternatives: ["configurar"], normalized: "set" },
37162
- get: { primary: "obter", normalized: "get" },
37423
+ set: { primary: "definir", alternatives: ["configurar", "defina"], normalized: "set" },
37424
+ get: { primary: "obter", alternatives: ["obtenha"], normalized: "get" },
37163
37425
  increment: { primary: "incrementar", alternatives: ["aumentar"], normalized: "increment" },
37164
37426
  decrement: { primary: "decrementar", alternatives: ["diminuir"], normalized: "decrement" },
37165
37427
  log: { primary: "registrar", alternatives: ["imprimir"], normalized: "log" },
37166
37428
  show: { primary: "mostrar", alternatives: ["exibir"], normalized: "show" },
37167
- hide: { primary: "ocultar", alternatives: ["esconder"], normalized: "hide" },
37429
+ hide: { primary: "ocultar", alternatives: ["esconder", "esconda"], normalized: "hide" },
37168
37430
  transition: { primary: "transi\xE7\xE3o", alternatives: ["animar"], normalized: "transition" },
37169
37431
  on: { primary: "em", alternatives: ["ao"], normalized: "on" },
37170
37432
  trigger: { primary: "disparar", alternatives: ["ativar"], normalized: "trigger" },
@@ -37183,13 +37445,13 @@ var init_portuguese = __esm({
37183
37445
  alternatives: ["ponto-interrupcao"],
37184
37446
  normalized: "breakpoint"
37185
37447
  },
37186
- go: { primary: "ir", alternatives: ["navegar"], normalized: "go" },
37448
+ go: { primary: "ir", alternatives: ["navegar", "v\xE1"], normalized: "go" },
37187
37449
  scroll: { primary: "rolar", alternatives: ["scroll"], normalized: "scroll" },
37188
37450
  push: { primary: "empurrar", alternatives: ["push"], normalized: "push" },
37189
37451
  replace: { primary: "repor", alternatives: ["recolocar"], normalized: "replace" },
37190
37452
  process: { primary: "processar", normalized: "process" },
37191
37453
  wait: { primary: "esperar", alternatives: ["aguardar"], normalized: "wait" },
37192
- fetch: { primary: "buscar", normalized: "fetch" },
37454
+ fetch: { primary: "buscar", alternatives: ["busque"], normalized: "fetch" },
37193
37455
  settle: { primary: "estabilizar", normalized: "settle" },
37194
37456
  if: { primary: "se", normalized: "if" },
37195
37457
  // salvo — single token ('salvo se' = unless). a_menos kept as an
@@ -40314,8 +40576,53 @@ var init_command_schemas = __esm({
40314
40576
  default: { type: "reference", value: "me" },
40315
40577
  svoPosition: 2,
40316
40578
  sovPosition: 1,
40317
- markerOverride: { en: "to" }
40318
- // "add .class to #element"
40579
+ // `add` is directional, but every profile's `destination` marker is
40580
+ // LOCATIVE (en on, es en, ar على, zh 在, fr sur, de auf, pt em) because
40581
+ // it also serves `toggle`/`show`. Without a per-language override the
40582
+ // rendered text said "add .class ON #element" in every language but
40583
+ // English — the gap lokascript-learn corrects with 6 of its 16 override
40584
+ // entries. ja に / ko 에 / tr e are already directional, so they keep
40585
+ // the profile default.
40586
+ //
40587
+ // Tier B (2.9): he/id/it/sw were the remaining locatives that this
40588
+ // language actually distinguishes.
40589
+ // he — `על` is "ON"; Hebrew adds with the allative `אל` (`ל` is a bound
40590
+ // prefix, so it cannot stand as a separate marker token).
40591
+ // id — `pada` is "at/on"; `ke` is the directional, and it is what the
40592
+ // i18n corpus already renders for every id destination.
40593
+ // it — `in` is locative; Italian adds with `a` (`aggiungere a`).
40594
+ // sw — `kwenye` is not merely locative, it is sw's EVENT keyword
40595
+ // (`on: 'kwenye'` in the dictionary), so reusing it as a
40596
+ // destination marker collides. `kwa` is the corpus rendering.
40597
+ // hi `में` / ru+uk `в` / th `ใน` / vi `vào` are already the right
40598
+ // container-directional for "add to", and keep the profile default.
40599
+ markerOverride: {
40600
+ en: "to",
40601
+ es: "a",
40602
+ ar: "\u0625\u0644\u0649",
40603
+ zh: "\u5230",
40604
+ fr: "\xE0",
40605
+ de: "zu",
40606
+ pt: "a",
40607
+ he: "\u05D0\u05DC",
40608
+ id: "ke",
40609
+ it: "a",
40610
+ sw: "kwa"
40611
+ },
40612
+ // Each language's previous primary marker (and its alternates) still
40613
+ // parses, so source written against ≤2.8 keeps working.
40614
+ markerLegacy: {
40615
+ es: ["en", "sobre", "hacia"],
40616
+ ar: ["\u0639\u0644\u0649", "\u0641\u064A", "\u0628"],
40617
+ zh: ["\u5728", "\u4E8E"],
40618
+ fr: ["sur", "dans"],
40619
+ de: ["auf", "in"],
40620
+ pt: ["em", "para"],
40621
+ he: ["\u05E2\u05DC", "\u05D1", "\u05DC"],
40622
+ id: ["pada", "di"],
40623
+ it: ["in", "su"],
40624
+ sw: ["kwenye"]
40625
+ }
40319
40626
  }
40320
40627
  ],
40321
40628
  // Runtime error documentation
@@ -40405,12 +40712,39 @@ var init_command_schemas = __esm({
40405
40712
  svoPosition: 2,
40406
40713
  sovPosition: 2,
40407
40714
  // SOV: destination comes second (に/에/a marker)
40408
- markerOverride: { en: "into" },
40409
- // "put 'hello' into #output"
40715
+ // "put 'hello' into #output" — directional, so the same locative-default
40716
+ // correction as `add`. es `en` and pt `em` are already right for "into",
40717
+ // as are ja に / ko 에 / tr e; only ar/zh/fr/de need an override.
40718
+ //
40719
+ // Tier B (2.9): `put` is ILLATIVE, so it diverges from `add` where the
40720
+ // two senses differ. he takes `ב` ("in/into" — `שים ב`), NOT the allative
40721
+ // `אל` that `add`/`go` take. it keeps its locative `in` (`mettere in`) —
40722
+ // it is `add`/`go` that needed `a`. id/sw change for the same reason as
40723
+ // `add` (directional / event-keyword collision). hi `में`, ru+uk `в`,
40724
+ // th `ใน` and vi `vào` are all already the illative.
40725
+ markerOverride: {
40726
+ en: "into",
40727
+ ar: "\u0641\u064A",
40728
+ zh: "\u5230",
40729
+ fr: "dans",
40730
+ de: "in",
40731
+ he: "\u05D1",
40732
+ id: "ke",
40733
+ sw: "kwa"
40734
+ },
40410
40735
  // `before` / `after` are alternate position markers; the matched marker
40411
40736
  // is recorded as a literal in the `method` role (a derived role with no
40412
40737
  // surface form of its own — populated by schema-driven role inference).
40413
40738
  markerVariants: { en: ["before", "after"] },
40739
+ markerLegacy: {
40740
+ ar: ["\u0639\u0644\u0649", "\u0625\u0644\u0649", "\u0628"],
40741
+ zh: ["\u5728", "\u4E8E"],
40742
+ fr: ["sur", "\xE0"],
40743
+ de: ["auf", "zu"],
40744
+ he: ["\u05E2\u05DC", "\u05D0\u05DC", "\u05DC"],
40745
+ id: ["pada", "di"],
40746
+ sw: ["kwenye"]
40747
+ },
40414
40748
  methodCarrier: "method"
40415
40749
  }
40416
40750
  ],
@@ -40545,8 +40879,11 @@ var init_command_schemas = __esm({
40545
40879
  // ending in a vowel (`doğru ya` = "true" in set-attribute). markerOverride
40546
40880
  // is a single string, so the generated tr set patterns carried only `e`
40547
40881
  // and set-attribute fell to the role-scrambling generic SOV extraction.
40548
- // markerVariants supplies the allomorphs the SOV two-role generators merge
40549
- // in as marker alternatives. See STRUCTURAL_ARCS_ROADMAP.md (tr set-attribute).
40882
+ // markerVariants supplies the allomorphs, merged in as marker alternatives.
40883
+ // Until 2026-07-25 only the SOV two-role generators merged them, so this
40884
+ // worked ONLY inside an event handler: `@disabled i doğru ya ayarla` did
40885
+ // not parse as a bare command while `tıklama da @disabled i doğru ya
40886
+ // ayarla` did. See STRUCTURAL_ARCS_ROADMAP.md (tr set-attribute).
40550
40887
  markerVariants: {
40551
40888
  tr: ["e", "a", "ye", "ya"]
40552
40889
  }
@@ -41478,17 +41815,100 @@ var init_command_schemas = __esm({
41478
41815
  expectedTypes: ["literal", "expression"],
41479
41816
  svoPosition: 1,
41480
41817
  sovPosition: 1,
41481
- markerOverride: { en: "to" },
41482
- // "go to /page" (parsing)
41483
- renderOverride: { en: "" },
41484
- // "go /page" (rendering — no preposition)
41818
+ // "go to /page" (parsing). Directional, so the same locative-default
41819
+ // correction as `add`/`put`.
41820
+ //
41821
+ // Tier B (2.9): `go` is pure ALLATIVE — motion toward a target — so it
41822
+ // needs the directional in more languages than `add`/`put` do, including
41823
+ // ones where a container-locative was fine for those two.
41824
+ // he — `אל` ("toward"), as `add`; `לך על url` read "go ON url".
41825
+ // hi — `पर`: Hindi navigates to a page with `पर जाएं`; `में` is
41826
+ // "go INTO", which is entering a place, not opening a URL.
41827
+ // id — `ke`, as `add`.
41828
+ // it — `a`: `andare a` for a specific target (`andare in` is for
41829
+ // regions — `andare in Italia`).
41830
+ // ru/uk — `на`: `перейти на сторінку` is the navigation idiom; `в`
41831
+ // ("into") is right for `add`/`put` but not for opening a page.
41832
+ // sw — `kwa`, as `add`.
41833
+ // th is NOT here — it renders bare, with zh and vi; see below.
41834
+ markerOverride: {
41835
+ en: "to",
41836
+ es: "a",
41837
+ ar: "\u0625\u0644\u0649",
41838
+ fr: "\xE0",
41839
+ de: "zu",
41840
+ pt: "para",
41841
+ he: "\u05D0\u05DC",
41842
+ hi: "\u092A\u0930",
41843
+ id: "ke",
41844
+ it: "a",
41845
+ ru: "\u043D\u0430",
41846
+ sw: "kwa",
41847
+ uk: "\u043D\u0430"
41848
+ },
41849
+ // "go /page" (rendering — no preposition).
41850
+ //
41851
+ // zh, vi and th render BARE.
41852
+ //
41853
+ // zh and vi because their `go` keyword already encodes the direction, so
41854
+ // any destination marker is a second one: zh `前往` is "proceed-to"
41855
+ // (`前往 到 url` = "proceed-to to url") and vi `đi đến` is literally
41856
+ // "go to" (`đi đến vào url` = "go-to into url"). Both are corrected in
41857
+ // the i18n corpus in the same change
41858
+ // (`patterns-reference/scripts/fix-translations.sql`).
41859
+ //
41860
+ // th because Thai motion verbs take a BARE destination — `ไปบ้าน`
41861
+ // ("go home"), `ไปโรงเรียน` ("go school") — so `ไป url` is the idiomatic
41862
+ // form. The profile default rendered `ไป ใน url` ("go IN url"), which is
41863
+ // what needed fixing; the obvious replacement `ยัง` (giving the formal
41864
+ // `ไปยัง`) is rejected because `ยัง` is also the very common adverb
41865
+ // "still/yet", and the V4 vocab gate correctly refuses to classify it as
41866
+ // a particle — promoting it would mis-tokenize ordinary Thai.
41867
+ //
41868
+ // Parsing is unaffected for all three: none has a `markerOverride`, so
41869
+ // each stays on the profile-default branch and keeps accepting its old
41870
+ // markers (th `ใน` / `ไปยัง`) from the profile itself.
41871
+ renderOverride: { en: "", zh: "", vi: "", th: "" },
41485
41872
  // `go back` renders the destination bare in en (history nav has no `to`),
41486
41873
  // and he/zh render it with their PATIENT marker (לך את back / 前往 把 back)
41487
41874
  // while go-url keeps the destination marker (לך על url / 前往 到 url) —
41488
41875
  // the corpus is ground truth, so en's `to` is optional and he/zh accept
41489
41876
  // the patient particle as a destination-marker alternative, scoped to go.
41490
- markerOptional: { en: true },
41491
- markerVariants: { he: ["\u05D0\u05EA"], zh: ["\u628A"] }
41877
+ // The render side drops the preposition for these four, so the parse
41878
+ // side cannot require it: `go /page`, `前往 url`, `đi đến url`, `ไป url`
41879
+ // must parse alongside the marked forms the profile still accepts.
41880
+ markerOptional: { en: true, zh: true, vi: true, th: true },
41881
+ // zh renders `前往 把 back` with its PATIENT particle before go's
41882
+ // destination — a synonym here, not a distinct shape, so it is accepted as
41883
+ // a marker alternative scoped to go. he's `את` is the same thing and sits
41884
+ // in `markerLegacy` below: it moved there in #763 because the two fields
41885
+ // were then read by DIFFERENT branches, so leaving it here silently
41886
+ // stopped `לך את back` parsing the moment he gained a `markerOverride`.
41887
+ // Both fields now merge on both branches (`schemaMarkerAlternatives`), so
41888
+ // that trap is gone and the split is historical.
41889
+ markerVariants: { zh: ["\u628A"] },
41890
+ markerLegacy: {
41891
+ es: ["en", "sobre", "hacia"],
41892
+ ar: ["\u0639\u0644\u0649", "\u0641\u064A", "\u0628"],
41893
+ fr: ["sur", "dans"],
41894
+ de: ["auf", "in"],
41895
+ pt: ["em", "a"],
41896
+ // `את` is he's PATIENT particle, which the transformer renders before
41897
+ // go's destination in `go back` (`לך את back`) — a parse-only synonym
41898
+ // here, never rendered, which is exactly what markerLegacy is for.
41899
+ he: ["\u05E2\u05DC", "\u05D1", "\u05DC", "\u05D0\u05EA"],
41900
+ hi: ["\u092E\u0947\u0902"],
41901
+ id: ["pada", "di"],
41902
+ it: ["in", "su"],
41903
+ ru: ["\u0432", "\u043A"],
41904
+ sw: ["kwenye"],
41905
+ uk: ["\u0432", "\u0434\u043E"]
41906
+ // zh, vi and th are NOT listed: none has a markerOverride, so all three
41907
+ // stay on the profile-default branch and keep accepting their old
41908
+ // markers from the profile itself. Only their RENDERING changed.
41909
+ // Listing them here would be dead config — markerLegacy is read ONLY by
41910
+ // the override branch.
41911
+ }
41492
41912
  }
41493
41913
  ],
41494
41914
  // `go to url "/page"` — without this variant the destination captures the
@@ -42490,7 +42910,7 @@ var init_command_schemas = __esm({
42490
42910
  roles: []
42491
42911
  }
42492
42912
  };
42493
- if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
42913
+ if (typeof process !== "undefined" && process.env.LOKASCRIPT_SCHEMA_VALIDATION === "1") {
42494
42914
  Promise.resolve().then(() => (init_schema_validator(), schema_validator_exports)).then(({ validateAllSchemas: validateAllSchemas2, formatValidationResults: formatValidationResults2 }) => {
42495
42915
  const validations = validateAllSchemas2(commandSchemas);
42496
42916
  if (validations.size > 0) {
@@ -44065,10 +44485,10 @@ function extractCssSelector(input, position) {
44065
44485
  }
44066
44486
  return null;
44067
44487
  }
44068
- var CssSelectorExtractor;
44488
+ var CssSelectorExtractor2;
44069
44489
  var init_css_selector = __esm({
44070
44490
  "src/tokenizers/extractors/css-selector.ts"() {
44071
- CssSelectorExtractor = class {
44491
+ CssSelectorExtractor2 = class {
44072
44492
  constructor() {
44073
44493
  this.name = "css-selector";
44074
44494
  }
@@ -47235,7 +47655,7 @@ function getHyperscriptExtractors() {
47235
47655
  new UrlExtractor(),
47236
47656
  new EventModifierExtractor(),
47237
47657
  new VariableRefExtractor(),
47238
- new CssSelectorExtractor()
47658
+ new CssSelectorExtractor2()
47239
47659
  ];
47240
47660
  }
47241
47661
  var init_extractor_helpers = __esm({
@@ -49058,12 +49478,16 @@ var init_spanish_keyword = __esm({
49058
49478
  const keywordEntry = this.context.lookupKeyword(word);
49059
49479
  const normalized2 = keywordEntry && keywordEntry.normalized !== keywordEntry.native ? keywordEntry.normalized : void 0;
49060
49480
  let morphNormalized;
49481
+ let morphStem;
49482
+ let morphConfidence;
49061
49483
  if (!keywordEntry && this.context.normalizer) {
49062
49484
  const morphResult = this.context.normalizer.normalize(word);
49063
49485
  if (morphResult.stem !== word && morphResult.confidence >= 0.7) {
49064
49486
  const stemEntry = this.context.lookupKeyword(morphResult.stem);
49065
49487
  if (stemEntry) {
49066
49488
  morphNormalized = stemEntry.normalized;
49489
+ morphStem = morphResult.stem;
49490
+ morphConfidence = morphResult.confidence;
49067
49491
  }
49068
49492
  }
49069
49493
  }
@@ -49072,6 +49496,8 @@ var init_spanish_keyword = __esm({
49072
49496
  length: pos2 - position,
49073
49497
  metadata: {
49074
49498
  normalized: normalized2 || morphNormalized,
49499
+ stem: morphStem,
49500
+ stemConfidence: morphConfidence,
49075
49501
  isPreposition
49076
49502
  }
49077
49503
  };
@@ -50198,12 +50624,16 @@ var init_portuguese_keyword = __esm({
50198
50624
  const keywordEntry = this.context.lookupKeyword(lower);
50199
50625
  const normalized2 = keywordEntry && keywordEntry.normalized !== keywordEntry.native ? keywordEntry.normalized : void 0;
50200
50626
  let morphNormalized;
50627
+ let morphStem;
50628
+ let morphConfidence;
50201
50629
  if (!keywordEntry && this.context.normalizer) {
50202
50630
  const morphResult = this.context.normalizer.normalize(word);
50203
50631
  if (morphResult.stem !== word && morphResult.confidence >= 0.7) {
50204
50632
  const stemEntry = this.context.lookupKeyword(morphResult.stem);
50205
50633
  if (stemEntry) {
50206
50634
  morphNormalized = stemEntry.normalized;
50635
+ morphStem = morphResult.stem;
50636
+ morphConfidence = morphResult.confidence;
50207
50637
  }
50208
50638
  }
50209
50639
  }
@@ -50212,6 +50642,8 @@ var init_portuguese_keyword = __esm({
50212
50642
  length: pos2 - position,
50213
50643
  metadata: {
50214
50644
  normalized: normalized2 || morphNormalized,
50645
+ stem: morphStem,
50646
+ stemConfidence: morphConfidence,
50215
50647
  isPreposition
50216
50648
  }
50217
50649
  };
@@ -50696,12 +51128,16 @@ var init_french_keyword = __esm({
50696
51128
  const keywordEntry = this.context.lookupKeyword(lower);
50697
51129
  const normalized2 = keywordEntry && keywordEntry.normalized !== keywordEntry.native ? keywordEntry.normalized : void 0;
50698
51130
  let morphNormalized;
51131
+ let morphStem;
51132
+ let morphConfidence;
50699
51133
  if (!keywordEntry && this.context.normalizer) {
50700
51134
  const morphResult = this.context.normalizer.normalize(word);
50701
51135
  if (morphResult.stem !== word && morphResult.confidence >= 0.7) {
50702
51136
  const stemEntry = this.context.lookupKeyword(morphResult.stem);
50703
51137
  if (stemEntry) {
50704
51138
  morphNormalized = stemEntry.normalized;
51139
+ morphStem = morphResult.stem;
51140
+ morphConfidence = morphResult.confidence;
50705
51141
  }
50706
51142
  }
50707
51143
  }
@@ -50710,6 +51146,8 @@ var init_french_keyword = __esm({
50710
51146
  length: pos2 - position,
50711
51147
  metadata: {
50712
51148
  normalized: normalized2 || morphNormalized,
51149
+ stem: morphStem,
51150
+ stemConfidence: morphConfidence,
50713
51151
  isPreposition
50714
51152
  }
50715
51153
  };
@@ -57343,8 +57781,10 @@ function resolveMarkerForRole(roleSpec, profile) {
57343
57781
  const overrideMarker = roleSpec.markerOverride?.[profile.code];
57344
57782
  const defaultMarker = profile.roleMarkers[roleSpec.role];
57345
57783
  if (overrideMarker !== void 0) {
57784
+ const alternatives = legacyMarkerAlternatives(roleSpec, profile.code, overrideMarker);
57346
57785
  return {
57347
57786
  primary: overrideMarker,
57787
+ ...alternatives && { alternatives },
57348
57788
  position: defaultMarker?.position ?? "before",
57349
57789
  isOverride: true
57350
57790
  };
@@ -57362,6 +57802,18 @@ function resolveMarkerForRole(roleSpec, profile) {
57362
57802
  }
57363
57803
  return null;
57364
57804
  }
57805
+ function legacyMarkerAlternatives(roleSpec, languageCode, overrideMarker) {
57806
+ const legacy = roleSpec.markerLegacy?.[languageCode];
57807
+ if (!legacy?.length) return void 0;
57808
+ const alternatives = [...new Set(legacy)].filter((a) => a && a !== overrideMarker);
57809
+ return alternatives.length ? alternatives : void 0;
57810
+ }
57811
+ function schemaMarkerAlternatives(roleSpec, languageCode, marker) {
57812
+ const legacy = roleSpec.markerLegacy?.[languageCode] ?? [];
57813
+ const variants = roleSpec.methodCarrier ? [] : roleSpec.markerVariants?.[languageCode] ?? [];
57814
+ const alternatives = [.../* @__PURE__ */ new Set([...legacy, ...variants])].filter((a) => a && a !== marker);
57815
+ return alternatives.length ? alternatives : void 0;
57816
+ }
57365
57817
  var init_marker_resolution = __esm({
57366
57818
  "src/parser/utils/marker-resolution.ts"() {
57367
57819
  }
@@ -57371,20 +57823,17 @@ function resolveRoleMarker(roleSpec, profile) {
57371
57823
  let alternatives;
57372
57824
  if (roleSpec.markerOverride && roleSpec.markerOverride[profile.code] !== void 0) {
57373
57825
  marker = roleSpec.markerOverride[profile.code];
57826
+ alternatives = marker ? schemaMarkerAlternatives(roleSpec, profile.code, marker) : void 0;
57374
57827
  } else {
57375
57828
  const roleMarker = profile.roleMarkers[roleSpec.role];
57376
57829
  if (roleMarker) {
57377
57830
  marker = roleMarker.primary;
57378
- alternatives = roleMarker.alternatives ? [...roleMarker.alternatives] : void 0;
57379
- }
57380
- }
57381
- const variants = roleSpec.markerVariants?.[profile.code];
57382
- if (variants && variants.length > 0) {
57383
- const merged = alternatives ? [...alternatives] : [];
57384
- for (const v2 of variants) {
57385
- if (v2 !== marker && !merged.includes(v2)) merged.push(v2);
57831
+ const schemaAlts = schemaMarkerAlternatives(roleSpec, profile.code, marker) ?? [];
57832
+ const merged = [.../* @__PURE__ */ new Set([...roleMarker.alternatives ?? [], ...schemaAlts])].filter(
57833
+ (a) => a !== marker
57834
+ );
57835
+ alternatives = merged.length ? merged : void 0;
57386
57836
  }
57387
- alternatives = merged;
57388
57837
  }
57389
57838
  return { marker, alternatives };
57390
57839
  }
@@ -57853,8 +58302,16 @@ function generateSOVTwoRoleDestFirstEventHandlerPattern(commandSchema, profile,
57853
58302
  var init_event_handlers_sov = __esm({
57854
58303
  "src/generators/event-handlers-sov.ts"() {
57855
58304
  init_command_schemas();
58305
+ init_marker_resolution();
57856
58306
  }
57857
58307
  });
58308
+ function mergeSchemaAlternatives(roleSpec, profile, roleMarker) {
58309
+ const schemaAlts = schemaMarkerAlternatives(roleSpec, profile.code, roleMarker.primary) ?? [];
58310
+ const merged = [.../* @__PURE__ */ new Set([...roleMarker.alternatives ?? [], ...schemaAlts])].filter(
58311
+ (a) => a !== roleMarker.primary
58312
+ );
58313
+ return merged.length ? merged : void 0;
58314
+ }
57858
58315
  function generateVSOEventHandlerPattern(commandSchema, profile, keyword, eventMarker, config2) {
57859
58316
  const tokens = [];
57860
58317
  if (eventMarker.position === "before") {
@@ -57967,11 +58424,12 @@ function generateVSOVerbFirstTwoRoleEventHandlerPattern(commandSchema, profile,
57967
58424
  let markerAlternatives;
57968
58425
  if (roleSpec.markerOverride && roleSpec.markerOverride[profile.code] !== void 0) {
57969
58426
  marker = roleSpec.markerOverride[profile.code];
58427
+ markerAlternatives = marker ? schemaMarkerAlternatives(roleSpec, profile.code, marker) : void 0;
57970
58428
  } else {
57971
58429
  const roleMarker = profile.roleMarkers[roleSpec.role];
57972
58430
  if (roleMarker) {
57973
58431
  marker = roleMarker.primary;
57974
- markerAlternatives = roleMarker.alternatives;
58432
+ markerAlternatives = mergeSchemaAlternatives(roleSpec, profile, roleMarker);
57975
58433
  }
57976
58434
  }
57977
58435
  if (marker) {
@@ -58024,11 +58482,12 @@ function generateVSOTwoRoleEventHandlerPattern(commandSchema, profile, keyword,
58024
58482
  let markerAlternatives;
58025
58483
  if (roleSpec.markerOverride && roleSpec.markerOverride[profile.code] !== void 0) {
58026
58484
  marker = roleSpec.markerOverride[profile.code];
58485
+ markerAlternatives = marker ? schemaMarkerAlternatives(roleSpec, profile.code, marker) : void 0;
58027
58486
  } else {
58028
58487
  const roleMarker = profile.roleMarkers[roleSpec.role];
58029
58488
  if (roleMarker) {
58030
58489
  marker = roleMarker.primary;
58031
- markerAlternatives = roleMarker.alternatives;
58490
+ markerAlternatives = mergeSchemaAlternatives(roleSpec, profile, roleMarker);
58032
58491
  }
58033
58492
  }
58034
58493
  if (marker) {
@@ -58161,6 +58620,7 @@ var init_event_handlers_vso = __esm({
58161
58620
  "src/generators/event-handlers-vso.ts"() {
58162
58621
  init_command_schemas();
58163
58622
  init_event_handlers_sov();
58623
+ init_marker_resolution();
58164
58624
  }
58165
58625
  });
58166
58626
  function getAllRegisteredProfiles() {
@@ -58558,7 +59018,13 @@ function buildRoleToken(roleSpec, profile) {
58558
59018
  const position = defaultMarker?.position ?? "before";
58559
59019
  const optionalMarker = roleSpec.markerOptional?.[profile.code] === true;
58560
59020
  const pushWord = (word) => {
58561
- const literal = suppressMarker ? { type: "literal", value: word, renderSuppress: true } : { type: "literal", value: word };
59021
+ const alternatives = markerWords.length === 1 ? schemaMarkerAlternatives(roleSpec, profile.code, word) ?? [] : [];
59022
+ const literal = {
59023
+ type: "literal",
59024
+ value: word,
59025
+ ...alternatives.length ? { alternatives } : {},
59026
+ ...suppressMarker ? { renderSuppress: true } : {}
59027
+ };
58562
59028
  tokens.push(optionalMarker ? { type: "group", optional: true, tokens: [literal] } : literal);
58563
59029
  };
58564
59030
  if (position === "before") {
@@ -58569,10 +59035,12 @@ function buildRoleToken(roleSpec, profile) {
58569
59035
  for (const word of markerWords) pushWord(word);
58570
59036
  }
58571
59037
  } else if (defaultMarker) {
58572
- const variantAlts = roleSpec.markerVariants?.[profile.code] ?? [];
58573
59038
  const asMarker = () => {
58574
59039
  const alternatives = [
58575
- .../* @__PURE__ */ new Set([...defaultMarker.alternatives ?? [], ...variantAlts])
59040
+ .../* @__PURE__ */ new Set([
59041
+ ...defaultMarker.alternatives ?? [],
59042
+ ...schemaMarkerAlternatives(roleSpec, profile.code, defaultMarker.primary) ?? []
59043
+ ])
58576
59044
  ].filter((a) => a !== defaultMarker.primary);
58577
59045
  return {
58578
59046
  type: "literal",
@@ -58608,11 +59076,19 @@ function buildExtractionRules(schema, profile) {
58608
59076
  if (roleSpec.valuePrefixLiteral?.[profile.code]) {
58609
59077
  rules[roleSpec.role] = { marker: roleSpec.valuePrefixLiteral[profile.code] };
58610
59078
  } else if (overrideMarker !== void 0) {
58611
- rules[roleSpec.role] = overrideMarker ? { marker: overrideMarker } : {};
59079
+ if (!overrideMarker) {
59080
+ rules[roleSpec.role] = {};
59081
+ } else {
59082
+ const isSingleWord = !/\s/.test(overrideMarker.trim());
59083
+ const markerAlternatives = isSingleWord ? schemaMarkerAlternatives(roleSpec, profile.code, overrideMarker) ?? [] : [];
59084
+ rules[roleSpec.role] = markerAlternatives.length ? { marker: overrideMarker, markerAlternatives } : { marker: overrideMarker };
59085
+ }
58612
59086
  } else if (defaultMarker && defaultMarker.primary) {
58613
- const variantAlts = roleSpec.markerVariants?.[profile.code] ?? [];
58614
59087
  const markerAlternatives = [
58615
- .../* @__PURE__ */ new Set([...defaultMarker.alternatives ?? [], ...variantAlts])
59088
+ .../* @__PURE__ */ new Set([
59089
+ ...defaultMarker.alternatives ?? [],
59090
+ ...schemaMarkerAlternatives(roleSpec, profile.code, defaultMarker.primary) ?? []
59091
+ ])
58616
59092
  ].filter((a) => a !== defaultMarker.primary);
58617
59093
  rules[roleSpec.role] = markerAlternatives.length ? { marker: defaultMarker.primary, markerAlternatives } : { marker: defaultMarker.primary };
58618
59094
  } else {
@@ -64892,6 +65368,12 @@ var _PatternMatcher2 = class _PatternMatcher3 {
64892
65368
  if (token.kind === "keyword" && token.value.toLowerCase() === value.toLowerCase()) {
64893
65369
  return "case-insensitive";
64894
65370
  }
65371
+ if (token.kind === "keyword") {
65372
+ const strippedToken = stripOptionalDiacritics(token.value).toLowerCase();
65373
+ if (strippedToken && strippedToken === stripOptionalDiacritics(value).toLowerCase()) {
65374
+ return "case-insensitive";
65375
+ }
65376
+ }
64895
65377
  return "none";
64896
65378
  }
64897
65379
  /**
@@ -69990,6 +70472,7 @@ function parseWithConfidence(hyperscript2, language) {
69990
70472
  tokensConsumed: confidenceResult.tokensConsumed
69991
70473
  };
69992
70474
  }
70475
+ init_registry();
69993
70476
  function toExplicit(input, sourceLanguage) {
69994
70477
  if (isExplicitSyntax$1(input)) {
69995
70478
  return input;
@@ -70030,28 +70513,22 @@ function roundTrip(input, sourceLanguage, targetLanguage) {
70030
70513
  matches: normalizedOriginal === normalizedRendered
70031
70514
  };
70032
70515
  }
70033
- function getAllTranslations(input, sourceLanguage, targetLanguages = [
70034
- "en",
70035
- "ja",
70036
- "ar",
70037
- "es",
70038
- "ko",
70039
- "zh",
70040
- "tr",
70041
- "pt",
70042
- "fr",
70043
- "de",
70044
- "id",
70045
- "qu",
70046
- "sw"
70047
- ]) {
70048
- const result = {};
70516
+ function getAllTranslations(input, sourceLanguage, targetLanguages = getRegisteredLanguages()) {
70517
+ return getAllTranslationsWithStatus(input, sourceLanguage, targetLanguages).translations;
70518
+ }
70519
+ function getAllTranslationsWithStatus(input, sourceLanguage, targetLanguages = getRegisteredLanguages()) {
70520
+ const translations = {};
70521
+ const failed = {};
70049
70522
  const node = parseAny(input, sourceLanguage);
70050
70523
  for (const lang of targetLanguages) {
70051
- result[lang] = render(node, lang);
70524
+ try {
70525
+ translations[lang] = render(node, lang);
70526
+ } catch (error) {
70527
+ failed[lang] = error instanceof Error ? error.message : String(error);
70528
+ }
70052
70529
  }
70053
- result["explicit"] = renderExplicit$1(node);
70054
- return result;
70530
+ translations["explicit"] = renderExplicit$1(node);
70531
+ return { translations, failed };
70055
70532
  }
70056
70533
  function validateTranslation(original, translated, originalLang, translatedLang) {
70057
70534
  try {
@@ -77150,7 +77627,7 @@ var index$1 = /* @__PURE__ */ Object.freeze({
77150
77627
  __proto__: null,
77151
77628
  ASTBuilder,
77152
77629
  get CssSelectorExtractor() {
77153
- return CssSelectorExtractor;
77630
+ return CssSelectorExtractor2;
77154
77631
  },
77155
77632
  DEFAULT_CONFIDENCE_THRESHOLD,
77156
77633
  get EventModifierExtractor() {
@@ -77270,6 +77747,7 @@ var index$1 = /* @__PURE__ */ Object.freeze({
77270
77747
  return germanProfile;
77271
77748
  },
77272
77749
  getAllTranslations,
77750
+ getAllTranslationsWithStatus,
77273
77751
  getCommandMapper,
77274
77752
  getCommandType,
77275
77753
  getDefaultExtractors,
@@ -85906,4 +86384,4 @@ export {
85906
86384
  toCoreAST,
85907
86385
  validatePartialContent
85908
86386
  };
85909
- //# sourceMappingURL=dist-DIXYSN7G.js.map
86387
+ //# sourceMappingURL=dist-EFDE4LTF.js.map