{"id":50762050,"url":"https://github.com/givensuman/go-ufo","last_synced_at":"2026-06-11T11:01:28.967Z","repository":{"id":355517302,"uuid":"1220251011","full_name":"givensuman/go-ufo","owner":"givensuman","description":"a go module for url utils","archived":false,"fork":false,"pushed_at":"2026-05-04T01:24:11.000Z","size":46,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-04T03:26:52.728Z","etag":null,"topics":["go-module","url"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/givensuman/go-ufo","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/givensuman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-24T17:52:24.000Z","updated_at":"2026-05-04T01:24:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/givensuman/go-ufo","commit_stats":null,"previous_names":["givensuman/go-ufo"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/givensuman/go-ufo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/givensuman%2Fgo-ufo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/givensuman%2Fgo-ufo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/givensuman%2Fgo-ufo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/givensuman%2Fgo-ufo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/givensuman","download_url":"https://codeload.github.com/givensuman/go-ufo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/givensuman%2Fgo-ufo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34195117,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go-module","url"],"created_at":"2026-06-11T11:01:27.804Z","updated_at":"2026-06-11T11:01:28.941Z","avatar_url":"https://github.com/givensuman.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"./assets/logo.svg\" alt=\"go-ufo\" width=\"200\" /\u003e\n\u003c/div\u003e\n\n# go-ufo\n\nThis is a port of the [unjs.io/ufo](https://unjs.io/packages/ufo) package to Go.\n\nPlease note function implementations aim for interoperability with JavaScript. If you're looking for \npure Go just use [net/url](https://pkg.go.dev/net/url).\n\n## Install\n\n```bash\ngo get github.com/givensuman/go-ufo\n```\n\nImport:\n\n```go\nimport (\n  \"github.com/givensuman/go-ufo\"\n)\n```\n\n## Encoding Utils\n\n### `Encode(text string) string`\n\nEncodes characters that need to be encoded in the path, search and hash sections of the URL.\n\n### `Decode(text string) string`\n\nDecodes text, returning the original text if decoding fails.\n\n### `DecodePath(text string) string`\n\nDecodes path section of the URL, consistent with `EncodePath`.\n\n### `DecodeQueryKey(text string) string`\n\nDecodes query key, consistent with `EncodeQueryKey`.\n\n### `DecodeQueryValue(text string) string`\n\nDecodes query value, consistent with `EncodeQueryValue`.\n\n### `EncodeHash(text string) string`\n\nEncodes characters that need to be encoded in the hash section of the URL.\n\n### `EncodePath(text string) string`\n\nEncodes characters that need to be encoded in the path section of the URL.\n\n### `EncodeParam(text string) string`\n\nEncodes characters that need to be encoded in the path section of the URL as a param. Also encodes the slash (`/`) character.\n\n### `EncodeQueryValue(input string) string`\n\nEncodes characters that need to be encoded for query values in the query section of the URL.\n\n### `EncodeQueryKey(text string) string`\n\nEncodes characters that need to be encoded for query keys in the query section of the URL and also encodes the `=` character.\n\n### `EncodeHost(name string) string`\n\nEncodes hostname with punycode encoding, returning the original name if encoding fails.\n\n## Parsing Utils\n\n### `ParsePath(input string) ParsedPath`\n\nSplits an input string into pathname, search, and hash:\n\n```go\nParsePath(\"http://foo.com/foo?test=123#token\")\n// ParsedPath{ Pathname: \"http://foo.com/foo\", Search: \"?test=123\", Hash: \"#token\" }\n```\n\n### `ParseURL(input string, defaultProto string) ParsedURL`\n\nParses a URL string into a `ParsedURL`. If the input has no protocol and `defaultProto` is non-empty, it is prepended:\n\n```go\nParseURL(\"http://foo.com/foo?test=123#token\", \"\")\n// ParsedURL{ Protocol: \"http:\", Host: \"foo.com\", Pathname: \"/foo\", Search: \"?test=123\", Hash: \"#token\" }\n\nParseURL(\"foo.com/foo?test=123#token\", \"\")\n// ParsedURL{ Pathname: \"foo.com/foo\", Search: \"?test=123\", Hash: \"#token\" }\n\nParseURL(\"foo.com/foo?test=123#token\", \"https://\")\n// ParsedURL{ Protocol: \"https:\", Host: \"foo.com\", Pathname: \"/foo\", Search: \"?test=123\", Hash: \"#token\" }\n```\n\n`ParsedURL` implements `fmt.Stringer`, so you can call `.String()` to reconstruct the URL.\n\n### `ParseAuth(input string) ParsedAuth`\n\nSplits a `username:password` string into its components:\n\n```go\nParseAuth(\"user:pass\")\n// ParsedAuth{ Username: \"user\", Password: \"pass\" }\n```\n\n### `ParseHost(input string) ParsedHost`\n\nSplits a `hostname:port` string into its components:\n\n```go\nParseHost(\"foo.com:8080\")\n// ParsedHost{ Hostname: \"foo.com\", Port: \"8080\" }\n```\n\n### `ParseFilename(input string, opts ParseFilenameOptions) string`\n\nReturns the last path segment from the URL's pathname. With `opts.Strict = true`, only returns a name that contains a dot (extension):\n\n```go\nParseFilename(\"http://example.com/path/to/filename.ext\", ParseFilenameOptions{})\n// \"filename.ext\"\n\nParseFilename(\"/path/to/.hidden-file\", ParseFilenameOptions{ Strict: true })\n// \"\"\n```\n\n## Query Utils\n\n### `ParseQuery(parametersString string) map[string]any`\n\nParses and decodes a query string into a map. The input can include or omit the leading `?`:\n\n```go\nParseQuery(\"?foo=bar\u0026baz=qux\")\n// map[string]any{ \"foo\": \"bar\", \"baz\": \"qux\" }\n\nParseQuery(\"tags=javascript\u0026tags=web\u0026tags=dev\")\n// map[string]any{ \"tags\": []string{\"javascript\", \"web\", \"dev\"} }\n```\n\n### `EncodeQueryItem(key string, value any) string`\n\nEncodes a key-value pair into a URL query string. If the value is a `[]any`, it is encoded as multiple key-value pairs with the same key:\n\n```go\nEncodeQueryItem(\"message\", \"Hello World\")\n// \"message=Hello+World\"\n\nEncodeQueryItem(\"tags\", []any{\"javascript\", \"web\", \"dev\"})\n// \"tags=javascript\u0026tags=web\u0026tags=dev\"\n```\n\n## Utils\n\n### `CleanDoubleSlashes(input string) string`\n\nRemoves double slashes from the URL:\n\n```go\nCleanDoubleSlashes(\"//foo//bar//\") // \"/foo/bar/\"\n\nCleanDoubleSlashes(\"http://example.com/analyze//http://localhost:3000//\")\n// \"http://example.com/analyze/http://localhost:3000/\"\n```\n\n### `FilterQuery(input string, callback func(k string, v any) bool) string`\n\nFilters query params of the URL using a predicate:\n\n```go\nFilterQuery(\"/foo?bar=1\u0026baz=2\", func(k string, v any) bool {\n    return k != \"bar\"\n}) // \"/foo?baz=2\"\n```\n\n### `GetQuery(input string) map[string]any`\n\nParses and decodes the query object of an input URL into a map:\n\n```go\nGetQuery(\"http://foo.com/foo?test=123\u0026unicode=%E5%A5%BD\")\n// map[string]any{ \"test\": \"123\", \"unicode\": \"好\" }\n```\n\n### `HasLeadingSlash(input string) bool`\n\nChecks if the input has a leading slash:\n\n```go\nHasLeadingSlash(\"/foo\") // true\n```\n\n### `HasProtocol(inputString string, opts *HasProtocolOptions) bool`\n\nChecks if the input has a protocol. Use `opts.AcceptRelative = true` to accept relative URLs, and `opts.Strict = true` for strict RFC 3986 protocol matching:\n\n```go\nHasProtocol(\"https://example.com\", nil) // true\nHasProtocol(\"//example.com\", nil) // false\nHasProtocol(\"//example.com\", \u0026HasProtocolOptions{ AcceptRelative: true }) // true\nHasProtocol(\"data:text/plain\", nil) // true\nHasProtocol(\"data:text/plain\", \u0026HasProtocolOptions{ Strict: true }) // false\n```\n\n### `HasTrailingSlash(input string, respectQueryAndFragment *RespectQueryAndFragmentOption) bool`\n\nChecks if the input has a trailing slash:\n\n```go\nHasTrailingSlash(\"/foo/\", nil) // true\nHasTrailingSlash(\"/foo\", nil) // false\nHasTrailingSlash(\"/foo?query=true\", \u0026RespectQueryAndFragmentOption{ true }) // false\nHasTrailingSlash(\"/foo/?query=true\", RespectQueryAndFragmentOption) // true\n```\n\n### `IsEmptyURL(url string) bool`\n\nChecks if the input URL is empty or `/`.\n\n### `IsEqual(a string, b string, opts *IsEqualOptions) bool`\n\nChecks if two paths are equal regardless of encoding, trailing slash, and leading slash differences. Use `opts` for strict comparisons:\n\n```go\nIsEqual(\"/foo\", \"foo\", nil) // true\nIsEqual(\"foo/\", \"foo\", nil) // true\nIsEqual(\"/foo bar\", \"/foo%20bar\", nil) // true\n\n// Strict comparisons\nIsEqual(\"/foo\", \"foo\", \u0026IsEqualOptions{ StrictLeadingSlash: true }) // false\nIsEqual(\"foo/\", \"foo\", \u0026IsEqualOptions{ StrictTrailingSlash: true }) // false\nIsEqual(\"/foo bar\", \"/foo%20bar\", \u0026IsEqualOptions{ StrictEncoding: true }) // false\n```\n\n### `IsNonEmptyURL(url string) bool`\n\nChecks if the input URL is neither empty nor `/`.\n\n### `IsRelative(inputString string) bool`\n\nChecks if a path starts with `./` or `../`:\n\n```go\nIsRelative(\"./foo\") // true\n```\n\n### `IsSamePath(a string, b string) bool`\n\nChecks if two paths are equal. Trailing slash and encoding are normalized before comparison:\n\n```go\nIsSamePath(\"/foo\", \"/foo/\") // true\n```\n\n### `IsScriptProtocol(protocol string) bool`\n\nChecks if the input is any of the dangerous `blob:`, `data:`, `javascript:`, or `vbscript:` protocols:\n\n```go\nIsScriptProtocol(\"javascript:alert(1)\") // true\nIsScriptProtocol(\"https://example.com\") // false\n```\n\n### `JoinRelativeURL(input ...string) string`\n\nJoins multiple URL segments into a single URL, handling relative paths with `./` and `../`:\n\n```go\nJoinRelativeURL(\"/a\", \"../b\", \"./c\") // \"/b/c\"\n```\n\n### `JoinURL(base string, input ...string) string`\n\nJoins multiple URL segments into a single URL:\n\n```go\nJoinURL(\"a\", \"/b\", \"/c\") // \"a/b/c\"\n```\n\n### `NormalizeURL(input string) string`\n\nNormalizes the input URL by ensuring proper encoding and that the pathname starts with a slash:\n\n```go\nNormalizeURL(\"test?query=123 123#hash, test\")\n// \"test?query=123%20123#hash,%20test\"\n```\n\n### `ResolveURL(base string, inputs ...string) string`\n\nResolves multiple URL segments into a single URL:\n\n```go\nResolveURL(\"http://foo.com/foo?test=123#token\", \"bar\", \"baz\")\n// \"http://foo.com/foo/bar/baz?test=123#token\"\n```\n\n### `WithBase(input string, base string) string`\n\nEnsures the URL or pathname starts with base:\n\n```go\nWithBase(\"/foo/bar\", \"/foo\") // \"/foo/bar\"\nWithBase(\"/foo/bar\", \"/baz\") // \"/baz/foo/bar\"\n```\n\n### `WithFragment(input string, hash string) string`\n\nAdds or replaces the fragment section of the URL:\n\n```go\nWithFragment(\"/foo\", \"bar\") // \"/foo#bar\"\nWithFragment(\"/foo#bar\", \"baz\") // \"/foo#baz\"\nWithFragment(\"/foo#bar\", \"\") // \"/foo\"\n```\n\n### `WithHTTP(input string) string`\n\nAdds or replaces the URL protocol with `http://`:\n\n```go\nWithHTTP(\"https://example.com\") // \"http://example.com\"\n```\n\n### `WithHTTPS(input string) string`\n\nAdds or replaces the URL protocol with `https://`:\n\n```go\nWithHTTPS(\"http://example.com\") // \"https://example.com\"\n```\n\n### `WithLeadingSlash(input string) string`\n\nEnsures the URL or pathname has a leading slash:\n\n```go\nWithLeadingSlash(\"foo\") // \"/foo\"\n```\n\n### `WithoutBase(input string, base string) string`\n\nRemoves the base from the URL or pathname:\n\n```go\nWithoutBase(\"/foo/bar\", \"/foo\") // \"/bar\"\n```\n\n### `WithoutFragment(input string) string`\n\nRemoves the fragment section from the URL:\n\n```go\nWithoutFragment(\"http://example.com/foo?q=123#bar\")\n// \"http://example.com/foo?q=123\"\n```\n\n### `WithoutHost(input string) string`\n\nRemoves the host from the URL while preserving everything else:\n\n```go\nWithoutHost(\"http://example.com/foo?q=123#bar\")\n// \"/foo?q=123#bar\"\n```\n\n### `WithoutLeadingSlash(input string) string`\n\nRemoves the leading slash from the URL or pathname:\n\n```go\nWithoutLeadingSlash(\"/foo\") // \"foo\"\n```\n\n### `WithoutProtocol(input string) string`\n\nRemoves the protocol from the input:\n\n```go\nWithoutProtocol(\"http://example.com\") // \"example.com\"\n```\n\n### `WithoutTrailingSlash(input string, respectQueryAndFragment *RespectQueryAndFragmentOption) string`\n\nRemoves the trailing slash from the URL or pathname:\n\n```go\nWithoutTrailingSlash(\"/foo/\", nil) // \"/foo\"\nWithoutTrailingSlash(\"/path/?query=true\", \u0026RespectQueryAndFragmentOption{ true }) // \"/path?query=true\"\n```\n\n### `WithProtocol(input string, protocol string) string`\n\nAdds or replaces the protocol of the input URL:\n\n```go\nWithProtocol(\"http://example.com\", \"ftp://\") // \"ftp://example.com\"\n```\n\n### `WithQuery(input string, query QueryObject) string`\n\nAdds or replaces query params of the URL:\n\n```go\nWithQuery(\"/foo?page=a\", QueryObject{ \"token\": \"secret\" })\n// \"/foo?page=a\u0026token=secret\"\n```\n\n### `WithTrailingSlash(input string, respectQueryAndFragment *RespectQueryAndFragmentOption) string`\n\nEnsures the URL ends with a trailing slash:\n\n```go\nWithTrailingSlash(\"/foo\", nil) // \"/foo/\"\nWithTrailingSlash(\"/path?query=true\", \u0026RespectQueryAndFragmentOption{ true }) // \"/path/?query=true\"\n```\n\n## License\n\n[MIT](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgivensuman%2Fgo-ufo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgivensuman%2Fgo-ufo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgivensuman%2Fgo-ufo/lists"}