@h3ravel/url 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,401 @@
1
+ import { ConfigException, ServiceProvider } from "@h3ravel/core";
2
+ import { hmac } from "@h3ravel/support";
3
+ import path from "node:path";
4
+
5
+ //#region src/RequestAwareHelpers.ts
6
+ /**
7
+ * Request-aware URL helper class
8
+ */
9
+ var RequestAwareHelpers = class {
10
+ baseUrl = "";
11
+ constructor(app) {
12
+ this.app = app;
13
+ try {
14
+ this.baseUrl = config("app.url", "http://localhost:3000");
15
+ } catch {}
16
+ }
17
+ /**
18
+ * Get the current request instance
19
+ */
20
+ getCurrentRequest() {
21
+ const request = this.app.make("http.request");
22
+ if (!request) throw new Error("Request instance not available in current context");
23
+ return request;
24
+ }
25
+ /**
26
+ * Get the current request URL (path only, no query string)
27
+ */
28
+ current() {
29
+ const raw = this.getCurrentRequest().getEvent().req.url ?? "/";
30
+ return new URL(raw, "http://localhost").pathname;
31
+ }
32
+ /**
33
+ * Get the full current URL with query string
34
+ */
35
+ full() {
36
+ const requestUrl = this.getCurrentRequest().getEvent().req.url ?? "/";
37
+ if (requestUrl.startsWith("http")) return requestUrl;
38
+ return new URL(requestUrl, this.baseUrl).toString();
39
+ }
40
+ /**
41
+ * Get the previous request URL from session or referrer
42
+ */
43
+ previous() {
44
+ const headers = this.getCurrentRequest().getEvent()?.node?.req?.headers;
45
+ let referrer = headers?.referer ?? headers?.referrer;
46
+ if (Array.isArray(referrer)) referrer = referrer[0];
47
+ if (referrer) return referrer;
48
+ return this.current();
49
+ }
50
+ /**
51
+ * Get the previous request path (without query string)
52
+ */
53
+ previousPath() {
54
+ const previousUrl = this.previous();
55
+ try {
56
+ return new URL(previousUrl).pathname;
57
+ } catch {
58
+ return previousUrl;
59
+ }
60
+ }
61
+ /**
62
+ * Get the current query parameters
63
+ */
64
+ query() {
65
+ return this.getCurrentRequest().query || {};
66
+ }
67
+ };
68
+ /**
69
+ * Global helper function factory
70
+ */
71
+ function createUrlHelper(app) {
72
+ return () => new RequestAwareHelpers(app);
73
+ }
74
+
75
+ //#endregion
76
+ //#region src/Url.ts
77
+ /**
78
+ * URL builder class with fluent API and request-aware helpers
79
+ */
80
+ var Url = class Url {
81
+ _scheme;
82
+ _host;
83
+ _port;
84
+ _path;
85
+ _query;
86
+ _fragment;
87
+ app;
88
+ constructor(app, scheme, host, port, path$1 = "/", query = {}, fragment) {
89
+ this.app = app;
90
+ this._scheme = scheme;
91
+ this._host = host;
92
+ this._port = port;
93
+ this._path = path$1.startsWith("/") ? path$1 : `/${path$1}`;
94
+ this._query = { ...query };
95
+ this._fragment = fragment;
96
+ }
97
+ /**
98
+ * Create a URL from a full URL string
99
+ */
100
+ static of(url$1, app) {
101
+ try {
102
+ const parsed = new URL(url$1);
103
+ const query = {};
104
+ parsed.searchParams.forEach((value, key) => {
105
+ query[key] = value;
106
+ });
107
+ return new Url(app, parsed.protocol.replace(":", ""), parsed.hostname, parsed.port ? parseInt(parsed.port) : void 0, parsed.pathname || "/", query, parsed.hash ? parsed.hash.substring(1) : void 0);
108
+ } catch {
109
+ throw new Error(`Invalid URL: ${url$1}`);
110
+ }
111
+ }
112
+ /**
113
+ * Create a URL from a path relative to the app URL
114
+ */
115
+ static to(path$1, app) {
116
+ let baseUrl = "";
117
+ try {
118
+ baseUrl = config("app.url", "http://localhost:3000");
119
+ } catch {}
120
+ const fullUrl = new URL(path$1, baseUrl).toString();
121
+ return Url.of(fullUrl, app);
122
+ }
123
+ /**
124
+ * Create a URL from a named route
125
+ */
126
+ static route(name, params = {}, app) {
127
+ if (!app) throw new Error("Application instance required for route generation");
128
+ const router = app.make("router");
129
+ if (!router || typeof router.route !== "function") throw new Error("Router not available or does not support route generation");
130
+ if (typeof router.route !== "function") throw new Error("Router does not support route generation");
131
+ const routeUrl = router.route(name, params);
132
+ if (!routeUrl) throw new Error(`Route "${name}" not found`);
133
+ return Url.to(routeUrl, app);
134
+ }
135
+ /**
136
+ * Create a signed URL from a named route
137
+ */
138
+ static signedRoute(name, params = {}, app) {
139
+ return Url.route(name, params, app).withSignature(app);
140
+ }
141
+ /**
142
+ * Create a temporary signed URL from a named route
143
+ */
144
+ static temporarySignedRoute(name, params = {}, expiration, app) {
145
+ return Url.route(name, params, app).withSignature(app, expiration);
146
+ }
147
+ /**
148
+ * Create a URL from a controller action
149
+ */
150
+ static action(controller, params, app) {
151
+ if (!app) throw new Error("Application instance required for action URL generation");
152
+ const [controllerName, methodName = "index"] = typeof controller === "string" ? controller.split("@") : controller;
153
+ const cname = typeof controllerName === "string" ? controllerName : controllerName.name;
154
+ const routes = app.make("app.routes");
155
+ if (!Array.isArray(routes)) throw new Error("Action URL generation requires router integration - not yet implemented");
156
+ if (routes.length < 1) throw new Error(`No routes available to resolve action: ${controller}`);
157
+ const found = routes.find((route$1) => {
158
+ return route$1.signature?.[0] === cname && (route$1.signature?.[1] || "index") === methodName;
159
+ });
160
+ if (!found) throw new Error(`No route found for ${cname}`);
161
+ const _params = Object.values(params ?? {}).join("/");
162
+ if (_params) return Url.to(path.join(found.path, _params));
163
+ return Url.to(found.path, app);
164
+ }
165
+ /**
166
+ * Set the scheme (protocol) of the URL
167
+ */
168
+ withScheme(scheme) {
169
+ return new Url(this.app, scheme, this._host, this._port, this._path, this._query, this._fragment);
170
+ }
171
+ /**
172
+ * Set the host of the URL
173
+ */
174
+ withHost(host) {
175
+ return new Url(this.app, this._scheme, host, this._port, this._path, this._query, this._fragment);
176
+ }
177
+ /**
178
+ * Set the port of the URL
179
+ */
180
+ withPort(port) {
181
+ return new Url(this.app, this._scheme, this._host, port, this._path, this._query, this._fragment);
182
+ }
183
+ /**
184
+ * Set the path of the URL
185
+ */
186
+ withPath(path$1) {
187
+ return new Url(this.app, this._scheme, this._host, this._port, path$1, this._query, this._fragment);
188
+ }
189
+ /**
190
+ * Set the query parameters of the URL
191
+ */
192
+ withQuery(query) {
193
+ return new Url(this.app, this._scheme, this._host, this._port, this._path, { ...query }, this._fragment);
194
+ }
195
+ /**
196
+ * Merge additional query parameters
197
+ */
198
+ withQueryParams(params) {
199
+ return new Url(this.app, this._scheme, this._host, this._port, this._path, {
200
+ ...this._query,
201
+ ...params
202
+ }, this._fragment);
203
+ }
204
+ /**
205
+ * Set the fragment (hash) of the URL
206
+ */
207
+ withFragment(fragment) {
208
+ return new Url(this.app, this._scheme, this._host, this._port, this._path, this._query, fragment);
209
+ }
210
+ /**
211
+ * Add a signature to the URL for security
212
+ */
213
+ withSignature(app, expiration) {
214
+ if (!(app || this.app)) throw new Error("Application instance required for URL signing");
215
+ let key = "";
216
+ try {
217
+ key = config("app.key");
218
+ } catch {}
219
+ if (!key) throw new ConfigException("APP_KEY and app.key", "any", this);
220
+ const url$1 = this.toString();
221
+ const queryParams = { ...this._query };
222
+ if (expiration) queryParams.expires = Math.floor(expiration / 1e3);
223
+ const payload = expiration ? `${url$1}?expires=${queryParams.expires}` : url$1;
224
+ queryParams.signature = hmac(payload, key);
225
+ return this.withQuery(queryParams);
226
+ }
227
+ /**
228
+ * Verify if a URL signature is valid
229
+ */
230
+ hasValidSignature(app) {
231
+ if (!(app || this.app)) return false;
232
+ const signature = this._query.signature;
233
+ if (!signature) return false;
234
+ if (this._query.expires !== void 0 && this._query.expires !== null) {
235
+ const expiresStr = String(this._query.expires);
236
+ const expirationTime = parseInt(expiresStr, 10) * 1e3;
237
+ if (isNaN(expirationTime) || Date.now() > expirationTime) return false;
238
+ }
239
+ const queryWithoutSignature = { ...this._query };
240
+ delete queryWithoutSignature.signature;
241
+ const urlWithoutSignature = new Url(this.app, this._scheme, this._host, this._port, this._path, queryWithoutSignature, this._fragment).toString();
242
+ const payload = this._query.expires ? `${urlWithoutSignature}?expires=${this._query.expires}` : urlWithoutSignature;
243
+ let key = "";
244
+ try {
245
+ key = config("app.key", "default-key");
246
+ } catch {}
247
+ const expectedSignature = hmac(payload, key);
248
+ return signature === expectedSignature;
249
+ }
250
+ /**
251
+ * Convert the URL to its string representation
252
+ */
253
+ toString() {
254
+ let url$1 = "";
255
+ if (this._scheme && this._host) {
256
+ url$1 += `${this._scheme}://${this._host}`;
257
+ if (this._port && !(this._scheme === "http" && this._port === 80 || this._scheme === "https" && this._port === 443)) url$1 += `:${this._port}`;
258
+ }
259
+ if (this._path) {
260
+ if (!this._path.startsWith("/")) url$1 += "/";
261
+ url$1 += this._path;
262
+ }
263
+ const queryEntries = Object.entries(this._query);
264
+ if (queryEntries.length > 0) {
265
+ const queryString = queryEntries.map(([key, value]) => {
266
+ if (Array.isArray(value)) return value.map((v) => `${encodeURIComponent(key)}%5B%5D=${encodeURIComponent(v)}`).join("&");
267
+ return `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`;
268
+ }).join("&");
269
+ url$1 += `?${queryString}`;
270
+ }
271
+ if (this._fragment) url$1 += `#${this._fragment}`;
272
+ return url$1;
273
+ }
274
+ /**
275
+ * Get the scheme
276
+ */
277
+ getScheme() {
278
+ return this._scheme;
279
+ }
280
+ /**
281
+ * Get the host
282
+ */
283
+ getHost() {
284
+ return this._host;
285
+ }
286
+ /**
287
+ * Get the port
288
+ */
289
+ getPort() {
290
+ return this._port;
291
+ }
292
+ /**
293
+ * Get the path
294
+ */
295
+ getPath() {
296
+ return this._path;
297
+ }
298
+ /**
299
+ * Get the query parameters
300
+ */
301
+ getQuery() {
302
+ return { ...this._query };
303
+ }
304
+ /**
305
+ * Get the fragment
306
+ */
307
+ getFragment() {
308
+ return this._fragment;
309
+ }
310
+ };
311
+
312
+ //#endregion
313
+ //#region src/Helpers.ts
314
+ /**
315
+ * Global helper functions for URL manipulation
316
+ */
317
+ /**
318
+ * Create a URL from a path relative to the app URL
319
+ */
320
+ function to(path$1, app) {
321
+ return Url.to(path$1, app);
322
+ }
323
+ /**
324
+ * Create a URL from a named route
325
+ */
326
+ function route(name, params = {}, app) {
327
+ return Url.route(name, params, app);
328
+ }
329
+ /**
330
+ * Create a signed URL from a named route
331
+ */
332
+ function signedRoute(name, params = {}, app) {
333
+ return Url.signedRoute(name, params, app);
334
+ }
335
+ /**
336
+ * Create a temporary signed URL from a named route
337
+ */
338
+ function temporarySignedRoute(name, params = {}, expiration, app) {
339
+ return Url.temporarySignedRoute(name, params, expiration, app);
340
+ }
341
+ /**
342
+ * Create a URL from a controller action
343
+ */
344
+ function action(controller, app) {
345
+ return Url.action(controller, app);
346
+ }
347
+ /**
348
+ * Get request-aware URL helpers
349
+ */
350
+ function url(app) {
351
+ if (!app) throw new Error("Application instance required for request-aware URL helpers");
352
+ return new RequestAwareHelpers(app);
353
+ }
354
+ /**
355
+ * Create URL helpers that are bound to an application instance
356
+ */
357
+ function createUrlHelpers(app) {
358
+ return {
359
+ to: (path$1) => Url.to(path$1, app),
360
+ route: (name, params = {}) => Url.route(name, params, app).toString(),
361
+ signedRoute: (name, params = {}) => Url.signedRoute(name, params, app),
362
+ temporarySignedRoute: (name, params = {}, expiration) => Url.temporarySignedRoute(name, params, expiration, app),
363
+ action: (controller, params) => Url.action(controller, params, app).toString(),
364
+ url: (path$1) => {
365
+ if (path$1) return Url.to(path$1).toString();
366
+ return new RequestAwareHelpers(app);
367
+ }
368
+ };
369
+ }
370
+
371
+ //#endregion
372
+ //#region src/Providers/UrlServiceProvider.ts
373
+ /**
374
+ * Service provider for URL utilities
375
+ */
376
+ var UrlServiceProvider = class extends ServiceProvider {
377
+ /**
378
+ * Register URL services in the container
379
+ */
380
+ register() {
381
+ this.app.singleton("app.url", () => Url);
382
+ this.app.singleton("app.url.helper", () => createUrlHelper(this.app));
383
+ this.app.singleton("app.url.helpers", () => createUrlHelpers(this.app));
384
+ if (typeof globalThis !== "undefined") {
385
+ const helpers = createUrlHelpers(this.app);
386
+ Object.assign(globalThis, {
387
+ url: helpers.url,
388
+ route: helpers.route,
389
+ action: helpers.action
390
+ });
391
+ }
392
+ }
393
+ /**
394
+ * Boot URL services
395
+ */
396
+ boot() {}
397
+ };
398
+
399
+ //#endregion
400
+ export { RequestAwareHelpers, Url, UrlServiceProvider, action, createUrlHelper, createUrlHelpers, route, signedRoute, temporarySignedRoute, to, url };
401
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
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 /**\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/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@h3ravel/url",
3
+ "version": "1.0.0",
4
+ "description": "Request-aware URI builder and URL manipulation utilities for H3ravel.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "module": "./dist/index.js",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "homepage": "https://h3ravel.toneflix.net",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/h3ravel/framework.git",
26
+ "directory": "packages/url"
27
+ },
28
+ "keywords": [
29
+ "h3ravel",
30
+ "modern",
31
+ "web",
32
+ "H3",
33
+ "framework",
34
+ "nodejs",
35
+ "typescript",
36
+ "laravel",
37
+ "url",
38
+ "uri",
39
+ "builder"
40
+ ],
41
+ "dependencies": {
42
+ "@h3ravel/support": "^0.13.0",
43
+ "@h3ravel/shared": "^0.21.0"
44
+ },
45
+ "peerDependencies": {
46
+ "@h3ravel/core": "^1.13.0",
47
+ "@h3ravel/http": "^11.3.2"
48
+ },
49
+ "devDependencies": {
50
+ "typescript": "^5.4.0"
51
+ },
52
+ "scripts": {
53
+ "barrel": "barrelsby --directory src --delete --singleQuotes",
54
+ "build": "tsdown --config-loader unconfig",
55
+ "dev": "tsx watch src/index.ts",
56
+ "start": "node dist/index.js",
57
+ "lint": "eslint . --ext .ts",
58
+ "test": "jest --passWithNoTests",
59
+ "version-patch": "pnpm version patch"
60
+ }
61
+ }