{"version":3,"file":"index.cjs","names":["#routeData","#Pattern","#withPattern","#url","#details","#route","#current","#events","#autoUpdate","#watchers","#summary","#getWatcherCleaner"],"sources":["../utility/functions.ts","../core/normalizers.ts","../utility/url.ts","../utility/index.ts","../core/router.ts"],"sourcesContent":["export const mapValues = <Value extends object, Result extends object>(\n  original: Value,\n  iteratee: (value: Value[keyof Value], key: keyof Value, original: Value) => Result,\n) => {\n  type Key = keyof Value;\n  type NewValues = Record<Key, Result>;\n\n  const values = Object(original);\n  const newValues = {} as NewValues;\n\n  Object.keys(values).forEach((key) => {\n    newValues[key as Key] = iteratee(values[key], key as Key, values);\n  });\n\n  return newValues;\n};\n\n/**\n * Checks if `string` ends with the given target string.\n */\nconst endsWith = (string: string, target: string, position?: number) => {\n  const { length } = string;\n  position = position === undefined ? length : +position;\n  if (position < 0 || position !== position) {\n    position = 0;\n  } else if (position > length) {\n    position = length;\n  }\n  const end = position;\n  position -= target.length;\n  return position >= 0 && string.slice(position, end) === target;\n};\n\nexport default endsWith;\n","import endsWith from \"$utility/functions\";\n\nexport const trailingSlash = {\n  pattern: \"{/}?\",\n  matchers: [\"/\", \"{/}\", \"{/}?\"],\n  matchAny(pathname: string) {\n    return !!this.matchers.filter((content) => endsWith(pathname, content)).length;\n  },\n};\n","import { trailingSlash } from \"$core/normalizers\";\n\ntype Value = string | number | bigint | boolean | undefined;\ntype Data = {\n  [key: string]: Value;\n};\nconst baseURL = self.location.origin;\nconst excludedQueryParams: Value[] = [undefined, \"\"];\n\nexport type URLBuilderOptions = {\n  params?: Data;\n  hash?: string;\n  queryParams?: Data;\n};\n\nexport const buildURL = (pathname: string, options?: URLBuilderOptions): URL => {\n  const { params = {}, queryParams = {}, hash } = { ...options };\n\n  // Exclude normalized pattern\n  if (trailingSlash.matchAny(pathname)) {\n    pathname = pathname.split(trailingSlash.pattern)[0];\n  }\n\n  const path = pathname\n    .split(\"/\")\n    .map((v) => {\n      const tokenName = v.replace(\":\", \"\");\n      const tokenValue = params[tokenName];\n\n      if (tokenValue && v.startsWith(\":\")) {\n        return v.replace(`:${tokenName}`, `${tokenValue}`);\n      }\n\n      return v;\n    })\n    .join(\"/\");\n\n  const url = new URL(path, baseURL);\n\n  for (const [name, value] of Object.entries(queryParams)) {\n    if (excludedQueryParams.includes(value)) continue;\n\n    url.searchParams.set(name, `${value}`);\n  }\n\n  if (hash) {\n    url.hash = hash;\n  }\n\n  return url;\n};\n","export * from \"./url\";\nexport * from \"./functions\";\n","import { Signal, signal, computed, effect } from \"@preact/signals-core\";\n\nimport { mapValues, buildURL } from \"$utility\";\n\nimport { RouteName, Data, RedirectMode, MainRedirectOptions, MetaValue } from \"./types\";\nimport { trailingSlash } from \"./normalizers\";\n\ntype Options<RN extends RouteName, FieldsMeta extends MetaValue> = {\n  /** Register your routes here. */\n  routes: Data<RN, FieldsMeta>;\n\n  /** Replace with ponyfill. */\n  URLPattern?: unknown;\n};\n\nconst getCurrentURL = () => new URL(self.location.toString());\n\nexport class CreateHistory<RN extends RouteName, FieldsMeta = MetaValue> {\n  #Pattern = URLPattern;\n\n  #url = signal(getCurrentURL());\n\n  /** `Reactive` */\n  #routeData: Signal<Data<RN, FieldsMeta>>;\n\n  /** `Reactive` */\n  #withPattern = computed(() =>\n    mapValues(this.#routeData.value, ({ pathname, hash, search, normalize = true, ...rest }) => {\n      const patternOptions = { hash, search, pathname };\n\n      if (normalize) {\n        if (!trailingSlash.matchAny(pathname)) {\n          patternOptions.pathname = `${pathname}{/}?`;\n        }\n      }\n\n      const pattern = new this.#Pattern(patternOptions);\n\n      return {\n        pattern,\n        normalize,\n        ...rest,\n      };\n    }),\n  );\n\n  /** `Reactive` */\n  #details = computed(() =>\n    mapValues(this.#withPattern.value, (routeData) => ({\n      ...routeData,\n\n      /** `Matched against current route` */\n      isMatch: routeData.pattern.test(this.#url.value),\n\n      /** `Matched against current route. In details` */\n      detail: routeData.pattern.exec(this.#url.value),\n    })),\n  );\n\n  /** `Reactive` */\n  #current = computed(() => {\n    const details = this.#details.value;\n\n    const routeNames = Object.keys(details) as RN[];\n\n    const route = routeNames\n      .map((name) => ({\n        name,\n        route: details[name],\n      }))\n      .filter(({ route }) => !route.ignore)\n      .find(({ route }) => route.isMatch);\n\n    return route?.name;\n  });\n\n  /** `Reactive` */\n  #route = computed(() => {\n    const url = this.#url.value;\n    const info = this.getCurrentDetail();\n\n    const detail = info?.detail;\n    const name = info?.name;\n\n    const is404 = info === undefined;\n    const params = {\n      ...detail?.pathname.groups,\n    };\n    const hash = url.hash;\n\n    const search = new URLSearchParams(url.search);\n    const queryParams = Object.fromEntries(search);\n\n    return {\n      name,\n      is404,\n      params,\n      queryParams,\n      hash,\n      info,\n    };\n  });\n\n  /** `Reactive` */\n  #summary = computed(() => ({\n    url: this.#url.value,\n    routeData: this.#routeData.value,\n    withPattern: this.#withPattern.value,\n    details: this.#details.value,\n    route: this.#route.value,\n    current: this.#current.value,\n  }));\n\n  /** Registered events. Invoked upon cleanup process */\n  #events: VoidFunction[] = [];\n\n  /** Registered effects. Invoked upon cleanup process */\n  #watchers: VoidFunction[] = [];\n\n  /** History API based router. */\n  constructor({ routes, URLPattern }: Options<RN, FieldsMeta>) {\n    // @ts-ignore\n    if (URLPattern) this.#Pattern = URLPattern;\n\n    this.#routeData = signal(routes);\n\n    this.#events.push(this.#autoUpdate());\n  }\n\n  #getWatcherCleaner = (watcher: VoidFunction) => {\n    return () => {\n      watcher();\n\n      this.#watchers = this.#watchers.filter((effect) => effect !== watcher);\n    };\n  };\n\n  /** Update upon URL change */\n  #autoUpdate = () => {\n    const action = () => {\n      this.update();\n    };\n\n    self.addEventListener(\"popstate\", action);\n\n    // Produce cleanup code\n    return () => {\n      self.removeEventListener(\"popstate\", action);\n    };\n  };\n\n  /** Overall detail */\n  get summaryState() {\n    return this.#summary.value;\n  }\n\n  /** Current route detail */\n  get routeState() {\n    return this.#route.value;\n  }\n\n  getDetail = (name: RN) => {\n    return this.#details.value[name];\n  };\n\n  getCurrentDetail = () => {\n    const name = this.#current.value;\n\n    if (!name) return;\n\n    return { name, ...this.getDetail(name) };\n  };\n\n  /** Refresh route information */\n  update = () => {\n    this.#url.value = getCurrentURL();\n  };\n\n  /** Check current route name */\n  on = (name: RN) => {\n    const currentRN = this.#current.value;\n\n    if (!currentRN) return false;\n\n    const { isMatch } = this.getDetail(currentRN);\n\n    return isMatch && currentRN === name;\n  };\n\n  /**\n   * Same as `on()` but accepts multiple route names.\n   *\n   * Returns `true` if one of them matches.\n   */\n  onOneOf = (names: RN[]) => {\n    const matches = names.map((name) => this.on(name));\n\n    return !!matches.filter(Boolean).length;\n  };\n\n  /**\n   * Similar to `on()` except this only check for route pattern.\n   *\n   * Whereas `on` consider options such as `ignore`.\n   */\n  onRouteMatch = (name: RN) => {\n    return this.getDetail(name).isMatch;\n  };\n\n  /** Jump between routes. */\n  redirect = (name: RN, options: MainRedirectOptions = {}) => {\n    const { hash, params, queryParams } = this.#route.value;\n\n    const { replace = false, ...rest } = (() => {\n      if (typeof options == \"function\") {\n        return options({\n          hash,\n          params,\n          queryParams,\n        });\n      }\n\n      return options;\n    })();\n\n    const { pattern } = this.getDetail(name);\n\n    const URL = buildURL(pattern.pathname, rest);\n\n    const method: RedirectMode = replace ? \"replaceState\" : \"pushState\";\n\n    history[method](null, \"\", URL);\n\n    this.update();\n  };\n\n  /** Watch: `summaryState` */\n  watchSummaryState = (\n    callback: (summaryState: CreateHistory<RN, FieldsMeta>[\"summaryState\"]) => void,\n  ) => {\n    const watcher = effect(() => callback(this.#summary.value));\n\n    this.#watchers.push(watcher);\n\n    return this.#getWatcherCleaner(watcher);\n  };\n\n  /** Watch: `routeState` */\n  watchRouteState = (\n    callback: (routeState: CreateHistory<RN, FieldsMeta>[\"routeState\"]) => void,\n  ) => {\n    const watcher = effect(() => callback(this.#route.value));\n\n    this.#watchers.push(watcher);\n\n    return this.#getWatcherCleaner(watcher);\n  };\n\n  /** De-register events, watchers */\n  destroy = () => {\n    this.#watchers.forEach((stop) => stop());\n    this.#events.forEach((stop) => stop());\n\n    this.#events = [];\n    this.#watchers = [];\n  };\n}\n\nexport default CreateHistory;\n"],"mappings":"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"}