@h3ravel/url 1.0.1 → 1.0.3

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/README.md CHANGED
@@ -1,6 +1,20 @@
1
- # @h3ravel/url
1
+ <div align="center">
2
+ <a href="https://h3ravel.toneflix.net" target="_blank">
3
+ <img src="https://raw.githubusercontent.com/h3ravel/assets/refs/heads/main/logo-full.svg" width="200" alt="H3ravel Logo">
4
+ </a>
5
+ <h1 align="center"><a href="https://h3ravel.toneflix.net/guide/urls">H3ravel Url</a></h1>
2
6
 
3
- Request-aware URI builder and URL manipulation utilities for H3ravel framework.
7
+ [![Framework][ix]][lx]
8
+ [![URL Package Version][i1]][l1]
9
+ [![Downloads][d1]][l1]
10
+ [![Tests][tei]][tel]
11
+ [![License][lini]][linl]
12
+
13
+ </div>
14
+
15
+ # About H3ravel/url
16
+
17
+ Request-aware URI builder and URL manipulation utilities for [H3ravel](https://h3ravel.toneflix.net) framework.
4
18
 
5
19
  ## Installation
6
20
 
@@ -21,19 +35,19 @@ npm install @h3ravel/url
21
35
  ### Basic URL Creation
22
36
 
23
37
  ```typescript
24
- import { Url } from '@h3ravel/url'
38
+ import { Url } from '@h3ravel/url';
25
39
 
26
40
  // From string
27
- const url = Url.of('https://example.com/path')
41
+ const url = Url.of('https://example.com/path');
28
42
 
29
43
  // From path relative to app URL
30
- const url = Url.to('/users')
44
+ const url = Url.to('/users');
31
45
 
32
46
  // From named route
33
- const url = Url.route('users.show', { id: 1 })
47
+ const url = Url.route('users.show', { id: 1 });
34
48
 
35
49
  // From controller action
36
- const url = Url.action('UserController@index')
50
+ const url = Url.action('UserController@index');
37
51
  ```
38
52
 
39
53
  ### Fluent Builder API
@@ -46,7 +60,7 @@ const url = Url.of('https://example.com')
46
60
  .withPath('/users')
47
61
  .withQuery({ page: 2 })
48
62
  .withFragment('section-1')
49
- .toString()
63
+ .toString();
50
64
  // -> "http://test.com:8000/users?page=2#section-1"
51
65
  ```
52
66
 
@@ -54,29 +68,33 @@ const url = Url.of('https://example.com')
54
68
 
55
69
  ```typescript
56
70
  // Get current URL
57
- url().current()
71
+ url().current();
58
72
 
59
73
  // Get full URL with query string
60
- url().full()
74
+ url().full();
61
75
 
62
76
  // Get previous URL
63
- url().previous()
77
+ url().previous();
64
78
 
65
79
  // Get previous path only
66
- url().previousPath()
80
+ url().previousPath();
67
81
 
68
82
  // Get current query parameters
69
- url().query()
83
+ url().query();
70
84
  ```
71
85
 
72
86
  ### Signed URLs
73
87
 
74
88
  ```typescript
75
89
  // Create signed route URL
76
- const signedUrl = Url.signedRoute('users.show', { id: 1 })
90
+ const signedUrl = Url.signedRoute('users.show', { id: 1 });
77
91
 
78
92
  // Create temporary signed route URL (expires in 5 minutes)
79
- const tempUrl = Url.temporarySignedRoute('users.index', {}, Date.now() + 300000)
93
+ const tempUrl = Url.temporarySignedRoute(
94
+ 'users.index',
95
+ {},
96
+ Date.now() + 300000
97
+ );
80
98
  ```
81
99
 
82
100
  ## API Reference
@@ -108,6 +126,28 @@ const tempUrl = Url.temporarySignedRoute('users.index', {}, Date.now() + 300000)
108
126
  - `previousPath()` - Get previous request path
109
127
  - `query()` - Get current query parameters
110
128
 
129
+ ## Contributing
130
+
131
+ Thank you for considering contributing to the H3ravel framework! The [Contribution Guide](https://h3ravel.toneflix.net/contributing) can be found in the H3ravel documentation and will provide you with all the information you need to get started.
132
+
133
+ ## Code of Conduct
134
+
135
+ In order to ensure that the H3ravel community is welcoming to all, please review and abide by the [Code of Conduct](#).
136
+
137
+ ## Security Vulnerabilities
138
+
139
+ If you discover a security vulnerability within H3ravel, please send an e-mail to Legacy via hamzas.legacy@toneflix.ng. All security vulnerabilities will be promptly addressed.
140
+
111
141
  ## License
112
142
 
113
- MIT
143
+ The H3ravel framework is open-sourced software licensed under the [MIT license](LICENSE).
144
+
145
+ [ix]: https://img.shields.io/npm/v/%40h3ravel%2Fcore?style=flat-square&label=Framework&color=%230970ce
146
+ [lx]: https://www.npmjs.com/package/@h3ravel/core
147
+ [i1]: https://img.shields.io/npm/v/%40h3ravel%2Furl?style=flat-square&label=@h3ravel/url&color=%230970ce
148
+ [l1]: https://www.npmjs.com/package/@h3ravel/url
149
+ [d1]: https://img.shields.io/npm/dt/%40h3ravel%2Furl?style=flat-square&label=Downloads&link=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2F%40h3ravel%2Furl
150
+ [linl]: https://github.com/h3ravel/framework/blob/main/LICENSE
151
+ [lini]: https://img.shields.io/github/license/h3ravel/framework
152
+ [tel]: https://github.com/h3ravel/framework/actions/workflows/test.yml
153
+ [tei]: https://github.com/h3ravel/framework/actions/workflows/test.yml/badge.svg
package/dist/index.cjs CHANGED
@@ -432,5 +432,4 @@ exports.route = route;
432
432
  exports.signedRoute = signedRoute;
433
433
  exports.temporarySignedRoute = temporarySignedRoute;
434
434
  exports.to = to;
435
- exports.url = url;
436
- //# sourceMappingURL=index.cjs.map
435
+ exports.url = url;
package/dist/index.d.cts CHANGED
@@ -295,5 +295,4 @@ declare class UrlServiceProvider extends ServiceProvider {
295
295
  boot(): void;
296
296
  }
297
297
  //#endregion
298
- export { HelpersContract, RequestAwareHelpers, RequestAwareUrlContract, RouteParams, Url, UrlContract, UrlServiceProvider, action, createUrlHelper, createUrlHelpers, route, signedRoute, temporarySignedRoute, to, url };
299
- //# sourceMappingURL=index.d.cts.map
298
+ export { HelpersContract, RequestAwareHelpers, RequestAwareUrlContract, RouteParams, Url, UrlContract, UrlServiceProvider, action, createUrlHelper, createUrlHelpers, route, signedRoute, temporarySignedRoute, to, url };
package/dist/index.d.ts CHANGED
@@ -295,5 +295,4 @@ declare class UrlServiceProvider extends ServiceProvider {
295
295
  boot(): void;
296
296
  }
297
297
  //#endregion
298
- export { HelpersContract, RequestAwareHelpers, RequestAwareUrlContract, RouteParams, Url, UrlContract, UrlServiceProvider, action, createUrlHelper, createUrlHelpers, route, signedRoute, temporarySignedRoute, to, url };
299
- //# sourceMappingURL=index.d.ts.map
298
+ export { HelpersContract, RequestAwareHelpers, RequestAwareUrlContract, RouteParams, Url, UrlContract, UrlServiceProvider, action, createUrlHelper, createUrlHelpers, route, signedRoute, temporarySignedRoute, to, url };
package/dist/index.js CHANGED
@@ -396,5 +396,4 @@ var UrlServiceProvider = class extends ServiceProvider {
396
396
  };
397
397
 
398
398
  //#endregion
399
- export { RequestAwareHelpers, Url, UrlServiceProvider, action, createUrlHelper, createUrlHelpers, route, signedRoute, temporarySignedRoute, to, url };
400
- //# sourceMappingURL=index.js.map
399
+ export { RequestAwareHelpers, Url, UrlServiceProvider, action, createUrlHelper, createUrlHelpers, route, signedRoute, temporarySignedRoute, to, url };
package/package.json CHANGED
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "name": "@h3ravel/url",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Request-aware URI builder and URL manipulation utilities for H3ravel.",
5
+ "h3ravel": {
6
+ "providers": [
7
+ "UrlServiceProvider"
8
+ ]
9
+ },
5
10
  "type": "module",
6
11
  "main": "./dist/index.js",
7
12
  "types": "./dist/index.d.ts",
@@ -39,12 +44,12 @@
39
44
  "builder"
40
45
  ],
41
46
  "dependencies": {
42
- "@h3ravel/support": "^0.14.0",
43
- "@h3ravel/shared": "^0.22.1"
47
+ "@h3ravel/support": "^0.14.2",
48
+ "@h3ravel/shared": "^0.22.3"
44
49
  },
45
50
  "peerDependencies": {
46
- "@h3ravel/core": "^1.15.0",
47
- "@h3ravel/http": "^11.3.3"
51
+ "@h3ravel/http": "^11.3.4",
52
+ "@h3ravel/core": "^1.17.0"
48
53
  },
49
54
  "devDependencies": {
50
55
  "typescript": "^5.4.0"
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","names":["app: Application","path","url","query: Record<string, unknown>","routes: RouteDefinition[]","route","ConfigException","queryParams: Record<string, unknown>","path","ServiceProvider"],"sources":["../src/RequestAwareHelpers.ts","../src/Url.ts","../src/Helpers.ts","../src/Providers/UrlServiceProvider.ts"],"sourcesContent":["import type { Application } from '@h3ravel/core'\nimport type { IRequest } from '@h3ravel/shared'\nimport { RouteParams } from './Contracts/UrlContract'\n\n/**\n * Request-aware URL helper class\n */\nexport class RequestAwareHelpers {\n private readonly baseUrl: string = ''\n\n constructor(private app: Application) {\n try {\n this.baseUrl = config('app.url', 'http://localhost:3000')\n } catch {/** */ }\n }\n\n /**\n * Get the current request instance\n */\n private getCurrentRequest (): IRequest {\n const request = this.app.make('http.request')\n if (!request) {\n throw new Error('Request instance not available in current context')\n }\n return request\n }\n\n /**\n * Get the current request URL (path only, no query string)\n */\n current (): string {\n const request = this.getCurrentRequest()\n const event = request.getEvent()\n\n // Get the path from the request\n const raw = event.req.url ?? '/'\n const url = new URL(raw, 'http://localhost')\n return url.pathname\n }\n\n /**\n * Get the full current URL with query string\n */\n full (): string {\n const request = this.getCurrentRequest()\n const event = request.getEvent()\n\n // Get the full URL including query string\n const requestUrl = event.req.url ?? '/'\n\n // If requestUrl is already absolute, use it directly, otherwise combine with baseUrl\n if (requestUrl.startsWith('http')) {\n return requestUrl\n }\n\n const fullUrl = new URL(requestUrl, this.baseUrl)\n return fullUrl.toString()\n }\n\n /**\n * Get the previous request URL from session or referrer\n */\n previous (): string {\n const request = this.getCurrentRequest()\n const event = request.getEvent()\n\n // Try to get from session first (if session is available)\n // For now, fallback to HTTP referrer header\n const headers = (event as any)?.node?.req?.headers as Record<string, string | string[] | undefined> | undefined\n // console.log(headers)\n let referrer = headers?.referer ?? headers?.referrer\n if (Array.isArray(referrer)) referrer = referrer[0]\n if (referrer) return referrer\n\n // Fallback to current URL if no referrer\n return this.current()\n }\n\n /**\n * Get the previous request path (without query string)\n */\n previousPath (): string {\n const previousUrl = this.previous()\n\n try {\n const url = new URL(previousUrl)\n return url.pathname\n } catch {\n // If previous URL is not a valid URL, return as-is\n return previousUrl\n }\n }\n\n /**\n * Get the current query parameters\n */\n query (): RouteParams {\n const request = this.getCurrentRequest()\n return request.query || {}\n }\n}\n\n/**\n * Global helper function factory\n */\nexport function createUrlHelper (app: Application): () => RequestAwareHelpers {\n return () => new RequestAwareHelpers(app)\n}\n","import { ConfigException, type Application } from '@h3ravel/core'\nimport { RouteParams } from './Contracts/UrlContract'\nimport { hmac } from '@h3ravel/support'\nimport { RouteDefinition, ExtractControllerMethods } from '@h3ravel/shared'\nimport path from 'node:path'\n\n/**\n * URL builder class with fluent API and request-aware helpers\n */\nexport class Url {\n private readonly _scheme?: string\n private readonly _host?: string\n private readonly _port?: number\n private readonly _path: string\n private readonly _query: Record<string, unknown>\n private readonly _fragment?: string\n private readonly app?: Application\n\n private constructor(\n app?: Application,\n scheme?: string,\n host?: string,\n port?: number,\n path: string = '/',\n query: Record<string, unknown> = {},\n fragment?: string\n ) {\n this.app = app\n this._scheme = scheme\n this._host = host\n this._port = port\n this._path = path.startsWith('/') ? path : `/${path}`\n this._query = { ...query }\n this._fragment = fragment\n }\n\n /**\n * Create a URL from a full URL string\n */\n static of (url: string, app?: Application): Url {\n try {\n const parsed = new URL(url)\n const query: Record<string, unknown> = {}\n\n // Parse query parameters\n parsed.searchParams.forEach((value, key) => {\n query[key] = value\n })\n\n return new Url(\n app,\n parsed.protocol.replace(':', ''),\n parsed.hostname,\n parsed.port ? parseInt(parsed.port) : undefined,\n parsed.pathname || '/',\n query,\n parsed.hash ? parsed.hash.substring(1) : undefined\n )\n } catch {\n throw new Error(`Invalid URL: ${url}`)\n }\n }\n\n /**\n * Create a URL from a path relative to the app URL\n */\n static to (path: string, app?: Application): Url {\n let baseUrl = ''\n try {\n baseUrl = config('app.url', 'http://localhost:3000')\n } catch {/** */ }\n\n const fullUrl = new URL(path, baseUrl).toString()\n\n return Url.of(fullUrl, app)\n }\n\n /**\n * Create a URL from a named route\n */\n // Route parameter map (declaration-mergeable by consumers)\n static route<TName extends string = string, TParams extends RouteParams = RouteParams> (\n name: TName,\n params: TParams = {} as TParams,\n app?: Application\n ): Url {\n if (!app) {\n throw new Error('Application instance required for route generation')\n }\n\n // Use (app as any).make to avoid TS error if make is not typed on Application\n const router = app.make('router')\n if (!router || typeof router.route !== 'function') {\n throw new Error('Router not available or does not support route generation')\n }\n\n if (typeof router.route !== 'function') {\n throw new Error('Router does not support route generation')\n }\n\n const routeUrl = router.route(name, params)\n if (!routeUrl) {\n throw new Error(`Route \"${name}\" not found`)\n }\n\n return Url.to(routeUrl, app)\n }\n\n /**\n * Create a signed URL from a named route\n */\n static signedRoute<TName extends string = string, TParams extends RouteParams = RouteParams> (\n name: TName,\n params: TParams = {} as TParams,\n app?: Application\n ): Url {\n const url = Url.route<TName, TParams>(name, params, app)\n return url.withSignature(app)\n }\n\n /**\n * Create a temporary signed URL from a named route\n */\n static temporarySignedRoute<TName extends string = string, TParams extends RouteParams = RouteParams> (\n name: TName,\n params: TParams = {} as TParams,\n expiration: number,\n app?: Application\n ): Url {\n const url = Url.route<TName, TParams>(name, params, app)\n return url.withSignature(app, expiration)\n }\n\n /**\n * Create a URL from a controller action\n */\n static action<C extends new (...args: any) => any> (\n controller: string | [C, methodName: ExtractControllerMethods<InstanceType<C>>],\n params?: Record<string, any>,\n app?: Application\n ): Url {\n if (!app) throw new Error('Application instance required for action URL generation')\n\n const [controllerName, methodName = 'index'] = typeof controller === 'string'\n ? controller.split('@')\n : controller\n\n const cname = typeof controllerName === 'string' ? controllerName : controllerName.name\n\n const routes: RouteDefinition[] = app.make('app.routes')\n\n if (!Array.isArray(routes)) {\n // Backward-compatible message expected by existing tests\n throw new Error('Action URL generation requires router integration - not yet implemented')\n }\n\n if (routes.length < 1) throw new Error(`No routes available to resolve action: ${controller}`)\n\n // Search for for the \n const found = routes.find(route => {\n return route.signature?.[0] === cname && (route.signature?.[1] || 'index') === methodName\n })\n\n if (!found) throw new Error(`No route found for ${cname}`)\n\n // Build the route parameters\n const _params = Object.values(params ?? {}).join('/')\n\n if (_params) {\n return Url.to(path.join(found.path, _params))\n }\n\n return Url.to(found.path, app)\n }\n\n /**\n * Set the scheme (protocol) of the URL\n */\n withScheme (scheme: string): Url {\n return new Url(\n this.app,\n scheme,\n this._host,\n this._port,\n this._path,\n this._query,\n this._fragment\n )\n }\n\n /**\n * Set the host of the URL\n */\n withHost (host: string): Url {\n return new Url(\n this.app,\n this._scheme,\n host,\n this._port,\n this._path,\n this._query,\n this._fragment\n )\n }\n\n /**\n * Set the port of the URL\n */\n withPort (port: number): Url {\n return new Url(\n this.app,\n this._scheme,\n this._host,\n port,\n this._path,\n this._query,\n this._fragment\n )\n }\n\n /**\n * Set the path of the URL\n */\n withPath (path: string): Url {\n return new Url(\n this.app,\n this._scheme,\n this._host,\n this._port,\n path,\n this._query,\n this._fragment\n )\n }\n\n /**\n * Set the query parameters of the URL\n */\n withQuery (query: Record<string, unknown>): Url {\n return new Url(\n this.app,\n this._scheme,\n this._host,\n this._port,\n this._path,\n { ...query },\n this._fragment\n )\n }\n\n /**\n * Merge additional query parameters\n */\n withQueryParams (params: Record<string, unknown>): Url {\n return new Url(\n this.app,\n this._scheme,\n this._host,\n this._port,\n this._path,\n { ...this._query, ...params },\n this._fragment\n )\n }\n\n /**\n * Set the fragment (hash) of the URL\n */\n withFragment (fragment: string): Url {\n return new Url(\n this.app,\n this._scheme,\n this._host,\n this._port,\n this._path,\n this._query,\n fragment\n )\n }\n\n /**\n * Add a signature to the URL for security\n */\n withSignature (app?: Application, expiration?: number): Url {\n const appInstance = app || this.app\n if (!appInstance) {\n throw new Error('Application instance required for URL signing')\n }\n\n let key = ''\n try {\n key = config('app.key')\n } catch {/** */ }\n\n if (!key) {\n throw new ConfigException('APP_KEY and app.key', 'any', this)\n }\n const url = this.toString()\n const queryParams: Record<string, unknown> = { ...this._query }\n\n if (expiration) {\n queryParams.expires = Math.floor(expiration / 1000)\n }\n\n // Create signature payload\n const payload = expiration\n ? `${url}?expires=${queryParams.expires}`\n : url\n\n const signature = hmac(payload, key)\n queryParams.signature = signature\n\n return this.withQuery(queryParams)\n }\n\n /**\n * Verify if a URL signature is valid\n */\n hasValidSignature (app?: Application): boolean {\n const appInstance = app || this.app\n if (!appInstance) {\n return false\n }\n\n const signature = this._query.signature\n if (!signature) {\n return false\n }\n\n // Check expiration if present\n if (this._query.expires !== undefined && this._query.expires !== null) {\n const expiresStr = String(this._query.expires)\n const expirationTime = parseInt(expiresStr, 10) * 1000\n if (isNaN(expirationTime) || Date.now() > expirationTime) {\n return false\n }\n }\n\n // Recreate URL without signature for verification\n const queryWithoutSignature = { ...this._query }\n delete queryWithoutSignature.signature\n\n const urlWithoutSignature = new Url(\n this.app,\n this._scheme,\n this._host,\n this._port,\n this._path,\n queryWithoutSignature,\n this._fragment\n ).toString()\n\n const payload = this._query.expires\n ? `${urlWithoutSignature}?expires=${this._query.expires}`\n : urlWithoutSignature\n\n let key = ''\n try {\n key = config('app.key', 'default-key')\n } catch {/** */ }\n const expectedSignature = hmac(payload, key)\n\n return signature === expectedSignature\n }\n\n /**\n * Convert the URL to its string representation\n */\n toString (): string {\n let url = ''\n\n // Add scheme and host\n if (this._scheme && this._host) {\n url += `${this._scheme}://${this._host}`\n\n // Add port if specified and not default\n if (this._port &&\n !((this._scheme === 'http' && this._port === 80) ||\n (this._scheme === 'https' && this._port === 443))) {\n url += `:${this._port}`\n }\n }\n\n // Add path\n if (this._path) {\n if (!this._path.startsWith('/')) {\n url += '/'\n }\n url += this._path\n }\n\n // Add query parameters\n const queryEntries = Object.entries(this._query)\n if (queryEntries.length > 0) {\n const queryString = queryEntries\n .map(([key, value]) => {\n if (Array.isArray(value)) {\n return value.map(v => `${encodeURIComponent(key)}%5B%5D=${encodeURIComponent(v)}`).join('&')\n }\n return `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`\n })\n .join('&')\n url += `?${queryString}`\n }\n\n // Add fragment\n if (this._fragment) {\n url += `#${this._fragment}`\n }\n\n return url\n }\n\n /**\n * Get the scheme\n */\n getScheme (): string | undefined {\n return this._scheme\n }\n\n /**\n * Get the host\n */\n getHost (): string | undefined {\n return this._host\n }\n\n /**\n * Get the port\n */\n getPort (): number | undefined {\n return this._port\n }\n\n /**\n * Get the path\n */\n getPath (): string {\n return this._path\n }\n\n /**\n * Get the query parameters\n */\n getQuery (): Record<string, unknown> {\n return { ...this._query }\n }\n\n /**\n * Get the fragment\n */\n getFragment (): string | undefined {\n return this._fragment\n }\n}\n","import { Application } from '@h3ravel/core'\nimport { HelpersContract } from './Contracts/UrlContract'\nimport { RequestAwareHelpers } from './RequestAwareHelpers'\nimport { Url } from './Url'\nimport { ExtractControllerMethods } from '@h3ravel/shared'\n\n/**\n * Global helper functions for URL manipulation\n */\n\n/**\n * Create a URL from a path relative to the app URL\n */\nexport function to (\n path: string,\n app?: Application\n): Url {\n return Url.to(path, app)\n}\n\n/**\n * Create a URL from a named route\n */\nexport function route<TName extends string, TParams extends Record<string, string> = Record<string, string>> (\n name: TName,\n params: TParams = {} as TParams,\n app?: Application\n): Url {\n return Url.route<TName, TParams>(name, params, app)\n}\n\n/**\n * Create a signed URL from a named route\n */\nexport function signedRoute<TName extends string, TParams extends Record<string, string> = Record<string, string>> (\n name: TName,\n params: TParams = {} as TParams,\n app?: Application\n): Url {\n return Url.signedRoute<TName, TParams>(name, params, app)\n}\n\n/**\n * Create a temporary signed URL from a named route\n */\nexport function temporarySignedRoute (\n name: string,\n params: Record<string, string> = {},\n expiration: number,\n app?: Application\n): Url {\n return Url.temporarySignedRoute(name, params, expiration, app)\n}\n\n/**\n * Create a URL from a controller action\n */\nexport function action (\n controller: string,\n app?: Application\n): Url {\n return Url.action(controller, app)\n}\n\n/**\n * Get request-aware URL helpers\n */\nexport function url (app?: Application): RequestAwareHelpers {\n if (!app) throw new Error('Application instance required for request-aware URL helpers')\n return new RequestAwareHelpers(app)\n}\n\n/**\n * Create URL helpers that are bound to an application instance\n */\nexport function createUrlHelpers (app: Application): HelpersContract {\n return {\n /**\n * Create a URL from a path relative to the app URL\n */\n to: (path: string) => Url.to(path, app),\n\n /**\n * Create a URL from a named route\n */\n route: (\n name: string,\n params: Record<string, any> = {}\n ) => Url.route(name, params, app).toString(),\n\n /**\n * Create a signed URL from a named route\n */\n signedRoute: (\n name: string,\n params: Record<string, any> = {}\n ) => Url.signedRoute(name, params, app),\n\n /**\n * Create a temporary signed URL from a named route\n */\n temporarySignedRoute: (\n name: string,\n params: Record<string, any> = {},\n expiration: number\n ) => Url.temporarySignedRoute(name, params, expiration, app),\n\n /**\n * Create a URL from a controller action\n */\n action: <C extends new (...args: any) => any> (\n controller: string | [C, methodName: ExtractControllerMethods<InstanceType<C>>],\n params?: Record<string, any>\n ) => Url.action(controller, params, app).toString(),\n\n /**\n * Get request-aware URL helpers\n */\n url: (path?: string) => {\n if (path) {\n return Url.to(path).toString() as never\n }\n return new RequestAwareHelpers(app) as never\n }\n }\n}\n","/// <reference path=\"../app.globals.d.ts\" />\nimport { ServiceProvider } from '@h3ravel/core'\nimport { Url } from '../Url'\nimport { createUrlHelper } from '../RequestAwareHelpers'\nimport { createUrlHelpers } from '../Helpers'\n\n/**\n * Service provider for URL utilities\n */\nexport class UrlServiceProvider extends ServiceProvider {\n public static priority = 897\n\n /**\n * Register URL services in the container\n */\n register (): void {\n // Register the Url class\n this.app.singleton('app.url', () => Url)\n // Register the url() helper function\n this.app.singleton('app.url.helper', () => createUrlHelper(this.app))\n\n // Register bound URL helpers\n this.app.singleton('app.url.helpers', () => createUrlHelpers(this.app))\n\n // Make url() globally available\n if (typeof globalThis !== 'undefined') {\n const helpers = createUrlHelpers(this.app)\n\n Object.assign(globalThis, {\n url: helpers.url,\n route: helpers.route,\n action: helpers.action,\n })\n }\n }\n\n /**\n * Boot URL services\n */\n boot (): void {\n // Any additional setup can be done here\n }\n}\n"],"mappings":"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"}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","names":["app: Application","path","url","query: Record<string, unknown>","routes: RouteDefinition[]","route","queryParams: Record<string, unknown>","path"],"sources":["../src/RequestAwareHelpers.ts","../src/Url.ts","../src/Helpers.ts","../src/Providers/UrlServiceProvider.ts"],"sourcesContent":["import type { Application } from '@h3ravel/core'\nimport type { IRequest } from '@h3ravel/shared'\nimport { RouteParams } from './Contracts/UrlContract'\n\n/**\n * Request-aware URL helper class\n */\nexport class RequestAwareHelpers {\n private readonly baseUrl: string = ''\n\n constructor(private app: Application) {\n try {\n this.baseUrl = config('app.url', 'http://localhost:3000')\n } catch {/** */ }\n }\n\n /**\n * Get the current request instance\n */\n private getCurrentRequest (): IRequest {\n const request = this.app.make('http.request')\n if (!request) {\n throw new Error('Request instance not available in current context')\n }\n return request\n }\n\n /**\n * Get the current request URL (path only, no query string)\n */\n current (): string {\n const request = this.getCurrentRequest()\n const event = request.getEvent()\n\n // Get the path from the request\n const raw = event.req.url ?? '/'\n const url = new URL(raw, 'http://localhost')\n return url.pathname\n }\n\n /**\n * Get the full current URL with query string\n */\n full (): string {\n const request = this.getCurrentRequest()\n const event = request.getEvent()\n\n // Get the full URL including query string\n const requestUrl = event.req.url ?? '/'\n\n // If requestUrl is already absolute, use it directly, otherwise combine with baseUrl\n if (requestUrl.startsWith('http')) {\n return requestUrl\n }\n\n const fullUrl = new URL(requestUrl, this.baseUrl)\n return fullUrl.toString()\n }\n\n /**\n * Get the previous request URL from session or referrer\n */\n previous (): string {\n const request = this.getCurrentRequest()\n const event = request.getEvent()\n\n // Try to get from session first (if session is available)\n // For now, fallback to HTTP referrer header\n const headers = (event as any)?.node?.req?.headers as Record<string, string | string[] | undefined> | undefined\n // console.log(headers)\n let referrer = headers?.referer ?? headers?.referrer\n if (Array.isArray(referrer)) referrer = referrer[0]\n if (referrer) return referrer\n\n // Fallback to current URL if no referrer\n return this.current()\n }\n\n /**\n * Get the previous request path (without query string)\n */\n previousPath (): string {\n const previousUrl = this.previous()\n\n try {\n const url = new URL(previousUrl)\n return url.pathname\n } catch {\n // If previous URL is not a valid URL, return as-is\n return previousUrl\n }\n }\n\n /**\n * Get the current query parameters\n */\n query (): RouteParams {\n const request = this.getCurrentRequest()\n return request.query || {}\n }\n}\n\n/**\n * Global helper function factory\n */\nexport function createUrlHelper (app: Application): () => RequestAwareHelpers {\n return () => new RequestAwareHelpers(app)\n}\n","import { ConfigException, type Application } from '@h3ravel/core'\nimport { RouteParams } from './Contracts/UrlContract'\nimport { hmac } from '@h3ravel/support'\nimport { RouteDefinition, ExtractControllerMethods } from '@h3ravel/shared'\nimport path from 'node:path'\n\n/**\n * URL builder class with fluent API and request-aware helpers\n */\nexport class Url {\n private readonly _scheme?: string\n private readonly _host?: string\n private readonly _port?: number\n private readonly _path: string\n private readonly _query: Record<string, unknown>\n private readonly _fragment?: string\n private readonly app?: Application\n\n private constructor(\n app?: Application,\n scheme?: string,\n host?: string,\n port?: number,\n path: string = '/',\n query: Record<string, unknown> = {},\n fragment?: string\n ) {\n this.app = app\n this._scheme = scheme\n this._host = host\n this._port = port\n this._path = path.startsWith('/') ? path : `/${path}`\n this._query = { ...query }\n this._fragment = fragment\n }\n\n /**\n * Create a URL from a full URL string\n */\n static of (url: string, app?: Application): Url {\n try {\n const parsed = new URL(url)\n const query: Record<string, unknown> = {}\n\n // Parse query parameters\n parsed.searchParams.forEach((value, key) => {\n query[key] = value\n })\n\n return new Url(\n app,\n parsed.protocol.replace(':', ''),\n parsed.hostname,\n parsed.port ? parseInt(parsed.port) : undefined,\n parsed.pathname || '/',\n query,\n parsed.hash ? parsed.hash.substring(1) : undefined\n )\n } catch {\n throw new Error(`Invalid URL: ${url}`)\n }\n }\n\n /**\n * Create a URL from a path relative to the app URL\n */\n static to (path: string, app?: Application): Url {\n let baseUrl = ''\n try {\n baseUrl = config('app.url', 'http://localhost:3000')\n } catch {/** */ }\n\n const fullUrl = new URL(path, baseUrl).toString()\n\n return Url.of(fullUrl, app)\n }\n\n /**\n * Create a URL from a named route\n */\n // Route parameter map (declaration-mergeable by consumers)\n static route<TName extends string = string, TParams extends RouteParams = RouteParams> (\n name: TName,\n params: TParams = {} as TParams,\n app?: Application\n ): Url {\n if (!app) {\n throw new Error('Application instance required for route generation')\n }\n\n // Use (app as any).make to avoid TS error if make is not typed on Application\n const router = app.make('router')\n if (!router || typeof router.route !== 'function') {\n throw new Error('Router not available or does not support route generation')\n }\n\n if (typeof router.route !== 'function') {\n throw new Error('Router does not support route generation')\n }\n\n const routeUrl = router.route(name, params)\n if (!routeUrl) {\n throw new Error(`Route \"${name}\" not found`)\n }\n\n return Url.to(routeUrl, app)\n }\n\n /**\n * Create a signed URL from a named route\n */\n static signedRoute<TName extends string = string, TParams extends RouteParams = RouteParams> (\n name: TName,\n params: TParams = {} as TParams,\n app?: Application\n ): Url {\n const url = Url.route<TName, TParams>(name, params, app)\n return url.withSignature(app)\n }\n\n /**\n * Create a temporary signed URL from a named route\n */\n static temporarySignedRoute<TName extends string = string, TParams extends RouteParams = RouteParams> (\n name: TName,\n params: TParams = {} as TParams,\n expiration: number,\n app?: Application\n ): Url {\n const url = Url.route<TName, TParams>(name, params, app)\n return url.withSignature(app, expiration)\n }\n\n /**\n * Create a URL from a controller action\n */\n static action<C extends new (...args: any) => any> (\n controller: string | [C, methodName: ExtractControllerMethods<InstanceType<C>>],\n params?: Record<string, any>,\n app?: Application\n ): Url {\n if (!app) throw new Error('Application instance required for action URL generation')\n\n const [controllerName, methodName = 'index'] = typeof controller === 'string'\n ? controller.split('@')\n : controller\n\n const cname = typeof controllerName === 'string' ? controllerName : controllerName.name\n\n const routes: RouteDefinition[] = app.make('app.routes')\n\n if (!Array.isArray(routes)) {\n // Backward-compatible message expected by existing tests\n throw new Error('Action URL generation requires router integration - not yet implemented')\n }\n\n if (routes.length < 1) throw new Error(`No routes available to resolve action: ${controller}`)\n\n // Search for for the \n const found = routes.find(route => {\n return route.signature?.[0] === cname && (route.signature?.[1] || 'index') === methodName\n })\n\n if (!found) throw new Error(`No route found for ${cname}`)\n\n // Build the route parameters\n const _params = Object.values(params ?? {}).join('/')\n\n if (_params) {\n return Url.to(path.join(found.path, _params))\n }\n\n return Url.to(found.path, app)\n }\n\n /**\n * Set the scheme (protocol) of the URL\n */\n withScheme (scheme: string): Url {\n return new Url(\n this.app,\n scheme,\n this._host,\n this._port,\n this._path,\n this._query,\n this._fragment\n )\n }\n\n /**\n * Set the host of the URL\n */\n withHost (host: string): Url {\n return new Url(\n this.app,\n this._scheme,\n host,\n this._port,\n this._path,\n this._query,\n this._fragment\n )\n }\n\n /**\n * Set the port of the URL\n */\n withPort (port: number): Url {\n return new Url(\n this.app,\n this._scheme,\n this._host,\n port,\n this._path,\n this._query,\n this._fragment\n )\n }\n\n /**\n * Set the path of the URL\n */\n withPath (path: string): Url {\n return new Url(\n this.app,\n this._scheme,\n this._host,\n this._port,\n path,\n this._query,\n this._fragment\n )\n }\n\n /**\n * Set the query parameters of the URL\n */\n withQuery (query: Record<string, unknown>): Url {\n return new Url(\n this.app,\n this._scheme,\n this._host,\n this._port,\n this._path,\n { ...query },\n this._fragment\n )\n }\n\n /**\n * Merge additional query parameters\n */\n withQueryParams (params: Record<string, unknown>): Url {\n return new Url(\n this.app,\n this._scheme,\n this._host,\n this._port,\n this._path,\n { ...this._query, ...params },\n this._fragment\n )\n }\n\n /**\n * Set the fragment (hash) of the URL\n */\n withFragment (fragment: string): Url {\n return new Url(\n this.app,\n this._scheme,\n this._host,\n this._port,\n this._path,\n this._query,\n fragment\n )\n }\n\n /**\n * Add a signature to the URL for security\n */\n withSignature (app?: Application, expiration?: number): Url {\n const appInstance = app || this.app\n if (!appInstance) {\n throw new Error('Application instance required for URL signing')\n }\n\n let key = ''\n try {\n key = config('app.key')\n } catch {/** */ }\n\n if (!key) {\n throw new ConfigException('APP_KEY and app.key', 'any', this)\n }\n const url = this.toString()\n const queryParams: Record<string, unknown> = { ...this._query }\n\n if (expiration) {\n queryParams.expires = Math.floor(expiration / 1000)\n }\n\n // Create signature payload\n const payload = expiration\n ? `${url}?expires=${queryParams.expires}`\n : url\n\n const signature = hmac(payload, key)\n queryParams.signature = signature\n\n return this.withQuery(queryParams)\n }\n\n /**\n * Verify if a URL signature is valid\n */\n hasValidSignature (app?: Application): boolean {\n const appInstance = app || this.app\n if (!appInstance) {\n return false\n }\n\n const signature = this._query.signature\n if (!signature) {\n return false\n }\n\n // Check expiration if present\n if (this._query.expires !== undefined && this._query.expires !== null) {\n const expiresStr = String(this._query.expires)\n const expirationTime = parseInt(expiresStr, 10) * 1000\n if (isNaN(expirationTime) || Date.now() > expirationTime) {\n return false\n }\n }\n\n // Recreate URL without signature for verification\n const queryWithoutSignature = { ...this._query }\n delete queryWithoutSignature.signature\n\n const urlWithoutSignature = new Url(\n this.app,\n this._scheme,\n this._host,\n this._port,\n this._path,\n queryWithoutSignature,\n this._fragment\n ).toString()\n\n const payload = this._query.expires\n ? `${urlWithoutSignature}?expires=${this._query.expires}`\n : urlWithoutSignature\n\n let key = ''\n try {\n key = config('app.key', 'default-key')\n } catch {/** */ }\n const expectedSignature = hmac(payload, key)\n\n return signature === expectedSignature\n }\n\n /**\n * Convert the URL to its string representation\n */\n toString (): string {\n let url = ''\n\n // Add scheme and host\n if (this._scheme && this._host) {\n url += `${this._scheme}://${this._host}`\n\n // Add port if specified and not default\n if (this._port &&\n !((this._scheme === 'http' && this._port === 80) ||\n (this._scheme === 'https' && this._port === 443))) {\n url += `:${this._port}`\n }\n }\n\n // Add path\n if (this._path) {\n if (!this._path.startsWith('/')) {\n url += '/'\n }\n url += this._path\n }\n\n // Add query parameters\n const queryEntries = Object.entries(this._query)\n if (queryEntries.length > 0) {\n const queryString = queryEntries\n .map(([key, value]) => {\n if (Array.isArray(value)) {\n return value.map(v => `${encodeURIComponent(key)}%5B%5D=${encodeURIComponent(v)}`).join('&')\n }\n return `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`\n })\n .join('&')\n url += `?${queryString}`\n }\n\n // Add fragment\n if (this._fragment) {\n url += `#${this._fragment}`\n }\n\n return url\n }\n\n /**\n * Get the scheme\n */\n getScheme (): string | undefined {\n return this._scheme\n }\n\n /**\n * Get the host\n */\n getHost (): string | undefined {\n return this._host\n }\n\n /**\n * Get the port\n */\n getPort (): number | undefined {\n return this._port\n }\n\n /**\n * Get the path\n */\n getPath (): string {\n return this._path\n }\n\n /**\n * Get the query parameters\n */\n getQuery (): Record<string, unknown> {\n return { ...this._query }\n }\n\n /**\n * Get the fragment\n */\n getFragment (): string | undefined {\n return this._fragment\n }\n}\n","import { Application } from '@h3ravel/core'\nimport { HelpersContract } from './Contracts/UrlContract'\nimport { RequestAwareHelpers } from './RequestAwareHelpers'\nimport { Url } from './Url'\nimport { ExtractControllerMethods } from '@h3ravel/shared'\n\n/**\n * Global helper functions for URL manipulation\n */\n\n/**\n * Create a URL from a path relative to the app URL\n */\nexport function to (\n path: string,\n app?: Application\n): Url {\n return Url.to(path, app)\n}\n\n/**\n * Create a URL from a named route\n */\nexport function route<TName extends string, TParams extends Record<string, string> = Record<string, string>> (\n name: TName,\n params: TParams = {} as TParams,\n app?: Application\n): Url {\n return Url.route<TName, TParams>(name, params, app)\n}\n\n/**\n * Create a signed URL from a named route\n */\nexport function signedRoute<TName extends string, TParams extends Record<string, string> = Record<string, string>> (\n name: TName,\n params: TParams = {} as TParams,\n app?: Application\n): Url {\n return Url.signedRoute<TName, TParams>(name, params, app)\n}\n\n/**\n * Create a temporary signed URL from a named route\n */\nexport function temporarySignedRoute (\n name: string,\n params: Record<string, string> = {},\n expiration: number,\n app?: Application\n): Url {\n return Url.temporarySignedRoute(name, params, expiration, app)\n}\n\n/**\n * Create a URL from a controller action\n */\nexport function action (\n controller: string,\n app?: Application\n): Url {\n return Url.action(controller, app)\n}\n\n/**\n * Get request-aware URL helpers\n */\nexport function url (app?: Application): RequestAwareHelpers {\n if (!app) throw new Error('Application instance required for request-aware URL helpers')\n return new RequestAwareHelpers(app)\n}\n\n/**\n * Create URL helpers that are bound to an application instance\n */\nexport function createUrlHelpers (app: Application): HelpersContract {\n return {\n /**\n * Create a URL from a path relative to the app URL\n */\n to: (path: string) => Url.to(path, app),\n\n /**\n * Create a URL from a named route\n */\n route: (\n name: string,\n params: Record<string, any> = {}\n ) => Url.route(name, params, app).toString(),\n\n /**\n * Create a signed URL from a named route\n */\n signedRoute: (\n name: string,\n params: Record<string, any> = {}\n ) => Url.signedRoute(name, params, app),\n\n /**\n * Create a temporary signed URL from a named route\n */\n temporarySignedRoute: (\n name: string,\n params: Record<string, any> = {},\n expiration: number\n ) => Url.temporarySignedRoute(name, params, expiration, app),\n\n /**\n * Create a URL from a controller action\n */\n action: <C extends new (...args: any) => any> (\n controller: string | [C, methodName: ExtractControllerMethods<InstanceType<C>>],\n params?: Record<string, any>\n ) => Url.action(controller, params, app).toString(),\n\n /**\n * Get request-aware URL helpers\n */\n url: (path?: string) => {\n if (path) {\n return Url.to(path).toString() as never\n }\n return new RequestAwareHelpers(app) as never\n }\n }\n}\n","/// <reference path=\"../app.globals.d.ts\" />\nimport { ServiceProvider } from '@h3ravel/core'\nimport { Url } from '../Url'\nimport { createUrlHelper } from '../RequestAwareHelpers'\nimport { createUrlHelpers } from '../Helpers'\n\n/**\n * Service provider for URL utilities\n */\nexport class UrlServiceProvider extends ServiceProvider {\n public static priority = 897\n\n /**\n * Register URL services in the container\n */\n register (): void {\n // Register the Url class\n this.app.singleton('app.url', () => Url)\n // Register the url() helper function\n this.app.singleton('app.url.helper', () => createUrlHelper(this.app))\n\n // Register bound URL helpers\n this.app.singleton('app.url.helpers', () => createUrlHelpers(this.app))\n\n // Make url() globally available\n if (typeof globalThis !== 'undefined') {\n const helpers = createUrlHelpers(this.app)\n\n Object.assign(globalThis, {\n url: helpers.url,\n route: helpers.route,\n action: helpers.action,\n })\n }\n }\n\n /**\n * Boot URL services\n */\n boot (): void {\n // Any additional setup can be done here\n }\n}\n"],"mappings":"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"}