which-url 0.0.1

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 ADDED
@@ -0,0 +1,195 @@
1
+ # which-url
2
+
3
+ Auto-detect your app's URL across hosting providers. Zero config.
4
+
5
+ ```bash
6
+ npm install which-url
7
+ ```
8
+
9
+ ```typescript
10
+ import whichUrl from 'which-url'
11
+
12
+ whichUrl.href // "https://myapp.vercel.app"
13
+ whichUrl.hostname // "myapp.vercel.app"
14
+ ```
15
+
16
+ Works on Vercel, Netlify, Cloudflare Pages, Railway, Fly.io, Render, DigitalOcean, and Heroku — automatically.
17
+
18
+ ## Usage
19
+
20
+ ### Named exports (plain strings — zero type friction)
21
+
22
+ ```typescript
23
+ import { href, hostname, origin, isProduction } from 'which-url'
24
+
25
+ // Pass directly to any function that expects a string
26
+ process.env.BETTER_AUTH_URL = href
27
+ fetch(`${href}/api/data`)
28
+ cookie.domain = hostname
29
+
30
+ if (isProduction) {
31
+ // production-only logic
32
+ }
33
+ ```
34
+
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
+ ## How it works
56
+
57
+ `which-url` reads environment variables that hosting providers set automatically. No configuration needed.
58
+
59
+ **Resolution priority:**
60
+
61
+ 1. `APP_URL` env var (your override — always wins)
62
+ 2. Provider auto-detection
63
+ 3. `window.location.origin` (browser)
64
+ 4. `http://localhost:${PORT || 3000}` (development)
65
+ 5. Throws in production if nothing detected
66
+
67
+ ## Provider support
68
+
69
+ | Provider | Detection | URL source |
70
+ |----------|-----------|------------|
71
+ | **Vercel** | `VERCEL` | `VERCEL_PROJECT_PRODUCTION_URL` / `VERCEL_BRANCH_URL` / `VERCEL_URL` |
72
+ | **Netlify** | `NETLIFY` | `URL` / `DEPLOY_PRIME_URL` / `DEPLOY_URL` |
73
+ | **Cloudflare Pages** | `CF_PAGES` | `CF_PAGES_URL` |
74
+ | **Railway** | `RAILWAY_PUBLIC_DOMAIN` | `RAILWAY_PUBLIC_DOMAIN` |
75
+ | **Fly.io** | `FLY_APP_NAME` | `{app}.fly.dev` |
76
+ | **Render** | `RENDER` | `RENDER_EXTERNAL_URL` |
77
+ | **DigitalOcean** | `DIGITALOCEAN_APP_PLATFORM` | `APP_URL` |
78
+ | **Heroku** | `HEROKU_APP_NAME` | `{app}.herokuapp.com` |
79
+
80
+ ## Override with `APP_URL`
81
+
82
+ Set `APP_URL` to override auto-detection. Useful for custom domains, tunnels, or unsupported providers.
83
+
84
+ ```bash
85
+ # .env.local
86
+ APP_URL=https://myapp.com
87
+ ```
88
+
89
+ `NEXT_PUBLIC_APP_URL` also works (for client-side access in Next.js).
90
+
91
+ ## Local development
92
+
93
+ Zero config — auto-detects `http://localhost:3000` (or `PORT` if set).
94
+
95
+ ```bash
96
+ # Custom port
97
+ APP_URL=http://localhost:4000
98
+
99
+ # Custom local domain
100
+ APP_URL=http://myapp.local:3000
101
+
102
+ # Local HTTPS (mkcert)
103
+ APP_URL=https://localhost:3000
104
+
105
+ # Tunnel
106
+ APP_URL=https://abc123.ngrok-free.app
107
+ ```
108
+
109
+ For tunnels with dynamic URLs:
110
+
111
+ ```bash
112
+ APP_URL=$(curl -s localhost:4040/api/tunnels | jq -r '.tunnels[0].public_url') npm run dev
113
+ ```
114
+
115
+ ## Error handling
116
+
117
+ In **production**, `which-url` throws if it can't detect the URL — preventing silent misconfiguration (broken OAuth, CORS, emails pointing to localhost).
118
+
119
+ Use `createUrl` with a fallback to opt into lenient behavior:
120
+
121
+ ```typescript
122
+ import { createUrl } from 'which-url'
123
+
124
+ const url = createUrl({ fallback: 'https://fallback.example.com' })
125
+ url.href // never throws
126
+ ```
127
+
128
+ In **development**, it always falls back to localhost.
129
+
130
+ ## Cloudflare Workers
131
+
132
+ Cloudflare Workers use runtime `env` bindings instead of `process.env`. Set `APP_URL` in `wrangler.toml`:
133
+
134
+ ```toml
135
+ [vars]
136
+ APP_URL = "https://myapp.workers.dev"
137
+ ```
138
+
139
+ Modern wrangler polyfills `process.env` from `[vars]`, so `which-url` picks it up automatically. Cloudflare Pages build-time env vars also work.
140
+
141
+ ## Integrations
142
+
143
+ ### Better Auth
144
+
145
+ ```typescript
146
+ import { href } from 'which-url'
147
+ import { betterAuth } from 'better-auth'
148
+
149
+ export const auth = betterAuth({
150
+ baseURL: href,
151
+ // ...
152
+ })
153
+ ```
154
+
155
+ ### NextAuth / Auth.js
156
+
157
+ ```typescript
158
+ import { href } from 'which-url'
159
+
160
+ // No need to set NEXTAUTH_URL manually
161
+ process.env.NEXTAUTH_URL = href
162
+ ```
163
+
164
+ ## API
165
+
166
+ ### Named exports
167
+
168
+ | Export | Type | Description |
169
+ |--------|------|-------------|
170
+ | `href` | `string` | Full URL (`https://myapp.vercel.app`) |
171
+ | `origin` | `string` | Origin (`https://myapp.vercel.app`) |
172
+ | `hostname` | `string` | Hostname (`myapp.vercel.app`) |
173
+ | `host` | `string` | Host with port (`myapp.vercel.app`) |
174
+ | `protocol` | `string` | Protocol (`https:`) |
175
+ | `port` | `string` | Port (empty if default) |
176
+ | `env` | `AppEnv` | `"production"` \| `"preview"` \| `"local"` |
177
+ | `isProduction` | `boolean` | `true` if production |
178
+ | `isPreview` | `boolean` | `true` if preview/staging |
179
+ | `isLocal` | `boolean` | `true` if local development |
180
+ | `createUrl(options?)` | `function` | Create a new instance with custom options |
181
+
182
+ ### `createUrl(options?)`
183
+
184
+ ```typescript
185
+ createUrl({ fallback?: string }): WhichUrl
186
+ ```
187
+
188
+ Re-resolves the URL from current environment. Use for:
189
+ - Custom fallbacks (never throws)
190
+ - Re-resolution in tests
191
+ - Dynamic configuration
192
+
193
+ ## License
194
+
195
+ MIT
package/dist/env.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { AppEnv } from "./types";
2
+ export declare function resolveEnv(): AppEnv;
3
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +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"}
package/dist/index.cjs ADDED
@@ -0,0 +1,224 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __moduleCache = /* @__PURE__ */ new WeakMap;
6
+ var __toCommonJS = (from) => {
7
+ var entry = __moduleCache.get(from), desc;
8
+ if (entry)
9
+ return entry;
10
+ entry = __defProp({}, "__esModule", { value: true });
11
+ if (from && typeof from === "object" || typeof from === "function")
12
+ __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
13
+ get: () => from[key],
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ }));
16
+ __moduleCache.set(from, entry);
17
+ return entry;
18
+ };
19
+ var __export = (target, all) => {
20
+ for (var name in all)
21
+ __defProp(target, name, {
22
+ get: all[name],
23
+ enumerable: true,
24
+ configurable: true,
25
+ set: (newValue) => all[name] = () => newValue
26
+ });
27
+ };
28
+
29
+ // src/index.ts
30
+ var exports_src = {};
31
+ __export(exports_src, {
32
+ protocol: () => protocol,
33
+ port: () => port,
34
+ origin: () => origin,
35
+ isProduction: () => isProduction,
36
+ isPreview: () => isPreview,
37
+ isLocal: () => isLocal,
38
+ href: () => href,
39
+ hostname: () => hostname,
40
+ host: () => host,
41
+ env: () => env,
42
+ default: () => src_default,
43
+ createUrl: () => createUrl
44
+ });
45
+ module.exports = __toCommonJS(exports_src);
46
+
47
+ // src/providers.ts
48
+ var providers = [
49
+ {
50
+ name: "vercel",
51
+ detect: (env) => env.VERCEL === "1",
52
+ resolveUrl: (env) => {
53
+ if (env.VERCEL_ENV === "production") {
54
+ return env.VERCEL_PROJECT_PRODUCTION_URL ?? env.VERCEL_URL ?? null;
55
+ }
56
+ return env.VERCEL_BRANCH_URL ?? env.VERCEL_URL ?? null;
57
+ },
58
+ resolveEnv: (env) => {
59
+ if (env.VERCEL_ENV === "production")
60
+ return "production";
61
+ if (env.VERCEL_ENV === "preview")
62
+ return "preview";
63
+ return "local";
64
+ }
65
+ },
66
+ {
67
+ name: "netlify",
68
+ detect: (env) => env.NETLIFY === "true",
69
+ resolveUrl: (env) => {
70
+ if (env.CONTEXT === "production")
71
+ return env.URL ?? null;
72
+ return env.DEPLOY_PRIME_URL ?? env.DEPLOY_URL ?? null;
73
+ },
74
+ resolveEnv: (env) => {
75
+ if (env.CONTEXT === "production")
76
+ return "production";
77
+ if (env.CONTEXT === "deploy-preview" || env.CONTEXT === "branch-deploy")
78
+ return "preview";
79
+ return "local";
80
+ }
81
+ },
82
+ {
83
+ name: "cloudflare",
84
+ detect: (env) => env.CF_PAGES === "1",
85
+ resolveUrl: (env) => env.CF_PAGES_URL ?? null,
86
+ resolveEnv: (env) => {
87
+ if (env.CF_PAGES_BRANCH === "main" || env.CF_PAGES_BRANCH === "master")
88
+ return "production";
89
+ return "preview";
90
+ }
91
+ },
92
+ {
93
+ name: "railway",
94
+ detect: (env) => !!env.RAILWAY_PUBLIC_DOMAIN,
95
+ resolveUrl: (env) => env.RAILWAY_PUBLIC_DOMAIN ?? null,
96
+ resolveEnv: (env) => {
97
+ if (env.RAILWAY_ENVIRONMENT === "production")
98
+ return "production";
99
+ return "preview";
100
+ }
101
+ },
102
+ {
103
+ name: "render",
104
+ detect: (env) => env.RENDER === "true",
105
+ resolveUrl: (env) => env.RENDER_EXTERNAL_URL ?? null,
106
+ resolveEnv: (env) => {
107
+ if (env.IS_PULL_REQUEST === "true")
108
+ return "preview";
109
+ return "production";
110
+ }
111
+ },
112
+ {
113
+ name: "fly",
114
+ detect: (env) => !!env.FLY_APP_NAME,
115
+ resolveUrl: (env) => {
116
+ const name = env.FLY_APP_NAME;
117
+ return name ? `${name}.fly.dev` : null;
118
+ },
119
+ resolveEnv: () => "production"
120
+ },
121
+ {
122
+ name: "digitalocean",
123
+ detect: (env) => !!env.DIGITALOCEAN_APP_PLATFORM,
124
+ resolveUrl: (env) => env.APP_URL ?? null,
125
+ resolveEnv: () => "production"
126
+ },
127
+ {
128
+ name: "heroku",
129
+ detect: (env) => !!env.HEROKU_APP_NAME,
130
+ resolveUrl: (env) => {
131
+ const name = env.HEROKU_APP_NAME;
132
+ return name ? `${name}.herokuapp.com` : null;
133
+ },
134
+ resolveEnv: () => "production"
135
+ }
136
+ ];
137
+
138
+ // src/normalize.ts
139
+ function normalizeUrl(raw, protocol = "https") {
140
+ let url = raw.trim();
141
+ url = url.replace(/\/+$/, "");
142
+ if (url.startsWith("http://") || url.startsWith("https://")) {
143
+ return url;
144
+ }
145
+ url = url.replace(/^\/+/, "");
146
+ return `${protocol}://${url}`;
147
+ }
148
+
149
+ // src/resolve.ts
150
+ function resolveUrl(options) {
151
+ const env = typeof process !== "undefined" ? process.env : {};
152
+ const override = env.APP_URL ?? env.NEXT_PUBLIC_APP_URL;
153
+ if (override)
154
+ return normalizeUrl(override);
155
+ for (const p of providers) {
156
+ if (p.detect(env)) {
157
+ const url = p.resolveUrl(env);
158
+ if (url)
159
+ return normalizeUrl(url);
160
+ }
161
+ }
162
+ if (typeof window !== "undefined" && window.location?.origin) {
163
+ return window.location.origin;
164
+ }
165
+ if (options?.fallback)
166
+ return normalizeUrl(options.fallback);
167
+ const isProduction = env.NODE_ENV === "production";
168
+ if (isProduction) {
169
+ throw new Error(`thisapp: Cannot detect app URL. Set APP_URL environment variable.
170
+ ` + "See: https://github.com/manishrc/thisapp#configuration");
171
+ }
172
+ const port = env.PORT ?? "3000";
173
+ return `http://localhost:${port}`;
174
+ }
175
+
176
+ // src/env.ts
177
+ function resolveEnv() {
178
+ const env = typeof process !== "undefined" ? process.env : {};
179
+ const override = env.APP_ENV;
180
+ if (override === "production" || override === "preview" || override === "local") {
181
+ return override;
182
+ }
183
+ if (env.NODE_ENV === "development")
184
+ return "local";
185
+ for (const p of providers) {
186
+ if (p.detect(env)) {
187
+ return p.resolveEnv(env);
188
+ }
189
+ }
190
+ if (env.NODE_ENV === "production")
191
+ return "production";
192
+ return "local";
193
+ }
194
+
195
+ // src/index.ts
196
+ function createUrl(options) {
197
+ const resolved = resolveUrl(options);
198
+ const parsed = new URL(resolved);
199
+ const env = resolveEnv();
200
+ return {
201
+ href: parsed.origin,
202
+ origin: parsed.origin,
203
+ hostname: parsed.hostname,
204
+ host: parsed.host,
205
+ protocol: parsed.protocol,
206
+ port: parsed.port,
207
+ env,
208
+ isProduction: env === "production",
209
+ isPreview: env === "preview",
210
+ isLocal: env === "local"
211
+ };
212
+ }
213
+ var _resolved = createUrl();
214
+ var href = _resolved.href;
215
+ var origin = _resolved.origin;
216
+ var hostname = _resolved.hostname;
217
+ var host = _resolved.host;
218
+ var protocol = _resolved.protocol;
219
+ var port = _resolved.port;
220
+ var env = _resolved.env;
221
+ var isProduction = _resolved.isProduction;
222
+ var isPreview = _resolved.isPreview;
223
+ var isLocal = _resolved.isLocal;
224
+ var src_default = _resolved;
@@ -0,0 +1,16 @@
1
+ import type { WhichUrl, AppEnv, CreateUrlOptions } from "./types";
2
+ export declare function createUrl(options?: CreateUrlOptions): WhichUrl;
3
+ declare const _resolved: WhichUrl;
4
+ export declare const href: string;
5
+ export declare const origin: string;
6
+ export declare const hostname: string;
7
+ export declare const host: string;
8
+ export declare const protocol: string;
9
+ export declare const port: string;
10
+ export declare const env: AppEnv;
11
+ export declare const isProduction: boolean;
12
+ export declare const isPreview: boolean;
13
+ export declare const isLocal: boolean;
14
+ export default _resolved;
15
+ export type { WhichUrl, AppEnv, CreateUrlOptions };
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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"}
package/dist/index.js ADDED
@@ -0,0 +1,184 @@
1
+ // src/providers.ts
2
+ var providers = [
3
+ {
4
+ name: "vercel",
5
+ detect: (env) => !!env.VERCEL,
6
+ resolveUrl: (env) => {
7
+ if (env.VERCEL_ENV === "production") {
8
+ return env.VERCEL_PROJECT_PRODUCTION_URL || env.VERCEL_URL || null;
9
+ }
10
+ return env.VERCEL_BRANCH_URL || env.VERCEL_URL || null;
11
+ },
12
+ resolveEnv: (env) => {
13
+ if (env.VERCEL_ENV === "production")
14
+ return "production";
15
+ if (env.VERCEL_ENV === "preview")
16
+ return "preview";
17
+ return "local";
18
+ }
19
+ },
20
+ {
21
+ name: "netlify",
22
+ detect: (env) => !!env.NETLIFY,
23
+ resolveUrl: (env) => {
24
+ if (env.CONTEXT === "production")
25
+ return env.URL || null;
26
+ return env.DEPLOY_PRIME_URL || env.DEPLOY_URL || null;
27
+ },
28
+ resolveEnv: (env) => {
29
+ if (env.CONTEXT === "production")
30
+ return "production";
31
+ if (env.CONTEXT === "deploy-preview" || env.CONTEXT === "branch-deploy")
32
+ return "preview";
33
+ return "local";
34
+ }
35
+ },
36
+ {
37
+ name: "cloudflare",
38
+ detect: (env) => !!env.CF_PAGES,
39
+ resolveUrl: (env) => env.CF_PAGES_URL || null,
40
+ resolveEnv: (env) => {
41
+ if (env.CF_PAGES_BRANCH === "main" || env.CF_PAGES_BRANCH === "master")
42
+ return "production";
43
+ return "preview";
44
+ }
45
+ },
46
+ {
47
+ name: "railway",
48
+ detect: (env) => !!env.RAILWAY_PUBLIC_DOMAIN,
49
+ resolveUrl: (env) => env.RAILWAY_PUBLIC_DOMAIN || null,
50
+ resolveEnv: (env) => {
51
+ if (env.RAILWAY_ENVIRONMENT === "production")
52
+ return "production";
53
+ return "production";
54
+ }
55
+ },
56
+ {
57
+ name: "fly",
58
+ detect: (env) => !!env.FLY_APP_NAME,
59
+ resolveUrl: (env) => env.FLY_APP_NAME ? `${env.FLY_APP_NAME}.fly.dev` : null,
60
+ resolveEnv: () => "production"
61
+ },
62
+ {
63
+ name: "render",
64
+ detect: (env) => !!env.RENDER,
65
+ resolveUrl: (env) => env.RENDER_EXTERNAL_URL || null,
66
+ resolveEnv: (env) => {
67
+ if (env.IS_PULL_REQUEST === "true")
68
+ return "preview";
69
+ return "production";
70
+ }
71
+ },
72
+ {
73
+ name: "digitalocean",
74
+ detect: (env) => !!env.DIGITALOCEAN_APP_PLATFORM,
75
+ resolveUrl: (env) => env.APP_URL || null,
76
+ resolveEnv: () => "production"
77
+ },
78
+ {
79
+ name: "heroku",
80
+ detect: (env) => !!env.HEROKU_APP_NAME,
81
+ resolveUrl: (env) => env.HEROKU_APP_NAME ? `${env.HEROKU_APP_NAME}.herokuapp.com` : null,
82
+ resolveEnv: () => "production"
83
+ }
84
+ ];
85
+
86
+ // src/normalize.ts
87
+ function normalizeUrl(raw) {
88
+ let url = raw.trim().replace(/\/+$/, "");
89
+ if (url.startsWith("http://") || url.startsWith("https://")) {
90
+ return url;
91
+ }
92
+ return `https://${url}`;
93
+ }
94
+
95
+ // src/resolve.ts
96
+ function resolveUrl(options) {
97
+ const env = typeof process !== "undefined" ? process.env : {};
98
+ const override = env.APP_URL || env.NEXT_PUBLIC_APP_URL;
99
+ if (override)
100
+ return normalizeUrl(override);
101
+ for (const p of providers) {
102
+ if (p.detect(env)) {
103
+ const url = p.resolveUrl(env);
104
+ if (url)
105
+ return normalizeUrl(url);
106
+ }
107
+ }
108
+ if (typeof window !== "undefined" && window.location) {
109
+ return window.location.origin;
110
+ }
111
+ const isProduction = env.NODE_ENV === "production";
112
+ if (!isProduction) {
113
+ const port = env.PORT || "3000";
114
+ return `http://localhost:${port}`;
115
+ }
116
+ if (options?.fallback) {
117
+ return normalizeUrl(options.fallback);
118
+ }
119
+ throw new Error("which-url: Cannot detect app URL. Set APP_URL environment variable.");
120
+ }
121
+
122
+ // src/env.ts
123
+ var validEnvs = ["production", "preview", "local"];
124
+ 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;
128
+ }
129
+ if (env.NODE_ENV === "development")
130
+ return "local";
131
+ for (const p of providers) {
132
+ if (p.detect(env)) {
133
+ return p.resolveEnv(env);
134
+ }
135
+ }
136
+ if (env.NODE_ENV === "production")
137
+ return "production";
138
+ return "local";
139
+ }
140
+
141
+ // src/index.ts
142
+ function createUrl(options) {
143
+ const resolved = resolveUrl(options);
144
+ const parsed = new URL(resolved);
145
+ const env = resolveEnv();
146
+ return {
147
+ href: parsed.origin,
148
+ origin: parsed.origin,
149
+ hostname: parsed.hostname,
150
+ host: parsed.host,
151
+ protocol: parsed.protocol,
152
+ port: parsed.port,
153
+ env,
154
+ isProduction: env === "production",
155
+ isPreview: env === "preview",
156
+ isLocal: env === "local"
157
+ };
158
+ }
159
+ var _resolved = createUrl();
160
+ var href = _resolved.href;
161
+ var origin = _resolved.origin;
162
+ var hostname = _resolved.hostname;
163
+ var host = _resolved.host;
164
+ var protocol = _resolved.protocol;
165
+ var port = _resolved.port;
166
+ var env = _resolved.env;
167
+ var isProduction = _resolved.isProduction;
168
+ var isPreview = _resolved.isPreview;
169
+ var isLocal = _resolved.isLocal;
170
+ var src_default = _resolved;
171
+ export {
172
+ protocol,
173
+ port,
174
+ origin,
175
+ isProduction,
176
+ isPreview,
177
+ isLocal,
178
+ href,
179
+ hostname,
180
+ host,
181
+ env,
182
+ src_default as default,
183
+ createUrl
184
+ };
@@ -0,0 +1,2 @@
1
+ export declare function normalizeUrl(raw: string): string;
2
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMhD"}
@@ -0,0 +1,3 @@
1
+ import type { ProviderDetector } from "./types";
2
+ export declare const providers: ProviderDetector[];
3
+ //# sourceMappingURL=providers.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,3 @@
1
+ import type { CreateUrlOptions } from "./types";
2
+ export declare function resolveUrl(options?: CreateUrlOptions): string;
3
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,23 @@
1
+ export type AppEnv = "production" | "preview" | "local";
2
+ export interface WhichUrl {
3
+ href: string;
4
+ origin: string;
5
+ hostname: string;
6
+ host: string;
7
+ protocol: string;
8
+ port: string;
9
+ env: AppEnv;
10
+ isProduction: boolean;
11
+ isPreview: boolean;
12
+ isLocal: boolean;
13
+ }
14
+ export interface CreateUrlOptions {
15
+ fallback?: string;
16
+ }
17
+ export interface ProviderDetector {
18
+ name: string;
19
+ detect: (env: Record<string, string | undefined>) => boolean;
20
+ resolveUrl: (env: Record<string, string | undefined>) => string | null;
21
+ resolveEnv: (env: Record<string, string | undefined>) => AppEnv;
22
+ }
23
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +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"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "which-url",
3
+ "version": "0.0.1",
4
+ "description": "Auto-detect your app's URL across hosting providers. Zero config.",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "default": "./dist/index.js"
10
+ }
11
+ },
12
+ "main": "./dist/index.js",
13
+ "module": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "files": ["dist"],
16
+ "sideEffects": false,
17
+ "keywords": ["url", "base-url", "vercel", "netlify", "cloudflare", "railway", "hosting", "auto-detect", "which-url"],
18
+ "author": {
19
+ "name": "manishrc",
20
+ "url": "https://manishrc.com"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/manishrc/which-url"
25
+ },
26
+ "license": "MIT",
27
+ "scripts": {
28
+ "build": "bun build src/index.ts --outdir dist --format esm && tsc --emitDeclarationOnly --outDir dist",
29
+ "test": "bun test",
30
+ "prepublishOnly": "bun run build"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^25.6.0",
34
+ "typescript": "^5.8.0"
35
+ }
36
+ }