which-url 0.0.6 → 0.0.7

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
@@ -160,6 +160,8 @@ Zero config — [portless](https://portless.sh) sets `PORTLESS_URL` and `which-u
160
160
  origin → https://myapp.localhost
161
161
  ```
162
162
 
163
+ When sharing via Tailscale (`portless run --tailscale`), `PORTLESS_TAILSCALE_URL` takes priority so `origin` returns the publicly-reachable URL.
164
+
163
165
  ### Tunnels (ngrok, Cloudflare Tunnel)
164
166
 
165
167
  Tunnel URLs can't be auto-detected — they're external to the app process. Set `APP_URL`:
@@ -182,10 +184,13 @@ Modern wrangler polyfills `process.env` from `[vars]`, so `which-url` picks it u
182
184
  ### Debugging
183
185
 
184
186
  ```typescript
185
- import { debug } from 'which-url'
187
+ import appUrl from 'which-url'
186
188
 
187
- console.log(debug)
188
- // "platform=vercel | source=provider | url=https://myapp.com | env=production"
189
+ console.log(appUrl.debug)
190
+ // "[provider:vercel] url=myapp.com | env=production (vercel:production)"
191
+ // "[override] APP_URL=https://custom.com | env=production (NODE_ENV=production)"
192
+ // "[portless] PORTLESS_URL=https://myapp.localhost | env=local (NODE_ENV=development)"
193
+ // "[fallback] PORT=3004 | env=local (NODE_ENV=development)"
189
194
  ```
190
195
 
191
196
  ## API
@@ -206,8 +211,9 @@ An object with URL properties and environment helpers.
206
211
  | `port` | `string` | `""` or `"3000"` |
207
212
  | `env` | `AppEnv` | `"production"` \| `"preview"` \| `"local"` |
208
213
  | `platform` | `Platform` | `"vercel"` \| `"netlify"` \| ... \| `null` |
209
- | `source` | `Source` | `"override"` \| `"provider"` \| `"browser"` \| `"fallback"` \| `null` |
210
- | `debug` | `string` | `"platform=vercel \| source=provider \| url=https://myapp.com \| env=production"` |
214
+ | `debug`* | `string` | `"[provider:vercel] url=myapp.com \| env=production (vercel:production)"` |
215
+
216
+ \* `debug` is non-enumerable — excluded from `JSON.stringify` to avoid React hydration mismatches. Access via `appUrl.debug`.
211
217
  | `isProduction` | `boolean` | |
212
218
  | `isPreview` | `boolean` | |
213
219
  | `isLocal` | `boolean` | |
@@ -1 +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"}
1
+ {"version":3,"file":"env-var.d.ts","sourceRoot":"","sources":["../src/env-var.ts"],"names":[],"mappings":"AAkGA;;;;;;;GAOG;AACH,wBAAgB,MAAM,CACpB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACvC,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,SAAS,CA4BpB;AAED,wBAAgB,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAK3D"}
package/dist/index.d.ts CHANGED
@@ -1,16 +1,40 @@
1
- import type { WhichUrl, AppEnv, Platform } from "./types";
2
- declare let _resolved: WhichUrl;
1
+ import type { WhichUrlWithDebug, AppEnv, Platform } from "./types";
2
+ declare let _resolved: WhichUrlWithDebug;
3
+ /** Full URL including protocol — `"https://myapp.com"` */
3
4
  export declare const href: string;
5
+ /** Full origin — `"https://myapp.com"` (same as href) */
4
6
  export declare const origin: string;
7
+ /** Hostname without port — `"myapp.com"` */
5
8
  export declare const hostname: string;
9
+ /** Hostname with port — `"myapp.com"` or `"localhost:3000"` */
6
10
  export declare const host: string;
11
+ /** Protocol with colon — `"https:"` or `"http:"` */
7
12
  export declare const protocol: string;
13
+ /** Port string — `""` for default ports, `"3000"` for custom */
8
14
  export declare const port: string;
15
+ /** Current environment — `"production"`, `"preview"`, or `"local"` */
9
16
  export declare const env: AppEnv;
17
+ /** Detected hosting platform — `"vercel"`, `"netlify"`, etc. or `null` */
10
18
  export declare const platform: Platform;
19
+ /** `true` when running in production */
11
20
  export declare const isProduction: boolean;
21
+ /** `true` when running in a preview/staging deployment */
12
22
  export declare const isPreview: boolean;
23
+ /** `true` when running locally (development) */
13
24
  export declare const isLocal: boolean;
25
+ /**
26
+ * Auto-detected app URL with environment metadata.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * import appUrl from 'which-url'
31
+ *
32
+ * appUrl.origin // "https://myapp.com"
33
+ * appUrl.env // "production"
34
+ * appUrl.platform // "vercel"
35
+ * appUrl.debug // "[provider:vercel] url=myapp.com | env=production (vercel:production)"
36
+ * ```
37
+ */
14
38
  export default _resolved;
15
- export type { WhichUrl, AppEnv, Platform };
39
+ export type { WhichUrl, WhichUrlWithDebug, AppEnv, Platform, PlatformName } from "./types";
16
40
  //# 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,QAAQ,EAAE,MAAM,SAAS,CAAA;AAqCzD,QAAA,IAAI,SAAS,EAAE,QAAQ,CAAA;AA8BvB,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,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAqClE,QAAA,IAAI,SAAS,EAAE,iBAAiB,CAAA;AA6BhC,0DAA0D;AAC1D,eAAO,MAAM,IAAI,EAAE,MAAuB,CAAA;AAC1C,yDAAyD;AACzD,eAAO,MAAM,MAAM,EAAE,MAAyB,CAAA;AAC9C,4CAA4C;AAC5C,eAAO,MAAM,QAAQ,EAAE,MAA2B,CAAA;AAClD,+DAA+D;AAC/D,eAAO,MAAM,IAAI,EAAE,MAAuB,CAAA;AAC1C,oDAAoD;AACpD,eAAO,MAAM,QAAQ,EAAE,MAA2B,CAAA;AAClD,gEAAgE;AAChE,eAAO,MAAM,IAAI,EAAE,MAAuB,CAAA;AAC1C,sEAAsE;AACtE,eAAO,MAAM,GAAG,EAAE,MAAsB,CAAA;AACxC,0EAA0E;AAC1E,eAAO,MAAM,QAAQ,EAAE,QAA6B,CAAA;AACpD,wCAAwC;AACxC,eAAO,MAAM,YAAY,EAAE,OAAgC,CAAA;AAC3D,0DAA0D;AAC1D,eAAO,MAAM,SAAS,EAAE,OAA6B,CAAA;AACrD,gDAAgD;AAChD,eAAO,MAAM,OAAO,EAAE,OAA2B,CAAA;AAEjD;;;;;;;;;;;;GAYG;AACH,eAAe,SAAS,CAAA;AAExB,YAAY,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA"}
package/dist/index.js CHANGED
@@ -1,4 +1,12 @@
1
1
  // src/env-var.ts
2
+ var ENV_VARS = [
3
+ "VERCEL_ENV",
4
+ "VERCEL_URL",
5
+ "VERCEL_BRANCH_URL",
6
+ "VERCEL_PROJECT_PRODUCTION_URL",
7
+ "APP_URL",
8
+ "APP_ENV"
9
+ ];
2
10
  function readStatic(name) {
3
11
  switch (name) {
4
12
  case "VERCEL_ENV":
@@ -13,13 +21,17 @@ function readStatic(name) {
13
21
  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
22
  case "APP_ENV":
15
23
  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;
24
+ default:
25
+ return;
16
26
  }
17
27
  }
18
28
  function getVar(env, name) {
19
29
  if (typeof process !== "undefined" && process?.env && env === process.env) {
20
- const val = readStatic(name);
21
- if (val)
22
- return val;
30
+ if (ENV_VARS.includes(name)) {
31
+ const val = readStatic(name);
32
+ if (val)
33
+ return val;
34
+ }
23
35
  }
24
36
  const PREFIXES = [
25
37
  "",
@@ -149,8 +161,12 @@ function resolveUrl() {
149
161
  const override = getVar(env, "APP_URL");
150
162
  if (override)
151
163
  return { url: normalizeUrl(override), debugLabel: `[override] APP_URL=${override}` };
152
- if (env.PORTLESS_URL)
164
+ if (env.PORTLESS_TAILSCALE_URL) {
165
+ return { url: env.PORTLESS_TAILSCALE_URL, debugLabel: `[portless:tailscale] PORTLESS_TAILSCALE_URL=${env.PORTLESS_TAILSCALE_URL}` };
166
+ }
167
+ if (env.PORTLESS_URL) {
153
168
  return { url: env.PORTLESS_URL, debugLabel: `[portless] PORTLESS_URL=${env.PORTLESS_URL}` };
169
+ }
154
170
  for (const p of providers) {
155
171
  if (p.detect(env)) {
156
172
  const url = p.resolveUrl(env);
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,UAAU,IAAI,aAAa,CAkC1C;AAED,wBAAgB,eAAe,IAAI,QAAQ,CAQ1C"}
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,UAAU,IAAI,aAAa,CAuC1C;AAED,wBAAgB,eAAe,IAAI,QAAQ,CAQ1C"}
package/dist/types.d.ts CHANGED
@@ -1,21 +1,36 @@
1
1
  export type AppEnv = "production" | "preview" | "local";
2
- export type Platform = "vercel" | "netlify" | "cloudflare" | "railway" | "fly" | "render" | "digitalocean" | "heroku" | null;
2
+ export type PlatformName = "vercel" | "netlify" | "cloudflare" | "railway" | "fly" | "render" | "digitalocean" | "heroku";
3
+ export type Platform = PlatformName | null;
3
4
  export interface WhichUrl {
4
- href: string;
5
- origin: string;
6
- hostname: string;
7
- host: string;
8
- protocol: string;
9
- port: string;
10
- env: AppEnv;
11
- platform: Platform;
12
- debug: string;
13
- isProduction: boolean;
14
- isPreview: boolean;
15
- isLocal: boolean;
5
+ /** Full URL including protocol — `"https://myapp.com"` */
6
+ readonly href: string;
7
+ /** Full origin — `"https://myapp.com"` (same as href) */
8
+ readonly origin: string;
9
+ /** Hostname without port — `"myapp.com"` */
10
+ readonly hostname: string;
11
+ /** Hostname with port — `"myapp.com"` or `"localhost:3000"` */
12
+ readonly host: string;
13
+ /** Protocol with colon — `"https:"` or `"http:"` */
14
+ readonly protocol: string;
15
+ /** Port string — `""` for default ports, `"3000"` for custom */
16
+ readonly port: string;
17
+ /** Current environment — `"production"`, `"preview"`, or `"local"` */
18
+ readonly env: AppEnv;
19
+ /** Detected hosting platform — `"vercel"`, `"netlify"`, etc. or `null` */
20
+ readonly platform: Platform;
21
+ /** `true` when running in production */
22
+ readonly isProduction: boolean;
23
+ /** `true` when running in a preview/staging deployment */
24
+ readonly isPreview: boolean;
25
+ /** `true` when running locally (development) */
26
+ readonly isLocal: boolean;
27
+ }
28
+ export interface WhichUrlWithDebug extends WhichUrl {
29
+ /** Resolution debug string (non-enumerable — excluded from JSON.stringify and object spread). */
30
+ readonly debug: string;
16
31
  }
17
32
  export interface ProviderDetector {
18
- name: string;
33
+ name: PlatformName;
19
34
  detect: (env: Record<string, string | undefined>) => boolean;
20
35
  resolveUrl: (env: Record<string, string | undefined>) => string | null;
21
36
  resolveEnv: (env: Record<string, string | undefined>) => AppEnv;
@@ -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,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,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,OAAO,CAAA;IACrB,SAAS,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;CACjB;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,YAAY,GACpB,QAAQ,GACR,SAAS,GACT,YAAY,GACZ,SAAS,GACT,KAAK,GACL,QAAQ,GACR,cAAc,GACd,QAAQ,CAAA;AAEZ,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,IAAI,CAAA;AAE1C,MAAM,WAAW,QAAQ;IACvB,0DAA0D;IAC1D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,yDAAyD;IACzD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,oDAAoD;IACpD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,gEAAgE;IAChE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,sEAAsE;IACtE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,wCAAwC;IACxC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAA;IAC9B,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAC1B;AAED,MAAM,WAAW,iBAAkB,SAAQ,QAAQ;IACjD,iGAAiG;IACjG,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,YAAY,CAAA;IAClB,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.6",
3
+ "version": "0.0.7",
4
4
  "description": "Auto-detect your app's URL across hosting providers. Zero config.",
5
5
  "type": "module",
6
6
  "exports": {