@hyperfixi/core 2.8.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -27526,6 +27526,26 @@ function filterBySeverity(diagnostics, severity) {
27526
27526
 
27527
27527
  // src/schema.ts
27528
27528
  function defineCommand(schema) {
27529
+ if (typeof schema !== "object" || schema === null) {
27530
+ throw new TypeError(`defineCommand: expected a schema object, got ${describeValue(schema)}.`);
27531
+ }
27532
+ if (typeof schema.action !== "string" || schema.action.length === 0) {
27533
+ throw new TypeError(
27534
+ `defineCommand: 'action' must be a non-empty string (got ${describeValue(schema.action)}).`
27535
+ );
27536
+ }
27537
+ if (!Array.isArray(schema.roles)) {
27538
+ throw new TypeError(
27539
+ `defineCommand("${schema.action}"): 'roles' must be an array of RoleSpec (got ${describeValue(schema.roles)}). Build entries with defineRole().`
27540
+ );
27541
+ }
27542
+ for (const [index, role] of schema.roles.entries()) {
27543
+ if (typeof role !== "object" || role === null || typeof role.role !== "string") {
27544
+ throw new TypeError(
27545
+ `defineCommand("${schema.action}"): roles[${index}] must be a RoleSpec with a string 'role' field (got ${describeValue(role)}). Build it with defineRole().`
27546
+ );
27547
+ }
27548
+ }
27529
27549
  return {
27530
27550
  description: schema.description || `${schema.action} command`,
27531
27551
  category: schema.category || "general",
@@ -27545,8 +27565,16 @@ function defineRole(role) {
27545
27565
  };
27546
27566
  }
27547
27567
  function getRoleSpec(schema, role) {
27568
+ if (!schema || !Array.isArray(schema.roles)) return void 0;
27548
27569
  return schema.roles.find((r) => r.role === role);
27549
27570
  }
27571
+ function describeValue(value) {
27572
+ if (value === null) return "null";
27573
+ if (Array.isArray(value)) return `an array of length ${value.length}`;
27574
+ if (typeof value === "string") return `the string ${JSON.stringify(value)}`;
27575
+ if (typeof value === "object") return "an object";
27576
+ return typeof value === "undefined" ? "undefined" : String(value);
27577
+ }
27550
27578
 
27551
27579
  // src/inference/infer-roles.ts
27552
27580
  function inferRolesFromSchema(schema, args, modifiers, target, adapter) {
@@ -30113,10 +30141,12 @@ function buildTokens$1(schema, profile, keyword, alternatives) {
30113
30141
  addRoleWithMarker(tokens, role, profile);
30114
30142
  }
30115
30143
  } else if (profile.wordOrder === "SOV") {
30144
+ const preVerb = [];
30145
+ const postVerb = [];
30116
30146
  for (const role of sortedRoles) {
30117
- addRoleWithMarker(tokens, role, profile);
30147
+ addRoleWithMarker(role.sovSlot === "postVerb" ? postVerb : preVerb, role, profile);
30118
30148
  }
30119
- tokens.push(keywordToken);
30149
+ tokens.push(...preVerb, keywordToken, ...postVerb);
30120
30150
  } else if (profile.wordOrder === "VSO") {
30121
30151
  tokens.push(keywordToken);
30122
30152
  for (const role of sortedRoles) {
@@ -30199,29 +30229,28 @@ function buildExtractionRules$1(schema, profile) {
30199
30229
  return rules;
30200
30230
  }
30201
30231
  function buildFormatString$1(schema, profile, keyword) {
30202
- const parts = [];
30203
- if (profile.wordOrder === "SVO" || profile.wordOrder === "VSO") {
30204
- parts.push(keyword);
30205
- }
30232
+ const isSOV = profile.wordOrder === "SOV";
30233
+ const preVerb = [];
30234
+ const postVerb = [];
30206
30235
  const sortedRoles = sortRolesByWordOrder$1(schema.roles, profile.wordOrder);
30207
30236
  for (const role of sortedRoles) {
30208
30237
  const marker = getMarkerForRole(role, profile);
30209
30238
  const roleName = `{${role.role}}`;
30239
+ const bucket = isSOV && role.sovSlot === "postVerb" ? postVerb : preVerb;
30210
30240
  if (marker) {
30211
30241
  const markerInfo = profile.roleMarkers?.[role.role];
30212
30242
  if (markerInfo?.position === "after") {
30213
- parts.push(`${roleName} ${marker}`);
30243
+ bucket.push(`${roleName} ${marker}`);
30214
30244
  } else {
30215
- parts.push(`${marker} ${roleName}`);
30245
+ bucket.push(`${marker} ${roleName}`);
30216
30246
  }
30217
30247
  } else {
30218
- parts.push(roleName);
30248
+ bucket.push(roleName);
30219
30249
  }
30220
30250
  }
30221
- if (profile.wordOrder === "SOV") {
30222
- parts.push(keyword);
30223
- }
30224
- return parts.join(" ");
30251
+ return (isSOV ? [...preVerb, keyword, ...postVerb] : [keyword, ...preVerb, ...postVerb]).join(
30252
+ " "
30253
+ );
30225
30254
  }
30226
30255
  function generatePatternVariants$1(schema, profile, config = defaultConfig$1) {
30227
30256
  const patterns = [];
@@ -30229,6 +30258,119 @@ function generatePatternVariants$1(schema, profile, config = defaultConfig$1) {
30229
30258
  return patterns;
30230
30259
  }
30231
30260
 
30261
+ // src/generation/renderer.ts
30262
+ function lookupKeyword(keywords, action, language) {
30263
+ return keywords[action]?.[language] ?? action;
30264
+ }
30265
+ function lookupMarker(markers, marker, language) {
30266
+ return markers[marker]?.[language] ?? marker;
30267
+ }
30268
+ function buildPhrase(...parts) {
30269
+ return parts.filter(Boolean).join(" ");
30270
+ }
30271
+ function buildTablesFromProfiles(schemas, profiles) {
30272
+ const keywords = {};
30273
+ const markers = {};
30274
+ for (const profile of profiles) {
30275
+ for (const [action, kw] of Object.entries(profile.keywords)) {
30276
+ if (!keywords[action]) keywords[action] = {};
30277
+ keywords[action][profile.code] = kw.primary;
30278
+ }
30279
+ }
30280
+ for (const schema of schemas) {
30281
+ for (const role of schema.roles) {
30282
+ if (role.markerOverride) {
30283
+ const markerKey = role.role;
30284
+ if (!markers[markerKey]) markers[markerKey] = {};
30285
+ for (const [lang, markerText] of Object.entries(role.markerOverride)) {
30286
+ markers[markerKey][lang] = markerText;
30287
+ }
30288
+ }
30289
+ }
30290
+ }
30291
+ for (const profile of profiles) {
30292
+ if (profile.roleMarkers) {
30293
+ for (const [markerKey, markerDef] of Object.entries(profile.roleMarkers)) {
30294
+ if (!markers[markerKey]) markers[markerKey] = {};
30295
+ markers[markerKey][profile.code] = markerDef.primary;
30296
+ }
30297
+ }
30298
+ }
30299
+ return { keywords, markers };
30300
+ }
30301
+ function detectWordOrders(profiles) {
30302
+ const sovLanguages = /* @__PURE__ */ new Set();
30303
+ const vsoLanguages = /* @__PURE__ */ new Set();
30304
+ for (const profile of profiles) {
30305
+ if (profile.wordOrder === "SOV") sovLanguages.add(profile.code);
30306
+ if (profile.wordOrder === "VSO") vsoLanguages.add(profile.code);
30307
+ }
30308
+ return { sovLanguages, vsoLanguages };
30309
+ }
30310
+ function createSchemaRenderer(schemas, profiles) {
30311
+ const tables = buildRenderTables(schemas, profiles);
30312
+ return {
30313
+ render(node, language) {
30314
+ return renderFromSchema(node, language, tables) ?? node.action;
30315
+ }
30316
+ };
30317
+ }
30318
+ function createDomainRenderer(config) {
30319
+ const { schemas, profiles, overrides } = config;
30320
+ let tables;
30321
+ return (node, language) => {
30322
+ const override = overrides?.[node.action];
30323
+ if (override) return override(node, language);
30324
+ tables ?? (tables = buildRenderTables(schemas, profiles));
30325
+ return renderFromSchema(node, language, tables);
30326
+ };
30327
+ }
30328
+ function buildRenderTables(schemas, profiles) {
30329
+ const { keywords, markers } = buildTablesFromProfiles(schemas, profiles);
30330
+ const { sovLanguages } = detectWordOrders(profiles);
30331
+ const markerPositions = {};
30332
+ for (const profile of profiles) {
30333
+ for (const [role, markerDef] of Object.entries(profile.roleMarkers ?? {})) {
30334
+ if (!markerDef.position) continue;
30335
+ markerPositions[role] ?? (markerPositions[role] = {});
30336
+ markerPositions[role][profile.code] = markerDef.position;
30337
+ }
30338
+ }
30339
+ const schemaMap = /* @__PURE__ */ new Map();
30340
+ for (const s of schemas) schemaMap.set(s.action, s);
30341
+ return { keywords, markers, markerPositions, sovLanguages, schemaMap };
30342
+ }
30343
+ function renderFromSchema(node, language, tables) {
30344
+ const schema = tables.schemaMap.get(node.action);
30345
+ if (!schema) return null;
30346
+ const keyword = lookupKeyword(tables.keywords, node.action, language);
30347
+ const isSOV = tables.sovLanguages.has(language);
30348
+ const ordered = sortRolesByWordOrder$1([...schema.roles], isSOV ? "SOV" : "SVO");
30349
+ const preVerb = [];
30350
+ const postVerb = [];
30351
+ for (const role of ordered) {
30352
+ let value = extractRoleValue(node, role.role);
30353
+ if (!value && role.default !== void 0) {
30354
+ value = String(extractValue(role.default));
30355
+ }
30356
+ if (!value) continue;
30357
+ if (role.quoteMultiword && /\s/.test(value) && !/^(["']).*\1$/.test(value)) {
30358
+ value = `"${value}"`;
30359
+ }
30360
+ const markerText = role.renderOverride?.[language] ?? role.markerOverride?.[language] ?? role.renderOverride?.["*"] ?? tables.markers[role.role]?.[language];
30361
+ const markerPosition = role.markerPositionOverride?.[language] ?? role.markerPosition ?? tables.markerPositions[role.role]?.[language] ?? (isSOV ? "after" : "before");
30362
+ const bucket = isSOV && role.sovSlot === "postVerb" ? postVerb : preVerb;
30363
+ if (markerText && markerPosition === "before") {
30364
+ bucket.push(markerText, value);
30365
+ } else if (markerText) {
30366
+ bucket.push(value, markerText);
30367
+ } else {
30368
+ bucket.push(value);
30369
+ }
30370
+ }
30371
+ return isSOV ? buildPhrase(...preVerb, keyword, ...postVerb) : buildPhrase(keyword, ...preVerb, ...postVerb);
30372
+ }
30373
+
30232
30374
  // src/grammar/types.ts
30233
30375
  function reorderRoles(roles, targetOrder) {
30234
30376
  const result = [];
@@ -30665,6 +30807,31 @@ var LatinExtendedIdentifierExtractor = class {
30665
30807
  return { value: input.slice(position, end), length: end - position };
30666
30808
  }
30667
30809
  };
30810
+ var SELECTOR_BODY_CHAR = /[\p{L}\p{N}_-]/u;
30811
+ var PARTICLE_SCRIPT_CHAR = /[\p{sc=Han}\p{sc=Hiragana}\p{sc=Katakana}\p{sc=Hangul}]/u;
30812
+ function isSelectorBodyChar(char) {
30813
+ return SELECTOR_BODY_CHAR.test(char) && !PARTICLE_SCRIPT_CHAR.test(char);
30814
+ }
30815
+ var CssSelectorExtractor$1 = class CssSelectorExtractor {
30816
+ constructor() {
30817
+ this.name = "css-selector";
30818
+ }
30819
+ canExtract(input, position) {
30820
+ const char = input[position];
30821
+ if (char !== "#" && char !== ".") return false;
30822
+ const next = input[position + 1];
30823
+ if (next === void 0) return false;
30824
+ return next === "_" || next === "-" || /\p{L}/u.test(next) && !PARTICLE_SCRIPT_CHAR.test(next);
30825
+ }
30826
+ extract(input, position) {
30827
+ let end = position + 1;
30828
+ while (end < input.length && isSelectorBodyChar(input[end])) {
30829
+ end++;
30830
+ }
30831
+ if (end === position + 1) return null;
30832
+ return { value: input.slice(position, end), length: end - position };
30833
+ }
30834
+ };
30668
30835
  var WhitespaceExtractor = class {
30669
30836
  constructor() {
30670
30837
  this.name = "whitespace";
@@ -31392,10 +31559,17 @@ function semanticValueToAST(value) {
31392
31559
 
31393
31560
  // src/api/create-dsl.ts
31394
31561
  var DSLRegistry = class {
31395
- constructor(config) {
31562
+ /**
31563
+ * @param partiallyTranslatedActions Actions permitted to lack a keyword in
31564
+ * some configured languages — extension commands, which may be added for a
31565
+ * subset of the DSL's languages. A built-in command missing a keyword stays
31566
+ * an error, since that is a domain-authoring mistake.
31567
+ */
31568
+ constructor(config, partiallyTranslatedActions = /* @__PURE__ */ new Set()) {
31396
31569
  this.patterns = /* @__PURE__ */ new Map();
31397
31570
  this.tokenizers = /* @__PURE__ */ new Map();
31398
31571
  this.schemas = config.schemas;
31572
+ this.partiallyTranslatedActions = partiallyTranslatedActions;
31399
31573
  for (const lang of config.languages) {
31400
31574
  this.registerLanguage(lang);
31401
31575
  }
@@ -31404,6 +31578,9 @@ var DSLRegistry = class {
31404
31578
  this.tokenizers.set(lang.code, lang.tokenizer);
31405
31579
  const patterns = [];
31406
31580
  for (const schema of this.schemas) {
31581
+ if (this.partiallyTranslatedActions.has(schema.action) && !lang.patternProfile.keywords[schema.action]) {
31582
+ continue;
31583
+ }
31407
31584
  const pattern = generatePattern$1(schema, lang.patternProfile);
31408
31585
  patterns.push(pattern);
31409
31586
  }
@@ -31420,13 +31597,25 @@ var DSLRegistry = class {
31420
31597
  }
31421
31598
  };
31422
31599
  var MultilingualDSLImpl = class {
31423
- constructor(config, registry, transformer) {
31600
+ constructor(config, registry, transformer, profileProvider) {
31424
31601
  this.registry = registry;
31425
31602
  this.matcher = new PatternMatcher$1();
31426
31603
  this.transformer = transformer;
31604
+ this.profileProvider = profileProvider;
31427
31605
  if (config.codeGenerator) {
31428
31606
  this.codeGenerator = config.codeGenerator;
31429
31607
  }
31608
+ if (config.renderer) {
31609
+ this.domainRenderer = config.renderer;
31610
+ }
31611
+ this.extensionRenderers = /* @__PURE__ */ new Map();
31612
+ for (const extension of config.extensions ?? []) {
31613
+ if (extension.render) this.extensionRenderers.set(extension.schema.action, extension.render);
31614
+ }
31615
+ this.schemaFallbackRenderer = createDomainRenderer({
31616
+ schemas: config.schemas,
31617
+ profiles: config.languages.map((l) => l.patternProfile)
31618
+ });
31430
31619
  const schemaMap = new Map(config.schemas.map((s) => [s.action, s]));
31431
31620
  this.schemaLookup = {
31432
31621
  getSchema: (action) => schemaMap.get(action)
@@ -31488,6 +31677,13 @@ var MultilingualDSLImpl = class {
31488
31677
  }
31489
31678
  translate(input, fromLanguage, toLanguage) {
31490
31679
  if (isExplicitSyntax$1(input)) return input;
31680
+ for (const language of [fromLanguage, toLanguage]) {
31681
+ if (!this.profileProvider.getProfile(language)) {
31682
+ throw new Error(
31683
+ `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'.`
31684
+ );
31685
+ }
31686
+ }
31491
31687
  return this.transformer.transform(input, fromLanguage, toLanguage);
31492
31688
  }
31493
31689
  compile(input, language) {
@@ -31516,6 +31712,18 @@ var MultilingualDSLImpl = class {
31516
31712
  };
31517
31713
  }
31518
31714
  }
31715
+ render(node, language) {
31716
+ const extensionRenderer = this.extensionRenderers.get(node.action);
31717
+ if (extensionRenderer) {
31718
+ const rendered = extensionRenderer(node, language);
31719
+ if (rendered != null) return rendered;
31720
+ }
31721
+ if (this.domainRenderer) {
31722
+ const rendered = this.domainRenderer(node, language);
31723
+ if (rendered != null) return rendered;
31724
+ }
31725
+ return this.schemaFallbackRenderer(node, language);
31726
+ }
31519
31727
  getSupportedLanguages() {
31520
31728
  return this.registry.getSupportedLanguages();
31521
31729
  }
@@ -31540,15 +31748,80 @@ function createDefaultProfileProvider(config) {
31540
31748
  }
31541
31749
  return new InMemoryProfileProvider(profiles);
31542
31750
  }
31751
+ function applyExtensions(config) {
31752
+ const extensions = config.extensions;
31753
+ if (!extensions || extensions.length === 0) return config;
31754
+ const configuredLanguages = new Set(config.languages.map((l) => l.code));
31755
+ const actions = new Set(config.schemas.map((s) => s.action));
31756
+ for (const extension of extensions) {
31757
+ const { action } = extension.schema;
31758
+ if (actions.has(action)) {
31759
+ throw new Error(
31760
+ `Extension command "${action}" collides with a command this DSL already defines. Pick a different action name.`
31761
+ );
31762
+ }
31763
+ actions.add(action);
31764
+ for (const code of Object.keys(extension.vocabulary)) {
31765
+ if (!configuredLanguages.has(code)) {
31766
+ throw new Error(
31767
+ `Extension command "${action}" supplies vocabulary for language "${code}", which is not configured on this DSL. Configured languages: ${[...configuredLanguages].join(", ")}.`
31768
+ );
31769
+ }
31770
+ }
31771
+ }
31772
+ const schemas = [...config.schemas, ...extensions.map((e) => e.schema)];
31773
+ const languages = config.languages.map((lang) => {
31774
+ const keywords = { ...lang.patternProfile.keywords };
31775
+ let roleMarkers = lang.patternProfile.roleMarkers;
31776
+ for (const extension of extensions) {
31777
+ const vocabulary = extension.vocabulary[lang.code];
31778
+ if (!vocabulary) continue;
31779
+ keywords[extension.schema.action] = { ...vocabulary.keyword };
31780
+ if (vocabulary.roleMarkers) {
31781
+ roleMarkers = { ...roleMarkers, ...vocabulary.roleMarkers };
31782
+ }
31783
+ }
31784
+ return {
31785
+ ...lang,
31786
+ patternProfile: {
31787
+ ...lang.patternProfile,
31788
+ keywords,
31789
+ ...roleMarkers !== void 0 && { roleMarkers }
31790
+ }
31791
+ };
31792
+ });
31793
+ const extensionGenerators = new Map(
31794
+ extensions.filter((e) => e.generate).map((e) => [e.schema.action, e.generate])
31795
+ );
31796
+ const baseGenerator = config.codeGenerator;
31797
+ const codeGenerator = extensionGenerators.size > 0 ? {
31798
+ generate(node) {
31799
+ const generate = extensionGenerators.get(node.action);
31800
+ if (generate) return generate(node);
31801
+ if (!baseGenerator) {
31802
+ throw new Error(`No code generator for action "${node.action}"`);
31803
+ }
31804
+ return baseGenerator.generate(node);
31805
+ }
31806
+ } : baseGenerator;
31807
+ return {
31808
+ ...config,
31809
+ schemas,
31810
+ languages,
31811
+ ...codeGenerator !== void 0 && { codeGenerator }
31812
+ };
31813
+ }
31543
31814
  function createMultilingualDSL(config) {
31544
- const dictionary = config.dictionary ?? createDefaultDictionary(config);
31545
- const profileProvider = config.profileProvider ?? createDefaultProfileProvider(config);
31815
+ const effectiveConfig = applyExtensions(config);
31816
+ const extensionActions = new Set((config.extensions ?? []).map((e) => e.schema.action));
31817
+ const dictionary = effectiveConfig.dictionary ?? createDefaultDictionary(effectiveConfig);
31818
+ const profileProvider = effectiveConfig.profileProvider ?? createDefaultProfileProvider(effectiveConfig);
31546
31819
  const transformer = new GrammarTransformer({
31547
31820
  dictionary,
31548
31821
  profileProvider
31549
31822
  });
31550
- const registry = new DSLRegistry(config);
31551
- return new MultilingualDSLImpl(config, registry, transformer);
31823
+ const registry = new DSLRegistry(effectiveConfig, extensionActions);
31824
+ return new MultilingualDSLImpl(effectiveConfig, registry, transformer, profileProvider);
31552
31825
  }
31553
31826
 
31554
31827
  // src/prompts/prompt-generator.ts
@@ -32519,6 +32792,11 @@ var DomainRegistry = class {
32519
32792
  rendered = typeof renderer === "function" ? renderer(node, to) : renderer.render(node, to);
32520
32793
  } catch {
32521
32794
  }
32795
+ } else if (dsl.render) {
32796
+ try {
32797
+ rendered = dsl.render(node, to);
32798
+ } catch {
32799
+ }
32522
32800
  }
32523
32801
  const roles = {};
32524
32802
  for (const [key, value] of node.roles) {
@@ -33073,96 +33351,6 @@ async function registryToAOTBackends(registry) {
33073
33351
  return backends;
33074
33352
  }
33075
33353
 
33076
- // src/generation/renderer.ts
33077
- function lookupKeyword(keywords, action, language) {
33078
- return keywords[action]?.[language] ?? action;
33079
- }
33080
- function lookupMarker(markers, marker, language) {
33081
- return markers[marker]?.[language] ?? marker;
33082
- }
33083
- function buildPhrase(...parts) {
33084
- return parts.filter(Boolean).join(" ");
33085
- }
33086
- function buildTablesFromProfiles(schemas, profiles) {
33087
- const keywords = {};
33088
- const markers = {};
33089
- for (const profile of profiles) {
33090
- for (const [action, kw] of Object.entries(profile.keywords)) {
33091
- if (!keywords[action]) keywords[action] = {};
33092
- keywords[action][profile.code] = kw.primary;
33093
- }
33094
- }
33095
- for (const schema of schemas) {
33096
- for (const role of schema.roles) {
33097
- if (role.markerOverride) {
33098
- const markerKey = role.role;
33099
- if (!markers[markerKey]) markers[markerKey] = {};
33100
- for (const [lang, markerText] of Object.entries(role.markerOverride)) {
33101
- markers[markerKey][lang] = markerText;
33102
- }
33103
- }
33104
- }
33105
- }
33106
- for (const profile of profiles) {
33107
- if (profile.roleMarkers) {
33108
- for (const [markerKey, markerDef] of Object.entries(profile.roleMarkers)) {
33109
- if (!markers[markerKey]) markers[markerKey] = {};
33110
- markers[markerKey][profile.code] = markerDef.primary;
33111
- }
33112
- }
33113
- }
33114
- return { keywords, markers };
33115
- }
33116
- function detectWordOrders(profiles) {
33117
- const sovLanguages = /* @__PURE__ */ new Set();
33118
- const vsoLanguages = /* @__PURE__ */ new Set();
33119
- for (const profile of profiles) {
33120
- if (profile.wordOrder === "SOV") sovLanguages.add(profile.code);
33121
- if (profile.wordOrder === "VSO") vsoLanguages.add(profile.code);
33122
- }
33123
- return { sovLanguages, vsoLanguages };
33124
- }
33125
- function createSchemaRenderer(schemas, profiles) {
33126
- const { keywords, markers } = buildTablesFromProfiles(schemas, profiles);
33127
- const { sovLanguages } = detectWordOrders(profiles);
33128
- const schemaMap = /* @__PURE__ */ new Map();
33129
- for (const s of schemas) schemaMap.set(s.action, s);
33130
- return {
33131
- render(node, language) {
33132
- const schema = schemaMap.get(node.action);
33133
- if (!schema) return node.action;
33134
- const keyword = lookupKeyword(keywords, node.action, language);
33135
- const isSOV = sovLanguages.has(language);
33136
- const roleParts = [];
33137
- for (const role of schema.roles) {
33138
- const value = extractRoleValue(node, role.role);
33139
- if (!value && !role.required) continue;
33140
- const markerText = role.markerOverride?.[language] ?? markers[role.role]?.[language] ?? void 0;
33141
- roleParts.push({
33142
- ...markerText != null && { marker: markerText },
33143
- value: value || "",
33144
- role
33145
- });
33146
- }
33147
- const parts = [];
33148
- if (isSOV) {
33149
- for (const rp of roleParts) {
33150
- if (rp.value) parts.push(rp.value);
33151
- if (rp.marker) parts.push(rp.marker);
33152
- }
33153
- parts.push(keyword);
33154
- } else {
33155
- parts.push(keyword);
33156
- for (const rp of roleParts) {
33157
- if (rp.marker) parts.push(rp.marker);
33158
- if (rp.value) parts.push(rp.value);
33159
- }
33160
- }
33161
- return buildPhrase(...parts);
33162
- }
33163
- };
33164
- }
33165
-
33166
33354
  // src/core/tokenization/token-utils.ts
33167
33355
  var TokenStreamImpl = class {
33168
33356
  constructor(tokens, language) {
@@ -33269,6 +33457,9 @@ function isQuote(char) {
33269
33457
  function isDigit(char) {
33270
33458
  return /\d/.test(char);
33271
33459
  }
33460
+ function stripOptionalDiacritics(word) {
33461
+ return word.replace(/[ً-ْٰ]/g, "");
33462
+ }
33272
33463
  function isAsciiLetter(char) {
33273
33464
  return /[a-zA-Z]/.test(char);
33274
33465
  }
@@ -34005,7 +34196,7 @@ var _BaseTokenizer = class _BaseTokenizer {
34005
34196
  * @returns Word without diacritics
34006
34197
  */
34007
34198
  removeDiacritics(word) {
34008
- return word.replace(/[\u064B-\u0652\u0670]/g, "");
34199
+ return stripOptionalDiacritics(word);
34009
34200
  }
34010
34201
  /**
34011
34202
  * Try to match a keyword from profile at the current position.
@@ -34096,24 +34287,40 @@ var _BaseTokenizer = class _BaseTokenizer {
34096
34287
  });
34097
34288
  }
34098
34289
  /**
34099
- * Look up a keyword by native word (case-insensitive).
34290
+ * Look up a keyword by native word (case-insensitive, diacritic-insensitive).
34100
34291
  * O(1) lookup using the keyword map.
34101
34292
  *
34293
+ * The map is INDEXED both with and without diacritics (see
34294
+ * `initializeKeywordsFromProfile`), so a stripped QUERY is the other half of
34295
+ * that: it lets a surface form carrying harakat the profile does not happen to
34296
+ * spell still find its entry. Only consulted after the exact lookup misses, so
34297
+ * every previously-matching word resolves byte-identically.
34298
+ *
34299
+ * Half-implementing this — indexing stripped but querying exact — is what made
34300
+ * diacritized `بَدِّل` (toggle) tokenize as `kind=particle normalized=with`:
34301
+ * `isKeyword` returned false, so the guard in `ArabicProcliticExtractor` that
34302
+ * exists to prevent exactly that handed the word on, and the single-char `ب`
34303
+ * bi- proclitic claimed it. A wrong CONCEPT, not a failed parse.
34304
+ *
34102
34305
  * @param native - Native word to look up
34103
34306
  * @returns KeywordEntry if found, undefined otherwise
34104
34307
  */
34105
34308
  lookupKeyword(native) {
34106
- return this.profileKeywordMap.get(native.toLowerCase());
34309
+ const exact = this.profileKeywordMap.get(native.toLowerCase());
34310
+ if (exact) return exact;
34311
+ const stripped = this.removeDiacritics(native);
34312
+ if (stripped === native) return void 0;
34313
+ return this.profileKeywordMap.get(stripped.toLowerCase());
34107
34314
  }
34108
34315
  /**
34109
- * Check if a word is a known keyword (case-insensitive).
34110
- * O(1) lookup using the keyword map.
34316
+ * Check if a word is a known keyword (case-insensitive, diacritic-insensitive).
34317
+ * O(1) lookup using the keyword map. See {@link lookupKeyword}.
34111
34318
  *
34112
34319
  * @param native - Native word to check
34113
34320
  * @returns true if the word is a keyword
34114
34321
  */
34115
34322
  isKeyword(native) {
34116
- return this.profileKeywordMap.has(native.toLowerCase());
34323
+ return this.lookupKeyword(native) !== void 0;
34117
34324
  }
34118
34325
  /**
34119
34326
  * Set the morphological normalizer for this tokenizer.
@@ -35092,6 +35299,7 @@ var index$2 = /*#__PURE__*/Object.freeze({
35092
35299
  BaseMorphologicalNormalizer: BaseMorphologicalNormalizer,
35093
35300
  BaseTokenizer: BaseTokenizer,
35094
35301
  CrossDomainDispatcher: CrossDomainDispatcher,
35302
+ CssSelectorExtractor: CssSelectorExtractor$1,
35095
35303
  DEFAULT_OPERATORS: DEFAULT_OPERATORS$1,
35096
35304
  DEFAULT_PUNCTUATION: DEFAULT_PUNCTUATION$1,
35097
35305
  DEFAULT_REFERENCES: DEFAULT_REFERENCES,
@@ -35128,6 +35336,7 @@ var index$2 = /*#__PURE__*/Object.freeze({
35128
35336
  createCompoundNode: createCompoundNode$1,
35129
35337
  createConditionalNode: createConditionalNode$1,
35130
35338
  createDiagnosticCollector: createDiagnosticCollector,
35339
+ createDomainRenderer: createDomainRenderer,
35131
35340
  createEventHandlerNode: createEventHandlerNode,
35132
35341
  createExpression: createExpression,
35133
35342
  createFlag: createFlag,
@@ -35219,6 +35428,8 @@ var index$2 = /*#__PURE__*/Object.freeze({
35219
35428
  semanticNodeToJSON: semanticNodeToJSON,
35220
35429
  semanticNodeToRuntimeAST: semanticNodeToRuntimeAST$1,
35221
35430
  semanticValueToAST: semanticValueToAST,
35431
+ sortRolesByWordOrder: sortRolesByWordOrder$1,
35432
+ stripOptionalDiacritics: stripOptionalDiacritics,
35222
35433
  synthesizeFromSchemas: synthesizeFromSchemas,
35223
35434
  toEnvelopeJSON: toEnvelopeJSON$1,
35224
35435
  toJSONL: toJSONL,
@@ -35581,9 +35792,12 @@ var init_arabic = __esm({
35581
35792
  behavior: { primary: "\u0633\u0644\u0648\u0643", normalized: "behavior" },
35582
35793
  install: { primary: "\u062A\u062B\u0628\u064A\u062A", alternatives: ["\u062B\u0628\u0651\u062A"], normalized: "install" },
35583
35794
  // `قِس` is the imperative with the kasra diacritic; the i18n dict (and real
35584
- // Arabic prose) emits it undiacritized as `قس`, so list both — otherwise the
35585
- // generated `قس width`/`قس x` (behavior-draggable/resizable) parse to null and
35586
- // the whole `measure` command drops from the event-handler body (lossy).
35795
+ // Arabic prose) emits it undiacritized as `قس`. BOTH stay listed, and not
35796
+ // for the tokenizer's sake — keyword lookup is diacritic-insensitive now, so
35797
+ // either spelling resolves. It is the vocab gate's V1 check, which compares
35798
+ // the profile against the i18n DICTIONARY as strings: the dictionary says
35799
+ // `قس`, so dropping it here fails V1 (verified). Diacritic-insensitivity
35800
+ // would have to reach that comparison too before this pair can collapse.
35587
35801
  measure: { primary: "\u0642\u064A\u0627\u0633", alternatives: ["\u0642\u0650\u0633", "\u0642\u0633"], normalized: "measure" },
35588
35802
  beep: { primary: "\u0635\u0641\u0651\u0631", normalized: "beep" },
35589
35803
  break: { primary: "\u062A\u0648\u0642\u0641", normalized: "break" },
@@ -36267,6 +36481,15 @@ var init_spanish = __esm({
36267
36481
  patient: { primary: "", position: "before" },
36268
36482
  style: { primary: "con", position: "before" }
36269
36483
  },
36484
+ // Imperative command forms are accepted on INPUT only — `primary` stays the
36485
+ // dictionary form, so rendering is unchanged (see LanguageProfile.defaultVerbForm:
36486
+ // infinitive is the industry standard for UI localization). Hyperscript is a
36487
+ // command language, though, and a native speaker giving a command writes the
36488
+ // imperative, so the parser should read it.
36489
+ //
36490
+ // Only the IRREGULARS are listed. The regular ones reach their keyword through
36491
+ // the morphological normalizer's stem (see spanish-keyword.ts and siblings),
36492
+ // which also covers conjugations nobody enumerated here.
36270
36493
  keywords: {
36271
36494
  // Class/Attribute operations
36272
36495
  toggle: { primary: "alternar", alternatives: ["conmutar", "toggle"], normalized: "toggle" },
@@ -36286,19 +36509,23 @@ var init_spanish = __esm({
36286
36509
  swap: { primary: "intercambiar", alternatives: ["permutar"], normalized: "swap" },
36287
36510
  morph: { primary: "transformar", alternatives: ["convertir"], normalized: "morph" },
36288
36511
  // Variable operations
36289
- set: { primary: "establecer", alternatives: ["fijar", "definir"], normalized: "set" },
36290
- get: { primary: "obtener", alternatives: ["conseguir"], normalized: "get" },
36512
+ set: {
36513
+ primary: "establecer",
36514
+ alternatives: ["fijar", "definir", "establece"],
36515
+ normalized: "set"
36516
+ },
36517
+ get: { primary: "obtener", alternatives: ["conseguir", "obt\xE9n"], normalized: "get" },
36291
36518
  increment: { primary: "incrementar", alternatives: ["aumentar"], normalized: "increment" },
36292
36519
  decrement: { primary: "decrementar", alternatives: ["disminuir"], normalized: "decrement" },
36293
36520
  log: { primary: "registrar", alternatives: ["imprimir"], normalized: "log" },
36294
36521
  // Visibility
36295
- show: { primary: "mostrar", alternatives: ["ense\xF1ar"], normalized: "show" },
36522
+ show: { primary: "mostrar", alternatives: ["ense\xF1ar", "muestra"], normalized: "show" },
36296
36523
  hide: { primary: "ocultar", alternatives: ["esconder"], normalized: "hide" },
36297
36524
  transition: { primary: "transici\xF3n", alternatives: ["animar"], normalized: "transition" },
36298
36525
  // Events
36299
36526
  on: { primary: "en", alternatives: ["al"], normalized: "on" },
36300
36527
  trigger: { primary: "disparar", alternatives: ["activar"], normalized: "trigger" },
36301
- send: { primary: "enviar", normalized: "send" },
36528
+ send: { primary: "enviar", alternatives: ["env\xEDa"], normalized: "send" },
36302
36529
  // DOM focus
36303
36530
  focus: { primary: "enfocar", alternatives: ["enfoque"], normalized: "focus" },
36304
36531
  blur: { primary: "desenfocar", alternatives: ["desenfoque"], normalized: "blur" },
@@ -36335,7 +36562,7 @@ var init_spanish = __esm({
36335
36562
  mousedown: { primary: "rat\xF3nabajo", normalized: "mousedown" },
36336
36563
  mouseup: { primary: "rat\xF3narriba", normalized: "mouseup" },
36337
36564
  // Navigation
36338
- go: { primary: "ir", alternatives: ["navegar"], normalized: "go" },
36565
+ go: { primary: "ir", alternatives: ["navegar", "ve"], normalized: "go" },
36339
36566
  push: { primary: "empujar", alternatives: ["push"], normalized: "push" },
36340
36567
  replace: { primary: "reemplazar", alternatives: ["sustituir"], normalized: "replace" },
36341
36568
  process: { primary: "procesar", normalized: "process" },
@@ -36524,11 +36751,24 @@ var init_french = __esm({
36524
36751
  patient: { primary: "", position: "before" },
36525
36752
  style: { primary: "avec", position: "before" }
36526
36753
  },
36754
+ // Imperative command forms are accepted on INPUT only — `primary` stays the
36755
+ // dictionary form, so rendering is unchanged (see LanguageProfile.defaultVerbForm:
36756
+ // infinitive is the industry standard for UI localization). Hyperscript is a
36757
+ // command language, though, and a native speaker giving a command writes the
36758
+ // imperative, so the parser should read it.
36759
+ //
36760
+ // Only the IRREGULARS are listed. The regular ones reach their keyword through
36761
+ // the morphological normalizer's stem (see spanish-keyword.ts and siblings),
36762
+ // which also covers conjugations nobody enumerated here.
36527
36763
  keywords: {
36528
36764
  toggle: { primary: "basculer", alternatives: ["alterner"], normalized: "toggle" },
36529
36765
  add: { primary: "ajouter", normalized: "add" },
36530
- remove: { primary: "supprimer", alternatives: ["enlever", "retirer"], normalized: "remove" },
36531
- put: { primary: "mettre", alternatives: ["placer"], normalized: "put" },
36766
+ remove: {
36767
+ primary: "supprimer",
36768
+ alternatives: ["enlever", "retirer", "retire"],
36769
+ normalized: "remove"
36770
+ },
36771
+ put: { primary: "mettre", alternatives: ["placer", "mets"], normalized: "put" },
36532
36772
  append: { primary: "annexer", normalized: "append" },
36533
36773
  prepend: { primary: "pr\xE9fixer", normalized: "prepend" },
36534
36774
  take: { primary: "prendre", normalized: "take" },
@@ -36537,16 +36777,16 @@ var init_french = __esm({
36537
36777
  swap: { primary: "\xE9changer", alternatives: ["permuter"], normalized: "swap" },
36538
36778
  morph: { primary: "transformer", alternatives: ["m\xE9tamorphoser"], normalized: "morph" },
36539
36779
  set: { primary: "d\xE9finir", alternatives: ["\xE9tablir"], normalized: "set" },
36540
- get: { primary: "obtenir", normalized: "get" },
36780
+ get: { primary: "obtenir", alternatives: ["obtiens"], normalized: "get" },
36541
36781
  increment: { primary: "incr\xE9menter", alternatives: ["augmenter"], normalized: "increment" },
36542
36782
  decrement: { primary: "d\xE9cr\xE9menter", alternatives: ["diminuer"], normalized: "decrement" },
36543
36783
  log: { primary: "enregistrer", alternatives: ["journaliser"], normalized: "log" },
36544
- show: { primary: "montrer", alternatives: ["afficher"], normalized: "show" },
36784
+ show: { primary: "montrer", alternatives: ["afficher", "montre"], normalized: "show" },
36545
36785
  hide: { primary: "cacher", alternatives: ["masquer"], normalized: "hide" },
36546
36786
  transition: { primary: "transition", alternatives: ["animer"], normalized: "transition" },
36547
36787
  on: { primary: "sur", alternatives: ["lors"], normalized: "on" },
36548
36788
  trigger: { primary: "d\xE9clencher", normalized: "trigger" },
36549
- send: { primary: "envoyer", normalized: "send" },
36789
+ send: { primary: "envoyer", alternatives: ["envoie"], normalized: "send" },
36550
36790
  focus: { primary: "focaliser", alternatives: ["concentrer"], normalized: "focus" },
36551
36791
  blur: { primary: "d\xE9focaliser", normalized: "blur" },
36552
36792
  // Phase 1 (v0.9.90): DOM / form state / debug
@@ -36560,13 +36800,13 @@ var init_french = __esm({
36560
36800
  clear: { primary: "effacer", normalized: "clear" },
36561
36801
  reset: { primary: "r\xE9initialiser", alternatives: ["reinitialiser"], normalized: "reset" },
36562
36802
  breakpoint: { primary: "point-arr\xEAt", alternatives: ["point-arret"], normalized: "breakpoint" },
36563
- go: { primary: "aller", alternatives: ["naviguer"], normalized: "go" },
36803
+ go: { primary: "aller", alternatives: ["naviguer", "va"], normalized: "go" },
36564
36804
  scroll: { primary: "d\xE9filer", alternatives: ["faire-d\xE9filer"], normalized: "scroll" },
36565
36805
  push: { primary: "pousser", normalized: "push" },
36566
36806
  replace: { primary: "remplacer", normalized: "replace" },
36567
36807
  process: { primary: "traiter", normalized: "process" },
36568
36808
  wait: { primary: "attendre", normalized: "wait" },
36569
- fetch: { primary: "chercher", alternatives: ["r\xE9cup\xE9rer"], normalized: "fetch" },
36809
+ fetch: { primary: "chercher", alternatives: ["r\xE9cup\xE9rer", "r\xE9cup\xE8re"], normalized: "fetch" },
36570
36810
  settle: { primary: "stabiliser", normalized: "settle" },
36571
36811
  if: { primary: "si", normalized: "if" },
36572
36812
  unless: { primary: "saufsi", normalized: "unless" },
@@ -37937,16 +38177,25 @@ var init_korean = __esm({
37937
38177
  event: { primary: "\uC744", alternatives: ["\uB97C"], position: "after" }
37938
38178
  // Event as object marker
37939
38179
  },
38180
+ // Imperative command forms are accepted on INPUT only — `primary` stays the
38181
+ // dictionary form, so rendering is unchanged (see LanguageProfile.defaultVerbForm:
38182
+ // infinitive is the industry standard for UI localization). Hyperscript is a
38183
+ // command language, though, and a native speaker giving a command writes the
38184
+ // imperative, so the parser should read it.
38185
+ //
38186
+ // Only the IRREGULARS are listed. The regular ones reach their keyword through
38187
+ // the morphological normalizer's stem (see spanish-keyword.ts and siblings),
38188
+ // which also covers conjugations nobody enumerated here.
37940
38189
  keywords: {
37941
38190
  // Class/Attribute operations
37942
38191
  toggle: { primary: "\uD1A0\uAE00", normalized: "toggle" },
37943
38192
  add: { primary: "\uCD94\uAC00", normalized: "add" },
37944
38193
  remove: { primary: "\uC81C\uAC70", alternatives: ["\uC0AD\uC81C"], normalized: "remove" },
37945
38194
  // Content operations
37946
- put: { primary: "\uB123\uB2E4", alternatives: ["\uB123\uAE30", "\uB193\uAE30"], normalized: "put" },
38195
+ put: { primary: "\uB123\uB2E4", alternatives: ["\uB123\uAE30", "\uB193\uAE30", "\uB123\uC73C\uC138\uC694"], normalized: "put" },
37947
38196
  append: { primary: "\uB367\uBD99\uC774\uB2E4", alternatives: ["\uB05D\uC5D0\uCD94\uAC00"], normalized: "append" },
37948
38197
  prepend: { primary: "\uC55E\uC5D0\uCD94\uAC00", alternatives: ["\uC120\uB450\uCD94\uAC00"], normalized: "prepend" },
37949
- take: { primary: "\uAC00\uC838\uC624\uB2E4", normalized: "take" },
38198
+ take: { primary: "\uAC00\uC838\uC624\uB2E4", alternatives: ["\uAC00\uC838\uC624\uC138\uC694"], normalized: "take" },
37950
38199
  make: { primary: "\uB9CC\uB4E4\uB2E4", normalized: "make" },
37951
38200
  clone: { primary: "\uBCF5\uC81C", normalized: "clone" },
37952
38201
  // 복제=duplicate/clone, 복사=copy
@@ -37954,13 +38203,13 @@ var init_korean = __esm({
37954
38203
  morph: { primary: "\uBCC0\uD615", alternatives: ["\uBCC0\uD658"], normalized: "morph" },
37955
38204
  // Variable operations
37956
38205
  set: { primary: "\uC124\uC815", normalized: "set" },
37957
- get: { primary: "\uC5BB\uB2E4", normalized: "get" },
38206
+ get: { primary: "\uC5BB\uB2E4", alternatives: ["\uC5BB\uC73C\uC138\uC694"], normalized: "get" },
37958
38207
  increment: { primary: "\uC99D\uAC00", normalized: "increment" },
37959
38208
  decrement: { primary: "\uAC10\uC18C", normalized: "decrement" },
37960
38209
  log: { primary: "\uB85C\uADF8", normalized: "log" },
37961
38210
  // Visibility
37962
- show: { primary: "\uBCF4\uC774\uB2E4", alternatives: ["\uD45C\uC2DC", "\uBCF4\uC774\uAE30"], normalized: "show" },
37963
- hide: { primary: "\uC228\uAE30\uB2E4", alternatives: ["\uC228\uAE30\uAE30"], normalized: "hide" },
38211
+ show: { primary: "\uBCF4\uC774\uB2E4", alternatives: ["\uD45C\uC2DC", "\uBCF4\uC774\uAE30", "\uBCF4\uC774\uC138\uC694"], normalized: "show" },
38212
+ hide: { primary: "\uC228\uAE30\uB2E4", alternatives: ["\uC228\uAE30\uAE30", "\uC228\uAE30\uC138\uC694"], normalized: "hide" },
37964
38213
  // primary is the loanword 트랜지션; 전환 ("switch/transition") is the form the
37965
38214
  // i18n transformer emits — registered as an alternative (passthrough-alignment).
37966
38215
  // toggle uses 토글, so 전환 carries no collision.
@@ -37968,7 +38217,7 @@ var init_korean = __esm({
37968
38217
  // Events
37969
38218
  on: { primary: "\uC5D0", alternatives: ["\uC2DC", "\uD560 \uB54C"], normalized: "on" },
37970
38219
  trigger: { primary: "\uD2B8\uB9AC\uAC70", normalized: "trigger" },
37971
- send: { primary: "\uBCF4\uB0B4\uB2E4", normalized: "send" },
38220
+ send: { primary: "\uBCF4\uB0B4\uB2E4", alternatives: ["\uBCF4\uB0B4\uC138\uC694"], normalized: "send" },
37972
38221
  // DOM focus
37973
38222
  focus: { primary: "\uD3EC\uCEE4\uC2A4", normalized: "focus" },
37974
38223
  blur: { primary: "\uBE14\uB7EC", normalized: "blur" },
@@ -38750,25 +38999,38 @@ var init_portuguese = __esm({
38750
38999
  patient: { primary: "", position: "before" },
38751
39000
  style: { primary: "com", position: "before" }
38752
39001
  },
39002
+ // Imperative command forms are accepted on INPUT only — `primary` stays the
39003
+ // dictionary form, so rendering is unchanged (see LanguageProfile.defaultVerbForm:
39004
+ // infinitive is the industry standard for UI localization). Hyperscript is a
39005
+ // command language, though, and a native speaker giving a command writes the
39006
+ // imperative, so the parser should read it.
39007
+ //
39008
+ // Only the IRREGULARS are listed. The regular ones reach their keyword through
39009
+ // the morphological normalizer's stem (see spanish-keyword.ts and siblings),
39010
+ // which also covers conjugations nobody enumerated here.
38753
39011
  keywords: {
38754
39012
  toggle: { primary: "alternar", alternatives: [], normalized: "toggle" },
38755
39013
  add: { primary: "adicionar", alternatives: ["acrescentar"], normalized: "add" },
38756
- remove: { primary: "remover", alternatives: ["eliminar", "apagar"], normalized: "remove" },
38757
- put: { primary: "colocar", alternatives: ["p\xF4r", "por"], normalized: "put" },
39014
+ remove: {
39015
+ primary: "remover",
39016
+ alternatives: ["eliminar", "apagar", "remova"],
39017
+ normalized: "remove"
39018
+ },
39019
+ put: { primary: "colocar", alternatives: ["p\xF4r", "por", "coloque"], normalized: "put" },
38758
39020
  append: { primary: "anexar", normalized: "append" },
38759
39021
  prepend: { primary: "preceder", normalized: "prepend" },
38760
- take: { primary: "pegar", normalized: "take" },
39022
+ take: { primary: "pegar", alternatives: ["pegue"], normalized: "take" },
38761
39023
  make: { primary: "fazer", alternatives: ["criar"], normalized: "make" },
38762
39024
  clone: { primary: "clonar", alternatives: [], normalized: "clone" },
38763
39025
  swap: { primary: "trocar", alternatives: ["substituir"], normalized: "swap" },
38764
39026
  morph: { primary: "transformar", alternatives: ["converter"], normalized: "morph" },
38765
- set: { primary: "definir", alternatives: ["configurar"], normalized: "set" },
38766
- get: { primary: "obter", normalized: "get" },
39027
+ set: { primary: "definir", alternatives: ["configurar", "defina"], normalized: "set" },
39028
+ get: { primary: "obter", alternatives: ["obtenha"], normalized: "get" },
38767
39029
  increment: { primary: "incrementar", alternatives: ["aumentar"], normalized: "increment" },
38768
39030
  decrement: { primary: "decrementar", alternatives: ["diminuir"], normalized: "decrement" },
38769
39031
  log: { primary: "registrar", alternatives: ["imprimir"], normalized: "log" },
38770
39032
  show: { primary: "mostrar", alternatives: ["exibir"], normalized: "show" },
38771
- hide: { primary: "ocultar", alternatives: ["esconder"], normalized: "hide" },
39033
+ hide: { primary: "ocultar", alternatives: ["esconder", "esconda"], normalized: "hide" },
38772
39034
  transition: { primary: "transi\xE7\xE3o", alternatives: ["animar"], normalized: "transition" },
38773
39035
  on: { primary: "em", alternatives: ["ao"], normalized: "on" },
38774
39036
  trigger: { primary: "disparar", alternatives: ["ativar"], normalized: "trigger" },
@@ -38787,13 +39049,13 @@ var init_portuguese = __esm({
38787
39049
  alternatives: ["ponto-interrupcao"],
38788
39050
  normalized: "breakpoint"
38789
39051
  },
38790
- go: { primary: "ir", alternatives: ["navegar"], normalized: "go" },
39052
+ go: { primary: "ir", alternatives: ["navegar", "v\xE1"], normalized: "go" },
38791
39053
  scroll: { primary: "rolar", alternatives: ["scroll"], normalized: "scroll" },
38792
39054
  push: { primary: "empurrar", alternatives: ["push"], normalized: "push" },
38793
39055
  replace: { primary: "repor", alternatives: ["recolocar"], normalized: "replace" },
38794
39056
  process: { primary: "processar", normalized: "process" },
38795
39057
  wait: { primary: "esperar", alternatives: ["aguardar"], normalized: "wait" },
38796
- fetch: { primary: "buscar", normalized: "fetch" },
39058
+ fetch: { primary: "buscar", alternatives: ["busque"], normalized: "fetch" },
38797
39059
  settle: { primary: "estabilizar", normalized: "settle" },
38798
39060
  if: { primary: "se", normalized: "if" },
38799
39061
  // salvo — single token ('salvo se' = unless). a_menos kept as an
@@ -41867,8 +42129,53 @@ var init_command_schemas = __esm({
41867
42129
  default: { type: "reference", value: "me" },
41868
42130
  svoPosition: 2,
41869
42131
  sovPosition: 1,
41870
- markerOverride: { en: "to" }
41871
- // "add .class to #element"
42132
+ // `add` is directional, but every profile's `destination` marker is
42133
+ // LOCATIVE (en on, es en, ar على, zh 在, fr sur, de auf, pt em) because
42134
+ // it also serves `toggle`/`show`. Without a per-language override the
42135
+ // rendered text said "add .class ON #element" in every language but
42136
+ // English — the gap lokascript-learn corrects with 6 of its 16 override
42137
+ // entries. ja に / ko 에 / tr e are already directional, so they keep
42138
+ // the profile default.
42139
+ //
42140
+ // Tier B (2.9): he/id/it/sw were the remaining locatives that this
42141
+ // language actually distinguishes.
42142
+ // he — `על` is "ON"; Hebrew adds with the allative `אל` (`ל` is a bound
42143
+ // prefix, so it cannot stand as a separate marker token).
42144
+ // id — `pada` is "at/on"; `ke` is the directional, and it is what the
42145
+ // i18n corpus already renders for every id destination.
42146
+ // it — `in` is locative; Italian adds with `a` (`aggiungere a`).
42147
+ // sw — `kwenye` is not merely locative, it is sw's EVENT keyword
42148
+ // (`on: 'kwenye'` in the dictionary), so reusing it as a
42149
+ // destination marker collides. `kwa` is the corpus rendering.
42150
+ // hi `में` / ru+uk `в` / th `ใน` / vi `vào` are already the right
42151
+ // container-directional for "add to", and keep the profile default.
42152
+ markerOverride: {
42153
+ en: "to",
42154
+ es: "a",
42155
+ ar: "\u0625\u0644\u0649",
42156
+ zh: "\u5230",
42157
+ fr: "\xE0",
42158
+ de: "zu",
42159
+ pt: "a",
42160
+ he: "\u05D0\u05DC",
42161
+ id: "ke",
42162
+ it: "a",
42163
+ sw: "kwa"
42164
+ },
42165
+ // Each language's previous primary marker (and its alternates) still
42166
+ // parses, so source written against ≤2.8 keeps working.
42167
+ markerLegacy: {
42168
+ es: ["en", "sobre", "hacia"],
42169
+ ar: ["\u0639\u0644\u0649", "\u0641\u064A", "\u0628"],
42170
+ zh: ["\u5728", "\u4E8E"],
42171
+ fr: ["sur", "dans"],
42172
+ de: ["auf", "in"],
42173
+ pt: ["em", "para"],
42174
+ he: ["\u05E2\u05DC", "\u05D1", "\u05DC"],
42175
+ id: ["pada", "di"],
42176
+ it: ["in", "su"],
42177
+ sw: ["kwenye"]
42178
+ }
41872
42179
  }
41873
42180
  ],
41874
42181
  // Runtime error documentation
@@ -41958,12 +42265,39 @@ var init_command_schemas = __esm({
41958
42265
  svoPosition: 2,
41959
42266
  sovPosition: 2,
41960
42267
  // SOV: destination comes second (に/에/a marker)
41961
- markerOverride: { en: "into" },
41962
- // "put 'hello' into #output"
42268
+ // "put 'hello' into #output" — directional, so the same locative-default
42269
+ // correction as `add`. es `en` and pt `em` are already right for "into",
42270
+ // as are ja に / ko 에 / tr e; only ar/zh/fr/de need an override.
42271
+ //
42272
+ // Tier B (2.9): `put` is ILLATIVE, so it diverges from `add` where the
42273
+ // two senses differ. he takes `ב` ("in/into" — `שים ב`), NOT the allative
42274
+ // `אל` that `add`/`go` take. it keeps its locative `in` (`mettere in`) —
42275
+ // it is `add`/`go` that needed `a`. id/sw change for the same reason as
42276
+ // `add` (directional / event-keyword collision). hi `में`, ru+uk `в`,
42277
+ // th `ใน` and vi `vào` are all already the illative.
42278
+ markerOverride: {
42279
+ en: "into",
42280
+ ar: "\u0641\u064A",
42281
+ zh: "\u5230",
42282
+ fr: "dans",
42283
+ de: "in",
42284
+ he: "\u05D1",
42285
+ id: "ke",
42286
+ sw: "kwa"
42287
+ },
41963
42288
  // `before` / `after` are alternate position markers; the matched marker
41964
42289
  // is recorded as a literal in the `method` role (a derived role with no
41965
42290
  // surface form of its own — populated by schema-driven role inference).
41966
42291
  markerVariants: { en: ["before", "after"] },
42292
+ markerLegacy: {
42293
+ ar: ["\u0639\u0644\u0649", "\u0625\u0644\u0649", "\u0628"],
42294
+ zh: ["\u5728", "\u4E8E"],
42295
+ fr: ["sur", "\xE0"],
42296
+ de: ["auf", "zu"],
42297
+ he: ["\u05E2\u05DC", "\u05D0\u05DC", "\u05DC"],
42298
+ id: ["pada", "di"],
42299
+ sw: ["kwenye"]
42300
+ },
41967
42301
  methodCarrier: "method"
41968
42302
  }
41969
42303
  ],
@@ -42098,8 +42432,11 @@ var init_command_schemas = __esm({
42098
42432
  // ending in a vowel (`doğru ya` = "true" in set-attribute). markerOverride
42099
42433
  // is a single string, so the generated tr set patterns carried only `e`
42100
42434
  // and set-attribute fell to the role-scrambling generic SOV extraction.
42101
- // markerVariants supplies the allomorphs the SOV two-role generators merge
42102
- // in as marker alternatives. See STRUCTURAL_ARCS_ROADMAP.md (tr set-attribute).
42435
+ // markerVariants supplies the allomorphs, merged in as marker alternatives.
42436
+ // Until 2026-07-25 only the SOV two-role generators merged them, so this
42437
+ // worked ONLY inside an event handler: `@disabled i doğru ya ayarla` did
42438
+ // not parse as a bare command while `tıklama da @disabled i doğru ya
42439
+ // ayarla` did. See STRUCTURAL_ARCS_ROADMAP.md (tr set-attribute).
42103
42440
  markerVariants: {
42104
42441
  tr: ["e", "a", "ye", "ya"]
42105
42442
  }
@@ -43031,17 +43368,100 @@ var init_command_schemas = __esm({
43031
43368
  expectedTypes: ["literal", "expression"],
43032
43369
  svoPosition: 1,
43033
43370
  sovPosition: 1,
43034
- markerOverride: { en: "to" },
43035
- // "go to /page" (parsing)
43036
- renderOverride: { en: "" },
43037
- // "go /page" (rendering — no preposition)
43371
+ // "go to /page" (parsing). Directional, so the same locative-default
43372
+ // correction as `add`/`put`.
43373
+ //
43374
+ // Tier B (2.9): `go` is pure ALLATIVE — motion toward a target — so it
43375
+ // needs the directional in more languages than `add`/`put` do, including
43376
+ // ones where a container-locative was fine for those two.
43377
+ // he — `אל` ("toward"), as `add`; `לך על url` read "go ON url".
43378
+ // hi — `पर`: Hindi navigates to a page with `पर जाएं`; `में` is
43379
+ // "go INTO", which is entering a place, not opening a URL.
43380
+ // id — `ke`, as `add`.
43381
+ // it — `a`: `andare a` for a specific target (`andare in` is for
43382
+ // regions — `andare in Italia`).
43383
+ // ru/uk — `на`: `перейти на сторінку` is the navigation idiom; `в`
43384
+ // ("into") is right for `add`/`put` but not for opening a page.
43385
+ // sw — `kwa`, as `add`.
43386
+ // th is NOT here — it renders bare, with zh and vi; see below.
43387
+ markerOverride: {
43388
+ en: "to",
43389
+ es: "a",
43390
+ ar: "\u0625\u0644\u0649",
43391
+ fr: "\xE0",
43392
+ de: "zu",
43393
+ pt: "para",
43394
+ he: "\u05D0\u05DC",
43395
+ hi: "\u092A\u0930",
43396
+ id: "ke",
43397
+ it: "a",
43398
+ ru: "\u043D\u0430",
43399
+ sw: "kwa",
43400
+ uk: "\u043D\u0430"
43401
+ },
43402
+ // "go /page" (rendering — no preposition).
43403
+ //
43404
+ // zh, vi and th render BARE.
43405
+ //
43406
+ // zh and vi because their `go` keyword already encodes the direction, so
43407
+ // any destination marker is a second one: zh `前往` is "proceed-to"
43408
+ // (`前往 到 url` = "proceed-to to url") and vi `đi đến` is literally
43409
+ // "go to" (`đi đến vào url` = "go-to into url"). Both are corrected in
43410
+ // the i18n corpus in the same change
43411
+ // (`patterns-reference/scripts/fix-translations.sql`).
43412
+ //
43413
+ // th because Thai motion verbs take a BARE destination — `ไปบ้าน`
43414
+ // ("go home"), `ไปโรงเรียน` ("go school") — so `ไป url` is the idiomatic
43415
+ // form. The profile default rendered `ไป ใน url` ("go IN url"), which is
43416
+ // what needed fixing; the obvious replacement `ยัง` (giving the formal
43417
+ // `ไปยัง`) is rejected because `ยัง` is also the very common adverb
43418
+ // "still/yet", and the V4 vocab gate correctly refuses to classify it as
43419
+ // a particle — promoting it would mis-tokenize ordinary Thai.
43420
+ //
43421
+ // Parsing is unaffected for all three: none has a `markerOverride`, so
43422
+ // each stays on the profile-default branch and keeps accepting its old
43423
+ // markers (th `ใน` / `ไปยัง`) from the profile itself.
43424
+ renderOverride: { en: "", zh: "", vi: "", th: "" },
43038
43425
  // `go back` renders the destination bare in en (history nav has no `to`),
43039
43426
  // and he/zh render it with their PATIENT marker (לך את back / 前往 把 back)
43040
43427
  // while go-url keeps the destination marker (לך על url / 前往 到 url) —
43041
43428
  // the corpus is ground truth, so en's `to` is optional and he/zh accept
43042
43429
  // the patient particle as a destination-marker alternative, scoped to go.
43043
- markerOptional: { en: true },
43044
- markerVariants: { he: ["\u05D0\u05EA"], zh: ["\u628A"] }
43430
+ // The render side drops the preposition for these four, so the parse
43431
+ // side cannot require it: `go /page`, `前往 url`, `đi đến url`, `ไป url`
43432
+ // must parse alongside the marked forms the profile still accepts.
43433
+ markerOptional: { en: true, zh: true, vi: true, th: true },
43434
+ // zh renders `前往 把 back` with its PATIENT particle before go's
43435
+ // destination — a synonym here, not a distinct shape, so it is accepted as
43436
+ // a marker alternative scoped to go. he's `את` is the same thing and sits
43437
+ // in `markerLegacy` below: it moved there in #763 because the two fields
43438
+ // were then read by DIFFERENT branches, so leaving it here silently
43439
+ // stopped `לך את back` parsing the moment he gained a `markerOverride`.
43440
+ // Both fields now merge on both branches (`schemaMarkerAlternatives`), so
43441
+ // that trap is gone and the split is historical.
43442
+ markerVariants: { zh: ["\u628A"] },
43443
+ markerLegacy: {
43444
+ es: ["en", "sobre", "hacia"],
43445
+ ar: ["\u0639\u0644\u0649", "\u0641\u064A", "\u0628"],
43446
+ fr: ["sur", "dans"],
43447
+ de: ["auf", "in"],
43448
+ pt: ["em", "a"],
43449
+ // `את` is he's PATIENT particle, which the transformer renders before
43450
+ // go's destination in `go back` (`לך את back`) — a parse-only synonym
43451
+ // here, never rendered, which is exactly what markerLegacy is for.
43452
+ he: ["\u05E2\u05DC", "\u05D1", "\u05DC", "\u05D0\u05EA"],
43453
+ hi: ["\u092E\u0947\u0902"],
43454
+ id: ["pada", "di"],
43455
+ it: ["in", "su"],
43456
+ ru: ["\u0432", "\u043A"],
43457
+ sw: ["kwenye"],
43458
+ uk: ["\u0432", "\u0434\u043E"]
43459
+ // zh, vi and th are NOT listed: none has a markerOverride, so all three
43460
+ // stay on the profile-default branch and keep accepting their old
43461
+ // markers from the profile itself. Only their RENDERING changed.
43462
+ // Listing them here would be dead config — markerLegacy is read ONLY by
43463
+ // the override branch.
43464
+ }
43045
43465
  }
43046
43466
  ],
43047
43467
  // `go to url "/page"` — without this variant the destination captures the
@@ -44043,7 +44463,7 @@ var init_command_schemas = __esm({
44043
44463
  roles: []
44044
44464
  }
44045
44465
  };
44046
- if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
44466
+ if (typeof process !== "undefined" && process.env.LOKASCRIPT_SCHEMA_VALIDATION === "1") {
44047
44467
  Promise.resolve().then(() => (init_schema_validator(), schema_validator_exports)).then(({ validateAllSchemas: validateAllSchemas2, formatValidationResults: formatValidationResults2 }) => {
44048
44468
  const validations = validateAllSchemas2(commandSchemas);
44049
44469
  if (validations.size > 0) {
@@ -50621,12 +51041,16 @@ var init_spanish_keyword = __esm({
50621
51041
  const keywordEntry = this.context.lookupKeyword(word);
50622
51042
  const normalized2 = keywordEntry && keywordEntry.normalized !== keywordEntry.native ? keywordEntry.normalized : void 0;
50623
51043
  let morphNormalized;
51044
+ let morphStem;
51045
+ let morphConfidence;
50624
51046
  if (!keywordEntry && this.context.normalizer) {
50625
51047
  const morphResult = this.context.normalizer.normalize(word);
50626
51048
  if (morphResult.stem !== word && morphResult.confidence >= 0.7) {
50627
51049
  const stemEntry = this.context.lookupKeyword(morphResult.stem);
50628
51050
  if (stemEntry) {
50629
51051
  morphNormalized = stemEntry.normalized;
51052
+ morphStem = morphResult.stem;
51053
+ morphConfidence = morphResult.confidence;
50630
51054
  }
50631
51055
  }
50632
51056
  }
@@ -50635,6 +51059,8 @@ var init_spanish_keyword = __esm({
50635
51059
  length: pos2 - position,
50636
51060
  metadata: {
50637
51061
  normalized: normalized2 || morphNormalized,
51062
+ stem: morphStem,
51063
+ stemConfidence: morphConfidence,
50638
51064
  isPreposition
50639
51065
  }
50640
51066
  };
@@ -51754,12 +52180,16 @@ var init_portuguese_keyword = __esm({
51754
52180
  const keywordEntry = this.context.lookupKeyword(lower);
51755
52181
  const normalized2 = keywordEntry && keywordEntry.normalized !== keywordEntry.native ? keywordEntry.normalized : void 0;
51756
52182
  let morphNormalized;
52183
+ let morphStem;
52184
+ let morphConfidence;
51757
52185
  if (!keywordEntry && this.context.normalizer) {
51758
52186
  const morphResult = this.context.normalizer.normalize(word);
51759
52187
  if (morphResult.stem !== word && morphResult.confidence >= 0.7) {
51760
52188
  const stemEntry = this.context.lookupKeyword(morphResult.stem);
51761
52189
  if (stemEntry) {
51762
52190
  morphNormalized = stemEntry.normalized;
52191
+ morphStem = morphResult.stem;
52192
+ morphConfidence = morphResult.confidence;
51763
52193
  }
51764
52194
  }
51765
52195
  }
@@ -51768,6 +52198,8 @@ var init_portuguese_keyword = __esm({
51768
52198
  length: pos2 - position,
51769
52199
  metadata: {
51770
52200
  normalized: normalized2 || morphNormalized,
52201
+ stem: morphStem,
52202
+ stemConfidence: morphConfidence,
51771
52203
  isPreposition
51772
52204
  }
51773
52205
  };
@@ -52247,12 +52679,16 @@ var init_french_keyword = __esm({
52247
52679
  const keywordEntry = this.context.lookupKeyword(lower);
52248
52680
  const normalized2 = keywordEntry && keywordEntry.normalized !== keywordEntry.native ? keywordEntry.normalized : void 0;
52249
52681
  let morphNormalized;
52682
+ let morphStem;
52683
+ let morphConfidence;
52250
52684
  if (!keywordEntry && this.context.normalizer) {
52251
52685
  const morphResult = this.context.normalizer.normalize(word);
52252
52686
  if (morphResult.stem !== word && morphResult.confidence >= 0.7) {
52253
52687
  const stemEntry = this.context.lookupKeyword(morphResult.stem);
52254
52688
  if (stemEntry) {
52255
52689
  morphNormalized = stemEntry.normalized;
52690
+ morphStem = morphResult.stem;
52691
+ morphConfidence = morphResult.confidence;
52256
52692
  }
52257
52693
  }
52258
52694
  }
@@ -52261,6 +52697,8 @@ var init_french_keyword = __esm({
52261
52697
  length: pos2 - position,
52262
52698
  metadata: {
52263
52699
  normalized: normalized2 || morphNormalized,
52700
+ stem: morphStem,
52701
+ stemConfidence: morphConfidence,
52264
52702
  isPreposition
52265
52703
  }
52266
52704
  };
@@ -58883,8 +59321,10 @@ function resolveMarkerForRole(roleSpec, profile) {
58883
59321
  const overrideMarker = roleSpec.markerOverride?.[profile.code];
58884
59322
  const defaultMarker = profile.roleMarkers[roleSpec.role];
58885
59323
  if (overrideMarker !== void 0) {
59324
+ const alternatives = legacyMarkerAlternatives(roleSpec, profile.code, overrideMarker);
58886
59325
  return {
58887
59326
  primary: overrideMarker,
59327
+ ...alternatives && { alternatives },
58888
59328
  position: defaultMarker?.position ?? "before",
58889
59329
  isOverride: true
58890
59330
  };
@@ -58902,6 +59342,18 @@ function resolveMarkerForRole(roleSpec, profile) {
58902
59342
  }
58903
59343
  return null;
58904
59344
  }
59345
+ function legacyMarkerAlternatives(roleSpec, languageCode, overrideMarker) {
59346
+ const legacy = roleSpec.markerLegacy?.[languageCode];
59347
+ if (!legacy?.length) return void 0;
59348
+ const alternatives = [...new Set(legacy)].filter((a) => a && a !== overrideMarker);
59349
+ return alternatives.length ? alternatives : void 0;
59350
+ }
59351
+ function schemaMarkerAlternatives(roleSpec, languageCode, marker) {
59352
+ const legacy = roleSpec.markerLegacy?.[languageCode] ?? [];
59353
+ const variants = roleSpec.methodCarrier ? [] : roleSpec.markerVariants?.[languageCode] ?? [];
59354
+ const alternatives = [.../* @__PURE__ */ new Set([...legacy, ...variants])].filter((a) => a && a !== marker);
59355
+ return alternatives.length ? alternatives : void 0;
59356
+ }
58905
59357
  var init_marker_resolution = __esm({
58906
59358
  "src/parser/utils/marker-resolution.ts"() {
58907
59359
  }
@@ -58913,20 +59365,17 @@ function resolveRoleMarker(roleSpec, profile) {
58913
59365
  let alternatives;
58914
59366
  if (roleSpec.markerOverride && roleSpec.markerOverride[profile.code] !== void 0) {
58915
59367
  marker = roleSpec.markerOverride[profile.code];
59368
+ alternatives = marker ? schemaMarkerAlternatives(roleSpec, profile.code, marker) : void 0;
58916
59369
  } else {
58917
59370
  const roleMarker = profile.roleMarkers[roleSpec.role];
58918
59371
  if (roleMarker) {
58919
59372
  marker = roleMarker.primary;
58920
- alternatives = roleMarker.alternatives ? [...roleMarker.alternatives] : void 0;
58921
- }
58922
- }
58923
- const variants = roleSpec.markerVariants?.[profile.code];
58924
- if (variants && variants.length > 0) {
58925
- const merged = alternatives ? [...alternatives] : [];
58926
- for (const v of variants) {
58927
- if (v !== marker && !merged.includes(v)) merged.push(v);
59373
+ const schemaAlts = schemaMarkerAlternatives(roleSpec, profile.code, marker) ?? [];
59374
+ const merged = [.../* @__PURE__ */ new Set([...roleMarker.alternatives ?? [], ...schemaAlts])].filter(
59375
+ (a) => a !== marker
59376
+ );
59377
+ alternatives = merged.length ? merged : void 0;
58928
59378
  }
58929
- alternatives = merged;
58930
59379
  }
58931
59380
  return { marker, alternatives };
58932
59381
  }
@@ -59395,10 +59844,18 @@ function generateSOVTwoRoleDestFirstEventHandlerPattern(commandSchema, profile,
59395
59844
  var init_event_handlers_sov = __esm({
59396
59845
  "src/generators/event-handlers-sov.ts"() {
59397
59846
  init_command_schemas();
59847
+ init_marker_resolution();
59398
59848
  }
59399
59849
  });
59400
59850
 
59401
59851
  // src/generators/event-handlers-vso.ts
59852
+ function mergeSchemaAlternatives(roleSpec, profile, roleMarker) {
59853
+ const schemaAlts = schemaMarkerAlternatives(roleSpec, profile.code, roleMarker.primary) ?? [];
59854
+ const merged = [.../* @__PURE__ */ new Set([...roleMarker.alternatives ?? [], ...schemaAlts])].filter(
59855
+ (a) => a !== roleMarker.primary
59856
+ );
59857
+ return merged.length ? merged : void 0;
59858
+ }
59402
59859
  function generateVSOEventHandlerPattern(commandSchema, profile, keyword, eventMarker, config) {
59403
59860
  const tokens = [];
59404
59861
  if (eventMarker.position === "before") {
@@ -59511,11 +59968,12 @@ function generateVSOVerbFirstTwoRoleEventHandlerPattern(commandSchema, profile,
59511
59968
  let markerAlternatives;
59512
59969
  if (roleSpec.markerOverride && roleSpec.markerOverride[profile.code] !== void 0) {
59513
59970
  marker = roleSpec.markerOverride[profile.code];
59971
+ markerAlternatives = marker ? schemaMarkerAlternatives(roleSpec, profile.code, marker) : void 0;
59514
59972
  } else {
59515
59973
  const roleMarker = profile.roleMarkers[roleSpec.role];
59516
59974
  if (roleMarker) {
59517
59975
  marker = roleMarker.primary;
59518
- markerAlternatives = roleMarker.alternatives;
59976
+ markerAlternatives = mergeSchemaAlternatives(roleSpec, profile, roleMarker);
59519
59977
  }
59520
59978
  }
59521
59979
  if (marker) {
@@ -59568,11 +60026,12 @@ function generateVSOTwoRoleEventHandlerPattern(commandSchema, profile, keyword,
59568
60026
  let markerAlternatives;
59569
60027
  if (roleSpec.markerOverride && roleSpec.markerOverride[profile.code] !== void 0) {
59570
60028
  marker = roleSpec.markerOverride[profile.code];
60029
+ markerAlternatives = marker ? schemaMarkerAlternatives(roleSpec, profile.code, marker) : void 0;
59571
60030
  } else {
59572
60031
  const roleMarker = profile.roleMarkers[roleSpec.role];
59573
60032
  if (roleMarker) {
59574
60033
  marker = roleMarker.primary;
59575
- markerAlternatives = roleMarker.alternatives;
60034
+ markerAlternatives = mergeSchemaAlternatives(roleSpec, profile, roleMarker);
59576
60035
  }
59577
60036
  }
59578
60037
  if (marker) {
@@ -59705,6 +60164,7 @@ var init_event_handlers_vso = __esm({
59705
60164
  "src/generators/event-handlers-vso.ts"() {
59706
60165
  init_command_schemas();
59707
60166
  init_event_handlers_sov();
60167
+ init_marker_resolution();
59708
60168
  }
59709
60169
  });
59710
60170
 
@@ -60104,7 +60564,13 @@ function buildRoleToken(roleSpec, profile) {
60104
60564
  const position = defaultMarker?.position ?? "before";
60105
60565
  const optionalMarker = roleSpec.markerOptional?.[profile.code] === true;
60106
60566
  const pushWord = (word) => {
60107
- const literal = suppressMarker ? { type: "literal", value: word, renderSuppress: true } : { type: "literal", value: word };
60567
+ const alternatives = markerWords.length === 1 ? schemaMarkerAlternatives(roleSpec, profile.code, word) ?? [] : [];
60568
+ const literal = {
60569
+ type: "literal",
60570
+ value: word,
60571
+ ...alternatives.length ? { alternatives } : {},
60572
+ ...suppressMarker ? { renderSuppress: true } : {}
60573
+ };
60108
60574
  tokens.push(optionalMarker ? { type: "group", optional: true, tokens: [literal] } : literal);
60109
60575
  };
60110
60576
  if (position === "before") {
@@ -60115,10 +60581,12 @@ function buildRoleToken(roleSpec, profile) {
60115
60581
  for (const word of markerWords) pushWord(word);
60116
60582
  }
60117
60583
  } else if (defaultMarker) {
60118
- const variantAlts = roleSpec.markerVariants?.[profile.code] ?? [];
60119
60584
  const asMarker = () => {
60120
60585
  const alternatives = [
60121
- .../* @__PURE__ */ new Set([...defaultMarker.alternatives ?? [], ...variantAlts])
60586
+ .../* @__PURE__ */ new Set([
60587
+ ...defaultMarker.alternatives ?? [],
60588
+ ...schemaMarkerAlternatives(roleSpec, profile.code, defaultMarker.primary) ?? []
60589
+ ])
60122
60590
  ].filter((a) => a !== defaultMarker.primary);
60123
60591
  return {
60124
60592
  type: "literal",
@@ -60154,11 +60622,19 @@ function buildExtractionRules(schema, profile) {
60154
60622
  if (roleSpec.valuePrefixLiteral?.[profile.code]) {
60155
60623
  rules[roleSpec.role] = { marker: roleSpec.valuePrefixLiteral[profile.code] };
60156
60624
  } else if (overrideMarker !== void 0) {
60157
- rules[roleSpec.role] = overrideMarker ? { marker: overrideMarker } : {};
60625
+ if (!overrideMarker) {
60626
+ rules[roleSpec.role] = {};
60627
+ } else {
60628
+ const isSingleWord = !/\s/.test(overrideMarker.trim());
60629
+ const markerAlternatives = isSingleWord ? schemaMarkerAlternatives(roleSpec, profile.code, overrideMarker) ?? [] : [];
60630
+ rules[roleSpec.role] = markerAlternatives.length ? { marker: overrideMarker, markerAlternatives } : { marker: overrideMarker };
60631
+ }
60158
60632
  } else if (defaultMarker && defaultMarker.primary) {
60159
- const variantAlts = roleSpec.markerVariants?.[profile.code] ?? [];
60160
60633
  const markerAlternatives = [
60161
- .../* @__PURE__ */ new Set([...defaultMarker.alternatives ?? [], ...variantAlts])
60634
+ .../* @__PURE__ */ new Set([
60635
+ ...defaultMarker.alternatives ?? [],
60636
+ ...schemaMarkerAlternatives(roleSpec, profile.code, defaultMarker.primary) ?? []
60637
+ ])
60162
60638
  ].filter((a) => a !== defaultMarker.primary);
60163
60639
  rules[roleSpec.role] = markerAlternatives.length ? { marker: defaultMarker.primary, markerAlternatives } : { marker: defaultMarker.primary };
60164
60640
  } else {
@@ -66337,6 +66813,12 @@ var _PatternMatcher = class _PatternMatcher {
66337
66813
  if (token.kind === "keyword" && token.value.toLowerCase() === value.toLowerCase()) {
66338
66814
  return "case-insensitive";
66339
66815
  }
66816
+ if (token.kind === "keyword") {
66817
+ const strippedToken = stripOptionalDiacritics(token.value).toLowerCase();
66818
+ if (strippedToken && strippedToken === stripOptionalDiacritics(value).toLowerCase()) {
66819
+ return "case-insensitive";
66820
+ }
66821
+ }
66340
66822
  return "none";
66341
66823
  }
66342
66824
  /**
@@ -71669,6 +72151,7 @@ function parseWithConfidence(hyperscript, language) {
71669
72151
  }
71670
72152
 
71671
72153
  // src/explicit/converter.ts
72154
+ init_registry();
71672
72155
  function toExplicit(input, sourceLanguage) {
71673
72156
  if (isExplicitSyntax$1(input)) {
71674
72157
  return input;
@@ -71709,28 +72192,22 @@ function roundTrip(input, sourceLanguage, targetLanguage) {
71709
72192
  matches: normalizedOriginal === normalizedRendered
71710
72193
  };
71711
72194
  }
71712
- function getAllTranslations(input, sourceLanguage, targetLanguages = [
71713
- "en",
71714
- "ja",
71715
- "ar",
71716
- "es",
71717
- "ko",
71718
- "zh",
71719
- "tr",
71720
- "pt",
71721
- "fr",
71722
- "de",
71723
- "id",
71724
- "qu",
71725
- "sw"
71726
- ]) {
71727
- const result = {};
72195
+ function getAllTranslations(input, sourceLanguage, targetLanguages = getRegisteredLanguages()) {
72196
+ return getAllTranslationsWithStatus(input, sourceLanguage, targetLanguages).translations;
72197
+ }
72198
+ function getAllTranslationsWithStatus(input, sourceLanguage, targetLanguages = getRegisteredLanguages()) {
72199
+ const translations = {};
72200
+ const failed = {};
71728
72201
  const node = parseAny(input, sourceLanguage);
71729
72202
  for (const lang of targetLanguages) {
71730
- result[lang] = render(node, lang);
72203
+ try {
72204
+ translations[lang] = render(node, lang);
72205
+ } catch (error) {
72206
+ failed[lang] = error instanceof Error ? error.message : String(error);
72207
+ }
71731
72208
  }
71732
- result["explicit"] = renderExplicit$1(node);
71733
- return result;
72209
+ translations["explicit"] = renderExplicit$1(node);
72210
+ return { translations, failed };
71734
72211
  }
71735
72212
  function validateTranslation(original, translated, originalLang, translatedLang) {
71736
72213
  try {
@@ -79038,6 +79515,7 @@ var index$1 = /*#__PURE__*/Object.freeze({
79038
79515
  generateSimplePattern: generateSimplePattern,
79039
79516
  get germanProfile () { return germanProfile; },
79040
79517
  getAllTranslations: getAllTranslations,
79518
+ getAllTranslationsWithStatus: getAllTranslationsWithStatus,
79041
79519
  getCommandMapper: getCommandMapper,
79042
79520
  getCommandType: getCommandType,
79043
79521
  getDefaultExtractors: getDefaultExtractors,