which-url 0.0.1 → 0.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 +36 -27
- package/dist/env-var.d.ts +11 -0
- package/dist/env-var.d.ts.map +1 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +81 -12
- package/dist/providers.d.ts.map +1 -1
- package/dist/resolve.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,16 +7,35 @@ npm install which-url
|
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
|
-
import
|
|
10
|
+
import appUrl from 'which-url'
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
whichUrl.hostname // "myapp.vercel.app"
|
|
12
|
+
auth({ baseURL: appUrl.href }) // just works — local, preview, production
|
|
14
13
|
```
|
|
15
14
|
|
|
16
15
|
Works on Vercel, Netlify, Cloudflare Pages, Railway, Fly.io, Render, DigitalOcean, and Heroku — automatically.
|
|
17
16
|
|
|
18
17
|
## Usage
|
|
19
18
|
|
|
19
|
+
### Default export (object with dot access)
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import appUrl from 'which-url'
|
|
23
|
+
|
|
24
|
+
appUrl.href // "https://myapp.vercel.app"
|
|
25
|
+
appUrl.origin // "https://myapp.vercel.app"
|
|
26
|
+
appUrl.hostname // "myapp.vercel.app"
|
|
27
|
+
appUrl.host // "myapp.vercel.app"
|
|
28
|
+
appUrl.protocol // "https:"
|
|
29
|
+
appUrl.port // ""
|
|
30
|
+
|
|
31
|
+
appUrl.env // "production" | "preview" | "local"
|
|
32
|
+
appUrl.isProduction // boolean
|
|
33
|
+
appUrl.isPreview // boolean
|
|
34
|
+
appUrl.isLocal // boolean
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Property names follow the [WHATWG URL spec](https://url.spec.whatwg.org/) — nothing new to learn.
|
|
38
|
+
|
|
20
39
|
### Named exports (plain strings — zero type friction)
|
|
21
40
|
|
|
22
41
|
```typescript
|
|
@@ -32,26 +51,6 @@ if (isProduction) {
|
|
|
32
51
|
}
|
|
33
52
|
```
|
|
34
53
|
|
|
35
|
-
### Default export (object with dot access)
|
|
36
|
-
|
|
37
|
-
```typescript
|
|
38
|
-
import whichUrl from 'which-url'
|
|
39
|
-
|
|
40
|
-
whichUrl.href // "https://myapp.vercel.app"
|
|
41
|
-
whichUrl.origin // "https://myapp.vercel.app"
|
|
42
|
-
whichUrl.hostname // "myapp.vercel.app"
|
|
43
|
-
whichUrl.host // "myapp.vercel.app"
|
|
44
|
-
whichUrl.protocol // "https:"
|
|
45
|
-
whichUrl.port // ""
|
|
46
|
-
|
|
47
|
-
whichUrl.env // "production" | "preview" | "local"
|
|
48
|
-
whichUrl.isProduction // boolean
|
|
49
|
-
whichUrl.isPreview // boolean
|
|
50
|
-
whichUrl.isLocal // boolean
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Property names follow the [WHATWG URL spec](https://url.spec.whatwg.org/) — nothing new to learn.
|
|
54
|
-
|
|
55
54
|
## How it works
|
|
56
55
|
|
|
57
56
|
`which-url` reads environment variables that hosting providers set automatically. No configuration needed.
|
|
@@ -127,6 +126,12 @@ url.href // never throws
|
|
|
127
126
|
|
|
128
127
|
In **development**, it always falls back to localhost.
|
|
129
128
|
|
|
129
|
+
## Gotchas
|
|
130
|
+
|
|
131
|
+
### Vercel: Redeploy after assigning a custom domain
|
|
132
|
+
|
|
133
|
+
Framework-prefixed env vars like `NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL` are inlined into the bundle at **build time** — the bundler replaces references with their literal values. If you assign a custom domain after deploying, the old deployment still has the previous URL baked in. Trigger a new deployment for the updated domain to take effect.
|
|
134
|
+
|
|
130
135
|
## Cloudflare Workers
|
|
131
136
|
|
|
132
137
|
Cloudflare Workers use runtime `env` bindings instead of `process.env`. Set `APP_URL` in `wrangler.toml`:
|
|
@@ -143,11 +148,11 @@ Modern wrangler polyfills `process.env` from `[vars]`, so `which-url` picks it u
|
|
|
143
148
|
### Better Auth
|
|
144
149
|
|
|
145
150
|
```typescript
|
|
146
|
-
import
|
|
151
|
+
import appUrl from 'which-url'
|
|
147
152
|
import { betterAuth } from 'better-auth'
|
|
148
153
|
|
|
149
154
|
export const auth = betterAuth({
|
|
150
|
-
baseURL: href,
|
|
155
|
+
baseURL: appUrl.href,
|
|
151
156
|
// ...
|
|
152
157
|
})
|
|
153
158
|
```
|
|
@@ -155,14 +160,18 @@ export const auth = betterAuth({
|
|
|
155
160
|
### NextAuth / Auth.js
|
|
156
161
|
|
|
157
162
|
```typescript
|
|
158
|
-
import
|
|
163
|
+
import appUrl from 'which-url'
|
|
159
164
|
|
|
160
165
|
// No need to set NEXTAUTH_URL manually
|
|
161
|
-
process.env.NEXTAUTH_URL = href
|
|
166
|
+
process.env.NEXTAUTH_URL = appUrl.href
|
|
162
167
|
```
|
|
163
168
|
|
|
164
169
|
## API
|
|
165
170
|
|
|
171
|
+
### Default export
|
|
172
|
+
|
|
173
|
+
An object with URL properties and environment helpers.
|
|
174
|
+
|
|
166
175
|
### Named exports
|
|
167
176
|
|
|
168
177
|
| Export | Type | Description |
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read an env var, checking all framework-prefixed versions.
|
|
3
|
+
* On the server, unprefixed wins (it's always set).
|
|
4
|
+
* On the client, the bundler inlines the prefixed version.
|
|
5
|
+
*
|
|
6
|
+
* When called with an `env` object (e.g. from provider detection),
|
|
7
|
+
* falls back to dynamic lookup for test/server compatibility.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getVar(env: Record<string, string | undefined>, name: string): string | undefined;
|
|
10
|
+
export declare function getEnv(): Record<string, string | undefined>;
|
|
11
|
+
//# sourceMappingURL=env-var.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-var.d.ts","sourceRoot":"","sources":["../src/env-var.ts"],"names":[],"mappings":"AAgGA;;;;;;;GAOG;AACH,wBAAgB,MAAM,CACpB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACvC,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,SAAS,CA0BpB;AAED,wBAAgB,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAK3D"}
|
package/dist/env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAIrC,wBAAgB,UAAU,IAAI,MAAM,CAwBnC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { WhichUrl, AppEnv, CreateUrlOptions } from "./types";
|
|
2
2
|
export declare function createUrl(options?: CreateUrlOptions): WhichUrl;
|
|
3
|
-
declare
|
|
3
|
+
declare let _resolved: WhichUrl;
|
|
4
4
|
export declare const href: string;
|
|
5
5
|
export declare const origin: string;
|
|
6
6
|
export declare const hostname: string;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAEjE,wBAAgB,SAAS,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,QAAQ,CAgB9D;AAGD,QAAA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAEjE,wBAAgB,SAAS,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,QAAQ,CAgB9D;AAGD,QAAA,IAAI,SAAS,EAAE,QAAQ,CAAA;AAsBvB,eAAO,MAAM,IAAI,EAAE,MAAuB,CAAA;AAC1C,eAAO,MAAM,MAAM,EAAE,MAAyB,CAAA;AAC9C,eAAO,MAAM,QAAQ,EAAE,MAA2B,CAAA;AAClD,eAAO,MAAM,IAAI,EAAE,MAAuB,CAAA;AAC1C,eAAO,MAAM,QAAQ,EAAE,MAA2B,CAAA;AAClD,eAAO,MAAM,IAAI,EAAE,MAAuB,CAAA;AAC1C,eAAO,MAAM,GAAG,EAAE,MAAsB,CAAA;AACxC,eAAO,MAAM,YAAY,EAAE,OAAgC,CAAA;AAC3D,eAAO,MAAM,SAAS,EAAE,OAA6B,CAAA;AACrD,eAAO,MAAM,OAAO,EAAE,OAA2B,CAAA;AAGjD,eAAe,SAAS,CAAA;AAExB,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,69 @@
|
|
|
1
|
+
// src/env-var.ts
|
|
2
|
+
function readStatic(name) {
|
|
3
|
+
switch (name) {
|
|
4
|
+
case "VERCEL_ENV":
|
|
5
|
+
return process.env.VERCEL_ENV || process.env.NEXT_PUBLIC_VERCEL_ENV || process.env.NUXT_ENV_VERCEL_ENV || process.env.VITE_VERCEL_ENV || process.env.PUBLIC_VERCEL_ENV || process.env.REACT_APP_VERCEL_ENV || process.env.GATSBY_VERCEL_ENV || process.env.VUE_APP_VERCEL_ENV || process.env.REDWOOD_ENV_VERCEL_ENV || process.env.SANITY_STUDIO_VERCEL_ENV || undefined;
|
|
6
|
+
case "VERCEL_URL":
|
|
7
|
+
return process.env.VERCEL_URL || process.env.NEXT_PUBLIC_VERCEL_URL || process.env.NUXT_ENV_VERCEL_URL || process.env.VITE_VERCEL_URL || process.env.PUBLIC_VERCEL_URL || process.env.REACT_APP_VERCEL_URL || process.env.GATSBY_VERCEL_URL || process.env.VUE_APP_VERCEL_URL || process.env.REDWOOD_ENV_VERCEL_URL || process.env.SANITY_STUDIO_VERCEL_URL || undefined;
|
|
8
|
+
case "VERCEL_BRANCH_URL":
|
|
9
|
+
return process.env.VERCEL_BRANCH_URL || process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL || process.env.NUXT_ENV_VERCEL_BRANCH_URL || process.env.VITE_VERCEL_BRANCH_URL || process.env.PUBLIC_VERCEL_BRANCH_URL || process.env.REACT_APP_VERCEL_BRANCH_URL || process.env.GATSBY_VERCEL_BRANCH_URL || process.env.VUE_APP_VERCEL_BRANCH_URL || process.env.REDWOOD_ENV_VERCEL_BRANCH_URL || process.env.SANITY_STUDIO_VERCEL_BRANCH_URL || undefined;
|
|
10
|
+
case "VERCEL_PROJECT_PRODUCTION_URL":
|
|
11
|
+
return process.env.VERCEL_PROJECT_PRODUCTION_URL || process.env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL || process.env.NUXT_ENV_VERCEL_PROJECT_PRODUCTION_URL || process.env.VITE_VERCEL_PROJECT_PRODUCTION_URL || process.env.PUBLIC_VERCEL_PROJECT_PRODUCTION_URL || process.env.REACT_APP_VERCEL_PROJECT_PRODUCTION_URL || process.env.GATSBY_VERCEL_PROJECT_PRODUCTION_URL || process.env.VUE_APP_VERCEL_PROJECT_PRODUCTION_URL || process.env.REDWOOD_ENV_VERCEL_PROJECT_PRODUCTION_URL || process.env.SANITY_STUDIO_VERCEL_PROJECT_PRODUCTION_URL || undefined;
|
|
12
|
+
case "APP_URL":
|
|
13
|
+
return process.env.APP_URL || process.env.NEXT_PUBLIC_APP_URL || process.env.NUXT_ENV_APP_URL || process.env.VITE_APP_URL || process.env.PUBLIC_APP_URL || process.env.REACT_APP_APP_URL || process.env.GATSBY_APP_URL || process.env.VUE_APP_APP_URL || process.env.REDWOOD_ENV_APP_URL || process.env.SANITY_STUDIO_APP_URL || undefined;
|
|
14
|
+
case "APP_ENV":
|
|
15
|
+
return process.env.APP_ENV || process.env.NEXT_PUBLIC_APP_ENV || process.env.NUXT_ENV_APP_ENV || process.env.VITE_APP_ENV || process.env.PUBLIC_APP_ENV || process.env.REACT_APP_APP_ENV || process.env.GATSBY_APP_ENV || process.env.VUE_APP_APP_ENV || process.env.REDWOOD_ENV_APP_ENV || process.env.SANITY_STUDIO_APP_ENV || undefined;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function getVar(env, name) {
|
|
19
|
+
if (typeof process !== "undefined" && process?.env && env === process.env) {
|
|
20
|
+
const val = readStatic(name);
|
|
21
|
+
if (val)
|
|
22
|
+
return val;
|
|
23
|
+
}
|
|
24
|
+
const PREFIXES = [
|
|
25
|
+
"",
|
|
26
|
+
"NEXT_PUBLIC_",
|
|
27
|
+
"NUXT_ENV_",
|
|
28
|
+
"VITE_",
|
|
29
|
+
"PUBLIC_",
|
|
30
|
+
"REACT_APP_",
|
|
31
|
+
"GATSBY_",
|
|
32
|
+
"VUE_APP_",
|
|
33
|
+
"REDWOOD_ENV_",
|
|
34
|
+
"SANITY_STUDIO_"
|
|
35
|
+
];
|
|
36
|
+
for (const prefix of PREFIXES) {
|
|
37
|
+
const val = env[prefix + name];
|
|
38
|
+
if (val)
|
|
39
|
+
return val;
|
|
40
|
+
}
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
function getEnv() {
|
|
44
|
+
if (typeof process !== "undefined" && process?.env) {
|
|
45
|
+
return process.env;
|
|
46
|
+
}
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
|
|
1
50
|
// src/providers.ts
|
|
2
51
|
var providers = [
|
|
3
52
|
{
|
|
4
53
|
name: "vercel",
|
|
5
|
-
detect: (env) => !!env.VERCEL,
|
|
54
|
+
detect: (env) => !!env.VERCEL || !!getVar(env, "VERCEL_ENV"),
|
|
6
55
|
resolveUrl: (env) => {
|
|
7
|
-
|
|
8
|
-
|
|
56
|
+
const vercelEnv = getVar(env, "VERCEL_ENV");
|
|
57
|
+
if (vercelEnv === "production") {
|
|
58
|
+
return getVar(env, "VERCEL_PROJECT_PRODUCTION_URL") || getVar(env, "VERCEL_URL") || null;
|
|
9
59
|
}
|
|
10
|
-
return env
|
|
60
|
+
return getVar(env, "VERCEL_BRANCH_URL") || getVar(env, "VERCEL_URL") || null;
|
|
11
61
|
},
|
|
12
62
|
resolveEnv: (env) => {
|
|
13
|
-
|
|
63
|
+
const vercelEnv = getVar(env, "VERCEL_ENV");
|
|
64
|
+
if (vercelEnv === "production")
|
|
14
65
|
return "production";
|
|
15
|
-
if (
|
|
66
|
+
if (vercelEnv === "preview")
|
|
16
67
|
return "preview";
|
|
17
68
|
return "local";
|
|
18
69
|
}
|
|
@@ -94,8 +145,8 @@ function normalizeUrl(raw) {
|
|
|
94
145
|
|
|
95
146
|
// src/resolve.ts
|
|
96
147
|
function resolveUrl(options) {
|
|
97
|
-
const env =
|
|
98
|
-
const override = env
|
|
148
|
+
const env = getEnv();
|
|
149
|
+
const override = getVar(env, "APP_URL");
|
|
99
150
|
if (override)
|
|
100
151
|
return normalizeUrl(override);
|
|
101
152
|
for (const p of providers) {
|
|
@@ -122,9 +173,10 @@ function resolveUrl(options) {
|
|
|
122
173
|
// src/env.ts
|
|
123
174
|
var validEnvs = ["production", "preview", "local"];
|
|
124
175
|
function resolveEnv() {
|
|
125
|
-
const env =
|
|
126
|
-
|
|
127
|
-
|
|
176
|
+
const env = getEnv();
|
|
177
|
+
const appEnv = getVar(env, "APP_ENV");
|
|
178
|
+
if (appEnv && validEnvs.includes(appEnv)) {
|
|
179
|
+
return appEnv;
|
|
128
180
|
}
|
|
129
181
|
if (env.NODE_ENV === "development")
|
|
130
182
|
return "local";
|
|
@@ -156,7 +208,24 @@ function createUrl(options) {
|
|
|
156
208
|
isLocal: env === "local"
|
|
157
209
|
};
|
|
158
210
|
}
|
|
159
|
-
var _resolved
|
|
211
|
+
var _resolved;
|
|
212
|
+
try {
|
|
213
|
+
_resolved = createUrl();
|
|
214
|
+
} catch (e) {
|
|
215
|
+
console.warn(`[which-url] Could not detect app URL. Set APP_URL (e.g. APP_URL=https://myapp.com or APP_URL=myapp.com)`);
|
|
216
|
+
_resolved = {
|
|
217
|
+
href: "",
|
|
218
|
+
origin: "",
|
|
219
|
+
hostname: "",
|
|
220
|
+
host: "",
|
|
221
|
+
protocol: "",
|
|
222
|
+
port: "",
|
|
223
|
+
env: "local",
|
|
224
|
+
isProduction: false,
|
|
225
|
+
isPreview: false,
|
|
226
|
+
isLocal: true
|
|
227
|
+
};
|
|
228
|
+
}
|
|
160
229
|
var href = _resolved.href;
|
|
161
230
|
var origin = _resolved.origin;
|
|
162
231
|
var hostname = _resolved.hostname;
|
package/dist/providers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAG/C,eAAO,MAAM,SAAS,EAAE,gBAAgB,EAgFvC,CAAA"}
|
package/dist/resolve.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,wBAAgB,UAAU,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAoC7D"}
|