which-url 0.0.2 → 0.0.4

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,126 +7,156 @@ npm install which-url
7
7
  ```
8
8
 
9
9
  ```typescript
10
- import appUrl from 'which-url'
10
+ import { origin } from 'which-url'
11
11
 
12
- auth({ baseURL: appUrl.href }) // just works — local, preview, production
12
+ auth({ baseURL: origin })
13
+ fetch(`${origin}/api/data`)
13
14
  ```
14
15
 
15
- Works on Vercel, Netlify, Cloudflare Pages, Railway, Fly.io, Render, DigitalOcean, and Heroku — automatically.
16
+ ```
17
+ origin env
18
+ Local http://localhost:3000 "local"
19
+ Preview https://myapp-git-feat.vercel.app "preview"
20
+ Production https://myapp.com "production"
21
+ ```
16
22
 
17
- ## Usage
23
+ Works across environments (local, preview, production), runtimes (server, client, edge), and [platforms](#platform-support).
18
24
 
19
- ### Default export (object with dot access)
25
+ The default export gives you everything as an object:
20
26
 
21
27
  ```typescript
22
28
  import appUrl from 'which-url'
23
29
 
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
30
+ appUrl.origin // "https://myapp.com"
31
+ appUrl.hostname // "myapp.com"
32
+ appUrl.protocol // "https:"
33
+ appUrl.env // "production"
34
+ appUrl.platform // "vercel"
35
+ appUrl.isProduction // true
35
36
  ```
36
37
 
37
- Property names follow the [WHATWG URL spec](https://url.spec.whatwg.org/) — nothing new to learn.
38
+ ## The problem
38
39
 
39
- ### Named exports (plain stringszero type friction)
40
+ Your app's base URL shows up everywhere OAuth callbacks, API calls, CORS, emails. Every one of these breaks if the URL is wrong:
40
41
 
41
42
  ```typescript
42
- import { href, hostname, origin, isProduction } from 'which-url'
43
+ // Auth needs the exact URL for OAuth redirects
44
+ auth({ baseURL: ??? })
43
45
 
44
- // Pass directly to any function that expects a string
45
- process.env.BETTER_AUTH_URL = href
46
- fetch(`${href}/api/data`)
47
- cookie.domain = hostname
46
+ // API calls from the client
47
+ fetch(`${???}/api/data`)
48
48
 
49
- if (isProduction) {
50
- // production-only logic
49
+ // Emails — links need to point somewhere real
50
+ `Click here to verify: ${???}/verify?token=${token}`
51
+
52
+ // CORS — needs to know its own origin
53
+ cors({ origin: ??? })
54
+ ```
55
+
56
+ Most teams end up with a helper that grows over time:
57
+
58
+ ```typescript
59
+ // lib/url.ts — every team has one of these
60
+ function getBaseUrl() {
61
+ if (typeof window !== 'undefined') return ''
62
+ if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`
63
+ return `http://localhost:${process.env.PORT ?? 3000}`
51
64
  }
65
+
66
+ // But wait — VERCEL_URL is the deployment URL, not your domain.
67
+ // And it doesn't work on the client. So you add more:
68
+ const baseUrl =
69
+ process.env.NEXT_PUBLIC_VERCEL_ENV === 'production'
70
+ ? `https://${process.env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL}`
71
+ : process.env.NEXT_PUBLIC_VERCEL_URL
72
+ ? `https://${process.env.NEXT_PUBLIC_VERCEL_URL}`
73
+ : `http://localhost:${process.env.PORT ?? 3000}`
74
+
75
+ // And then Netlify uses different env vars. And Cloudflare uses different ones.
76
+ // And someone forgets the https://. And preview URLs break in production...
52
77
  ```
53
78
 
54
79
  ## How it works
55
80
 
56
- `which-url` reads environment variables that hosting providers set automatically. No configuration needed.
57
-
58
- **Resolution priority:**
81
+ Reads environment variables that hosting providers set automatically:
59
82
 
60
83
  1. `APP_URL` env var (your override — always wins)
61
- 2. Provider auto-detection
62
- 3. `window.location.origin` (browser)
63
- 4. `http://localhost:${PORT || 3000}` (development)
64
- 5. Throws in production if nothing detected
65
-
66
- ## Provider support
67
-
68
- | Provider | Detection | URL source |
69
- |----------|-----------|------------|
70
- | **Vercel** | `VERCEL` | `VERCEL_PROJECT_PRODUCTION_URL` / `VERCEL_BRANCH_URL` / `VERCEL_URL` |
71
- | **Netlify** | `NETLIFY` | `URL` / `DEPLOY_PRIME_URL` / `DEPLOY_URL` |
72
- | **Cloudflare Pages** | `CF_PAGES` | `CF_PAGES_URL` |
73
- | **Railway** | `RAILWAY_PUBLIC_DOMAIN` | `RAILWAY_PUBLIC_DOMAIN` |
74
- | **Fly.io** | `FLY_APP_NAME` | `{app}.fly.dev` |
75
- | **Render** | `RENDER` | `RENDER_EXTERNAL_URL` |
76
- | **DigitalOcean** | `DIGITALOCEAN_APP_PLATFORM` | `APP_URL` |
77
- | **Heroku** | `HEROKU_APP_NAME` | `{app}.herokuapp.com` |
84
+ 2. Provider auto-detection (Vercel, Netlify, etc.)
85
+ 3. `window.location.origin` (browser fallback)
86
+ 4. `http://localhost:${PORT || 3000}` (development fallback)
87
+
88
+ If nothing is detected in production, the singleton logs a warning and returns empty strings. Call `createUrl()` directly if you want it to throw instead.
78
89
 
79
90
  ## Override with `APP_URL`
80
91
 
81
- Set `APP_URL` to override auto-detection. Useful for custom domains, tunnels, or unsupported providers.
92
+ Set `APP_URL` when auto-detection isn't enough custom domains, tunnels, or unsupported providers:
82
93
 
83
94
  ```bash
84
95
  # .env.local
85
96
  APP_URL=https://myapp.com
86
97
  ```
87
98
 
88
- `NEXT_PUBLIC_APP_URL` also works (for client-side access in Next.js).
99
+ Works with or without protocol (`APP_URL=myapp.com` `https://myapp.com`).
89
100
 
90
- ## Local development
101
+ **Client-side frameworks:** All framework prefixes are supported automatically — `NEXT_PUBLIC_APP_URL`, `VITE_APP_URL`, `PUBLIC_APP_URL`, `NUXT_ENV_APP_URL`, etc.
91
102
 
92
- Zero config — auto-detects `http://localhost:3000` (or `PORT` if set).
103
+ ## Platform support
93
104
 
94
- ```bash
95
- # Custom port
96
- APP_URL=http://localhost:4000
105
+ | Platform | Detection | URL source | Verified |
106
+ |----------|-----------|------------|:--------:|
107
+ | **Vercel** | `VERCEL` | `VERCEL_PROJECT_PRODUCTION_URL` / `VERCEL_BRANCH_URL` / `VERCEL_URL` | [x] |
108
+ | **Netlify** | `NETLIFY` | `URL` / `DEPLOY_PRIME_URL` / `DEPLOY_URL` | [ ] |
109
+ | **Cloudflare Pages** | `CF_PAGES` | `CF_PAGES_URL` | [ ] |
110
+ | **Railway** | `RAILWAY_PUBLIC_DOMAIN` | `RAILWAY_PUBLIC_DOMAIN` | [ ] |
111
+ | **Fly.io** | `FLY_APP_NAME` | `{app}.fly.dev` | [ ] |
112
+ | **Render** | `RENDER` | `RENDER_EXTERNAL_URL` | [ ] |
113
+ | **DigitalOcean** | `DIGITALOCEAN_APP_PLATFORM` | `APP_URL` | [ ] |
114
+ | **Heroku** | `HEROKU_APP_NAME` | `{app}.herokuapp.com` | [ ] |
97
115
 
98
- # Custom local domain
99
- APP_URL=http://myapp.local:3000
116
+ On the client, Vercel's framework-prefixed env vars (`NEXT_PUBLIC_VERCEL_URL`, `VITE_VERCEL_URL`, etc.) are detected automatically.
100
117
 
101
- # Local HTTPS (mkcert)
102
- APP_URL=https://localhost:3000
118
+ **Help us verify:** If you're using one of these providers, [open an issue](https://github.com/manishrc/which-url/issues) with the output of `import appUrl from 'which-url'; console.log(appUrl)` from your deployment. We'll mark it as verified.
103
119
 
104
- # Tunnel
105
- APP_URL=https://abc123.ngrok-free.app
106
- ```
120
+ ## Examples
107
121
 
108
- For tunnels with dynamic URLs:
122
+ ```typescript
123
+ import { origin, hostname, isProduction } from 'which-url'
109
124
 
110
- ```bash
111
- APP_URL=$(curl -s localhost:4040/api/tunnels | jq -r '.tunnels[0].public_url') npm run dev
125
+ // Better Auth
126
+ betterAuth({ baseURL: origin })
127
+
128
+ // API calls
129
+ fetch(`${origin}/api/data`)
130
+
131
+ // CORS
132
+ cors({ origin })
133
+
134
+ // Cookies
135
+ cookie.domain = hostname
136
+
137
+ // Environment checks
138
+ if (isProduction) {
139
+ enableAnalytics()
140
+ }
112
141
  ```
113
142
 
114
- ## Error handling
143
+ ## Gotchas
115
144
 
116
- In **production**, `which-url` throws if it can't detect the URL — preventing silent misconfiguration (broken OAuth, CORS, emails pointing to localhost).
145
+ ### Vercel: Redeploy after assigning a custom domain
117
146
 
118
- Use `createUrl` with a fallback to opt into lenient behavior:
147
+ 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.
119
148
 
120
- ```typescript
121
- import { createUrl } from 'which-url'
149
+ ## Advanced
122
150
 
123
- const url = createUrl({ fallback: 'https://fallback.example.com' })
124
- url.href // never throws
125
- ```
151
+ ### Tunnels (ngrok, Cloudflare Tunnel)
152
+
153
+ Tunnel URLs can't be auto-detected — they're external to the app process. Set `APP_URL`:
126
154
 
127
- In **development**, it always falls back to localhost.
155
+ ```bash
156
+ APP_URL=https://abc123.ngrok-free.app npm run dev
157
+ ```
128
158
 
129
- ## Cloudflare Workers
159
+ ### Cloudflare Workers
130
160
 
131
161
  Cloudflare Workers use runtime `env` bindings instead of `process.env`. Set `APP_URL` in `wrangler.toml`:
132
162
 
@@ -135,29 +165,17 @@ Cloudflare Workers use runtime `env` bindings instead of `process.env`. Set `APP
135
165
  APP_URL = "https://myapp.workers.dev"
136
166
  ```
137
167
 
138
- Modern wrangler polyfills `process.env` from `[vars]`, so `which-url` picks it up automatically. Cloudflare Pages build-time env vars also work.
139
-
140
- ## Integrations
141
-
142
- ### Better Auth
143
-
144
- ```typescript
145
- import appUrl from 'which-url'
146
- import { betterAuth } from 'better-auth'
168
+ Modern wrangler polyfills `process.env` from `[vars]`, so `which-url` picks it up automatically.
147
169
 
148
- export const auth = betterAuth({
149
- baseURL: appUrl.href,
150
- // ...
151
- })
152
- ```
170
+ ### `createUrl(options?)`
153
171
 
154
- ### NextAuth / Auth.js
172
+ Re-resolves the URL from the current environment. Unlike the singleton (which catches errors), `createUrl()` throws in production if no URL is detected.
155
173
 
156
174
  ```typescript
157
- import appUrl from 'which-url'
175
+ import { createUrl } from 'which-url'
158
176
 
159
- // No need to set NEXTAUTH_URL manually
160
- process.env.NEXTAUTH_URL = appUrl.href
177
+ const url = createUrl({ fallback: 'https://fallback.example.com' })
178
+ url.origin // never throws
161
179
  ```
162
180
 
163
181
  ## API
@@ -168,30 +186,19 @@ An object with URL properties and environment helpers.
168
186
 
169
187
  ### Named exports
170
188
 
171
- | Export | Type | Description |
172
- |--------|------|-------------|
173
- | `href` | `string` | Full URL (`https://myapp.vercel.app`) |
174
- | `origin` | `string` | Origin (`https://myapp.vercel.app`) |
175
- | `hostname` | `string` | Hostname (`myapp.vercel.app`) |
176
- | `host` | `string` | Host with port (`myapp.vercel.app`) |
177
- | `protocol` | `string` | Protocol (`https:`) |
178
- | `port` | `string` | Port (empty if default) |
189
+ | Export | Type | Example |
190
+ |--------|------|---------|
191
+ | `origin` | `string` | `"https://myapp.vercel.app"` |
192
+ | `hostname` | `string` | `"myapp.vercel.app"` |
193
+ | `host` | `string` | `"myapp.vercel.app"` or `"localhost:3000"` |
194
+ | `href` | `string` | Same as `origin` |
195
+ | `protocol` | `string` | `"https:"` |
196
+ | `port` | `string` | `""` or `"3000"` |
179
197
  | `env` | `AppEnv` | `"production"` \| `"preview"` \| `"local"` |
180
- | `isProduction` | `boolean` | `true` if production |
181
- | `isPreview` | `boolean` | `true` if preview/staging |
182
- | `isLocal` | `boolean` | `true` if local development |
183
- | `createUrl(options?)` | `function` | Create a new instance with custom options |
184
-
185
- ### `createUrl(options?)`
186
-
187
- ```typescript
188
- createUrl({ fallback?: string }): WhichUrl
189
- ```
190
-
191
- Re-resolves the URL from current environment. Use for:
192
- - Custom fallbacks (never throws)
193
- - Re-resolution in tests
194
- - Dynamic configuration
198
+ | `platform` | `Platform` | `"vercel"` \| `"netlify"` \| ... \| `null` |
199
+ | `isProduction` | `boolean` | |
200
+ | `isPreview` | `boolean` | |
201
+ | `isLocal` | `boolean` | |
195
202
 
196
203
  ## License
197
204
 
package/dist/env-var.d.ts CHANGED
@@ -1,3 +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
+ */
1
9
  export declare function getVar(env: Record<string, string | undefined>, name: string): string | undefined;
2
10
  export declare function getEnv(): Record<string, string | undefined>;
3
11
  //# sourceMappingURL=env-var.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"env-var.d.ts","sourceRoot":"","sources":["../src/env-var.ts"],"names":[],"mappings":"AAaA,wBAAgB,MAAM,CACpB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACvC,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,SAAS,CAMpB;AAED,wBAAgB,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAK3D"}
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/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { WhichUrl, AppEnv, CreateUrlOptions } from "./types";
1
+ import type { WhichUrl, AppEnv, Platform, CreateUrlOptions } from "./types";
2
2
  export declare function createUrl(options?: CreateUrlOptions): WhichUrl;
3
3
  declare let _resolved: WhichUrl;
4
4
  export declare const href: string;
@@ -8,9 +8,10 @@ export declare const host: string;
8
8
  export declare const protocol: string;
9
9
  export declare const port: string;
10
10
  export declare const env: AppEnv;
11
+ export declare const platform: Platform;
11
12
  export declare const isProduction: boolean;
12
13
  export declare const isPreview: boolean;
13
14
  export declare const isLocal: boolean;
14
15
  export default _resolved;
15
- export type { WhichUrl, AppEnv, CreateUrlOptions };
16
+ export type { WhichUrl, AppEnv, Platform, CreateUrlOptions };
16
17
  //# sourceMappingURL=index.d.ts.map
@@ -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,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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE3E,wBAAgB,SAAS,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,QAAQ,CAkB9D;AAGD,QAAA,IAAI,SAAS,EAAE,QAAQ,CAAA;AAuBvB,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,QAAQ,EAAE,QAA6B,CAAA;AACpD,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,QAAQ,EAAE,gBAAgB,EAAE,CAAA"}
package/dist/index.js CHANGED
@@ -1,18 +1,39 @@
1
1
  // src/env-var.ts
2
- var FRAMEWORK_PREFIXES = [
3
- "",
4
- "NEXT_PUBLIC_",
5
- "NUXT_ENV_",
6
- "VITE_",
7
- "PUBLIC_",
8
- "REACT_APP_",
9
- "GATSBY_",
10
- "VUE_APP_",
11
- "REDWOOD_ENV_",
12
- "SANITY_STUDIO_"
13
- ];
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
+ }
14
18
  function getVar(env, name) {
15
- for (const prefix of FRAMEWORK_PREFIXES) {
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) {
16
37
  const val = env[prefix + name];
17
38
  if (val)
18
39
  return val;
@@ -148,6 +169,15 @@ function resolveUrl(options) {
148
169
  }
149
170
  throw new Error("which-url: Cannot detect app URL. Set APP_URL environment variable.");
150
171
  }
172
+ function resolvePlatform() {
173
+ const env = getEnv();
174
+ for (const p of providers) {
175
+ if (p.detect(env)) {
176
+ return p.name;
177
+ }
178
+ }
179
+ return null;
180
+ }
151
181
 
152
182
  // src/env.ts
153
183
  var validEnvs = ["production", "preview", "local"];
@@ -174,6 +204,7 @@ function createUrl(options) {
174
204
  const resolved = resolveUrl(options);
175
205
  const parsed = new URL(resolved);
176
206
  const env = resolveEnv();
207
+ const platform = resolvePlatform();
177
208
  return {
178
209
  href: parsed.origin,
179
210
  origin: parsed.origin,
@@ -182,6 +213,7 @@ function createUrl(options) {
182
213
  protocol: parsed.protocol,
183
214
  port: parsed.port,
184
215
  env,
216
+ platform,
185
217
  isProduction: env === "production",
186
218
  isPreview: env === "preview",
187
219
  isLocal: env === "local"
@@ -200,6 +232,7 @@ try {
200
232
  protocol: "",
201
233
  port: "",
202
234
  env: "local",
235
+ platform: null,
203
236
  isProduction: false,
204
237
  isPreview: false,
205
238
  isLocal: true
@@ -212,6 +245,7 @@ var host = _resolved.host;
212
245
  var protocol = _resolved.protocol;
213
246
  var port = _resolved.port;
214
247
  var env = _resolved.env;
248
+ var platform = _resolved.platform;
215
249
  var isProduction = _resolved.isProduction;
216
250
  var isPreview = _resolved.isPreview;
217
251
  var isLocal = _resolved.isLocal;
@@ -219,6 +253,7 @@ var src_default = _resolved;
219
253
  export {
220
254
  protocol,
221
255
  port,
256
+ platform,
222
257
  origin,
223
258
  isProduction,
224
259
  isPreview,
package/dist/resolve.d.ts CHANGED
@@ -1,3 +1,4 @@
1
- import type { CreateUrlOptions } from "./types";
1
+ import type { CreateUrlOptions, Platform } from "./types";
2
2
  export declare function resolveUrl(options?: CreateUrlOptions): string;
3
+ export declare function resolvePlatform(): Platform;
3
4
  //# sourceMappingURL=resolve.d.ts.map
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEzD,wBAAgB,UAAU,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAoC7D;AAED,wBAAgB,eAAe,IAAI,QAAQ,CAQ1C"}
package/dist/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export type AppEnv = "production" | "preview" | "local";
2
+ export type Platform = "vercel" | "netlify" | "cloudflare" | "railway" | "fly" | "render" | "digitalocean" | "heroku" | null;
2
3
  export interface WhichUrl {
3
4
  href: string;
4
5
  origin: string;
@@ -7,6 +8,7 @@ export interface WhichUrl {
7
8
  protocol: string;
8
9
  port: string;
9
10
  env: AppEnv;
11
+ platform: Platform;
10
12
  isProduction: boolean;
11
13
  isPreview: boolean;
12
14
  isLocal: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,MAAM,GAAG,YAAY,GAAG,SAAS,GAAG,OAAO,CAAA;AAEvD,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,OAAO,CAAA;IACrB,SAAS,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,KAAK,OAAO,CAAA;IAC5D,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,KAAK,MAAM,GAAG,IAAI,CAAA;IACtE,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,KAAK,MAAM,CAAA;CAChE"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,MAAM,GAAG,YAAY,GAAG,SAAS,GAAG,OAAO,CAAA;AAEvD,MAAM,MAAM,QAAQ,GAChB,QAAQ,GACR,SAAS,GACT,YAAY,GACZ,SAAS,GACT,KAAK,GACL,QAAQ,GACR,cAAc,GACd,QAAQ,GACR,IAAI,CAAA;AAER,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,QAAQ,CAAA;IAClB,YAAY,EAAE,OAAO,CAAA;IACrB,SAAS,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,KAAK,OAAO,CAAA;IAC5D,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,KAAK,MAAM,GAAG,IAAI,CAAA;IACtE,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,KAAK,MAAM,CAAA;CAChE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "which-url",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Auto-detect your app's URL across hosting providers. Zero config.",
5
5
  "type": "module",
6
6
  "exports": {