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 CHANGED
@@ -7,16 +7,35 @@ npm install which-url
7
7
  ```
8
8
 
9
9
  ```typescript
10
- import whichUrl from 'which-url'
10
+ import appUrl from 'which-url'
11
11
 
12
- whichUrl.href // "https://myapp.vercel.app"
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 { href } from 'which-url'
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 { href } from 'which-url'
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":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAIrC,wBAAgB,UAAU,IAAI,MAAM,CAuBnC"}
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 const _resolved: WhichUrl;
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;
@@ -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,MAAM,SAAS,UAAc,CAAA;AAG7B,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"}
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
- if (env.VERCEL_ENV === "production") {
8
- return env.VERCEL_PROJECT_PRODUCTION_URL || env.VERCEL_URL || null;
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.VERCEL_BRANCH_URL || env.VERCEL_URL || null;
60
+ return getVar(env, "VERCEL_BRANCH_URL") || getVar(env, "VERCEL_URL") || null;
11
61
  },
12
62
  resolveEnv: (env) => {
13
- if (env.VERCEL_ENV === "production")
63
+ const vercelEnv = getVar(env, "VERCEL_ENV");
64
+ if (vercelEnv === "production")
14
65
  return "production";
15
- if (env.VERCEL_ENV === "preview")
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 = typeof process !== "undefined" ? process.env : {};
98
- const override = env.APP_URL || env.NEXT_PUBLIC_APP_URL;
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 = typeof process !== "undefined" ? process.env : {};
126
- if (env.APP_ENV && validEnvs.includes(env.APP_ENV)) {
127
- return env.APP_ENV;
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 = createUrl();
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;
@@ -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;AAE/C,eAAO,MAAM,SAAS,EAAE,gBAAgB,EA8EvC,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"}
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,wBAAgB,UAAU,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAoC7D"}
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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "which-url",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Auto-detect your app's URL across hosting providers. Zero config.",
5
5
  "type": "module",
6
6
  "exports": {