{
  "version": 3,
  "sources": ["../../lib/matcher.ts", "../../lib/simplify.ts", "../../lib/stemmer.ts", "../../lib/sort_object.ts", "../../lib/presets_id.ts", "../../lib/presets_josm.ts"],
  "sourcesContent": [
    "import { LocationConflation } from '@rapideditor/location-conflation';\nimport { simplify } from './simplify.ts';\n\nimport type { HasLocationSet, HasLocationSetID, LocationSetID, Vec2 } from '@rapideditor/location-conflation';\nimport type { MatchHit, MatchIndexBranch, NsiData, NsiMatchGroupsJSON, NsiPath, NsiTree, NsiTreeProperties } from './types.ts';\n\n// Imported JSON (will be inlined by bun)\nimport matchGroupsJSON from '../config/matchGroups.json' with {type: 'json'};\nimport genericWordsJSON from '../config/genericWords.json' with {type: 'json'};\nimport treesJSON from '../config/trees.json' with {type: 'json'};\n\n/** Match-group definitions keyed by group name. */\nconst matchGroups: NsiMatchGroupsJSON['matchGroups'] = matchGroupsJSON.matchGroups;\n\n/** Tree configuration keyed by tree name (e.g. `brands`, `operators`). */\nconst trees: Record<NsiTree, NsiTreeProperties> = treesJSON.trees;\n\n\n//  This is an unfortunate TypeScript-ism, essentially it does not consider a class instance\n//  with private or protected members to be type-compatible with itself.\n//  The workaround is to define an interface type so that instead of saying\n//   \"buildLocationIndex accepts a LocationConflation instance\"\n//  we need to say\n//   \"buildLocationIndex accepts something that looks like a LocationConflation instance\"\n// @see https://www.reddit.com/r/typescript/comments/wv3d5m/private_properties_different_versions_of_same/\n// @see https://github.com/microsoft/TypeScript/issues/18499 (and related)\n/** LocationConflation _structural_ type - see name-suggestion-index#12150 **/\nexport interface LocationResolver {\n  registerLocationSets<T extends HasLocationSet>(objects: T[]): (T & HasLocationSetID)[];\n  locationSetsAt(loc: Vec2): Map<LocationSetID, number>;\n  getLocationSetArea(locationSetID: LocationSetID): number | undefined;\n}\n\n\n/**\n * Matches OpenStreetMap `[key, value, name]` tuples against the\n * Name Suggestion Index (NSI) canonical items.\n *\n * Typical usage:\n * ```ts\n * const matcher = new Matcher();\n * matcher.buildMatchIndex(data);\n * matcher.buildLocationIndex(data, loco);   // optional\n * const hits = matcher.match('amenity', 'bank', 'Wells Fargo', [-122.4, 37.8]);\n * ```\n */\nexport class Matcher {\n  /** Primary match index: `kv → { primary, alternate, excludeGeneric, excludeNamed }`. */\n  private matchIndex: Map<string, MatchIndexBranch> | undefined;\n  /** Map of generic-word pattern strings to compiled RegExp objects. */\n  private genericWords = new Map<string, RegExp>();\n  /** The location resolver used to resolve locationSets (set by {@link buildLocationIndex}). */\n  private loco: LocationResolver | undefined;\n  /** Map of item id → locationSetID, populated by {@link buildLocationIndex}. */\n  private itemLocationSetID: Map<string, LocationSetID> | undefined;\n  /** Warnings collected during index building (e.g. duplicate cache keys). */\n  private warnings: Array<string> = [];\n\n\n  /**\n   * Creates a new Matcher and initialises the generic-word regex table\n   * from `config/genericWords.json`.\n   */\n  constructor() {\n    // The `matchIndex` is a specialized structure that allows us to quickly answer\n    //   _\"Given a [key/value tagpair, name, location], what canonical items (brands etc) can match it?\"_\n    //\n    // The index contains all valid combinations of k/v tagpairs and names\n    // matchIndex:\n    // {\n    //   'k/v': {\n    //     'primary':         Map (String 'nsimple' -> Set (itemIDs…),   // matches for tags like `name`, `name:xx`, etc.\n    //     'alternate':       Map (String 'nsimple' -> Set (itemIDs…),   // matches for tags like `alt_name`, `brand`, etc.\n    //     'excludeNamed':    Map (String 'pattern' -> RegExp),\n    //     'excludeGeneric':  Map (String 'pattern' -> RegExp)\n    //   },\n    // }\n    //\n    // {\n    //   'amenity/bank': {\n    //     'primary': {\n    //       'firstbank':              Set (\"firstbank-978cca\", \"firstbank-9794e6\", \"firstbank-f17495\", …),\n    //       …\n    //     },\n    //     'alternate': {\n    //       '1stbank':                Set (\"firstbank-f17495\"),\n    //       …\n    //     }\n    //   },\n    //   'shop/supermarket': {\n    //     'primary': {\n    //       'coop':                   Set (\"coop-76454b\", \"coop-ebf2d9\", \"coop-36e991\", …),\n    //       'coopfood':               Set (\"coopfood-a8278b\", …),\n    //       …\n    //     },\n    //     'alternate': {\n    //       'coop':                   Set (\"coopfood-a8278b\", …),\n    //       'federatedcooperatives':  Set (\"coop-76454b\", …),\n    //       'thecooperative':         Set (\"coopfood-a8278b\", …),\n    //       …\n    //     }\n    //   }\n    // }\n    //\n    this.matchIndex = undefined;\n\n    // The `genericWords` structure matches the contents of genericWords.json to instantiated RegExp objects\n    // Map (String 'pattern' -> RegExp),\n    this.genericWords = new Map();\n    for (const s of (genericWordsJSON.genericWords || [])) {\n      this.genericWords.set(s, new RegExp(s, 'i'));\n    }\n\n    // A reference to the `LocationConflation` instance supplied to `buildLocationIndex`.\n    // At match time we call `loco.locationSetsAt(point)` and `loco.getLocationSetArea(id)`\n    // instead of maintaining our own parallel indexes.\n    this.loco = undefined;\n    this.itemLocationSetID = undefined;\n\n    // Array of match conflict pairs (currently unused)\n    this.warnings = [];\n  }\n\n\n  /**\n   * Builds the primary match index from NSI category data.\n   * After calling this method the matcher is ready to use via {@link match}.\n   *\n   * `data` must be an object keyed by `tree/key/value` paths, e.g.:\n   * ```json\n   * {\n   *   \"brands/amenity/bank\": { \"properties\": {}, \"items\": [ … ] },\n   *   \"brands/amenity/bar\":  { \"properties\": {}, \"items\": [ … ] }\n   * }\n   * ```\n   * (typically the cache built by `fileTree.read` or loaded from `dist/nsi.json`)\n   *\n   * @param data - NSI category data indexed by `tree/key/value` path\n   */\n  buildMatchIndex(data: NsiData): void {\n    if (this.matchIndex) return;   // it was built already\n\n    const matchIndex = new Map<string, MatchIndexBranch>();\n    this.matchIndex = matchIndex;\n\n    const seenTree = new Map();  // warn if the same [k, v, nsimple] appears in multiple trees - #5625\n\n    // For certain categories we do not want to match generic KV pairs like `building/yes` or `amenity/yes`\n    const skipGenericKVMatches = (t: string, k: string, v: string): boolean => {\n      return (\n        t === 'flags' ||\n        t === 'transit' ||\n        k === 'landuse' ||\n        v === 'atm' ||\n        v === 'bicycle_parking' ||\n        v === 'car_sharing' ||\n        v === 'caravan_site' ||\n        v === 'charging_station' ||\n        v === 'dog_park' ||\n        v === 'parking' ||\n        v === 'phone' ||\n        v === 'playground' ||\n        v === 'post_box' ||\n        v === 'public_bookcase' ||\n        v === 'recycling' ||\n        v === 'vending_machine'\n      );\n    };\n\n    // Insert this item into the matchIndex\n    const insertName = (which: 'primary' | 'alternate', t: string, kv: string, nsimple: string, itemID: string) => {\n      if (!nsimple) {\n        this.warnings.push(`Warning: skipping empty ${which} name for item ${t}/${kv}: ${itemID}`);\n        return;\n      }\n\n      let branch = matchIndex.get(kv);\n      if (!branch) {\n        branch = {\n          primary: new Map(),\n          alternate: new Map(),\n          excludeGeneric: new Map(),\n          excludeNamed: new Map()\n        };\n        matchIndex.set(kv, branch);\n      }\n\n      let leaf = branch[which].get(nsimple);\n      if (!leaf) {\n        leaf = new Set();\n        branch[which].set(nsimple, leaf);\n      }\n\n      leaf.add(itemID);   // insert\n\n      // check for duplicates - #5625\n      if (!/yes$/.test(kv)) {  // ignore genericKV like amenity/yes, building/yes, etc\n        const kvnsimple = `${kv}/${nsimple}`;\n        const existing = seenTree.get(kvnsimple);\n        if (existing && existing !== t) {\n          const items = Array.from(leaf);\n          this.warnings.push(`Duplicate cache key \"${kvnsimple}\" in trees \"${t}\" and \"${existing}\", check items: ${items}`);\n          return;\n        }\n        seenTree.set(kvnsimple, t);\n      }\n    };\n\n    for (const tkv of Object.keys(data) as NsiPath[]) {\n      const category = data[tkv];\n      const parts = tkv.split('/', 3);     // tkv = \"tree/key/value\"\n      const t = parts[0] as NsiTree;\n      const k = parts[1];\n      const v = parts[2];\n      const thiskv = `${k}/${v}`;\n      const tree = trees[t];\n\n      let branch = matchIndex.get(thiskv);\n      if (!branch) {\n        branch = {\n          primary: new Map(),\n          alternate: new Map(),\n          excludeGeneric: new Map(),\n          excludeNamed: new Map()\n        };\n        matchIndex.set(thiskv, branch);\n      }\n\n      // ADD EXCLUSIONS\n      const properties = category.properties || {};\n      const exclude = properties.exclude || {};\n      for (const s of (exclude.generic || [])) branch.excludeGeneric.set(s, new RegExp(s, 'i'));\n      for (const s of (exclude.named || []))   branch.excludeNamed.set(s, new RegExp(s, 'i'));\n      const excludeRegexes = [...branch.excludeGeneric.values(), ...branch.excludeNamed.values()];\n\n\n      // ADD ITEMS\n      const items = category.items;\n      if (!Array.isArray(items) || !items.length) continue;\n\n\n      // Primary name patterns, match tags to take first\n      //  e.g. `name`, `name:ru`\n      const primaryName = new RegExp(tree.nameTags.primary, 'i');\n\n      // Alternate name patterns, match tags to consider after primary\n      //  e.g. `alt_name`, `short_name`, `brand`, `brand:ru`, etc..\n      const alternateName = new RegExp(tree.nameTags.alternate, 'i');\n\n      // There are a few exceptions to the name matching regexes.\n      // Usually a tag suffix contains a language code like `name:en`, `name:ru`\n      // but we want to exclude things like `operator:type`, `name:etymology`, etc..\n      const notName = /:(colou?r|type|forward|backward|left|right|etymology|pronunciation|signed|wikipedia)$/i;\n\n      // For certain categories we do not want to match generic KV pairs like `building/yes` or `amenity/yes`\n      const skipGenericKV = skipGenericKVMatches(t, k, v);\n\n      // We will collect the generic KV pairs anyway (for the purpose of filtering them out of matchTags)\n      const genericKV = new Set([`${k}/yes`, `building/yes`]);\n\n      // Collect alternate tagpairs for this kv category from matchGroups.\n      // We might also pick up a few more generic KVs (like `shop/yes`)\n      const matchGroupKV = new Set();\n      for (const matchGroup of Object.values(matchGroups)) {\n        const inGroup = matchGroup.some(otherkv => otherkv === thiskv);\n        if (!inGroup) continue;\n\n        for (const otherkv of matchGroup) {\n          if (otherkv === thiskv) continue;   // skip self\n          matchGroupKV.add(otherkv);\n\n          const otherk = otherkv.split('/', 2)[0];   // we might pick up a `shop/yes`\n          genericKV.add(`${otherk}/yes`);\n        }\n      }\n\n      // For each item, insert all [key, value, name] combinations into the match index\n      for (const item of items) {\n        if (!item.id) continue;\n\n        // Automatically remove redundant `matchTags` - #3417, #8137\n        // (i.e. This kv is already covered by matchGroups, so it doesn't need to be in `item.matchTags`\n        //  or this kv is the primary kv, so it doesn't need to be duplicated in `item.matchTags`)\n        if (Array.isArray(item.matchTags) && item.matchTags.length) {\n          item.matchTags = item.matchTags\n            .filter(matchTag => !matchGroupKV.has(matchTag) && (matchTag !== thiskv) && !genericKV.has(matchTag));\n\n          if (!item.matchTags.length) delete item.matchTags;\n        }\n\n        // key/value tagpairs to insert into the match index..\n        let kvTags = [`${thiskv}`]\n          .concat(item.matchTags || []);\n\n        if (!skipGenericKV) {\n          kvTags = kvTags\n            .concat(Array.from(genericKV));  // #3454 - match some generic tags\n        }\n\n        // Index all the namelike tag values\n        for (const osmkey of Object.keys(item.tags)) {\n          if (notName.test(osmkey)) continue;   // osmkey is not a namelike tag, skip\n          const osmvalue = item.tags[osmkey];\n          if (!osmvalue || excludeRegexes.some(regex => regex.test(osmvalue))) continue;   // osmvalue missing or excluded\n\n          if (primaryName.test(osmkey)) {\n            for (const kv of kvTags) insertName('primary', t, kv, simplify(osmvalue), item.id);\n          } else if (alternateName.test(osmkey)) {\n            for (const kv of kvTags) insertName('alternate', t, kv, simplify(osmvalue), item.id);\n          }\n        }\n\n        // Index `matchNames` after indexing all other names..\n        const keepMatchNames = new Set<string>();\n        for (const matchName of (item.matchNames || [])) {\n          // If this matchname isn't already indexed, add it to the alternate index\n          const nsimple = simplify(matchName);\n          for (const kv of kvTags) {\n            const branch = matchIndex.get(kv);\n            const primaryLeaf = branch && branch.primary.get(nsimple);\n            const alternateLeaf = branch && branch.alternate.get(nsimple);\n            const inPrimary = primaryLeaf && primaryLeaf.has(item.id);\n            const inAlternate = alternateLeaf && alternateLeaf.has(item.id);\n\n            if (!inPrimary && !inAlternate) {\n              insertName('alternate', t, kv, nsimple, item.id);\n              keepMatchNames.add(matchName);\n            }\n          }\n        }\n\n        // Automatically remove redundant `matchNames` - #3417\n        // (i.e. This name got indexed some other way, so it doesn't need to be in `item.matchNames`)\n        if (keepMatchNames.size) {\n          item.matchNames = Array.from(keepMatchNames);\n        } else {\n          delete item.matchNames;\n        }\n\n      }   // each item\n    }   // each tkv\n  }\n\n\n  /**\n   * Registers every item's `locationSet` with the supplied {@link LocationConflation}\n   * instance so that {@link match} can do location-aware filtering.  This is optional —\n   * skip it if you don't need location-aware matching.\n   *\n   * Under the hood this just calls `loco.registerLocationSets(items)`, which:\n   *   - assigns `item.locationSetID` in place (e.g. `'+[Q30]'`),\n   *   - builds an inverted spatial index without resolving combined polygons,\n   *   - is tolerant of bad/empty locationSets (falls back to world).\n   *\n   * `data` must be an object keyed by `tree/key/value` paths (same format as\n   * {@link buildMatchIndex}).\n   *\n   * @param data - NSI category data indexed by `tree/key/value` path\n   * @param loco - Optional `LocationConflation` instance used to index locationSets.\n   *   If omitted, a new bare instance is created internally.  Callers that have their\n   *   own configured instance (e.g. with a FeatureCollection of custom `.geojson`\n   *   features) should pass it in so indexing and lookups share the same cache.\n   *   Whichever instance is used, the matcher keeps a reference and delegates\n   *   `locationSetsAt` / `getLocationSetArea` calls to it at match time.\n   */\n  buildLocationIndex(data: NsiData, loco?: LocationResolver): void {\n    loco = loco ?? new LocationConflation();\n    this.loco = loco;\n\n    const itemLocationSetID = new Map<string, LocationSetID>();\n    this.itemLocationSetID = itemLocationSetID;\n\n    for (const tkv of Object.keys(data) as NsiPath[]) {\n      const items = data[tkv].items;\n      if (!Array.isArray(items) || !items.length) continue;\n      const registered = loco.registerLocationSets(items);\n      for (const item of registered) {\n        itemLocationSetID.set(item.id, item.locationSetID);\n      }\n    }\n  }\n\n\n  /**\n   * Matches a `[key, value, name]` tuple against the index and returns results.\n   *\n   * **Case 1 — canonical match:**\n   * Returns an array of {@link Hit} objects sorted by match quality:\n   *   - `\"primary\"` hits (matches `name` tag) come first,\n   *   - `\"alternate\"` hits (matches `alt_name`, `brand`, etc.) come second.\n   *\n   * Within each group, results are sorted by area:\n   *   - **area descending** (worldwide → local) when no `loc` is given,\n   *   - **area ascending** (local → worldwide) when `loc` is given.\n   *\n   * Each hit includes the item's `area` in km².\n   *\n   * **Case 2 — exclude match:**\n   * Returns a single-element array with either:\n   *   - `{ match: 'excludeGeneric', pattern, kv }` — a generic word (e.g. \"Food Court\")\n   *     that is probably not a real name.\n   *   - `{ match: 'excludeNamed', pattern, kv }` — a real but common name (e.g. \"Kebabai\")\n   *     that is not a brand.\n   *\n   * **Case 3 — no match:**\n   * Returns `null`.\n   *\n   * @param   k   - OSM key (e.g. `\"amenity\"`)\n   * @param   v   - OSM value (e.g. `\"bank\"`)\n   * @param   n   - A name-like string to look up (e.g. `\"Wells Fargo\"`)\n   * @param   loc - Optional `[lon, lat]` coordinate to restrict results by location\n   * @returns An array of {@link Hit} results, or `null` if nothing matched.\n   * @throws  {Error} If the match index has not been built yet.\n   */\n  match(k: string, v: string, n: string, loc?: Vec2): Array<MatchHit> | null {\n    if (!this.matchIndex) {\n      throw new Error('match:  matchIndex not built.');\n    }\n    const matchIndex = this.matchIndex;\n    const loco = this.loco;\n    const itemLocationSetID = this.itemLocationSetID;\n\n    // If we were supplied a location, and the location index has been set up,\n    // get the locationSetIDs that are valid there so we can filter results.\n    let validHere: Map<LocationSetID, number> | null = null;\n    if (Array.isArray(loc) && loco) {\n      validHere = loco.locationSetsAt(loc);\n    }\n\n    const nsimple = simplify(n);\n\n    const seen = new Set();\n    const results: Array<MatchHit> = [];\n\n    // Sort smaller (more local) locations first.\n    const byAreaAscending = (hitA: MatchHit, hitB: MatchHit): number => {\n      return (hitA.area || 0) - (hitB.area || 0);\n    };\n    // Sort larger (more worldwide) locations first.\n    const byAreaDescending = (hitA: MatchHit, hitB: MatchHit): number => {\n      return (hitB.area || 0) - (hitA.area || 0);\n    };\n\n    const isValidLocation = (hit: MatchHit) => {\n      if (!validHere || !itemLocationSetID) return true;\n      const locationSetID = itemLocationSetID.get(hit.itemID!);\n      return locationSetID ? validHere.has(locationSetID) : false;\n    };\n\n    const tryMatch = (which: 'primary' | 'alternate' | 'exclude', kv: string): boolean => {\n      const branch = matchIndex.get(kv);\n      if (!branch) return false;\n\n      if (which === 'exclude') {  // Test name `n` against named and generic exclude patterns\n        let regex = [...branch.excludeNamed.values()].find(regex => regex.test(n));\n        if (regex) {\n          results.push({ match: 'excludeNamed', pattern: String(regex), kv: kv });\n          return false;\n        }\n        regex = [...branch.excludeGeneric.values()].find(regex => regex.test(n));\n        if (regex) {\n          results.push({ match: 'excludeGeneric', pattern: String(regex), kv: kv });\n          return false;\n        }\n        return false;\n      }\n\n      const leaf = branch[which].get(nsimple);\n      if (!leaf || !leaf.size) return false;\n      if (!(which === 'primary' || which === 'alternate')) return false;\n\n      // If we get here, we matched something..\n      // Prepare the results, calculate areas (if location index was set up)\n      let hits: Array<MatchHit> = [];\n      for (const itemID of [...leaf]) {\n        let area = Infinity;\n        if (loco && itemLocationSetID) {\n          const setID = itemLocationSetID.get(itemID);\n          if (setID) {\n            area = loco.getLocationSetArea(setID) ?? Infinity;\n          }\n        }\n        hits.push({ match: which, itemID: itemID, area: area, kv: kv, nsimple: nsimple });\n      }\n\n      let sortFn = byAreaDescending;\n\n      // Filter the match to include only results valid in the requested `loc`..\n      if (validHere) {\n        hits = hits.filter(isValidLocation);\n        sortFn = byAreaAscending;\n      }\n\n      if (!hits.length) return false;\n\n      // push results\n      for (const hit of hits.sort(sortFn)) {\n        if (seen.has(hit.itemID)) continue;\n        seen.add(hit.itemID);\n        results.push(hit);\n      }\n\n      return true;\n    };\n\n    const gatherResults = (which: 'primary' | 'alternate' | 'exclude'): void => {\n      // First try an exact match on k/v\n      const kv = `${k}/${v}`;\n      let didMatch = tryMatch(which, kv);\n      if (didMatch) return;\n\n      // If that didn't work, look in match groups for other pairs considered equivalent to k/v..\n      for (const matchGroup of Object.values(matchGroups)) {\n        const inGroup = matchGroup.some(otherkv => otherkv === kv);\n        if (!inGroup) continue;\n\n        for (const otherkv of matchGroup) {\n          if (otherkv === kv) continue;  // skip self\n          didMatch = tryMatch(which, otherkv);\n          if (didMatch) return;\n        }\n      }\n\n      // If finished 'exclude' pass and still haven't matched anything, try the global `genericWords.json` patterns\n      if (which === 'exclude') {\n        const regex = [...this.genericWords.values()].find(regex => regex.test(n));\n        if (regex) {\n          results.push({ match: 'excludeGeneric', pattern: String(regex) });  // note no `branch`, no `kv`\n          return;\n        }\n      }\n    };\n\n    gatherResults('primary');\n    gatherResults('alternate');\n    if (results.length) return results;\n\n    gatherResults('exclude');\n    return results.length ? results : null;\n  }\n\n\n  /**\n   * Returns any warnings discovered while building the match index\n   * (e.g. duplicate cache keys across trees).\n   *\n   * @returns An array of warning message strings (may be empty).\n   */\n  getWarnings(): Array<string> {\n    return this.warnings;\n  }\n}\n",
    "import diacritics from 'diacritics';\n\n/**\n * Simplifies a string by removing spaces, punctuation, and diacritics,\n * replacing `&` with `and`, and lowercasing the result.\n * Useful for generating normalized keys that can be compared across languages and scripts.\n *\n * For punctuation ranges see https://stackoverflow.com/a/21224179\n *\n * @param   str - The input string to simplify\n * @returns A normalized, lowercase string with all whitespace, punctuation, and diacritics removed.\n *          Returns an empty string if the input is not a string.\n */\nexport function simplify(str?: string): string {\n  if (typeof str !== 'string') return '';\n\n  return diacritics.remove(\n    str\n      .replace(/&/g, 'and')\n      .replace(/(İ|i̇)/ig, 'i')   // for BİM, İşbank - NSI#5017, NSI#8261\n      .replace(/[\\s\\-=_!\"#%'*{},.\\/:;?\\(\\)\\[\\]@\\\\$\\^*+<>«»~`’\\u00a1\\u00a7\\u00b6\\u00b7\\u00bf\\u037e\\u0387\\u055a-\\u055f\\u0589\\u05c0\\u05c3\\u05c6\\u05f3\\u05f4\\u0609\\u060a\\u060c\\u060d\\u061b\\u061e\\u061f\\u066a-\\u066d\\u06d4\\u0700-\\u070d\\u07f7-\\u07f9\\u0830-\\u083e\\u085e\\u0964\\u0965\\u0970\\u0af0\\u0df4\\u0e4f\\u0e5a\\u0e5b\\u0f04-\\u0f12\\u0f14\\u0f85\\u0fd0-\\u0fd4\\u0fd9\\u0fda\\u104a-\\u104f\\u10fb\\u1360-\\u1368\\u166d\\u166e\\u16eb-\\u16ed\\u1735\\u1736\\u17d4-\\u17d6\\u17d8-\\u17da\\u1800-\\u1805\\u1807-\\u180a\\u1944\\u1945\\u1a1e\\u1a1f\\u1aa0-\\u1aa6\\u1aa8-\\u1aad\\u1b5a-\\u1b60\\u1bfc-\\u1bff\\u1c3b-\\u1c3f\\u1c7e\\u1c7f\\u1cc0-\\u1cc7\\u1cd3\\u2000-\\u206f\\u2cf9-\\u2cfc\\u2cfe\\u2cff\\u2d70\\u2e00-\\u2e7f\\u3001-\\u3003\\u303d\\u30fb\\ua4fe\\ua4ff\\ua60d-\\ua60f\\ua673\\ua67e\\ua6f2-\\ua6f7\\ua874-\\ua877\\ua8ce\\ua8cf\\ua8f8-\\ua8fa\\ua92e\\ua92f\\ua95f\\ua9c1-\\ua9cd\\ua9de\\ua9df\\uaa5c-\\uaa5f\\uaade\\uaadf\\uaaf0\\uaaf1\\uabeb\\ufe10-\\ufe16\\ufe19\\ufe30\\ufe45\\ufe46\\ufe49-\\ufe4c\\ufe50-\\ufe52\\ufe54-\\ufe57\\ufe5f-\\ufe61\\ufe68\\ufe6a\\ufe6b\\ufeff\\uff01-\\uff03\\uff05-\\uff07\\uff0a\\uff0c\\uff0e\\uff0f\\uff1a\\uff1b\\uff1f\\uff20\\uff3c\\uff61\\uff64\\uff65]+/g,'')\n      .toLowerCase()\n  );\n}\n",
    "import { simplify } from './simplify.ts';\n\n\n/**\n * Removes common \"noise\" words from a name string and then simplifies the result.\n * Used to generate a stem for catching near-duplicate names\n * (e.g. \"First National Bank\" and \"First National\" would produce the same stem).\n *\n * Noise words removed: bank/banc/banco, банк, coop, express, gas/fuel, wireless, shop/store.\n *\n * @param   str - The input name string to stem\n * @returns A simplified, de-noised string. Returns an empty string if the input is not a string.\n */\nexport function stemmer(str?: string): string {\n  if (typeof str !== 'string') return '';\n\n  const noise = [\n    /ban(k|c)(a|o)?/ig,\n    /банк/ig,\n    /coop/ig,\n    /express/ig,\n    /(gas|fuel)/ig,\n    /wireless/ig,\n    /(shop|store)/ig\n  ];\n\n  str = noise.reduce((acc, regex) => acc.replace(regex, ''), str);\n  return simplify(str);\n}\n",
    "const withLocale = new Intl.Collator('en-US').compare;  // specify 'en-US' for stable sorting\n\n\n/**\n * Returns a shallow copy of an object with its keys sorted and any array values sorted.\n * Keys that look like Wikidata QIDs (e.g. `Q42`) are sorted numerically;\n * all other keys are sorted with en-US locale collation.\n * This is useful for producing deterministic JSON output for file diffing.\n *\n * @param   obj - The input object to sort\n * @returns A new object with sorted keys and sorted array values, or `null` if the input is falsy.\n */\nexport function sortObject<T extends object>(obj: T): T;\nexport function sortObject<T extends Record<string, unknown>>(obj: T): T | null {\n  if (!obj) return null;\n\n  const sorted: Record<string, unknown> = {};\n  const keys = Object.keys(obj).sort(keyCompare);\n  for (const k of keys) {\n    const v = obj[k];\n    sorted[k] = Array.isArray(v) ? v.sort(withLocale) : v;\n  }\n  return sorted as T;\n\n\n  /**\n   * Compares two object keys, sorting Wikidata QIDs (`Q123`) numerically\n   * and everything else with en-US locale collation.\n   *\n   * @param   a - First key\n   * @param   b - Second key\n   * @returns Negative if a < b, positive if a > b, zero if equal\n   */\n  function keyCompare(a: string, b: string): number {\n    const qid = /^Q(\\d+)$/;\n    const aMatch = a.match(qid);\n    const bMatch = b.match(qid);\n    if (aMatch && bMatch) {\n      return parseInt(aMatch[1], 10) - parseInt(bMatch[1], 10);   // sort QIDs numerically\n    } else {\n      return withLocale(a, b);\n    }\n  }\n}\n",
    "import type { Preset, Presets } from '@openstreetmap/id-tagging-schema';\nimport { sortObject } from './sort_object.ts';\n\nimport type {\n  DissolvedMap,\n  NsiData,\n  NsiPath,\n  NsiTree,\n  NsiTreeProperties,\n  OsmTags,\n  IDPreset,\n  WikidataMap\n} from './types.ts';\n\nconst withLocale = new Intl.Collator('en-US').compare;  // specify 'en-US' for stable sorting\n\n// Imported JSON (will be inlined by bun)\nimport treesJSON from '../config/trees.json' with {type: 'json'};\nconst trees: Record<NsiTree, NsiTreeProperties> = treesJSON.trees;\n\n\n/** Options for {@link buildIDPresets}. */\nexport interface BuildIDPresetsOptions {\n  /** The id-tagging-schema source presets dictionary (the `presets` field of `presets.json`). */\n  sourcePresets: Presets;\n  /** Map of QID → wikidata info, used to source preset `imageURL` values. */\n  wikidata?: WikidataMap;\n  /** Map of NSI item id → dissolved record. Items present here become non-searchable. */\n  dissolved?: DissolvedMap;\n}\n\n/** Result of {@link buildIDPresets}. */\nexport interface BuildIDPresetsResult {\n  /** The generated iD/Rapid presets, keyed by `<presetID>/<itemID>`. */\n  presets: Record<string, IDPreset>;\n  /** Sorted list of NSI `tree/key/value` paths for which no source iD preset was found. */\n  missing: NsiPath[];\n}\n\n\n// Exceptions where the NSI `key/value` doesn't match the iD preset path `key/value`.\n// See also https://github.com/openstreetmap/iD/issues/11527\n// id-tagging-schema occasionally moves their presets around, changing their presetIDs.\nconst presetPathOverrides: Record<string, string> = {\n  'highway/bus_stop':        'public_transport/platform/bus_point',\n  'amenity/ferry_terminal':  'public_transport/station_ferry',\n  'amenity/college':         'education/college',\n  'amenity/driving_school':  'education/driving_school',\n  'amenity/dancing_school':  'education/dancing_school',\n  'amenity/kindergarten':    'education/kindergarten',\n  'amenity/language_school': 'education/language_school',\n  'amenity/music_school':    'education/music_school',\n  'amenity/prep_school':     'education/prep_school',\n  'amenity/school':          'education/school',\n  'amenity/university':      'education/university',\n  'emergency/water_rescue':  'emergency/lifeboat_station'\n};\n\n// Tags that NSI allows to process as multi-valued\nconst semicolonSplittedKeys = ['beauty', 'clothes', 'cuisine', 'healthcare:speciality', 'social_facility', 'sport', 'vending', 'waste'];\n\n// Prefer a wiki commons logo for these QIDs.\n// Related issues list: iD#6361, NSI#2798, NSI#3122, NSI#8042, NSI#8373\nconst preferCommons: Record<string, boolean> = {\n  Q177054: true,    // Burger King\n  Q524757: true,    // KFC\n  Q779845: true,    // CBA\n  Q1205312: true,   // In-N-Out\n  Q10443115: true,  // Carlings\n  Q38076: true      // McDonald's\n};\n\n\n/** Resolves the iD preset path to look under for a given NSI tkv. */\nfunction resolvePresetPath(tkv: NsiPath, k: string, v: string, kv: string, ferryIndex: number): string {\n  if (tkv === 'transit/route/ferry') {\n    return ferryIndex === 0 ? 'type/route/ferry' : 'route/ferry';\n  }\n  if (k === 'route') return `type/route/${v}`;\n  return presetPathOverrides[kv] || kv;\n}\n\n\n/** Picks the most specific iD preset matching an NSI item's tags. */\nfunction pickBestChildPreset(\n  childPresets: Map<string, Preset>,\n  tags: OsmTags\n): { presetID?: string; preset?: Preset } {\n  if (childPresets.size === 0) return {};\n\n  if (childPresets.size === 1) {\n    const entry = childPresets.entries().next().value;\n    return entry ? { presetID: entry[0], preset: entry[1] } : {};\n  }\n\n  // The best iD preset for an NSI entry is determined by count of tags that have\n  // matched (more is better) and position for multi-value tags (e.g. cuisine)\n  let matchTagsCount = 0;\n  let matchSemicolonRating = 0;\n  let matchPresetPath: string | undefined;\n  let matchPreset: Preset | undefined;\n\n  for (const [checkPresetPath, checkPreset] of childPresets) {\n    const checkPresetTags = Object.entries(checkPreset.tags as OsmTags);\n    let currentMatchSemicolonRating = 0;\n\n    const isPresetMatch = checkPresetTags.every(kv => {\n      const osmKey = kv[0];\n      const osmVal = kv[1];\n\n      const nsiVal = tags[osmKey];\n      if (!nsiVal) return false;\n\n      if (semicolonSplittedKeys.includes(osmKey)) {\n        const vals = nsiVal.split(';');\n        const findResult = vals.indexOf(osmVal);\n        if (findResult === -1) return false;\n        // For a smaller element index rating will be higher\n        currentMatchSemicolonRating -= findResult;\n        return true;\n      }\n      return (osmVal === nsiVal);\n    });\n\n    // If rating of current element is higher than the saved one, we overwrite saved\n    if (isPresetMatch && (\n      (checkPresetTags.length > matchTagsCount) ||\n      (checkPresetTags.length === matchTagsCount && currentMatchSemicolonRating > matchSemicolonRating)\n    )) {\n      matchTagsCount = checkPresetTags.length;\n      matchSemicolonRating = currentMatchSemicolonRating;\n      matchPresetPath = checkPresetPath;\n      matchPreset = checkPreset;\n    }\n  }\n\n  return matchPreset && matchPresetPath\n    ? { presetID: matchPresetPath, preset: matchPreset }\n    : {};\n}\n\n\n/** Picks a logo URL from wikidata for a given QID. */\nfunction pickLogoURL(qid: string, wikidata: WikidataMap): string | undefined {\n  const logoURLs = wikidata[qid] && wikidata[qid].logos;\n  if (!logoURLs) return undefined;\n  if (logoURLs.wikidata && preferCommons[qid]) return logoURLs.wikidata;\n  if (logoURLs.facebook) return logoURLs.facebook;\n  return logoURLs.wikidata;\n}\n\n\n/**\n * Collects search terms for an NSI item — its matchNames plus name-like tag values.\n */\nfunction collectTerms(\n  item: { matchNames?: string[]; tags: OsmTags },\n  primaryName: RegExp,\n  alternateName: RegExp,\n  notName: RegExp\n): Set<string> {\n  const terms = new Set(item.matchNames || []);\n  for (const osmkey of Object.keys(item.tags)) {\n    if (osmkey === 'name') continue;      // exclude `name` tag, as iD prioritizes it above `preset.terms` already\n    if (notName.test(osmkey)) continue;   // osmkey is not a namelike tag, skip\n    if (primaryName.test(osmkey) || alternateName.test(osmkey)) {\n      terms.add(item.tags[osmkey].toLowerCase());\n    }\n  }\n  return terms;\n}\n\n\n/**\n * Returns the `fields` array for an NSI preset when `^name` is being preserved,\n * or `undefined` otherwise.\n *\n * If we're preserving the `name` tag, make sure both \"name\" and \"brand\"/\"operator\"\n * fields are shown.  This triggers iD to lock the brand/operator field but allow\n * edits to the \"name\" field.\n */\nfunction buildFields(t: string, preset: Preset & { originalFields?: string[] }, preserveTags: string[]): string[] {\n  const fields = preset.originalFields || preset.fields || [];\n\n  if (!preserveTags.some(s => s === '^name')) return fields;\n  // `originalFields` is preferred over `fields` to be backwards compatible with old versions\n  // of iD (released between May 2026 and July 2026). This can eventually be removed once there\n  // is no one using these old versions of iD.\n  if (t === 'brands')    return ['name', 'brand', ...fields];\n  if (t === 'operators') return ['name', 'operator', ...fields];\n  return fields;\n}\n\n\n/**\n * Build iD/Rapid presets from NSI data.\n *\n * This is a pure function: it does no I/O, performs no console output, and does not\n * mutate any of its inputs. Suitable for use in browser-based downstream projects\n * that fetch the NSI data on-the-fly.\n *\n * @param data - NSI category data indexed by `tree/key/value` path (the `_nsi.path` cache, or `nsi.json`'s `nsi` field)\n * @param opts - Sources for id-tagging-schema presets, wikidata logos, and dissolutions\n * @returns the generated presets plus a list of paths missing a source iD preset\n */\nexport function buildIDPresets(data: NsiData, opts: BuildIDPresetsOptions): BuildIDPresetsResult {\n  const sourcePresets = opts.sourcePresets;\n  const wikidata = opts.wikidata || {};\n  const dissolved = opts.dissolved || {};\n\n  //\n  // First we'll match every NSI item to a source iD preset.\n  // The source iD presets look like this:\n  //\n  // \"amenity\": {\n  //   \"name\": \"Amenity\"\n  //   \"fields\": […],\n  //   \"geometry\": […],\n  //   \"tags\": {\n  //     \"amenity\": \"*\"\n  //   },\n  //   \"searchable\": false\n  // },\n  // \"amenity/fast_food\": {\n  //   \"name\": \"Fast Food\",\n  //   \"icon\": \"maki-fast-food\",\n  //   \"fields\": […],\n  //   \"geometry\": […],\n  //   \"terms\": […],\n  //   \"tags\": {\n  //     \"amenity\": \"fast_food\"\n  //   }\n  // },\n  // \"amenity/fast_food/sandwich\": {\n  //   \"name\": \"Sandwich Fast Food\",\n  //   \"icon\": \"temaki-sandwich\",\n  //   \"fields\": […],\n  //   \"geometry\": […],\n  //   \"terms\": […],\n  //   \"tags\": {\n  //     \"amenity\": \"fast_food\",\n  //     \"cuisine\": \"sandwich\"\n  //   }\n  // },\n  //\n  // There are a few special behaviors in the iD presets are important to us:\n  // - They each have stable identifiers like `key`, `key/value`, `key/value/anothervalue`\n  // - Presets with increasing specificity \"inherit\" fields from presets of less specificity\n  //    (e.g. the sandwich fast food preset inherits all the fields of the regular fast food preset)\n  // - We can generate presets with NSI identifiers that hang off the end of this specificity chain\n  //    (e.g. \"amenity/fast_food/sandwich/arbys-3c08fb\")\n  // - NSI identifiers will not collide with the preset identifiers (NSI ids don't look like tag values)\n  //\n\n  const targetPresets: Record<string, Preset> = {};\n  const missing = new Set<string>();\n  const paths = Object.keys(data);\n\n  // Ferry hack! ⛴\n  // Append a duplicate tkv path for Ferry routes so we can generate them twice..\n  // These actually exist as 2 iD presets:\n  // `type/route/ferry` - for a Route Relation\n  // `route/ferry` - for a Way\n  let ferryCount = 0;\n  if (data['transit/route/ferry']) {\n    paths.push('transit/route/ferry');   // add a duplicate tkv\n  }\n\n  for (const tkv of paths.sort(withLocale) as NsiPath[]) {\n    const properties = data[tkv].properties || {};\n    const items = data[tkv].items;\n    if (!Array.isArray(items) || !items.length) continue;\n\n    const [t, k, v] = tkv.split('/', 3);   // tkv = \"tree/key/value\"\n    const tree = trees[t as NsiTree];\n    const kv = `${k}/${v}`;\n\n    // Ferry hack! ⛴ - duplicated tkv generates `type/route/ferry` then `route/ferry`\n    const ferryIndex = (tkv === 'transit/route/ferry') ? ferryCount++ : 0;\n    const presetPath = resolvePresetPath(tkv, k, v, kv, ferryIndex);\n\n    // Which wikidata tag is considered the \"main\" tag for this tree?\n    const wdTag = tree.mainTag;\n\n    // Primary/alternate names may be used as preset search terms\n    const primaryName = new RegExp(tree.nameTags.primary, 'i');\n    const alternateName = new RegExp(tree.nameTags.alternate, 'i');\n\n    // There are a few exceptions to the name matching regexes.\n    // Usually a tag suffix contains a language code like `name:en`, `name:ru`\n    // but we want to exclude things like `operator:type`, `name:etymology`, etc..\n    // NOTE: here we intentionally exclude `:wikidata`, in `matcher.ts` we do not.\n    const notName = /:(colour|type|left|right|etymology|pronunciation|wikipedia|wikidata)$/i;\n\n    // Look for iD presets that would fit this NSI presetPath.\n    const childPresets = new Map<string, Preset>();\n    for (const checkPath in sourcePresets) {\n      if (checkPath.startsWith(presetPath)) {\n        childPresets.set(checkPath, sourcePresets[checkPath]);\n      }\n    }\n\n    for (const item of items) {\n      const tags = item.tags;\n      const qid = tags[wdTag];\n      if (!qid || !/^Q\\d+$/.test(qid)) continue;   // wikidata tag missing or looks wrong..\n\n      // Sometimes we can choose a more specific iD preset than `key/value`,\n      // otherwise fall back to a generic like `amenity/yes`, `shop/yes`.\n      let { presetID, preset } = pickBestChildPreset(childPresets, tags);\n      if (!preset) {\n        presetID = k;\n        preset = sourcePresets[presetID];\n        missing.add(tkv);\n      }\n      if (!preset) continue;   // *still* no match - bail out\n\n      // Gather search terms - include all primary/alternate names and matchNames\n      // (There is similar code in lib/matcher.ts)\n      const terms = collectTerms(item, primaryName, alternateName, notName);\n\n      // generate our target preset\n      const targetID = `${presetID}/${item.id}`;\n\n      const targetTags: OsmTags = {};\n      targetTags[wdTag] = tags[wdTag]; // add the `*:wikidata` tag\n      for (const presetKey in preset.tags) {  // prioritize NSI tags over iD preset tags (for `vending`, `cuisine`, etc)\n        targetTags[presetKey] = tags[presetKey] || preset.tags[presetKey];\n      }\n\n      const logoURL = pickLogoURL(qid, wikidata);\n\n      const preserveTags = item.preserveTags || properties.preserveTags || [];\n\n      const targetPreset: IDPreset = {\n        name:         item.displayName,\n        locationSet:  item.locationSet as {} & IDPreset['locationSet'],\n        icon:         preset.icon!,\n        geometry:     preset.geometry,\n        fields:       buildFields(t, preset, preserveTags),\n        moreFields:   preset.moreFields || [],\n        tags:         sortObject(targetTags),\n        matchScore:   2\n      };\n\n      if (logoURL)             targetPreset.imageURL = logoURL;\n      if (terms.size)          targetPreset.terms = Array.from(terms).sort(withLocale);\n      if (preset.reference)    targetPreset.reference = preset.reference;\n      if (dissolved[item.id])  targetPreset.searchable = false;  // dissolved/closed businesses\n      if (preserveTags.length) targetPreset.preserveTags = preserveTags; // see NSI#10083\n\n      targetPreset.tags = sortObject(targetTags) as OsmTags;\n      targetPreset.addTags = sortObject(Object.assign({}, item.tags, targetTags)) as OsmTags;\n\n      targetPresets[targetID] = targetPreset;\n    }\n  }\n\n  return {\n    presets: targetPresets,\n    missing: Array.from(missing).sort(withLocale)\n  };\n}\n",
    "import XMLBuilder from 'fast-xml-builder';\n\nimport type { DissolvedMap, NsiData, NsiPath, NsiTree, NsiTreeProperties } from './types.ts';\nimport type { XmlBuilderOptions } from 'fast-xml-builder';\n\nconst xmlBuilderOptions = {\n  ignoreAttributes: false,\n  suppressEmptyNode: true\n} satisfies XmlBuilderOptions;\n\n// Imported JSON (will be inlined by bun)\nimport treesJSON from '../config/trees.json' with {type: 'json'};\n\nconst trees: Record<NsiTree, NsiTreeProperties> = treesJSON.trees;\nconst withLocale = new Intl.Collator('en-US').compare;  // specify 'en-US' for stable sorting\n\n\n/** Options for {@link buildJOSMPresets}. */\nexport interface BuildJOSMPresetsOptions {\n  /** Project version stamped into the `<presets version=\"...\">` attribute. */\n  version: string;\n  /** Project description stamped into the `<presets description=\"...\">` attribute. */\n  description: string;\n  /** Map of NSI item id → dissolved record. Items present here are excluded from the output. */\n  dissolved?: DissolvedMap;\n}\n\n/** Options for {@link JOSMPresetsSerializer.serialize}. */\nexport interface JOSMPresetsSerializerOptions {\n  /** If `true`, produce indented, human-readable XML; if absent or `false`, produce compact single-line XML. */\n  prettyPrint?: boolean;\n}\n\n/**\n * Returned by {@link buildJOSMPresets}. Holds the built-up preset data and can\n * serialize it to XML on demand, with or without pretty-printing.\n */\nexport interface JOSMPresetsSerializer {\n  /**\n   * Serialize the presets to an XML string beginning with the UTF-8 declaration.\n   * @param opts - Serialization options\n   * @returns An XML string. Pass `{ prettyPrint: true }` for indented output;\n   *          omit or pass `false` for compact single-line output.\n   */\n  serialize(opts?: JOSMPresetsSerializerOptions): string;\n}\n\ninterface JOSMKeyXML {\n  '@_key': string;\n  '@_value': string;\n}\n\ninterface JOSMItemXML {\n  '@_name': string;\n  '@_type': string;\n  key: JOSMKeyXML[];\n}\n\ninterface JOSMBranchGroupXML {\n  '@_name': string;\n  group: JOSMGroupXML[];\n}\n\ninterface JOSMLeafGroupXML {\n  '@_name': string;\n  item: JOSMItemXML[];\n}\n\ntype JOSMGroupXML = JOSMBranchGroupXML | JOSMLeafGroupXML;\n\ninterface JOSMPresetsXML {\n  '?xml': {\n    '@_version': '1.0';\n    '@_encoding': 'UTF-8';\n  };\n  presets: {\n    '@_xmlns': string;\n    '@_author': string;\n    '@_shortdescription': string;\n    '@_description': string;\n    '@_link': string;\n    '@_version': string;\n    group: JOSMBranchGroupXML;\n  };\n}\n\n\n/**\n * Build JOSM tagging presets from NSI data, organised into nested groups\n * by `tree → key → value`.\n *\n * Returns a {@link JOSMPresetsSerializer}; call `result.serialize({ prettyPrint: true })`\n * for indented output or `result.serialize()` for compact single-line output.\n *\n * This is a pure function: it does no I/O, performs no console output, and does not\n * mutate any of its inputs.\n *\n * @see https://josm.openstreetmap.de/wiki/TaggingPresets\n *\n * @param data - NSI category data indexed by `tree/key/value` path\n * @param opts - Project metadata and optional dissolution map\n * @returns A serializer that produces either pretty-printed or minified XML\n */\nexport function buildJOSMPresets(data: NsiData, opts: BuildJOSMPresetsOptions): JOSMPresetsSerializer {\n  const dissolved = opts.dissolved || {};\n\n  const topGroup: JOSMBranchGroupXML = { '@_name': 'Name Suggestion Index', group: [] };\n  const xml: JOSMPresetsXML = {\n    '?xml': { '@_version': '1.0', '@_encoding': 'UTF-8' },\n    presets: {\n      '@_xmlns': 'http://josm.openstreetmap.de/tagging-preset-1.0',\n      '@_author': 'Name Suggestion Index',\n      '@_shortdescription': 'Name Suggestion Index',\n      '@_description': opts.description,\n      '@_link': 'https://github.com/osmlab/name-suggestion-index',\n      '@_version': opts.version,\n      group: topGroup\n    }\n  };\n\n  let tPrev, kPrev, vPrev;\n  let tGroup: JOSMBranchGroupXML | undefined;\n  let kGroup: JOSMBranchGroupXML | undefined;\n  let vGroup: JOSMLeafGroupXML | undefined;\n\n  const paths = Object.keys(data).sort(withLocale) as NsiPath[];\n  for (const tkv of paths) {\n    const [t, k, v] = tkv.split('/', 3);     // tkv = \"tree/key/value\"\n\n    // Which wikidata tag is considered the \"main\" tag for this tree?\n    const wdTag = trees[t as NsiTree].mainTag;\n\n    // Include only items that have a wikidata tag and are not dissolved..\n    const items = (data[tkv].items || [])\n      .filter(item => {\n        const qid = item.tags[wdTag];\n        if (!qid || !/^Q\\d+$/.test(qid)) return false;   // wikidata tag missing or looks wrong..\n        if (dissolved[item.id]) return false;            // dissolved/closed businesses..\n        return true;\n      });\n\n    if (!items.length) continue;  // skip this path\n\n    // Create new menu groups as t/k/v change\n    const tChanged = t !== tPrev;\n    const kChanged = tChanged || k !== kPrev;\n    const vChanged = kChanged || v !== vPrev;\n\n    if (tChanged) {\n      tGroup = { '@_name': t, group: [] };\n      topGroup.group.push(tGroup);\n    }\n    if (kChanged) {\n      kGroup = { '@_name': k, group: [] };\n      tGroup!.group.push(kGroup);\n    }\n    if (vChanged) {\n      vGroup = { '@_name': v, item: [] };\n      kGroup!.group.push(vGroup);\n    }\n\n    // Choose allowable geometries for the category\n    let presetType;\n    if (t === 'flags') {\n      presetType = 'node';\n    } else if (k === 'route') {\n      if (v === 'ferry') {  // Ferry hack! ⛴\n        presetType = 'way,closedway,relation';\n      } else {\n        presetType = 'relation';\n      }\n    } else if (k === 'power' && (v === 'line' || v === 'minor_line')) {\n      presetType = 'way,closedway';\n    } else if (k === 'power' && (v === 'pole' || v === 'tower')) {\n      presetType = 'node';\n    } else {\n      presetType = 'node,closedway,multipolygon';   // default for POIs\n    }\n\n    for (const item of items) {\n      vGroup!.item.push({\n        '@_name': item.displayName,\n        '@_type': presetType,\n        key: Object.entries(item.tags).map(([osmkey, osmvalue]) => ({\n          '@_key': osmkey,\n          '@_value': osmvalue\n        }))\n      });\n    }\n\n    tPrev = t;\n    kPrev = k;\n    vPrev = v;\n  }\n\n  return {\n    serialize(opts?: JOSMPresetsSerializerOptions): string {\n      const builder = new XMLBuilder({\n        ...xmlBuilderOptions,\n        format: opts?.prettyPrint === true\n      });\n      return builder.build(xml);\n    }\n  };\n}\n"
  ],
  "mappings": "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",
  "debugId": "B8BCD5248FFF3BA264756E2164756E21",
  "names": []
}