@xema/omni-protocol 0.1.127 → 0.1.128

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
@@ -160,7 +160,8 @@ export function localizedText(text, languages) {
160
160
  return undefined;
161
161
  const byTag = new Map(Object.entries(text).map(([tag, value]) => [tag.toLowerCase(), value]));
162
162
  for (const language of languages) {
163
- for (let tag = language.toLowerCase(); tag !== ""; tag = tag.includes("-") ? tag.slice(0, tag.lastIndexOf("-")) : "") {
163
+ // RFC 4647 Lookup: drop subtags from the end, and a single-letter extension marker with the one before it.
164
+ for (let tag = language.toLowerCase(); tag !== ""; tag = shorterTag(tag)) {
164
165
  const found = byTag.get(tag);
165
166
  if (found !== undefined)
166
167
  return found;
@@ -168,6 +169,12 @@ export function localizedText(text, languages) {
168
169
  }
169
170
  return undefined;
170
171
  }
172
+ function shorterTag(tag) {
173
+ if (!tag.includes("-"))
174
+ return "";
175
+ const shorter = tag.slice(0, tag.lastIndexOf("-"));
176
+ return /-[a-z0-9]$/.test(shorter) ? shorter.slice(0, -2) : shorter;
177
+ }
171
178
  // ---------------------------------------------------------------------------
172
179
  // Utilities.
173
180
  // ---------------------------------------------------------------------------
@@ -561,8 +561,17 @@ export function validateManifest(manifest, path = "manifest") {
561
561
  }
562
562
  return into.violations;
563
563
  }
564
- /** Language tags as BCP 47 writes them: a language, then subtags of up to eight letters or digits. */
565
- const LANGUAGE_TAG = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{1,8})*$/;
564
+ /** A well-formed BCP 47 tag as ECMA-402 reads one, a Unicode locale identifier: judged by its spelling, never by what the machine has installed. */
565
+ function isLanguageTag(value) {
566
+ if (typeof value !== "string" || value === "")
567
+ return false;
568
+ try {
569
+ return Intl.getCanonicalLocales(value).length === 1;
570
+ }
571
+ catch {
572
+ return false;
573
+ }
574
+ }
566
575
  /** The agent's languages, as a host states them on both contexts: BCP 47 tags, most preferred first, at least one. */
567
576
  export function validateLanguages(value, path = "context.languages") {
568
577
  const into = new Collector();
@@ -572,7 +581,7 @@ export function validateLanguages(value, path = "context.languages") {
572
581
  }
573
582
  const seen = new Set();
574
583
  value.forEach((tag, index) => {
575
- if (typeof tag !== "string" || !LANGUAGE_TAG.test(tag)) {
584
+ if (!isLanguageTag(tag)) {
576
585
  into.add("context.languages.tag", `${path}[${index}]`, `${describeValue(tag)} is not a BCP 47 language tag`);
577
586
  return;
578
587
  }
@@ -633,7 +642,7 @@ function validateLocalizedText(value, rule, path, into) {
633
642
  }
634
643
  const seen = new Set();
635
644
  for (const [tag, text] of Object.entries(value)) {
636
- if (!into.require(LANGUAGE_TAG.test(tag), `${rule}.language`, `${path}.${tag}`, `${describeValue(tag)} is not a language tag such as en, hi or en-IN`))
645
+ if (!into.require(isLanguageTag(tag), `${rule}.language`, `${path}.${tag}`, `${describeValue(tag)} is not a language tag such as en, hi or en-IN`))
637
646
  continue;
638
647
  into.require(!seen.has(tag.toLowerCase()), `${rule}.language.unique`, `${path}.${tag}`, "language tags are case-insensitive, and each language is stated once");
639
648
  seen.add(tag.toLowerCase());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xema/omni-protocol",
3
- "version": "0.1.127",
3
+ "version": "0.1.128",
4
4
  "description": "The Omni protocol: the contract every provider adapter implements",
5
5
  "type": "module",
6
6
  "license": "MIT",