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