@mostajs/url
v0.3.0
Published
URL helpers — absoluteUrl, joinPath, etc. Indispensable derrière un reverse proxy où req.url donne le bind interne et non l'URL publique.
Maintainers
Readme
@mostajs/url
URL helpers framework-agnostic. Construit des URLs absolues à partir d'une base configurée — utile derrière un reverse proxy où req.url ne donne que le bind interne.
Auteur : Dr Hamid MADANI [email protected]
Installation
npm install @mostajs/urlPourquoi ?
Derrière Apache / nginx avec ProxyPass, req.url côté Node donne http://localhost:3020/... et non l'URL publique https://app.example.com/.... Toute construction d'URL — magic-link, email callback, redirect — doit s'appuyer sur une base configurée explicitement.
Usage rapide
import { absoluteUrl, setBasePicker } from '@mostajs/url'
import { getEnv } from '@mostajs/config'
// Au démarrage : configure le picker global (lit env)
setBasePicker(() => getEnv('NEXTAUTH_URL') || getEnv('PUBLIC_URL'))
// Partout dans l'app
absoluteUrl('/auth/callback')
// → 'https://app.example.com/auth/callback'
absoluteUrl('users/42')
// → 'https://app.example.com/users/42'API
setBasePicker(picker | null)
Configure le picker global qui retourne la base URL. Appelé une seule fois au démarrage.
absoluteUrl(path, opts?)
Construit une URL absolue. Résolution de la base, dans l'ordre :
opts.basesi fourni- retour du picker global
opts.fallback'http://localhost:3000'
absoluteUrl('/x', { base: 'https://other.com' })
// → 'https://other.com/x' (override)joinPath(...parts)
Joint des segments en chemin propre, gère les slashes et préserve le protocole.
joinPath('/api/', '/users/', '42') // → '/api/users/42'
joinPath('https://x.com/', 'a', 'b') // → 'https://x.com/a/b'isAbsolute(url)
true si l'URL a un protocole (http://, https://, data:, file://) ou est protocol-relative (//host/...).
Helpers query string (v0.2+)
import { appendQuery, appendQueryParams, stripQuery, parseQuery, splitUrl } from '@mostajs/url'
appendQuery('/api?x=1', 'y', '2') // → '/api?x=1&y=2'
appendQuery('/api?x=1', 'x', '99') // → '/api?x=99' (override)
appendQuery('/api?x=1#h', 'y', '2') // → '/api?x=1&y=2#h' (hash préservé)
appendQuery('/api?x=1', 'x', null) // → '/api' (suppression)
appendQueryParams('/x', { a: 1, b: 'foo', c: null })
// → '/x?a=1&b=foo' (null ignoré)
stripQuery('/api?x=1&y=2', 'x') // → '/api?y=2'
parseQuery('/api?x=1&y=2') // → { x: '1', y: '2' }
splitUrl('/api?x=1#frag')
// → { base: '/api', query: '?x=1', hash: '#frag' }Helpers magic-link (v0.2+)
import { combineMagicLink, withNext } from '@mostajs/url'
combineMagicLink({
baseUrl: 'https://app.example.com/api/auth/magic',
token: 'eyJ...',
next: '/projet-x',
})
// → 'https://app.example.com/api/auth/magic?token=eyJ...&next=%2Fprojet-x'
withNext('/api/auth/magic?token=abc', '/projet-x')
// → '/api/auth/magic?token=abc&next=%2Fprojet-x'
withNext('/api/auth/magic?token=abc', 'https://evil.com')
// → '/api/auth/magic?token=abc' (path externe rejeté → anti open-redirect)Roadmap
- v0.3 : helpers signed-url (URLs signées HMAC pour CDN / pré-signed S3)
- v0.4 : helper internationalisé (préfixe locale
/fr/...,/ar/...) - v0.5 : URL parsing typé avancé (extract subdomain, slug, multi-value query)
Licence
AGPL-3.0-or-later.
